Files
obsidian-vault/work/tech-design/coredata-code-defined-models.md
T

3.2 KiB
Raw Blame History

title, created, tags, status, chosen-approach, asana
title created tags status chosen-approach asana
Tech Design: Code-defined Core Data models 2026-09-25
tech-design
apple
core-data
persistence
spm
build
draft Schemas defined in Swift with full version history; explicit lightweight migration TODO

Author: Alexey Martemyanov Reviewer: Sam Symons Stakeholders: Bartek, Dominik, Federico Cappelli, Michal Smaga Project: Build the apps and packages outside Xcode (VS Code / swift build)


Background & Requirements

We're moving development and CI to VS Code and SwiftPM builds that don't go through Xcode.

Core Data schemas live in .xcdatamodeld files. Only Xcode compiles them into the .momd the app loads, so SwiftPM builds and tests outside Xcode have no model. BSK tests work around this by running momc at runtime.

Requirements:

  • existing user stores open unchanged
  • upgrades from any older version keep data
  • minimum OS stays at iOS 15 / macOS 12.3.

Problem Statement

Core Data models can't be built outside Xcode.

Define each model in Swift instead of .xcdatamodeld, one file per model, containing every shipped schema version. A small Persistence helper builds the model at runtime and migrates stores created by older versions.[1]

Existing stores are unaffected: Core Data matches a store to a model by schema hashes, and the code-defined versions produce the same hashes as the compiled .momd. The hashes are pinned in tests.

Steps (one PR each):

  1. Persistence support + PrivacyStats and BrowsingHistory.
  2. Bookmarks + SyncMetadata.
  3. Stores combining several models: HTTPSUpgrade, RemoteMessaging, AppRatingPrompt.[2]
  4. macOS app models: History, Downloads, legacy Bookmark.
  5. Remove the momc workaround.

Each step is revertible: the on-disk format doesn't change.

Notes

[1] Old versions are needed because migration looks for the old schema in the compiled models, which will no longer exist. NSStagedMigrationManager handles code-defined versions but requires iOS 17 / macOS 14.

[2] A combined store needs the old versions of all its models, so they're converted together.

Alternatives:

  • Remove only the generated dynamic CoreData property accessors: momc is still needed and compiled as a separate Swift Build System Plugin step.
  • Commit precompiled .momd files: binary artifacts that can drift from the source.

Trade-off: we lose Xcode's visual model editor (but we get standardized, structured, great&shiny code definitions!)

Testing

  • Unit tests pin the schema hashes of every shipped version.
  • Migration tests upgrade old-version stores and check the data.
  • Package tests run with plain swift test.
  • Manual check on iOS and macOS: upgrade from the App Store build and confirm the data is intact.

Additional Considerations

  • Privacy — No change.
  • Security — No change.
  • Site Breakage — N/A.
  • Experimentation — N/A. The schema can't be feature-flagged; rollback is a revert.
  • Operational — Package builds no longer need Xcode‘s momd compiler. Migration failures are reported through the existing store-loading errors.
  • Localization / Internationalization — N/A.