Skip to content

Commit c87e079

Browse files
committed
merge main
2 parents 011b1da + b45e02f commit c87e079

File tree

16 files changed

+143
-45
lines changed

16 files changed

+143
-45
lines changed

CHANGELOG.md

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
11
# Changelog
22

3-
## 1.5.0 (unreleased)
3+
## 1.6.0 (unreleased)
4+
5+
* Remove internal SQLDelight and SQLiter dependencies.
6+
* Add `rawConnection` getter to `ConnectionContext`, which is a `SQLiteConnection` instance from
7+
`androidx.sqlite` that can be used to step through statements in a custom way.
8+
9+
## 1.5.0
410

511
* Add `PowerSyncDatabase.getCrudTransactions()`, returning a flow of transactions. This is useful
612
to upload multiple transactions in a batch.
713
* Fix modifying severity of the global Kermit logger
814
* Add `PowerSync` tag for the logs
9-
* Remove internal SQLDelight and SQLiter dependencies.
10-
* Add `rawConnection` getter to `ConnectionContext`, which is a `SQLiteConnection` instance from
11-
`androidx.sqlite` that can be used to step through statements in a custom way.
15+
* Fix `null` values in CRUD entries being reported as `"null"` strings.
16+
* [INTERNAL] Added helpers for Swift read and write lock exception handling.
1217

1318
## 1.4.0
1419

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)

core/src/commonIntegrationTest/kotlin/com/powersync/CrudTest.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,11 @@ class CrudTest {
134134
)
135135
batch.crud[0].previousValues shouldBe null
136136

