Framework Oriented Programming with Swift
Part 2, Considerations
In the previous link, we talked about Frameworks on iOS and did a quick introduction with the motivation for organizing our apps in Frameworks. On this post, we’ll go through a list of considerations that you should keep in mind when architecting your apps in Frameworks. We call it “Framework Oriented Programming” and it’s been the base architecture of GitDo.

Versioning: Backward compatibility
Version your frameworks as you version apps. Why? Let’s say a bug was introduced in your framework in version X.Y.(Z+1). As a result, your app starts showing off an unexpected behaviour. You can go through the commits and find which one introduced the bug, or if you are under pressure, just point your app to the previous version X.Y.Z and ship it until the bug can be fixed.
With every new version, prepare a detailed changelog as a reference: list the bugs the version fixed, new features introduced, breaking changes that might imply refactors in the framework consumers. Reference the commits related to these changes and be very clear with the description. You’ll thank it in the future.
Multiple versions of the framework can be included in the same bundle ensuring backward compatibility.
The screenshot below shows an example of a version of GitDo (with funny name included)

Dependencies
When designing the frameworks that your app/s will depend on drawing them and with arrows illustrate the dependencies between them. In the case of having dependencies between them prefer vertical over horizontal. Look for simplicity, complexity might lead to circular dependencies and more than one headache in the future.
On GitDo we’ve got defined principles for the architecture. One of these principles is called“One Step Dependency” which says that each framework should depend only on frameworks 1 level below itself and no more levels. Frameworks public interfaces should be wrapped up to be consumed by the level just on top of you. That way if you remove one of the “lego” blocks, only the level over this one would be affected.
Think of your Frameworks as a stack where you have a Core Framework and you build the others on top of each other. If you need inspiration, Apple is a good example. Dive into their frameworks and how they are structured and connected to each other: UIKit, AVFoundation,Foundation, MapKit…
An example of stack of Frameworks for your apps could be (sorry if I am not good enough using Sketch) the one shown below:

