Skip to content

Duplicate interface definition in SKIE-generated Objective-C header when multiple iOS framework link tasks run in parallel #177

Description

@anguyenMedallia

Summary

We are seeing intermittent build failures in CI where SKIE-generated Objective‑C headers contain duplicate @interface definitions (e.g., __SkieLambdaErrorType and __SkieUnknownCInteropFrameworkErrorType). This appears when multiple linkFrameworkIos tasks run in parallel across multiple KMP modules/targets. The failure is resolved when those iOS link tasks are serialized.

Environment

• OS: macOS (CI agent)
• Xcode: 26.2.0
• Kotlin: 2.2.21
• Gradle: 8.12
• SKIE plugin: touchlab.skie
• Build context: multiple KMP modules (3+) with iOS targets (iosArm64, iosX64, iosSimulatorArm64) AND iOS native projects
• Gradle parallel: org.gradle.parallel=true

Observed Failure

CI fails during iOS framework linking with duplicate interface definitions in a SKIE-generated header:
/Users/jenkins/workspace/.../core-project/core/build/skie/binaries/debugFramework/DEBUG/iosArm64/cache/kotlin-framework/SDKCoreInternal.framework/Headers/SDKCoreInternal.h:40:1: error: duplicate interface definition for class '__SkieLambdaErrorType'
@interface __SkieLambdaErrorType : NSObject
...
/Users/jenkins/workspace/.../SDKCoreInternal.h:50:1: error: duplicate interface definition for class '__SkieUnknownCInteropFrameworkErrorType'
@interface __SkieUnknownCInteropFrameworkErrorType : NSObject
Suspected Root Cause SKIE cache generation appears to be non‑thread‑safe when multiple linkFrameworkIos tasks run concurrently. Multiple modules/targets are writing into the same SKIE cache folder under build/skie/.../cache/kotlin-framework/..., resulting in duplicated interface blocks inside the generated header.

Reproduction (approximate)

  1. Have a multi‑module KMP project where multiple modules apply the touchlab.skie plugin, and also a native iOS module directly integrated with a Xcode Build Script phase which calls ./gradlew :<Shared module name>:embedAndSignAppleFrameworkForXcode
  2. Enable Gradle parallel execution: org.gradle.parallel=true.
  3. Trigger iOS framework link tasks in parallel (e.g., linkDebugFrameworkIosArm64, linkDebugFrameworkIosX64, linkDebugFrameworkIosSimulatorArm64, embedAndSignAppleFrameworkForXcode) across modules. See https://kotlinlang.org/docs/multiplatform/multiplatform-direct-integration.html#connect-the-framework-to-your-project
  4. CI intermittently fails with duplicate interface definitions inside SKIE-generated headers.

Expected Behavior

SKIE‑generated headers are deterministic and free of duplicate @interface definitions, even when multiple iOS framework link tasks run in parallel across modules.

Actual Behavior

Intermittent failures with duplicate interface definitions (see log snippet above).
Workaround / Mitigation We avoided the failure by serializing only the leaf iOS framework link tasks (per target) across all modules, while keeping overall Gradle parallelism enabled. This suggests the issue is due to concurrent SKIE cache writes.

Questions for Maintainers

• Is SKIE’s cache generation for iOS framework linking intended to be safe under parallel execution across modules?
• Is there a recommended configuration to isolate SKIE caches per task/module/target to avoid concurrent writes?
• Would SKIE accept a PR to namespace cache output by module/target to prevent cross‑task collisions?
Additional Context We also noticed this happens only on CI with higher concurrency; local builds are less likely to fail, which further points to a race condition.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions