SwiftData Persistence
Relevant source files
The following files were used as context for generating this wiki page:
- Trackurr/Dependencies/Persistence/Migration/AppMigrationPlan.swift
- Trackurr/Dependencies/Persistence/Migration/SchemaV1.swift
- Trackurr/Dependencies/Persistence/Migration/SchemaV8.swift
- Trackurr/Dependencies/Persistence/Store/DataExportServiceImpl.swift
- README.md
- CLAUDE.md
- TrackurrTests/Persistence/Stores/CategoryStoreTests.swift
- TrackurrTests/Persistence/Stores/PreferencesStoreTests.swift
SwiftData Persistence serves as the primary data storage and management layer for the Trackurr application. It handles local data storage, private iCloud synchronization via CloudKit, and complex schema migrations as the application evolves. The system is designed with a clear separation between domain models and persistence entities, utilizing a modular architecture to ensure maintainability and testability.
The persistence layer is organized within the Trackurr/Dependencies/Persistence/ directory, containing subdirectories for data stores, schema versions, and migration plans. This structure supports a robust tech stack featuring Swift 5.9, iOS 17+, and the SwiftData framework.
Sources: README.md:19-25, CLAUDE.md:27-30
Architecture and Core Components
The architecture relies on decentralized "Store" implementations that wrap ModelContext to perform CRUD operations. These stores are injected into view models via the Factory dependency injection container.
Persistence Layer Organization
The following diagram illustrates the relationship between the persistence components and the rest of the application.
The diagram shows how View Models interact with Data Stores, which in turn manage the ModelContext and Schema Versions to interact with CloudKit or local exports.
Sources: CLAUDE.md:12-16, README.md:92-98
Key Store Implementations
- CategoryStoreImpl: Manages both user-created categories and local-only system categories.
- PreferencesStoreImpl: Manages application settings such as currency, date formats, and haptic feedback.
- TagStoreImpl: Handles the creation and management of user-defined tags.
- SubscriptionStoreImpl: The core store for tracking recurring expenses, linked to categories and tags.
Sources: TrackurrTests/Persistence/Stores/CategoryStoreTests.swift:13-17, TrackurrTests/Persistence/Stores/PreferencesStoreTests.swift:13-16, TrackurrTests/Persistence/Stores/TagStoreTests.swift:14-18
Schema and Data Modeling
Trackurr utilizes versioned schemas to maintain data integrity across updates. The current implementation has reached SchemaV8.
Entity Relationships (Schema V8)
Schema V8 introduced a significant change by separating system entities from CloudKit-synced entities to ensure local-only data remains consistent without polluting the user's private iCloud database.
This ER diagram reflects the state of the model in Schema V8, where Subscription holds direct references to Category and Tag entities, but uses ID strings for SystemCategory.
Sources: Trackurr/Dependencies/Persistence/Migration/SchemaV8.swift:20-55
Schema Evolution Table
| Version | Key Changes | Reason |
|---|---|---|
| V1 | Initial Schema (Subscription, Category, Tag, PaymentMethod). | Base implementation. |
| V2 | Added Preferences entity. | Store user settings. |
| V3 | Removed @Attribute(.unique) constraints. | Required for CloudKit compatibility. |
| V4 | All attributes made optional or given default values. | CloudKit schema requirements. |
| V5 | Added appearanceMode to Preferences. | Theme support. |
| V8 | Added SystemCategory and SystemPaymentMethod (local-only). | Separate seeded data from iCloud sync. |
| Sources: Trackurr/Dependencies/Persistence/Migration/AppMigrationPlan.swift:26-62, Trackurr/Dependencies/Persistence/Migration/SchemaV8.swift:12-19 |
Migration Strategy
The AppMigrationPlan defines how data is transformed between versions. While many migrations are lightweight (automatic), some require custom logic.
Custom Migration Logic (V7 to V8)
The migration to V8 involves re-mapping existing system-level categories to stable ID strings stored directly on the Subscription entity.
- Identify System Records: The migration looks for categories matching hardcoded names like "Finance", "Media", or "AI".
- Update Subscriptions: Sets the
systemCategoryIdon the subscription and nullifies the direct relationship to the oldCategoryentity. - Cleanup: Deletes old system category and payment method records that were previously marked with
isSystem.
Sources: Trackurr/Dependencies/Persistence/Migration/AppMigrationPlan.swift:69-125
Data Export and Backup
The DataExportServiceImpl handles the serialization of the persistent store into JSON format for user-initiated backups and restoration.
Export/Import Process
- Export: Fetches all subscriptions, custom categories, tags, and preferences, encoding them into an ISO-8601 formatted JSON file.
- Import: Decodes the JSON, validates the version (rejecting files newer than the current app version), and performs deduplication by ID and Name to avoid redundant records.
Sources: Trackurr/Dependencies/Persistence/Store/DataExportServiceImpl.swift:15-53
Testing Persistence
The project uses ModelContext.inMemory() to ensure that unit and integration tests do not persist data to disk or interfere with real user data. Tests verify CRUD operations, relationship resolution during import, and deduplication logic.
Preference Store Testing
Tests for the PreferencesStore ensure that default preferences are automatically created if they don't exist and that updates preserve unrelated fields.
Sources: TrackurrTests/Persistence/Stores/PreferencesStoreTests.swift:20-56
Data Seeding
For development and debugging, the system supports seeding the database with fictional or live-world subscription sets (e.g., subscriptions_15, subscriptions_live).
Sources: README.md:143-156
Summary
SwiftData Persistence in Trackurr provides a mature, multi-versioned storage solution tailored for iCloud synchronization. By utilizing a versioned migration plan and separating local system data from user-generated content, it ensures a reliable and scalable foundation for tracking recurring expenses across multiple iOS devices.