iOS : Design Patterns
Design patterns are reusable solutions to common problems in software design. They’re templates designed to help you write code that’s easy to understand and reuse.
Lets get started….🏄🏻🏄🏻🏄🏻
Facade
The Facade design pattern provides a single interface to a complex subsystem. Instead of exposing a set of classes and their APIs to the user(client), it only expose one simple unified API.
This pattern is ideal while working with a large number of classes, particularly when they are complicated to use or difficult to understand. This is also useful if the classes under the facade are likely to change, as the facade class can retain the same API while things change behind the scenes. For example, if we want to replace backend service or core logic, we don’t have to change the code that uses this API.
Decorator
The Decorator pattern dynamically adds behaviors and responsibilities to an object without modifying its code. It’s an alternative to subclassing where you modify a class’s behavior by wrapping it with another object.
In Objective-C there are two very common implementations of this pattern: Category and Delegation. In Swift there are also two very common implementations of this pattern: Extensions and Delegation.
Delegate: It is used to keep implementation specific behaviour out of the generic class. Many UI elements in iOS use delegates to control their behaviour e.g. UIScrollView. The UIScrollView class has no knowledge of what it should be scrolling as that is an application specific task. So to notify the application of scrolling events, it uses the UIScrollViewDelegate. The application can implement the delegate and then intercept the scrolling events sent to it by the UIScrollView.
Memento
Memento Pattern saves your stuff somewhere. Later on, this externalized state can be restored without violating encapsulation; that is, private data remains private. Implementations example of the Memento pattern is Archiving, Serialization and State Restoration.
Adapter
The Adapter design pattern converts the interface of a class into another interface that clients expect. Adapter lets classes work together that couldn’t otherwise because of incompatible interfaces. It decouples the client from the class of the targeted object. Apple uses protocols to do the similar job. You may be familiar with protocols like UITableViewDelegate
, UIScrollViewDelegate
, NSCoding
and NSCopying
. As an example, with the NSCopying
protocol, any class can provide a standard copy
method.
Observer
The Observer design pattern defines a one-to-many dependency between objects so that when one object changes its state, all its dependents are notified and updated automatically. The Observer pattern is essentially a publish-and-subscribe model in which the subject and its observers are loosely coupled. Cocoa implements the observer pattern in two ways: Notifications and Key-Value Observing (KVO).
Strategy
Strategy pattern allows you to change the behaviour of an algorithm at run time. Using interfaces, we are able to define a family of algorithms, allowing us to select which algorithm to execute at run time interchangeable. For more information check these two out by Thomas Hanning and Sam Stone.
Factory
Factory method pattern makes the codebase more flexible to add or remove new types. To add a new type, we just need a new class for the type and a new factory to produce it like the following code. For more information check this out.
Singleton
The Singleton design pattern ensures a class only has one instance, and provides a global point of access to it. The class keeps track of its sole instance and ensures that no other instance can be created. Singleton classes are appropriate for situations where it makes sense for a single object to provide access to a global resource. It usually uses lazy loading to create the single instance when it’s needed the first time.
Singleton, Facade and Decorator — Raywenderlich tutorials.
Adaptor, Observer and Momento — Raywenderlich tutorials
⚠️ Below we will be briefly discuss about the some Architectural Design Patterns. ⚠️
MVC
- Models — responsible for the domain data or a data access layer which manipulates the data, think of ‘Person’ or ‘PersonDataProvider’ classes.
- Views — responsible for the presentation layer (GUI), for iOS environment think of everything starting with ‘UI’ prefix.
- Controller/Presenter/ViewModel — the glue or the mediator between the Model and the View, in general responsible for altering the Model by reacting to the user’s actions performed on the View and updating the View with changes from the Model.
The ViewController contains the View and owns the Model. The problem is that we used to write the controller code as well as the view code in the ViewController. It makes the ViewController too complex. That’s why we called it a Massive View Controller. While writing a test for the ViewController, you need to mock the view and the life cycle of it. But views are difficult to be mocked. And we actually don’t want to mock the view if we only want to test the controller logic. All these things make writing tests very complicated.
MVP
Model View Presenter has three components: the Presenter (UIKit independent mediator), the Passive View (UIView and/or UIViewController) and the Model. This pattern defines Views as recipients of the UI events, which then call the appropriate Presenter as needed. The Presenter is, in fact, responsible for updating the View with the new data returned by the Model. View is more loosely coupled to the model. The Presenter is responsible for binding the Model to the View. It is easier to write unit test cases because interaction with the view is through an interface. Usually View to Presenter = map one-to-one. Complex views may have multi presenters.
MVVM
In MVVM, the View consists of only visual elements like layout, animation, initializing UI components, etc. There’s a special layer between the View and the Model called the ViewModel. The ViewModel is a canonical representation of the View. That is, the ViewModel provides a set of interfaces, each of which represents a UI component in the View. We use a technique called “binding” to connect UI components to ViewModel interfaces.
Specifically, for MVVM in iOS development, the UIView/UIViewController represent the View. We only do:
- Initiate/Layout/Present UI components.
- Bind UI components with the ViewModel.
On the other hand, in the ViewModel, we do:
- Write controller logics such as pagination, error handling, etc.
- Write presentational logic, provide interfaces to the View.
In Swift, there are various ways to achieve the “binding”: KVO (Key-Value Observing) pattern, 3rd party libraries for FRP (Functional Reactive Programming) such as RxSwift and ReactiveCocoa and by crafting it yourself.
Check this awesome article, it covers everything about MVVM. 👌🏼
- Distribution — it is not clear in our tiny example, but, in fact, the MVVM’s View has more responsibilities than the MVP’s View. Because the first one updates it’s state from the View Model by setting up bindings, when the second one just forwards all events to the Presenter and doesn’t update itself.
- Testability — the View Model knows nothing about the View, this allows us to test it easily. The View might be also tested, but since it is UIKit dependant you might want to skip it.
- Easy of use — its has the same amount of code as the MVP in our example, but in the real app where you’d have to forward all events from the View to the Presenter and to update the View manually, MVVM would be much skinnier if you used bindings.
The MVVM is very attractive, since it combines benefits of the aforementioned approaches, and, in addition, it doesn’t require extra code for the View updates due to the bindings on the View side. Nevertheless, testability is still on a good level.
VIPER
Problems with MVVM:
- It is complicated on iOS by a lack of bindings and by a tendency to continue putting too many responsibilities that were in a bloated view controller class into a bloated view model class instead
- Without being broken down into good, reusable, testable, and single-responsibility components, any MVVM implementation is of limited usefulness
- Simply saying that “we’re using MVVM” or simply having a rough implementation of MVVM in a project, without taking a thorough look at how it’s improving your code reuse, efficiency and adherence to the single responsibility principle will mislead you as to how valuable or maintainable the architecture really is.
VIPER is an application of Clean Architecture to iOS apps. The word VIPER is a backronym for View, Interactor, Presenter, Entity, and Routing. Clean Architecture divides an app’s logical structure into distinct layers of responsibility. This makes it easier to isolate dependencies (e.g. your database) and to test the interactions at the boundaries between layers.
The main parts of VIPER are:
- View: displays what it is told to by the Presenter and relays user input back to the Presenter.
- Interactor: contains the business logic as specified by a use case.
- Presenter: contains view logic for preparing content for display (as received from the Interactor) and for reacting to user inputs (by requesting new data from the Interactor).
- Entity: contains basic model objects used by the Interactor.
- Routing: contains navigation logic for describing which screens are shown in which order.
Check below article for more detail.
Thank you for reading 🧑🏻💻
Be sure to clap 👏🏼 and follow 🚶🏻♂️
Questions❓Feedback 📫 — please drop you comments 💭
If you like this article, feel free to share it with your friends 📨
Follow me: Linkedin | X(Twitter) | Github 🤝🏼