Skip to content

Conversation

@stevensJourney
Copy link
Contributor

@stevensJourney stevensJourney commented Sep 8, 2025

Overview

This builds off the amazing work of powersync-ja/powersync-kotlin#230. The linked PR adds support for creating a PowerSyncDatabase by supplying a custom SQLiteConnectionPool which manages SQLite connections.

Some small additions and wip tests were added to the Kotlin implementation here

This PR adds Swift bindings to the Kotlin PR and then adds a PowerSyncGRDB Product to our SPM package. This product adapts a GRDB DatabasePool to a SQLiteConnectionPool accepted by the PowerSync SDK.

This makes use of the fact that GRDB exposes the raw C SQLite pointer for it's connections. We then use this pointer with the newly introduced native Database kotlin implementation.

The PowerSyncGRDB product provides a means to generate a GRDB Configuration. This configuration is used when creating the GRDB DatabasePool. We supply implementation for the configuration to load our PowerSync Rust core extension.

APIs are still a WIP
Consumers can create a Pool with:

var config = Configuration()

config.configurePowerSync(
    schema: schema
)

let documentsDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let dbURL = documentsDir.appendingPathComponent("test.sqlite")
let pool = try DatabasePool(
    path: dbURL.path,
    configuration: config
)

Consumers can then pass this pool when creating the PowerSyncDatabase.

let powerSync = openPowerSyncWithGRDB(
    pool: grdb,
    schema: schema,
    identifier: "mydatabase.sqlite"
)

Using the DatabasePool in the PowerSync SDK results in the same locking mechanisms being used between instances of the PowerSyncDatabase and DatabasePool. Consumers should be safe to alternate between both clients.

try await powerSync.execute(
    "INSERT INTO users(id, name, count) VALUES(uuid(), 'steven', 1)"
)

let initialUsers = try await powerSync.getAll(
    "SELECT * FROM users"
) { cursor in
    try cursor.getString(name: "name")
}
print("initial users \(initialUsers)")

// Now use a GRDB query
struct Users: Codable, Identifiable, FetchableRecord, PersistableRecord {
    var id: String
    var name: String
    var count: Int

    enum Columns {
        static let name = Column(CodingKeys.name)
        static let count = Column(CodingKeys.count)
    }
}

let grdbUsers = try await pool.read { db in
    try Users.fetchAll(db)
}

Demo

A new GRDB demo application has been added to the demos folder. This demo uses GRDB queries in Views.

grdbdemo.mp4

TODOs:

  • Implement LeaseAll to lease all GRDB connections if possible
    • Documented that updating the Schema, after init, is not currently supported
  • Update notifications
  • Test latest version of GRDB with support for SQLite view mutations

@stevensJourney stevensJourney changed the title [WIP] GRDB Support GRDB Support Oct 23, 2025
@stevensJourney stevensJourney marked this pull request as ready for review October 31, 2025 11:39
Copy link
Contributor

@simolus3 simolus3 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is really cool 🚀 I only have a few minor comments, the overal architecture for this looks great and I'm happy to see this integration added :)

switch baseResult.blockResult {
case let success as PowerSyncResult.Success:
do {
let casted = try safeCast(success.value, to: ReturnType.self)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that this is synchronous, is it possible to do something like var innerResult: ReturnType, assign to it in block and then return that value instead of casting here? That way we wouldn't have to cast to any and back.

Use resolvePowerSyncLoadableExtensionPath for GRDB integration.
Fix WatchOS tests.
#### Limitations

- Updating the PowerSync schema, with `updateSchema`, is not currently fully supported with GRDB connections.
- This integration requires currently statically linking PowerSync and GRDB.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- This integration requires currently statically linking PowerSync and GRDB.
- This integration currently requires statically linking PowerSync and GRDB.

Co-authored-by: benitav <benitav@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants