Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .buildkite/swift-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ function build_for_real_device() {
echo "--- :swift: Building for $platform device"
export NSUnbufferedIO=YES
xcodebuild -destination "generic/platform=$platform" \
-scheme WordPressAPI \
-scheme WordPressAPI-Package \
-derivedDataPath DerivedData \
-skipPackagePluginValidation \
build | xcbeautify
Expand Down
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
/docs
/docs.tar.gz

# Test DB Files
kotlin.db
swift.db

# Ignore Gradle project-specific cache directory
.gradle

Expand Down Expand Up @@ -33,7 +37,6 @@ fastlane/report.xml
libwordPressFFI.xcframework*
/swift-docs.tar.gz


# Auto-generated Swift Files
native/swift/Sources/wordpress-api-wrapper/*.swift

Expand Down
56 changes: 56 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ _build-apple-%-tvos _build-apple-%-tvos-sim _build-apple-%-watchos _build-apple-

# Build the library for a specific target
_build-apple-%:
cargo $(CARGO_OPTS) $(cargo_config_library) build --target $* --package wp_api --profile $(CARGO_PROFILE)
cargo $(CARGO_OPTS) $(cargo_config_library) build --target $* --package wp_api --profile $(CARGO_PROFILE) --no-default-features
./scripts/swift-bindings.sh target/$*/$(CARGO_PROFILE_DIRNAME)/libwp_api.a

# Build the library for one single platform, including real device and simulator.
Expand Down
2 changes: 1 addition & 1 deletion Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 19 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ var package = Package(
.library(
name: "WordPressAPI",
targets: ["WordPressAPI"]
),
.library(
name: "WordPressApiCache",
targets: ["WordPressApiCache"]
)
],
dependencies: [
Expand Down Expand Up @@ -56,6 +60,13 @@ var package = Package(
.swiftLanguageMode(.v5)
]
),
.target(
name: "WordPressApiCache",
dependencies: [
.target(name: "WordPressAPIInternal")
],
path: "native/swift/Sources/wordpress-api-cache"
),
libwordpressFFI,
.testTarget(
name: "WordPressAPITests",
Expand All @@ -68,6 +79,14 @@ var package = Package(
swiftSettings: [
.define("PROGRESS_REPORTING_ENABLED", .when(platforms: [.iOS, .macOS, .tvOS, .watchOS]))
]
),
.testTarget(
name: "WordPressApiCacheTests",
dependencies: [
.target(name: "WordPressApiCache"),
.target(name: "WordPressAPIInternal")
],
path: "native/swift/Tests/wordpress-api-cache"
)
].addingIntegrationTests()
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.parallel.Execution
import org.junit.jupiter.api.parallel.ExecutionMode
import rs.wordpress.cache.kotlin.WordPressApiCache
import rs.wordpress.cache.kotlin.WordPressApiCacheDelegate
import kotlin.test.assertEquals

@Execution(ExecutionMode.CONCURRENT)
class WordPressApiCacheTest {

@Test
fun testThatMigrationsWork() = runTest {
assertEquals(2, WordPressApiCache().performMigrations())
}

@Test
fun testBackgroundUpdateNotificationsWork() = runTest {
var updateCount = 0
val delegate = WordPressApiCacheDelegate(
callback = { updateHook ->
updateCount += 1
}
)

val cache = WordPressApiCache(delegate = delegate)
cache.startListeningForUpdates()

val migrationCount = cache.performMigrations()
assertEquals(updateCount, migrationCount)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package rs.wordpress.cache.kotlin

import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.coroutines.withContext
import uniffi.wp_api.DatabaseDelegate
import uniffi.wp_api.UpdateHook
import uniffi.wp_api.WpApiCache
import java.nio.file.Path
import java.util.concurrent.Executors

class WordPressApiCacheLoggingDelegate: DatabaseDelegate {
override fun didUpdate(updateHook: UpdateHook) {
println("Received update: $updateHook")
}
}
class WordPressApiCacheDelegate(
private val callback: (updateHook: UpdateHook) -> Unit
) : DatabaseDelegate {

override fun didUpdate(updateHook: UpdateHook) {
callback(updateHook)
}
}

class WordPressApiCache {
private val cache: WpApiCache
private val internalDispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
private val delegate: DatabaseDelegate?

// Creates a new in-memory cache
constructor(delegate: WordPressApiCacheDelegate? = null) : this(":memory:", delegate)

// Creates a new cache at the specified file system URL
constructor(path: Path, delegate: WordPressApiCacheDelegate? = null) : this(path.toString(), delegate)

// Creates a new cache at the specified path
constructor(string: String, delegate: WordPressApiCacheDelegate? = null) {
this.cache = WpApiCache(string)
this.delegate = delegate
}

suspend fun performMigrations(): Int = withContext(internalDispatcher) {
cache.performMigrations().toInt()
}
fun startListeningForUpdates() {
if (this.delegate != null) {
this.cache.startListeningForUpdates(this.delegate)
}
}

fun stopListeningForUpdates() {
this.cache.stopListeningForUpdates()
}
}
57 changes: 57 additions & 0 deletions native/swift/Sources/wordpress-api-cache/WordPressApiCache.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import Foundation
import WordPressAPIInternal

public actor WordPressApiCache {

private let cache: WpApiCache
private let delegate: any DatabaseDelegate

public struct Notifications {
public static let cacheDidUpdate = Notification.Name("WordPressApiCache.cacheDidUpdate")

public static func name(for table: String) -> Notification.Name {
Notification.Name(rawValue: "WordPressApiCache.cacheDidUpdate.\(table)")
}
}

final public class ApiCacheDelegate: DatabaseDelegate {
public init() {}

public func didUpdate(updateHook: WordPressAPIInternal.UpdateHook) {
let name = Notifications.name(for: updateHook.tableName)
NotificationCenter.default.post(name: name, object: updateHook)
}
}

/// Creates a new in-memory cache
public init(delegate: DatabaseDelegate = ApiCacheDelegate()) throws {
try self.init(path: ":memory:", delegate: delegate)
}

/// Creates a new cache at the specified file system URL
public init(url: URL, delegate: DatabaseDelegate = ApiCacheDelegate()) throws {
try self.init(path: url.absoluteString, delegate: delegate)
}

/// Creates a new cache at the specified path
public init(path: String, delegate: DatabaseDelegate = ApiCacheDelegate()) throws {
self.cache = try WpApiCache(path: path)
self.delegate = delegate
}

public func performMigrations() async throws -> Int {
return Int(try self.cache.performMigrations())
}

public func startListeningForUpdates() {
self.cache.startListeningForUpdates(delegate: self.delegate)
}

public func stopListeningForUpdates() {
self.cache.stopListeningForUpdates()
}

deinit {
self.cache.stopListeningForUpdates()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import Foundation
import Testing
import WordPressApiCache

actor Test {

private var cache: WordPressApiCache!
private var changeCount = 0

init() throws {
self.cache = try WordPressApiCache()
}

@Test func testMigrationsWork() async throws {
let migrationsPerformed = try await self.cache.performMigrations()
#expect(migrationsPerformed == 2)
}

@Test func testBackgroundUpdateNotificationsWork() async throws {
let name = WordPressApiCache.Notifications.name(for: "_migrations")

let handle = Task {
for await _ in NotificationCenter.default.notifications(named: name) {
self.incrementChangeCount()
}
}

await self.cache.startListeningForUpdates()
let migrationCount = try await self.cache.performMigrations()

// Wait for NotificationCenter to finish delivery
try await Task.sleep(nanoseconds: 10 * NSEC_PER_MSEC)

#expect(migrationCount == self.changeCount)
handle.cancel()
}

func incrementChangeCount() {
self.changeCount += 1
}
}
5 changes: 5 additions & 0 deletions scripts/swift-bindings.sh
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ extension $error_type: LocalizedError {
}
EOF
done

# Use sed to replace `import SQLite3` with the wrapped version
sed -i.bak 's/^import SQLite3$/#if canImport(SQLite3)\
import SQLite3\
#endif/' $swift_binding
}

for swift_binding in "$output_dir"/*.swift; do
Expand Down
2 changes: 1 addition & 1 deletion scripts/xcodebuild-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ device_id=$(xcrun simctl list --json devices available | jq -re ".devices.\"com.
export NSUnbufferedIO=YES

xcodebuild \
-scheme WordPressAPI \
-scheme WordPressAPI-Package \
-derivedDataPath DerivedData \
-destination "id=${device_id}" \
-skipPackagePluginValidation \
Expand Down
Loading