Skip to content

Commit 6ed7103

Browse files
authored
Update Kotlin SDK and core extension (#83)
1 parent fb34270 commit 6ed7103

File tree

9 files changed

+95
-48
lines changed

9 files changed

+95
-48
lines changed

.github/workflows/build_and_test.yaml

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,25 +14,14 @@ jobs:
1414
uses: maxim-lobanov/setup-xcode@v1
1515
with:
1616
xcode-version: latest-stable
17+
18+
- name: Test with strict concurrency
19+
run: |
20+
swift build -Xswiftc -strict-concurrency=complete
21+
swift test -Xswiftc -strict-concurrency=complete
22+
1723
- name: Build and Test
1824
run: |
1925
xcodebuild test -scheme PowerSync-Package -destination "platform=iOS Simulator,name=iPhone 16"
2026
xcodebuild test -scheme PowerSync-Package -destination "platform=macOS,arch=arm64,name=My Mac"
2127
xcodebuild test -scheme PowerSync-Package -destination "platform=watchOS Simulator,arch=arm64,name=Apple Watch Ultra 2 (49mm)"
22-
23-
buildSwift6:
24-
name: Build and test with Swift 6
25-
runs-on: macos-latest
26-
steps:
27-
- uses: actions/checkout@v4
28-
- name: Set up XCode
29-
uses: maxim-lobanov/setup-xcode@v1
30-
with:
31-
xcode-version: latest-stable
32-
- name: Use Swift 6
33-
run: |
34-
sed -i '' 's|^// swift-tools-version:.*$|// swift-tools-version:6.1|' Package.swift
35-
- name: Build and Test
36-
run: |
37-
swift build -Xswiftc -strict-concurrency=complete
38-
swift test -Xswiftc -strict-concurrency=complete

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

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.resolved

Lines changed: 11 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.swift

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// swift-tools-version: 5.7
1+
// swift-tools-version: 6.1
22
// The swift-tools-version declares the minimum version of Swift required to build this package.
33

44
import PackageDescription
@@ -25,16 +25,18 @@ var kotlinTargetDependency = Target.Dependency.target(name: "PowerSyncKotlin")
2525
if let kotlinSdkPath = localKotlinSdkOverride {
2626
// We can't depend on local XCFrameworks outside of this project's root, so there's a Package.swift
2727
// in the PowerSyncKotlin project pointing towards a local build.
28-
conditionalDependencies.append(.package(path: "\(kotlinSdkPath)/PowerSyncKotlin"))
28+
conditionalDependencies.append(.package(path: "\(kotlinSdkPath)/internal/PowerSyncKotlin"))
2929

3030
kotlinTargetDependency = .product(name: "PowerSyncKotlin", package: "PowerSyncKotlin")
3131
} else {
3232
// Not using a local build, so download from releases
33-
conditionalTargets.append(.binaryTarget(
34-
name: "PowerSyncKotlin",
35-
url: "https://github.com/powersync-ja/powersync-kotlin/releases/download/v1.7.0/PowersyncKotlinRelease.zip",
36-
checksum: "836ac106c26a184c10373c862745d9af195737ad01505bb965f197797aa88535"
37-
))
33+
conditionalTargets.append(
34+
.binaryTarget(
35+
name: "PowerSyncKotlin",
36+
url:
37+
"https://github.com/powersync-ja/powersync-kotlin/releases/download/v1.9.0/PowersyncKotlinRelease.zip",
38+
checksum: "6d9847391ab2bbbca1f6a7abe163f0682ddca4a559ef5a1d2567b3e62e7d9979"
39+
))
3840
}
3941

4042
var corePackageName = "powersync-sqlite-core-swift"
@@ -43,18 +45,19 @@ if let corePath = localCoreExtension {
4345
corePackageName = "powersync-sqlite-core"
4446
} else {
4547
// Not using a local build, so download from releases
46-
conditionalDependencies.append(.package(
47-
url: "https://github.com/powersync-ja/powersync-sqlite-core-swift.git",
48-
exact: "0.4.6"
49-
))
48+
conditionalDependencies.append(
49+
.package(
50+
url: "https://github.com/powersync-ja/powersync-sqlite-core-swift.git",
51+
exact: "0.4.10"
52+
))
5053
}
5154

5255
let package = Package(
5356
name: packageName,
5457
platforms: [
5558
.iOS(.v15),
5659
.macOS(.v12),
57-
.watchOS(.v9)
60+
.watchOS(.v9),
5861
],
5962
products: [
6063
// Products define the executables and libraries a package produces, making them visible to other packages.
@@ -71,22 +74,25 @@ let package = Package(
7174
// Dynamic linking is particularly important for XCode previews.
7275
type: .dynamic,
7376
targets: ["PowerSync"]
74-
)
77+
),
78+
],
79+
dependencies: conditionalDependencies + [
80+
.package(url: "https://github.com/powersync-ja/CSQLite.git", revision: "3.51.1")
7581
],
76-
dependencies: conditionalDependencies,
7782
targets: [
7883
// Targets are the basic building blocks of a package, defining a module or a test suite.
7984
// Targets can depend on other targets in this package and products from dependencies.
8085
.target(
8186
name: packageName,
8287
dependencies: [
8388
kotlinTargetDependency,
84-
.product(name: "PowerSyncSQLiteCore", package: corePackageName)
89+
.product(name: "PowerSyncSQLiteCore", package: corePackageName),
90+
.product(name: "CSQLite", package: "CSQLite"),
8591
]
8692
),
8793
.testTarget(
8894
name: "PowerSyncTests",
8995
dependencies: ["PowerSync"]
90-
)
96+
),
9197
] + conditionalTargets
9298
)

