diff --git a/Package.swift b/Package.swift index dddcb15..2d45ce1 100644 --- a/Package.swift +++ b/Package.swift @@ -14,7 +14,7 @@ let package = Package( .package(url: "https://source.skip.tools/skip.git", from: "1.2.1"), .package(url: "https://source.skip.tools/skip-foundation.git", from: "1.2.0"), .package(url: "https://source.skip.tools/skip-bridge.git", "0.0.0"..<"2.0.0"), - .package(url: "https://source.skip.tools/swift-android-native.git", "0.0.0"..<"2.0.0") + .package(url: "https://source.skip.tools/swift-android-native.git", from: "1.1.0") ], targets: [ // mode=kotlin @@ -27,7 +27,7 @@ let package = Package( // mode=swift .target(name: "SkipAndroidBridge", dependencies: [ "SkipAndroidSDKBridge", - .product(name: "AndroidNative", package: "swift-android-native", condition: .when(platforms: [.android])), + .product(name: "AndroidNative", package: "swift-android-native"/*, condition: .when(platforms: [.android])*/), ], plugins: [.plugin(name: "skipstone", package: "skip")]), // mode=kotlin .target(name: "SkipAndroidBridgeKt", @@ -36,12 +36,17 @@ let package = Package( ], plugins: [.plugin(name: "skipstone", package: "skip")]), +// .target(name: "SkipAndroidBridgeTestsSupport", +// dependencies: ["SkipAndroidBridgeKt"], +// plugins: [.plugin(name: "skipstone", package: "skip")]), + .testTarget(name: "SkipAndroidSDKBridgeTests", dependencies: [ "SkipAndroidSDKBridge", .product(name: "SkipTest", package: "skip"), ], plugins: [.plugin(name: "skipstone", package: "skip")]), .testTarget(name: "SkipAndroidBridgeTests", dependencies: [ "SkipAndroidBridge", + //"SkipAndroidBridgeTestsSupport", .product(name: "SkipBridgeKt", package: "skip-bridge"), .product(name: "SkipTest", package: "skip"), ], plugins: [.plugin(name: "skipstone", package: "skip")]), diff --git a/Sources/SkipAndroidBridge/AndroidBridgeBootstrap.swift b/Sources/SkipAndroidBridge/AndroidBridgeBootstrap.swift index 044170e..4c07da9 100644 --- a/Sources/SkipAndroidBridge/AndroidBridgeBootstrap.swift +++ b/Sources/SkipAndroidBridge/AndroidBridgeBootstrap.swift @@ -16,6 +16,14 @@ import SkipAndroidSDKBridge #elseif canImport(OSLog) @_exported import OSLog #endif +#if canImport(AndroidLooper) +@_exported import AndroidLooper +#endif +#if canImport(AndroidChoreographer) +@_exported import AndroidChoreographer +#endif + +import Dispatch fileprivate let logger: Logger = Logger(subsystem: "SkipAndroidBridge", category: "AndroidBridgeToKotlin") #endif @@ -39,6 +47,14 @@ public class AndroidBridgeBootstrap { if androidBridgeInit == true { return } defer { androidBridgeInit = true } + precondition(Thread.isMainThread, "initAndroidBridge must be called from main thread") + + #if os(Android) + // set up the main thread looper; note that we do not need to do this for Robolectric, since it uses the standard CFRunLoop + AndroidLooper.setupMainLooper() + AndroidChoreographer.setupMainChoreographer() + #endif + let start = Date.now logger.log("initAndroidBridge started") guard let context = AndroidContext.shared as AndroidContext? else { diff --git a/Sources/SkipAndroidBridge/AndroidBridgeMainActor.swift b/Sources/SkipAndroidBridge/AndroidBridgeMainActor.swift new file mode 100644 index 0000000..39cb23f --- /dev/null +++ b/Sources/SkipAndroidBridge/AndroidBridgeMainActor.swift @@ -0,0 +1,15 @@ +// Copyright 2024 Skip +// +// This is free software: you can redistribute and/or modify it +// under the terms of the GNU Lesser General Public License 3.0 +// as published by the Free Software Foundation https://fsf.org + +#if !SKIP +#if os(Android) +import AndroidLooper + +// this mechanism overrides the MainActor with an AndroidMainActor that uses the Looper API to enqueue events +public typealias MainActor = AndroidMainActor + +#endif +#endif diff --git a/Sources/SkipAndroidBridgeTestsSupport/Skip/skip.yml b/Sources/SkipAndroidBridgeTestsSupport/Skip/skip.yml new file mode 100644 index 0000000..89267ca --- /dev/null +++ b/Sources/SkipAndroidBridgeTestsSupport/Skip/skip.yml @@ -0,0 +1,4 @@ +# Configuration file for https://skip.tools project +skip: + mode: 'native' + bridging: true diff --git a/Sources/SkipAndroidBridgeTestsSupport/SkipAndroidBridgeTestsSupport.swift b/Sources/SkipAndroidBridgeTestsSupport/SkipAndroidBridgeTestsSupport.swift new file mode 100644 index 0000000..44c28f2 --- /dev/null +++ b/Sources/SkipAndroidBridgeTestsSupport/SkipAndroidBridgeTestsSupport.swift @@ -0,0 +1,18 @@ +// Copyright 2024 Skip +// +// This is free software: you can redistribute and/or modify it +// under the terms of the GNU Lesser General Public License 3.0 +// as published by the Free Software Foundation https://fsf.org + +import Foundation +import SkipAndroidBridge + +@MainActor public class MainActorSample { + public init() { + } + + public func callMainActorFunction() -> String { + assert(Thread.isMainThread) + return "ABC" + } +} diff --git a/Tests/SkipAndroidBridgeTests/AndroidBridgeTests.swift b/Tests/SkipAndroidBridgeTests/AndroidBridgeTests.swift index 13bf67c..dc5f737 100644 --- a/Tests/SkipAndroidBridgeTests/AndroidBridgeTests.swift +++ b/Tests/SkipAndroidBridgeTests/AndroidBridgeTests.swift @@ -9,6 +9,7 @@ import OSLog import Foundation import SkipBridgeKt @testable import SkipAndroidBridge +//import SkipAndroidBridgeTestsSupport let logger: Logger = Logger(subsystem: "SkipAndroidBridge", category: "Tests") @@ -17,6 +18,7 @@ final class AndroidBridgeTests: XCTestCase { override func setUp() { #if SKIP loadPeerLibrary(packageName: "skip-android-bridge", moduleName: "SkipAndroidBridge") + //try! AndroidBridgeBootstrap.initAndroidBridge() #endif } @@ -56,7 +58,11 @@ final class AndroidBridgeTests: XCTestCase { // make sure we can read and write to the filesDir try "ABC".write(to: filesDir.appendingPathComponent("test.txt"), atomically: true, encoding: .utf8) try "XYZ".write(to: cacheDir.appendingPathComponent("test.txt"), atomically: true, encoding: .utf8) - - try AndroidBridgeBootstrap.initAndroidBridge() } + +// func testMainActor() async { +// let mainActorSample = await MainActorSample() +// let result = await mainActorSample.callMainActorFunction() +// XCTAssertEqual("ABC", result) +// } }