Skip to content

intercom/cocoapods-mangle

Repository files navigation

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

About

A CocoaPods plugin which mangles the symbols of your dependencies

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 7

Languages