iOS database selection: a decision framework for CTOs and mobile leads

The iOS database recommendation that ranks first on Google today was written before two shifts that change the answer. Realm's cloud sync is deprecated, SwiftData has reached production parity, and the real decision now runs on a four-axis scorecard.

By Gaurav Masaniya· June 1, 2026

The iOS database recommendation that ranks first on Google today is one MongoDB has formally deprecated. Atlas Device Sync and the Atlas Device SDKs, known as Realm, were marked for end-of-life on 9 September 2024, with cloud sync support ending 30 September 2025. Every top-ranking guide still treats Realm as live. The decision that actually faces an iOS team now is four-way: SwiftData with CloudKit, Core Data with CloudKit Sharing, SQLite via GRDB, or a Firebase or Supabase backend.

The iOS database landscape just changed

Three shifts inside twenty months reshaped the iOS database recommendation we now make to clients. The first is the most public. MongoDB deprecated Atlas Device Sync and the Atlas Device SDKs on 9 September 2024, with full end-of-life on 30 September 2025. Realm continues as an open-source client library, but without MongoDB engineering behind it.

The second shift is quieter and as consequential. SwiftData reached production parity in iOS 26. Apple closed several of the gaps that made the framework a partial Core Data substitute, and back-ported the fixes to iOS 17. The WWDC 2025 SwiftData session walks through model inheritance, predicate-on-Codable properties, and a sortBy on history fetches.

The third shift is procedural. Apple's Developer Technical Support team routes serious multi-user iOS apps to Core Data with CloudKit Sharing, not to SwiftData, because SwiftData still lacks shared-database sync. Michael Tsai catalogued the pattern after WWDC25, almost every serious multi-user use case ends with a DTS conversation pointing back to Core Data.

These shifts compound. A team that read a "best iOS database" guide in early 2024 and started a Realm-backed greenfield app today owns a year of migration work. A team that ruled out SwiftData in 2023 because of the missing predicates and fetched results controllers may want a second look. And a team that defaulted to Core Data with CloudKit for multi-user data made the right call. Every comparison framework on the first page of Google would have framed Realm as the more "modern" choice.

The iOS 18 deployment target reaches roughly 88% of devices, which moves the SwiftData-only argument from theoretical to defensible for most consumer apps. The remaining question is which option fits which app, and that is the scorecard the rest of this post runs.

The iOS data-layer scorecard

Speed and "scalability" are not where iOS database options actually differ. Every modern option clears the bar an iOS app cares about. The decision separates on four axes we have learned to grade first: sync model, schema mutability, offline behavior, and vendor risk.

Sync model describes who shares the data and how. Per-user-only sync through the CloudKit private database is one shape. Shared-record sync between two users through CloudKit Sharing is another. Real-time fan-out to thousands of clients through Firestore or Supabase Realtime is a third. The shape determines the framework, not the other way around.

Schema mutability describes how often the data model changes after launch and what migrations cost. SwiftData hides migration behind macros, which works until a destructive change forces a manual plan. SQL-backed options give explicit migration files at the cost of more boilerplate.

Offline behavior describes what happens when the network is gone. Two questions matter: does the framework keep working without code changes, and what conflict-resolution policy ships out of the box? SwiftData, Core Data, GRDB, and Firebase all answer the first question with yes. They diverge on the second.

Vendor risk describes the cost of the vendor's roadmap changing. Apple-first options carry near-zero vendor risk for iOS-only apps. Third-party options carry the risk MongoDB just made concrete. This is the axis the listicle frameworks never grade, and the reason every comparison post we audited still treats Realm as live.

The scorecard, one row per option:

Option

Sync model

Schema mutability

Offline behavior

Vendor risk

SwiftData + CloudKit private

Single user across devices

Macros, lightweight migration

Native, last-write-wins

Apple-only, low

Core Data + CloudKit Sharing

Single user plus shared records

Versioned, manual or lightweight

Native, configurable merge policy

Apple-only, low

SQLite via GRDB or WCDB

None (app-defined sync)

Explicit migration files

Native, app-defined conflict

Open source, low

Firebase Firestore

Real-time fan-out, multi-user

Schemaless, client-driven

Cached, server-authoritative

Google ecosystem, medium

Supabase

Real-time, multi-user

Postgres migrations

Cached, server-authoritative

Open source, low

Realm (legacy)

Local only after Sep 2025

Object model, automatic

Native, last-write-wins

High, sync deprecated

The rest of this post takes each option in turn. The scorecard is what product discovery work should resolve before line one of code.

SwiftData with CloudKit for SwiftUI apps

SwiftData paired with CloudKit private sync is now our default for greenfield SwiftUI apps. The shape that fits: single-user data, an iOS 17+ deployment target, no need for NSCompoundPredicate or NSFetchedResultsController, no shared records across users.

The framework was thin when Paul Hudson catalogued the gaps in 2023, no compound predicates, no fetched results controllers, no derived attributes, no sectioned fetches, no abstract classes, no child contexts. Two years of WWDC iterations closed several of these. iOS 26 added model inheritance, predicate-on-Codable properties, and a sortBy on history fetches. Most fixes back-ported to iOS 17, which means a team building today on the iOS 17 SDK can use them without raising the deployment floor.

What is still missing matters. NSCompoundPredicate has no SwiftData equivalent. NSFetchedResultsController, which most production Core Data apps depend on for sectioned and observed queries, has no equivalent either. Shared-database sync through CloudKit Sharing is unavailable. SwiftData syncs to a user's private database only. Apps that need multi-user shared records belong on Core Data, not SwiftData.

The integration story with SwiftUI is the strongest reason teams pick SwiftData over Core Data. @Model, @Query, and @ModelActor plug into SwiftUI views with no NSManagedObjectContext plumbing. A Core Data app reaching for @FetchRequest from inside SwiftUI works, but the seams show. SwiftData hides the seams.

In the iOS engagements we run, the rough rule we apply runs two ways. If the app would have been a one-developer Core Data project with three model entities and a UIKit base, SwiftData saves real time. If the app would have been a multi-engineer Core Data project with twenty entities and fetched results controllers driving a UICollectionView, SwiftData saves nothing. The refactor cost beats the productivity win.

For founders shipping their first iOS product, SwiftData removes a week of Core Data plumbing, which matters when the first production release deadline is the gating constraint. The pattern holds for almost any single-user data shape.

Core Data with CloudKit for multi-user data

Two users editing the same record is where SwiftData stops being the answer. Core Data paired with NSPersistentCloudKitContainer and CloudKit Sharing remains Apple's only first-party path to multi-user shared records on iOS. The alternative is to bolt a custom backend onto SwiftData, which defeats the point of going first-party.

The capabilities Core Data offers that SwiftData does not yet:

  • NSCompoundPredicate for complex query composition
  • NSFetchedResultsController for sectioned and observed result sets
  • Derived attributes for fields that change without a write
  • Child contexts for transactional staging
  • Shared-database sync through CloudKit zones

Migration tooling is also more mature, lightweight migrations cover column-add changes, mapping models handle destructive changes.

The tradeoff is verbosity. Core Data ships with a managed-object-context, a persistent-store-coordinator, a persistent-container, and a model file. SwiftData hides this behind macros. A team trained on SwiftData reads Core Data as legacy code. A team migrating from Core Data to SwiftData walks away from features the product depends on.

For software maintenance and migration engagements we run, the question we ask first is whether the app actually needs shared records. If two users only ever see their own data, a personal task manager, a fitness tracker, a single-player journaling app. Core Data is overkill. If two users share a list, edit the same record, or hand off ownership, Core Data is the floor.

CloudKit Sharing comes with its own constraints. Shared records live in a separate zone, conflict resolution is opt-in through merge policies, and the user has to be signed into iCloud on every device. None of this is a blocker. All of it is plumbing the framework gives us without a custom backend.

