Modular iOS Part 3: Configuration & Testing of Modules
By: Sam Dods
Tags: iOS, Xcode, Swift, Engineering, Engineer

In the previous articles in this series I discussed the benefits of splitting your iOS codebase into modules and how to achieve this. I recommend reading those articles before continuing with this one. 😎

In this article, I’ll cover:

Setting up CocoaPods with multiple modules

Testing feature modules as part of the main app

Sharing test logic between modules

And I’ll save Sharing configuration between modules for the final post in this Modular iOS series.

Setting up CocoaPods with multiple modules

If you use CocoaPods, you need to ensure the dependencies are installed for each target that needs them. And all dependencies must be installed for the main app target.

To avoid having to update your pod version numbers in multiple places, it’s good practice to define reusable methods. Given that a Podfile is just Ruby, you can do this like so:

See the networkAndParsingPods and uiPods methods in the Podfile above. They are referenced in the target sections. This removes duplication of pod references, and avoids any chance of a discrepancy of pod versions.

Testing feature modules as part of the main app

Typically, you’ll want to run the unit tests for your feature modules when the main app’s unit tests execute. This means at time of development, you don’t need to switch schemes to test different feature modules. And in terms of continuous integration, you simply specify a single scheme’s unit tests to execute, and all dependent modules’ tests are run at the same time.

You achieve this by editing your primary scheme.

Select your scheme (i.e. FunkySocial in the image above) and hit Edit Scheme…

Select Test from the panel on the left, and select the + button at the bottom of the panel that appears on the right.

From the window that appears next, select all the test targets you wish to run at the same time as the main app’s tests.

When you’ve selected all the test targets, hit Add.

You’ll end up with something that looks like this:

Sharing test logic between modules

Just like we share common production code between modules, by exposing it from a Common module, there may be a need to share common test logic. We don’t want to put this in Common , because it’s not production code.

So instead we define all our shared test logic in a TestHelpers module.

This module might contain such functions as loading mock JSON from a file, or the creation of shared mock objects.

The linking of the TestHelpers module to your test targets is done slightly differently from embedding the feature modules into your main app.

Select the test target, e.g. ProfileTests . You’ll notice on the General tab that there is no way to add embedded binaries or linked frameworks.

Instead go to the Build Phases tab. Expand the Link Binary with Libraries section, and hit the + button. You’ll see a dialogue like the one below:

search for your TestHelpers module, and hit Add.

Do this for each module between which you need to share code.

Coming up next

In the next article in this series, I’ll present a robust, tried and tested method of sharing configuration between the main app and your feature modules, and how to inject this configuration for unit tests.

If you’re interested in solving problems like this, and want to join the most passionate, collaborative iOS team in London, head to our careers page! 😍