JetBrains / compose-multiplatform

Compose Multiplatform, a modern UI framework for Kotlin that makes building performant and beautiful user interfaces easy and enjoyable.
https://jetbrains.com/lp/compose-multiplatform
Apache License 2.0
14.85k stars 1.08k forks source link

iOS build fails with Duplicate Symbols Error when upgraded to 1.6.2 #4723

Open Vaibhav2002 opened 2 weeks ago

Vaibhav2002 commented 2 weeks ago

Describe the bug iOS build fails with Duplicate Symbols error when upgraded from 1.5.11 to 1.6.2

Affected platforms

Versions

Additional context When I upgraded my project from Compose Multiplatform 1.5.11 to 1.6.2, iOS builds start failing with Duplicate Symbols issue, it shows duplicate symbols in 2 of my modules, 2nd module being a grand child module of the first

This is the error

duplicate symbol '_OBJC_METACLASS_$_CMPViewController' in:
    /Users/vaibhav/Projects/Medial/MedialMultiplatform/shared/build/cocoapods/framework/shared.framework/shared[arm64][2665](CMPViewController.o)
    /Users/vaibhav/Projects/Medial/MedialMultiplatform/shared/ui/screens/build/cocoapods/framework/shared_ui_screens.framework/shared_ui_screens[arm64][2665](CMPViewController.o)
duplicate symbol '_OBJC_CLASS_$_CMPViewController' in:
    /Users/vaibhav/Projects/Medial/MedialMultiplatform/shared/build/cocoapods/framework/shared.framework/shared[arm64][2665](CMPViewController.o)
    /Users/vaibhav/Projects/Medial/MedialMultiplatform/shared/ui/screens/build/cocoapods/framework/shared_ui_screens.framework/shared_ui_screens[arm64][2665](CMPViewController.o)
duplicate symbol '_OBJC_IVAR_$_CMPViewController._lifecycleState' in:
    /Users/vaibhav/Projects/Medial/MedialMultiplatform/shared/build/cocoapods/framework/shared.framework/shared[arm64][2665](CMPViewController.o)
    /Users/vaibhav/Projects/Medial/MedialMultiplatform/shared/ui/screens/build/cocoapods/framework/shared_ui_screens.framework/shared_ui_screens[arm64][2665](CMPViewController.o)
duplicate symbol '_OBJC_METACLASS_$_CMPAccessibilityElement' in:
    /Users/vaibhav/Projects/Medial/MedialMultiplatform/shared/build/cocoapods/framework/shared.framework/shared[arm64][2667](CMPAccessibilityElement.o)
    /Users/vaibhav/Projects/Medial/MedialMultiplatform/shared/ui/screens/build/cocoapods/framework/shared_ui_screens.framework/shared_ui_screens[arm64][2667](CMPAccessibilityElement.o)
duplicate symbol '_OBJC_CLASS_$_CMPAccessibilityContainer' in:
    /Users/vaibhav/Projects/Medial/MedialMultiplatform/shared/build/cocoapods/framework/shared.framework/shared[arm64][2666](CMPAccessibilityContainer.o)
    /Users/vaibhav/Projects/Medial/MedialMultiplatform/shared/ui/screens/build/cocoapods/framework/shared_ui_screens.framework/shared_ui_screens[arm64][2666](CMPAccessibilityContainer.o)
duplicate symbol '_OBJC_CLASS_$_CMPAccessibilityElement' in:
    /Users/vaibhav/Projects/Medial/MedialMultiplatform/shared/build/cocoapods/framework/shared.framework/shared[arm64][2667](CMPAccessibilityElement.o)
    /Users/vaibhav/Projects/Medial/MedialMultiplatform/shared/ui/screens/build/cocoapods/framework/shared_ui_screens.framework/shared_ui_screens[arm64][2667](CMPAccessibilityElement.o)
duplicate symbol '_OBJC_METACLASS_$_CMPAccessibilityContainer' in:
    /Users/vaibhav/Projects/Medial/MedialMultiplatform/shared/build/cocoapods/framework/shared.framework/shared[arm64][2666](CMPAccessibilityContainer.o)
    /Users/vaibhav/Projects/Medial/MedialMultiplatform/shared/ui/screens/build/cocoapods/framework/shared_ui_screens.framework/shared_ui_screens[arm64][2666](CMPAccessibilityContainer.o)
duplicate symbol '_OBJC_IVAR_$_CMPAccessibilityElement._inDealloc' in:
    /Users/vaibhav/Projects/Medial/MedialMultiplatform/shared/build/cocoapods/framework/shared.framework/shared[arm64][2667](CMPAccessibilityElement.o)
    /Users/vaibhav/Projects/Medial/MedialMultiplatform/shared/ui/screens/build/cocoapods/framework/shared_ui_screens.framework/shared_ui_screens[arm64][2667](CMPAccessibilityElement.o)
ld: 8 duplicate symbols
clang: error: linker command failed with exit code 1 (use -v to see invocation)

This is my module structure

image
ASalavei commented 2 weeks ago