SQLite with GRDB and WCDB

SQLite is the answer when the database is the source of truth, not the framework around it. Apps reaching for SQLite via GRDB or WCDB share three needs. The first is compound SQL queries beyond what NSPredicate allows. The second is deterministic migration files the team controls. The third is cross-platform schema parity with an Android twin running on Room.

GRDB is the Swift-native ORM most iOS teams pick first for SQLite. It exposes raw SQL when the team needs it, model persistence and observation when the team does not, and migration support that reads like a versioned changelog. GRDB also integrates with Combine and the Swift concurrency model, which keeps SQL access off the main thread without manual queue management.

WCDB is the WeChat-built alternative. It ships multi-threading optimisations, encryption at rest by default through SQLCipher, and a stronger story for high-write workloads. Teams running chat apps, media-heavy apps, or apps with hundreds of writes per second tend to land here. The tradeoff is a smaller Swift community than GRDB, and a steeper learning curve for engineers not coming from C++.

The encryption case is where SQLite-via-GRDB pulls ahead of the Apple-native options. SQLCipher provides 256-bit AES encryption at rest, with a key derivation that lets the app gate database access on a user-supplied passphrase. Healthcare apps under HIPAA review and fintech apps under PCI scope often need this. Apple's Data Protection API is real but coarser-grained, and CloudKit only encrypts data in transit between devices, not the local store.

Full-text search is the other native SQLite advantage. FTS5 ships with the SQLite bundled into iOS and supports tokenised search across very large text columns without an external search index. A notes app, a chat history, or a messaging archive that wants in-app search gets it from SQLite directly. SwiftData has no FTS equivalent today.

The tradeoff for all of this is boilerplate. GRDB and WCDB do not hide the database. That is the point.

Firebase and Supabase as backends

Firebase and Supabase are not really "iOS databases", they are backends with iOS SDKs. The decision is whether to pay the cloud tax for real-time sync, an out-of-the-box auth stack, hosted file storage, and serverless functions. Both products bundle these. The differences are in pricing risk, in the data model, and in the ecosystem cost.

Firebase Firestore is Google's serverless document store with a real-time SDK that fans changes out to every connected client. Chat apps, collaborative editors, multiplayer games, and live-tracking apps lean on it because the synchronisation is the product. The free tier is generous; the costs after the free tier are not, and the pricing curve surprises teams that ship a viral feature. Firestore also ties the project to Google Cloud. Migrating off Firestore once the data model is wired into thousands of lines of client code is a six-month project, not a one-sprint task.

Supabase is the open-source alternative built on PostgreSQL. The data model is relational, which fits teams whose engineers already think in tables. Real-time subscriptions, row-level security, authentication, storage, and edge functions ship with the platform. The vendor risk is lower than Firebase because Supabase can be self-hosted, the schema is portable in a way Firestore's never is. PostgreSQL is also the most-used database among professional developers in the Stack Overflow Developer Survey 2024, which matters when the team has to hire backend engineers later.

The choice between them is rarely about features. It is about the team. A SwiftUI-first iOS team with no backend engineers picks Firebase because the SDK ergonomics are better and the auth flows are turnkey. A team with one full-stack engineer who already knows SQL picks Supabase because the database speaks a language the team can extend.

For apps where the backend engineering for iOS apps we run is a custom Rails or Node service on Railway or Fly, neither Firebase nor Supabase is the answer. The cloud tax pays for itself only when the alternative is a backend the team would have built anyway.

Realm and the migration that follows

No new iOS project we are briefed on should start on Realm. MongoDB deprecated Atlas Device Sync and Atlas Device SDKs on 9 September 2024, with full end-of-life on 30 September 2025. The client-side Realm SDK continues as an open-source project, without MongoDB engineering investment behind it. For greenfield apps, this is a vendor-risk score the scorecard rates as the highest of any option on the list.

