diff --git a/README.md b/README.md index 02053fc..795ccf3 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,45 @@ # Swift Android Examples -This repository contains example applications that demonstrate how to use the [Swift SDK for Android](https://swift.org/install). Each example showcases different integration patterns for Swift on Android. +This repository contains example applications that demonstrate how to use the +[Swift SDK for Android](https://swift.org/install). Each example showcases different +integration patterns for Swift on Android. -## Available Examples +## Getting Started -Examples using [swift-java](https://github.com/swiftlang/swift-java) to generate necessary Swift/Java bridging: -- **[hello-swift-java](hello-swift-java/)** - application that demonstrates how to call Swift code from an Android app with automatically generated Java wrappers and JNI code using [swift-java](https://github.com/swiftlang/swift-java). +The **[hello-swift-java](hello-swift-java/)** example is the recommended +approach for integrating Swift into Android applications. This example +demonstrates how to build a Swift library that can be seamlessly called from +Kotlin/Java code using [swift-java](https://github.com/swiftlang/swift-java), +which automatically generates the necessary Java wrappers and JNI bindings for +you. + +The example consists of two components: + +- **hashing-lib**: A Swift package that uses `swift-crypto` to provide SHA256 + hashing functionality +- **hashing-app**: A Kotlin Android app using Jetpack Compose UI that calls the + Swift library + +When you press the "Hash" button in the app, it calls directly from Kotlin into +Swift code to compute a SHA256 hash—no manual JNI code required. The swift-java +tooling handles all the bridging automatically, making it easy to expose Swift +APIs to your Android application with type safety and minimal boilerplate. + +This approach is ideal for production Android applications where you want to write +business logic, algorithms, or libraries in Swift, while maintaining a standard +Kotlin/Java frontend. + +## Other Examples + +For those who want to explore alternative integration patterns or understand +lower-level details of how Swift integrates with Android, there are a number of +more manual examples provided. + +An example of a purely native Swift app, which calls no Java APIs: +- **[native-activity](native-activity/)** - complete native Android activity with +OpenGL ES rendering written entirely in Swift. Examples using raw JNI, without generated bridging sources: - **[hello-swift-raw-jni](hello-swift-raw-jni/)** - basic Swift integration that calls a Swift function. - **[hello-swift-raw-jni-callback](hello-swift-raw-jni-callback/)** - demonstrates bidirectional communication with Swift timer callbacks updating Android UI. - **[hello-swift-raw-jni-library](hello-swift-raw-jni-library/)** - shows how to package Swift code as a reusable Android library component. -- **[native-activity](native-activity/)** - complete native Android activity with OpenGL ES rendering written entirely in Swift. diff --git a/hello-swift-java/README.md b/hello-swift-java/README.md index 70d8830..f913387 100644 --- a/hello-swift-java/README.md +++ b/hello-swift-java/README.md @@ -48,7 +48,7 @@ Now restart the terminal so that the `sdk` utility is added to your path, and th ```bash sdk install java 25.0.1-amzn --use # only in order to publish swift-java artifacts locally -export JAVA_HOME="${HOME}//.sdkman/candidates/java/current" +export JAVA_HOME="${HOME}/.sdkman/candidates/java/current" ``` Next, let's prepare and publish the swift-java support libraries: @@ -83,4 +83,58 @@ Next, let's prepare and publish the swift-java support libraries: ./gradlew :hello-swift-java-hashing-lib:assembleRelease ``` -3. After a successful build, the Android library will be located at `swift-java-hashing-example/hashing-lib/build/outputs/aar/hashing-lib-release.aar`. +2. After a successful build, the Android library will be located at `swift-java-hashing-example/hashing-lib/build/outputs/aar/hashing-lib-release.aar`. + + +## Exploring `swift-java` +We encourage exploring `swift-java` by writing Swift code to the file `hello-swift-java/hashing-lib/Sources/SwiftHashing/SwiftHashing.swift` and building the `hashing-app` target.\ +> [!TIP] +> The list of supported features can be found at:\ +> https://swiftpackageindex.com/swiftlang/swift-java/main/documentation/swiftjavadocumentation/supportedfeatures#JExtract-calling-Swift-from-Java\ + + +Once the `hashing-app` build completes, the newly generated Java code will be available to use in the Android project. + +## Troubleshooting + +> Most of these issues are temporary and will be resolved in the future as the swift-java project matures and automates more of the build steps. + + +### New Swift Code is not available in Java once Android build completes: +If after adding new code to `SwiftHashing.swift` the same is not available on Android after a build, please delete the swift `.build` folder located at `hello-swift-java/hashing-lib/.build` (Attention to the dot before the folder name `.build`) + +Build the Android `hashing-app` target again and the Swift API should be available in Java. +If the code is still not available, the `swift-java` project might not support the specific Swift code. Please make sure the feature is listed as supported in the [Supported Features](https://swiftpackageindex.com/swiftlang/swift-java/main/documentation/swiftjavadocumentation/supportedfeatures#JExtract-calling-Swift-from-Java) documentation. + +#### Crash: Library not found +If the code added to `SwiftHashing.swift` introduces a new library dependency, forexample `import Observation`, and the app crashes with the following error:\ +`Caused by: java.lang.UnsatisfiedLinkError: dlopen failed: library "libswiftObservation.so" not found` +1. Locate the file `build.gradle (Module :hello-swift-java-hashing-lib` +2. Locate the defined list of libraries `def swiftRuntimeLibs` +3. Include the library specified in the error message + +In the example log above, `Observation` cannot be found. This is how the `swiftRuntimeLibs` show look after including the missing library. +``` +def swiftRuntimeLibs = [ + "swiftCore", + "swift_Concurrency", + "swift_StringProcessing", + "swift_RegexParser", + "swift_Builtin_float", + "swift_math", + "swiftAndroid", + "dispatch", + "BlocksRuntime", + "swiftSwiftOnoneSupport", + "swiftDispatch", + "Foundation", + "FoundationEssentials", + "FoundationInternationalization", + "_FoundationICU", + "swiftSynchronization", + "swiftObservation", <===================== NEW LIBRARY +] +``` + + + diff --git a/hello-swift-java/hashing-app/build.gradle.kts b/hello-swift-java/hashing-app/build.gradle.kts index bddfdc0..e087f09 100644 --- a/hello-swift-java/hashing-app/build.gradle.kts +++ b/hello-swift-java/hashing-app/build.gradle.kts @@ -28,11 +28,11 @@ android { } } compileOptions { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } kotlinOptions { - jvmTarget = "11" + jvmTarget = "17" } buildFeatures { compose = true @@ -48,6 +48,7 @@ dependencies { implementation(libs.androidx.ui.graphics) implementation(libs.androidx.ui.tooling.preview) implementation(libs.androidx.material3) + implementation("org.swift.swiftkit:swiftkit-core:1.0-SNAPSHOT") implementation(project(":hello-swift-java-hashing-lib")) testImplementation(libs.junit) androidTestImplementation(libs.androidx.junit) @@ -56,4 +57,4 @@ dependencies { androidTestImplementation(libs.androidx.ui.test.junit4) debugImplementation(libs.androidx.ui.tooling) debugImplementation(libs.androidx.ui.test.manifest) -} \ No newline at end of file +} diff --git a/hello-swift-java/hashing-app/src/main/java/com/example/hashingapp/MainActivity.kt b/hello-swift-java/hashing-app/src/main/java/com/example/hashingapp/MainActivity.kt index 3ac0265..24cd5c8 100644 --- a/hello-swift-java/hashing-app/src/main/java/com/example/hashingapp/MainActivity.kt +++ b/hello-swift-java/hashing-app/src/main/java/com/example/hashingapp/MainActivity.kt @@ -24,7 +24,6 @@ import androidx.compose.foundation.layout.padding import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Scaffold import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.material3.TextField @@ -33,9 +32,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp import com.example.hashingapp.ui.theme.HashingAppTheme import com.example.swifthashing.SwiftHashing diff --git a/hello-swift-java/hashing-lib/Package.swift b/hello-swift-java/hashing-lib/Package.swift index a28d581..0295118 100644 --- a/hello-swift-java/hashing-lib/Package.swift +++ b/hello-swift-java/hashing-lib/Package.swift @@ -60,8 +60,10 @@ let package = Package( .product(name: "Crypto", package: "swift-crypto"), .product(name: "SwiftJava", package: "swift-java"), .product(name: "CSwiftJavaJNI", package: "swift-java"), + .product(name: "SwiftJavaRuntimeSupport", package: "swift-java"), ], swiftSettings: [ + .swiftLanguageMode(.v5), .unsafeFlags(["-I\(javaIncludePath)", "-I\(javaPlatformIncludePath)"], .when(platforms: [.macOS, .linux, .windows])) ], plugins: [ diff --git a/hello-swift-java/hashing-lib/build.gradle b/hello-swift-java/hashing-lib/build.gradle index 818f756..f0170c9 100644 --- a/hello-swift-java/hashing-lib/build.gradle +++ b/hello-swift-java/hashing-lib/build.gradle @@ -14,11 +14,11 @@ android { defaultConfig { minSdkVersion 28 } -} -java { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } } dependencies { diff --git a/hello-swift-raw-jni-callback/README.md b/hello-swift-raw-jni-callback/README.md index 065c995..8ca0413 100644 --- a/hello-swift-raw-jni-callback/README.md +++ b/hello-swift-raw-jni-callback/README.md @@ -16,8 +16,8 @@ The project consists of: Before you can build and run this project, you need to have the following installed: * **Java Development Kit (JDK)**: We recommend using JDK 21. Ensure the `JAVA_HOME` environment variable is set to your JDK installation path. -* **Swiftly**: You need to install [Swiftly](https://www.swift.org/install/) -* **Swift SDK for Android**: You need to install the [Swift SDK for Android](https://swift.org/install) +* **Swiftly**: You need to install [Swiftly](https://www.swift.org/swiftly/documentation/swiftly/getting-started/) +* **Swift SDK for Android**: You need to install the [Swift SDK for Android](https://www.swift.org/documentation/articles/swift-sdk-for-android-getting-started.html) ## Running the example diff --git a/hello-swift-raw-jni-library/README.md b/hello-swift-raw-jni-library/README.md index 564d8b6..10f6e9b 100644 --- a/hello-swift-raw-jni-library/README.md +++ b/hello-swift-raw-jni-library/README.md @@ -14,8 +14,8 @@ The project consists of: Before you can build and run this project, you need to have the following installed: * **Java Development Kit (JDK)**: We recommend using JDK 25. Ensure the `JAVA_HOME` environment variable is set to your JDK installation path. -* **Swiftly**: You need to install [Swiftly](https://www.swift.org/install/) -* **Swift SDK for Android**: You need to install the [Swift SDK for Android](https://swift.org/install) +* **Swiftly**: You need to install [Swiftly](https://www.swift.org/swiftly/documentation/swiftly/getting-started/) +* **Swift SDK for Android**: You need to install the [Swift SDK for Android](https://www.swift.org/documentation/articles/swift-sdk-for-android-getting-started.html) ## Building the library diff --git a/hello-swift-raw-jni/README.md b/hello-swift-raw-jni/README.md index f1a1fe1..330fba2 100644 --- a/hello-swift-raw-jni/README.md +++ b/hello-swift-raw-jni/README.md @@ -16,8 +16,8 @@ The project consists of: Before you can build and run this project, you need to have the following installed: * **Java Development Kit (JDK)**: We recommend using JDK 25. Ensure the `JAVA_HOME` environment variable is set to your JDK installation path. -* **Swiftly**: You need to install [Swiftly](https://www.swift.org/install/) -* **Swift SDK for Android**: You need to install the [Swift SDK for Android](https://swift.org/install) +* **Swiftly**: You need to install [Swiftly](https://www.swift.org/swiftly/documentation/swiftly/getting-started/) +* **Swift SDK for Android**: You need to install the [Swift SDK for Android](https://www.swift.org/documentation/articles/swift-sdk-for-android-getting-started.html) ## Running the example diff --git a/native-activity/README.md b/native-activity/README.md index 4d76082..e4134e3 100644 --- a/native-activity/README.md +++ b/native-activity/README.md @@ -16,8 +16,8 @@ The project consists of: Before you can build and run this project, you need to have the following installed: * **Java Development Kit (JDK)**: We recommend using JDK 25. Ensure the `JAVA_HOME` environment variable is set to your JDK installation path. -* **Swiftly**: You need to install [Swiftly](https://www.swift.org/install/) -* **Swift SDK for Android**: You need to install the [Swift SDK for Android](https://swift.org/install) +* **Swiftly**: You need to install [Swiftly](https://www.swift.org/swiftly/documentation/swiftly/getting-started/) +* **Swift SDK for Android**: You need to install the [Swift SDK for Android](https://www.swift.org/documentation/articles/swift-sdk-for-android-getting-started.html) * **Device/emulator with OpenGL ES support** ## Running the example