Unlock This Episode
Our Free plan includes 1 subscriber-only episode of your choice, plus weekly updates from our newsletter.
We have now completed a full modularization of our application. What used to be a single application target with 8 Swift files and many hundreds of lines of code is now 7 Swift modules, each with just one or two files, and each file under 200 lines except for our SwiftUI helpers file and UIKit files.
Previously any change to a file, no matter how small, would trigger a build of the application target, and we would just have to hope that Swift’s incremental compilation algorithm was smart enough to not build more than is necessary. Swift’s incremental compilation is really, really good, but there are still times it gets tripped up and a build will take a lot longer than you expect. Or worse, if you need to merge main into your branch to get up-to-date with what your colleagues are doing, you will most likely trigger a full re-compilation of your entire project because many things have probably changed.
Now, with feature modules, we have a lot more control over what gets built and what doesn’t. If you are deep in focus mode on just the item view, then you can choose to build only the “ItemFeature”. Then you should feel free to merge main into your branch as often as you want because, at worse, you will only trigger a rebuild of the “ItemFeature” module, which is a lot smaller than the full application. This can be a huge boon to productivity.
But, we can take this even further. Right now the “AppFeature” has a pretty significant amount of logic that spans the responsibilities of many feature modules we have just created, and that’s the deep linking functionality. The “AppFeature” is handling deep linking for the entire application, even though the only view the module holds is a tab view, and the only deep linking logic important for that view is to figure out which tab we should switch to. All the other deep linking logic just delegates to
navigate(to:) methods that are defined on the child view models.
What if we could fully modularize our deep linking logic? Not only would we move the
navigate(to:) methods to each feature’s view model, but we would even move the parsers themselves to the feature modules. That would mean we could even work on parsing and deep linking logic in complete isolation from the rest of the application, which would be pretty incredible.
Let’s give it a shot.
Add a “Routing.swift” file to the “ItemFeature” target, and extract item-specific routing to it, like the
Create an “InventoryPreviewApp” that allows you to run the full inventory feature in a dedicated, sandboxed application. Make it capable of deep linking through a custom URL scheme.
An article from Increment magazine about modularizing a code base into small feature applications:
How an emerging architecture pattern inspired by microservices can invigorate feature development and amplify developer velocity.
Once you modularize your code base you can begin uncovering new ways to speed up build times. This tool from Spotify allows you to cache and share build artifacts so that you can minimize the number of times you must build your project from scratch:
At Spotify, we constantly work on creating the best developer experience possible for our iOS engineers. Improving build times is one of the most common requests for infrastructure teams and, as such, we constantly seek to improve our infrastructure toolchain. We are excited to be open sourcing XCRemoteCache, the library we created to mitigate long local builds.
We previously discussed modularity and modern Xcode projects in our tour of isowords.
isowords is our new word game for iOS, built in SwiftUI and the Composable Architecture. We open sourced the entire code base (including the server, which is also written in Swift!), and in this multi-part tour we show how we’ve applied many concepts from Point-Free episodes to build a large, complex application.