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
becomesMyFramework_AFNetworkReachabilityManager
. - C and Objective C constants.
AFNetworkingReachabilityDidChangeNotification
becomesMyFramework_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 useNSClassFromString(@"Prefix_MyClass")
for this to work correctly.
Related links
-
CocoaPods Packager has similar mangling functionality for packaging
.podspec
files. - http://blog.sigmapoint.pl/avoiding-dependency-collisions-in-ios-static-library-managed-by-cocoapods/
- http://pdx.esri.com/blog/namespacing-dependencies/