objective-cconfiguration-managementcocoapodsxcconfig

Howto define distinct xcconfig parameters in Cocoapod subspecs?


I've developed an iOS project that is a class library dealing with different servers. Only one server is needed per application in which the library is used. The server type is configurable through a preprocessor definition at compile time.

In the podspec of my library, I defined various subspecs for each server like this:

s.name = "ServerLib"
[...]
s.subspec 'ServerA' do |a|
    a.source_files = 'Classes/A/**/*.{h,m}'
    a.xcconfig = { "GCC_PREPROCESSOR_DEFINITIONS" => "$(inherited) ServerA=1" }
end

s.subspec 'ServerB' do |b|
    b.source_files = 'Classes/B/**/*.{h,m}'
    b.xcconfig = { "GCC_PREPROCESSOR_DEFINITIONS" => "$(inherited) ServerB=1" }
end

My application is a multi-customer app with one target per customer. Each customer uses a specific server from the library project. So, my Podfile looks like this:

platform :ios, '5.0'

pod 'MyCore'
pod '3rdPartyLib'

target :'Customer1', :exclusive => true do
    pod 'ServerLib/ServerA'
end

target :'Customer2', :exclusive => true do
    pod 'ServerLib/ServerB'
end

What the pod install script does, is merging ALL flags defined in the subspecs into one value in every pod-customerN.xcconfig file

GCC_PREPROCESSOR_DEFINITIONS = $(inherited) 3RD_PARTY_FLAGS $(inherited) ServerA=1 $(inherited) ServerB=1

Any suggestions how to circumvent this wrong(?) behavior of Cocoapods? As far as I understand the documentation, subspec properties should inherit only from its parent specs not same-level subspecs.


Solution

  • Found a workaround, maybe not that elegant:

    Since pod install merges all compiler flags into one, I had to remove the GCC_PREPROCESSOR_DEFINITIONS from the library's podspec file. But without this definition, my library does not build.

    In Xcode this can be fixed easily by adding the definition to each library's target. But when I use my library in an application, the Pods project is generated out of the Podspec which doesn't include the required flag.

    The solution is to use the post_install hook in the application's Podfile to manipulate the generated xcconfig of the Pods project.

    post_install do |installer|
    
        file_names = ['./Pods/Pods-ServerA.xcconfig',
                      './Pods/Pods-ServerB.xcconfig']
    
        # rename existing pre-processor definitions
        file_names.each do |file_name|
            text = File.read(file_name)
            File.open(file_name, 'w') { |f| f.puts text.gsub(/GCC_PREPROCESSOR_DEFINITIONS/, "GCC_PREPROCESSOR_DEFINITIONS_SHARED")}
        end
    
        # merge existing and required definition for ServerA
        File.open('./Pods/Pods-ServerA.xcconfig', 'a') { |f|
            f.puts "\nGCC_PREPROCESSOR_DEFINITIONS=$(GCC_PREPROCESSOR_DEFINITIONS_SHARED) ServerA=1"
        }
    
        # merge existing and required definition for ServerB
        File.open('./Pods/Pods-ServerB.xcconfig', 'b') { |f|
            f.puts "\nGCC_PREPROCESSOR_DEFINITIONS=$(GCC_PREPROCESSOR_DEFINITIONS_SHARED) ServerB=1"
        }
    
    end
    

    The code is a bit verbose since I'm not familiar with Ruby but it works. It should be easily possible to automate this renaming-append process as long as variable and library follow a naming scheme.