Skip to content

chore: make execute, watch and transaction functions throwable in swift #21

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Feb 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

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

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

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