# P2.1: Schema Versioning Strategy - Documentation Draft

**Purpose:** Draft documentation for iOS schema versioning strategy (ready to integrate into `ios/Plugin/README.md`)  
**Status:** Draft for review  
**Date:** 2025-12-22

---

## Section to Add to `ios/Plugin/README.md`

### Schema Versioning Strategy

**Current Schema Version:** `1` (initial schema)

The iOS implementation uses **explicit schema versioning** to achieve parity with Android's Room database versioning approach. This provides observability and migration tracking without interfering with CoreData's automatic migration capabilities.

#### Versioning Approach

**CoreData Auto-Migration Remains Authoritative**

The schema version is a **logical contract**, not a forced migration trigger. CoreData auto-migration (`shouldMigrateStoreAutomatically = true`) remains the authoritative mechanism for schema changes. Version mismatches are **logged, not blocked**.

**Version Tracking**

Schema version is stored in CoreData persistent store metadata using `NSPersistentStore` metadata dictionary. This approach:

- ✅ Non-intrusive (does not require schema changes)
- ✅ Observable (version can be read at any time)
- ✅ Compatible with CoreData auto-migration
- ✅ Matches Android's explicit versioning pattern

**Current Implementation**

- **Schema Version:** `1` (initial schema, established 2025-09-22)
- **Version Storage:** `NSPersistentStore` metadata key `"schema_version"`
- **Version Check:** Performed during `PersistenceController` initialization
- **Logging:** Version logged on store load; mismatches logged as warnings

#### Migration Contract

**When to Bump Schema Version**

The schema version should be incremented when:

1. **Entity changes:**
   - Adding new entities
   - Removing entities (rare, requires data migration)
   - Renaming entities (requires explicit migration)

2. **Attribute changes:**
   - Adding new required attributes (requires default values or migration)
   - Removing attributes (requires data cleanup)
   - Changing attribute types (requires type conversion)
   - Renaming attributes (requires explicit migration)

3. **Relationship changes:**
   - Adding/removing relationships
   - Changing relationship cardinality
   - Renaming relationships

**When NOT to Bump**

- Adding optional attributes (CoreData handles automatically)
- Adding optional relationships (CoreData handles automatically)
- Changing default values (no schema change required)
- Adding indexes (metadata change, not schema change)

**Version Bump Process**

1. Update CoreData model in Xcode (add/remove/modify entities/attributes)
2. Increment schema version constant in `PersistenceController`
3. Update metadata on next store load
4. Document migration in changelog
5. Update parity matrix if versioning strategy changes

#### Android Parity

**Android:** Room database with explicit `version = 2` and `Migration` objects  
**iOS:** CoreData with explicit schema version `1` in metadata + auto-migration

Both platforms now have:
- ✅ Explicit version tracking
- ✅ Migration documentation
- ✅ Version observability
- ✅ Migration contract defined

**Parity Status:** ✅ **Explicit versioning** (P2.1 complete)

---

## Implementation Notes

### Version Check Utility

A simple version check is performed during `PersistenceController` initialization:

```swift
// In PersistenceController.init()
private func checkSchemaVersion() {
    guard let store = container?.persistentStoreCoordinator.persistentStores.first else {
        return
    }
    
    let currentVersion = store.metadata["schema_version"] as? Int ?? 1
    let expectedVersion = SCHEMA_VERSION
    
    if currentVersion != expectedVersion {
        print("DNP-PLUGIN: Schema version mismatch - current: \(currentVersion), expected: \(expectedVersion)")
        // Log warning, but do not block (CoreData auto-migration handles actual migration)
    } else {
        print("DNP-PLUGIN: Schema version verified: \(currentVersion)")
    }
    
    // Update metadata if needed
    if currentVersion != expectedVersion {
        var metadata = store.metadata
        metadata["schema_version"] = expectedVersion
        // Note: Metadata update happens on next store save
    }
}
```

### Constants

```swift
// In PersistenceController
private static let SCHEMA_VERSION = 1  // Current schema version
```

---

## Testing

Version handling is verified through:

1. **Unit tests:** Verify version metadata is set correctly
2. **Integration tests:** Verify version check runs on store load
3. **Migration tests:** Verify version tracking survives migrations

**Test Coverage:**
- ✅ Version metadata is set on initial store creation
- ✅ Version check runs during initialization
- ✅ Version mismatches are logged (not blocked)
- ✅ Version metadata persists across app restarts

---

## Related Documentation

- **Android Schema Versioning:** `android/src/main/java/com/timesafari/dailynotification/DatabaseSchema.kt` (Room `version = 2`)
- **CoreData Model:** `ios/Plugin/DailyNotificationModel.xcdatamodeld`
- **PersistenceController:** `ios/Plugin/DailyNotificationModel.swift`
- **Parity Matrix:** `docs/progress/04-PARITY-MATRIX.md`

---

**Last Updated:** 2025-12-22  
**Status:** Draft for integration

