Alexander Tsepkov
Alexander Tsepkov

Reputation: 4186

Embedding frameworks inside closed-source Swift framework

Our company wants to distribute a closed-source SDK for iOS to our clients. I've been using Cocoapods to build the framework and built an example app making use of it. Previously the app worked fine on the simulator as well as when deployed on the device. However, I was also embedding the Pods.framework file in the app itself. One other piece of information that may be of interest is that the framework is written in Swift, the included cocoapods dependencies are both Swift and Objective-C.

I wanted to make the pods requirements easier to manage so the user doesn't need to be concerned with them and tried to embed the Pods.framework file inside of the SDK we're building - so I removed the steps to Embed Pods Frameworks and Copy Pods Resources from the example app, leaving them only in the framework, I also removed Pods.framework as a dependency of the example app, leaving it only in the SDK. This seemed to work in the simulator, but the app now crashes on mobile device with dyld: Library not loaded error.

Upon researching it, I stumbled into a few related discussions: https://github.com/CocoaPods/CocoaPods/issues/344 https://objectpartners.com/2014/06/25/developing-private-in-house-libraries-with-cocoapods/

However, the suggested solution of using private pods does not look like it would work for us, it's my understanding that the source code in the private pod would still be open, and we can't share it with our clients.

Could someone advise on a solution that would work in this case?

Upvotes: 8

Views: 5090

Answers (1)

Alexander Tsepkov
Alexander Tsepkov

Reputation: 4186

OK, I finally have a more durable solution. It's a modified, cleaner version of my old one now that I understand how Xcode links in my Swift sub-frameworks better

Problem that makes distribution/compilation a bit ugly:

Since Swift standard libraries aren't bundled on the device like Obj-C, nor are they guaranteed to be stable between versions yet (stable binary interface promised in Swift 3: https://github.com/apple/swift-evolution#development-major-version--swift-30) we have to make sure the entire project is compiled against the same version of Swift. That means the guy using your closed-source framework has to be using the same version of Swift in their Xcode for their project as you did for compiling the library, even if he's not using Swift in his code because ultimately it's his version of Swift that gets bundled into the app and your SDK runs against. This is only an issue for closed-source frameworks because open-source ones will always be compiled against the same version as final project. Possible workaround is to restrict clients to same version you use or distribute multiple compilations (i.e. Swift 2.1 and Swift 2.0). To remedy this, you could provide users with copies of binaries compiled against multiple versions of Swift.

Aside from that, here is what I had to do during compilation/distribution to make a binary framework that works in Swift:

When building the framework:

  • In project target, make sure to add Pods.framework to Linked Frameworks and Libraries (make sure this is a pre-compiled RED version of Pods.framework, I had a black compiled Pods.framework in the same directory which built fine but then resulted in a framework that would cause the project to complain about missing armv7 architecture during linker phase in later project)
  • In Build Settings, under User-Defined section, add a field called BITCODE_GENERATION_MODE and set it to bitcode
  • DO NOT #import any frameworks in your bridging header, all instructions telling you to do that are leftover from Swift 1.0-1.2 days, you don't need it anymore and it does more harm than good (the later project will complain that it can't find these headers that aren't even exposed to it)
  • Change build target to Generic iOS Device, Archive and Export the framework

When building the project using the framework:

  • Drag and drop the framework into the project, in General tab add it to Embedded Binaries and Linked Frameworks and Libraries (you only need to add the framework itself, not the sub-frameworks or the pods file)
  • In Build Settings tab, add a new path to Framework Search Paths: $(PROJECT_DIR)/MyFramework.framework/Frameworks
  • Build the project

Upvotes: 4

Related Questions