My talk – with presenter notes – from NSBudapest meetup held as part of Craft Conf 2018 in Budapest, on May 10th.
If you need version without presenter notes:
It's a short dive into a proper use of Coordinators and Layers architecture to really separate View Controllers into manageable, isolated units.
M I N D - M A P
Y O U R A P P
A l e k s a n d a r Va c i ć
C R A F T 2 0 1 8 · N S B u d a p e s t
Mind-mapping an app…”what the heck that even means”.
I’d like to think of it as somewhat new approach to architecting an app. Or maybe it’s an old approach, dusted-out from the basement of programming.
You decide what it is, at the end.
App starts with an idea.
A business plan would help too,
but let’s not digress too much.
App sprouts from an idea. I see a problem that I feel I have a good solution for.
Thus I usually start sketching on paper (and obviousy I’m not the only one).
If you’re lucky to have a proper designer in the team, this rough phase is followed up wirefrmes, where you ﬂesh-out the user stories.
Then User Experience is detailed out, with layouts for each screen and transitions and connections between them.
Finally look & feel is applied on top of the wireframes.
So, we now need to code this up.
UI / UX is done.
The only thing left: code
It is my ﬁrm belief that the best code is the code you don’t have to write nor maintain.
Thus before jumping into Xcode, I ﬁrst spend as many days as needed to plan code: data models, data ﬂows, what each of those user stories and
workﬂows needs to make them a reality.
This is crucial high-level step as it helps to identify data types before you get deep into the boilerplate code required for collection views and what not.
Some teams use post-it notes. It’s a..good tool..to ﬁgure out the details: where each screen ﬁts inside the app, what initiates its display, what data is
needed to display the content, is data coming (a)sync etc.
After days of brainstorming and moving little colored paper slips here and there – now you know what View Controllers you need. Where you need to
implement Delegate pattern so data can ﬂow back. What VC will push which other controllers. What’s being presented as popup and when.
Code is done.
What’s left to do is the grunt work of actually writing all that code.
It should be easy, peasy, now…right?
If only. Heh…
And why: because this is ﬁction. I don’t know a single, even moderately complex app that is this clean.
I mean – just look at this lovely UX we have here. You have a home screen, views loads stuﬀ in proper order: promo blocks, some content categories, nice
browsing system; then somewhere along the line we will have Add-to-cart, display Cart, check Account status (and login if needed) then UX ﬂow proceeds
with Payment etc.
If you follow UIKit by the book, this means you will HomeController, which will instantiate & display ProductController and give it a list of Products.
Where, after some cell tap, a ProductDetailsController will be instantiated and passed a single Product from the received list.
Etc. You are happy, there’s a clear path how the app works.
So you implement all this, beta 1 is ready and you send it oﬀ to all stakeholders for approval and to show oﬀ the progress. And then…
They congratulate you and tell you that app needs to be able to jump directly to the Details screen all the way down here. You need to deep link a product
and needs to be shown instantly after the app start. But, you may say, app needs to load the products list ﬁrst, then to instantiate the intermediary VCs and
fetch data sources and only then it can show the product…And they look at you, like “ok, yeah…?”
Weather you manage to charge the client for that feature creep and change – you still need to do it.
With the UIKit as it is, you will start to wing it.
Most likely add some property called deepLinkProduct and then if it’s not nil DetailController will be pushed automatically onto UINC stack without even
showing the Home and Products browse…
Architecture of complex apps is
ever evolving, quite often in
Then you follow it up with a Boolean ﬂag here. Another property there. Few more ifs in the business logic of the app to accomodate another new user-ﬂow
that comes along.
Over time you add far too many special cases. Your code is a mess where not even you can ﬁgure out all the code paths.
– M A R T I N F O W L E R
“Architectural refactoring is hard, and we’re still
ignorant of its full costs, but it isn’t impossible.”
Patterns of Enterprise Application Architecture
Hence, quite possibly even before you reach ver 1.0, you may need to re-factor your app’s architecture. Depending on what comes up, this may be a partial
or total rewrite.
If you think – well, it can’t possibly go really, really bad… – think again.
In order to avoid an insanity like this – that’s a real story, not ﬁction – you need an architecture that will allow re-factoring of any complexity.
So: how to prepare yourself for any possible scope change in the project lifetime? In an app of any complexity? (Really inﬁnite).
O N I O N
Your app needs to be an
(Psst - I know this is garlic, but bear with me)
Your app needs to be an onion.
Lovely, beautiful onion with layers as thin as they can be, so if you add one or remove or replace one it does not break the overal structure.
You need as composable architecture as possible, with ability to seamlessly plug-in layers that you need.
Each layer should be self-contained, as much as possible.
(1) data storage separated from data sourcing
(2) data source separated from data delivery
Because if you combine any of these domains, you are bound to get into a situation where you will repeat the same code in multiple places or be entirelly
unable to implement a feature.
What’s Delivery mechanism? It can be network (URLSession) or it can be ﬁle(s) loaded from the Bundle. Or app’s sandbox. This layer have no idea what it
deals with - it gets binary Data blob and transfer it to the Source layer.
Source is usually an API. And another API. Or wrapper for a ﬁle hierarchy or chunks of data from socket streaming. It knows what Data blobs actually are
converts them from Data into JSON or XML. Something that Data Manager can work with.
Data Manager handles Storage which can be Realm or Core Data or in-memory store. This module convert the raw data formats into classes, structs and
enums – your Model objects: Product, Cart, Item, Account, Customer, Song, Album etc.
Anything that comprises your app’s internal data model.
Let’s look at UX ﬂows from before. Everything is sort of ﬁne…
…until your controllers need to jump hoops.
Add Product to Cart. 1-click buy which jumps to Payment. Etc.
VC represented by just one independent post-it note is a lie, because it’s not independent, it’s glued, hard-wired with other VCs around it.
This pile of stuﬀ is (one of) the reason for the so-called MassiveVC problem. The problem does not stop there though.
What’s missing here..?
Where are all the data paths between UI and Data – if I add them, this will turn into a ball of twine…
N O T M Y FA U LT … H O N E S T !
It’s messy as hell.
All that wiring going in and out – that’s not the job for the UIViewController.
Starting and instantiating UIVCs – that’s also not the job for the UIViewController.
(This is why I can’t stand UISegue and wish I could burn it out of existence).
UIKit is digging its own hole here. Let’s stop digging and ﬁrst get rid of this mess…
Every one of these boxes should be unaware of any other box.
Your VCs should be entirely boxed-in. They take some data and use to display a view. They recognize signiﬁcant actions inside the views and pass them
along…to something else. They don’t care who is going to react to those actions – their job is to recognize them and pass them along, up the chain.
If VCs deal with just one task, they become small and manageable.
Content VCs shoud not care where they are. In NC, presented as popup, inside PageVC, they literally don’t know nor care.
But someone else needs to handle these connections, something else should control the UX/data ﬂow…
…and that’s where Coordinators comes in.
Their only job is to be the wiring between the global data store for the app and various views those data are shown in. Between the VCs. To move the data
We have one more missing layer here…
…which I call middleware managers.
These are things like AccountManager which handles what Customer is currently logged in. CartManager which manages the one speciﬁc Order being
built. Etc. This is the business logic of your app.
Pay attention – this is not Core Data’s job. Data Storage is fairly simplistic, it reads and writes data but does not handle data transactions. For example: it
knows to create an instance of some Customer object when requested to do so by AccountManager, which knows that this particular instance is specially
important because it’s a logged-in Customer. DataManager could not care less, it creates the objects, passes it on and forgets about it.
(Middleware sounds a lot like micro-services which are all the fancy in web backend world right now).
If you manage to separate all this,
then you don’t need colored post-it notes.
You can mind-map in code, no intermediary
prototyping tools required.
Code Mapping, enabled by…
(If you manage…)
(You can mind-map…)
You just need Xcode and your knowledge of Swift and UIKit.
L AY E R S
A R C H I T E C T U R E
So let me introduce you to my implementation of LAYERS architecture with middleware and a special kind of Coordinator implementation.
I promise you it is really this yummy, unlike the onions. Or garlic.
Swift implementation of Coordinator pattern, with
several advanced features
Thin URLSession wrapper which allow seamless usage
with Operation and OperationQueue
Various useful extensions, particularly AsyncOperation
subclass of Operation
It’s loose puzzle of several Swift micro libraries, all available on my GitHub repository.
Instead of showing you dry code and explaining those libraries which may take an hour or so, I’m going to show them in action. Which I hope will be
enough to pick your interest and then you’ll ﬁgure them out on your own.
Let’s demo this approach by code-
mapping a bespoke Spotify app…
in 15-ish minutes
Follow me as @radiantav on Twitter.
Read my iOS dev blog at aplus.rs.
Use my open source stuff on GitHub.com/radianttap.
Photos (from Unsplash) by
• delfi de la Rua
• Otto Norin
• Mike Kenneally