Cocoapods: Debug and Release vendor library in one podspec

Created on 19 Nov 2014  Â·  13Comments  Â·  Source: CocoaPods/CocoaPods

We want to use CocoaPods to manage a private vendor library we need to use. This vendor ships 2 libraries in every release. One of them is for production use (release or app store environment) and the other is for debugging purposes. Lets say they are called 'lib123.a' and 'lib123debug.a'

I tried different approaches but couldn't find a solution which fits our needs. We need to include the release library in the Release configuration and the debug library in the Debug configuration. I know that you can import different pods for different configurations like this:

source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '7.0'

target 'TestApp' do
  pod 'sdk-ios-debug', :git => '[email protected]:whatever/sdk-ios.git', :tag => '2.6.9', :configurations => ['Debug']
  pod 'sdk-ios', :git => '[email protected]:whatever/sdk-ios.git', :tag => '2.6.9', :configurations => ['Release', 'AppStore']
end

The SDK has two podspecs with different names:

Pod::Spec.new do |s|
  s.name                    = 'sdk-ios'
  s.version                 = '2.6.9'
  s.license                 = 'Commercial'
  s.summary                 = '..'
  s.description             = '..'
  s.source                  = { :git => '[email protected]:whatever/sdk-ios.git', :tag => '2.6.9' }
  s.platform                = :ios, '7.0'
  s.source_files            = "*.h"
  s.ios.vendored_library    = 'lib123.a'
  s.ios.framework           = 'SystemConfiguration', 'ExternalAccessory', 'Security'
  s.ios.libraries           = 'z', 'iconv', 'bz2', 'c++'
end
Pod::Spec.new do |s|
  s.name                    = 'sdk-ios-debug'
  s.version                 = '2.6.9'
  s.license                 = 'Commercial'
  s.summary                 = '..'
  s.description             = '..'
  s.source                  = { :git => '[email protected]:whatever/sdk-ios.git', :tag => '2.6.9' }
  s.platform                = :ios, '7.0'
  s.source_files            = "*.h"
  s.ios.vendored_library    = 'lib123debug.a'
  s.ios.framework           = 'SystemConfiguration', 'ExternalAccessory', 'Security'
  s.ios.libraries           = 'z', 'iconv', 'bz2', 'c++'
end

But this approach doesn't work quite well because I need to #import or #import .

What could I do to get it right? My next idea would be to have one podspec for this vendor library and remove libraries for the given stages in the pod_install hook. For example:

Pod::Spec.new do |s|
  s.name                    = 'sdk-ios'
  s.version                 = '2.6.9'
  s.license                 = 'Commercial'
  s.summary                 = '..'
  s.description             = '..'
  s.source                  = { :git => '[email protected]:whatever/sdk-ios.git', :tag => '2.6.9' }
  s.platform                = :ios, '7.0'
  s.source_files            = "*.h"
  s.ios.vendored_library    = 'lib123.a', 'lib123debug.a'
  s.ios.framework           = 'SystemConfiguration', 'ExternalAccessory', 'Security'
  s.ios.libraries           = 'z', 'iconv', 'bz2', 'c++'
end
discussion

Most helpful comment

I personally ended up creating 2 podspecs, one with the suffix Debug, which vendored "#{MAIN_POD_NAME}/Frameworks/Debug/#{MAIN_POD_NAME}.framework", where MAIN_POD_NAME is the name of the pod, without the suffix.

All 13 comments

The only way I could achieve the wanted behavior was to include both vendor libraries into podspec:

Pod::Spec.new do |s|
  s.name                    = 'sdk-ios'
  s.version                 = '2.6.9'
  s.license                 = 'Commercial'
  s.summary                 = '..'
  s.description             = '..'
  s.source                  = { :git => '[email protected]:whatever/sdk-ios.git', :tag => '2.6.9' }
  s.platform                = :ios, '7.0'
  s.source_files            = "*.h"
  s.ios.vendored_library    = 'lib123.a', 'lib123debug.a'
  s.ios.framework           = 'SystemConfiguration', 'ExternalAccessory', 'Security'
  s.ios.libraries           = 'z', 'iconv', 'bz2', 'c++'
end

And to remove unwanted libs in a specific configuration with a pod_install hook like this:

source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '7.0'

project_target_name = 'TestApp'

