Hints and patterns for using swift-java on Android.
When using Swift from an Android app, work submitted to Swift's main executor is not executed by default, so integration with the host environment is needed. Without it, asynchronous work that switches to the main actor may stop making progress. This applies both to public Swift APIs called directly from Java and to uses of the main actor inside a Swift library.
The swift-android-native implementation is one example of this integration. It can run Swift's main actor on Android's UI thread.
Since swift-java uses JNI and reflection APIs, the R8 optimizer must be told to keep the wrapped types. Otherwise they are stripped from the APK/AAR and calls into them crash at runtime.
The SwiftKit Java library already contains a Proguard consumer file,
which is automatically detected by R8, so any org.swift.swiftkit types are already ignored.
However, you must still provide rules for your own types.
For example, if your library's package is org.swift.exampleapp, add the following rules to your proguard file:
-keep class org.swift.exampleapp.** { *; }
-keep interface org.swift.exampleapp.** { *; }
If you are using Core Library Desugaring in your
Android project, enable the AndroidCoreLibraryDesugaring trait so that SwiftJava can find classes that
desugaring relocates to a j$ package (for example, java.util.Optional becomes j$.util.Optional):
let package = Package(
name: "MyProject",
products: [
// ...
],
dependencies: [
.package(url: "https://github.com/swiftlang/swift-java", from: "...", traits: ["AndroidCoreLibraryDesugaring"]),
],
targets: [
// ...
]
)Enabling the trait does not bake a fixed java.* -> j$.* mapping into the binary. Instead it enables a
runtime probe: the first time SwiftJava needs to resolve a desugarable class, it checks -- once per class name,
cached for the life of the process -- whether the j$ name is actually loadable in this process, and falls back
to the original java.* name otherwise.
When wrapping the Android SDK (android.jar) you can provide the optional --android-api-version-file option to swift-java wrap-java.
This file contains availability information for Android APIs, which swift-java will take into account when generating the wrappers.
All APIs will therefore be annotated with their respective availability, expressed using Swift's @available:
#if compiler(>=6.3)
@available(Android 3 /* Cupcake */, *)
@available(Android, deprecated: 29, message: "Deprecated in Android API 29 /* Android 10 */")
#endif
@JavaClass("com.example.OldVersionedClass")
open class OldVersionedClass: JavaObject {
}Annotations are generated for the "since", "deprecated" and "removed" attributes.
Note: To use Android platform availability you must use at least Swift 6.3, which introduced the
Androidplatform.