2015-02-07

How to embed private Objective-C framework into iOS app on Xcode 6.

I'm creating this post since Apple's documentation on this topic is an out-of-date lie that mentions Xcode 2.4. And Stack Overflow is overrun with point collectors who can only answer minor variations of the same question over and over again.

The simplest form of this recipe is easy once you get past a few snags. I hope this helps someone some day before it also falls out of date.

Warning: this only works for iOS 8 or better. I'm not sure the linker should seg fault because of this though.


I start with a project named Pen1 in a workspace named SandBox2.


Elsewhere, I have already created a Cocoa Touch Framework like this.


The framework is a separate project from the SandBox2 workspace. I did this to simulate the scenario of:

  • You created some code in one project.
  • You have to add the code to another project.
  • You thought to yourself, "I'll turn this small chunk of code into a framework."
  • So then you create a separate project to house the framework with the idea of adding it into other projects and workspaces later.


The framework is named BobLib. It consists of 3 main files: BobLib.h, Newhart.h, and Newhart.m. Here are the contents of those files.



I want to use the method doThatThing from the Newhart class in my Pen1 project.

Next is something you must do, and I've only ever seen it mentioned in a WWDC 2014 video. You must select the Newhart.h file in the Project Navigator. Then in the Utilities Area, select the File Inspector. Inside the Target Membership section, you will have to change the value from "Project" to "Public".


If you don't do this, then after you add BobLib to Pen1, the compile of Pen1 will fail because it can't find Newhart.h.



I build BobLib for both simulator and device. After the build, wait for Xcode to index files, and then BobLib.framework in the Project Navigator under Products group will turn from red text (not found, I suppose) to black text. Control-click BobLib.framework and select Show in Finder from the menu.



I open SandBox2 in Xcode. I drag the simulator build of BobLib.framework in Finder into Pen1 in Xcode.


I guess the options to select here are based on what you are doing with your source code control system. I have not worked out the best thing to do for my own environment yet. For this example, I accepted the default choices.


In the project target General settings, I can see that Xcode automatically added the framework to the Linked Frameworks and Libraries section. Thank you, Xcode.


And it will build successfully, but when I ran it in the simulator, I hit this:


With this error message:

dyld: Library not loaded: @rpath/BobLib.framework/BobLib Referenced from: /Users/jeff/Library/Developer/CoreSimulator/Devices/EF59ACE7-ACD5-40CB-92ED-A4BBBBE619F0/data/Containers/Bundle/Application/7151B3F9-A962-44C0-A5A8-84B4AB619697/Pen1.app/Pen1 Reason: image not found

What Xcode did not do is add BobLib to the Embedded Binaries section of the project target General settings. I am not certain if that makes sense or not since I am so new to embedded frameworks.


Hit the + button, add BobLib.framework, and build and run again.

For some reason, after adding it to Embedded Binaries, Xcode adds it again to Linked Frameworks and Libraries. I delete that extra one.


Here is my code in Pen1 that will test if the app can use BobLib. Since I only copied the framework build for simulator, I can only test this out on Simulator.


After doing the above steps. I know the app has successfully used the framework by checking the console.


One final note. The WWDC 2014 video Building Modern Frameworks is bloody frustrating. The speaker is too enamored of himself, but this would be tolerable if he provided useful information. It is mostly slideshow in which he repeats the stuff we all know from the method naming guidelines. He drones on for 45 minutes before we get to see him play around in Xcode for 10 minutes, and then he casually races past crucial details that are not documented anywhere else on Earth but in this damned video.

5 comments:

  1. Wow, I looked everywhere for how to do this and it took me a while to find it. No where else from apple docs or stackoverflow had such a good tutorial. And yet, I still found this difficult.

    For me I had the opposite problem where it will run fine in the simulator, but on the device it got that exception even after doing the embedded binaries thing. Any ideas or updates on that?

    ReplyDelete
    Replies
    1. It has been a while, but I think the problem is you have to pick whether the binary lib will run on simulator or device. Back in the step in Project Navigator when you control-click BobLib.framework and select Show in Finder from the menu. That step. Just pull the files from "Debug-iphoneos" folder instead to run on device. You can see that folder in my screen shot up there. I also think Apple has some docs somewhere showing you have to archive these 2 files together to create a single binary lib that will run on both simulator and device.

      Delete
  2. Thanks for the very clear and concise explanation.
    I feel your frustration with Apple.

    ReplyDelete
  3. Here is an extended article that provides a solution for the issue with crashing simulator and building for it:
    https://www.insert.io/frameworkios8xcode6/

    In essence - it provides a build script that builds your framework for all platforms necessary to distribute your framework alone without entire source code archive.

    ReplyDelete