← All writing
HackerNoonAugust 2026

iOS Data Storage: Classify Your Data Before You Choose a Database

Every new iOS project reaches the same fork in the road, and it is almost always framed as a framework decision: is SwiftData production-ready yet, should we stay on Core Data, would GRDB give us more control? Reasonable questions, wrong starting point. Before any of them can be answered you need to know what the app stores, how long it has to survive, how sensitive it is, whether a copy can be fetched from the server again, whether offline use is required, and whether it has to travel between devices.

So the piece works through a sample app and sorts its data by those properties rather than by convenience. UI preferences belong in UserDefaults. Tokens and credentials belong in the Keychain. Domain entities should be designed from the queries you will actually run, not from whichever persistence framework is fashionable. Large attachments belong on the filesystem, with the database holding references. Cache is not user data and should not be treated as if losing it were a bug.

It also walks the decisions that are painful to reverse after shipping: where the store's container lives, what happens when the store refuses to open, and why synchronisation is a separate system rather than a checkbox on your persistence layer.

Choosing the engine for the one category that does need a database is the subject of the follow-up, “SwiftData, Core Data, or GRDB: Choose by the Queries You Actually Run”.

What's inside

  • It introduces a hypothetical travel-planning app and sorts everything it needs to store into six categories — preferences, secrets, domain entities, user files, disposable cache, and sync metadata — each carrying a different set of requirements before any framework enters the discussion.
  • It backs UserDefaults for small UI preferences but warns that something like a full search history stops being a preference once it needs sorting or deletion, and notes that UserDefaults itself is unencrypted and swept into device backups, which is precisely why secrets don't belong there.
  • It covers Keychain for tokens and credentials, centering on the accessibility setting that decides whether a secret survives being restored onto different hardware, and argues that setting should be chosen on purpose rather than left at whatever the API defaults to.
  • It details storing large attachments like tickets and photos on the filesystem with only metadata in the database, touching file-protection levels, per-file backup exclusion, and the separate reliability problem of keeping a file and its database row from drifting apart.
  • It treats cached data as something the app must be willing to lose without asking anyone, contrasts that with anything the user actually typed, and sets out what a launch-time failure to open the store should do instead of silently starting over empty.
  • It frames synchronization as its own subsystem with open questions about record versions, conflicting edits, and retry queues, and points to new iOS 27 APIs for composing filters and observing local history that help a sync engine know what still needs uploading.
The takeaway

The argument is that classification comes before any framework decision: five of the six data categories in the sample app are settled without touching a database at all, and only the domain entities are left over for the companion piece on SwiftData, Core Data, and GRDB to resolve.

Read the full classification, with code for every layer, on HackerNoon ↗
Part of a pairSwiftData, Core Data, or GRDB: Choose by the Queries You Actually Run