Key Considerations Before Using SwiftData

Published on

At the recently concluded Let’s Vision 2025 conference, I received many questions about SwiftData: “Is SwiftData mature enough to be used in real projects?” and “As a beginner, how can I use SwiftData efficiently?” These questions not only reflect developers’ keen interest in Apple’s latest data persistence framework but also reveal hesitations when making technology choices.

This article aims to serve as a guide for developers interested in SwiftData, helping you understand its strengths and limitations so you can make informed decisions based on your project needs. Whether you’re considering adopting SwiftData in a new project or planning a migration from another persistence solution, the following content will provide valuable insights to support your decision-making process.

How Does SwiftData Differ from Frameworks like GRDB/SQLite.swift?

A few weeks ago, Point-Free released an open-source library called SharingGRDB, which greatly optimizes the use of GRDB.swift in SwiftUI projects. This development led some developers to wonder: if SharingGRDB is so convenient, is there still a need to use SwiftData?

In fact, the core difference between SwiftData/Core Data and tools like GRDB.swift/SQLite.swift is not about coding style or API design, but rather the fundamental differences in data abstraction levels and organizational approaches.

SwiftData embraces the philosophy of “data is an object.” Its data models seamlessly integrate with Swift’s object-oriented programming paradigm and naturally align with SwiftUI’s declarative programming style. In contrast, tools like GRDB and SQLite.swift lean towards the idea of “data is a database,” making them more suitable for database-centric development workflows.

If you are a developer well-versed in SQL databases, you might prefer using tools like GRDB. They grant you greater autonomy and flexibility, allowing you to fully exploit SQLite’s powerful capabilities for organizing and optimizing data. You might find the concepts of “tables,” “columns,” and “rows” very familiar, and you’re likely accustomed to mapping these database structures directly to your Swift code.

However, if you lean more towards object-oriented programming or prefer not to (or need not) concern yourself with the underlying persistence details, SwiftData is undoubtedly the better choice. It enables you to manage data objects and their relationships in a manner that feels almost native to Swift, while the database-specific concepts such as “tables,” “columns,” and “rows” are automatically abstracted away by the framework.

Beyond these core differences, SwiftData/Core Data offers some advantages that are hard to match by third-party frameworks:

  • Seamless integration of iCloud-based backup and data synchronization for free, which is almost a standard requirement for developers building apps within the Apple ecosystem, especially for small teams or independent developers.
  • Full support as a native system framework—in scenarios with resource-constrained environments such as widgets or App Intents, both SwiftData and Core Data run smoothly and support real-time data change notifications (achieved via SwiftData History or Persistent History Tracking).

Thus, if your project only requires local data storage, you can choose the appropriate tool based on your team’s background, project requirements, and technical preferences. However, if your project involves persistence in resource-constrained scenarios, opting for SwiftData will save you a great deal of hassle.

SwiftData Entities Are Not the Same as Regular Swift Objects

Apple provides the @Model macro for SwiftData to simplify the data modeling process. Many introductory tutorials emphasize its convenience, which leads some beginners to mistakenly assume that building data models is as straightforward as working with ordinary Swift objects. However, even though SwiftData strives to hide the underlying persistence details, it is still bound by the physical limitations of SQLite: ultimately, all data types will be converted into formats supported by SQLite (SwiftData/Core Data have not yet fully exploited all of SQLite’s supported data formats).

When building data models with SwiftData, beginners should note the following:

  • Prefer using basic types: such as Int, Double, Date, URL, String, etc., for model properties.
  • Choose stable complex types: For complex types conforming to Codable, try to use those that are widely adopted and have a stable format, such as CGPoint or CLLocation.
  • Use custom Codable types cautiously: Avoid using custom Codable types directly unless you are sure they won’t need to be modified later.
  • Using Enumerations: Enumerations with a RawValue can be used directly.
  • Handling complex Codable types: For types that are not yet finalized or are overly complex, consider manually converting them to basic types for storage and using computed properties to simplify read/write operations.
  • Limitations in sorting and filtering: Properties within Codable types can be used for sorting but not for filtering. If certain properties need to serve as filtering criteria, it’s advisable to abstract them into separate entities and associate them via relationships.
  • Optionality of properties and relationships: Try to define properties as optional or provide default values, and it’s recommended to do the same for relationships.
  • Avoid assigning relationship properties in initializers: Since SwiftData uses a pattern of creating objects first and then inserting them into a context, do not assign relationship properties directly in the model’s initializer.

