Frameworks: embed or not embed that's the question

The importance of knowing how and why embed Frameworks.

Subscribe to my newsletter and never miss my upcoming articles

Hello my fellow peers, Leo here.

Today we'll explore one thing that I recently discovered and I want to share with you all. When you create a framework in iOS development it can be various things, today we'll focus on only two of those options - a static library or a dynamic library. The advantages of modularization are plenty: development can be divided, you have more readable programs, programming erros (or features who knows?) are easy to handle, it allows re-use of code, you have improved manageability and so on.

It's not new that create frameworks is a good strategy for big teams because they have literally hundreds of thousands lines of code to maintain, so it's important to have it neatly done to don't become a really big mess. And isn't in the scope of this post what the are the advantages and the disadvantages of using frameworks, this might appear in some latter writings.

The painting that I picked for this article is Hamlet and Ophelia from Agnes Pringle (1853–1934) and the reference is the "to be, or not to be, that's the question" from the famous masterpiece that I put not secretly in the title.

Let's go!

Problem

I want to know what is a better strategy for my new created framework, it's better static or dynamic? Embed or not?

First for this topic we'll need some theory about what are the the advantages and disadvantages of each one of the choices and after that I'll show you an example of each one.

Dynamic Library vs Static Library

The main difference between those two is how each one is linked to the executable file.

Dynamic libraries as the name says are dynamic linked to the application, they are not part of the application executable file. It means that when the app will load all of them in runtime, you might be thinking exactly the same thing like me right now "But if it's a runtime load, if I add a lot o dynamic libraries into the project and all or the majority of them be loaded in the startup, this could be a problem." AND you are right as commented at this WWDC16 talk it's not a good idea having a lot of dynamic libraries inside your app.

The process goes like this: When an app is launched the code is first loaded into the target address space. Then the dynamic linker comes into play, linking everything that the app needs to work properly. The dynamic linker will resolve each dependency location on the file system based on their install name and then linking the undefined external symbols ( in this case the code that you are relying to) to your app.

On the other hand, the static library when linking it will use the static linker. That means, when at the build time you are using such library the linker will merge in one single executable file all the framework code and the target application code. This way your final executable will be bigger but you guarantee that all the code needed to run, are already available to use. When the application launches all the application code plus the framework code will be loaded in the application address spaces.

Framework

In apple world, the frameworks can be various things. The Apple documentation says:

A framework is a hierarchical directory that encapsulates shared resources, such as a dynamic shared library, nib files, image files, localized strings, header files, and reference documentation in a single package. Multiple applications can use all of these resources simultaneously. The system loads them into memory as needed and shares the one copy of the resource among all applications whenever possible. [...] Frameworks serve the same purpose as static and dynamic shared libraries, that is, they provide a library of routines that can be called by an application to perform a specific task.

After you create a framework, how can you tell if your framework is a static library, dynamic library or something else? To answer this question you need to search for the Mach-o-type inside your framework, like the image below.

Screen Shot 2021-03-06 at 14.27.20.png

You just need to go in your target -> build settings -> Linking -> Mach-o-Type. You can CHANGE the type of the framework to any other options. When you create a framework from Xcode it'll automatically set it to dynamic library, but you can change for whatever you want, like the image below shows.

Screen Shot 2021-03-06 at 14.28.17.png

For the example of this post I just created a plain new framework with "command + shift + n" shortcut.

Screen Shot 2021-03-06 at 14.37.25.png

Embedding (or not) a framework

When you create an app you can have multiples frameworks, and dependencies in it. All of them you can set in the General tab in the Target of your choice. As the image below suggest:

Screen Shot 2021-03-06 at 14.45.18.png

Frameworks have this three choices: Do not Embed, Embed & Sign and Embed Without Signing.

Screen Shot 2021-03-06 at 14.47.17.png

Do not Embed

The "Do Not Embed" choice you are literally saying: please don't pack all the contents of this framework with the main application. This implies that the final application package will not contain a folder called (by default) Frameworks with all the framework code.

But wait... early you didn't say that the dynamic library is loaded outside the application code in runtime? Yes, and if you have a framework with the mach-o-type dynamic library and in the project "do not embed" when your app try to dynamically find the dependency inside the package it will give you a runtime error. So be careful with this. And yes, Apple could check if you are doing this kind of thing but all we have now is hope that this could be Xcode feature for the future.

