This gives you the benefits of “single-table inheritance” for SQLite tables without the pain of actual class inheritance! 😂 It lets you fully use Swift's powerful domain modeling tools for you database, such as structs and enums.
Posts by Brandon Williams
Our most recent library, SQLiteData, embodies this principle.
It pre-supposes: what if I don't want to massively refactor my codebase just because at some point I need to write a bit of code outside of a SwiftUI view 🤪
github.com/pointfreeco/...
Tired of the “To MVVM or not to MVVM” discussions that plague our community? Us too 😅
That’s why we build tools that work in SwiftUI views, @Observable models, UIKit, AppKit, Linux, and more! You should build your app in the way that makes the most sense for you and your team.
It’s finally here: SQLiteData 1.0: a fast, lightweight replacement for SwiftData with full CloudKit support.
www.pointfree.co/blog/posts/1...
SwiftData offers a promise of simple persistence with easy synchronization. We feel it currently falls short of that goal, but in the fullness of time it may achieve it.
But, for the present, we are working on tools that achieve SwiftData's promise, and honestly a lot more.
Next week: We are hosting a live stream where we will preview some fantastic new features coming to our SQLite persistence library, including CloudKit sync and sharing, and answer your questions!
Submit your questions *today* 👇
www.pointfree.co/blog/posts/1...
That's right. You can have the best of both worlds. You get unfettered access to SQLite, a fantastic technology that has stood the test of time, while behind the scenes every change is synchronized across devices using CloudKit.
Seems almost too good to be true!
A partially obscured screenshot giving a sneak peek of how our SharingGRDB library will eventually work with CloudKit for cloud syncing.
We've had a major breakthrough in the most requested feature of our SwiftData alternative: SharingGRDB.
More details coming soon... 👀
We finish the lists feature of our Apple's Reminders app rebuild. We introduce advanced queries including counts and custom data types. And we show how “drafts” allow us to create and update lists using the same view while keeping the domain as precise as possible.
www.pointfree.co/episodes/ep3...
Announcing a toolkit that provides a SwiftData-like experience to SQLite: SharingGRDB with StructuredQueries.
www.pointfree.co/blog/posts/1...
Don't miss out on our exciting series that designs this SQL building library from scratch! We explore many advanced topics of Swift along the way, as well as a crash course in SQL and databases!
www.pointfree.co/collections/...
This query is traversing a recursive one-to-many relationship in SQL (Category belongs to parent Category) to print out the hierarchy of categories.
Why do this work in application code when SQL can knock it out efficiently in just a few lines??
We add sorting to our SQL builder, which will give us a powerful, succinct syntax for controlling the order of results. We will start small but build up to parameter packs and even a custom result builder to get the most flexibility out of our API.
www.pointfree.co/episodes/ep3...
Even in a Swift query builder scares you, there is nothing stopping you from writing raw SQL with a static description of your database schema!
A code snippet showing how the #sql macro can specify a query: @SharedReader( .fetchAll( #sql( """ SELECT count(\(Reminder.id)), \(RemindersList.columns) FROM \(RemindersList.self) JOIN \(Reminder.self) ON \(Reminder.remindersListID) = \(RemindersList.id) WHERE NOT \(Reminder.isCompleted) GROUP BY \(RemindersList.id) """, as: ReminderListState.self ), animation: .default ) ) private var remindersLists
A code snippet showing a type-safe query builder: @SharedReader( .fetchAll( RemindersList.group(by: \.id) .join(Reminder.incomplete) { $0.id.eq($1.remindersListID) } .select { ReminderListState.Columns(reminderCount: $1.count(), remindersList: $0) }, animation: .default ) ) private var remindersLists
Some have expressed skepticism over using our upcoming query builder vs. writing raw SQL. Well our library lets you pick your poison!
Both options are schema-safe and safe from SQL injection, but one is further type-safe and guaranteed to generate valid SQL:
We now have a type-safe syntax for generating SELECT statements using key paths, but we can do better. Let’s introduce a more advanced syntax that leverages variadic generics and supports more complex query expressions.
www.pointfree.co/episodes/ep3...
While building our SQL building library we have created a state-of-the-art testing tool that allows us to simultaneously snapshot the SQL generated by the library, and the results fetched from the database.
Screenshot of Xcode showing off the reusability aspects of our new query building library. The code reads as follows: @Table struct SyncUp { let id: Int var duration: Int var title: String static let withAttendeeCount = SyncUp .group(by: \.id) .join(Attendee.all()) { $0.id == $1.syncUpID } .select { ($0, $1.id.count()) } } @Table struct Attendee { let id: Int var name: String var syncUpID: Int static let withSyncUp = join(SyncUp.all()) { $0.syncUpID == $1.id } } let blobAttendees = Attendee.withSyncUp .where { attendees, _ in attendees.name.collate(.nocase).like("%blob%") } let morningSyncUps = SyncUp.withAttendeeCount .where { syncUps, _ in syncUps.title.collate(.nocase).like("%blob%") }
Imagine a SQL building library for which it is easy to define query fragment helpers that can be pieced together in all kinds of interesting ways.
That is exactly what we are building, and this week we give a sneak peek of the library: www.pointfree.co/episodes/ep3...
Screenshot of code showing how an optional ID is forced when dealing with auto incrementing primary keys in SQLite. The code reads as follows: struct Reminder: Identifiable { // 😒 Mutable, optional ID necessary since // SQLite determines the ID. var id: Int? var title: String var isCompleted = false } let reminder1 = Reminder(title: "Get groceries") let reminder2 = Reminder(title: "Get haircut") reminder1.id == reminder2.id // ⚠️ Unexpected!
A screenshot of code showing how our @Table macro fixes this problem by generating a dedicated Draft type that does not have an ID. That allows our model to have an immutable, non-optional ID. The code reads as follows: @Table struct Reminder: Identifiable { // 😃 Can use immutable, non-optional ID because the // inner Draft type represents an unsaved record. let id: Int var title: String var isCompleted = false } let draft1 = Reminder.Draft(title: "Get groceries") let draft2 = Reminder.Draft(title: "Get haircut") try Reminder.insert( Reminder.Draft(title: "Get groceries") ) .returning(\.self) /* INSERT INTO "reminders" ("title") VALUES ('Get groceries') RETURNING "id", "title", "isCompleted" */
A tricky part to domain modeling with SQLite is how to handle auto incrementing primary keys. You are forced to use optional IDs, but that leaks complexity throughout your app and complicates Identifiable conformances.
Our @Table macro handily fixes this by generating a Draft type with no ID.
Screenshot of code showing how to use Swift 6.1's meta type key paths to chain together static helpers. Code reads as follows: @Table struct SyncUp { var id: Int var isDeleted = false var duration: Int var title: String static let notDeleted = Self.where { !$0.isDeleted } static let shortDuration = Self.where { $0.duration <= 60 * 5 } } // 🤯 Swift 6.1 metatype key paths and dynamic member lookup // make this possible even though 'notDeleted' and 'shortDuration' // are both static properties: let query = SyncUp .notDeleted .shortDuration /* SELECT "syncUps"."id", "syncUps"."duration", "syncUps"."isDeleted", "syncUps"."title" FROM "syncUps" WHERE NOT "syncUps"."isDeleted" AND "syncUps"."duration" <= 300 */
Swift 6.1 brings meta type key paths to the language, and that unlocks a new level of composability in our SQL building library. You can chain together multiple static helpers to build complex queries.
Watch us do this live in this week's episode:
👉 www.pointfree.co/episodes/ep3...
This is an incredibly powerful library that simply would not have been possible to build in Swift from a year ago. We are very excited to dive deep into this in our next series of episodes!
Watch us live code a complex query to fetch all reminders that are high priority, or flagged, or has the tag "#kids" associated with it. This requires a complex subquery that is not easily accomplishable in SwiftData.
Get all the details here: www.pointfree.co/clips/105827...
Our livestream is now up!
• We discuss advanced uses of our Sharing library: Firebase and Wasm
• We release a brand new library: SharingGRDB, an alternative to SwiftData
• We preview a new library: StructuredQueries
• And we answer dozens of viewer questions!
👉 www.pointfree.co/episodes/ep3...
In today's livestream we announced a brand new open source library: SharingGRDB.
It's an alternative to SwiftData that gives you direct access to SQLite, works in UIKit, @Observable models and SwiftUI views.
Oh, and it back deploys to iOS 13 😲
www.pointfree.co/blog/posts/1...
Reminder that we are going live in a few hours! We'll be discussing:
🤯 Advanced topics of our new Sharing library
🎉 Announcing a brand new open source library
👀 Sneak peek at our next episode series
🙋 Questions from our viewers
www.pointfree.co/live
Screenshot of code showing how to group multiple SQL statements in a single transaction and power a SwiftUI view with that state. The code reads as follows: struct FactsView: View { @SharedReader(.fetch(Facts(ordering: .savedAt))) var facts = Facts.Value() var body: some View { List { Section { Text("Unarchived facts: \(facts.unarchivedFactsCount)") Text("Archived facts: \(facts.archivedFactsCount)") } ForEach(facts.favoriteFacts) { fact in Text(fact.value) } } } } struct Facts: FetchKeyRequest { struct State { var archivedFactsCount = 0 var favoriteFacts: [Fact] = [] var unarchivedFactsCount = 0 } let ordering: Ordering func fetch(_ db: Database) throws -> State { let archived = Fact.filter(Column("isArchived")) let unarchived = Fact.filter(!Column("isArchived")) return try State( archivedFactsCount: archived.fetchCount(db), favoriteFacts: unarchived.order(ordering.orderingTerm).fetchAll(db), unarchivedFactsCount: unarchived.fetchCount(db) ) } }
Our tools make it possible to execute multiple SQL queries in a single DB transaction, and then use that data directly in a SwiftUI view. This is more efficient and makes it possible to group related state together.
Screenshot of our website showing all of the episodes in our Sharing with SQLite series. The text on the page reads as follows: Sharing with SQLite Section • 4 episodes • 2 hr 38 min Our Swift Sharing library allows one to hold onto state in feature code that is secretly powered by an external storage system, such as user defaults and the file system. But it is also flexible enough to be powered by SQLite too, and when done properly a lot of amazing powers are unlocked. Core lessons Sharing with SQLite: The Problems 45 min Sharing with SQLite: The Solution 45 min Sharing with SQLite: Advanced Queries 27 min Sharing with SQLite: Dynamic Queries 39 min
This week we finished an amazing series showing how to build a SwiftData alternative using simple SQLite. And the best part? All of these tools work going all the way back to iOS 13! 🤯
👉 www.pointfree.co/collections/...
We now have several features using SQLite via a simple property wrapper that offers the same ergonomics as SwiftData’s @Query macro, and it automatically keeps the view in sync with the database.
Let's now see how to handle dynamic queries!
👉 www.pointfree.co/episodes/ep3...
Mark your calendars! Point-Free is hosting a live stream on Feb 14, 9am PST / 5pm GMT.
We'll discuss our Sharing library as a modern persistence toolkit, a sneak peek at a new library we are working on, and we will take questions from our viewers!
The Q&A is already open: www.pointfree.co/live
Screenshot of a part of our blog post that reads: Point-Free turns 7! 🥳 Wednesday January 29, 2025 It’s hard to believe, but we launched Point-Free 7 years ago today! In that time we have released dozens of open source projects that collectively are cloned hundreds of thousands of times a week, responded to thousands of comments and discussions, and explored a wide breadth of topics. Everything from mathematical topics (algebraic data types, existentials, foundations of equality) to practical, real world topics (SQLite, parsers, architecture, cross-platform Swift), and everything in between.
7 years ago today we launched Point-Free!
What have been some of your favorite episodes, open source libraries, or moments since we launched?
www.pointfree.co/blog/posts/1...