Class is implemented in both, One of the two will be used. Which one is undefined

IosXcodeCocoapods

Ios Problem Overview


I have an issue with dependencies included in Cocoapods.

I have a Framework project (MyFramework target), which also has App target (MyFrameworkExampleApp). When I try to run the app, I get a console full of errors like so:

> Class PodsDummy_AFNetworking is implemented in both /private/var/containers/Bundle/Application/AD85D7EC-2652-4019-94FB-C799D0FBA69B/MyFrameworkExampleApp.app/Frameworks/MyFramework.framework/MyFramework (0x1019a0438) and /var/containers/Bundle/Application/AD85D7EC-2652-4019-94FB-C799D0FBA69B/MyFrameworkExampleApp.app/MyFrameworkExampleApp (0x10107c558). One of the two will be used. Which one is undefined.

The thing is, the errors come from the libraries included only in MyFramework target

Here are the contents of my podfile:

# Specify platform.
platform :ios, '9.0'

# Let's ignore all warnings from all pods
inhibit_all_warnings!

target 'MyFramework’ do

    # ReactiveCocoa for easier binding between UI and data models.
    pod 'ReactiveCocoa', '< 3.0'

    # ReactiveViewModel for easier handling of active/inactive view models.
    pod 'ReactiveViewModel', '0.3'

    # An Objective-C extension with some nice helpers including @weakify/@strongify.
    pod 'libextobjc', '~> 0.4.1'

    # AFNetworking Security stuff
    pod 'AFNetworking/Security', '~> 2.5.4'

    # KZPropertyMapper to easily map JSON dicts to properties
    pod "KZPropertyMapper"

    # Simple wrapper for KeyChain
    pod 'UICKeyChainStore', '~> 2.0.6'

    # Animated gifs
    pod 'FLAnimatedImage', '~> 1.0'

    # Firebase push notifications
    pod 'Firebase/Core'
    pod 'Firebase/Messaging'

    # Easy image downloading with cache.
    pod 'SDWebImage', '~> 3.7.2'

    # Activity indicator for RBSlider
    pod 'DGActivityIndicatorView'

end

target 'MyFrameworkExampleApp' do

    # Progress indicator
    pod 'MBProgressHUD', '~> 1.0.0'

    # Color picker
    pod 'iOS-Color-Picker'

    # Hockey SDK
    pod 'HockeySDK', '~> 5.0.0'

end

As you can see, App target does not inherit any pods, nor do I have any global pods. What might be the reason for this?

Ios Solutions


Solution 1 - Ios

I don't know the reason, but if you open your app's Pods-[AppName].debug.xcconfig file that cocoapods creates you'll find OTHER_LDFLAGS and you'll see it links to the same frameworks you link in your framework. So if you remove -framework [Duplicated framework] the warning disappears.

Seems to be a cocoapods error

Solution 2 - Ios

I also found another a script someone wrote that fix the bug automatically. It's simply make the same I answered above. Add it to your Podfile:

post_install do |installer|
    sharedLibrary = installer.aggregate_targets.find { |aggregate_target| aggregate_target.name == 'Pods-[MY_FRAMEWORK_TARGET]' }
    installer.aggregate_targets.each do |aggregate_target|
        if aggregate_target.name == 'Pods-[MY_APP_TARGET]'
            aggregate_target.xcconfigs.each do |config_name, config_file|
                sharedLibraryPodTargets = sharedLibrary.pod_targets
                aggregate_target.pod_targets.select { |pod_target| sharedLibraryPodTargets.include?(pod_target) }.each do |pod_target|
                    pod_target.specs.each do |spec|
                        frameworkPaths = unless spec.attributes_hash['ios'].nil? then spec.attributes_hash['ios']['vendored_frameworks'] else spec.attributes_hash['vendored_frameworks'] end || Set.new
                        frameworkNames = Array(frameworkPaths).map(&:to_s).map do |filename|
                            extension = File.extname filename
                            File.basename filename, extension
                        end
                    end
                    frameworkNames.each do |name|
                        if name != '[DUPLICATED_FRAMEWORK_1]' && name != '[DUPLICATED_FRAMEWORK_2]'
                            raise("Script is trying to remove unwanted flags: #{name}. Check it out!")
                        end
                        puts "Removing #{name} from OTHER_LDFLAGS"
                        config_file.frameworks.delete(name)
                    end
                end
            end
            xcconfig_path = aggregate_target.xcconfig_path(config_name)
            config_file.save_as(xcconfig_path)
        end
    end
end

Solution 3 - Ios

https://github.com/CocoaPods/CocoaPods/issues/7126#issuecomment-399395611

