From a66ddde768f7dd112c07469de194a882d42fd827 Mon Sep 17 00:00:00 2001 From: "Jeremy W. Sherman" Date: Fri, 1 Nov 2019 13:35:55 -0400 Subject: [PATCH 01/10] Drop \ that broke shell script --- .../03_CreatingSharedCode.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Targeting iOS and Android with Kotlin Multiplatform/03_CreatingSharedCode.md b/Targeting iOS and Android with Kotlin Multiplatform/03_CreatingSharedCode.md index 7880e74..275a2f8 100644 --- a/Targeting iOS and Android with Kotlin Multiplatform/03_CreatingSharedCode.md +++ b/Targeting iOS and Android with Kotlin Multiplatform/03_CreatingSharedCode.md @@ -79,7 +79,7 @@ val packForXcode by tasks.creating(Sync::class) { gradlew.writeText("#!/bin/bash\n" + "export 'JAVA_HOME=${System.getProperty("java.home")}'\n" + "cd '${rootProject.rootDir}'\n" - + "./gradlew \$@\n") + + "./gradlew $@\n") gradlew.setExecutable(true) } } From d176806c4b18e5327d328b7c7ee4e2a2f6fffb12 Mon Sep 17 00:00:00 2001 From: "Jeremy W. Sherman" Date: Fri, 1 Nov 2019 13:36:14 -0400 Subject: [PATCH 02/10] Quote words Fixes shellcheck error SC2068: Double quote array expansions to avoid re-splitting elements. https://www.shellcheck.net/wiki/SC2068 --- .../03_CreatingSharedCode.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Targeting iOS and Android with Kotlin Multiplatform/03_CreatingSharedCode.md b/Targeting iOS and Android with Kotlin Multiplatform/03_CreatingSharedCode.md index 275a2f8..0e9de01 100644 --- a/Targeting iOS and Android with Kotlin Multiplatform/03_CreatingSharedCode.md +++ b/Targeting iOS and Android with Kotlin Multiplatform/03_CreatingSharedCode.md @@ -79,7 +79,7 @@ val packForXcode by tasks.creating(Sync::class) { gradlew.writeText("#!/bin/bash\n" + "export 'JAVA_HOME=${System.getProperty("java.home")}'\n" + "cd '${rootProject.rootDir}'\n" - + "./gradlew $@\n") + + "./gradlew "$@"\n") gradlew.setExecutable(true) } } From 512bf6cfee3027508485d1a55875f67df1bb10c7 Mon Sep 17 00:00:00 2001 From: "Jeremy W. Sherman" Date: Fri, 1 Nov 2019 13:38:56 -0400 Subject: [PATCH 03/10] Fail if cd fails Fix shellcheck warning SC2164: Use 'cd ... || exit' or 'cd ... || return' in case cd fails. https://www.shellcheck.net/wiki/SC2164 --- .../03_CreatingSharedCode.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Targeting iOS and Android with Kotlin Multiplatform/03_CreatingSharedCode.md b/Targeting iOS and Android with Kotlin Multiplatform/03_CreatingSharedCode.md index 0e9de01..0e664ca 100644 --- a/Targeting iOS and Android with Kotlin Multiplatform/03_CreatingSharedCode.md +++ b/Targeting iOS and Android with Kotlin Multiplatform/03_CreatingSharedCode.md @@ -78,7 +78,7 @@ val packForXcode by tasks.creating(Sync::class) { val gradlew = File(targetDir, "gradlew") gradlew.writeText("#!/bin/bash\n" + "export 'JAVA_HOME=${System.getProperty("java.home")}'\n" - + "cd '${rootProject.rootDir}'\n" + + "cd '${rootProject.rootDir}' || exit 1\n" + "./gradlew "$@"\n") gradlew.setExecutable(true) } From 456f2fa2409af6432d698080eceff6aaa20665e6 Mon Sep 17 00:00:00 2001 From: "Jeremy W. Sherman" Date: Fri, 1 Nov 2019 13:41:50 -0400 Subject: [PATCH 04/10] Use multiline string --- .../03_CreatingSharedCode.md | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/Targeting iOS and Android with Kotlin Multiplatform/03_CreatingSharedCode.md b/Targeting iOS and Android with Kotlin Multiplatform/03_CreatingSharedCode.md index 0e664ca..6d6247a 100644 --- a/Targeting iOS and Android with Kotlin Multiplatform/03_CreatingSharedCode.md +++ b/Targeting iOS and Android with Kotlin Multiplatform/03_CreatingSharedCode.md @@ -75,12 +75,17 @@ val packForXcode by tasks.creating(Sync::class) { /// generate a helpful ./gradlew wrapper with embedded Java path doLast { - val gradlew = File(targetDir, "gradlew") - gradlew.writeText("#!/bin/bash\n" - + "export 'JAVA_HOME=${System.getProperty("java.home")}'\n" - + "cd '${rootProject.rootDir}' || exit 1\n" - + "./gradlew "$@"\n") - gradlew.setExecutable(true) + val gradleWrapper = File(targetDir, "gradlew") + gradleWrapper.writeText( + """ + #!/bin/bash + export 'JAVA_HOME=${System.getProperty("java.home")}' + cd '${rootProject.rootDir}' || exit 1 + ./gradlew "$@" + + """.trimIndent() + ) + gradleWrapper.setExecutable(true) } } From 4a18266f92956fe8674f3f0e7bd5575220f38dfb Mon Sep 17 00:00:00 2001 From: "Jeremy W. Sherman" Date: Fri, 1 Nov 2019 13:47:47 -0400 Subject: [PATCH 05/10] Add explanatory comments to build.gradle.kts --- .../03_CreatingSharedCode.md | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/Targeting iOS and Android with Kotlin Multiplatform/03_CreatingSharedCode.md b/Targeting iOS and Android with Kotlin Multiplatform/03_CreatingSharedCode.md index 6d6247a..17ad3ac 100644 --- a/Targeting iOS and Android with Kotlin Multiplatform/03_CreatingSharedCode.md +++ b/Targeting iOS and Android with Kotlin Multiplatform/03_CreatingSharedCode.md @@ -38,6 +38,7 @@ kotlin { else ::iosX64 + // Build SharedCode.framework iOSTarget("ios") { binaries { framework { @@ -46,34 +47,41 @@ kotlin { } } + // Build android.jar jvm("android") + // Use the Kotlin/Native stdlib sourceSets["commonMain"].dependencies { implementation("org.jetbrains.kotlin:kotlin-stdlib-common") } + // Use the Kotlin/JVM stdlib sourceSets["androidMain"].dependencies { implementation("org.jetbrains.kotlin:kotlin-stdlib") } } +// Replace the destination directory with the source files. +// (It's Sync-like-rsync, not Sync-like-atomic.) val packForXcode by tasks.creating(Sync::class) { - val targetDir = File(buildDir, "xcode-frameworks") - - /// selecting the right configuration for the iOS - /// framework depending on the environment - /// variables set by Xcode build + // Let xcodebuild CONFIGURATION pick the kind of framework val mode = System.getenv("CONFIGURATION") ?: "DEBUG" - val framework = kotlin.targets - .getByName("ios") - .binaries.getFramework(mode) + // Tell Gradle "mode" factors into what we're building. + // This way, changing CONFIGURATION will cause a rebuild. inputs.property("mode", mode) + // Tell Gradle to link the framework for that configuration before running packForXcode. + val framework = + kotlin.targets + .getByName("ios") + .binaries.getFramework(mode) dependsOn(framework.linkTask) + // Configure the paths to keep in sync from({ framework.outputDirectory }) + val targetDir = File(buildDir, "xcode-frameworks") into(targetDir) - /// generate a helpful ./gradlew wrapper with embedded Java path + // After syncing, add a script to ensure xcodebuild uses the correct Java when calling gradlew. doLast { val gradleWrapper = File(targetDir, "gradlew") gradleWrapper.writeText( From 8b004295f064be7d414ac00678f8b4dc689c5fb0 Mon Sep 17 00:00:00 2001 From: "Jeremy W. Sherman" Date: Fri, 1 Nov 2019 14:06:49 -0400 Subject: [PATCH 06/10] Correct capitalization of "Xcode" --- .../05_CreatingiOSApp.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Targeting iOS and Android with Kotlin Multiplatform/05_CreatingiOSApp.md b/Targeting iOS and Android with Kotlin Multiplatform/05_CreatingiOSApp.md index cd465fa..e52f11d 100644 --- a/Targeting iOS and Android with Kotlin Multiplatform/05_CreatingiOSApp.md +++ b/Targeting iOS and Android with Kotlin Multiplatform/05_CreatingiOSApp.md @@ -15,7 +15,7 @@ it may require an Apple developer account and a developer certificate. Xcode doe best to get us through the process. Let's make sure we can run the application on the iPhone simulator or device by clicking the play button -from the XCode window title bar. +from the Xcode window title bar. The `step-006` branch of the [github.com/kotlin-hands-on/mpp-ios-android](https://github.com/kotlin-hands-on/mpp-ios-android/tree/step-006) From 12acf0982597b277596db288beda01a123aee0a6 Mon Sep 17 00:00:00 2001 From: "Jeremy W. Sherman" Date: Fri, 1 Nov 2019 14:08:07 -0400 Subject: [PATCH 07/10] Agree task name with earlier step Xcode vs XCode again --- .../06_SettingUpKotlinFramework.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Targeting iOS and Android with Kotlin Multiplatform/06_SettingUpKotlinFramework.md b/Targeting iOS and Android with Kotlin Multiplatform/06_SettingUpKotlinFramework.md index 8273370..a776981 100644 --- a/Targeting iOS and Android with Kotlin Multiplatform/06_SettingUpKotlinFramework.md +++ b/Targeting iOS and Android with Kotlin Multiplatform/06_SettingUpKotlinFramework.md @@ -52,13 +52,13 @@ first position, and add the following code to the shell script text: ```bash cd "$SRCROOT/../../SharedCode/build/xcode-frameworks" -./gradlew :SharedCode:packForXCode -PXCODE_CONFIGURATION=${CONFIGURATION} +./gradlew :SharedCode:packForXcode -PXCODE_CONFIGURATION=${CONFIGURATION} ``` Note, here we use the `$SRCROOT/../..` as the path to the root of our Gradle project. It can depend on how the Xcode project was created. Also, we use the generated -`SharedCode/build/xcode-frameworks/gradlew` script, -the `packForXCode` task generates it. We assumed that the Gradle build is executed at least once, +`SharedCode/build/xcode-frameworks/gradlew` script +generated by the `packForXcode` task. We assume that the Gradle build is executed at least once *before* opening the Xcode project on a new machine. The `step-007` branch of the From bbd170b3b058405199e5fdb6a9e45dadc3ce82f0 Mon Sep 17 00:00:00 2001 From: "Jeremy W. Sherman" Date: Fri, 1 Nov 2019 14:08:51 -0400 Subject: [PATCH 08/10] Drop unneeded system property decl The configuration is ready from the environment, not the property that was declared here. --- .../06_SettingUpKotlinFramework.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Targeting iOS and Android with Kotlin Multiplatform/06_SettingUpKotlinFramework.md b/Targeting iOS and Android with Kotlin Multiplatform/06_SettingUpKotlinFramework.md index a776981..4b55245 100644 --- a/Targeting iOS and Android with Kotlin Multiplatform/06_SettingUpKotlinFramework.md +++ b/Targeting iOS and Android with Kotlin Multiplatform/06_SettingUpKotlinFramework.md @@ -52,7 +52,7 @@ first position, and add the following code to the shell script text: ```bash cd "$SRCROOT/../../SharedCode/build/xcode-frameworks" -./gradlew :SharedCode:packForXcode -PXCODE_CONFIGURATION=${CONFIGURATION} +./gradlew :SharedCode:packForXcode ``` Note, here we use the `$SRCROOT/../..` as the path to the root of our Gradle project. From ad94994b3810d0b066aff47764015d19c761aa01 Mon Sep 17 00:00:00 2001 From: "Jeremy W. Sherman" Date: Fri, 1 Nov 2019 14:09:42 -0400 Subject: [PATCH 09/10] Provide useful error output if someone failed to generate the xcode-frameworks wrapper Since this is probably going to be copy-pasted into a lot of KMP projects, let's try to smoothe that rough edge. --- .../06_SettingUpKotlinFramework.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Targeting iOS and Android with Kotlin Multiplatform/06_SettingUpKotlinFramework.md b/Targeting iOS and Android with Kotlin Multiplatform/06_SettingUpKotlinFramework.md index 4b55245..f82f3d8 100644 --- a/Targeting iOS and Android with Kotlin Multiplatform/06_SettingUpKotlinFramework.md +++ b/Targeting iOS and Android with Kotlin Multiplatform/06_SettingUpKotlinFramework.md @@ -51,7 +51,10 @@ We can open the *Build Phases* tab and click `+` to add the *New Run Script Phas first position, and add the following code to the shell script text: ```bash -cd "$SRCROOT/../../SharedCode/build/xcode-frameworks" +cd "$SRCROOT/../../SharedCode/build/xcode-frameworks" && test -x ./gradlew || { + echo "Initial build of SharedCode:packForXcode must be done from Android Studio in order to record JAVA_HOME." >&2 + exit 1 +} ./gradlew :SharedCode:packForXcode ``` From af204ae22fe71b888d907f9f1d9e2a6ede581c10 Mon Sep 17 00:00:00 2001 From: "Jeremy W. Sherman" Date: Fri, 1 Nov 2019 14:10:10 -0400 Subject: [PATCH 10/10] Simplify example function decls Let's show off that pretty Kotlin syntax. :) --- .../03_CreatingSharedCode.md | 20 +++++++------------ 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/Targeting iOS and Android with Kotlin Multiplatform/03_CreatingSharedCode.md b/Targeting iOS and Android with Kotlin Multiplatform/03_CreatingSharedCode.md index 17ad3ac..fa17fd3 100644 --- a/Targeting iOS and Android with Kotlin Multiplatform/03_CreatingSharedCode.md +++ b/Targeting iOS and Android with Kotlin Multiplatform/03_CreatingSharedCode.md @@ -117,10 +117,8 @@ package com.jetbrains.handson.mpp.mobile expect fun platformName(): String -fun createApplicationScreenMessage() : String { - return "Kotlin Rocks on ${platformName()}" -} - +fun createApplicationScreenMessage() = + "Kotlin Rocks on ${platformName()}" ``` That is the common part. The code to generate the final message. It `expect`s the platform part @@ -132,10 +130,7 @@ Now we need to create the implementation file (and missing directories) for Andr ```kotlin package com.jetbrains.handson.mpp.mobile -actual fun platformName(): String { - return "Android" -} - +actual fun platformName() = "Android" ``` We create a similar implementation file (and missing directories) for the iOS target in the `SharedCode/src/iosMain/kotlin/actual.kt`: @@ -145,11 +140,10 @@ package com.jetbrains.handson.mpp.mobile import platform.UIKit.UIDevice -actual fun platformName(): String { - return UIDevice.currentDevice.systemName() + - " " + - UIDevice.currentDevice.systemVersion -} +actual fun platformName() = + UIDevice.currentDevice.run { + "$systemName $systemVersion" + } ``` Here we can use the [UIDevice](https://developer.apple.com/documentation/uikit/uidevice?language=objc)