Freelancing for Pale Blue

Looking for flexible work opportunities that fit your schedule?


How to setup Xcode Cloud with Kotlin Multiplatform (KMM/KMP)

iOS Aug 2, 2024

When starting with Kotlin Multiplatform, it can get a little bit overwhelming with all the new changes you have to make to your project. You also might be tempted to move all the CI/CD logic into Github actions (centralized for iOS and Android) but it's a pity to leave the generous 25 hours of allowance that Xcode Cloud offers.

Here's a straightforward guide to enabling Xcode Cloud to build your Kotlin Multiplatform project:

Step 1: Project or Workspace Configuration

Set the project path for your workflow to include the iOSApp folder. This can be done either from the Xcode Cloud website or within Xcode:

  1. Go to Xcode -> Integrate -> Manage Workflow -> Select the workflow -> General.
  2. Set the path to include the iOSApp folder (e.g., /iosApp/Example.xcodeproj).

Replace iosApp with the name of your iOS folder in the Kotlin Multiplatform project.

Step 2: CI Script Setup

Create a folder named ci_scripts in the root directory of your iOS project. This folder will contain scripts to run before or after the build process. Inside ci_scripts, create a file named ci_post_clone.sh with the following script (credit):

#!/bin/sh

root_dir=$CI_WORKSPACE_PATH
repo_dir=$CI_PRIMARY_REPOSITORY_PATH
jdk_dir="${CI_DERIVED_DATA_PATH}/JDK"

gradle_dir="${repo_dir}/Common"
cache_dir="${CI_DERIVED_DATA_PATH}/.gradle"

jdk_version="20.0.1"

# Check if we stored gradle caches in DerivedData.
recover_cache_files() {
    
    echo "\nRecover cache files"

    if [ ! -d $cache_dir ]; then
        echo " - No valid caches found, skipping"
        return 0
    fi

    echo " - Copying gradle cache to ${gradle_dir}"
    rm -rf "${gradle_dir}/.gradle"
    cp -r $cache_dir $gradle_dir

    return 0
}

# Install the JDK
install_jdk_if_needed() {

    echo "\nInstall JDK if needed"

    if [[ $(uname -m) == "arm64" ]]; then
        echo " - Detected M1"
        arch_type="macos-aarch64"
    else
        echo " - Detected Intel"
        arch_type="macos-x64"
    fi

    # Location of version / arch detection file.
    detect_loc="${jdk_dir}/.${jdk_version}.${arch_type}"

    if [ -f $detect_loc ]; then
        echo " - Found a valid JDK installation, skipping install"
        return 0
    fi

    echo " - No valid JDK installation found, installing..."

    tar_name="jdk-${jdk_version}_${arch_type}_bin.tar.gz"

    # Download and un-tar JDK to our defined location.
    curl -OL "https://download.oracle.com/java/20/archive/${tar_name}"
    tar xzf $tar_name -C $root_dir

    # Move the JDK to our desired location.
    rm -rf $jdk_dir
    mkdir -p $jdk_dir
    mv "${root_dir}/jdk-${jdk_version}.jdk/Contents/Home" $jdk_dir

    # Some cleanup.
    rm -r "${root_dir}/jdk-${jdk_version}.jdk"
    rm $tar_name

    # Add the detection file for subsequent builds.
    touch $detect_loc

    echo " - Set JAVA_HOME in Xcode Cloud to ${jdk_dir}/Home"

    return 0
}

recover_cache_files
install_jdk_if_needed

This script installs JDK in the environment to compile the shared logic part of KMM. Run the workflow and look for the message in the logs:

Set JAVA_HOME in Xcode Cloud to ${jdk_dir}/Home

Copy the JAVA_HOME value (e.g., /Volumes/workspace/DerivedData/JDK/Home) for the next step.

Step 3: Environment Variable Configuration

In Xcode Cloud, select the workflow and add a new environment variable:

JAVA_HOME=/Volumes/workspace/DerivedData/JDK/Home

Run the workflow, and your app should build successfully.

That's it! Happy coding!

Tags

Great! You've successfully subscribed.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.