Reputation: 6992
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?
Upvotes: 76
Views: 126644
Reputation: 1
Project File >
Build Settings >
Other Linker Flags >
add -ld_classic
It's cuz of a new compiler from Apple Xcode 15
Use last version of compiler using a linker flag
Upvotes: 0
Reputation: 91
For me non of the above solutions worked because I use Carthage dependancy manager.
This fixed the issue for me:
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.
Upvotes: 0
Reputation: 357
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.
Upvotes: 0
Reputation: 683
I don't know what the actual issue is but after installing the app on a different simulator. Worked for me :).
Upvotes: 0
Reputation: 21
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.
Upvotes: 0
Reputation: 8500
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'
Upvotes: 3
Reputation: 274
I was facing the same problem and in my case just defining "use_frameworks!" as global resolved the problem.
After Podfile changes you should,
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
Upvotes: 4
Reputation: 295
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
Upvotes: 8
Reputation: 579
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.
Upvotes: 8
Reputation: 1364
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
Upvotes: 11
Reputation: 1364
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
Upvotes: 41