Hi! We're using CMPUIKit static library in compose framework. You're importing 2 frameworks both have own copy of it. I would recommend to use only one top-level dependency of Multiplatform framework. In your case if shared-ui-screens uses shared-ui as dependency, I would include only the first one into iOS App, and propagate symbols from dependent shared-ui.

Also, you can provide minimum viable repro application sample to let us make close look into it.

Vaibhav2002 commented 2 weeks ago

Hi, shared-ui module uses shared-ui-screens as an implementation dependency Same, shared uses shared-ui as an implementation dependency

shared-ui

kotlin {

    sourceSets {
        commonMain.dependencies {
            implementation(project(":shared:core"))
            implementation(project(":shared:ui:core"))
            implementation(project(":shared:ui:components"))
            implementation(project(":shared:ui:screens"))
            implementation(project(":shared:domain"))

            implementation(libs.bundles.decompose)
            implementation(libs.moko.permissions)
        }
    }
}

shared

kotlin {

    cocoapods {
        podfile = project.file("../iosApp/Podfile")
    }

    sourceSets {
        commonMain.dependencies {
            implementation(project(":shared:core"))
            implementation(project(":shared:ui"))
            implementation(project(":shared:domain"))
            implementation(libs.decompose.core)
            implementation(libs.moko.permissions)
        }

        androidMain.dependencies {
            implementation(androidLibs.activity.compose)
        }
    }
}
ASalavei commented 2 weeks ago

Could you please remove everything valuable from your project and upload minimal sources set that reproduces your issue on GitHub to do proper investigation?

Vaibhav2002 commented 2 weeks ago

@ASalavei Here is the minimal reproducer https://github.com/Medial-Tech/Medial-App-Reproducer/tree/main It builds fine on 1.5.11 but as soon as i upgrade to 1.6.2 it throws "Duplicate symbols" when built from Android Studio

ASalavei commented 1 week ago

Thank you for the reproducer. I can see you're using 4 multiplatform frameworks added via CocoaPods to you iOS app. It's recommended to create umbrella framework to solve this issue. Also you can use export dependencies to propagate symbols to your umbrella framework. As a bonus, you will get reduced application size and faster launch time.

Vaibhav2002 commented 1 week ago

Thank you for the reproducer. I can see you're using 4 multiplatform frameworks added via CocoaPods to you iOS app. It's recommended to create umbrella framework to solve this issue. Also you can use export dependencies to propagate symbols to your umbrella framework. As a bonus, you will get reduced application size and faster launch time.

The pod dependencies in all those child modules which are exported as a separate framework is not actually used in the iOS native side, but only within the shared module.

Do i still need to include those in the umbrella framework? Do i also need to include all other child modules, even those which dont have any pod dependencies?

I only need the files from the parent "shared" module in iOSApp, none from the child modules. Thats why I have used implementation() everywhere instead of api()

As said here

I use pods like GoogleSignIn, Ably, Reachability, Amplitude which are only used in the shared module and not in Swift

ASalavei commented 1 week ago

I only need the files from the parent "shared" module in iOSApp, none from the child modules. Thats why I have used implementation() everywhere instead of api()

Hmm.. your iOS app currently depends on 4 frameworks that was build from different modules. Do you really need these dependencies? If you can remove safely all except one, then the issue will be solved.

Vaibhav2002 commented 1 week ago

I only need the files from the parent "shared" module in iOSApp, none from the child modules. Thats why I have used implementation() everywhere instead of api()

Hmm.. your iOS app currently depends on 4 frameworks that was build from different modules. Do you really need these dependencies? If you can remove safely all except one, then the issue will be solved.

I tried, but then I get Undefined Symbols issue Mentioning some files of Ably, Reachability, Amplitude and other pods

If I mention those pods again in iosApp/PodFile it works, but I dont think this is right as I only use them in shared and are already defined in the child module its used in

Had to add the last 4 pods here to fix the Undefined Symbols issue, but I dont use them directly in Swift Used only inside shared

target 'Medial' do
  use_frameworks!
  platform :ios, '14.1'

  pod 'shared', :path => '../shared'

  pod 'FirebaseMessaging'
  pod 'FirebaseAnalytics'
  pod 'FirebaseCrashlytics'
  pod 'Siren'
  pod 'BranchSDK'
  pod 'Ably'
  pod 'Reachability'
  pod 'GoogleSignIn'
  pod 'Amplitude', '8.17.1'
end
ASalavei commented 1 week ago

It means your iOS app does not know where to find these frameworks. Something (Someone) should tell this info to iOS app via Build Settings > Framework Search Paths. Maybe it can be done using podespec of the shared framework - see script inside each file. Unfortunately I don't know how to do it exactly, but it definitely the way to go. Try to ask in our Slack channels: https://kotlinlang.slack.com/archives/C3PQML5NU https://kotlinlang.slack.com/archives/C0346LWVBJ4

Btw, the Podfile you shared looks OK until you are using dynamic frameworks (use_frameworks! option).

Vaibhav2002 commented 1 week ago

Thanks for the help @ASalavei I will check this out