137+
batch.crud[1].opData shouldBe
138+
mapOf(
139+
"a" to "te\"xt",
140+
"b" to null,
141+
)
137142
batch.crud[1].opData?.typed shouldBe
138143
mapOf(
139144
"a" to "te\"xt",

core/src/commonMain/kotlin/com/powersync/db/crud/SerializedRow.kt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import kotlinx.serialization.json.JsonElement
44
import kotlinx.serialization.json.JsonNull
55
import kotlinx.serialization.json.JsonObject
66
import kotlinx.serialization.json.JsonPrimitive
7+
import kotlinx.serialization.json.contentOrNull
78
import kotlinx.serialization.json.jsonPrimitive
89
import kotlin.experimental.ExperimentalObjCRefinement
910
import kotlin.native.HiddenFromObjC
@@ -47,11 +48,11 @@ internal class SerializedRow(
4748

4849
private data class ToStringEntry(
4950
val inner: Map.Entry<String, JsonElement>,
50-
) : Map.Entry<String, String> {
51+
) : Map.Entry<String, String?> {
5152
override val key: String
5253
get() = inner.key
53-
override val value: String
54-
get() = inner.value.jsonPrimitive.content
54+
override val value: String?
55+
get() = inner.value.jsonPrimitive.contentOrNull
5556
}
5657

5758
private class TypedRow(

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

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package com.powersync.db.internal
22

3-
import androidx.sqlite.SQLiteConnection
43
import com.powersync.ExperimentalPowerSyncAPI
54
import com.powersync.PowerSyncException
65
import com.powersync.db.SqlCursor
@@ -30,7 +29,6 @@ internal class InternalDatabaseImpl(
3029
// Could be scope.coroutineContext, but the default is GlobalScope, which seems like a bad idea. To discuss.
3130
private val dbContext = Dispatchers.IO
3231

33-
3432
override suspend fun execute(
3533
sql: String,
3634
parameters: List<Any?>?,
@@ -171,9 +169,7 @@ internal class InternalDatabaseImpl(
171169
withContext(dbContext) {
172170
runWrapped {
173171
useConnection(true) { connection ->
174-
catchSwiftExceptions {
175-
callback(connection)
176-
}
172+
callback(connection)
177173
}
178174
}
179175
}
@@ -186,9 +182,7 @@ internal class InternalDatabaseImpl(
186182
override suspend fun <R> readTransaction(callback: ThrowableTransactionCallback<R>): R =
187183
internalReadLock {
188184
it.runTransaction { tx ->
189-
catchSwiftExceptions {
190-
callback.execute(tx)
191-
}
185+
callback.execute(tx)
192186
}
193187
}
194188

@@ -197,9 +191,7 @@ internal class InternalDatabaseImpl(
197191
withContext(dbContext) {
198192
pool.write { writer ->
199193
runWrapped {
200-
catchSwiftExceptions {
201-
callback(writer)
202-
}
194+
callback(writer)
203195
}
204196
}
205197
}
@@ -213,10 +205,7 @@ internal class InternalDatabaseImpl(
213205
internalWriteLock {
214206

215207
it.runTransaction { tx ->
216-
// Need to catch Swift exceptions here for Rollback
217-
catchSwiftExceptions {
218-
callback.execute(tx)
219-
}
208+
callback.execute(tx)
220209
}
221210
}
222211

demos/android-supabase-todolist/build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ fun getLocalProperty(
2020

2121
android {
2222
namespace = "com.powersync.androidexample"
23-
compileSdk = 35
23+
compileSdk = libs.versions.android.compileSdk.get().toInt()
2424

2525
buildFeatures {
2626
buildConfig = true
@@ -29,7 +29,7 @@ android {
2929
defaultConfig {
3030
applicationId = "com.powersync.androidexample"
3131
minSdk = 24
32-
targetSdk = 35
32+
targetSdk = libs.versions.android.targetSdk.get().toInt()
3333
versionCode = 1
3434
versionName = "1.0"
3535

demos/android-supabase-todolist/src/main/java/com/powersync/androidexample/MainActivity.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package com.powersync.androidexample
33
import android.os.Bundle
44
import androidx.activity.ComponentActivity
55
import androidx.activity.compose.setContent
6+
import androidx.activity.enableEdgeToEdge
67
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
78
import com.powersync.androidexample.ui.CameraService
89
import com.powersync.demos.App
@@ -15,6 +16,9 @@ class MainActivity : ComponentActivity() {
1516

1617
super.onCreate(savedInstanceState)
1718

19+
// Needed to render system bar properly
20+
enableEdgeToEdge()
21+
1822
setContent {
1923
App(
2024
cameraService = cameraService,

demos/android-supabase-todolist/src/main/java/com/powersync/androidexample/screens/SignInScreen.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
package com.powersync.demos.screens
22

33
import androidx.compose.foundation.layout.*
4+
import androidx.compose.foundation.rememberScrollState
5+
import androidx.compose.foundation.text.KeyboardOptions
6+
import androidx.compose.foundation.verticalScroll
47
import androidx.compose.material3.*
58
import androidx.compose.runtime.*
69
import androidx.compose.ui.Alignment
710
import androidx.compose.ui.Modifier
11+
import androidx.compose.ui.text.input.KeyboardType
812
import androidx.compose.ui.text.input.PasswordVisualTransformation
913
import androidx.compose.ui.unit.dp
1014
import com.powersync.demos.AuthViewModel
@@ -24,10 +28,12 @@ internal fun SignInScreen(
2428
var isLoading by remember { mutableStateOf(false) }
2529

2630
val coroutineScope = rememberCoroutineScope()
31+
val scrollState = rememberScrollState()
2732

2833
Column(
2934
modifier = Modifier
3035
.fillMaxSize()
36+
.verticalScroll(scrollState)
3137
.padding(16.dp),
3238
verticalArrangement = Arrangement.Center,
3339
horizontalAlignment = Alignment.CenterHorizontally
@@ -52,6 +58,7 @@ internal fun SignInScreen(
5258
onValueChange = { password = it },
5359
label = { Text("Password") },
5460
visualTransformation = PasswordVisualTransformation(),
61+
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password),
5562
modifier = Modifier
5663
.fillMaxWidth()
5764
.padding(bottom = 16.dp)

demos/android-supabase-todolist/src/main/java/com/powersync/androidexample/screens/SignUpScreen.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
package com.powersync.demos.screens
22

33
import androidx.compose.foundation.layout.*
4+
import androidx.compose.foundation.rememberScrollState
5+
import androidx.compose.foundation.text.KeyboardOptions
6+
import androidx.compose.foundation.verticalScroll
47
import androidx.compose.material3.*
58
import androidx.compose.runtime.*
69
import androidx.compose.ui.Alignment
710
import androidx.compose.ui.Modifier
11+
import androidx.compose.ui.text.input.KeyboardType
812
import androidx.compose.ui.text.input.PasswordVisualTransformation
913
import androidx.compose.ui.unit.dp
1014
import com.powersync.demos.AuthViewModel
@@ -24,10 +28,12 @@ internal fun SignUpScreen(
2428
var isLoading by remember { mutableStateOf(false) }
2529

2630
val coroutineScope = rememberCoroutineScope()
31+
val scrollState = rememberScrollState()
2732

2833
Column(
2934
modifier = Modifier
3035
.fillMaxSize()
36+
.verticalScroll(scrollState)
3137
.padding(16.dp),
3238
verticalArrangement = Arrangement.Center,
3339
horizontalAlignment = Alignment.CenterHorizontally
@@ -48,6 +54,7 @@ internal fun SignUpScreen(
4854
onValueChange = { password = it },
4955
label = { Text("Password") },
5056
visualTransformation = PasswordVisualTransformation(),
57+
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password),
5158
modifier = Modifier
5259
.fillMaxWidth()
5360
.padding(bottom = 16.dp)
@@ -58,6 +65,7 @@ internal fun SignUpScreen(
5865
onValueChange = { confirmPassword = it },
5966
label = { Text("Confirm Password") },
6067
visualTransformation = PasswordVisualTransformation(),
68+
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password),
6169
modifier = Modifier
6270
.fillMaxWidth()
6371
.padding(bottom = 16.dp)

demos/supabase-todolist/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ all items have been received.
4141
1. Clone this repo: ```git clone https://github.com/powersync-ja/powersync-kotlin.git```
4242
2. Open the repo in Android Studio. This creates a `local.properties` file in root and should contain a `sdk.dir=/path/to/android/sdk` line.
4343
3. Sync the project with Gradle (this should happen automatically, or choose File > Sync project with Gradle Files).
44-
4. Insert your Supabase project URL, Supabase Anon Key, and PowerSync instance URL into the `demos/supabase-todlist/local.properties` file:
44+
4. Insert your Supabase project URL, Supabase Anon Key, and PowerSync instance URL into the `demos/supabase-todolist/local.properties` file:
4545

4646
```bash
4747
# local.properties

0 commit comments

Comments
 (0)