Brandon Williams's Avatar

Brandon Williams

@mbrandonw.bsky.social

Subterranean homesick mathematician. Co-host of @pointfree.co. https://www.fewbutripe.com

479 Followers  |  140 Following  |  23 Posts  |  Joined: 16.11.2024  |  2.9566

Latest posts by mbrandonw.bsky.social on Bluesky

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.

05.08.2025 17:20 β€” πŸ‘ 6    πŸ” 1    πŸ’¬ 0    πŸ“Œ 0
Preview
Upcoming live stream: A vision for modern persistence We are hosting a live stream on June 25th to unveil our vision for modern persistence. Learn how to seamlessly synchronize your app’s data across many devices, including sharing data with other iCloud...

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...

17.06.2025 18:33 β€” πŸ‘ 6    πŸ” 3    πŸ’¬ 0    πŸ“Œ 2

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!

22.05.2025 14:32 β€” πŸ‘ 19    πŸ” 3    πŸ’¬ 2    πŸ“Œ 0
A partially obscured screenshot giving a sneak peek of how our SharingGRDB library will eventually work with CloudKit for cloud syncing.

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... πŸ‘€

21.05.2025 16:07 β€” πŸ‘ 17    πŸ” 3    πŸ’¬ 1    πŸ“Œ 1
Video thumbnail

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...

19.05.2025 16:36 β€” πŸ‘ 2    πŸ” 1    πŸ’¬ 0    πŸ“Œ 0
Preview
A fast, lightweight replacement for SwiftData Replace SwiftData with a fast, ergonomic and lightweight suite of tools powered by SQL. It provides APIs similar to @Model, @Query and #Predicate, but is tuned for direct access to the underlying data...

Announcing a toolkit that provides a SwiftData-like experience to SQLite: SharingGRDB with StructuredQueries.

www.pointfree.co/blog/posts/1...

22.04.2025 17:55 β€” πŸ‘ 11    πŸ” 5    πŸ’¬ 0    πŸ“Œ 0
Preview
SQLite β€Ί SQL Building SQL is one of the most powerful and concise programming languages ever invented, but it is usually written as a raw string in some other language, such as Swift, Javascript, Ruby, and so on. In this s...

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/...

26.03.2025 16:52 β€” πŸ‘ 2    πŸ” 1    πŸ’¬ 1    πŸ“Œ 0

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??

26.03.2025 18:48 β€” πŸ‘ 1    πŸ” 1    πŸ’¬ 0    πŸ“Œ 0
Video thumbnail

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...

24.03.2025 18:10 β€” πŸ‘ 4    πŸ” 1    πŸ’¬ 0    πŸ“Œ 0

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!

20.03.2025 14:11 β€” πŸ‘ 4    πŸ” 1    πŸ’¬ 0    πŸ“Œ 0
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 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

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:

19.03.2025 19:21 β€” πŸ‘ 11    πŸ” 1    πŸ’¬ 0    πŸ“Œ 1
Video thumbnail

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...

17.03.2025 15:28 β€” πŸ‘ 4    πŸ” 2    πŸ’¬ 0    πŸ“Œ 0
Post image

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.

13.03.2025 16:32 β€” πŸ‘ 4    πŸ” 1    πŸ’¬ 0    πŸ“Œ 1
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%")
  }

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...

04.03.2025 17:11 β€” πŸ‘ 15    πŸ” 2    πŸ’¬ 1    πŸ“Œ 0
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!

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 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.

06.03.2025 17:39 β€” πŸ‘ 9    πŸ” 1    πŸ’¬ 2    πŸ“Œ 0
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
*/

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...

05.03.2025 17:06 β€” πŸ‘ 9    πŸ” 2    πŸ’¬ 0    πŸ“Œ 0

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!

20.02.2025 19:57 β€” πŸ‘ 10    πŸ” 2    πŸ’¬ 0    πŸ“Œ 0
Preview
Powering state with a complex SQL query Watch us live write a complex SQL query to load "really important reminders", and seamlessly integrate that state in our app. The database will automatically be observed for changes so that we can re-...

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...

19.02.2025 16:47 β€” πŸ‘ 7    πŸ” 3    πŸ’¬ 0    πŸ“Œ 0
Video thumbnail

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...

