Optimize your iOS projects creating binaries Frameworks

Cristian Barril
7 min readMay 1, 2018

Hello again developer friend!

In this final chapter we will learn how to distributes our frameworks as binaries. Why? Well, there are different reasons. Maybe you are working in a company that develops an SDK and doesn’t want to share the code for competitive reasons. Or maybe you just wants to optimise the project compilation time and using your frameworks as binaries avoid the re-compilation of them.

To do this, we are going to use the LogsFramework that we create in previews chapters. Also we will need the Demo Project created in chapter 2.

Chapter 3: Distribute binaries framework with Cocoapods

1 - Setup project to generate binary

By default, Cocoa Touch Frameworks cannot be archived. You can enable it by editing the framework target’s Build Settings and setting Skip Install to No:

Remember to do it in both targets, LogsFramework and WatchLogsFramework

However, if you attempt to archive now, Xcode will only build the armv7 and arm64executables, which would make it impossible to run the framework on the iOS simulator.

2 - Generating Universal Framework

To include binaries for the iOS, Watch or Tv simulator, you have to configure a post-archive script that will build your framework for the simulator after you archive it and merge both, the simulator and device binaries, into one fat universal framework.

To have Xcode run the script after archiving automatically, click Product -> Scheme -> Edit Scheme (or Cmd + Shift + <), and configure a Run Script post-actions for the Archive command:

Here you can download the correspondent script for iOS, watchOS and tvOS.

Unzip the file with the scripts you just downloaded and open the correct one for each platform: iOS_Script.sh for LogsFramework and WatchOS_Script.sh for WatchLogsFramework.

Then copy the content of the script and paste it into the Run Script window, like this:

Run Script window

Be sure to select LogsFramework for the Provide build settings from setting and click Close to apply the changes.

3 - Create the binary Framework

Finally, archive LogsFramework by clicking Build -> Archive in the menu bar. If the option is greyed out, make sure to select a physical iOS device and not the iOS simulator.

Once the bundle is archived, the Xcode Organizer will pop up. Wait a few more seconds, and the Finder should also open up to your project directory with a universal LogsFramework.framework for iOS inside it.

You can verify that LogsFramework.framework is indeed a universal framework by running file LogsFramework within the LogsFramework.framework directory:

As the output suggests, the executable contains binaries for the x86_64 and arm64 architectures, which makes it a universal fat framework.

If something went wrong, we can find the output file from the script in the temporal folder, like this:

Repeat this steps for every other platform that you have develop in the framework (like WatchLogsFramework).

Distribute the binary with Cocoapods

Once that we have the LogsFramework.framework and the WatchLogsFramework.framework files, it’s time to setup our podspec, so when someone install our pod as a dependency, instead of receiving our source code, it will receive the .framework file.

1 - Generate zip file

First we need to generate a zip file with all our binaries. Select the LogsFramework.framework and WatchLogsFramework.framework files and zip them into one Binaries.zip

2 - Upload zip file

We need to save our zip file in a server, so every cocoapods installation can find it. I suggest to use Dropbox or any similar free tool. After you upload it, generate the share URL of the file.

3 - Setup Cocoapods

Now we need to change our podspec. Instead of setting the source files, we will setup the binary corresponding to each platform, like this:

What is changed?

  • s.version is increased
  • s.source change from the Git URL to the URL of the Binaries.zip file in Dropbox.
  • s.source_files removed. The common code is now inside each framework
  • s.ios.source_files replaced with s.ios.vendored_frameworks, where we specified the name of the framework for iOS
  • s.watchos.source_files replaced with s.watchos.vendored_frameworks, where we specified the name of the framework for watchOS

4 - Push the changes and tag

To use the new version of our project, we need to:

  • Change the version in Xcode from every target to 0.0.2 (Optional)
  • Push all the changes to the LogsFramework repo
  • Tag the last commit with the version 0.0.2

When all this is done, we can go to the final step!

5 - Upload the new podspec

Let’s push our new podspec to the Podspecs repo to finish our distribution.

First we have to go to the project root folder in Terminal:

cd /Path/to/Project/LogsFramework

and type:

pod repo push Podspecs ./*.podspec --allow-warnings --sources=Podspecs,master --verbose

This is the same that we did the first time when we push our LogsFramework 0.0.1 in the preview chapter

We finish!!

Yes, we did it! Now, the final test. Let’s try our binary framework in the Demo Project that we use before.

Installing the binary framework

Go to the folder where you have the TestApp project and open the Podfile. All we need to do is change the version of the dependency from 0.0.1 to 0.0.2

Then in Terminal type:

pod update

After the installation is complete, open the TestApp project. Select the target TestApp and Run. You will see in the Xcode console the next line:

Great! This means that our LogsFramework.framework is still working for iOS. Now let’s check how is it going in watchOS. Select the TestAppWatch target and Run.

Oops!

Damn, this was going too well. What happend? This used to work before!!

Well, the problem is that some things have changed. Before we have one dependency called LogsFramework with all the sources classes for iOS and Watch. Now we have two different binaries frameworks for each platform. And the correct framework for watchOS is not LogsFramework.framework but WatchLogsFramework.framework, so we need to change the import statement to use it.

Change import LogsFramework to import WatchLogsFramework. Then click Run again. The outcome should be:

Now we are talking!

Yes, now we can celebrate! But before, let’s understand a little bit more what is different here.

Source code VS Binaries

Before we had the source files of our framework in the App, like this:

BEFORE Binaries

This is great if you don’t mind to share your code with others, but sometimes that is not possible in the business world. Besides, having the source code means that every time you Build or Run your projects from scratch, these classes are compiled as well. So, let’s see what we have now:

AFTER Binaries

This fixes the last two thing that we speak before. Our code is private and it is already compiled. Of course that I’m an Open Source fan, but sometimes this can be the best approach.

Conclusions

Now we know how to create frameworks for different platforms, how to distribute them using Cocoapods and also how to ship them as binaries. Now is up to you which one to use.

Tip: If you have a framework that nobody wants to use because it greatly increases the compilation times, try to distribute it as binary and you will see that no one hates it anymore.

You can download the source code from this repo. If you want to see it working, here is the TestApp repo.

If you have any questions feel free to add me on GitHub or LinkedIn.

--

--