Still talking about the "Do Not Embed", if you do not embed a framework that is mach-o-type static library, your code will run normally because the code inside the framework when you archive it as static linked to your main code, this means the code inside the framework was packed together inside the application code. Therefore, you don't need to embed it to your app unless you want to have access to some "media bundle" inside the static library framework that you are using.

Obviously when you don't embed the framework code your final ".ipa" will be smaller, but in case of dynamic library you don't have choice of not embedding them.

Embed framework (signing or not)

The "Embed and Sign" and "Embed Without Signing" the only diference is if you need to sign the framework or not. If is already sign you don't need to sign it again ( ad hoc doesn't count on this).

Embedding a framework that mach-o-type is a dynamic library you are assuring that all the files of that framework will be available in the final bundle of the app. When the app try to resolve the external symbols that are not inside the main app code it will find inside the app bundle in the framework folder. As explained above, this could affect seriously your startup time so be careful dynamic linking.

Now finally, when embedding a framework with mach-o-type of static library is something that you might rarely do. If you already have your code inside the main app executable you actually will "duplicate" the size of your dependencies embedding a static framework on your app. Of course, if for some (out-of-your-control or not) reason you need to get access to the "media bundle" inside the static library you can do it embedding the static framework to your final bundle, but keep in mind the "size duplication" problem stated above.

TL;DR

Now you will be able to compare in a single view the main differences between those four options.

Screen Shot 2021-03-06 at 15.41.23.png

And now let's go the example app that shows the final size differences of each one of the options listed above.

Exempli Gratia - Code Setup

To the setup I create a simple app that have one ViewController that I want to have all the option to choose the color from another framework that I will create. The application is called BundleForAllExample and the framework I will create is called MyColors.

The application code is pretty straightforward. Just create a new app and paste the Code below to the ViewController.swift archive.

import UIKit
//import MyColors // mark 1

class ViewController: UIViewController {

    private var titleLabel: UILabel!
    private var pressMeButton: UIButton!

    override func viewDidLoad() {
        super.viewDidLoad()
        //view.backgroundColor = UIColor.Material.blueA100 //mark 2
         view.backgroundColor = .blue
        configureTitleLabel()
        configureButton()
    }

    private func configureTitleLabel() {
        titleLabel = UILabel()
        view.addSubview(titleLabel)
        titleLabel.translatesAutoresizingMaskIntoConstraints = false
        titleLabel.text = "THIS IS JUST AN EXAMPLE"

        NSLayoutConstraint.activate([
            titleLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 30),
            titleLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor)
        ])
    }

    private func configureButton() {
        pressMeButton = UIButton()
        view.addSubview(pressMeButton)
        pressMeButton.translatesAutoresizingMaskIntoConstraints = false

        pressMeButton.setTitle("Press Me", for: .normal)
        pressMeButton.setTitleColor(.black, for: .normal)
        pressMeButton.backgroundColor = .yellow

        NSLayoutConstraint.activate([
            pressMeButton.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 20),
            pressMeButton.centerXAnchor.constraint(equalTo: view.centerXAnchor)
        ])
    }
}

You should see this:

Screen Shot 2021-03-06 at 16.00.42.png

But what you really want is to have that beautiful Blue A100 color from material pallete as your background color.

Now please remove the actual view.background color line and remove the comment signs from the import mark 1 and the view.background color mark 2. The code will be broken because your project don't have the MyColors framework dependency added, we will solve this problem soon but first let me explain the 1 and 2 mark on code.

The 1 mark is when I'm telling the code to import the dependency of the framework we'll create. This is very important because without him, the Swift compiler can't tell that the extensions that will be using on the 2 mark exists. When we add that we are telling to the compiler: "Swift bring the UIKit module and the MyColors module to this file".

The 2 mark on the "view.backgroundColor = UIColor.Material.blueA100" is when we use the MyColors framework to get the Material color blue A100. I'm not telling that material colors are better than the UIColor default colors, it's just an example ok guys.

Now it's time to create the framework.

  1. Create a new framework press command+shift+n and select Framework.
  2. Name it MyColors.
  3. Create a swift file MainColors.swift.
  4. Add all the content of this repo to the file.

