Brandon Williams

@mbrandonw.bsky.social

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

A brand new library from Point-Free! DebugSnaphots can instantly make your classes and @Observable models debuggable and testable. Simply apply the macro and get insight into how data changes over time, and write exhaustive tests on your feature's logic. www.pointfree.co/blog/posts/2...

DebugSnapshots: Public beta

DebugSnapshots is now in public beta! After incubating in Point-Free Beta Previews, it is the first library to graduate to the public, bringing exhaustive testing and focused debugging tools to…

pointfree.co

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.

Point-Free@pointfree.co · 10mo ago

It is even possible to group mutually exclusive data into a Swift enum for your SQLite tables! This allows you to mimic what “single-table inheritance” gives you without the pain of reference types and class inheritance.

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

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

github.com

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.

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.

Point-Free@pointfree.co · last yr.

It takes only these few lines of code to immediately unlock CloudKit synchronization to our local-only reminders app. It just works™, and it's only the tip of the iceberg. Check it out for yourself! 👉 github.com/pointfreeco/...

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

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

pointfree.co

We've had a major breakthrough in the most requested feature of our SwiftData alternative: SharingGRDB. More details coming soon... 👀

A partially obscured screenshot giving a sneak peek of how our SharingGRDB library will eventually work with CloudKit for cloud syncing.

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

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

Point-Free@pointfree.co · last yr.

Common Table Expressions (CTEs) are incredibly powerful, and will be fully supported in our upcoming SQL building library. This demonstrates a type-safe way to traverse a tree in a depth-first manner. Our builder is 20% fewer characters than the equivalent SQL, and 100% safer!

A screenshot of Xcode showing a test of a CTE query:

assertQuery(
  With {
    CategoryLevel(id: 1, name: "Point-Free", level: 0).union(
      Category
        .join(CategoryLevel.all()) { $0.parentID.eq($1.id) }
        .select { CategoryLevel.Columns(id: $0.id, name: $0.name, level: $1.level + 1) }
        .order { ($1.level + 1).desc() }
    )
  } query: {
    CategoryLevel.select { String(repeating: ".", count: 12).substr(1, $0.level * 3) + $0.name }
  }
) {
  """
  WITH "categoryLevels" AS (
    SELECT 1 AS "id", 'Point-Free' AS "name", 0 AS "level" 
    UNION 
    SELECT "categories"."id" AS "id", "categories"."name" AS "name", ("categoryLevels"."level" + 1) AS "level" 
    FROM "categories" JOIN "categoryLevels" ON ("categories"."parentID" = "categoryLevels"."id")
    ORDER BY "categoryLevels"."level" + 1 DESC
  ) 
  SELECT substr('............', 1, "categoryLevels"."level" * 3) || "categoryLevels"."name" 
  FROM "categoryLevels"
  """
} results: {
  """
  ┌──────────────────────────────────┐
  │ "Point-Free"                     │
  │ "...Back to basics"              │
  │ "......Equatable and Hashable"   │
  │ "......Generics"                 │
  │ "......Values versus References" │
  │ "...SQLite"                      │
  │ "......Introduction to SQLite"   │
  │ "......Modern Persistence"       │
  │ "......SQL Building"             │
  │ ".........JOIN"                  │
  │ ".........ORDER BY"              │
  │ ".........SELECT"                │
  │ ".........WHERE"                 │
  │ "...SwiftUI"                     │
  │ "......Animations"               │
  │ "......Navigation"               │
  │ "......Observation"              │
  └──────────────────────────────────┘
  """
}

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:

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

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.

Bild

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 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"
*/

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

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

pointfree.co

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

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

pointfree.co

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 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)
    )
  }
}