Much ado about iOS app architecture
I have an opinion. Yes, I know that joke.
Nov 5, 2017 by Aleksandar Vacić 6 minute read
I started as indie developer in 2008, making fairly simple apps with just several screens in them so I did not need to think much about app architecture and was happy to use MVC as Apple recommends. In last two years, I did multiple large scale projects, big apps with dozens and dozens of more or less interconnected parts. This naturally led me to research the topic of architecture quite a bit.
Cocoa Touch is (I suspect) deliberately very simple: there’s Target-Action pattern, Dependency Injection, Delegate and MVC. That’s about it, really. Apparently it’s way too simple for some.
Of those patterns, MVC is most visible; it used to be the only kid on the UI block when the platform was simpler; that block became very crowded with acronyms like MVP, MVVM, VIPER, Redux, Cake and specialized roles with names like Router, Presenter etc, unidirectional this and that…
Most of these have truly noble goals, on paper. They aim to cleanly separate data model, model-view communication, view itself and also the controller that connects the view and the model. They aim to turn the views into declarative API where you puzzle the pieces and the rest of the app follows along.
Diving deeper and deeper into all this I increasingly felt that people are over-complicating what UIKit has to offer — an API proven to work and perform well on its target platform. People were not trying to improve UIKit, they were actively fighting against it.
Why does every single article, describing any of these new architectures, starts with a variant “Apple’s (sic!) MVC leads to a bloated M(assive)VC”.
🙄
When I read that, I see: here’s another wannabe who never bothered to learn how to properly Delegate. Who never learned to use container controllers and compartmentalize functionality into embedded controllers.
PSA: No one is forcing you to implement multiple DataSources in one Controller. To initiate network calls in viewDidLoad. To parse JSONs in UIViewController. To hard-wire Views with Singleton instances.
If you do that, it’s your fault; don’t blame MVC.
…
This pisses me off a lot lately, since I’ve already seen it.
I spent my first 15 professional dev years in front-end web development. I witnessed how simple, declarative world of HTML/CSS and original baby Javascript morphed over the years into increasingly complicated quagmire of “advanced something” until most front-end developers forgot how CSS selector specificity work. How property value inheritance works. Developers couldn’t be bothered to learn it because apparently they can’t do shit without variables.
I can’t shake the feeling these same kind of Javascript architects descended into iOS in full force and with them brought the habits of creating new amazing · best · evar library every few months.
Or maybe they are J2EE devs. It would be a crime to paraphrase that comment thus I’m simply going to quote it (emphasis mine):
No. VIPER is what happens when former enterprise Java programmers invade the iOS world. It imposes so much abstraction and structure that maintainability of code is reduced, not improved. I mean, honestly, can anyone really look at this and think, “yes, all these extra classes and protocols really improve my ability to reason through this code”?
Just look at the section starting with “Implement Expiration Date Business Logic in the Interactor”. The following protocols and classes are involved in reading a date out of a UIDatePicker and displaying it in a UITextField:
CreateOrder_FormatExpirationDate_Request
CreateOrder_FormatExpirationDate_Response
CreateOrder_FormatExpirationDate_ViewModel
CreateOrderInteractorInput
CreateOrderInteractorOutput
CreateOrderInteractor
CreateOrderPresenterInput
CreateOrderPresenterOutput
CreateOrderPresenter
CreateOrderViewControllerInput
CreateOrderViewController
For the love of god, why?! Does no one remember how Java’s Spring Framework ended up with
AbstractSingletonProxyFactoryBean
? That way lies madness.
(I deeply bow, n0damage.)
So much wasted effort to present new approaches as better alternative to UIKit, while building on top of UIKit.
Rules of engagement for maintainable projects
Let’s turn the mood of this post into some positive action. Here’s something I learned while working on half a dozen “enterprise”-level apps.
(picked up from Reddit)
1. Write as little code as possible
This is funny but also true – strive to write as less code as possible. Really is that simple.
If you make this your no.1 rule, things become very, very easy to decide. Many chiq new architecture approaches fall flat when faced with this rule.
2. Don’t fight the SDK / tools
- Use patterns that iOS frameworks – especially UIKit – are already based on.
- MVC, Delegate, Dependency Injection, Target / Action
- Extend MVC with Coordinators and
coordinatingResponder
methods to handle data flow - This lets UIVCs have just one job to do.
On the topic of tools:
- Writing UI in code violates the rule 1
- Use .storyboard / .xib for the UI as much as possible. Do not write UI in code unless really, really, really necessary.
- Do not use storyboards for data flow. Use them as glorified .xib, housing one or two View Controllers.
3. Don’t replace system frameworks
Use frameworks that Apple made, don’t augment nor replace them unless absolutely necessary.
One example: if there’s one thing iOS community does not lack are attempts to rewrite or even entirely replace Auto Layout. While I am fascinated by these libraries and learn a ton looking at that code, I would never used them in any serious personal app and absolutely never in client project.
I consider Auto Layout one of the top 5, maybe top 3 technologies to ever come out of Apple. It’s beautiful, easy to work with and there’s nothing I can’t do with it. It’s Apple’s choice for UI layout and you should master it, not avoid it.
Let’s not repeat the CSS mistake from 10 years ago.
4. Lean on Swift expressivity
Extract away the implementation details as much as possible, to keep the code DRY as much as possible. Swift is wonderfully expressive, use that to your advantage.
But mind the rules no. 2 and 3: I’ve seen some fascinating Swift code which almost entirely rewrites how essential components like UITableView
and UICollectionView
are fed and controlled. To the point of needing to re-learn how to work with those components.
If you do that, you are increasing the steepness of the knowledge curve for new team members, increase time to explain how app works, add yourself (needless?) tasks each June when Apple adds new fancy abilities on top of those system components.
5. Use as little 3rd party dependencies as possible
Swift is powerful and extensible, but you can’t always avoid using a 3rd party open-source library. My most recent projects use just a handful of 3rd party libs, each with very specific purpose.
- The ones I use the most are wrappers around low level C-based system frameworks: Crypto, Keychain.
- Then also those that help with tedious and error prone tasks, like JSON parsing where I currently prefer Marshal but will see what Swift 4’s Codable brings along.
- Then there’s TinyConstraints, the tiniest possible helper for Auto Layout in code, for very rare cases I need to do that.
6. Use consistent code formatting
...one style to rule them all.
- Always place the code elements at the same place
- Use the same order
MARK:-
up consistently and liberally
Order of stuff in my UIVCs:
- Delegates
- Init / deinit
- UI outlets
- Dependencies (if any)
- Local data source
- Embedded Controllers (if any)
- Notification tokens
- Commented-out list of
coordinatorResponder
methods that particular file is overriding - Any additional stuff
If I can’t figure out all the inputs just by scanning the top of the file, it’s not good.
When in doubt
Q: Should I write clever or obvious code?
A: Always be obvious!