I'm having issues with a pod called DCIntrospect-ARC which should only work in DEBUG mode. It checks if the DEBUG macro is defined before running. However, it is not defined in the CocoaPods target and even though I am running in debug mode in Xcode it fails to run because the DEBUG macro is not defined.
I can define the DEBUG macro in the podspec using
s.xcconfig = { "GCC_PREPROCESSOR_DEFINITIONS" => '$(inherited) DEBUG=1' }
but this defined DEBUG for all build configurations and not only the DEBUG configuration.
Is this a CocoaPods issue? Shouldn't the DEBUG macro generally be defined for Pods?
Can I work around this in the Podspec file and declare the DEBUG macro in the Debug build configuration only?
you can use the post_install hook in Podfile.
This hook allows you to make any last changes to the generated Xcode project before it is written to disk, or any other tasks you might want to perform.
http://guides.cocoapods.org/syntax/podfile.html#post_install
post_install do |installer_representation|
installer_representation.pods_project.targets.each do |target|
target.build_configurations.each do |config|
if config.name != 'Release'
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= ['$(inherited)', 'DEBUG=1']
end
end
end
end
Thanks to John I completed my custom Podfile script, which also changes the optimization level to zero and enables assertions.
I've got multiple debug configurations (for ACC and PROD), so I needed to update several properties for debugging purposes.
post_install do |installer|
installer.pods_project.build_configurations.each do |config|
if config.name.include?("Debug")
# Set optimization level for project
config.build_settings['GCC_OPTIMIZATION_LEVEL'] = '0'
# Add DEBUG to custom configurations containing 'Debug'
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= ['$(inherited)']
if !config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'].include? 'DEBUG=1'
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] << 'DEBUG=1'
end
end
end
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
if config.name.include?("Debug")
# Set optimization level for target
config.build_settings['GCC_OPTIMIZATION_LEVEL'] = '0'
# Add DEBUG to custom configurations containing 'Debug'
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= ['$(inherited)']
if !config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'].include? 'DEBUG=1'
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] << 'DEBUG=1'
end
# Enable assertions for target
config.build_settings['ENABLE_NS_ASSERTIONS'] = 'YES'
config.build_settings['OTHER_CFLAGS'] ||= ['$(inherited)']
if config.build_settings['OTHER_CFLAGS'].include? '-DNS_BLOCK_ASSERTIONS=1'
config.build_settings['OTHER_CFLAGS'].delete('-DNS_BLOCK_ASSERTIONS=1')
end
end
end
end
end
The accepted answer as of now doesn't work for Swift Pods.
Here is a one-line change to that answer that appears to work for both.
post_install do |installer_representation|
installer_representation.pods_project.targets.each do |target|
target.build_configurations.each do |config|
if config.name != 'Release'
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= ['$(inherited)', 'DEBUG=1']
config.build_settings['OTHER_SWIFT_FLAGS'] = ['$(inherited)', '-DDEBUG']
end
end
end
end
I think the accepted answer is not so right for me. config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= ['$(inherited)', 'DEBUG=1']
||= is used to assign a empty or nil variable, but if the config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] is not empty ?
The array cannot be modified at all. The value is ["POD_CONFIGURATION_PRODUCTION=1", "$(inherited)"] for me.
So I gave the complete anwser.
post_install do |installer_representation|
installer_representation.pods_project.build_configurations.each do |config|
if config.name == 'Release' || config.name == 'Production' || config.name == 'Release-InHouse'
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= []
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] |= ['$(inherited)', 'NDEBUG=1']
end
end
end
||= [] make sure the variable is a valid array. and arrayA |= arrayB means arrayA + arrayB and rid the the repeated element, and then return to arrayA.
Even easier: just ensure you have the DEBUG=1 macro to your GCC_PREPROCESSOR_DEFINITIONS in your project in xCode for debug mode but not release mode. If you add it the the project level (not specific targets) it will be inherited by all targets (debug-test, custom targets, etc). This is set by default on new projects, and generally expected to be there. If you are missing it, that could have broad impact.
If it's still not working, ensure you also have $(inherited) in all your targets for GCC_PREPROCESSOR_DEFINITIONS. CocoaPods and DEBUG both count on that.
Related
My Problem: I defined the preprocessor flag for my Xcode project, but it doesn't work as I used it in my Swift code.
What I've Done: I defined multiple Targets for my Xcode project (and multiple schemes as well) and for each target created a custom build configuration file (.xcconfig file). For each Target, I override SWIFT_ACTIVE_COMPILATION_CONDITIONS flag in my .xcconfig file.
My ca805.debug.xcconfig file is like this:
#include "Pods/Target Support Files/Pods-Common-ca805/Pods-Common-ca805.debug.xcconfig"
#include "base.debug.xcconfig"
// other configs
SWIFT_ACTIVE_COMPILATION_CONDITIONS = CA805
And I assigned each build configuration to each Target in .xcodeproj file.
In Swift code I used this flag like below:
#if CA805
print("I'm using CA805 target specific code")
#else
print("I'm using common code")
#endif
And it printed I'm using common code.
Also, I've tried to add SWIFT_ACTIVE_COMPILATION_CONDITIONS flag using Podfile but it didn't work:
platform :ios, '12.0'
use_frameworks!
abstract_target 'Common' do
project 'Main', 'base.debug' => :debug, 'base.release' => :release
use_frameworks!
# Pods for all targets
# ...
# Define Targets
target 'Main'
target 'ca805'
end
post_install do |installer|
installer.pods_project.targets.each do |target|
if target.name.start_with?('Pods-Common')
target.build_configurations.each do |config|
if !config.name.start_with?('base')
target_name = target.name.split('-')[2].upcase
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= ['$(inherited)', "#{target_name}=1"]
config.build_settings['OTHER_SWIFT_FLAGS'] ||= ['$(inherited)', "-D#{target_name}"]
config.build_settings['SWIFT_ACTIVE_COMPILATION_CONDITIONS'] ||= ['$(inherited)', "#{target_name}"]
end
end
end
end
end
I checked Active Compilation Conditions section in my Target's Build Settings tab and something overrides my flag and I don't know the reason.
Active Compilation Conditions section in my project
Finally, I found a workaround for this issue. I modified .xcconfig files:
post_install do |installer|
installer.pods_project.targets.each do |target|
if target.name.start_with?('Pods-Common')
target.build_configurations.each do |config|
if !config.name.start_with?('base')
target_name = target.name.split('-')[2]
flag = target_name.upcase
puts "[Injecting flag:'#{flag}' to '#{target_name}.#{config.name.downcase}.xcconfig'"
xcconfig_path = config.base_configuration_reference.real_path
# read from xcconfig to build_settings dictionary
build_settings = Hash[*File.read(xcconfig_path).lines.map{|x| x.split(/\s*=\s*/, 2)}.flatten]
# modify FLAGS
build_settings['SWIFT_ACTIVE_COMPILATION_CONDITIONS'] = "$(inherited) #{flag}"
# write build_settings dictionary to xcconfig
File.open(xcconfig_path, 'w') {|file| file.puts ''}
build_settings.each do |key, value|
File.open(xcconfig_path, 'a') {|file| file.puts "#{key} = #{value}"}
end
end
end
end
end
end
Porting apps to mac is finally possible thanks to Catalyst, problem is, numerous pods don't support AppKit.
Most common one would be Crashlytics / Firebase.
In [...]/Pods/Crashlytics/iOS/Crashlytics.framework/Crashlytics(CLSInternalReport.o), building for Mac Catalyst, but linking in object file built for iOS Simulator, file '[...]/Pods/Crashlytics/iOS/Crashlytics.framework/Crashlytics' for architecture x86_64
Since it's a recent topic, I couldn't find doc on how to remove a pod from my build for macOS but keep it for iOS and iPadOS.
It is possible to use in code:
#if !targetEnvironment(macCatalyst)
// Code to exclude for your macOS app
#endif
But that one part of the problem, the other part is to link the pod only for iOS...
What would be the easiest/best course of action when the library is not vital for macOS but still wanted on iOS?
For the best approach of handling unsupported framweorks for Catalyst, you guys should read the solution of Fernando Moya de Rivas, he has a github with a solution here with more up to date information.
He basically said you just need to define an array of all of the libs you don't want to install on mac osx, like this: ['Fabric', 'Crashlytics', 'Firebase/Core', ...].
Then, your pod file can look simple as this:
# Podfile
load 'remove_unsupported_libraries.rb'
target 'My target' do
use_frameworks!
# Install your pods
pod ...
end
# define unsupported pods
def catalyst_unsupported_pods
['Fabric', 'Crashlytics', 'Firebase/Core', ...]
end
# Remove unsupported pods from your project
post_install do |installer|
installer.configure_support_catalyst
end
Following #ajgryc answer, I was able to make a sleek solution:
In your podfile add
post_install do |installer|
installer.pods_project.targets.each do |target|
if target.name == "Pods-[Name of Project]"
puts "Updating #{target.name} OTHER_LDFLAGS to OTHER_LDFLAGS[sdk=iphone*]"
target.build_configurations.each do |config|
xcconfig_path = config.base_configuration_reference.real_path
xcconfig = File.read(xcconfig_path)
new_xcconfig = xcconfig.sub('OTHER_LDFLAGS =', 'OTHER_LDFLAGS[sdk=iphone*] =')
File.open(xcconfig_path, "w") { |file| file << new_xcconfig }
end
end
end
end
Since Cocoapods 1.8.4
post_install do |installer|
installer.pods_project.targets.each do |target|
if target.name == "Pods-[Name of Project]"
puts "Updating #{target.name} to exclude Crashlytics/Fabric"
target.build_configurations.each do |config|
xcconfig_path = config.base_configuration_reference.real_path
xcconfig = File.read(xcconfig_path)
xcconfig.sub!('-framework "Crashlytics"', '')
xcconfig.sub!('-framework "Fabric"', '')
new_xcconfig = xcconfig + 'OTHER_LDFLAGS[sdk=iphone*] = -framework "Crashlytics" -framework "Fabric"'
File.open(xcconfig_path, "w") { |file| file << new_xcconfig }
end
end
end
end
And then in run script build phase for Fabric:
if [[$ARCHS != "x86_64"]]; then
"${PODS_ROOT}/Fabric/run" [your usual key]
fi
Open your Pods-$projectname.release.xcconfig file in your project's Pods directory, and locate the OTHER_LDFLAGS line. Add [sdk=iphone*] immediately after the variable name (as an example, mine now looks like this):
OTHER_LDFLAGS[sdk=iphone*] = $(inherited) -ObjC -l"MailCore-ios" -l"c++" -l"iconv" -l"resolv" -l"xml2" -l"z"
That conditionally sets the link options only when building iphone variants, preventing the pod from being linked on OSX. Of course as you mention, this needs to be combined with #if !targetEnvironment(macCatalyst) and #endif surrounding the code calling the pod or you'll get linker errors.
This allowed me to get past the same problem. (And in case you're wondering what other cool things besides conditional variables you can add to your .xcconfig files, here's a reference I found: https://pewpewthespells.com/blog/xcconfig_guide.html )
I have an updated solution that works for me with the following Google pods:
pod 'FirebaseUI/Auth'
pod 'FirebaseUI/Phone'
pod 'FirebaseUI/Email'
pod 'Firebase/Auth'
pod 'Firebase/Analytics'
pod 'Fabric', '~> 1.10.2'
pod 'Firebase/Crashlytics'
pod 'Firebase/AdMob'
post_install do |installer|
installer.pods_project.targets.each do |target|
if target.name.start_with?("Pods")
puts "Updating #{target.name} to exclude Crashlytics/Fabric"
target.build_configurations.each do |config|
xcconfig_path = config.base_configuration_reference.real_path
xcconfig = File.read(xcconfig_path)
xcconfig.sub!('-framework "FirebaseAnalytics"', '')
xcconfig.sub!('-framework "FIRAnalyticsConnector"', '')
xcconfig.sub!('-framework "GoogleMobileAds"', '')
xcconfig.sub!('-framework "Google-Mobile-Ads-SDK"', '')
xcconfig.sub!('-framework "GoogleAppMeasurement"', '')
xcconfig.sub!('-framework "Fabric"', '')
new_xcconfig = xcconfig + 'OTHER_LDFLAGS[sdk=iphone*] = $(inherited) -framework "FirebaseAnalytics" -framework "FIRAnalyticsConnector" -framework "GoogleMobileAds" -framework "GoogleAppMeasurement" -framework "GoogleUtilities" "-AppMeasurement" -framework "Fabric"'
File.open(xcconfig_path, "w") { |file| file << new_xcconfig }
end
end
end
end
With cocoapods 1.8.4, I had to adapt #AncAinu's excellent answer as follows:
post_install do |installer|
installer.pods_project.targets.each do |target|
if target.name == "Pods-[Name of Project]"
puts "Updating #{target.name} to exclude Crashlytics/Fabric"
target.build_configurations.each do |config|
xcconfig_path = config.base_configuration_reference.real_path
xcconfig = File.read(xcconfig_path)
xcconfig.sub!('-framework "Crashlytics"', '')
xcconfig.sub!('-framework "Fabric"', '')
new_xcconfig = xcconfig + 'OTHER_LDFLAGS[sdk=iphone*] = -framework "Crashlytics" -framework "Fabric"'
File.open(xcconfig_path, "w") { |file| file << new_xcconfig }
end
end
end
end
Based on what has already been discussed here... here is my solution for projects with multiple targets. It basically is validating the usage of the libs on each target instead of following the target name.
post_install do |installer|
installer.pods_project.targets.each do |target|
# handle non catalyst libs
libs = ["FirebaseAnalytics", "Google-Mobile-Ads-SDK"]
target.build_configurations.each do |config|
xcconfig_path = config.base_configuration_reference.real_path
xcconfig = File.read(xcconfig_path)
values = ""
libs.each { |lib|
if xcconfig["-framework \"#{lib}\""]
puts "Found '#{lib}' on target '#{target.name}'"
xcconfig.sub!(" -framework \"#{lib}\"", '')
values += " -framework \"#{lib}\""
end
}
if values.length > 0
puts "Preparing '#{target.name}' for Catalyst\n\n"
new_xcconfig = xcconfig + 'OTHER_LDFLAGS[sdk=iphone*] = $(inherited)' + values
File.open(xcconfig_path, "w") { |file| file << new_xcconfig }
end
end
end
end
It outputs something like this
Generating Pods project
Found 'Google-Mobile-Ads-SDK' on target 'Pods-TheApp'
Found 'FirebaseAnalytics' on target 'Pods-TheApp'
Preparing 'Pods-TheApp' for Catalyst
Found 'Google-Mobile-Ads-SDK' on target 'Pods-TheApp-TheAppTests'
Found 'FirebaseAnalytics' on target 'Pods-TheApp-TheAppTests'
Preparing 'Pods-TheApp-TheAppTests' for Catalyst
Found 'Google-Mobile-Ads-SDK' on target 'Pods-TheApp-TheApp_iOS_UI_Tests'
Found 'FirebaseAnalytics' on target 'Pods-TheApp-TheApp_iOS_UI_Tests'
Preparing 'Pods-TheApp-TheApp_iOS_UI_Tests' for Catalyst
Found 'Google-Mobile-Ads-SDK' on target 'Pods-TheAppIntentsExtension'
Found 'FirebaseAnalytics' on target 'Pods-TheAppIntentsExtension'
Preparing 'Pods-TheAppIntentsExtension' for Catalyst
Found 'Google-Mobile-Ads-SDK' on target 'Pods-TheAppTodayExtension'
Found 'FirebaseAnalytics' on target 'Pods-TheAppTodayExtension'
Preparing 'Pods-TheAppTodayExtension' for Catalyst
I want to update my pod to let users activating/desactivating a feature.
To do this I've added a Preprocessor macro in my podspec :
s.xcconfig = { 'GCC_PREPROCESSOR_DEFINITIONS' => 'FEATURE=1' }
Now, for a user the right thing to do (of what I've understood) should be to use a post install hook in the podfile to change the definition of FEATURE
post_install do |installer_representation|
installer_representation.project.targets.each do |target|
if target.name == "Pods-MyPod"
target.build_configurations.each do |config|
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= ['$(inherited)', 'FEATURE=0']
end
end
end
end
But it did not do anything at all ... FEATURE value is still 1
Am I doing something wrong ?
EDIT :
I did take a look at this answer, but it did not help.
In the end, I found a working version.
post_install do |installer_representation|
installer_representation.pods_project.targets.each do |target|
if target.name == "Pods-MyPod"
target.build_configurations.each do |config|
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] = ['COCOAPODS=1', 'FEATURE=0']
end
end
end
end
I am trying to add $(PLATFORM_DIR)/Developer/Library/Frameworks path to Specta target Header search paths using post install hook. This obviously is not vital, but it really annoys me to added this path manually each time I do "pod update".
I got to the following script:
post_install do |installer_representation|
installer_representation.project.targets.each do |target|
if target.name == 'Specta'
target.build_configurations.each do |config|
headers = config.build_settings['HEADER_SEARCH_PATHS']
if headers
config.build_settings['HEADER_SEARCH_PATHS'] += ' $(PLATFORM_DIR)/Developer/Library/Frameworks'
end
end
end
end
end
I would be really happy if someone could point me to the right direction, because I am really stuck.
P.S. I already noticed that this path is already added by CocoaPods, but still I am highly interested in how this can be done, since this knowledge can be useful later. Thanks!
Define a method in your Podfile:
def append_header_search_path(target, path)
target.build_configurations.each do |config|
# Note that there's a space character after `$(inherited)`.
config.build_settings["HEADER_SEARCH_PATHS"] ||= "$(inherited) "
config.build_settings["HEADER_SEARCH_PATHS"] << path
end
end
Then call the method in the post_install:
installer.pods_project.targets.each do |target|
if target.name == "Specta"
append_header_search_path(target, "$(PLATFORM_DIR)/Developer/Library/Frameworks")
end
end
Looking at the project file for Cocoapods Pods.xcodeproj it looks like every single scheme for every library (except for Debug scheme) has an optimization level of Fastest, Smallest.
Is there a quick and easy way to change the Podfile or some other configuration of Cocoapods so that the optimization level is None for specific schemes when I use pod install?
I use post_install hook in Podfile. Like this:
post_install do |installer|
installer.pods_project.build_configurations.each do |config|
if config.name.include?("Debug")
config.build_settings['GCC_OPTIMIZATION_LEVEL'] = '0'
config.build_settings['SWIFT_OPTIMIZATION_LEVEL'] = '-Onone'
end
end
end
EDIT
GCC_OPTIMIZATION_LEVEL is ignored for Swift Pods. If you're using Swift Pods you also need to set SWIFT_OPTIMIZATION_LEVEL.
To anybody seeing this using cocoapods 0.38.0 or later:
Use "pods_project" instead of "project"
The previous answers use the word "project" (installer.project.build_configurations.each)
Project was deprecated and replaced with pods_project.
https://github.com/CocoaPods/CocoaPods/issues/3747
post_install do |installer|
installer.pods_project.build_configurations.each do |config|
if config.name.include?("Debug")
config.build_settings['GCC_OPTIMIZATION_LEVEL'] = '0'
end
end
end
If you also want the DEBUG macro added for debugging with assertions enabled, you can use the following script:
post_install do |installer|
installer.project.build_configurations.each do |config|
if config.name.include?("Debug")
# Set optimization level for project
config.build_settings['GCC_OPTIMIZATION_LEVEL'] = '0'
# Add DEBUG to custom configurations containing 'Debug'
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= ['$(inherited)']
if !config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'].include? 'DEBUG=1'
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] << 'DEBUG=1'
end
end
end
installer.project.targets.each do |target|
target.build_configurations.each do |config|
if config.name.include?("Debug")
# Set optimization level for target
config.build_settings['GCC_OPTIMIZATION_LEVEL'] = '0'
# Add DEBUG to custom configurations containing 'Debug'
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= ['$(inherited)']
if !config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'].include? 'DEBUG=1'
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] << 'DEBUG=1'
end
# Enable assertions for target
config.build_settings['ENABLE_NS_ASSERTIONS'] = 'YES'
config.build_settings['OTHER_CFLAGS'] ||= ['$(inherited)']
if config.build_settings['OTHER_CFLAGS'].include? '-DNS_BLOCK_ASSERTIONS=1'
config.build_settings['OTHER_CFLAGS'].delete('-DNS_BLOCK_ASSERTIONS=1')
end
end
end
end
end
Instead of post_install you can add following line to Podfile:
project 'ProjectName', 'DebugConfigurationName' => :debug