Optimize your iOS projects creating binaries Frameworks
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 arm64
executables, 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:
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
andarm64
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 increaseds.source
change from the Git URL to the URL of theBinaries.zip
file in Dropbox.s.source_files
removed. The common code is now inside each frameworks.ios.source_files
replaced withs.ios.vendored_frameworks
, where we specified the name of the framework for iOSs.watchos.source_files
replaced withs.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
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.
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:
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:
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:
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.