Mediator – Design Patterns in Swift

Introduction

The Mediator design pattern solves two problems. One is a tight coupling between a set of interacting objects should be avoided. The other is to change the interaction between a set of objects independently.

Mediator Pattern – UML

Use Mediator Pattern in Swift

The Mediator interface declares a method used by components to notify the mediator about various events. The Mediator may react to these events and pass the execution to other components.

import XCTest

protocol Mediator: AnyObject {

    func notify(sender: BaseComponent, event: String)
}

Concrete Mediators implement cooperative behavior by coordinating several components.

class ConcreteMediator: Mediator {

    private var component1: Component1
    private var component2: Component2

    init(_ component1: Component1, _ component2: Component2) {
        self.component1 = component1
        self.component2 = component2

        component1.update(mediator: self)
        component2.update(mediator: self)
    }

    func notify(sender: BaseComponent, event: String) {
        if event == "A" {
            print("Mediator reacts on A and triggers following operations:")
            self.component2.doC()
        }
        else if (event == "D") {
            print("Mediator reacts on D and triggers following operations:")
            self.component1.doB()
            self.component2.doC()
        }
    }
}

The Base Component provides the basic functionality of storing a mediator’s instance inside component objects.

class BaseComponent {

    fileprivate weak var mediator: Mediator?

    init(mediator: Mediator? = nil) {
        self.mediator = mediator
    }

    func update(mediator: Mediator) {
        self.mediator = mediator
    }
}

Concrete Components implement various functionality. They don’t depend on other components. They also don’t depend on any concrete mediator classes.

class Component1: BaseComponent {

    func doA() {
        print("Component 1 does A.")
        mediator?.notify(sender: self, event: "A")
    }

    func doB() {
        print("Component 1 does B.\n")
        mediator?.notify(sender: self, event: "B")
    }
}

class Component2: BaseComponent {

    func doC() {
        print("Component 2 does C.")
        mediator?.notify(sender: self, event: "C")
    }

    func doD() {
        print("Component 2 does D.")
        mediator?.notify(sender: self, event: "D")
    }
}

Let’s see how it all works together.

class MediatorConceptual: XCTestCase {

    func testMediatorConceptual() {

        let component1 = Component1()
        let component2 = Component2()

        let mediator = ConcreteMediator(component1, component2)
        print("Client triggers operation A.")
        component1.doA()

        print("\nClient triggers operation D.")
        component2.doD()

        print(mediator)
    }
}

Conclusion

In summary, The Mediator makes it easy to modify, extend and reuse individual components because they’re no longer dependent on the dozens of other classes. The most popular usage of the Mediator pattern in Swift code is facilitating communications between GUI components of an app. The synonym of the Mediator is the Controller part of the MVC pattern.

Leave a Reply

Your email address will not be published. Required fields are marked *