P2.1: iOS Schema Versioning Strategy
- Added SCHEMA_VERSION constant and checkSchemaVersion() method in PersistenceController
- Version stored in NSPersistentStore metadata (observability contract, not migration gate)
- CoreData auto-migration remains authoritative; version mismatches logged, not blocked
- Documentation added to ios/Plugin/README.md with migration contract
P2.2: Combined Edge Case Tests
- Added 3 resilience test scenarios to DailyNotificationRecoveryTests.swift:
- test_combined_dst_boundary_duplicate_delivery_cold_start()
- test_combined_rollover_duplicate_delivery_cold_start()
- test_combined_schema_version_cold_start_recovery()
- All tests labeled with @resilience @combined-scenarios comments
- Tests verify idempotency and correctness under combined stressors
P2.3: Android Combined Tests Design
- Created P2.3-DESIGN.md with scope, invariants, and acceptance criteria
- Created P2.3-IMPLEMENTATION-CHECKLIST.md with step-by-step execution plan
- Design ready for implementation to achieve parity with iOS P2.2
Documentation Updates
- Fixed parity matrix: iOS invalid data handling now correctly shows "✅ Recovery tested" with test references
- Updated progress docs (00-STATUS.md, 01-CHANGELOG-WORK.md, 03-TEST-RUNS.md, 04-PARITY-MATRIX.md)
- Updated P2-DESIGN.md to reflect P2.3 scope (Android combined tests)
- Updated SYSTEM_INVARIANTS.md baseline tag references
Baseline Tag
- Created and pushed v1.0.11-p2-complete tag
- Tag represents P2.x completion (schema versioning + combined resilience tests)
All invariants preserved. CI passes. Tests runnable via xcodebuild on macOS.
7.6 KiB
P2.1: Schema Versioning Strategy - Implementation Plan
Purpose: Step-by-step implementation plan for P2.1 schema versioning
Status: Ready for execution
Date: 2025-12-22
Estimated Effort: 4-6 hours
Overview
Add explicit schema versioning to iOS CoreData implementation to achieve parity with Android's Room database versioning. This is a documentation-first, minimal-code approach that provides observability without interfering with CoreData's automatic migration.
Implementation Steps
Step 1: Add Schema Version Constant (5 min)
File: ios/Plugin/DailyNotificationModel.swift
Location: Add near top of PersistenceController class
Code:
class PersistenceController {
// MARK: - Schema Versioning
/// Current schema version (incremented when schema changes)
private static let SCHEMA_VERSION = 1
// ... existing code ...
}
Verification:
- Constant added
- Compiles without errors
Step 2: Add Version Check Method (15 min)
File: ios/Plugin/DailyNotificationModel.swift
Location: Add as private method in PersistenceController class
Code:
/**
* Check and log schema version
*
* Schema version is a logical contract, not a forced migration trigger.
* CoreData auto-migration remains authoritative; version mismatches are
* logged, not blocked.
*/
private func checkSchemaVersion() {
guard let store = container?.persistentStoreCoordinator.persistentStores.first else {
return
}
let currentVersion = store.metadata["schema_version"] as? Int ?? 1
let expectedVersion = PersistenceController.SCHEMA_VERSION
if currentVersion != expectedVersion {
print("DNP-PLUGIN: Schema version mismatch - current: \(currentVersion), expected: \(expectedVersion)")
print("DNP-PLUGIN: CoreData auto-migration will handle schema changes")
// Update metadata for future reference (does not trigger migration)
var metadata = store.metadata
metadata["schema_version"] = expectedVersion
// Note: Metadata persists on next store save
} else {
print("DNP-PLUGIN: Schema version verified: \(currentVersion)")
}
}
Verification:
- Method added
- Compiles without errors
- Follows existing code style
Step 3: Call Version Check on Initialization (5 min)
File: ios/Plugin/DailyNotificationModel.swift
Location: In init(inMemory:) method, after container is successfully loaded
Code:
// Configure view context
if let context = tempContainer?.viewContext {
context.automaticallyMergesChangesFromParent = true
context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
}
self.container = tempContainer
// Check schema version (after container is initialized)
checkSchemaVersion()
// Verify all entities are available (after container is initialized)
if let context = tempContainer?.viewContext {
verifyEntities(in: context)
}
Verification:
- Version check called after container initialization
- Compiles without errors
- Version logged on app launch
Step 4: Set Initial Version Metadata (10 min)
File: ios/Plugin/DailyNotificationModel.swift
Location: In init(inMemory:) method, when creating new store
Code:
// Configure persistent store options
let description = tempContainer?.persistentStoreDescriptions.first
description?.shouldMigrateStoreAutomatically = true
description?.shouldInferMappingModelAutomatically = true
// Set initial schema version metadata (for new stores)
if !inMemory {
var metadata = description?.metadata ?? [:]
if metadata["schema_version"] == nil {
metadata["schema_version"] = PersistenceController.SCHEMA_VERSION
description?.metadata = metadata
}
}
Verification:
- Initial version metadata set for new stores
- Compiles without errors
- Version metadata persists across app restarts
Step 5: Add Documentation to README (30 min)
File: ios/Plugin/README.md
Location: Add new section after "Implementation Details" section
Content: Use the draft from docs/progress/P2.1-SCHEMA-VERSIONING-DRAFT.md
Steps:
- Copy the "Schema Versioning Strategy" section from the draft
- Paste into
ios/Plugin/README.mdafter "Implementation Details" - Update "Last Updated" date in README header
- Verify markdown formatting
Verification:
- Documentation added
- Markdown renders correctly
- All links/references are valid
- "Last Updated" date updated
Step 6: Update Parity Matrix (5 min)
File: docs/progress/04-PARITY-MATRIX.md
Location: Update "Storage & Persistence" section
Change:
| Schema versioning | ✅ Room migrations | ✅ Explicit | iOS has explicit version tracking in CoreData metadata |
Verification:
- Parity matrix updated
- Status changed from "⚠️ Partial" to "✅ Explicit"
- Notes section updated
Step 7: Update Progress Docs (10 min)
Files:
docs/progress/00-STATUS.mddocs/progress/01-CHANGELOG-WORK.mddocs/progress/03-TEST-RUNS.md
Updates:
- Mark P2.1 as complete in status
- Add changelog entry
- Add test run entry (manual verification)
Verification:
- All progress docs updated
- Dates are correct
- Status reflects completion
Step 8: Run CI and Verify (10 min)
Command:
./ci/run.sh
Verification:
- CI passes
- No new errors introduced
- Version logging appears in console output (manual check)
Testing Checklist
Manual Testing
- New Store: Create new CoreData store, verify version metadata is set
- Existing Store: Load existing store, verify version check runs
- Version Logging: Verify version logged on app launch
- Metadata Persistence: Verify version metadata persists across app restarts
Code Review
- Code follows existing style
- Comments are clear and accurate
- No breaking changes introduced
- CoreData auto-migration still works
Acceptance Criteria Checklist
- iOS schema versioning strategy documented (with explicit "logical contract" clarification)
- Version tracking implemented in CoreData model (metadata)
- Migration contract defined (when to bump versions)
- Version check utility added (logs version on init, does not block)
- Parity matrix updated (schema versioning: ✅ Explicit)
- All CI checks pass
- Progress docs updated
Rollback Plan
If issues arise:
- Revert code changes: Remove version check method and calls
- Revert documentation: Remove schema versioning section from README
- Revert parity matrix: Change back to "⚠️ Partial"
- Update progress docs: Mark P2.1 as incomplete
Baseline tag available: v1.0.11-p0-p1.4-p1.5-p2.6-p2.7-complete
Next Steps After P2.1
- Checkpoint: Run
./ci/run.sh, update progress docs - Proceed to P2.2: Combined edge case tests
- Optional: Create baseline tag
v1.0.11-p2.1-completeif desired
Last Updated: 2025-12-22
Status: Ready for execution