Search code examples
swiftxcodefirebasestatic-framework

How to avoid duplication of clases in a multiproject workspace using cocoapods


I'm making an app in my spare time and I wanted to ask you a question, my workspace has 3 subprojects: presentation, domain and data, each one is a static framework and has its own pods, and all very cool, but now I have a problem that I don't know how to solve it. I have several firebase dependencies that some of them must be in Presentation and others in Data, the problem comes when executing because it gives me an error :

Class PodsDummy_FirebaseUI is implemented in both /Users/.../Debug-iphonesimulator/PresentationCleanExample.framework/PresentationCleanExample (0x104ffada0) and /Users/.../data/Containers/Bundle/Application/A15FF8B8-7B67-4512-8DFD-04F008175660/CleanExample.app/CleanExample (0x100c6e288). One of the two will be used. Which one is undefined.

So what I've see in the podspec of FirebaseUI/Storage is the following: https://github.com/firebase/FirebaseUI-iOS/blob/master/FirebaseStorageUI.podspec

s.dependency 'Firebase/Storage'
s.dependency 'GTMSessionFetcher/Core', '~> 1.5.0'
s.dependency 'SDWebImage', '~> 5.6'

This makes me think that cocoapods is not resolving the dependency Firebase/Storage properly.

enter image description here

And the problem is that this is only one of the thousands of duplicate classes that I have from Firebase for following the diagram above. I suppose there is a way to inject the Firebase dependency only once and thus avoid class duplication, but I don't know how.

My Podfile:

platform :ios, '13.6'

workspace 'CleanExample'
use_frameworks!

def firebase_pods
  pod 'Firebase/Core', '7.11.0'
  pod 'Firebase/Auth', '7.11.0'
  pod 'Firebase/Firestore', '7.11.0'
  pod 'Firebase/Storage', '7.11.0'
  pod 'FirebaseFirestoreSwift', '7.11.0-beta'
end


target 'CleanExample' do
  project 'CleanExample'
  firebase_pods
  pod 'FirebaseUI/Storage'
end

target 'PresentationCleanExample' do
  project 'PresentationCleanExample/PresentationCleanExample.xcodeproj'
#  firebase_pods
  pod 'FirebaseUI/Storage'
end

target 'DomainCleanExample' do
  project 'DomainCleanExample/DomainCleanExample.xcodeproj'
end

target 'DataCleanExample' do
  project 'DataCleanExample/DataCleanExample.xcodeproj'
  firebase_pods
end

post_install do |installer|
    installer.pods_project.targets.each do |target|
        target.build_configurations.each do |config|
            config.build_settings['EXPANDED_CODE_SIGN_IDENTITY'] = ""
            config.build_settings['CODE_SIGNING_REQUIRED'] = "NO"
            config.build_settings['CODE_SIGNING_ALLOWED'] = "NO"
        end
    end
end

Github

I've uploaded the code https://github.com/rchampa/CleanExample

Questions

  • Is there something wrong with the FirebaseStorageUI.podspec file or is a limitation of cocoapods?

  • Is my Podfile wrong?


Solution

  • Finally I solved my problem inspired by this post https://medium.com/@GalvinLi/tinysolution-fix-cocoapods-duplicate-implement-warning-5a2e1a505ea8 because it let me understand the problem.

    If i understand correctly OTHER_LDFLAGS is adding the frameworks by duplicate.

    Then to avoid the original message problem Class *** is implemented in both I install all the pods in the target 'CleanExample' and remove the OTHER_LDFLAGS line from the following files.

    • Pods-DataCleanExample.debug.xcconfig
    • Pods-DataCleanExample.release.xcconfig
    • Pods-PresentationCleanExample.debug.xcconfig
    • Pods-PresentationCleanExample.release.xcconfig

    Podfile

    # Uncomment the next line to define a global platform for your project
    # platform :ios, '9.0'
    # Inspired by https://medium.com/@GalvinLi/tinysolution-fix-cocoapods-duplicate-implement-warning-5a2e1a505ea8
    
    platform :ios, '13.6'
    
    workspace 'CleanExample'
    use_frameworks!
    
    def data_pods
      pod 'Firebase/Core', '7.11.0'
      pod 'Firebase/Auth', '7.11.0'
      pod 'Firebase/Firestore', '7.11.0'
      pod 'Firebase/Storage', '7.11.0'
      pod 'FirebaseFirestoreSwift', '7.11.0-beta'
    end
    
    def presentation_pods
      pod 'FirebaseUI/Storage', '10.0.2'
      pod 'Firebase/Storage', '7.11.0'
      pod 'lottie-ios'
    end
    
    target 'CleanExample' do
      project 'CleanExample'
      presentation_pods
      data_pods
      
    end
    
    target 'PresentationCleanExample' do
      project 'PresentationCleanExample/PresentationCleanExample.xcodeproj'
      presentation_pods
    end
    
    target 'DomainCleanExample' do
      project 'DomainCleanExample/DomainCleanExample.xcodeproj'
    end
    
    target 'DataCleanExample' do
      project 'DataCleanExample/DataCleanExample.xcodeproj'
      data_pods
    end
    
    post_install do |installer|
      removeOTHERLDFLAGS(['PresentationCleanExample', 'DataCleanExample'], installer)
      installer.pods_project.targets.each do |target|
        target.build_configurations.each do |config|
          config.build_settings['EXPANDED_CODE_SIGN_IDENTITY'] = ""
          config.build_settings['CODE_SIGNING_REQUIRED'] = "NO"
          config.build_settings['CODE_SIGNING_ALLOWED'] = "NO"
        end
      end
    end
    
    
    
    def removeOTHERLDFLAGS(target_names, installer)
      pods_targets_names = target_names.map{ |str| 'Pods-' + str }
      handle_app_targets(pods_targets_names, 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 handle_app_targets(names, installer)
      puts "handle_app_targets"
      puts "names: #{names}"
      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 = ""
          new_xcconfig = xcconfig.sub(old_line, new_line)
          File.open(xcconfig_path, "w") { |file| file << new_xcconfig }
        end
      end
    end
    

    Github

    I've uploaded the code https://github.com/rchampa/CleanExample