Today we are releasing SQLiteData 1.0, an alternative to SwiftData that provides familiar tool to build apps with complex persistence and query needs, with none of the gotchas, based on SQLite:
Model your data types as concisely as possible using everything Swift has to offer, such as structs and enums.
Perform type-safe and schema-safe queries to fetch your data in anyway you want.
Decode data from the database using a performant, custom SQLite decoder that is only a tiny bit slower than dealing directly with SQLite’s C functions.
Leverage property wrappers similar to SwiftData’s
@Query
that allow you to fetch data in SwiftUI views so that when the database changes the view automatically refreshes. These property wrappers even work outside of SwiftUI views, such as in@Observable
models and even UIKit view controllers.Direct support for CloudKit synchronization so that your users’ data is distributed across all of their devices.
Support for iCloud sharing, which allows your users to share a record (and all of its associated data) with another iCloud user.
Powered by SQLite, a battle tested technology that is over 25 years old and one of the most widely deployed pieces of software in history.
It accomplishes all of this (and more) in a lightweight and ergonomic API that is fully documented and comes with a wide variety of demo apps and case studies.
SQLiteData allows you to model your domain types as concisely as possible, using all of the amazing tools that Swift gives us. This means you can use structs instead of classes, raw representable enums to model choices, and immutable let
s for identifiers that should not change after instantiation:
import SQLiteData
@Table
struct Reminder: Identifiable {
let id: UUID
var title = ""
var isCompleted = false
var priority: Priority?
enum Priority: Int {
case low, medium, high
}
}
After a little bit of work to prepare your SQLite database, you can immediately start fetching this data from the database using the @FetchAll
property wrapper:
struct RemindersView: View {
@FetchAll var reminders: [Reminder]
var body: some View {
ForEach(reminders) { reminder in
Text(reminder.title)
}
}
}
Even better, you can describe the SQL query that powers this data directly inline with the property wrapper. For example, to sort incomplete reminders above completed ones you can do:
@FetchAll(Reminder.order(by: \.isCompleted))
var reminders
Or to fetch only high-priority reminders:
@FetchAll(Reminder.where { $0.priority.eq(Priority.high) })
var reminders
This barely scratches the surface of the kinds of queries you can build. Our library supports SQL joins, aggregates, common table expressions, and more. See the full docs of our StructuredQueries for more information on writing type-safe and schema-safe queries.
SQLiteData comes with many, many example projects to show you the best practices of getting started, and how to solve the most common problems one comes across in apps with complex persistence and querying needs:
Case Studies
Demonstrates how to solve some common application problems in a simplified environment, in both SwiftUI and UIKit. This includes animation, dynamic queries, database transactions, and more.CloudKit Demo
A simplified demo that shows how to synchronize a SQLite database to CloudKit and how to share records with other iCloud users. See our dedicated articles on CloudKit Synchronization and CloudKit Sharing for more information.Reminders
A rebuild of Apple’s Reminders app that uses a SQLite database to model the reminders, lists, and tags. It features many advanced queries, such as searching, stats aggregation, and multi-table joins. It also features CloudKit synchronization and sharing.SyncUps
This application is a faithful reconstruction of one of Apple’s more interesting sample projects called Scrumdinger, and it uses SQLite to persist its data for meetings. We have also added CloudKit synchronization so that all changes are automatically made available on all of the user’s devices.
If there are more examples or case studies you would like to see built, start a discussion!
The library comes fully documented with many articles exploring the more nuanced topics of data persistence and querying in complex applications:
Fetching model data
Learn how to use the@FetchAll
,@FetchOne
and@Fetch
property wrappers for performing SQL queries to load data from your database.Observing changes to model data
Learn how to observe changes to your database in SwiftUI views, UIKit view controllers, and more.Preparing a SQLite database
Learn how to create, configure and migrate the SQLite database that holds your application’s data.Dynamic queries
Learn how to load model data based on information that isn’t known at compile time.Comparison with SwiftData
Learn how SQLiteData compares to SwiftData when solving a variety of problems.Getting started with CloudKit
Learn how to seamlessly add CloudKit synchronization to your SQLiteData application.Sharing data with other iCloud users
Learn how to allow your users to share certain records with other iCloud users for collaboration.
If there is anything you feel is lacking from our documentation, please open a discussion and we can figure out how to better document the library!
Give the library a spin today by cloning the repo, opening Examples.xcodeproj and running one of the many example apps. If you have any questions, feel free to start a discussion.