diff --git a/CHANGELOG.md b/CHANGELOG.md index 3923238..40a8370 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## 1.6.1 (unreleased) + +* Update Kotlin SDK to 1.7.0. + ## 1.6.0 * Update core extension to 0.4.6 ([changelog](https://github.com/powersync-ja/powersync-sqlite-core/releases/tag/v0.4.6)) diff --git a/Package.resolved b/Package.resolved index a62a2d5..75347d5 100644 --- a/Package.resolved +++ b/Package.resolved @@ -8,6 +8,15 @@ "revision" : "b2a81af14e9ad83393eb187bb02e62e6db8b5ad6", "version" : "0.4.6" } + }, + { + "identity" : "sqlcipher.swift", + "kind" : "remoteSourceControl", + "location" : "https://github.com/sqlcipher/SQLCipher.swift.git", + "state" : { + "revision" : "247b042574b4838c7eefdb95d6a250a843dcf1ad", + "version" : "4.11.0" + } } ], "version" : 2 diff --git a/Package.swift b/Package.swift index 797d586..6dafc3a 100644 --- a/Package.swift +++ b/Package.swift @@ -7,12 +7,14 @@ let packageName = "PowerSync" // Set this to the absolute path of your Kotlin SDK checkout if you want to use a local Kotlin // build. Also see docs/LocalBuild.md for details -let localKotlinSdkOverride: String? = nil +let localKotlinSdkOverride: String? = "/Users/simon/src/powersync-kotlin" // Set this to the absolute path of your powersync-sqlite-core checkout if you want to use a // local build of the core extension. let localCoreExtension: String? = nil +let encryption = true + // Our target and dependency setup is different when a local Kotlin SDK is used. Without the local // SDK, we have no package dependency on Kotlin and download the XCFramework from Kotlin releases as // a binary target. @@ -22,18 +24,24 @@ var conditionalDependencies: [Package.Dependency] = [] var conditionalTargets: [Target] = [] var kotlinTargetDependency = Target.Dependency.target(name: "PowerSyncKotlin") +if encryption { + conditionalDependencies.append(.package(url: "https://github.com/sqlcipher/SQLCipher.swift.git", from: "4.10.0")) +} else { + conditionalDependencies.append(.package(url: "https://github.com/sbooth/CSQLite.git", from: "3.50.4")) +} + if let kotlinSdkPath = localKotlinSdkOverride { // We can't depend on local XCFrameworks outside of this project's root, so there's a Package.swift // in the PowerSyncKotlin project pointing towards a local build. - conditionalDependencies.append(.package(path: "\(kotlinSdkPath)/PowerSyncKotlin")) + conditionalDependencies.append(.package(path: "\(kotlinSdkPath)/internal/PowerSyncKotlin")) kotlinTargetDependency = .product(name: "PowerSyncKotlin", package: "PowerSyncKotlin") } else { // Not using a local build, so download from releases conditionalTargets.append(.binaryTarget( name: "PowerSyncKotlin", - url: "https://github.com/powersync-ja/powersync-kotlin/releases/download/v1.6.0/PowersyncKotlinRelease.zip", - checksum: "4f70331c11e30625eecf4ebcebe7b562e2e0165774890d2a43480ebc3a9081cc" + url: "https://github.com/powersync-ja/powersync-kotlin/releases/download/v1.7.0/PowersyncKotlinRelease.zip", + checksum: "836ac106c26a184c10373c862745d9af195737ad01505bb965f197797aa88535" )) } @@ -81,7 +89,10 @@ let package = Package( name: packageName, dependencies: [ kotlinTargetDependency, - .product(name: "PowerSyncSQLiteCore", package: corePackageName) + .product(name: "PowerSyncSQLiteCore", package: corePackageName), + encryption ? + .product(name: "SQLCipher", package: "SQLCipher.swift") : + .product(name: "CSQLite", package: "CSQLite"), ] ), .testTarget( diff --git a/Sources/PowerSync/Kotlin/KotlinPowerSyncDatabaseImpl.swift b/Sources/PowerSync/Kotlin/KotlinPowerSyncDatabaseImpl.swift index 10bbd97..066733f 100644 --- a/Sources/PowerSync/Kotlin/KotlinPowerSyncDatabaseImpl.swift +++ b/Sources/PowerSync/Kotlin/KotlinPowerSyncDatabaseImpl.swift @@ -13,9 +13,10 @@ final class KotlinPowerSyncDatabaseImpl: PowerSyncDatabaseProtocol, init( schema: Schema, dbFilename: String, - logger: DatabaseLogger + logger: DatabaseLogger, + initialStatements: [String] = [], ) { - let factory = PowerSyncKotlin.DatabaseDriverFactory() + let factory = sqlite3DatabaseFactory(initialStatements: initialStatements) kotlinDatabase = PowerSyncDatabase( factory: factory, schema: KotlinAdapter.Schema.toKotlin(schema), diff --git a/Sources/PowerSync/Kotlin/sync/KotlinSyncStatus.swift b/Sources/PowerSync/Kotlin/sync/KotlinSyncStatus.swift index db8ce2d..95a187f 100644 --- a/Sources/PowerSync/Kotlin/sync/KotlinSyncStatus.swift +++ b/Sources/PowerSync/Kotlin/sync/KotlinSyncStatus.swift @@ -5,7 +5,7 @@ import PowerSyncKotlin final class KotlinSyncStatus: KotlinSyncStatusDataProtocol, SyncStatus { private let baseStatus: PowerSyncKotlin.SyncStatus - var base: any PowerSyncKotlin.SyncStatusData { + var base: PowerSyncKotlin.SyncStatusData { baseStatus } diff --git a/Tests/PowerSyncTests/EncryptionTests.swift b/Tests/PowerSyncTests/EncryptionTests.swift new file mode 100644 index 0000000..74c78e8 --- /dev/null +++ b/Tests/PowerSyncTests/EncryptionTests.swift @@ -0,0 +1,67 @@ +@testable import PowerSync +import XCTest + + +final class EncryptionTests: XCTestCase { + + func testLinksSqlcipher() async throws { + let database = KotlinPowerSyncDatabaseImpl( + schema: Schema(), + dbFilename: ":memory:", + logger: DatabaseLogger(DefaultLogger()) + ) + + let version = try await database.get("pragma cipher_version", mapper: {cursor in + try cursor.getString(index: 0) + }); + + XCTAssertEqual(version, "4.11.0 community") + try await database.close() + } + + func testEncryption() async throws { + let database = KotlinPowerSyncDatabaseImpl( + schema: Schema(tables: [ + Table( + name: "users", + columns: [ + .text("name") + ] + ), + ]), + dbFilename: "encrypted.db", + logger: DatabaseLogger(DefaultLogger()), + initialStatements: [ + "pragma key = 'foobar'" + ], + ) + + try await database.execute("INSERT INTO users (id, name) VALUES (uuid(), 'test')") + try await database.close() + + let another = KotlinPowerSyncDatabaseImpl( + schema: Schema(tables: [ + Table( + name: "users", + columns: [ + .text("name") + ] + ), + ]), + dbFilename: "encrypted.db", + logger: DatabaseLogger(DefaultLogger()), + initialStatements: [ + "pragma key = 'wrong password'" + ], + ) + + var hadError = false + do { + try await database.execute("DELETE FROM users") + } catch let error { + hadError = true + } + + XCTAssertTrue(hadError) + } +}