100

I am learning how to create iOS and OSX frameworks. Let's take iOS for example, the following steps work for me so far:

  1. xcodebuild framework using -sdk iphonesimulator and Build action
  2. xcodebuild framework using -sdk iphoneos and Build action
  3. 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:

  1. 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?

  2. 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.

Stanislav Pankevich
  • 11,044
  • 8
  • 69
  • 129
  • [This comment](https://github.com/Carthage/Carthage/issues/399#issuecomment-86089516) suggests using 'CODE_SIGN_IDENTITY = iPhone Developer' but it is not clear why 'Developer' is used instead of 'iPhone Distribution'. – Stanislav Pankevich Jun 21 '15 at 19:55
  • Related topic: [Exporting app with embedded framework](https://devforums.apple.com/message/1085069#1085069) but with no definite answer. – Stanislav Pankevich Jun 21 '15 at 23:02
  • I have also duped this question on Apple Developer Forums: https://forums.developer.apple.com/thread/6400. – Stanislav Pankevich Jun 23 '15 at 10:15
  • i have a question when you distribute your framework, you distribute a debug build or release build? and if you distribute it in release build, how to do this? – niczm25 Feb 23 '17 at 05:35
  • @niczm25, this is one way of how I do it: https://stanislaw.github.io/2015/11/23/how-to-build-static-framework-using-make.html. – Stanislav Pankevich Feb 23 '17 at 09:02

2 Answers2

153

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:

  1. Builds OSX framework
  2. Gives it to Consumer

No codesigning activities are required from Developer.

Consumer:

  1. Receives OSX framework from Developer
  2. Copies framework to Frameworks/ directory and codesigns it automatically on their, Consumer's, behalf as part of "Code Sign on Copy" process.

iOS

Developer:

  1. Builds iOS framework for device. Codesigning is required by Xcode, "iPhone Developer" identity is enough.
  2. Builds iOS framework for simulator.
  3. 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".
  4. Gives it to Consumer

Consumer:

  1. Receives iOS framework from Developer
  2. Copies framework to Frameworks/ directory (this step may be redundant depending on what script in step 3 is.)
  3. 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.
jackslash
  • 8,550
  • 45
  • 56
Stanislav Pankevich
  • 11,044
  • 8
  • 69
  • 129
  • 12
    This is a valuable write up. Nice one – jackslash Jun 30 '15 at 11:27
  • @Stanislaw thanks for the insights and the valuable data. As i am writing a framework my self designed for developers i want them to be able to take the framework as is and use it without needing to make special script in order to upload to app-store. I think GoogleMobileAd work that way. but you say they must run certain script? you have idea how GoogleMobileAds dont require that? thanks – Michael A Jul 07 '15 at 12:36
  • @MichaelA, indeed that's interesting. I'll look. – Stanislav Pankevich Jul 07 '15 at 12:55
  • Could you give me a link to GoogleMobileAds you're using? – Stanislav Pankevich Jul 07 '15 at 12:57
  • @MichaelA, looks like they ship not a dynamic framework but static library wrapped in a framework bundle. Try `file GoogleMobileAds.framework/GoogleMobileAds` and you'll see: `current ar archive random library` whereas `file` on dynamic frameworks usually produces: `Mach-O dynamically linked shared library ...`. Also that explains why they ship it for iOS < 8 (iOS dynamic frameworks are only supported since iOS 8.0). Does that make sense? – Stanislav Pankevich Jul 07 '15 at 13:07
  • I guess so, if i understand correctly when using dynamic framework (a must for me since i code in swift + my framework contain images and assets) there is currently no way to prevent the consumer need to strip down the framework simulator part when wanting to publish the app? thats really suck especially since you might encounter that issue only when you encounter the rejection from apple – Michael A Jul 07 '15 at 14:45
  • Yes, I also think that it is not convenient but looks like it is present state of affairs. I didn't try to submit not-stripped frameworks myself yet but Carthage and Realm topics are convincing enough. You could try it yourself and let us know :) Though Realm's script taken as is worked perfectly for me: see IMPORTANT DETAIL part in my other answer I've just posted: [How to export “fat” Cocoa Touch Framework (for Simulator and Device)?](http://stackoverflow.com/questions/29634466/how-to-export-fat-cocoa-touch-framework-for-simulator-and-device/31270427#31270427) – Stanislav Pankevich Jul 07 '15 at 15:32
  • 1
    Thank you for saving my time, that worked for me. Xcode 7.3, Mac OS X 10.11.4. – eugen Apr 07 '16 at 13:33
  • Stanislav, your empiric observation #3 is wrong, lipo does not strip signatures: http://pastebin.com/CjwrpB45 - this is documented [here](https://developer.apple.com/library/ios/documentation/Security/Conceptual/CodeSigningGuide/Procedures/Procedures.html): "Because each architecture component is signed independently, it is all right to perform universal-binary operations (such as running the lipo command) on signed programs. The result will still be validly signed as long as you make no other changes." – Jaka Jančar Jun 04 '16 at 08:12
  • About "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" - have Apple changed this? Because I just built `Release-iphoneos` "Cocoa Touch Framework" in XCode 8.2.1 (I used Build for profiling) with empty signing settings: http://i.imgur.com/S37J3HN.png – JustAMartin Jan 18 '17 at 22:18
  • At this date, once I forced Xcode to always sign my framework so both simulator and iphone builds were signed, after doing `lipo create` to make the universal framework, the universal framework was still signed. – abc123 Feb 17 '17 at 17:35
  • @StanislavPankevich i don't know how will distribute universal framework with strip script included, but is it possible to distribute it to consumer in two forms, 1 is the universal for development and then 1 with the acceptable framework when uploading the app into app store? would this work? – niczm25 Feb 23 '17 at 07:51
  • @niczm25, usually people distribute only one framework: the universal one. The article in my answer contains the instructions on how to add a strip script to your Xcode project's **Run Script** phase: http://ikennd.ac/blog/2015/02/stripping-unwanted-architectures-from-dynamic-libraries-in-xcode/. – Stanislav Pankevich Feb 23 '17 at 09:11
  • @StanislavPankevich i created a framework with pods (AFNetworking) on it, and i can't seem to find a script to create a universal framework that works on workspace, – niczm25 Feb 23 '17 at 10:23
  • I don't have problem with the simulator build, but I am have trouble runing on device with not my own provision profile on a demo app that includes my framework, which includes WebRTC framework, which is a dynamic library. Should I have a script to let my costumer who use my demo app to re-code sign the demo app, too? https://stackoverflow.com/questions/51918306/cound-not-run-on-a-device-with-a-different-team-provision-profile-after-adding-a – allenlinli Aug 20 '18 at 01:32
24

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.

jackslash
  • 8,550
  • 45
  • 56
  • in your answer, in the second paragraph of it, you're saying that there is no need to codesign framework because it will be re-codesigned by consumer and I am also now 95% sure it is true, but I don't get why at the same time in the first paragraph you're saying: "If you are distributing the binary framework you need to code sign it" - what's the difference - if I distribute binary framework (i.e. not its source via carthage or cocoapods) why I would need to codesign it? – Stanislav Pankevich Jun 24 '15 at 00:15
  • I think I should not codesign my framework whatever kind of distribution I use (zipped .framework file or carthage or cocoapods). Examples: all of the following binary framework distributions are not codesigned: [Realm](https://github.com/realm/realm-cocoa/releases/download/v0.93.2/realm-objc-0.93.2.zip), [ReactiveCocoa](https://github.com/ReactiveCocoa/ReactiveCocoa/releases/download/v3.0-beta.8/ReactiveCocoa.framework.zip), [OCMockito](https://github.com/jonreid/OCMockito/releases/download/v1.4.0/OCMockito-1.4.0.zip). – Stanislav Pankevich Jun 24 '15 at 00:15
  • So I'm a bit confused with your "If you are distributing the binary framework you need to code sign it" which (imho) contradicts to the rest of your answer. Please clarify. Also please note that I opened this bounty as "Looking for an answer drawing from credible and/or official sources.". – Stanislav Pankevich Jun 24 '15 at 00:21
  • Added a reason. All of those linked frameworks are code signed. See the `_CodeSignature` folder within the .framework – jackslash Jun 24 '15 at 10:22
  • while I also see that Release iOS target doesn't compile producing "CodeSign error" (looks like it is not the case for OSX), I am not sure about Realm, RAC and OCMockito - `codesign -d -vvvv` on their frameworks gives me: "code object is not signed at all". How do you verify a fact that particular framework is / is not codesigned? – Stanislav Pankevich Jun 24 '15 at 10:39
  • huh you're right the RAC framework isn't signed, but Realm is. I looked at RAC's Xcode project and it seems to have a signing identity set so I don't know what they're doing there. Maybe they delete the signature file before release. OCMockito is built as a static library and then put into a framework folder structure using an older framework creation method. – jackslash Jun 24 '15 at 11:13
  • 1
    Yes, I have also looked at Realm and OCMockito. Realm has "iPhone Developer" identities and yes, OCMockito is a static library. I think I understand the issue - it is lipo that removes codesigning from iphoneos when it combines codesigned iphoneos and not-codesigned iphonesimulator. – Stanislav Pankevich Jun 24 '15 at 11:21
  • I guess that combination of these comments on Carthage pretty much constitutes the same what you and I see about the process: [this](https://github.com/Carthage/Carthage/issues/399#issuecomment-85829665), [this](https://github.com/Carthage/Carthage/issues/281#issuecomment-70126876) and [this](https://github.com/Carthage/Carthage/issues/399#issuecomment-86089516), so I am close to accept your answer. Still I am not satisfied with that it is not written officially by Apple anywhere and I needed a couple of days to get through all these pieces of knowledge. – Stanislav Pankevich Jun 24 '15 at 11:24
  • Welcome to developing third party frameworks for iOS ;) All I can say is at least we have a way to build and ship proper frameworks now. – jackslash Jun 24 '15 at 11:28
  • 1
    i have a question when you distribute your framework, you distribute a debug build or release build? and if you distribute it in release build, how to do this? – niczm25 Feb 23 '17 at 05:43