Single Responsibility Principle
Your Frameworks must have a purpose. Answer this question before starting coding the framework:
What problem is the Framework trying to solve? Do I really need it?
If we analyse existing Frameworks and try to answer that same question:
- CoreData: Defines a set of components for interacting with local databases. (Reusable in multiple platforms)
- UIKit: Provides a set of classes that represent interface elements like buttons, bars, labels, …. Reusable on iOS, OS X, watchOS, and tvOS. (Reusable in multiple platforms)
- RxSwift: Provides components to work with Reactive Programming concepts quickly.
If your Framework has multiple purposes, you might need more than one framework instead. If you can not answer the question, you might not need a Framework.
Do not reinvent the wheel. If there is another framework for the same purpose and your project is aligned with its principle, go with it.
Visibility
With Swift we have keywords, public, private, internal for specifying the visibility of our classes, structs, properties, functions, … When coding an app we don’t usually pay attention to the visibility since by default it’s internal and we don’t have to expose anything out of the app scope. But what about Frameworks? In this case, you have to make public all the components that the consumer of your Framework has access to (and it’ll likely be a few of all the components). As by default the visibility if internal our suggestion is that you first, code the framework, and then, start using it and making public these methods that have to be accessed.
Although the default visibility is internal, we prefer being more explicit and specifying the visibility everywhere, but it is not actually needed.
Document
Once you have the Framework ready to be used, your teammates will link the framework with the main app target and start using it. In order to make it easy for them make sure that your Framework is documented:
- Add a brief explanation about how to integrate the framework with any existing project (in case of having compatibility with any 3rd dependency manager, explain how to use it to link the app with your framework).
- Introduce your Framework components, its purpose and how to use them.
- Add documentation to the public entities (methods, attributes, classes, …).
You can use a tool like Jazzy from Realm that generates a delightful HTML website with the documentation using Apple’s style. You can even use GitHub Pages in order to upload your documentation website and make it available for the rest. If the Framework project is integrated with any CI platform you can run an script for deploying the documentation to that GitHub repository.
Even though you can generate automatic documentation from your code comments, having a manually written reference explaining how to use your frameworks is very handy for developers in your team depending on your frameworks. Whenever they have questions or concerns the can just use that reference.
Guidelines
Define the guidelines for your Framework project, from the principles of the Framework to the code style. Think that a teammate that don’t usually develop the Framework, might need to implement and propose something for the Framework. If you didn’t have these guidelines defines the developer would start adding code to the frameworks without mattering the code style, the architecture you decided for these frameworks, or why not, the design principles your frameworks stick to.
Making it with GitHub is relatively easy, create a file CONTRIBUTING.md. Every time the developers propose changes, they will be alerted about these existing guidelines and will suggest reading them before going on with the proposal.
Undocumented code components are like black boxes with public “elements” that can be consumer. If it works, none is going to jump into implementation details. But if something went bad developers would go into that black box and start adding workaround for the problems found or the features required. Enforce good practices with a guideline and don’t allow them to make this black box even darker.
Responsible team
If you have enough people in the team assign people to frameworks are responsible people.Why?
- They ensure the quality of the code. When you don’t have people responsible for that piece of code in your project, everyone can go into the framework and change it according to their needs (and most of the times without following the defined guidelines).
- If something goes wrong, they know about changes and versions in the framework and they can quickly revert it.
- Instead of having people jumping between UI, Data, Business logic you have people focused on only one of these pieces. If you have people expert on UI or that loves designing with Interface Builder, why not keeping that person in the UI framework?
It depends on how many people you are in your team. If you have enough people, it’s definitively something worth it.
Loading time using Dynamic Frameworks
We had a bad experience in loading time when using a lot of Frameworks. Since Frameworks are dynamically loaded in runtime it takes an extra time to load all your Framework. The firsts versions of GitDo which had around 8–9 Frameworks took around 6–7 to load fully. What was the solution? Identify these Frameworks that weren’t simultaneously dependency of multiple Framework and remove them from the embedded section. After that were able to decrease the load time to 2–3 seconds. When I was writing this article, I did research to understand why this is happening. I got to this thread on the Artsy Eigen repository trying to find a solution to this problem.
Access to Bundle Resources
As I mentioned earlier, Frameworks support assets in its own bundle. Instead of accessing NSBundle.mainBundle() you have to get the bundle using any class from your Framework:
NSBundle(forClass: MyFrameworkClass.classForCoder())
Accessing resources with static libraries and CocoaPods led to some missing resources when Frameworks support was introduced in CocoaPods since developers accessed resources using NSBundle.mainBundle()
Dependency manager
There’s recently a big significant about dependency managers, CocoaPods had been the reference from years, but Carthage came out a few months ago as an alternative to CocoaPods offering a decentralized approach. You can create your Frameworks architecture without any dependency manager. The steps to get it done are:
- Creating a workspace with all the projects.
- Connecting the project dependencies and importing the resulting frameworks.
- Compiling. Voila!
However, you might need some external dependencies (which we recommend you to minimize). But if you need them, your dependencies tree becomes more complex. You can resolve them manually and use tools like git submodules to clone them locally or automatize it CocoaPods or Carthage.
CocoaPods or Carthage?
Our first version of the architecture used Carthage. In case you don’t have experience with Carthage what it does is resolving the dependency tree using GitHub, flatting these frameworks in a single project and, if you specify it, also get the compiled version of these Frameworks. The step of adding these frameworks to the project is up to you, and that’s the different key point compared with CocoaPods.
Carthage doesn’t modify anything from your .xcodeproj, or .xcworkspace.
After months of use we found a few problems that made our development slower:
- We were initially using the compiled frameworks. When a Framework is compiled with a version of Xcode 7.XX and you try to use them from 7.(XX)+1 then the Frameworks are not valid and has to be recompiled. It has to recompile all the frameworks when the new version of Xcode is released (cross your fingers because if the source project doesn’t have the correct project configuration the compilation might fail).
- As Carthage doesn’t create the configuration of the Framework to be compile if the source developer didn’t setup everything properly you might have troubles compiling your dependencies. Attempting to compile a project for a platform and the developer forgot to add that platform as a supported one. Or trying to compile the project and there’s a flag somewhere that makes it fail when Carthage tries to compile it. If your list of Carthage dependencies is large, with new updates it supposes a big headache, especially when your workmates try to setup everything.
- Carthage + Debugging = Xcode Crashes. We couldn’t debug the project. Every time we tried to add a breakpoint in the project the application crashed and we ended up debugging using logs. We thought it was related to Xcode or a lot of Reactive code but after migrating with CocoaPods, the debugging is much nicer now.
We ended up migrating to CocoaPods. Each framework has its own .podspec file with the configuration of that Framework and we resolve these local dependencies using CocoaPods(which also bring the external dependencies). Working with local dependencies and CocoaPods is very straightforward since it creates a group in the project where you can modify your files from. After this migration to CocoaPods, problems setting up the project decreased.
We’re not saying Carthage is bad compared with CocoaPods but in our case we found some troubles with the setup. Carthage might not be the perfect one for our setup or we didn’t setup properly the dependencies after following all the steps on the repository, these are the problems that came up.

Creating our Framework
We were going to write the steps to create a Framework Carthage/CocoaPods compatible but Thoughtbot recently did it on this post. They explain all the necessary steps to create a project for your framework, make it compatible with the popular dependency managers and finally add a testing target to test your Frameworks.
Link to the post