post_install do |installer|
    applicationTargets = [
        'Pods-SampleApp',
    ]
    libraryTargets = [
        'Pods-SampleLib',
    ]

    embedded_targets = installer.aggregate_targets.select { |aggregate_target|
        libraryTargets.include? aggregate_target.name
    }
    embedded_pod_targets = embedded_targets.flat_map { |embedded_target| embedded_target.pod_targets }
    host_targets = installer.aggregate_targets.select { |aggregate_target|
        applicationTargets.include? aggregate_target.name
    }

    # We only want to remove pods from Application targets, not libraries
    host_targets.each do |host_target|
        host_target.xcconfigs.each do |config_name, config_file|
            host_target.pod_targets.each do |pod_target|
                if embedded_pod_targets.include? pod_target
                    pod_target.specs.each do |spec|
                        if spec.attributes_hash['ios'] != nil
                            frameworkPaths = spec.attributes_hash['ios']['vendored_frameworks']
                            else
                            frameworkPaths = spec.attributes_hash['vendored_frameworks']
                        end
                        if frameworkPaths != nil
                            frameworkNames = Array(frameworkPaths).map(&:to_s).map do |filename|
                                extension = File.extname filename
                                File.basename filename, extension
                            end
                            frameworkNames.each do |name|
                                puts "Removing #{name} from OTHER_LDFLAGS of target #{host_target.name}"
                                config_file.frameworks.delete(name)
                            end
                        end
                    end
                end
            end
            xcconfig_path = host_target.xcconfig_path(config_name)
            config_file.save_as(xcconfig_path)
        end
    end
end

Solution 4 - Ios

Updated: I write a blog article for my solution: https://medium.com/@GalvinLi/tinysolution-fix-cocoapods-duplicate-implement-warning-5a2e1a505ea8

And a demo project: https://github.com/bestwnh/TinySolution


I got the solution idea from internet but can't find one solution work, so I make a workaround by myself. Maybe the code is a bit long, but it work. Hope it can help someone.

The auto_process_target(,,) is the key function, just change it to fit your project and all should work fine. (Because I use one framework for multi app target, so I make the app target parameter be an array.)

post_install do |installer|

  # you should change the sample auto_process_target method call to fit your project

  # sample for the question
  auto_process_target(['MyFrameworkExampleApp'], 'MyFramework', installer)
  # sample for the multi app use on same framework
  auto_process_target(['exampleiOSApp', 'exampleMacApp'], 'exampleFramework', installer)

end

# the below code no need to modify

def auto_process_target(app_target_names, embedded_target_name, installer)
  words = find_words_at_embedded_target('Pods-' + embedded_target_name,
                                        installer)
  handle_app_targets(app_target_names.map{ |str| 'Pods-' + str },
                     words,
                     installer)
end

def find_line_with_start(str, start)
  str.each_line do |line|
      if line.start_with?(start)
        return line
      end
  end
  return nil
end

def remove_words(str, words)
  new_str = str
  words.each do |word| 
    new_str = new_str.sub(word, '')
  end
  return new_str
end

def find_words_at_embedded_target(target_name, installer)
  target = installer.pods_project.targets.find { |target| target.name == target_name }
  target.build_configurations.each do |config|
    xcconfig_path = config.base_configuration_reference.real_path
    xcconfig = File.read(xcconfig_path)
    old_line = find_line_with_start(xcconfig, "OTHER_LDFLAGS")
    
    if old_line == nil 
      next
    end
    words = old_line.split(' ').select{ |str| str.start_with?("-l") }.map{ |str| ' ' + str }
    return words
  end
end

def handle_app_targets(names, words, installer)
  installer.pods_project.targets.each do |target|
    if names.index(target.name) == nil
      next
    end
    puts "Updating #{target.name} OTHER_LDFLAGS"
    target.build_configurations.each do |config|
      xcconfig_path = config.base_configuration_reference.real_path
      xcconfig = File.read(xcconfig_path)
      old_line = find_line_with_start(xcconfig, "OTHER_LDFLAGS")
      
      if old_line == nil 
        next
      end
      new_line = remove_words(old_line, words)
      
      new_xcconfig = xcconfig.sub(old_line, new_line)
      File.open(xcconfig_path, "w") { |file| file << new_xcconfig }
    end
  end
end

If all work. You will see Update xxxx OTHER_LDFLAGS when you pod install or pod update. Then the warning gone.

enter image description here

Solution 5 - Ios

I was facing the same problem and in my case just defining "use_frameworks!" as global resolved the problem.

After Podfile changes you should,

  1. Clean project
  2. Quit from xcode
  3. Delete derived data
  4. Pod install

similar Podfile to mine below,

workspace 'sample'
# Comment the next line if you don't want to use dynamic frameworks
use_frameworks!
platform :ios, '13.0'

def sample_pods
  pod 'Alamofire' , '~> 5.1.0'
  pod 'RxSwift'
  pod 'CodableAlamofire'
end

def sample_framework_pods
  pod 'Alamofire' , '~> 5.1.0'
  pod 'RxSwift'
  pod 'CodableAlamofire'
end

target 'sample' do
  # Pods for sample
	sample_pods
end

target 'sample_framework' do
  project 'sample_framework/sample_framework.xcodeproj'
  # Pods for sample_framework
  sample_framework_pods
end

Solution 6 - Ios

Most solutions I found solve only the problem if there are shared dependencies between selected two targets or between the main target and other targets. However, we have a much more complicated project with multiple targets where duplicated frameworks could occur between any two targets.

