Advertisement · 728 × 90

Posts by Brandon Williams

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.

6 months ago 8 1 0 0
Preview
GitHub - pointfreeco/sqlite-data: A fast, lightweight replacement for SwiftData, powered by SQL and supporting CloudKit synchronization. A fast, lightweight replacement for SwiftData, powered by SQL and supporting CloudKit synchronization. - pointfreeco/sqlite-data

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

6 months ago 3 1 0 0

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.

6 months ago 9 2 1 0
Preview
SQLiteData 1.0: An alternative to SwiftData with CloudKit sync and sharing Announcing SQLiteData 1.0: A fast, lightweight alternative to SwiftData with full support for CloudKit synchronization and data sharing.

It’s finally here: SQLiteData 1.0: a fast, lightweight replacement for SwiftData with full CloudKit support.
www.pointfree.co/blog/posts/1...

7 months ago 28 6 0 0

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.

8 months ago 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...

10 months ago 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!

11 months ago 18 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... 👀

11 months ago 17 3 1 1
Video

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

11 months ago 2 1 0 0
Advertisement
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...

1 year ago 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/...

1 year ago 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??

1 year ago 1 1 0 0
Video

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

1 year ago 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!

1 year ago 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:

1 year ago 11 1 0 1
Video

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

1 year ago 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.

1 year ago 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...

1 year ago 15 2 1 0
Advertisement
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.

1 year ago 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...

1 year ago 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!

1 year ago 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...

1 year ago 7 3 0 0
Video

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

1 year ago 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...

1 year ago 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

1 year ago 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.

1 year ago 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/...

1 year ago 9 3 0 1
Video

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

1 year ago 4 1 0 0
Advertisement
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

1 year ago 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...

1 year ago 17 3 9 0