Is there any option to alternate between `embedAnd...
# multiplatform
m
Is there any option to alternate between
embedAndSignAppleFrameworkForXcode
and an alternative command that only reuses the previously built framework? Right now it's rebuilding the whole framework every time we press run, even if it's not really needed because we just built it. I'm trying to achieve something like this from our iOS Run Script and switch the schema/configuration when we don't want to rebuild the framework:
Copy code
if [ "$CONFIGURATION" = "Prebuilt framework (debug)" ];
then
    echo "✅ Skipping KMP framework rebuild"
    exit 0
fi

echo "⚙️ Not skipping KMP framework rebuild"

cd "$SRCROOT/../.."
./gradlew :umbrella:embedAndSignAppleFrameworkForXcode
n
In our Xcode project, we use the Based on dependency analysis flag on our "Build KMP Framework" task with the built framework as Output File, and an Input File List generated from another script that runs in a Run Script Phase before the "Build KMP Framework" Run Script Phase. The "Generate Kotlin File List" script looks like this:
Copy code
#!/usr/bin/env bash

set -e

GENERATED_DIR="$SRCROOT/Generated"
XCFILELIST="$GENERATED_DIR/shared.xcfilelist"
echo "Generating list of Kotlin files for iOS in: $XCFILELIST"

mkdir -p "$GENERATED_DIR"

cd "$SRCROOT/../shared/src/commonMain" && find ~+ -type f > "$XCFILELIST"
cd "$SRCROOT/../shared/src/iosMain" && find ~+ -type f >> "$XCFILELIST"
This makes it so Xcode is not even calling out to Gradle to build the KMP framework, if none of the files making up the framework has changed. Since you're using an umbrella framework, you might have to expand the content of the file list to include all of your KMP modules.
m
Thanks for the reply! I'm trying to follow your implementation, but I still see the build pipeline being triggered every time. Are you also using the
embedAndSignAppleFrameworkForXcode
to generate the framework, or something different? Would you mind sharing how you generate the framework and define the right output file? This is how my script and input/output options currently look, but they are not working 🤔
n
We have actually re-reimplemented the old
packForXcode
task like this:
Copy code
val packForXcode by tasks.creating(Sync::class) {
    val mode = System.getenv("KOTLIN_FRAMEWORK_BUILD_TYPE") ?: System.getenv("CONFIGURATION") ?: "DEBUG"
    val sdkName = System.getenv("SDK_NAME") ?: "iphonesimulator18.0"

    val framework = kotlin.targets.getByName<KotlinNativeTarget>("ios").binaries.getFramework(mode)
    val targetDir = layout.buildDirectory.dir("xcode-frameworks")

    group = "build"
    dependsOn(framework.linkTaskProvider)
    inputs.property("mode", mode)
    inputs.property("sdkName", sdkName)

    from({ framework.outputDirectory })
    into(targetDir)
}

tasks.getByName("build").dependsOn(packForXcode)
This uses environment variables passed by Xcode (
CONFIGURATION
,
SDK_NAME
) to determine which iOS target to build for, ensuring seamless transition between running on device and on the iPhone Simulator. The Xcode Build Phases has the side-effect of requiring the presence of the built framework to even consider starting the build, so we use a script with the following code when setting up a new machine (and in CI):
Copy code
#!/usr/bin/env bash

export CONFIGURATION="${CONFIGURATION:=Debug}"
export PLATFORM_NAME="${PLATFORM_NAME:=iphonesimulator}"
export SDK_NAME="${SDK_NAME:=${PLATFORM_NAME}${IPHONE_SDK_VERSION}}"

echo "${_GREEN}[Gradle] Building shared${_RESET}"
echo "  NATIVE_ARCH=$NATIVE_ARCH"
echo "  CONFIGURATION=$CONFIGURATION"
echo "  PLATFORM_NAME=$PLATFORM_NAME"
echo "  SDK_NAME=$SDK_NAME"
echo ""
echo "  $ ./gradlew :shared:packForXcode"
echo ""
./gradlew :shared:packForXcode
echo ""
In CI we pass the same environment variables as we expect the build to use, so we don't end up first building the framework for
Debug
, only to build it for
Release
when building the Xcode application right after.
We just haven't had a reason to change to the newer
embedAndSignAppleFrameworkForXcode
task, so I don't know if that would solve the same issue.
m
Very useful info! Thanks @Nicklas Jensen, I will try to play around with this next week