Leonardo Maia Pugliese
Holy Swift

Holy Swift

Create Generic Factory in Swift

Create Generic Factory in Swift

Let's fabricate them all!

Leonardo Maia Pugliese's photo
Leonardo Maia Pugliese

Published on Sep 16, 2021

6 min read

Subscribe to my newsletter and never miss my upcoming articles

Hallo koningen en koninginnen, Leo hier.

Today we'll explore some Factory pattern in Swift. The factory method is a very interesting one because is a creational pattern. The creational design patterns provide various object creation mechanisms such as initialisers or functions, which increase flexibility and reuse of existing code.

Builders, Factories, Prototypes and Singletons are all creational patterns.

Each creational pattern solve a specific pain, in the factory pattern we use the factory to produce a product that we want to use. This way we can move the configuration logic from one class, to the factory itself. In other words, if you want to hide the complexity of create an object in your project the Factory is here to help you!

One good thing to say about design pattern is: Design Pattern are like clothes, use the appropriate clothes for the weather. You can wear a lot of layers of clothes but not necessarily it will be the best outfit for you.

A final disclosure: I will not explain what is a factory and all the theory crafting behind it nor will follow by the book the abstract factory pattern, although the factory explained here very simple kind of factory. No more talking, let's code! But first...

The Painting

The painting is an Mille Miglia Bentley 6.5 Litre 1926 from Paul Smith. He was born in 1948 and grew up in the centre of London. He bought his first tubes of oil paint when he was about 15 year old in their local art shop in Hampstead.

He has been interested in classic cars and classic motor sport since his first visit to Brighton Speed trials in 1968, it's remarkable that the cars he was fascinated in then are still around today. He still paint landscape and London street scenes, but his main interest is in the classic car scene.

I choose this painting because the example of Factory remember me a lot cars, and the code examples below are all about making Car Factories.

The Problem

Imagine that you are writing a class A and you need to use the class B. But the class B takes several steps to be initialised, but you don't want that class A know how to configure class B.

First of all let's create a class Ferrari:

struct Ferrari {
    let engine: String
    var timestamp: Date?

    init(year: String, nickname: String, driver: String, engine: String) {
        self.engine = engine
    }

    func drive() {
        print("The car type is \(engine)")
    }
}

As you see the Ferrari class has a lot to configure. The year, the nickname of the car, the driver and the engine. And also even after we have created the Ferrari object we still need to configure the timestamp of it. In your codebase this could be an object that need a lot of other objects/steps to be initialised. Like a manager that need other services or initialisations to work.

So if I want to a CarSeller to drive the Ferrari I could do something like this:

class CarSeller {
    func showCars() {
        let ferrari = Ferrari(year: "get from the yearCatalog Service",
                              nickname: "Search in user defaults the nickname of the car",
                              driver: "Get the first from a Core Data table",
                              engine: "3.5")
        ferrari.timestamp = Date()
        ferrari.drive()
    }
}

let carSeller = CarSeller()
carSeller.showCars()

This works but you see that now CarSeller have to know how to initialise and configure the Ferrari class?

Extract the Ferrari creation

So to simplify this CarSeller class we will create a generic factory to handle every possible object that we want to create, let's start with the protocol:

protocol GenericFactory {
    associatedtype Input
    associatedtype Output
    func build(config: Input) -> Output
}

This protocol says: Hey every generic factory should have an Input Type, an Output Type and a function that receives the Input Type and return the Output Type. Observe that Input and Output type can be everything and we can do great tricks with them.

Now we should implement the Factory itself. The factory will take a generic factory and the configuration to return the product of any possible factory.

struct Factory {
    static func create<Output, Input, Factory: GenericFactory>(_ object: Factory,_ configuration: Input) -> Output where Factory.Output == Output,
                 Factory.Input == Input {
        object.build(config: configuration)
    }
}

There's a lot going on here so let's break it down:

  1. First you have the generic parameters inside angle brackets: Output, Input and a Factory that conforms to GenericFactory protocol.
  2. You have the function parameters: the factory object that will be any object that conforms to GenericFactory, and the configuration that is anything that the Generic factory consider to be the necessary configuration.
  3. The return type of the factory static function is Output. We have to tie this to the output of the Factory in the where Factory.Output == Output. We also are tying together the configuration input from the function parameters to the Factory(GenericFactory) Input type, you see this in the where clause Factory.Input == Input.
  4. Now we just need to call the object.build(config:configuration) and it's done! (We can omit the return in this case because the function body have one line and the return type of that line matches with the return type of the enclosing function)

We are all set to start to fabricate our fabrics! Let's start with the FerrariFactory:

struct FerrariFactory: GenericFactory {
    func build(config: String) -> Ferrari {

        var ferrari = Ferrari(year: "getFrom Year service", nickname: "get from other data structure", driver: "Find the first avaliable in Core Data", engine: config)

        // imagine very big configuration with a lot of steps here
        ferrari.timestamp = Date()

        return ferrari
    }
}

And you can now create your Ferraris inside your CarSeller class like this:

class CarSeller {
    func showCars() {
        var ferrari = Factory.create(FerrariFactory(), "3.5")

        ferrari.drive()
    }
}

Very cool isn't?

Using Swift types to leverage the solution

I have two more things to show that you might be thinking: A product with more than one parameter and a product with zero parameters.

For the zero parameter case you case use the Optional Never type like this:

struct FordFactory: GenericFactory {

    func build(config: Never?) -> Ford {

        // very big configuration here

        return Ford(year: "getFrom Year service", nickname: "get from other structure", driver: "Find the first avaliable in Core Data", engine: "")
    }
}

let newFord = Factory.create(FordFactory(), nil)

And if you want more than one parameter we can use a simple Tuple:

struct FordFactory: GenericFactory {

    func build(config: (engine: String, year: String)) -> Ford {

        // very big configuration here

        return Ford(year: config.year, nickname: "get from other structure", driver: "Find the first avaliable in Core Data", engine: config.engine)
    }
}

let newFord = Factory.create(FordFactory(), (engine: "2.4",year: "1993"))

That's all!

Summary

Today we see a very good pattern to extract the configuration and creation of your objects. This can simplify your stack and upgrade the readability of your functions and classes. Use design patterns when necessary and to solve specific problems.

That's all my people, I hope you liked as I enjoyed write this article. If you want to support this blog you can Buy Me a Coffee or just leave a comment saying hello. You can also sponsor posts! Just reach me in LinkedIn or Twitter for details.

Thanks for the reading and... That's all folks.

credits: image

Did you find this article valuable?

Support Leonardo Maia Pugliese by becoming a sponsor. Any amount is appreciated!

Learn more about Hashnode Sponsors
 
Share this