Skip to content

Commit 1dafc36

Browse files
committed
Split into multiple modules to accomodate single-mode bridging changes
1 parent 4455b03 commit 1dafc36

File tree

17 files changed

+257
-185
lines changed

17 files changed

+257
-185
lines changed

Package.swift

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,47 @@ let package = Package(
77
platforms: [.iOS(.v16), .macOS(.v13), .tvOS(.v16), .watchOS(.v9), .macCatalyst(.v16)],
88
products: [
99
.library(name: "SkipAndroidBridge", type: .dynamic, targets: ["SkipAndroidBridge"]),
10+
.library(name: "SkipAndroidBridgeKt", type: .dynamic, targets: ["SkipAndroidBridgeKt"]),
11+
.library(name: "SkipAndroidSDKBridge", type: .dynamic, targets: ["SkipAndroidSDKBridge"]),
1012
],
1113
dependencies: [
12-
.package(url: "https://source.skip.tools/skip.git", from: "1.1.16"),
14+
.package(url: "https://source.skip.tools/skip.git", branch: "main"),
1315
.package(url: "https://source.skip.tools/skip-foundation.git", from: "1.0.0"),
1416
.package(url: "https://source.skip.tools/skip-bridge.git", branch: "main"),
1517
.package(url: "https://source.skip.tools/swift-android-native.git", branch: "main")
1618
],
1719
targets: [
20+
// mode=kotlin
21+
.target(name: "SkipAndroidSDKBridge",
22+
dependencies: [
23+
.product(name: "SkipBridgeKt", package: "skip-bridge"),
24+
.product(name: "SkipFoundation", package: "skip-foundation"),
25+
],
26+
plugins: [.plugin(name: "skipstone", package: "skip")]),
27+
// mode=swift
1828
.target(name: "SkipAndroidBridge", dependencies: [
19-
.product(name: "SkipBridge", package: "skip-bridge"),
20-
.product(name: "SkipFoundation", package: "skip-foundation"),
21-
.product(name: "AndroidNative", package: "swift-android-native"),
29+
"SkipAndroidSDKBridge",
30+
.product(name: "AndroidNative", package: "swift-android-native", condition: .when(platforms: [.android])),
31+
], plugins: [.plugin(name: "skipstone", package: "skip")]),
32+
// mode=kotlin
33+
.target(name: "SkipAndroidBridgeKt",
34+
dependencies: [
35+
"SkipAndroidBridge",
36+
],
37+
plugins: [.plugin(name: "skipstone", package: "skip")]),
38+
39+
.testTarget(name: "SkipAndroidSDKBridgeTests", dependencies: [
40+
"SkipAndroidSDKBridge",
41+
.product(name: "SkipTest", package: "skip"),
2242
], plugins: [.plugin(name: "skipstone", package: "skip")]),
2343
.testTarget(name: "SkipAndroidBridgeTests", dependencies: [
2444
"SkipAndroidBridge",
45+
.product(name: "SkipBridgeKt", package: "skip-bridge"),
46+
.product(name: "SkipTest", package: "skip"),
47+
], plugins: [.plugin(name: "skipstone", package: "skip")]),
48+
.testTarget(name: "SkipAndroidBridgeKtTests", dependencies: [
49+
"SkipAndroidBridgeKt",
50+
.product(name: "SkipBridgeKt", package: "skip-bridge"),
2551
.product(name: "SkipTest", package: "skip"),
2652
], plugins: [.plugin(name: "skipstone", package: "skip")]),
2753
]

README.md

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,19 @@
11
# SkipAndroidBridge
22

3-
This is a free [Skip](https://skip.tools) Swift/Kotlin library project containing the following modules:
43

5-
SkipAndroidBridge
4+
```
5+
ls -la ~/Library/Developer/Xcode/DerivedData/*/SourcePackages/plugins/skipapp-appdroid.output/AppDroid/skipstone/AppDroidModel/src/main/swift/Packages
66
7-
## Building
7+
skip-android-bridge -> ~/Library/Developer/Xcode/DerivedData/Skip-Everything-aqywrhrzhkbvfseiqgxuufbdwdft/SourcePackages/plugins/skip-android-bridge.output/SkipAndroidBridge/skipstone/SkipAndroidBridge/src/main/swift
8+
skip-bridge -> /opt/src/github/skiptools/skip-bridge
9+
skip-foundation -> /opt/src/github/skiptools/skip-foundation
10+
skip-lib -> /opt/src/github/skiptools/skip-lib
11+
skip-model -> /opt/src/github/skiptools/skip-model
12+
skip-unit -> /opt/src/github/skiptools/skip-unit
13+
swift-algorithms -> ~/Library/Developer/Xcode/DerivedData/Skip-Everything-aqywrhrzhkbvfseiqgxuufbdwdft/SourcePackages/checkouts/swift-algorithms
14+
swift-android-native -> /opt/src/github/skiptools/swift-android-native
15+
swift-numerics -> ~/Library/Developer/Xcode/DerivedData/Skip-Everything-aqywrhrzhkbvfseiqgxuufbdwdft/SourcePackages/checkouts/swift-numerics
16+
```
817

9-
This project is a free Swift Package Manager module that uses the
10-
[Skip](https://skip.tools) plugin to transpile Swift into Kotlin.
1118

12-
Building the module requires that Skip be installed using
13-
[Homebrew](https://brew.sh) with `brew install skiptools/skip/skip`.
14-
This will also install the necessary build prerequisites:
15-
Kotlin, Gradle, and the Android build tools.
1619

17-
## Testing
18-
19-
The module can be tested using the standard `swift test` command
20-
or by running the test target for the macOS destination in Xcode,
21-
which will run the Swift tests as well as the transpiled
22-
Kotlin JUnit tests in the Robolectric Android simulation environment.
23-
24-
Parity testing can be performed with `skip test`,
25-
which will output a table of the test results for both platforms.

Sources/SkipAndroidBridge/AndroidBridge.swift

Lines changed: 0 additions & 24 deletions
This file was deleted.

Sources/SkipAndroidBridge/Swift/AndroidBridgeToKotlin.swift renamed to Sources/SkipAndroidBridge/AndroidBridgeToKotlin.swift

Lines changed: 36 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
// under the terms of the GNU Lesser General Public License 3.0
55
// as published by the Free Software Foundation https://fsf.org
66

7+
#if !SKIP
78
import Foundation
9+
import SkipAndroidSDKBridge
810
@_exported import SkipBridge
911
#if canImport(FoundationNetworking)
1012
@_exported import FoundationNetworking
@@ -16,6 +18,7 @@ import Foundation
1618
#endif
1719

1820
fileprivate let logger: Logger = Logger(subsystem: "SkipAndroidBridge", category: "AndroidBridgeToKotlin")
21+
#endif
1922

2023
#if os(Android)
2124
public let isAndroid = true
@@ -26,64 +29,40 @@ public let isAndroid = false
2629

2730
private var androidBridgeInit = false
2831

29-
// SKIP @BridgeToKotlin
3032
public class AndroidBridgeKotlin {
31-
public init() {
32-
}
33-
34-
}
35-
36-
/// Perform all the setup that is needed to get `Foundation` idioms working with Android conventions.
37-
///
38-
/// This includes:
39-
/// - Using the Android certificate store for HTTPS validation
40-
/// - Using the AndroidContext files locations for `FileManager.url`
41-
// SKIP @BridgeToKotlin
42-
func initAndroidBridge() throws {
43-
if androidBridgeInit == true { return }
44-
defer { androidBridgeInit = true }
45-
46-
let start = Date.now
47-
logger.log("initAndroidBridge started")
48-
#if os(Android)
49-
#if !SKIP
50-
try setupFileManagerProperties(context: AndroidContext.shared)
51-
try installSystemCertificates()
52-
#endif
53-
#endif
54-
logger.log("initAndroidBridge done in \(Date.now.timeIntervalSince(start))")
55-
}
56-
57-
58-
// URL.applicationSupportDirectory exists in Darwin's Foundation but not in Android's Foundation
59-
#if os(Android)
60-
extension URL {
61-
public static var applicationSupportDirectory: URL {
62-
try! FileManager.default.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
63-
}
64-
65-
public static var cachesDirectory: URL {
66-
try! FileManager.default.url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
33+
/// Perform all the setup that is needed to get `Foundation` idioms working with Android conventions.
34+
///
35+
/// This includes:
36+
/// - Using the Android certificate store for HTTPS validation
37+
/// - Using the AndroidContext files locations for `FileManager.url`
38+
public static func initAndroidBridge() throws {
39+
if androidBridgeInit == true { return }
40+
defer { androidBridgeInit = true }
41+
42+
let start = Date.now
43+
logger.log("initAndroidBridge started")
44+
guard let context = AndroidContext.shared as AndroidContext? else {
45+
fatalError("no AndroidContext.shared")
46+
}
47+
#if os(Android)
48+
try setupFileManagerProperties(filesDir: context.filesDir, cacheDir: context.cacheDir)
49+
try installSystemCertificates()
50+
#endif
51+
logger.log("initAndroidBridge done in \(Date.now.timeIntervalSince(start)) applicationSupportDirectory=\(URL.applicationSupportDirectory.path)")
6752
}
6853
}
69-
#endif
7054

71-
#if !SKIP
72-
private func setupFileManagerProperties(context: AndroidContext?) throws {
73-
guard let context else { return }
74-
let filesDir = context.filesDir
75-
let cacheDir = context.cacheDir
7655

56+
private func setupFileManagerProperties(filesDir: String, cacheDir: String) throws {
7757
// https://github.com/swiftlang/swift-foundation/blob/main/Sources/FoundationEssentials/FileManager/SearchPaths/FileManager%2BXDGSearchPaths.swift#L46
7858
setenv("XDG_CACHE_HOME", cacheDir, 1)
7959
// https://github.com/swiftlang/swift-foundation/blob/main/Sources/FoundationEssentials/FileManager/SearchPaths/FileManager%2BXDGSearchPaths.swift#L37
8060
setenv("XDG_DATA_HOME", filesDir, 1)
8161

82-
// ensure that we can get the `.applicationSupportDirectory`, which should use the `XDG_DATA_HOME` envrionment
62+
// ensure that we can get the `.applicationSupportDirectory`, which should use the `XDG_DATA_HOME` environment
8363
//let applicationSupportDirectory = URL.applicationSupportDirectory // unavailable on Android
8464
let applicationSupportDirectory = try! FileManager.default.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
8565
logger.debug("setupFileManagerProperties: applicationSupportDirectory=\(applicationSupportDirectory.path)")
86-
8766
}
8867

8968
/// Collects all the certificate files from the Android certificate store and writes them to a single `cacerts.pem` file that can be used by libcurl,
@@ -136,5 +115,16 @@ private func installSystemCertificates(fromCertficateFolders certsFolders: [Stri
136115
//setenv("URLSessionCertificateAuthorityInfoFile", "/system/etc/security/cacerts/", 1) // doesn't work for directories
137116
setenv("URLSessionCertificateAuthorityInfoFile", generatedCacertsURL.path, 1)
138117
}
139-
#endif
140118

119+
// URL.applicationSupportDirectory exists in Darwin's Foundation but not in Android's Foundation
120+
#if os(Android)
121+
extension URL {
122+
public static var applicationSupportDirectory: URL {
123+
try! FileManager.default.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
124+
}
125+
126+
public static var cachesDirectory: URL {
127+
try! FileManager.default.url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
128+
}
129+
}
130+
#endif
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright 2024 Skip
2+
//
3+
// This is free software: you can redistribute and/or modify it
4+
// under the terms of the GNU Lesser General Public License 3.0
5+
// as published by the Free Software Foundation https://fsf.org
6+
7+
import SkipAndroidSDKBridge
8+
9+
// Current limitations on Robolectric testing require us to go through a compiled wrapper in order to perform our
10+
// tests of bridging Kotlin to Swift.
11+
12+
public func testSupport_appendStrings(_ a: String, _ b: String) -> String {
13+
a + b
14+
}
15+
16+
public func testSupport_getJavaSystemProperty(_ name: String) -> String? {
17+
getJavaSystemProperty(name)
18+
}
19+
20+
public func testSupport_getAndroidContext() -> AndroidContext {
21+
AndroidContext.shared
22+
}
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
# Configuration file for https://skip.tools project
22
skip:
3-
mode: 'kotlin'
3+
mode: 'swift'
4+
bridging: true

Sources/SkipAndroidBridge/Swift/AndroidBridgeToKotlinTestSupport.swift

Lines changed: 0 additions & 46 deletions
This file was deleted.
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright 2024 Skip
2+
//
3+
// This is free software: you can redistribute and/or modify it
4+
// under the terms of the GNU Lesser General Public License 3.0
5+
// as published by the Free Software Foundation https://fsf.org
6+
7+
#if SKIP
8+
import Foundation
9+
import OSLog
10+
import SkipAndroidBridge
11+
12+
fileprivate let logger: Logger = Logger(subsystem: "SkipAndroidBridge", category: "AndroidBridge")
13+
14+
/// The entry point from a Kotlin Main.kt into the bridged `SkipAndroidBridge`.
15+
///
16+
/// This class handles the initial Kotlin-side setup of the Swift bridging, which currently
17+
/// just involves loading the specific library and calling the Swift `AndroidBridgeKotlin.initAndroidBridge()`,
18+
/// which will, in turn, perform all the Foundation-level setup.
19+
public class AndroidBridge {
20+
/// This is called at app initialization time, typically from the `Main.kt`
21+
///
22+
/// It will look like: `skip.android.bridge.kt.AndroidBridge.initBridge(this, "AppDroidModel")`
23+
public static func initBridge(app: android.app.Application, _ libraryName: String) throws {
24+
let context = app.applicationContext
25+
ProcessInfo.launch(context)
26+
logger.debug("loading library: \(libraryName)")
27+
try System.loadLibrary(libraryName)
28+
try AndroidBridgeKotlin.initAndroidBridge()
29+
}
30+
}
31+
#endif
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#skip:
2+
# mode: 'kotlin'
3+
# bridging: true

0 commit comments

Comments
 (0)