Sources/PowerSync/Kotlin/KotlinAdapter.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ enum KotlinAdapter {
4949
return PowerSyncKotlin.RawTable(
5050
name: table.name,
5151
put: translateStatement(table.put),
52-
delete: translateStatement(table.delete)
52+
delete: translateStatement(table.delete),
53+
clear: table.clear
5354
);
5455
}
5556

Sources/PowerSync/Kotlin/KotlinPowerSyncDatabaseImpl.swift

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import Foundation
22
import PowerSyncKotlin
3+
import CSQLite
34

45
final class KotlinPowerSyncDatabaseImpl: PowerSyncDatabaseProtocol,
56
// `PowerSyncKotlin.PowerSyncDatabase` cannot be marked as Sendable
@@ -16,7 +17,12 @@ final class KotlinPowerSyncDatabaseImpl: PowerSyncDatabaseProtocol,
1617
dbFilename: String,
1718
logger: DatabaseLogger
1819
) {
19-
let factory = PowerSyncKotlin.DatabaseDriverFactory()
20+
let rc = sqlite3_initialize();
21+
if (rc != 0) {
22+
fatalError("Call to sqlite3_initialize() failed with \(rc)")
23+
}
24+
25+
let factory = sqlite3DatabaseFactory(initialStatements: [])
2026
kotlinDatabase = PowerSyncDatabase(
2127
factory: factory,
2228
schema: KotlinAdapter.Schema.toKotlin(schema),
@@ -89,9 +95,10 @@ final class KotlinPowerSyncDatabaseImpl: PowerSyncDatabaseProtocol,
8995
try await kotlinDatabase.disconnect()
9096
}
9197

92-
func disconnectAndClear(clearLocal: Bool = true) async throws {
98+
func disconnectAndClear(clearLocal: Bool, soft: Bool) async throws {
9399
try await kotlinDatabase.disconnectAndClear(
94-
clearLocal: clearLocal
100+
clearLocal: clearLocal,
101+
soft: soft
95102
)
96103
}
97104

Sources/PowerSync/Protocol/PowerSyncDatabaseProtocol.swift

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -216,12 +216,20 @@ public protocol PowerSyncDatabaseProtocol: Queries, Sendable {
216216
func disconnect() async throws
217217

218218
/// Disconnect and clear the database.
219-
/// Use this when logging out.
220-
/// The database can still be queried after this is called, but the tables
221-
/// would be empty.
222219
///
223-
/// - Parameter clearLocal: Set to false to preserve data in local-only tables. Defaults to `true`.
224-
func disconnectAndClear(clearLocal: Bool) async throws
220+
/// Clearing the database is useful when a user logs out, to ensure another user logging in later would not see
221+
/// previous data.
222+
///
223+
/// The database can still be queried after this is called, but the tables would be empty.
224+
///
225+
/// To perserve data in local-only tables, set `clearLocal` to `false`.
226+
///
227+
/// A `soft` clear deletes publicly visible data, but keeps internal copies of data synced in the database. This
228+
/// usually means that if the same user logs out and back in again, the first sync is very fast because all internal
229+
/// data is still available. When a different user logs in, no old data would be visible at any point.
230+
/// Using soft clears is recommended where it's not a security issue that old data could be reconstructed from
231+
/// the database.
232+
func disconnectAndClear(clearLocal: Bool, soft: Bool) async throws
225233

226234
/// Close the database, releasing resources.
227235
/// Also disconnects any active connection.
@@ -301,7 +309,15 @@ public extension PowerSyncDatabaseProtocol {
301309
}
302310

303311
func disconnectAndClear() async throws {
304-
try await disconnectAndClear(clearLocal: true)
312+
try await disconnectAndClear(clearLocal: true, soft: false)
313+
}
314+
315+
func disconnectAndClear(clearLocal: Bool) async throws {
316+
try await disconnectAndClear(clearLocal: clearLocal, soft: false)
317+
}
318+
319+
func disconnectAndClear(soft: Bool) async throws {
320+
try await disconnectAndClear(clearLocal: true, soft: soft)
305321
}
306322

307323
func getCrudBatch(limit: Int32 = 100) async throws -> CrudBatch? {

Sources/PowerSync/Protocol/Schema/RawTable.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,15 @@ public struct RawTable: BaseTableProtocol {
2424

2525
/// The statement to run when the sync client has to delete a row.
2626
public let delete: PendingStatement
27+
28+
/// An optional statement to run when the database is cleared.
29+
public let clear: String?
2730

28-
public init(name: String, put: PendingStatement, delete: PendingStatement) {
31+
public init(name: String, put: PendingStatement, delete: PendingStatement, clear: String? = nil) {
2932
self.name = name
3033
self.put = put
3134
self.delete = delete
35+
self.clear = clear
3236
}
3337
}
3438

Tests/PowerSyncTests/CrudTests.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,4 +238,19 @@ final class CrudTests: XCTestCase {
238238
let finalTx = try await database.getNextCrudTransaction()
239239
XCTAssertEqual(finalTx!.crud.count, 15)
240240
}
241+
242+
func testSoftClear() async throws {
243+
try await database.execute(sql: "INSERT INTO users (id, name) VALUES (uuid(), ?)", parameters: ["test"]);
244+
try await database.execute(sql: "INSERT INTO ps_buckets (name, last_applied_op) VALUES (?, ?)", parameters: ["bkt", 10])
245+
246+
// Doing a soft-clear should delete data but keep the bucket around.
247+
try await database.disconnectAndClear(soft: true)
248+
let entries = try await database.getAll("SELECT name FROM ps_buckets", mapper: { cursor in try cursor.getString(index: 0) })
249+
XCTAssertEqual(entries.count, 1)
250+
251+
// Doing a default clear also deletes buckets.
252+
try await database.disconnectAndClear();
253+
let newEntries = try await database.getAll("SELECT name FROM ps_buckets", mapper: { cursor in try cursor.getString(index: 0) })
254+
XCTAssertEqual(newEntries.count, 0)
255+
}
241256
}

0 commit comments

Comments
 (0)