target project_target_name do
    pod 'sdk-ios', :path => '../../sdk-ios'
end

post_install do |installer|
  libs_to_remove_in_xconfigs = {
    :Debug => ['-l"123"'],
    :Release => ['-l"123debug"'],
    :AppStore => ['-l"123debug"']
  }

  libs_to_remove_in_xconfigs.each do |xconfig, libs_to_remove| 

    default_library = installer.libraries.detect { |i| i.target_definition.name ==  project_target_name }
    config_file_path = default_library.library.xcconfig_path(xconfig)

    File.open("config.tmp", "w") do |io|
      file_str = File.read(config_file_path)

      libs_to_remove.each do |lib|
        # Add whitespace after lib name because otherwise you 
        # will have a lot of spaces after replacing libs from 
        # string
        if lib.split(//).last != ' ' then
          lib += ' '
        end

        file_str.gsub!(lib, '')
      end

      io << file_str
    end

    FileUtils.mv("config.tmp", config_file_path)
  end
end

I can't shake off the feeling that this seems wrong in some way. Is there an alternative way?

But this approach doesn't work quite well because I need to #import or #import .

You can get around this problem, using #if __has_include(<FacebookSDK/FacebookSDK.h>) and then including it.

In case you only use the ‘debug’ archive in the simulator and the ‘release’ archive on devices, you can keep it simple, in the same way that many vendors do, by creating 1 archive that contains the i386 and x86_64 slices from the ‘debug’ archive (so only archs for the simulator) and the arm* slices from the ‘release’ archive.

Examples on how to use lipo to do this are plentiful, but here’s a good one http://stackoverflow.com/questions/2996235/how-to-build-a-library-for-both-iphone-simulator-and-device.

__has_include will actually not work, because all headers are available to all targets and configurations, see #2774.

Thanks guys for your suggestions. Using #if __has_include() could solve the issue but I don't think this makes the code more readable.

@alloy unfortunately the release version is a hardened version which doesn't log error messages and doesn't allow the debugger to run. That means we could do it this way but then I wouldn't be able to debug on my phone and many other things.

Why not amend the header search path in your post_install, so that you do not need to specify the Pod's name in the import statement?

@Oemera Ok, I think the only simple solution then is to use the different configuration options in your Podfile and fix this:

But this approach doesn't work quite well because I need to #import <sdk-ios-debug/SdkiOS.h> or #import <sdk-ios/SdkiOS.h>.

By using a CPP conditional that imports the correct header for the configuration, possibly by consolidating that into one header if you need to import this into multiple places.

just suggestion:
Create two frameworks (debug + release) instead of two libraries + use vendored_frameworks + use two separate podspec (as you do now) - it allow to use one #import
I did it by this way.

Closing this as a wontfix, since I see no way of doing this that wouldn't involve an inordinate amount of complexity.

I noticed that CocoaPods modified the Pods-Target-frameworks.sh to include:

if [[ "$CONFIGURATION" == "Debug" ]]; then
  install_framework "${PODS_ROOT}/PrivatePodName/Frameworks/PrivatePodName.framework"
fi
if [[ "$CONFIGURATION" == "Staging" ]]; then
  install_framework "${PODS_ROOT}/PrivatePodName/Frameworks/PrivatePodName.framework"
fi
if [[ "$CONFIGURATION" == "Release" ]]; then
  install_framework "${PODS_ROOT}/PrivatePodName/Frameworks/PrivatePodName.framework"
fi

where it installs the same .framework. But it looks like it could also install a different .framework depending on the main app's configuration somehow, but I can't find a way to create the .podspec in order to achieve this.

Any update?

Hello @Speakus! If you still hear us, could you please explain with more details how did you build both .podspec files? How did you referenced both frameworks on vendored_frameworks, what differences do they have to have, please?

I personally ended up creating 2 podspecs, one with the suffix Debug, which vendored "#{MAIN_POD_NAME}/Frameworks/Debug/#{MAIN_POD_NAME}.framework", where MAIN_POD_NAME is the name of the pod, without the suffix.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

marzapower picture marzapower  Â·  3Comments

Curtis-Halbrook picture Curtis-Halbrook  Â·  3Comments

evermeer picture evermeer  Â·  3Comments

gerchicov-bp picture gerchicov-bp  Â·  3Comments

tlandsmancars picture tlandsmancars  Â·  3Comments