Lightweight Design Patterns in iOS (Part 4) - Factory

Jean Mainguy
EGYM Software Development
4 min readNov 26, 2018

--

Pssst! I started my own blog!
You can read this very same article over there too!
No paywall, no ad, no Javascript — no bullshit, just pure content.
See you there!

Design Patterns have been a part of Mobile Development for a while now and a revolution towards Quality Assurance is on. Such patterns have become a standard among the community, yet implementing an academic one might come with a price — complexity.

Because some Design Patterns solve very complex problems, they often result in complicated implementation as well. So complex sometimes, that we tend to use heavy Third-Party Frameworks, even for simple use cases.

The time has come to combine quality and simplicity.

🎬 Hi there, I’m Jean!

Previously in the series Lightweight Design Patterns in iOS, we have covered the case of the Coordinator Pattern or how to implement a lightweight navigation flow! 🚦
In this episode, we will take a look at the Factory Pattern, the problem it solves as well as how to implement it in a simple, yet powerful manner! 🏭

Factory Pattern

The role of the Factory is to instantiate new objects along with their dependencies. 💉
This means in iOS, instantiating a ViewController from a XIB or StoryBoard as well as injecting the ViewModel and the Coordinator for instance. 🛣
The advantage of such a pattern is mainly to extract Instantiation Logic from any other layer (ViewController, ViewModel, Coordinator…) as sometimes a lot of dependencies need to be injected. 😱
Also, the Factory Pattern gives us a really nice way to define the Dependency Tree! 💯

Let’s now look at an example! 👀

The Factory in practice

We have an app, which displays a list of “things” preceded by a picture. Easy. 👍

In order to keep all our dependencies centralized, let’s first create a DependencyManager! This dependency manager will contain in essence a NetworkManager for the network calls. 👌

struct DependencyManager {
let networkManager: NetworkManagerProtocol
}

Now, from this NetworkManager, we might want to fetch different kinds of Data, it could be an Image, or a Something… it doesn’t matter: we will implement a new Manager, responsible for the parsing logic, for each use case. ✅

Looking at an ImageManager for instance, this would mean creating an extension on DependencyManager in order to make an ImageManager, by injecting the NetworkManager in it! 💉

And here is our first Factory: ImageFactory! 🏭

protocol ImageFactory {
func makeImageManager() -> ImageManagerProtocol
}

extension DependencyManager: ImageFactory {
func makeImageManager() -> ImageManagerProtocol {
return ImageManager(networkManager: networkManager)
}
}

Extending it to the use case of fetching “Something”, we would make the SomethingManager the exact same way: SomethingFactory is born! 👶

protocol SomethingFactory {
func makeSomethingManager() -> SomethingManagerProtocol
}

extension DependencyManager: SomethingFactory {
func makeSomethingManager() -> SomethingManagerProtocol {
return SomethingManager(networkManager: networkManager)
}
}

Looking fit! 💪

Now, a Factory does not have to limit itself to instantiating Managers; ViewControllers are great candidates too! 🚀

Going back to the example, making a SomethingViewController would be as simple as instantiating it from a StoryBoard or a XIB first, and then injecting the necessary dependencies (i.e. a ViewModel) through the attributes! 💉

protocol ViewControllerFactory {
func makeSomethingViewController() -> SomethingViewController
}

extension DependencyManager: ViewControllerFactory {
func makeSomethingViewController() -> SomethingViewController {
let viewController = SomethingViewController.storyBoardInstance
viewController.viewModel = SomethingViewModel(factory: self)

return viewController
}
}

Now, if you pay attention to the way the ViewModel has been instantiated above, you’ll realize that the Managers are not injected directly, but the Factory itself is! 🤯

This is really nice to keep the Dependency Definition inside the Entity. 👌
That way, when a refactor happens and a new Dependency needs to be injected, only the ViewModel and the corresponding Factory will be affected, not the ViewControllerFactory! 😎
More separation of concerns = less problems 😉

Now, looking back at our SomethingViewModel, we can use the famous Protocol Composition trick to define exactly which use case the factory should have access to! 👮
In our case, we define the typealias Factory as being an ImageFactory and SomethingFactory. That way, injecting the Managers can be done very elegantly and in a lazy manner! 💉

final class SomethingViewModel {

typealias Factory = ImageFactory & SomethingFactory
private let factory: Factory

private lazy var imageManager: ImageManagerProtocol = factory.makeImageManager()
private lazy var somethingManager: SomethingManagerProtocol = factory.makeSomethingManager()

init(factory: Factory) {
self.factory = factory
}

...
}

And boom! 💥
We just made a lightweight Factory! 🎉

Was it worth it? Did that help you see what problems the Factory Pattern tries to solve? How to make such a pattern lightweight?

If so:

  1. Let me know in the comments below! 👇
  2. Don’t forget to hit that subscribe button! ✅
  3. Follow me on Twitter, I’ll be happy to answer any of your questions and you’ll be the first ones to know when a new article comes out! 👌

Bye-bye! 👋

--

--