Article: App Architecture, iOS Application Design Patterns in Swift Review and Author Q&A
MMS • RSS
Article originally posted on InfoQ. Visit InfoQ
- The book tries to give a gradual introduction to each covered architecture starting from a general description of their features, then going into a more detailed discussion.
- The architecture of an application defines how it integrates different services, such as event handling, network and file services, graphics and window management, and more.
- Covered architectures go from the ubiquitous Model-View-Controller to a few more experimental ones, such as the Elm architecture.
- The key differentiator between architectures is the way they handle state representation and change.
App Architecture, iOS Application Design Patterns in Swift by Chris Eidhof, Matt Gallagher, and Florian Kugler presents a number of architectures for iOS and Swift applications, from the ubiquitous Model-View-Controller to a few more experimental ones. The book is accompanied by a set of videos that are available as a separate purchase and add a live-coding dimension to the book content.
The architecture of an application defines how it integrates different services, such as event handling, network and file services, graphics and window management, and more. The book targets both experienced and novice iOS app developers by providing ground-up explanations for all relevant concepts, including for example why model and view are such fundamental concepts. Furthermore, the book tries to give a gradual introduction to each covered architecture starting from a general description of their features, then going into a more detailed discussion.
The first architecture presented is the well-known Model-View-Controller (MVC), which is standard in Cocoa and aims to decouple the internal representation of information (model) from the ways it is transformed (controller) and presented to the user (view).
The second architecture, Model-View-ViewModel+Coordinator (MVVM-C), is a variation of MVC that stresses the importance of having multiple view-models, one for each view, and a coordinator in charge of dealing with transitioning from a view to another.
The next architecture presented, Model-View-Controller+ViewState (MVC-S), is a more experimental one, based on property observation and aiming to streamline view state management and to make it more manageable by explicitly encoding each view state change in a view state object. This makes the data flow through your app more unidirectional in comparison with MVC, where it is usual that views store part of their state and are taken as a source of truth regarding their state. On the other hand, in MVC-S, the view state is the source of truth regarding the view state.
The fourth architecture presented, ModelAdapter-ViewBinder (MAVB), is another experimental pattern that fully leverages the idea of bindings, which lie at the foundations of Cocoa. In MAVB, you construct view binders instead of views and only deal with state in model adapters. A view binder wraps a view class and exposes a static list of bindings that affect the underlying view.
Finally, the book presents the Elm architecture (TEA) in the context of an iOS app, which is a more radical departure from MVC. The basic idea in TEA is encoding both model and view state in a single object, then change the app state by sending events to this object, and update the view through a virtual view hierarchy, which is derived from the state object. The appeal of this approach lies in the fact that the code that generate the view can be a pure function that computes the view hierarchy without any side effects.
The book describes the main features of each architecture and their pros and cons by implementing in five different ways a reference application that is able to record and play back voice notes. The reference application strikes a balance between complexity and simplicity to provide the right amount of information to the reader.
As mentioned, the book authors have recorded a collection of videos to accompany the book and that can be purchased separately. The videos offer over 8 hours of in-depth discussion covering all six architectural patterns, showing how to add a new feature to each of the five versions of the recording app, and going through the whole process of implementing a simple app for each of the covered architectures.
InfoQ has taken the chance to speak with Chris Eidhof, Matt Gallagher, and Florian Kugler to learn more about their book.
Could you summarize your background and your experience with iOS app architectures?
Chris Eidhof: Ever since the release of the first public SDK, I’ve been developing apps for iOS, first in Objective-C and later in Swift. I’ve seen very different kinds of architectures with both clean and messy code bases. Before writing code for iOS, I was deeply into functional programming. Today, I have large functional code bases, as well as large object-oriented code bases (and everything in between!).
Matt Gallagher: I’ve been writing iOS apps professionally since the App Store was unveiled in 2008. Prior to that, I was a professional Mac developer. Over the last decade I’ve written iOS apps in many different styles, with many different teams, using many different architectures. I’ve written nearly 200 articles on Mac and iOS development on cocoawithlove.com
Florian Kugler: I’ve been working on the iOS and macOS platforms for the last six years, and on various other platforms before that. Application architecture is a concern that always has been part of my work, although more on an implicit level until I started to work on this book.
What makes this book necessary? What gap does it fill?
CE: UIKit is designed for MVC. However, outside of the iOS world, people have been writing apps in very different architectures, such as MVVM (on Windows), or React (on the web). We want to give an overview of the different architectures, as well as a thorough description of how these architectures work, what their philosophy is, and what they look like at the code level.
MG: In the first 5 years of iOS, I feel that programmers were mostly trying to keep up with iOS itself, doing anything they could to get things working. Quality, maintainability and good architecture were secondary considerations.
In the last 5 years, people have started to realise that an “anything goes” approach leads to code being thrown out every few years, and it might be better to start with a clear, maintainable approach. To do this, you need a better understanding of architecture, so you can continuously refactor to address limitations and maintain the code.
FK: Application architecture is often a topic where people have very strong opinions about the “best” way of doing things. We wanted to take a more neutral look at a wide variety of design patterns, because we believe that the same problem can be solved in many different ways with different trade-offs. We don’t want to sell you on one particular pattern, but present the similarities and differences of various patterns, from the conceptual level all the way down to the implementation details.
In your book you describe five different architectures. Besides the standard MVC and its variant MVVM-C, you also consider three more experimental architectures. Can you explain where those originated and how you chose them?
CE: We wanted to show ideas from different architectures used outside of the iOS world. For example, The Elm Architecture is centered around view state, and uses a virtual view hierarchy to describe how to translate the view state into a view hierarchy. It needs quite a bit of infrastructure, but the resulting application code is very small.
MVC+ViewState takes the idea of centralized view state, and applies it to MVC. It does not have a virtual view hierarchy, but works with regular MVC components.
MG: I’ve been working on ModelAdapter-ViewBinder (MAVB) for a long time. I first started experimenting with some of its ideas in 2015. The premise started with defining everything about an object — in particular, views — on construction, instead of through mutable property changes, method overloads and delegates. These require a huge amount of knowledge about the implicit behavior of UIKit objects and it’s too easy to conflict with this implicit behavior, or your own behavior, or otherwise make mistakes. Reactive bindings, and encapsulating all state in model-adapters, flowed very naturally from the idea of configuring all behaviours on construction. Through this, the CwlView library and the MAVB was born.
I showed the CwlViews library to Chris Eidhof when we were both at the Playgrounds conference in 2017. The code was still a mess at that point, but it led to us discussing application architectures and eventually led to the book.
MVC+ViewState was an effort I started during the writing of the book to bring some of the ideas from The Elm Architecture and MAVB back to MVC. I consider it a teaching architecture; it shows how view-state can take a more major role — particularly around navigation — without requiring any functional programming or abstract ideas like reducers.
FK: We chose the experimental design patterns primarily because we found them to be sufficiently different from the more common patterns: they stretch your comfort zone while still being instructive and providing applicable lessons.
In broad terms, when should a developer stick with one of the two more traditional architectures and when should they check into one of the experimental ones? Do the experimental architecture fill a niche or are they as general as MVC?
CE: In a production code base, we wouldn’t recommend using an experimental technique for something as big as an architecture. That said, it is definitely possible to write production apps using any of these architectures. We believe it is important to really understand these architectures first, and choose which architecture you want second.
For each architecture, we have described lessons in the book that you can bring to other architectures. For example, we show how you can use coordinators with MVC, or how you can build a declarative view hierarchy (similar to TEA or MAVB) for parts of an existing app.
MG: Given that UIKit/AppKit are built to expect an MVC approach — and most sample code you find will also be MVC — I think most people should start with MVC. If you’re writing code with novice programmers, you should stick with MVC. Experience is probably the best guide: if you’ve written projects in the past that suffered from issues, try something new and see if you can address those issues. There are different flavors of MVC including with ViewState, MVVM in minimal, no-Rx, and full-Rx; if you want to try something more experimental you can fully test out our TEA or MAVB approaches, or simply take lessons from them.
As for whether the experimental architectures are niche, or otherwise limited, the only significant limitation is that Chris’ Elm implementation and my CwlViews implementation doesn’t cover all of UIKit. The patterns themselves are robust and scalable. If you’re prepared to add additional framework functionality as needed, these approaches will scale and handle all common scenarios.
FK: The experimental architectures are as general as MVC, but they come with the usual trade-offs when deviating from the mainstream on a certain platform: you have to build and maintain the necessary framework code yourself, there are less resources out there, and there’s a steeper learning curve for new developers in your team. You should definitely try out experimental patterns on a small scale before committing to them on a large project; you need to evaluate if you have a sufficiently good reason to incur the costs of deviating from the norm.
Could you shortly summarize the strengths and shortcomings of each of the architectures you present?
MG: MVC is simple and works well with Apple’s frameworks but requires significant vigilance to maintain data dependencies, and it requires skill to keep classes small and modular.
MVVM-C maintains data dependencies well, especially presentation dependencies, but some people find RxSwift confusing. Navigation logic and interaction logic are not as well covered in the pattern as presentation logic.
MVC+ViewState is almost as simple as MVC and handles navigation in a very elegant way. However, it requires a lot of observation functions, and it doesn’t include default tools for managing complex observing callbacks.
MAVB is syntactically efficient and has a clear story for all data ownership and dependencies. However, if you find Rx transformations difficult to read, it’s going to be terse and confusing.
The Elm Architecture describes an entire program through value types and a small set of transformations — it’s very simple to reason about. However, it is highly framework dependent and the framework we used is very rough, implementing only what is required for our program, and its implementation is likely to be a bottleneck until a robust, widely used framework becomes established.
FK: MVC has very little overhead and gives you a lot of freedom to make your own implementation choices, which is its weakness at the same time: you have to fill in the gaps and be disciplined to keep your project maintainable.
MVVM-C encourages you to think about your program in terms of data transformation from the model to the view and provides an easily testable interface to your view data. On the flip side, MVVM projects often require you to understand a reactive programming library.
In The Elm Architecture you describe your views declaratively based on the current state of the application. This eliminates a lot of glue code and the possibility for mistakes that come with a mutable view hierarchy. However, TEA requires a framework wrapping UIKit that has to be maintained, and animations are a non-trivial problem to solve.
The ModelAdapter-View Bindings architecture has a striking resonance with one fundamental Cocoa concept: bindings. Bindings are also a feature that is greatly supported by a language like Objective-C. Generally speaking, do you see any differences across the architectures you cover from the point of view of which language the app is written in?
MG: All the features of Cocoa Bindings could be implemented in Swift using Swift key paths and protocols. The difference in Swift, compared to Cocoa, is that you must declare the protocol conformance at compile time, instead of blindly sending messages and hoping they’ll work at runtime. It’s a little more code but the reliability saving is worth the effort.
Ultimately, I think people used Objective-C’s dynamic message send and dynamic class construction because they could, and not because there was no other way. It produced a few ‘cute tricks’ but the reliability cost was real, which is why Swift has chosen to drop support for the most dynamic features.
FK: In theory, all architectures could be implemented in either Objective-C and Swift. However, Swift’s support for value types (structs and enums) is of great help when implementing more functional patterns like the Elm Architecture, and unidirectional data flow patterns in general.
Has the introduction of Swift shifted the architectural paradigms for iOS app development?
CE: Absolutely, yes. Features like structs, enums, and Swift’s type system enable us to write code in a very different style. For example, an architecture like Elm would be very impractical in Objective-C. At the same time, higher-order functions are more common in Swift (by now, everyone uses methods like
filter). This makes the barrier for libraries such as RxSwift or ReactiveSwift much lower.
MG: Yes, without doubt. Swift has a much more sophisticated type system, and it can create more compile-time guarantees. The compiler can prove more of your program’s validity, but only if your architecture is written to encode inter-component behaviors in the interfaces between components.
It’s a complicated way of saying that generics and composition over inheritance are improving reliability and reusability.
Of the architectures you decided not to cover in the book, what are the most promising in your opinion?
MG: I think a minimal MVVM (using Swift key path observing) is a light-weight, easy to understand architecture. It doesn’t solve a huge number of problems compared to MVC but it does solves some, and when talking about the book to lots of programmers it’s clear that reactive programming is a big sticking point for many; they’d rather something lighter.
Short of starting off developing a new app, what is the best way for developers to begin experimenting with any of the more experimental architectures you present?
CE: The book comes with a lot of sample code. Throughout the book we show the Recordings sample app, and we wrote this app in all of the different architectures. You can quickly get started with an architecture when you add a new feature to the sample app.
Another great way is to take an existing app and rewrite part of it in a new architecture. By working in a domain you know, you can focus solely on the code.
MG: The book offers many “Lessons” at the end of each chapter, which focus on taking the ideas of that chapter’s major pattern and applying them to small components, usually within an MVC app.
FK: Often you can experiment with different patterns in parts of an existing app, like a settings screen or something similar that’s relatively independent. Additionally, you can take ideas from other patterns and apply them within the frame of you current architecture.
Choosing the right architecture is usually a key factor in determining the overall success of an app (including factors such as development model, performance, time to market, etc.). What should developers take into account to maximize their project’s chances of success when choosing the right architecture?
CE: The more familiar you are with an architecture, the easier it will be for you to write correct, clean, and maintainable code. However, some architectures really help to eliminate common problems, such as observation, or dealing with view-state. We believe you should understand the different architectures, and then look at how they would work in your context. For example, whether your team enjoys object-oriented programming or functional programming can make a big impact on deciding which architecture to use.
We don’t believe any of the described architectures is “best” or “worst”. Instead, we think you should evaluate for yourself.
MG: The best architecture is the one that your team wants to work on. Find out what your team likes and let people have fun.
FK: I’d argue that social factors mostly outweigh the technical ones in terms of the “best” architecture for a certain project. Experience with a certain pattern, general skill level, and personal preferences amongst many other factors play an important role in making a good choice.
Finally, a word about your decision to create a series of companion videos to the book. What was your goal with them? What value do they provide and to which audience?
CE: The videos are really quite different from the book. In the book, we can give an overview of the architectures, describe the ideas and code, and give a lot of background information. The videos show what it’s like to work in each architecture. For example, we add a mini-player feature to the recordings app, once for each architecture, and we need to touch almost every part of the code base to build this.
MG: Chris and Florian made a compelling case. I’d never previously recorded videos except for conference talks. We wanted to work on some more “interactive” ideas, relative to the book which is inherently static. The result was a number of refactoring videos. I also developed an “8 patterns in a single file” idea; I wanted to try and distill a number of patterns down to their most basic core, and touch on some patterns that we didn’t cover in the book. I think it worked out really well!
To read more about the book and the videos, or to buy them, visit objc.io.
Chris Eidhof is one of the objc.io co-founders, and host of Swift Talk. He also co-authored the Functional Swift and Advanced Swift books. Before, he wrote apps such as Deckset and Scenery.
Matt Gallagher works as a software developer and consultant based in Melbourne, Australia; both independently and through his streaming media technologies company Zqueue.
Florian Kugler is one of the objc.io co-founders. He worked on Mac Apps like Deckset, co-authored the Functional Swift and Core Data books, and hosts the weekly Swift Talk video series.