A CocoaPods plugin which mangles the symbols of your dependencies
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
.circleci
lib
spec
.gitignore
.ruby-version
CHANGELOG.md
Gemfile
Gemfile.lock
Intercom_logo-github.png
LICENSE
README.md
cocoapods-mangle.gemspec

README.md

Intercom

Apache License CircleCI

cocoapods-mangle

cocoapods-mangle is a CocoaPods plugin which mangles the symbols of your dependencies. Mangling your dependencies' symbols allows more than one copy of a dependency to exist in an app. This is particularly useful for iOS frameworks which do not want to interfere with the host app.

Installation

$ gem install cocoapods-mangle

What is mangling?

Mangling or namespacing your dependencies is a way of ensuring that there are no conflicts between multiple copies of the same dependency in an app. This is most useful when developing third-party frameworks.

For example, if you are developing a framework MyFramework.framework and you include AFNetworking as a dependency, all AFNetworking classes are included in your framework's binary:

➜ nm -gU MyFramework.framework/MyFramework | grep "_OBJC_CLASS_\$.*AF.*"
00000000000000e0 S _OBJC_CLASS_$_PodsDummy_AFNetworking
00000000000013f0 S _OBJC_CLASS_$_AFNetworkReachabilityManager
0000000000001f20 S _OBJC_CLASS_$_AFSecurityPolicy
000000000000a938 S _OBJC_CLASS_$_AFHTTPBodyPart
000000000000a898 S _OBJC_CLASS_$_AFHTTPRequestSerializer
000000000000a9d8 S _OBJC_CLASS_$_AFJSONRequestSerializer
000000000000a910 S _OBJC_CLASS_$_AFMultipartBodyStream
000000000000aa28 S _OBJC_CLASS_$_AFPropertyListRequestSerializer
000000000000a848 S _OBJC_CLASS_$_AFQueryStringPair
000000000000a8c0 S _OBJC_CLASS_$_AFStreamingMultipartFormData
0000000000004870 S _OBJC_CLASS_$_AFCompoundResponseSerializer
00000000000046e0 S _OBJC_CLASS_$_AFHTTPResponseSerializer
0000000000004820 S _OBJC_CLASS_$_AFImageResponseSerializer
0000000000004730 S _OBJC_CLASS_$_AFJSONResponseSerializer
00000000000047d0 S _OBJC_CLASS_$_AFPropertyListResponseSerializer
0000000000004780 S _OBJC_CLASS_$_AFXMLParserResponseSerializer

This means that if an app includes both MyFramework.framework and AFNetworking, the app will fail to build with an error that looks something like:

ld: 16 duplicate symbols for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

However, with mangling enabled through cocoapods-mangle, we can see that the AFNetworking classes are now prefixed with MyFramework_:

➜ nm -gU MyFramework.framework/MyFramework | grep "_OBJC_CLASS_\$.*AF.*"
00000000000000e0 S _OBJC_CLASS_$_MyFramework_PodsDummy_AFNetworking
00000000000013f0 S _OBJC_CLASS_$_MyFramework_AFNetworkReachabilityManager
0000000000001f20 S _OBJC_CLASS_$_MyFramework_AFSecurityPolicy
000000000000a938 S _OBJC_CLASS_$_MyFramework_AFHTTPBodyPart
000000000000a898 S _OBJC_CLASS_$_MyFramework_AFHTTPRequestSerializer
000000000000a9d8 S _OBJC_CLASS_$_MyFramework_AFJSONRequestSerializer
000000000000a910 S _OBJC_CLASS_$_MyFramework_AFMultipartBodyStream
000000000000aa28 S _OBJC_CLASS_$_MyFramework_AFPropertyListRequestSerializer
000000000000a848 S _OBJC_CLASS_$_MyFramework_AFQueryStringPair
000000000000a8c0 S _OBJC_CLASS_$_MyFramework_AFStreamingMultipartFormData
0000000000004870 S _OBJC_CLASS_$_MyFramework_AFCompoundResponseSerializer
00000000000046e0 S _OBJC_CLASS_$_MyFramework_AFHTTPResponseSerializer
0000000000004820 S _OBJC_CLASS_$_MyFramework_AFImageResponseSerializer
0000000000004730 S _OBJC_CLASS_$_MyFramework_AFJSONResponseSerializer
00000000000047d0 S _OBJC_CLASS_$_MyFramework_AFPropertyListResponseSerializer
0000000000004780 S _OBJC_CLASS_$_MyFramework_AFXMLParserResponseSerializer

