Data Export & Backup
Relevant source files
The following files were used as context for generating this wiki page:
- Trackurr/Dependencies/Persistence/Store/DataExportServiceImpl.swift
- Features/SettingsFeature/Sources/SettingsFeature/Views/BackupExport/BackupExportViewModel.swift
- Packages/Domain/Sources/Domain/AppBackup.swift
- Features/SettingsFeature/Sources/SettingsFeature/Views/BackupExport/BackupExportView.swift
- TrackurrTests/Persistence/Stores/DataExportServiceTests.swift
- Trackurr/Dependencies/Persistence/Migration/AppMigrationPlan.swift
The Data Export & Backup system provides users with the ability to secure their application data through JSON-based backups. It enables the extraction of all user-generated content—including subscriptions, custom categories, tags, and preferences—into a portable format that can be shared or restored later.
This system is integrated within the Settings Feature and acts as a safety mechanism for local-first data storage. While the application supports CloudKit Sync, the manual backup provides a versioned, point-in-time snapshot of the database state.
Sources: README.md, Packages/Domain/Sources/Domain/AppBackup.swift:4-14
Architecture and Data Flow
The architecture follows a standard MVVM pattern, delegating the heavy lifting of data transformation and persistence to a specialized service layer.
Core Components
| Component | Responsibility |
|---|---|
BackupExportView | SwiftUI interface for triggering export/import and displaying progress. |
BackupExportViewModel | Manages view state, analytics logging, and entitlement checks. |
DataExportServiceImpl | Main logic for serializing domain models to JSON and restoring them to SwiftData. |
AppBackup | The portable data structure used for encoding and decoding backups. |
Sources: Features/SettingsFeature/Sources/SettingsFeature/Views/BackupExport/BackupExportView.swift:6-10, Features/SettingsFeature/Sources/SettingsFeature/Views/BackupExport/BackupExportViewModel.swift:10-25, Trackurr/Dependencies/Persistence/Store/DataExportServiceImpl.swift:9-15, Packages/Domain/Sources/Domain/AppBackup.swift:4-14
Data Export Flow
The export process aggregates data from multiple SwiftData entities, converts them to pure Domain models, and packages them into a single versioned AppBackup struct.
The system distinguishes between "System" and "Custom" entities. Only custom categories and payment methods are exported, as system entities are seeded locally by the application logic during initialization or migration.
Sources: Trackurr/Dependencies/Persistence/Store/DataExportServiceImpl.swift:20-40, Features/SettingsFeature/Sources/SettingsFeature/Views/BackupExport/BackupExportViewModel.swift:45-62
Data Structure and Schema
Backups are versioned to ensure compatibility with evolving app schemas. The current version is 2.
AppBackup Model
| Field | Type | Description |
|---|---|---|
version | Int | Schema version (currently 2). |
exportDate | Date | Timestamp of when the backup was created. |
preferences | Preferences | User settings like currency and haptics. |
subscriptions | [Subscription] | Full list of subscription data including amounts and cycles. |
categories | [Category] | Custom categories created by the user. |
tags | [Tag] | User-defined tags for organizing subscriptions. |
Sources: Packages/Domain/Sources/Domain/AppBackup.swift:4-14, Trackurr/Dependencies/Persistence/Store/DataExportServiceImpl.swift:48-55
Import Logic and Deduplication
The import process is designed to be additive and non-destructive for existing relationships. It performs deep deduplication to prevent duplicate records if the same file is imported multiple times.
Deduplication occurs by checking both the unique id (UUID) and the name of entities. If a category with the same name already exists in the local database, the importer skips creating a new entity and instead resolves existing relationships to that local entity.
Sources: Trackurr/Dependencies/Persistence/Store/DataExportServiceImpl.swift:72-108, TrackurrTests/Persistence/Stores/DataExportServiceTests.swift:186-215
Premium Entitlements
Access to the data export feature is gated behind a premium tier. The BackupExportViewModel checks the entitlementService for the .dataExport feature flag before allowing an export operation.
- Free Users: The export button is locked, and a footer informs them to upgrade. Tapping the button triggers a
ShowPaywallEvent. - Premium Users: The export button is active, and a descriptive footer is shown.
Sources: Features/SettingsFeature/Sources/SettingsFeature/Views/BackupExport/BackupExportViewModel.swift:39-43, Features/SettingsFeature/Sources/SettingsFeature/Views/BackupExport/BackupExportView.swift:77-98
Implementation Details
JSON Configuration
The export service uses specific JSONEncoder and JSONDecoder configurations to ensure cross-version stability:
- Date Strategy:
.iso8601for both encoding and decoding. - Formatting:
.prettyPrintedand.sortedKeysare applied during export for human readability and version control friendliness.
Sources: Trackurr/Dependencies/Persistence/Store/DataExportServiceImpl.swift:34-36, Trackurr/Dependencies/Persistence/Store/DataExportServiceImpl.swift:46-47
Relationship Resolution
When importing subscriptions, the service must reconnect them to their related entities (Category, Tags, PaymentMethod).
- System Entities: Resolved via
systemCategoryIdorsystemPaymentMethodId. - Custom Entities: The service attempts to find an existing entity by ID; if not found, it searches by Name. If both fail, it creates a new entity from the backup data.
Sources: Trackurr/Dependencies/Persistence/Store/DataExportServiceImpl.swift:115-144, Trackurr/Dependencies/Persistence/Migration/AppMigrationPlan.swift:68-88
Summary
The Data Export & Backup system ensures data portability and user ownership. By combining a versioned JSON schema with robust deduplication logic, it allows for seamless transitions between devices or clean installs while maintaining the integrity of complex relational data like subscriptions and their categories.