For existing apps already on Realm, the migration path depends on how much of the product depends on Realm's sync layer versus its local persistence layer. Local-only Realm apps can keep using the open-source SDK for some time. Bug fixes will be community-paced, and security work is the team's responsibility. Apps that depended on Atlas Device Sync for cloud synchronisation had until 30 September 2025 to migrate the sync layer.

Four migration paths cover most cases. SwiftData with CloudKit handles single-user data. Core Data with CloudKit Sharing covers multi-user records. Couchbase Mobile keeps the Realm-shaped object model with a working sync layer. A custom backend on Firebase, Supabase, or self-hosted Postgres works when the team wants to own the sync logic.

The migration cost concentrates in three layers. The model layer. Realm objects do not map 1:1 to SwiftData @Model classes, and the relationships sometimes need reshaping. The sync layer, whatever replaces Atlas Device Sync has its own conflict-resolution semantics. The query layer. Realm's filter API does not translate cleanly to either NSPredicate or #Predicate.

The software maintenance and migration work we run typically starts with the sync layer because the deadline is fixed. The model and query layers can move in parallel, and the migration plan ships in three to five increments rather than a single rewrite.

The migration matrix we walk teams through:

Migration target

Best for

Cost

Sync layer

SwiftData + CloudKit

Single-user iOS-only apps

Low for simple models

First-party

Core Data + CloudKit Sharing

Multi-user iOS-only apps

Medium, closer feature parity

First-party

Couchbase Mobile

Apps wanting Realm-shaped sync

Medium, closest object model fit

Third-party, supported

Custom backend

Teams owning the sync logic

Highest, full backend build

Self-managed

Three production failure modes

The database failures that destroy a launch are the ones that surface only in production. Three categories matter most: at-rest encryption, sync conflict, and quota.

Encryption at rest is the gap most often discovered in a HIPAA or SOC 2 audit. iOS gives us several layers. Apple's Data Protection API ties file encryption to the device passcode through the Secure Enclave, which covers most of the threat model for most consumer apps. For apps that need per-record encryption with a user-supplied key, medical records, financial records, regulated chat. SQLCipher remains the cleanest path. SwiftData and Core Data do not expose a SQLCipher integration. The path there is SQLite via GRDB or WCDB. We bring this up early in the iOS engagements we run for healthcare and fintech clients. Retrofitting encryption after the schema is locked in costs a quarter of engineering time.

Sync conflict is what happens when two devices write to the same record while one is offline. CloudKit (with SwiftData or Core Data) ships a default merge policy, last-write-wins on a per-field basis, with the framework hiding most of the resolution. Firestore and Supabase Realtime use server-authoritative resolution by default, which means the client never sees a conflict but may see a write get silently overwritten. GRDB and WCDB do not ship a conflict-resolution policy at all, the app defines what "conflict" means. The right framework here is the one whose default matches the product's correctness requirements.

Quota is the failure mode most teams never plan for. CloudKit's free tier covers the early months of most apps. Each app gets 10 GB of asset storage, 100 MB of database storage, 2 GB of transfer per month, and 40 requests per second. The limits scale upward with active users, and the cloud architecture practice we run sizes the headroom with clients before launch. Private-database data counts against the user's iCloud quota, not the app's. A user with a full iCloud account hits CKError.quotaExceeded on the first write, and the app needs to handle the error before the user blames the app. We see this fail in App Store review more often than in production. Apple's review accounts often have full iCloud.

Frequently asked questions about iOS databases

What is the difference between SwiftData and Core Data?

SwiftData is Apple's Swift-native persistence framework, built on top of Core Data and introduced at WWDC 2023. It replaces NSManagedObject and .xcdatamodeld files with Swift macros and pure Swift classes annotated with @Model. The integration with SwiftUI is materially better. The tradeoff is feature coverage. SwiftData still lacks NSCompoundPredicate, NSFetchedResultsController, derived attributes, and shared-database sync through CloudKit Sharing. For most new SwiftUI-first apps with single-user data, SwiftData is now the right starting point. For complex multi-user data, Core Data remains the floor.

Can SwiftData be used in a UIKit app?

