Chain of responsibility Design pattern -swift- Examples

Shrawan K Sharma
3 min readApr 16, 2023

The Chain of Responsibility design pattern is a behavioral pattern that allows a set of loosely-coupled objects to handle requests in a flexible and hierarchical manner. In this pattern, each object in the chain has the ability to either handle a request or pass it on to the next object in the chain.

The Chain of Responsibility pattern is particularly useful in situations where there are multiple objects that can handle a particular request, and it is not known in advance which object will handle it. Examples of such scenarios include error handling, logging, and security checks.

Some benefits of using the Chain of responsibility design pattern:

Decouples sender and receiver: The pattern allows for a loose coupling between the sender and receiver of a request, which increases flexibility and promotes scalability.

Simplifies object management: The pattern simplifies the management of objects by reducing the number of dependencies between objects. This makes it easier to add or remove handlers without affecting other parts of the system.

Provides a flexible and extensible solution: The pattern allows for new handlers to be added or existing handlers to be modified easily, without affecting the rest of the system.

Promotes code reusability: The pattern allows for code reuse by promoting the creation of small, focused handlers that can be reused in different parts of the system.

Enables dynamic behavior: The pattern allows for the dynamic creation and modification of the chain of handlers at runtime. This means that the system can adapt to changing requirements without requiring major changes to the code.

Here are different examples to learn Chain of responsibility Design Patterns:-

// Define a protocol for the Chain of Responsibility Logger
protocol Logger {
var nextLogger: Logger? { get set }
func log(message: String, level: LogLevel)
}

// Define a concrete Chain of Responsibility Logger
class ConsoleLogger: Logger {
var nextLogger: Logger?

func log(message: String, level: LogLevel) {
if level == .DEBUG {
print("ConsoleLogger: \(message)")
} else {
nextLogger?.log(message: message, level: level)
}
}
}

class FileLogger: Logger {
var nextLogger: Logger?

func log(message: String, level: LogLevel) {
if level == .INFO {
print("FileLogger: \(message)")
} else {
nextLogger?.log(message: message, level: level)
}
}
}

class EmailLogger: Logger {
var nextLogger: Logger?

func log(message: String, level: LogLevel) {
if level == .ERROR {
print("EmailLogger: \(message)")
} else {
nextLogger?.log(message: message, level: level)
}
}
}

// Define an enum to represent different log levels
enum LogLevel {
case DEBUG
case INFO
case ERROR
}

// Client code that uses the Chain of Responsibility Loggers
class LoggingSystem {
private var consoleLogger = ConsoleLogger()
private var fileLogger = FileLogger()
private var emailLogger = EmailLogger()

init() {
consoleLogger.nextLogger = fileLogger
fileLogger.nextLogger = emailLogger
}

func log(message: String, level: LogLevel) {
consoleLogger.log(message: message, level: level)
}
}

// Usage
let loggingSystem = LoggingSystem()
loggingSystem.log(message: "Debugging information", level: .DEBUG)
loggingSystem.log(message: "Informational message", level: .INFO)
loggingSystem.log(message: "Error occurred", level: .ERROR)
// Define a protocol for touch event handlers
protocol TouchEventHandler {
var nextHandler: TouchEventHandler? { get set }
func handleTouch(_ touch: UITouch, with event: UIEvent?) -> Bool
}

// Define a concrete touch event handler
class ViewTouchHandler: TouchEventHandler {
var nextHandler: TouchEventHandler?
private weak var view: UIView?

init(view: UIView) {
self.view = view
}

func handleTouch(_ touch: UITouch, with event: UIEvent?) -> Bool {
if view?.frame.contains(touch.location(in: view)) ?? false {
// handle touch event
return true
} else {
return nextHandler?.handleTouch(touch, with: event) ?? false
}
}
}

// Define a view that uses the Chain of Responsibility touch event handlers
class TouchableView: UIView {
private var touchHandlers: TouchEventHandler?

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
touchHandlers?.handleTouch(touch, with: event)
}

func addTouchHandler(_ handler: TouchEventHandler) {
if let currentHandler = touchHandlers {
var lastHandler = currentHandler
while lastHandler.nextHandler != nil {
lastHandler = lastHandler.nextHandler!
}
lastHandler.nextHandler = handler
} else {
touchHandlers = handler
}
}
}

// Usage
let view1 = TouchableView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
let view2 = TouchableView(frame: CGRect(x: 50, y: 50, width: 100, height: 100))
let view3 = TouchableView(frame: CGRect(x: 100, y: 100, width: 100, height: 100))

let handler1 = ViewTouchHandler(view: view1)
let handler2 = ViewTouchHandler(view: view2)
let handler3 = ViewTouchHandler(view: view3)

view1.addTouchHandler(handler1)
view2.addTouchHandler(handler2)
view3.addTouchHandler(handler3)

--

--

Shrawan K Sharma

iOS App Developer - Frontend(ReactJS) - Building innovative products - MNNIT