Skip to content

Commit f50f8dd

Browse files
chore: make execute, watch and transaction functions throwable in swift (#21)
* chore: make execute and watch throwable in swift * chore: add transaction and watch error handling * chore: update package
1 parent af67832 commit f50f8dd

File tree

9 files changed

+297
-80
lines changed

9 files changed

+297
-80
lines changed

CHANGELOG.md

+18
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,23 @@
11
# Changelog
22

3+
## 1.0.0-Beta.6
4+
5+
* BREAKING CHANGE: `watch` queries are now throwable and therefore will need to be accompanied by a `try` e.g.
6+
7+
```swift
8+
try database.watch()
9+
```
10+
11+
* BREAKING CHANGE: `transaction` functions are now throwable and therefore will need to be accompanied by a `try` e.g.
12+
13+
```swift
14+
try await database.writeTransaction { transaction in
15+
try transaction.execute(...)
16+
}
17+
```
18+
* Allow `execute` errors to be handled
19+
* `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.
20+
321
## 1.0.0-Beta.5
422

523
* Implement improvements to errors originating in Kotlin so that they can be handled in Swift

Demo/PowerSyncExample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515
"kind" : "remoteSourceControl",
1616
"location" : "https://github.com/powersync-ja/powersync-kotlin.git",
1717
"state" : {
18-
"revision" : "61d195816585f30260181dcbd157bf1660c9ac4e",
19-
"version" : "1.0.0-BETA22.0"
18+
"revision" : "203db74889df8a20e3c6ac38aede6b0186d2e3b5",
19+
"version" : "1.0.0-BETA23.0"
2020
}
2121
},
2222
{

Demo/PowerSyncExample/Components/TodoListView.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ struct TodoListView: View {
2828
ForEach(todos) { todo in
2929
TodoListRow(todo: todo) {
3030
Task {
31-
await toggleCompletion(of: todo)
31+
try await toggleCompletion(of: todo)
3232
}
3333
}
3434
}

Demo/PowerSyncExample/PowerSync/SystemManager.swift

+40-32
Original file line numberDiff line numberDiff line change
@@ -34,19 +34,23 @@ class SystemManager {
3434
}
3535

3636
func watchLists(_ callback: @escaping (_ lists: [ListContent]) -> Void ) async {
37-
for await lists in self.db.watch<[ListContent]>(
38-
sql: "SELECT * FROM \(LISTS_TABLE)",
39-
parameters: [],
40-
mapper: { cursor in
41-
ListContent(
42-
id: try cursor.getString(name: "id"),
43-
name: try cursor.getString(name: "name"),
44-
createdAt: try cursor.getString(name: "created_at"),
45-
ownerId: try cursor.getString(name: "owner_id")
46-
)
37+
do {
38+
for try await lists in try self.db.watch<ListContent>(
39+
sql: "SELECT * FROM \(LISTS_TABLE)",
40+
parameters: [],
41+
mapper: { cursor in
42+
try ListContent(
43+
id: cursor.getString(name: "id"),
44+
name: cursor.getString(name: "name"),
45+
createdAt: cursor.getString(name: "created_at"),
46+
ownerId: cursor.getString(name: "owner_id")
47+
)
48+
}
49+
) {
50+
callback(lists)
4751
}
48-
) {
49-
callback(lists)
52+
} catch {
53+
print("Error in watch: \(error)")
5054
}
5155
}
5256

@@ -59,11 +63,11 @@ class SystemManager {
5963

6064
func deleteList(id: String) async throws {
6165
_ = try await db.writeTransaction(callback: { transaction in
62-
_ = transaction.execute(
66+
_ = try transaction.execute(
6367
sql: "DELETE FROM \(LISTS_TABLE) WHERE id = ?",
6468
parameters: [id]
6569
)
66-
_ = transaction.execute(
70+
_ = try transaction.execute(
6771
sql: "DELETE FROM \(TODOS_TABLE) WHERE list_id = ?",
6872
parameters: [id]
6973
)
@@ -72,24 +76,28 @@ class SystemManager {
7276
}
7377

7478
func watchTodos(_ listId: String, _ callback: @escaping (_ todos: [Todo]) -> Void ) async {
75-
for await todos in self.db.watch(
76-
sql: "SELECT * FROM \(TODOS_TABLE) WHERE list_id = ?",
77-
parameters: [listId],
78-
mapper: { cursor in
79-
return Todo(
80-
id: try cursor.getString(name: "id"),
81-
listId: try cursor.getString(name: "list_id"),
82-
photoId: try cursor.getStringOptional(name: "photo_id"),
83-
description: try cursor.getString(name: "description"),
84-
isComplete: try cursor.getBoolean(name: "completed"),
85-
createdAt: try cursor.getString(name: "created_at"),
86-
completedAt: try cursor.getStringOptional(name: "completed_at"),
87-
createdBy: try cursor.getStringOptional(name: "created_by"),
88-
completedBy: try cursor.getStringOptional(name: "completed_by")
89-
)
79+
do {
80+
for try await todos in try self.db.watch(
81+
sql: "SELECT * FROM \(TODOS_TABLE) WHERE list_id = ?",
82+
parameters: [listId],
83+
mapper: { cursor in
84+
try Todo(
85+
id: cursor.getString(name: "id"),
86+
listId: cursor.getString(name: "list_id"),
87+
photoId: cursor.getStringOptional(name: "photo_id"),
88+
description: cursor.getString(name: "description"),
89+
isComplete: cursor.getBoolean(name: "completed"),
90+
createdAt: cursor.getString(name: "created_at"),
91+
completedAt: cursor.getStringOptional(name: "completed_at"),
92+
createdBy: cursor.getStringOptional(name: "created_by"),
93+
completedBy: cursor.getStringOptional(name: "completed_by")
94+
)
95+
}
96+
) {
97+
callback(todos)
9098
}
91-
) {
92-
callback(todos)
99+
} catch {
100+
print("Error in watch: \(error)")
93101
}
94102
}
95103

@@ -117,7 +125,7 @@ class SystemManager {
117125

118126
func deleteTodo(id: String) async throws {
119127
_ = try await db.writeTransaction(callback: { transaction in
120-
transaction.execute(
128+
try transaction.execute(
121129
sql: "DELETE FROM \(TODOS_TABLE) WHERE id = ?",
122130
parameters: [id]
123131
)

Package.resolved

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
"kind" : "remoteSourceControl",
66
"location" : "https://github.com/powersync-ja/powersync-kotlin.git",
77
"state" : {
8-
"revision" : "61d195816585f30260181dcbd157bf1660c9ac4e",
9-
"version" : "1.0.0-BETA22.0"
8+
"revision" : "203db74889df8a20e3c6ac38aede6b0186d2e3b5",
9+
"version" : "1.0.0-BETA23.0"
1010
}
1111
},
1212
{

Package.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ let package = Package(
1616
targets: ["PowerSync"]),
1717
],
1818
dependencies: [
19-
.package(url: "https://github.com/powersync-ja/powersync-kotlin.git", exact: "1.0.0-BETA22.0"),
19+
.package(url: "https://github.com/powersync-ja/powersync-kotlin.git", exact: "1.0.0-BETA23.0"),
2020
.package(url: "https://github.com/powersync-ja/powersync-sqlite-core-swift.git", "0.3.9"..<"0.4.0")
2121
],
2222
targets: [

Sources/PowerSync/Kotlin/KotlinPowerSyncDatabaseImpl.swift

+56-29
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ final class KotlinPowerSyncDatabaseImpl: PowerSyncDatabaseProtocol {
7979
mapper: mapper
8080
) as! RowType
8181
}
82-
82+
8383
func get<RowType>(
8484
sql: String,
8585
parameters: [Any]?,
@@ -93,7 +93,7 @@ final class KotlinPowerSyncDatabaseImpl: PowerSyncDatabaseProtocol {
9393
}
9494
) as! RowType
9595
}
96-
96+
9797
func getAll<RowType>(
9898
sql: String,
9999
parameters: [Any]?,
@@ -105,7 +105,7 @@ final class KotlinPowerSyncDatabaseImpl: PowerSyncDatabaseProtocol {
105105
mapper: mapper
106106
) as! [RowType]
107107
}
108-
108+
109109
func getAll<RowType>(
110110
sql: String,
111111
parameters: [Any]?,
@@ -131,7 +131,7 @@ final class KotlinPowerSyncDatabaseImpl: PowerSyncDatabaseProtocol {
131131
mapper: mapper
132132
) as! RowType?
133133
}
134-
134+
135135
func getOptional<RowType>(
136136
sql: String,
137137
parameters: [Any]?,
@@ -150,48 +150,75 @@ final class KotlinPowerSyncDatabaseImpl: PowerSyncDatabaseProtocol {
150150
sql: String,
151151
parameters: [Any]?,
152152
mapper: @escaping (SqlCursor) -> RowType
153-
) -> AsyncStream<[RowType]> {
154-
AsyncStream { continuation in
153+
) throws -> AsyncThrowingStream<[RowType], Error> {
154+
AsyncThrowingStream { continuation in
155155
Task {
156-
for await values in try self.kotlinDatabase.watch(
157-
sql: sql,
158-
parameters: parameters,
159-
mapper: mapper
160-
) {
161-
continuation.yield(values as! [RowType])
156+
do {
157+
for await values in try self.kotlinDatabase.watch(
158+
sql: sql,
159+
parameters: parameters,
160+
mapper: mapper
161+
) {
162+
continuation.yield(values as! [RowType])
163+
}
164+
continuation.finish()
165+
} catch {
166+
continuation.finish(throwing: error)
162167
}
163-
continuation.finish()
164168
}
165169
}
166170
}
167-
171+
168172
func watch<RowType>(
169173
sql: String,
170174
parameters: [Any]?,
171175
mapper: @escaping (SqlCursor) throws -> RowType
172-
) -> AsyncStream<[RowType]> {
173-
AsyncStream { continuation in
176+
) throws -> AsyncThrowingStream<[RowType], Error> {
177+
AsyncThrowingStream { continuation in
174178
Task {
175-
for await values in try self.kotlinDatabase.watch(
176-
sql: sql,
177-
parameters: parameters,
178-
mapper: { cursor in
179-
try! mapper(cursor)
179+
do {
180+
for await values in try self.kotlinDatabase.watch(
181+
sql: sql,
182+
parameters: parameters,
183+
mapper: { cursor in
184+
try! mapper(cursor)
185+
}
186+
) {
187+
continuation.yield(values as! [RowType])
180188
}
181-
) {
182-
continuation.yield(values as! [RowType])
189+
continuation.finish()
190+
} catch {
191+
continuation.finish(throwing: error)
183192
}
184-
continuation.finish()
185193
}
186194
}
187195
}
188-
189-
public func writeTransaction<R>(callback: @escaping (any PowerSyncTransaction) -> R) async throws -> R {
190-
return try await kotlinDatabase.writeTransaction(callback: callback) as! R
196+
197+
public func writeTransaction<R>(callback: @escaping (any PowerSyncTransaction) throws -> R) async throws -> R {
198+
return try await kotlinDatabase.writeTransaction(callback: TransactionCallback(callback: callback)) as! R
199+
}
200+
201+
public func readTransaction<R>(callback: @escaping (any PowerSyncTransaction) throws -> R) async throws -> R {
202+
return try await kotlinDatabase.readTransaction(callback: TransactionCallback(callback: callback)) as! R
191203
}
204+
}
205+
206+
class TransactionCallback<R>: PowerSyncKotlin.ThrowableTransactionCallback {
207+
let callback: (PowerSyncTransaction) throws -> R
192208

193-
public func readTransaction<R>(callback: @escaping (any PowerSyncTransaction) -> R) async throws -> R {
194-
return try await kotlinDatabase.readTransaction(callback: callback) as! R
209+
init(callback: @escaping (PowerSyncTransaction) throws -> R) {
210+
self.callback = callback
211+
}
212+
213+
func execute(transaction: PowerSyncKotlin.PowerSyncTransaction) throws -> Any{
214+
do {
215+
return try callback(transaction)
216+
} catch let error {
217+
return PowerSyncKotlin.PowerSyncException(
218+
message: error.localizedDescription,
219+
cause: PowerSyncKotlin.KotlinThrowable(message: error.localizedDescription)
220+
)
221+
}
195222
}
196223
}
197224

Sources/PowerSync/QueriesProtocol.swift

+6-6
Original file line numberDiff line numberDiff line change
@@ -59,21 +59,21 @@ public protocol Queries {
5959
sql: String,
6060
parameters: [Any]?,
6161
mapper: @escaping (SqlCursor) -> RowType
62-
) -> AsyncStream<[RowType]>
62+
) throws -> AsyncThrowingStream<[RowType], Error>
6363

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

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

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

7979
extension Queries {
@@ -105,7 +105,7 @@ extension Queries {
105105
public func watch<RowType>(
106106
_ sql: String,
107107
mapper: @escaping (SqlCursor) -> RowType
108-
) -> AsyncStream<[RowType]> {
109-
return watch(sql: sql, parameters: [], mapper: mapper)
108+
) throws -> AsyncThrowingStream<[RowType], Error> {
109+
return try watch(sql: sql, parameters: [], mapper: mapper)
110110
}
111111
}

0 commit comments

Comments
 (0)