Yes. SwiftData is not tied to SwiftUI. The @Model classes, the ModelContext, and the query APIs work from UIKit view controllers without any SwiftUI imports. The reason the framework is associated with SwiftUI is the @Query property wrapper, which integrates with SwiftUI view updates. In a UIKit app, the equivalent pattern is a manual FetchDescriptor call and a notification-driven UI refresh, closer to how Core Data integrates with UIKit. The ergonomics are still better than Core Data, but the SwiftUI integration is where SwiftData earns the most time back.

What replaced Realm after MongoDB's end-of-life cutoff?

MongoDB ended Atlas Device Sync and the Atlas Device SDKs on 30 September 2025, with the deprecation announced on 9 September 2024. The client-side Realm database continues as an open-source project, without MongoDB engineering support. Four migration paths cover most cases. SwiftData with CloudKit handles single-user data. Core Data with CloudKit Sharing covers multi-user records. Couchbase Mobile keeps a Realm-shaped object model with working sync. A custom backend on Firebase, Supabase, or self-hosted Postgres works when the team prefers to own the sync logic.

How much free CloudKit storage does each user get?

CloudKit splits storage between the user's private database and the app's public database. Private data counts against the user's personal iCloud quota. Apple gives each user 5 GB free, with paid tiers scaling to 12 TB. Public database storage counts against the app's allocation. The starting allocation is 100 MB of database storage, 10 GB of assets, 2 GB of transfer per month, and 40 requests per second. The limits scale upward with the count of active users. The full pricing schedule lives on Apple's iCloud developer page and changes occasionally. Verify before sizing capacity.

Does Firebase work offline on iOS?

Yes. The Firestore iOS SDK ships with offline persistence enabled by default, reads and writes are queued locally and synchronised when the network returns. The same is true of the Realtime Database SDK with keepSynced(true) configured. The tradeoff is that offline conflict resolution is server-authoritative. When the device reconnects, the server's view of the record wins, and local writes can be silently overwritten. For apps where the device should be the source of truth between syncs, CloudKit or a custom backend with client-authoritative conflict policy is a better fit.

Is SQLite encrypted by default on iOS?

No. The SQLite library bundled with iOS does not encrypt database files at rest by default. File-level encryption is available through Apple's Data Protection API, which ties encryption to the device passcode through the Secure Enclave, sufficient for most consumer apps. For per-record encryption with a user-supplied key, SQLCipher is the standard path. SQLCipher uses 256-bit AES and integrates cleanly with GRDB and WCDB. Healthcare apps under HIPAA review and fintech apps under PCI scope generally need SQLCipher or an equivalent layer. Data Protection alone is not sufficient for those threat models.

Is it worth migrating an existing Core Data app to SwiftData?

Usually no. Core Data is mature, well-documented, and continues to receive Apple engineering investment. There is no roadmap to deprecate it. The migration cost depends on what the existing app uses. Apps relying on NSCompoundPredicate, NSFetchedResultsController, derived attributes, child contexts, or CloudKit Sharing have feature gaps in SwiftData that block a clean port. Apps with a simple model and a SwiftUI front-end can migrate productively. The conservative pattern we recommend in maintenance engagements is to leave Core Data in place, build new modules in SwiftData if iOS 17+ is the deployment target, and let the two coexist.

Make the call before the schema is locked

The data-layer decision is the part of an iOS engagement most teams hand to engineering on day 60. By then, the cost of changing it has tripled. Schemas are wired into views, sync policies are wired into product logic, and the migration is a rewrite, not a refactor.

Tech Kodainya's iOS development practice runs the data-layer scorecard with the client before kickoff. SwiftData, Core Data with CloudKit Sharing, GRDB on SQLite, and Firebase or Supabase backends are all on the menu. The option is chosen on the four axes above, not on the framework the engineer learned first.

Ready to make the call before the schema is locked in? Talk to our iOS engineering team.

iOS database: a decision framework for CTOs and mobile leads — Tech Kodainya