Following these recommendations can effectively reduce compatibility issues caused by future adjustments to your models.

For more related content, please refer to: Considerations for Using Codable and Enums in SwiftData Models and Unveiling the Data Modeling Principles of SwiftData.

Data Synchronization

One of the key reasons many developers choose SwiftData/Core Data is its free integration of iCloud-based backup and data synchronization. Unlike typical network data synchronization solutions, SwiftData’s synchronization mechanism features several distinct characteristics:

Primarily Local Storage, with Cloud Sync as a Supplement

The cloud synchronization feature of SwiftData/Core Data is an add-on built on top of local storage. Specifically, Apple provides an automatic mechanism that converts the data saved in SQLite by SwiftData/Core Data into a format supported by CloudKit. Developers can opt not to use this built-in functionality and instead implement their own cloud synchronization mechanism. In fact, before Apple introduced Core Data with CloudKit, many developers adopted this approach.

CloudKit Is Not a Real-Time Database

The data format conversion is performed asynchronously, and Apple dynamically adjusts the synchronization frequency based on the device’s current conditions (such as network connection, battery level, and user sync settings). Therefore, developers should not equate SwiftData’s cloud synchronization with the performance of a real-time database. In practice, SwiftData’s cloud sync behaves more like the synchronization seen in Apple’s system apps (such as Photos or Notes).

iCloud-Based Authentication: Benefits and Limitations

SwiftData’s cloud sync relies on iCloud’s account authentication, meaning that users do not need to log in separately to sync data across all devices using the same iCloud account. However, this also brings certain limitations: the built-in sync functionality is tightly coupled with a specific iCloud account. If the user switches iCloud accounts, the local data associated with the original account will be cleared and replaced with data from the new account. It is therefore essential for developers to clearly explain this mechanism to avoid misunderstandings.

Special Requirements for Models in Syncing

Because the data formats of CloudKit and SwiftData are not entirely identical, certain guidelines must be followed when creating SwiftData models to ensure local data is compatible with the cloud:

  1. Avoid using uniqueness constraints: Do not use @Attribute(.unique).
  2. Ensure properties are optional or have default values: All properties must either be optional or have default values.
  3. Limitations on deletion rules for relationships: Setting a relationship’s deletion rule to Deny is not supported.
  4. Relationships must be optional: All relationship properties must be defined as optional.

Once you enable SwiftData’s built-in cloud synchronization, any model changes that break lightweight migration compatibility are disallowed. Developers should clarify this from the beginning of a project.

Custom Synchronization Mechanisms

In fact, many of Apple’s system apps have combined SwiftData/Core Data with pure CloudKit API solutions. This approach is more flexible than the built-in sync feature and can accommodate more complex synchronization needs. If you find that SwiftData’s built-in sync does not meet your requirements but still wish to leverage iCloud’s storage and authentication mechanisms, you can implement custom synchronization logic using CKSyncEngine. However, this solution typically applies to specific scenarios, is less universal, and is more challenging to implement.

If your project requires high real-time synchronization or efficiency, you might consider using more efficient third-party sync solutions, using SwiftData solely for local storage—or even choosing frameworks like GRDB or SQLite.swift in place of SwiftData.

Performance

Generally, performance tends to be inversely proportional to the level of abstraction. Since SwiftData provides a higher level of abstraction, its data read/write performance is noticeably inferior compared to frameworks that interact directly with SQLite. In terms of pure data read/write performance, the ranking is: Frameworks that work directly with SQLite > Core Data > SwiftData.

However, when considering factors such as development efficiency, automated memory management, and the intended use cases, SwiftData is more than capable of handling the demands of mainstream mobile and desktop applications given today’s hardware capabilities.

