Skip to content

Commit

Permalink
chore: make execute, watch and transaction functions throwable in swi…
Browse files Browse the repository at this point in the history
…ft (#21)

* chore: make execute and watch throwable in swift

* chore: add transaction and watch error handling

* chore: update package
  • Loading branch information
DominicGBauer authored Feb 13, 2025
1 parent af67832 commit f50f8dd
Show file tree
Hide file tree
Showing 9 changed files with 297 additions and 80 deletions.
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
# Changelog

## 1.0.0-Beta.6

* BREAKING CHANGE: `watch` queries are now throwable and therefore will need to be accompanied by a `try` e.g.

```swift
try database.watch()
```

* BREAKING CHANGE: `transaction` functions are now throwable and therefore will need to be accompanied by a `try` e.g.

```swift
try await database.writeTransaction { transaction in
try transaction.execute(...)
}
```
* Allow `execute` errors to be handled
* `userId` is now set to `nil` by default and therefore it is no longer required to be set to `nil` when instantiating `PowerSyncCredentials` and can therefore be left out.

## 1.0.0-Beta.5

* Implement improvements to errors originating in Kotlin so that they can be handled in Swift
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/powersync-ja/powersync-kotlin.git",
"state" : {
"revision" : "61d195816585f30260181dcbd157bf1660c9ac4e",
"version" : "1.0.0-BETA22.0"
"revision" : "203db74889df8a20e3c6ac38aede6b0186d2e3b5",
"version" : "1.0.0-BETA23.0"
}
},
{
Expand Down
2 changes: 1 addition & 1 deletion Demo/PowerSyncExample/Components/TodoListView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ struct TodoListView: View {
ForEach(todos) { todo in
TodoListRow(todo: todo) {
Task {
await toggleCompletion(of: todo)
try await toggleCompletion(of: todo)
}
}
}
Expand Down
72 changes: 40 additions & 32 deletions Demo/PowerSyncExample/PowerSync/SystemManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,19 +34,23 @@ class SystemManager {
}

func watchLists(_ callback: @escaping (_ lists: [ListContent]) -> Void ) async {
for await lists in self.db.watch<[ListContent]>(
sql: "SELECT * FROM \(LISTS_TABLE)",
parameters: [],
mapper: { cursor in
ListContent(
id: try cursor.getString(name: "id"),
name: try cursor.getString(name: "name"),
createdAt: try cursor.getString(name: "created_at"),
ownerId: try cursor.getString(name: "owner_id")
)
do {
for try await lists in try self.db.watch<ListContent>(
sql: "SELECT * FROM \(LISTS_TABLE)",
parameters: [],
mapper: { cursor in
try ListContent(
id: cursor.getString(name: "id"),
name: cursor.getString(name: "name"),
createdAt: cursor.getString(name: "created_at"),
ownerId: cursor.getString(name: "owner_id")
)
}
) {
callback(lists)
}
) {
callback(lists)
} catch {
print("Error in watch: \(error)")
}
}

Expand All @@ -59,11 +63,11 @@ class SystemManager {

func deleteList(id: String) async throws {
_ = try await db.writeTransaction(callback: { transaction in
_ = transaction.execute(
_ = try transaction.execute(
sql: "DELETE FROM \(LISTS_TABLE) WHERE id = ?",
parameters: [id]
)
_ = transaction.execute(
_ = try transaction.execute(
sql: "DELETE FROM \(TODOS_TABLE) WHERE list_id = ?",
parameters: [id]
)
Expand All @@ -72,24 +76,28 @@ class SystemManager {
}

func watchTodos(_ listId: String, _ callback: @escaping (_ todos: [Todo]) -> Void ) async {
for await todos in self.db.watch(
sql: "SELECT * FROM \(TODOS_TABLE) WHERE list_id = ?",
parameters: [listId],
mapper: { cursor in
return Todo(
id: try cursor.getString(name: "id"),
listId: try cursor.getString(name: "list_id"),
photoId: try cursor.getStringOptional(name: "photo_id"),
description: try cursor.getString(name: "description"),
isComplete: try cursor.getBoolean(name: "completed"),
createdAt: try cursor.getString(name: "created_at"),
completedAt: try cursor.getStringOptional(name: "completed_at"),
createdBy: try cursor.getStringOptional(name: "created_by"),
completedBy: try cursor.getStringOptional(name: "completed_by")
)
do {
for try await todos in try self.db.watch(
sql: "SELECT * FROM \(TODOS_TABLE) WHERE list_id = ?",
parameters: [listId],
mapper: { cursor in
try Todo(
id: cursor.getString(name: "id"),
listId: cursor.getString(name: "list_id"),
photoId: cursor.getStringOptional(name: "photo_id"),
description: cursor.getString(name: "description"),
isComplete: cursor.getBoolean(name: "completed"),
createdAt: cursor.getString(name: "created_at"),
completedAt: cursor.getStringOptional(name: "completed_at"),
createdBy: cursor.getStringOptional(name: "created_by"),
completedBy: cursor.getStringOptional(name: "completed_by")
)
}
) {
callback(todos)
}
) {
callback(todos)
} catch {
print("Error in watch: \(error)")
}
}

Expand Down Expand Up @@ -117,7 +125,7 @@ class SystemManager {

func deleteTodo(id: String) async throws {
_ = try await db.writeTransaction(callback: { transaction in
transaction.execute(
try transaction.execute(
sql: "DELETE FROM \(TODOS_TABLE) WHERE id = ?",
parameters: [id]
)
Expand Down
4 changes: 2 additions & 2 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/powersync-ja/powersync-kotlin.git",
"state" : {
"revision" : "61d195816585f30260181dcbd157bf1660c9ac4e",
"version" : "1.0.0-BETA22.0"
"revision" : "203db74889df8a20e3c6ac38aede6b0186d2e3b5",
"version" : "1.0.0-BETA23.0"
}
},
{
Expand Down
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ let package = Package(
targets: ["PowerSync"]),
],
dependencies: [
.package(url: "https://github.com/powersync-ja/powersync-kotlin.git", exact: "1.0.0-BETA22.0"),
.package(url: "https://github.com/powersync-ja/powersync-kotlin.git", exact: "1.0.0-BETA23.0"),
.package(url: "https://github.com/powersync-ja/powersync-sqlite-core-swift.git", "0.3.9"..<"0.4.0")
],
targets: [
Expand Down
85 changes: 56 additions & 29 deletions Sources/PowerSync/Kotlin/KotlinPowerSyncDatabaseImpl.swift
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ final class KotlinPowerSyncDatabaseImpl: PowerSyncDatabaseProtocol {
mapper: mapper
) as! RowType
}

func get<RowType>(
sql: String,
parameters: [Any]?,
Expand All @@ -93,7 +93,7 @@ final class KotlinPowerSyncDatabaseImpl: PowerSyncDatabaseProtocol {
}
) as! RowType
}

func getAll<RowType>(
sql: String,
parameters: [Any]?,
Expand All @@ -105,7 +105,7 @@ final class KotlinPowerSyncDatabaseImpl: PowerSyncDatabaseProtocol {
mapper: mapper
) as! [RowType]
}

func getAll<RowType>(
sql: String,
parameters: [Any]?,
Expand All @@ -131,7 +131,7 @@ final class KotlinPowerSyncDatabaseImpl: PowerSyncDatabaseProtocol {
mapper: mapper
) as! RowType?
}

func getOptional<RowType>(
sql: String,
parameters: [Any]?,
Expand All @@ -150,48 +150,75 @@ final class KotlinPowerSyncDatabaseImpl: PowerSyncDatabaseProtocol {
sql: String,
parameters: [Any]?,
mapper: @escaping (SqlCursor) -> RowType
) -> AsyncStream<[RowType]> {
AsyncStream { continuation in
) throws -> AsyncThrowingStream<[RowType], Error> {
AsyncThrowingStream { continuation in
Task {
for await values in try self.kotlinDatabase.watch(
sql: sql,
parameters: parameters,
mapper: mapper
) {
continuation.yield(values as! [RowType])
do {
for await values in try self.kotlinDatabase.watch(
sql: sql,
parameters: parameters,
mapper: mapper
) {
continuation.yield(values as! [RowType])
}
continuation.finish()
} catch {
continuation.finish(throwing: error)
}
continuation.finish()
}
}
}

func watch<RowType>(
sql: String,
parameters: [Any]?,
mapper: @escaping (SqlCursor) throws -> RowType
) -> AsyncStream<[RowType]> {
AsyncStream { continuation in
) throws -> AsyncThrowingStream<[RowType], Error> {
AsyncThrowingStream { continuation in
Task {
for await values in try self.kotlinDatabase.watch(
sql: sql,
parameters: parameters,
mapper: { cursor in
try! mapper(cursor)
do {
for await values in try self.kotlinDatabase.watch(
sql: sql,
parameters: parameters,
mapper: { cursor in
try! mapper(cursor)
}
) {
continuation.yield(values as! [RowType])
}
) {
continuation.yield(values as! [RowType])
continuation.finish()
} catch {
continuation.finish(throwing: error)
}
continuation.finish()
}
}
}

public func writeTransaction<R>(callback: @escaping (any PowerSyncTransaction) -> R) async throws -> R {
return try await kotlinDatabase.writeTransaction(callback: callback) as! R

public func writeTransaction<R>(callback: @escaping (any PowerSyncTransaction) throws -> R) async throws -> R {
return try await kotlinDatabase.writeTransaction(callback: TransactionCallback(callback: callback)) as! R
}

public func readTransaction<R>(callback: @escaping (any PowerSyncTransaction) throws -> R) async throws -> R {
return try await kotlinDatabase.readTransaction(callback: TransactionCallback(callback: callback)) as! R
}
}

class TransactionCallback<R>: PowerSyncKotlin.ThrowableTransactionCallback {
let callback: (PowerSyncTransaction) throws -> R

public func readTransaction<R>(callback: @escaping (any PowerSyncTransaction) -> R) async throws -> R {
return try await kotlinDatabase.readTransaction(callback: callback) as! R
init(callback: @escaping (PowerSyncTransaction) throws -> R) {
self.callback = callback
}

func execute(transaction: PowerSyncKotlin.PowerSyncTransaction) throws -> Any{
do {
return try callback(transaction)
} catch let error {
return PowerSyncKotlin.PowerSyncException(
message: error.localizedDescription,
cause: PowerSyncKotlin.KotlinThrowable(message: error.localizedDescription)
)
}
}
}

Expand Down
12 changes: 6 additions & 6 deletions Sources/PowerSync/QueriesProtocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -59,21 +59,21 @@ public protocol Queries {
sql: String,
parameters: [Any]?,
mapper: @escaping (SqlCursor) -> RowType
) -> AsyncStream<[RowType]>
) throws -> AsyncThrowingStream<[RowType], Error>

/// Execute a read-only (SELECT) query every time the source tables are modified
/// and return the results as an array in a Publisher.
func watch<RowType>(
sql: String,
parameters: [Any]?,
mapper: @escaping (SqlCursor) throws -> RowType
) -> AsyncStream<[RowType]>
) throws -> AsyncThrowingStream<[RowType], Error>

/// Execute a write transaction with the given callback
func writeTransaction<R>(callback: @escaping (any PowerSyncTransaction) -> R) async throws -> R
func writeTransaction<R>(callback: @escaping (any PowerSyncTransaction) throws -> R) async throws -> R

/// Execute a read transaction with the given callback
func readTransaction<R>(callback: @escaping (any PowerSyncTransaction) -> R) async throws -> R
func readTransaction<R>(callback: @escaping (any PowerSyncTransaction) throws -> R) async throws -> R
}

extension Queries {
Expand Down Expand Up @@ -105,7 +105,7 @@ extension Queries {
public func watch<RowType>(
_ sql: String,
mapper: @escaping (SqlCursor) -> RowType
) -> AsyncStream<[RowType]> {
return watch(sql: sql, parameters: [], mapper: mapper)
) throws -> AsyncThrowingStream<[RowType], Error> {
return try watch(sql: sql, parameters: [], mapper: mapper)
}
}
Loading

0 comments on commit f50f8dd

Please sign in to comment.