Skip to content

Commit 2ffdf53

Browse files
stevensJourneysimolus3
authored andcommitted
Add Swift lock exception handler utils
1 parent 8e8e4e1 commit 2ffdf53

File tree

4 files changed

+74
-32
lines changed

4 files changed

+74
-32
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
to upload multiple transactions in a batch.
77
* Fix modifying severity of the global Kermit logger
88
* Add `PowerSync` tag for the logs
9+
* [INTERNAL] Added helpers for Swift read and write lock exception handling.
910

1011
## 1.4.0
1112

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package com.powersync
2+
3+
import com.powersync.db.ThrowableLockCallback
4+
import com.powersync.db.ThrowableTransactionCallback
5+
import com.powersync.db.internal.ConnectionContext
6+
import com.powersync.db.internal.PowerSyncTransaction
7+
8+
/**
9+
* The Kotlin [Result] type is an inline class which cannot be used on the Swift side.
10+
* This declares something similar which will help to bridge exceptions to Kotlin.
11+
* SKIEE doesn't handle generics well.
12+
* Making the Result type generic will cause the return type to be casted to "Any", but
13+
* also add restrictions that the generic should extend an object - which causes issues when returning
14+
* primitive types like `Int` or `String`.
15+
*/
16+
public sealed class PowerSyncResult {
17+
public data class Success(
18+
val value: Any,
19+
) : PowerSyncResult()
20+
21+
public data class Failure(
22+
val exception: PowerSyncException,
23+
) : PowerSyncResult()
24+
}
25+
26+
// Throws the [PowerSyncException] if the result is a failure, or returns the value if it is a success.
27+
// We throw the exception on behalf of the Swift SDK.
28+
@Throws(PowerSyncException::class)
29+
private fun handleLockResult(result: PowerSyncResult): Any {
30+
when (result) {
31+
is PowerSyncResult.Failure -> {
32+
throw result.exception
33+
}
34+
35+
is PowerSyncResult.Success -> {
36+
return result.value
37+
}
38+
}
39+
}
40+
41+
public class LockContextWrapper(
42+
private val handler: (context: ConnectionContext) -> PowerSyncResult,
43+
) : ThrowableLockCallback<Any> {
44+
override fun execute(context: ConnectionContext): Any = handleLockResult(handler(context))
45+
}
46+
47+
public class TransactionContextWrapper(
48+
private val handler: (context: PowerSyncTransaction) -> PowerSyncResult,
49+
) : ThrowableTransactionCallback<Any> {
50+
override fun execute(transaction: PowerSyncTransaction): Any = handleLockResult(handler(transaction))
51+
}
52+
53+
public fun wrapContextHandler(handler: (context: ConnectionContext) -> PowerSyncResult): ThrowableLockCallback<Any> =
54+
LockContextWrapper(handler)
55+
56+
public fun wrapTransactionContextHandler(handler: (context: PowerSyncTransaction) -> PowerSyncResult): ThrowableTransactionCallback<Any> =
57+
TransactionContextWrapper(handler)

PowerSyncKotlin/src/appleMain/kotlin/com/powersync/SDK.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
package com.powersync
44

5+
import com.powersync.db.ThrowableLockCallback
6+
import com.powersync.db.ThrowableTransactionCallback
7+
import com.powersync.db.internal.ConnectionContext
8+
import com.powersync.db.internal.PowerSyncTransaction
59
import com.powersync.sync.SyncClientConfiguration
610
import com.powersync.sync.SyncOptions
711
import io.ktor.client.plugins.logging.LogLevel

core/src/commonMain/kotlin/com/powersync/db/internal/InternalDatabaseImpl.kt

Lines changed: 12 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -181,9 +181,7 @@ internal class InternalDatabaseImpl(
181181
withContext(dbContext) {
182182
runWrapped {
183183
readPool.withConnection {
184-
catchSwiftExceptions {
185-
callback(it)
186-
}
184+
callback(it)
187185
}
188186
}
189187
}
@@ -196,23 +194,19 @@ internal class InternalDatabaseImpl(
196194
override suspend fun <R> readTransaction(callback: ThrowableTransactionCallback<R>): R =
197195
internalReadLock {
198196
it.transactor.transactionWithResult(noEnclosing = true) {
199-
catchSwiftExceptions {
200-
callback.execute(
201-
PowerSyncTransactionImpl(
202-
it.driver,
203-
),
204-
)
205-
}
197+
callback.execute(
198+
PowerSyncTransactionImpl(
199+
it.driver,
200+
),
201+
)
206202
}
207203
}
208204

209205
private suspend fun <R> internalWriteLock(callback: (TransactorDriver) -> R): R =
210206
withContext(dbContext) {
211207
writeLockMutex.withLock {
212208
runWrapped {
213-
catchSwiftExceptions {
214-
callback(writeConnection)
215-
}
209+
callback(writeConnection)
216210
}.also {
217211
// Trigger watched queries
218212
// Fire updates inside the write lock
@@ -229,31 +223,17 @@ internal class InternalDatabaseImpl(
229223
override suspend fun <R> writeTransaction(callback: ThrowableTransactionCallback<R>): R =
230224
internalWriteLock {
231225
it.transactor.transactionWithResult(noEnclosing = true) {
232-
// Need to catch Swift exceptions here for Rollback
233-
catchSwiftExceptions {
234-
callback.execute(
235-
PowerSyncTransactionImpl(
236-
it.driver,
237-
),
238-
)
239-
}
226+
callback.execute(
227+
PowerSyncTransactionImpl(
228+
it.driver,
229+
),
230+
)
240231
}
241232
}
242233

243234
// Register callback for table updates on a specific table
244235
override fun updatesOnTables(): SharedFlow<Set<String>> = writeConnection.driver.updatesOnTables()
245236

246-
// Unfortunately Errors can't be thrown from Swift SDK callbacks.
247-
// These are currently returned and should be thrown here.
248-
private fun <R> catchSwiftExceptions(action: () -> R): R {
249-
val result = action()
250-
251-
if (result is PowerSyncException) {
252-
throw result
253-
}
254-
return result
255-
}
256-
257237
private suspend fun getSourceTables(
258238
sql: String,
259239
parameters: List<Any?>?,

0 commit comments

Comments
 (0)