The app that includes both MyFramework.framework and AFNetworking will now build successfully 🎉

How it works

As demonstrated above, nm can be used to inspect the symbols such as classes, constants and selectors in a Mach-O binary. When you run pod install, cocoapods-mangle builds your dependencies if they have changed, and parses the output of nm. It places this output in an xcconfig file that looks something like this:

MANGLING_DEFINES = PodsDummy_AFNetworking=MyFramework_PodsDummy_AFNetworking AFNetworkReachabilityManager=MyFramework_AFNetworkReachabilityManager AFSecurityPolicy=MyFramework_AFSecurityPolicy AFHTTPBodyPart=MyFramework_AFHTTPBodyPart AFHTTPRequestSerializer=MyFramework_AFHTTPRequestSerializer AFJSONRequestSerializer=MyFramework_AFJSONRequestSerializer AFMultipartBodyStream=MyFramework_AFMultipartBodyStream AFPropertyListRequestSerializer=MyFramework_AFPropertyListRequestSerializer AFQueryStringPair=MyFramework_AFQueryStringPair AFStreamingMultipartFormData=MyFramework_AFStreamingMultipartFormData AFCompoundResponseSerializer=MyFramework_AFCompoundResponseSerializer AFHTTPResponseSerializer=MyFramework_AFHTTPResponseSerializer AFImageResponseSerializer=MyFramework_AFImageResponseSerializer AFJSONResponseSerializer=MyFramework_AFJSONResponseSerializer AFPropertyListResponseSerializer=MyFramework_AFPropertyListResponseSerializer AFXMLParserResponseSerializer=MyFramework_AFXMLParserResponseSerializer

MANGLED_SPECS_CHECKSUM = 18f61e6e6172fb87ddc7341f3537f30f8c7a3edc

This is included in GCC_PREPROCESSOR_DEFINITIONS of the xcconfig file for every target. All of these symbols will be mangled on subsequent builds.

The symbols that will be mangled are:

  • Objective C classes. e.g. AFNetworkReachabilityManager becomes MyFramework_AFNetworkReachabilityManager.
  • C and Objective C constants. AFNetworkingReachabilityDidChangeNotification becomes MyFramework_AFNetworkingReachabilityDidChangeNotification.
  • Objective C category selectors. The first component of the selector is mangled. e.g. -[NSString xxx_abc:def] becomes -[NSString MyFramework_xxx_abc:def].

The plugin has only been fully tested with Objective C dependencies. There is no reason why this could not also work for Swift.

Usage

cocoapods-mangle can be used by adding it to your Podfile like this:

source 'https://github.com/CocoaPods/Specs.git'

platform :ios, '8.0'
plugin 'cocoapods-mangle'

target :MyTarget do
  # Dependencies here
end

Now, each time you run pod install, cocoapods-mangle updates the xcconfig files for all targets to ensure that all symbols in your dependencies are mangled.

The plugin can be optionally configured with :xcconfig_path, :mangle_prefix or :targets. Here is an example:

plugin 'cocoapods-mangle', targets: ['MyTarget'],
                           mangle_prefix: 'Prefix_'
                           xcconfig_path: 'path/to/mangle.xcconfig'

Caveats

  • cocoapods-mangle will only work for source dependencies. Pre-compiled frameworks cannot be mangled.
  • Currently only supports iOS. It should be very straightforward to extend support to macOS, tvOS or watchOS.
  • Category mangling may cause issues if the dependency does not correctly prefix its category selectors (see http://nshipster.com/namespacing/#method-prefixes).
  • Usage of NSClassFromString(@"MyClass") will not work after mangling has been applied. You will need to use NSClassFromString(@"Prefix_MyClass") for this to work correctly.

Related links