Enforcing modular code with frameworks in Xcode
Every iOS developer I know dreams of writing code that’s DRY, modular, testable and reusable. While this is a great goal to strive for it’s often quite hard to write code that is completely modular. It just takes one oversight to blow most of the modularity you have achieved right out the window.
One technique that people use to make it easier to write modular code is to try and ensure that a certain part of their code, for instance the networking logic or their models, know nothing about other parts of the app. Again, a great idea but this isn’t really enforced by anything other than you and your team.
Wouldn’t it be great if there was a way to enforce this level of modularity automatically? Well, I have some great news for you. This is entirely possible and once I tried it for myself it was amazing how straightforward the solution actually is to implement. You can add multiple targets to your Xcode project. This can be used to turn certain parts of your code into frameworks which means that these parts of your code live in isolation from the rest of your code.
The outline of setting this up is as follows:
1. Identify code that should live in a framework
2. Add a new framework target to your project
3. Transfer relevant existing code to the framework target
4. Optional if you use Cocoapods: add dependencies to your new target
5. Write code independent from your app
6. Write tests for your framework
7. Use the framework in your app
We’ll go over each of these steps briefly so you get a good feel of how you can adopt this workflow in your own apps.
Identifying code for your framework
The first step to creating a framework is figuring out what code should live in the framework. It isn’t always obvious which parts of your app could be a framework and which parts can’t be. A good way to figure this out is to think about putting a group of files together in a folder. Can you find a couple of related classes that could happily be put together in a folder where they could only be aware of other classes in the same folder? If you can, it’s probably a good idea to put those files together in a framework.
Another way of finding code that could be better off in a framework is by looking at your code. Are there certain parts of your code that are littered throughout your project that would make changing certain things really hard? That could be a problem in the long run and it might very well be that you actually made some decisions along the way that have destroyed the modularity of your code. Moving some of this tightly coupled functionality into a framework could help you rethink the structure of this tight coupling and it could improve your code drastically.
Adding a framework target in Xcode
Once you have figured out which part of your app you want to put in a framework you need to add a new framework target in Xcode. The easiest way to do this is by going to your project’s settings by clicking on your project name in the Project navigator. Next, look for the + button at the bottom of the panel that lists your targets. This will present you with the same dialog that is presented when you create a new project. In the left sidebar pick the Framework & Library option and select Cocoa Touch Framework.
Now confirm your choice and create the framework by following the on screen steps. If you want to write tests for this target, which you should, make sure to select the Add Unit Tests checkbox. After creating your framework, it is automatically linked to your main app and two new folders were created. One for the framework and one the framework tests.
Adding code to your framework
If you are transferring existing code to your framework, drag the files from your main project folder to the framework folder so your code is logically organised. Next, select the files you just transferred and open the sidebar on the right side of Xcode. Look for the Target Membership header in the File Inspector. You’ll see that the checkbox next to your app is checked. Uncheck this checkbox and check the one next to your framework’s name. This will link the code to your framework instead of your main app.
When you create new files, make sure that the Target Membership is set correctly. Usually Xcode is smart enough to see which folder is currently active and it will add the code to the correct target. It’s always a good idea to double check because you don’t want to go hunting for issues only to find out you forget to correctly set the target for your files.
Integrating with Cocoapods
There’s a good chance your framework has some dependencies. If you’re writing an API framework, you could have a dependency on
Alamofire or maybe
SwiftyJSON. Cocoapods only links to one framework at a time so your
Podfile needs to be updated so it can handle your new framework target. Setting up a
Podfile with multiple targets can be done with the following skeleton.
target ‘MyApp’ do
PODS GO HERE
target ‘MyFramework’ do
PODS GO HERE
This is very similar to the
Podfile you probably already have, expect it has one more target; your framework. Cocoapods will set up your framework target just like it sets up your app, you don’t need to do anything special if you’re adding Cocoapods to a framework.
Writing code independent from your app
Now that you’re all set up, you can start writing your framework code. You do this the same way you’re used to except there is one major difference. In a framework, any code you want to expose to your main application should be marked as
public. Code that isn’t marked as
public is considered
internal and can only be accessed by your framework. Code marked as
private still works the same, it can only be accessed by code that lives in the same file.
The big upside of this is that your app can’t know more about the framework then it should. That’s already a big win in terms of modularity.
Writing tests for your framework
Because your tests can only access the public code in your API it’s wise to use your tests as the place where you develop and test your framework. By this I mean that your tests will be the first client that uses your framework. Developing your framework like this makes sure that your framework has great test coverage and you’re testing your framework in isolation from the rest of your app. This forces you to think about your framework instead of the entire app.
Using the framework in your app
Since Xcode has already linked your framework to your app, using your framework works just the same as using any other framework you’ve used. Any file that uses your framework needs to add an
import for the framework at the top of the file and from that point out your framework is available to rest of your code.
As you’ve seen, adding your own frameworks to your project isn’t very complex and it forces you to think of your app in a different way. Instead of creating an app where any object can use and access almost any other object you’re creating an app and one or more frameworks. Each of the frameworks you create can function independent from the rest of your app. This means that you could eventually add the framework to other projects or even open source it if you’d like.
Testing your framework is also a lot easier because you know which parts of codes are the API that’s public and those should be the first parts you test. If all you write tests for is your public API, you know that using your framework should work as expected and intended before you even use it in your app.
The approach outlined in this post is especially useful if you only want to use your frameworks in a single app. Using them in multiple apps would require a slightly different approach because you would want the frameworks to live outside of your main project but the benefits and most steps should be very similar.