And your framework is done, just build it to generate inside the Products folder the MyColors.framework and we are ready to set up in the BundleForAllExample app.

Adding the framework to the app

Now you just need to drag the MyColors.framework of your framework project to the "Frameworks, Libraries and Embedded Content" of the BundleForAllExample app like the image below:

Screen Shot 2021-03-06 at 16.11.35.png

After you drag into your app be aware if Xcode set in the "Build Phases" tab of your Target the MyColors.framework. If not, you should just lookup in your folders and link manually. Troubleshooting tip: if your Xcode don't collaborate linking automatically the framework, you can go to Build Settings -> Framework Search Paths, and there you put the exact path ( go to the left bar framework folder -> right-click on MyColors.framework -> show in finder -> get the path to it -> add the path to the Framework Search Paths)

Screen Shot 2021-03-08 at 07.48.28.png

Now back to the General tab, let seed the options of embedding the framework.

Screen Shot 2021-03-08 at 07.50.06.png

And if you click in the right side arrows, the options will appear:

Screen Shot 2021-03-08 at 07.52.31.png

This is how you can manipule all the options that we discussed earlier. If go inside your framework and change the mach-o-type of it, you can choose between static library or dynamic library. The same way, if you want to embed or not your framework in your final app bundle you can manipulate those options in General -> Frameworks, Libraries, and Embedded Content.

Go back to the ViewController and make sure your add two things in it.

This:

import MyColors

And inside viewDidLoad() this:

view.backgroundColor = UIColor.Material.blueA100

Run your project, the blue should be the new material A100 blue. What a lovely day!

Screen Shot 2021-03-08 at 08.40.01.png

Sizes Comparison between .IPA files

To finish this and wrap up all that we discussed I generated four .ipa archive files to demonstrate how different the size can be depending on what options you choose. I'll sort in ascending order and I think you can imagine already what options will be the smallest and the biggest one. I'll use the archive process for development team because it's easier and it's just to show the differences between sizes, for your environment you should check if you will need to sign or not in case of embedding content.

First let's the the see the smallest option with 33kb .ipa : Do Not Embed in App and Dynamic Library in framework mach-o-type options. This is obviously the smaller one because if you don't embed a dynamic library, not a single line of code of the framework will be present in the final bundle, this means that if your app depends on this framework to run production code, it will crash.

Screen Shot 2021-03-08 at 08.50.16.png

Second one with 64kb .ipa is: Not Embed in the App and Static Library in framework mach-o-type options. This is also pretty straightforward by now: as the static library is static linked to the main app code, the swift compiler can do some optimizations to compress the size of the framework inside the final app code.

Screen Shot 2021-03-08 at 08.56.54.png

The third .ipa file has 84kb and has the options: Embed in the App and Dynamic Library in framework mach-o-type. This will bundle the framework code into the final .ipa file but the symbols inside the main app code are external and available at run time through dynamic linking. The default behaviour is generate a folder called framework that your app can link in runtime the files.

Screen Shot 2021-03-08 at 09.10.24.png

The fourth and it's not surprise with 371kb .ipa file is: Embed in the App and Static Library in framework mach-o-type. This archive will in the .ipa have the Framework folder with all the contents of the framework and also the main code will have all the code that the static library has. So this option is only useful if you need to get the media bundle inside the static framework, otherwise is a really bad idea.

Screen Shot 2021-03-08 at 09.14.06.png

The End

I hope you, my dear readers, enjoy this content as much as I enjoyed preparing and studying this. This is just an introductory topic on Frameworks and Xcode packing but it's some foundation knowledge that I thought it would be great to share with you.

As always if you have any comment or feedback, please don't hesitate to share below. I want to know your impressions on this.

And if you want to help me keeping this work, you can buy a swift book from amazon from my link.

Thanks for the reading and... that's all folks.

credits: image

Comments (2)

Ryan Man's photo

Great artical, easy to understand and learnt a lot!

But it seems there is typo in the section Do not Embed

This implies that the final application package will note contain a folder called (by default) Frameworks with all the framework code.

Is ...will note contain... supposed to be ...will not contain?

It's a little confusing at first glance for non-native English reader.

But whatever, it's a great artical, again. Thanks.

Leonardo Maia Pugliese's photo

Thank you very much for the corrections. You made my day with this comment, glad you liked :) Have a nice week man!