Based on TadeasKriz's code I managed to implement a solution that checks duplicates across all targets and between each other.

def remove_duplicated_frameworks(app_pod_name, installer)
    test_targets = get_test_targets(app_pod_name, installer)
    puts "Detected test targets: #{test_targets}"

    puts "Removing duplicated frameworks from OTHER_LDFLAGS"
    targets = installer.aggregate_targets.select { |x| !test_targets.include?(x.name) }

    # Checks each pair of targets if they have common pods. Duplicates are removed from the first one's xcconfig.
    for i in 0..targets.size-1 do
        target = targets[i]
        remainingAppPodTargets = targets[i+1..targets.size-1].flat_map(&:pod_targets)

        target.xcconfigs.each do |config_name, config_file|
            # Removes all frameworks which exist in other pods
            remainingAppPodTargets
                .flat_map { |pod_target| get_framework_names(pod_target) }
                .each { |framework| config_file.frameworks.delete(framework) }

            # Saves updated xcconfig
            xcconfig_path = target.xcconfig_path(config_name)
            config_file.save_as(xcconfig_path)
        end
    end
end

def get_test_targets(app_pod_name, installer)
    main_target_name = app_pod_name.gsub("Pods-", "")

    installer.aggregate_targets
        .find { |x| x.name == app_pod_name }
        .user_project
        .targets
        .select { |x| x.test_target_type? }
        .flat_map { |x| ["Pods-#{x}", "Pods-#{main_target_name}-#{x}"] }
        .select { |x| installer.aggregate_targets.map(&:name).include?(x) }
        .uniq
end

def get_framework_names(pod_target)
    frameworkNames = pod_target.specs.flat_map do |spec|
        # We should take framework names from 'vendored_frameworks'.
        # If it's not defined, we use 'spec.name' instead.
        #
        # spec.name can be defined like Framework/Something - we take the first part
        # because that's what appears in OTHER_LDFLAGS.
        frameworkPaths = unless spec.attributes_hash['ios'].nil?
            then spec.attributes_hash['ios']['vendored_frameworks']
            else spec.attributes_hash['vendored_frameworks']
            end || [spec.name.split(/\//, 2).first]

        map_paths_to_filenames(frameworkPaths)
    end

    frameworkNames.uniq
end

def map_paths_to_filenames(paths)
    Array(paths).map(&:to_s).map do |filename|
        extension = File.extname filename
        File.basename filename, extension
    end
end

Usage (put this at the end of Podfile):

post_install do |installer|
    remove_duplicated_frameworks('Pods-YourMainTargetName', installer)
end

You can also save the whole solution as a separate file, let's say DuplicatedFrameworksRemover.rb and include it in your Podfile using:

load 'DuplicatedFrameworksRemover.rb'

Solution 7 - Ios

I don't know exactly why this problem occurs in Xcode, but in my case, looking for the library in my project directory and directly copying its name and pasting it in the Bridging-Header file solved my problem and it worked.

Solution 8 - Ios

I don't know what the actual issue is but after installing the app on a different simulator. Worked for me :).

Solution 9 - Ios

Try running on a different Simulator

I came across this error when I was debugging an app crash when running it in the Simulator. After trying all the other advice I could find, I ran the same app on a different Simulator device (e.g. iPhone 12 vs iPhone 11) and the same app built and ran with no issues.

I previously tried clean-installing the app to the flaky Simulator and resetting it with no success. I have no idea what was happening here but I thought I'd share in case it helps anyone else.

Solution 10 - Ios

For me non of the above solutions worked because I use Carthage dependancy manager.

This fixed the issue for me:

  1. under general tab of the project settings, make MyFramewrok.framework -> do not embed
  2. in linked binaries under build phases tab of your project setting, find same framework and instead of 'required' make it 'optional'

The framework is once embeded in the project by one dynamic dependency and once by an static dependency. And it ends up being integrated in the bundle twice. You should be able to pin point which framework you need to make optional and the error shall disappear.

For example if you are using both appcenter crashes and data dog crashes (just an example) in your project, you will end up both those dependencies requiring 'PLCrashReporter.framework'. this is used by 'DataDogCrashReporter' that is a dynamic library and AppCenterCrashReporter that is a static library. I made 'DataDogCrashReporter' an optional and didnt embed it any more.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionmag_zbcView Question on Stackoverflow
Solution 1 - IosDavid BemerguyView Answer on Stackoverflow
Solution 2 - IosDavid BemerguyView Answer on Stackoverflow
Solution 3 - IosMateo MarinView Answer on Stackoverflow
Solution 4 - IosGalvinView Answer on Stackoverflow
Solution 5 - IosFırat YenidünyaView Answer on Stackoverflow
Solution 6 - IosWojciech KulikView Answer on Stackoverflow
Solution 7 - IosJuan GilView Answer on Stackoverflow
Solution 8 - IosM.MuzammilView Answer on Stackoverflow
Solution 9 - IosmintymuzView Answer on Stackoverflow
Solution 10 - Iosdiv-aneView Answer on Stackoverflow