Creating iOS/OSX Frameworks: is it necessary to codesign them before distributing to other developers?
I am learning how to create iOS and OSX frameworks. Let's take iOS for example, the following steps work for me so far:
- xcodebuild framework using -sdk iphonesimulator and Build action
- xcodebuild framework using -sdk iphoneos and Build action
- Use lipo tool to create universal binary so that
lipo -info
produces expected:
Architectures in the fat file: Foo.framework/Foo are: i386 x86_64 armv7 arm64
The questions are:
I read that my framework can be re-signed by developer who is using it: "Code Sign on Copy" but I don't understand what are preconditions for it i.e. should I add codesign step to codesign that universal binary with my signing identity before distributing it to other developers?
if previous is positive - should I use my "iPhone Distribution: ..." identity or "iPhone Developer: ..." is enough (so that my framework being a part of some iOS project passes all kinds of validations especially App Store validation)?.
Background for my answer is the "CodeSign error: code signing is required for product type 'Framework' in SDK 'iOS 8.3'" which I have seen on a number of third-party frameworks and Carthage#235 or "code object is not signed at all" (one example: issue I reported on Realm#1998.
So I want to be sure that users of my frameworks will not encounter any codesigning issues when they use them.
P.S. This question gets even more interesting when applied not to a single developer but to an organization which is a framework vendor.
I opened the bounty: "Looking for an answer drawing from credible and/or official sources." but haven't receive such since then.
While answer provided by @jackslash is correct, it tells only a part of story so I want to write my own in a way I would like to have seen it at the moment I was asking this question.
The actuality of this answer is: July 2015. It is most likely that things will change.
First of all let's assert that actions needed for correct code signing of framework should be divided into steps that framework's Developer has to take and steps that framework's Consumer has to take.
TLDR;
For OSX framework: Developer is free to distribute OSX framework without codesigning it as Consumer will re-codesign it anyway.
For iOS framework: Developer is free to distribute iOS framework without codesigning it as Consumer will re-codesign it anyway, but Developer is forced by Xcode to codesign their framework when they build for iOS device.
Because of radar: "iOS frameworks containing simulator slices can't be submitted to the App Store" Consumer of iOS framework is forced to run special script like "copy_frameworks" or "strip_frameworks" which uses lipo -remove
to strip off simulator slices from iOS framework and re-codesigns stripped framework because at this point its codesigning identity whatever it was (or wasn't) is removed as side effect of lipo -remove
manipulation.
Longer answer follows.
This answer is not a "drawing from credible and/or official sources" one but is rather based on a number of empiric observations.
Empiric observation #1: Consumer does not care because they will re-codesign framework they receive from Developer
Binary framework distributions of well-known open source projects on Github are not codesigned. Command codesign -d -vvvv
gives: "code object is not signed at all" on all of the binary iOS and OSX frameworks I used to explore. Some examples: ReactiveCocoa and Mantle, Realm, PromiseKit.
From this observation it is clear that authors of these frameworks intend them to be codesigned by Consumer, on their behalf i.e. a Consumer must use either "Code Sign on Copy" flag in "Embed frameworks" build phase provided by Xcode or use some custom shell script which does the same thing manually: codesigns framework on the Consumer's behalf.
I didn't find any single example of the opposite: open source framework that would be distributed with codesigning identity in it so in the rest of the answer I am assuming this widely adopted approach as correct one: there is no need for framework Developer to distribute their framework to other developers with codesigning identity in it because Consumer will anyway re-codesign it.
Empiric observation #2 which applies to iOS only and which is entirely a Developer's concern
While Consumer does not care whether framework they receive from Developer is codesigned or not, Developer still needs to codesign their iOS framework as part of its build process when they build it for iOS device because otherwise Xcode does not build: CodeSign error: code signing is required for product type 'Framework' in SDK 'iOS 8.1'
. To quote Justin Spahr-Summers:
OS X frameworks don't need to be codesigned at build... Unfortunately, Xcode does require that iOS frameworks be codesigned at build time.
This pretty well answers on my question #2: "iPhone Developer" identity is enough to cajole Xcode so that it would build iOS framework for device. This comment on Carthage#339 says the same thing.
Empiric observation #3: lipo tool
Specific behavior of lipo tool: when applied to framework binary, it always recursively removes any codesign identities from it: lipo -create/-remove codesigned framework ... -> not codesigned framework
.
This could be an answer why all of the examples in observation #1 are not codesigned at all: their codesigning identity is blown away after lipo is applied but since according to observation #1 Consumer does not care it is fine.
This observation is especially relevant to the next observation #4 about AppStore.
Empiric observation #4: iOS frameworks containing simulator slices can't be submitted to the App Store
This is widely discussed in: Realm#1163 and Carthage#188 and radar is opened: rdar://19209161.
This is entirely Consumer's concern: for iOS universal framework that Consumer includes in their application, when application is being built, they must run special script (custom Run Script Phase) that removes simulator slice from that framework's binary so that app could pass AppStore validation.
The good example for binary frameworks I found in Realm: strip-frameworks.sh.
It uses lipo
to remove all slices of architectures other than ${VALID_ARCHS}
and then re-codesigns it with Consumer's identity - this is where observation #3 kicks in: framework is to be re-codesigned because of lipo manipulations on it.
Carthage has CopyFrameworks.swift script which does the same thing to all the frameworks included by Consumer: it strips off the simulator slices and re-codesigns framework on behalf on Consumer.
Also there is good article: Stripping Unwanted Architectures From Dynamic Libraries In Xcode.
Now the overview of steps required to produce both iOS and OSX from both Developer's and Consumer's perspectives. First the easier one:
OSX
Developer:
- Builds OSX framework
- Gives it to Consumer
No codesigning activities are required from Developer.
Consumer:
- Receives OSX framework from Developer
- Copies framework to Frameworks/ directory and codesigns it automatically on their, Consumer's, behalf as part of "Code Sign on Copy" process.
iOS
Developer:
- Builds iOS framework for device. Codesigning is required by Xcode, "iPhone Developer" identity is enough.
- Builds iOS framework for simulator.
- Uses lipo that produces universal iOS framework from previous two. At this point the codesigning identity of 1 step is lost: universal framework binary "is not signed at all" but that is fine since "Consumer does not care".
- Gives it to Consumer
Consumer:
- Receives iOS framework from Developer
- Copies framework to Frameworks/ directory (this step may be redundant depending on what script in step 3 is.)
- Uses special script as a part of build process: this script strips simulator slices off the iOS framework and then re-codesigns it on their, Consumer's, behalf.
From reading the linked thread on the Carthage repo it seems relatively simple. If you are distributing the binary framework you need to code sign it and if you are distributing the source via carthage or cocoa pods you do not as those tools take care of this via different methods.
The reason you need to code sign it when you distribute the binary framework is that Xcode won't produce a framework binary without code signing it. If you attempt to not code sign the binary framework you get this error:
CodeSign error: code signing is required for product type 'Framework' in SDK 'iOS 8.1'
It doesn't matter which identity you code sign the framework with (iPhone Developer or iPhone Distribution) because, as you point out, the framework will be re-codesigned with the "code sign on copy" setting. This means that your framework will be re-codesigned by the appropriate certificate from the framework consumer's developer profile when your framework is copied into their application. This means there will be no issues with the App Store as it will only see the final code signature from the framework consumer.
In the end of the day, you might as well code sign your .framework binary as you don't want to have to maintain an exotic build process, and as Xcode will only output signed frameworks you shouldn't move too far away from the defaults. It doesn't really matter anyway because the end consumer will be re-signing it.