Design Patterns in iOS — part 2

ShreeThaanu Raveendran
4 min readOct 25, 2022

--

In my previous blog, i have explained about what’s design patterns and how we use it in iOS. If you haven’t gone through it please do check it for a overall idea.

In this blog I’m writing about Behavioral Design patterns, what i found fascinating about it and what are the challenges i faced using them and how to choose your patterns.

Behavioral patterns are concerned with algorithms and the assignment of responsibilities between objects. They Identifies common communication patterns between entities and implement these patterns.

I personally used 2 patterns Observer and visitor patterns.

Observer Pattern :

Observer is a behavioral design pattern that lets you define a subscription mechanism to notify multiple objects about any events that happen to the object they’re observing.

Above is a simple diagram of how it works, so publishers store list of subscribers it has to send notification on what have changed. the subscribers subscribe to the publishers on asking for the update that happened.

This would look complicated but is most elegant way to share data to many and in an efficient way. Also using observer pattern you don’t have to go and check for the update, the iteration of visiting and looking for the change is avoided.

Complications of using observer pattern is its not notified to subscribers in an ordered way and also you may loose track of all subscribers.

Below is a simple use case i found.

protocol Observable {
func notify()
}

protocol Observer {
var id: Int { get set }
func update()
}

class AppleSeller: Observable {
private var observers: [Observer] = []
private var count: Int = 0

var appleCount: Int {
set {
count = newValue
notify()
}
get {
return count
}
}

func notify() {
for observer in observers {
observer.update()
}
}
}

class Customer: Observer {
var id: Int
var observable: AppleSeller
var name: String

init(name: String, observable: AppleSeller, customerId: Int) {
self.name = name
self.observable = observable
self.id = customerId
self.observable.add(customer: self)
}

func update() {
print("Hurry \(name)! \(observable.appleCount) apples arrived at shop.")
}
}
Output :Hurry James! 10 apples arrived at shop.

Visitor Pattern :

Visitor is a behavioral design pattern that lets you separate algorithms from the objects on which they operate.

This pattern might be useful when there’s a need in performing some similar actions with a group of objects of different type. Or perform an action with an object depending on its specific type, not knowing this type.

  • Ensure to create a visitor object that holds a functionality/List of operations to be performed on the passed object structure.
  • Each clients are traversed to the object structure and call visitor that delegates the request to the “accepted visitor object”.
  • The visitor object then performs the operation on the object that visits the objects.

This makes it possible to create new operations independently from the classes of an object structure by adding new visitor objects.

Here is a real world example i found which made the above thing more clear.

Imagine a seasoned insurance agent who’s eager to get new customers. He can visit every building in a neighborhood, trying to sell insurance to everyone he meets. Depending on the type of organization that occupies the building, he can offer specialized insurance policies:

  • If it’s a residential building, he sells medical insurance.
  • If it’s a bank, he sells theft insurance.
  • If it’s a coffee shop, he sells fire and flood insurance.
struct ColorVisitor: Visitor
{
func visit(_ view: FirstView) -> String { "red" }
func visit(_ view: SecondView) -> String { "green" }
func visit(_ view: ThirdView) -> String { "blue" }
func visit<O>(_ object: O) -> String {
if let o = object as? FirstView {
return visit(o)
}
if let o = object as? SecondView {
return visit(o)
}
if let o = object as? ThirdView {
return visit(o)
}

fatalError("Visit method unimplemented for type \(O.self)")
}
}
protocol AcceptsVisitor {
func accept<V: Visitor>(_ visitor: V) -> V.R
}
extension AcceptsVisitor {
func accept<V: Visitor>(_ visitor: V) -> V.R {
visitor.visit(self)
}
}
let visitor = ColorVisitor()
let view1: AcceptsVisitor = FirstView()
let view2: AcceptsVisitor = SecondView()
let view3: AcceptsVisitor = ThirdView()
print(view1.accept(visitor))
print(view2.accept(visitor))
print(view3.accept(visitor))

In the above code we can achieve the colour change with the help of colour visitor without a need to modify. To achieve this we have an accept visitor protocol that has a func to adapt the new visitor that comes for the alteration. By this way we can separate the logic for a common object and dynamically change for same object without hurting others.

The issues we may endup facing are updating all visitors each time a class gets added or removed from heiarachy. But as i’ve seen visitor methods make SwiftUI more powerful in adaption and view changes. I would go with visitor pattern when i have a need to do with SwiftUI.

For all these patterns there are few example in my Github. Please do have a look.

Feel free to comment if you feel struck on any block. Let me know in comments if you felt it’s useful and feedbacks are welcome 🙌.

In my next blog i would be writing about structural patterns.

Follow me on other social networks too.

LinkedIn: https://www.linkedin.com/in/shreethaanu-raveendran-7a6275b2/

Github : https://github.com/shreethaanu

Instagram : https://www.instagram.com/shreethaanu_blogs/

Portfolio : https://strlabz.com

Patreon : https://www.patreon.com/shreethaanu

If you like my post and wanna see more support me ☕️

--

--

ShreeThaanu Raveendran

Apple Platforms Developer | XR Researcher | DesignTechnologist