It is also worth noting that SwiftData and Core Data share the same underlying storage format. If SwiftData falls short in meeting performance requirements for a particular scenario, developers can optimize by integrating Core Data for tasks like batch operations or advanced SQL queries that SwiftData does not yet support.

For beginners, it is sufficient to be aware of these optimization paths. Unless you encounter an insurmountable performance bottleneck, there is usually no need to mix SwiftData and Core Data from the outset. Although they share the same data source at the backend, using both frameworks simultaneously poses its own set of challenges.

For more on batch operations and advanced querying, consider reading: Batch Operations in Core Data and Count Queries in Core Data: The Master Guide.

Features

Compared to Core Data, SwiftData currently lacks some functionalities, primarily in the following aspects:

  • Incomplete predicate support: It does not fully support all predicate functionalities available in Core Data. Both SQL-based advanced querying and dynamic predicate merging require higher system versions to be partially implemented.
  • Limited support for batch operations: Full batch operations (such as batch reads and updates) are not yet supported.
  • Cloud synchronization limitations: It does not yet support features like public databases or data sharing across different iCloud accounts.
  • Partial sync support is lacking: It does not support partial synchronization of entity data (wherein the same entity model’s data can be stored in different persistent stores in Core Data).

Additionally, given SwiftData’s design goals, many of the fine-grained controls over data objects and contexts available in Core Data may never be supported in SwiftData.

Nevertheless, the current version of SwiftData is capable of meeting the demands of most application scenarios.

Stability

Although SwiftData has only released two major versions so far, Apple made significant internal adjustments in the second version (iOS 18), some of which were even disruptive. This has occasionally resulted in SwiftData being less stable on iOS 18 compared to iOS 17. Besides uncontrollable factors such as system bugs, the following recommendations can help improve the stability of your SwiftData projects:

  • Prefer using the @ModelActor macro: In SwiftData, only PersistentIdentifier and ModelContainer conform to the Sendable protocol. Therefore, it is recommended to use the @ModelActor macro to implement concurrent operations.
  • Avoid assigning relationship properties in initializers: Since SwiftData creates objects first and then explicitly inserts them into a context, do not assign relationship properties in the model’s initializer.
  • Abstract complex properties: If a property is of a Codable type and needs to be used for sorting or filtering, consider abstracting it into a separate entity and linking it via relationships.
  • View refresh issues: In iOS 18, updates to data using @ModelActor do not automatically refresh the view (although deletion and addition operations are not affected). For data updates that need to be reflected immediately, consider performing them within the view’s context.

Currently, the most timely channel for getting feedback and reporting issues with SwiftData is Apple’s official developer forums. It is advisable to subscribe to SwiftData-related content to be informed of bugs and corresponding solutions as soon as they emerge.

Plan Before Acting

The seemingly “easy-to-learn and easy-to-use” nature of SwiftData can easily mislead beginners into hastily adopting it in production environments. However, such rushed decisions often lead to unforeseen consequences: only when compatibility issues or stability crises emerge do developers realize their understanding of the framework is far from adequate. This “jump on the bandwagon” approach is particularly dangerous when selecting foundational infrastructure like data persistence solutions.

For core frameworks like SwiftData, the wise approach is to follow the principle of “plan before acting”—thoroughly understand its features and limitations, weigh the pros and cons, and validate its feasibility through small-scale experiments before making final technical decisions. This cautious attitude is especially important today, with AI-assisted programming tools rapidly spreading and code generation efficiency dramatically increasing. After all, technical debt can accumulate much faster than we anticipate.

As one of the most critical data persistence solutions in the Apple ecosystem for the next decade, SwiftData undoubtedly deserves developers’ time and energy for in-depth study. It not only represents Apple’s latest thinking on data handling, but its evolutionary direction will continue to influence development paradigms across the entire ecosystem.

Should you choose SwiftData or alternative solutions? The answer doesn’t depend entirely on the technology itself, but rather on your clear understanding of project requirements and team capabilities. I hope this article provides valuable reference for your decision-making process, helping you avoid unnecessary detours on your journey of technology selection.

Weekly Swift & SwiftUI highlights!