17.02.2025 17:28 β€” πŸ‘ 5    πŸ” 1    πŸ’¬ 0    πŸ“Œ 0
Preview
SharingGRDB: A SwiftData Alternative We are excited to announce a new open source library that can serve as a SwiftData alternative for many types of apps out there today. It provides tools that work in SwiftUI views, @Observable models,...

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...

14.02.2025 19:23 β€” πŸ‘ 10    πŸ” 3    πŸ’¬ 0    πŸ“Œ 0
Preview
πŸ”΄ Point-Free Live Point-Free Live is a periodic livestream where we discuss topics from episodes, explore our open source libraries, and take questions from our viewers.

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

14.02.2025 14:48 β€” πŸ‘ 3    πŸ” 1    πŸ’¬ 0    πŸ“Œ 1
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)
    )
  }
}

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.

05.02.2025 17:03 β€” πŸ‘ 9    πŸ” 1    πŸ’¬ 1    πŸ“Œ 0
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

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/...

04.02.2025 15:53 β€” πŸ‘ 9    πŸ” 3    πŸ’¬ 0    πŸ“Œ 1
Video thumbnail

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...

03.02.2025 16:51 β€” πŸ‘ 4    πŸ” 1    πŸ’¬ 0    πŸ“Œ 0
Preview
πŸ”΄ Point-Free Live Point-Free Live is a periodic livestream where we discuss topics from episodes, explore our open source libraries, and take questions from our viewers.

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

31.01.2025 15:36 β€” πŸ‘ 6    πŸ” 1    πŸ’¬ 0    πŸ“Œ 0
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.

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...

29.01.2025 19:39 β€” πŸ‘ 17    πŸ” 3    πŸ’¬ 9    πŸ“Œ 0
Preview
Episode #311: Sharing with SQLite: Advanced Queries Let’s leverage our new @Shared SQLite strategy by adding a brand new feature: archiving. We will see how easy it is to incorporate queries directly into a SwiftUI view, and we will expand our tools to...

Learn about these advanced techniques in this week's episode: www.pointfree.co/episodes/ep3...

29.01.2025 15:43 β€” πŸ‘ 2    πŸ” 1    πŸ’¬ 0    πŸ“Œ 0

SQL is a powerful language and it's a bummer to put any abstraction over it that prevents access to all of its features.

We are building tools that allow you to embrace a bit of abstraction where needed, but you always have access to all of SQL.

29.01.2025 16:51 β€” πŸ‘ 5    πŸ” 2    πŸ’¬ 0    πŸ“Œ 0
A screenshot of code showing how to use the tools of Swift Sharing library to power a feature's state from a SQLite query. The code reads as follows:

@Observable
@MainActor
class FactFeatureModel {
  //@Shared(.fileStorage(.factsURL)))
  //var favoriteFacts: [Fact] = []

  @SharedReader(.fetchAll("SELECT * FROM facts"))
  var favoriteFacts: [Fact] = []

  // ...
}

A screenshot of code showing how to use the tools of Swift Sharing library to power a feature's state from a SQLite query. The code reads as follows: @Observable @MainActor class FactFeatureModel { //@Shared(.fileStorage(.factsURL))) //var favoriteFacts: [Fact] = [] @SharedReader(.fetchAll("SELECT * FROM facts")) var favoriteFacts: [Fact] = [] // ... }

With just a bit of work you can switch from using JSON to persist your app's state to using a powerful SQLite database. Queries will define how your feature's access state, and they will automatically update if the database changes.

πŸ‘‰ www.pointfree.co/episodes/ep3...

21.01.2025 17:33 β€” πŸ‘ 3    πŸ” 1    πŸ’¬ 0    πŸ“Œ 0

Some say that trying to use Swift Data outside of a view (such as an @Observable model) is "fighting the framework".

But the fact that @Query does not work outside of views is only a limitation of Swift Data, and an unneeded one. We build a tool similar to @Query, but it can be used anywhere!

20.01.2025 19:29 β€” πŸ‘ 12    πŸ” 2    πŸ’¬ 0    πŸ“Œ 0

@mbrandonw is following 20 prominent accounts