Simplicity Via Complexity
After iOS 8 introduced dynamic frameworks I started using them extensively in my own projects. I don’t just use them for reusable libraries, I also use them to separate the layers of them app. Going a step beyond that, I even broke the different sections of the app into frameworks, each of which linked to the Model framework, shared view style framework, and the appropriate segment frameworks.
The fact I was nine months into the project when I did this meant that it required some refactoring, but overall it led to a cleaner app structure, and it made sure that I couldn’t cheat and blur the lines between model and view. It made it possible to have framework-internal functions and classes that nothing outside of that framework could see.
Limiting the public exposure of a framework was a huge benefit. A great example was the product search UI. The only public declarations for the entire module were a function initialize the root view controller with completion and cancel blocks. Everything else in that UI, which included multiple search strategies, results sort/filtering, product info, and more, was a black box that nothing else in the app had access to.
Framework headers can buy you a dramatically lower number of
#import statements if you’re working with a model that includes many moving parts. This is doubly true if the model uses categories to extend the model objects. If you
@import a framework that the section of the app that you’re working with doesn’t already include, you’re going to get an error in the build process, which adds a layer of sanity checking on “should I be importing this?”
The Sticking Points
One downside to this is that the “build dependancies” phase of my project was a bit baroque, the build order required me to think through the directed acyclic graph of which frameworks are required by which other frameworks, and therefore must be built before the next could be built. That being said, once that was done it wasn’t an issue again, and with good planning it shouldn’t be a pain point. As a reference, I plan to separate my game into
Controller (or possibly
In an interesting confluence, I was reading Anathem while working on the first project that I broke into a series of separate frameworks, so the fact that I was working with a directed acyclic graph stuck.
The other issue that comes to mind is that I broke the project into separate git repos, then imported those as submodules into the main project. Carthage didn’t exist when I did this, and Cocoapods didn’t support dynamic frameworks yet, so I was on my own for anything resembling dependency management. The whole process got fiddly fast. I don’t recommend this, doing it again I plan to keep everything in one repository and xCode project until the code matures. When modules get mature and won’t change often, Carthage can handle working with submodules nicely.
The first attempt worked well, I’m hopeful that my second attempt with planning from the ground up, better tools, and more planning on module flow will really shine.
For those of you who missed my post last night, I broke out the game update for the week into a separate post, I plan to keep those going up on Thursdays from now on, schedule for just around noon in the future.