Design Patterns in iOS

Design patterns help in structuring code efficiently and making it reusable, maintainable, and scalable. Here are some commonly used design patterns in Swift with examples:

  1. Creational Patterns → Deal with object creation.
  2. Structural Patterns → Focus on class and object composition.
  3. Behavioural Patterns → Define communication between objects.

1. Creational Design Patterns (Object Creation)

1.1 Singleton Pattern

Ensures only one instance of a class exists.

class DatabaseManager {
    static let shared = DatabaseManager() // Single shared instance
    
    private init() {} // Private constructor prevents multiple instances
    
    func fetchData() {
        print("Fetching data from database")
    }
}

// Usage
DatabaseManager.shared.fetchData()

1.2 Factory Pattern

Creates objects without specifying the exact class.

protocol Notification {
    func send()
}

class EmailNotification: Notification {
    func send() { print("Sending Email") }
}

class SMSNotification: Notification {
    func send() { print("Sending SMS") }
}

class NotificationFactory {
    static func createNotification(type: String) -> Notification? {
        switch type {
        case "Email": return EmailNotification()
        case "SMS": return SMSNotification()
        default: return nil
        }
    }
}

// Usage
let notification = NotificationFactory.createNotification(type: "Email")
notification?.send()  // Output: Sending Email

1.3 Builder Pattern

Creates complex objects step by step.

class User {
    var isLoggedIn: Bool = false
    var isRegistered: Bool = false
}

class UserState {
    private var user = User()
    
    func setUserLoginState() -> UserState {
        user.isLoggedIn = true
        return self
    }
    
    func setUserRegnState() -> UserState {
        user.isRegistered = true
        return self
    }
    
    func build() -> User {
        return user
    }
}

// Usage
let myUser = UserState().setUserLoginState().setUserRegnState().build()
print(myUser.isLoggedIn)  // Output: true

2. Structural Design Patterns (Class & Object Composition)

These patterns define object relationships.

2.1 Adapter Pattern

protocol ButtonProtocol {
    func onClick()
}

class ThirdPartyButton {
    func press() {
        print("Third-party button was pressed")
    }
}

class ButtonAdapter: ButtonProtocol {
    private let thirdPartyButton: ThirdPartyButton
    
    init(thirdPartyButton: ThirdPartyButton) {
        self.thirdPartyButton = thirdPartyButton
    }
    
    func onClick() {
        print("Adapter converting button press...")
        thirdPartyButton.press()  // Adapting the method
    }
}

// Usage
let thirdPartyButton = ThirdPartyButton()
let adaptedButton = ButtonAdapter(thirdPartyButton: thirdPartyButton)

adaptedButton.onClick()


//Output
Adapter converting button press...
Third-party button was pressed

2.2 Decorator Pattern

Adds functionality to an object dynamically.

protocol Coffee {
    func cost() -> Double
}

class BasicCoffee: Coffee {
    func cost() -> Double { return 5.0 }
}

class MilkDecorator: Coffee {
    private let base: Coffee
    
    init(base: Coffee) {
        self.base = base
    }
    
    func cost() -> Double {
        return base.cost() + 2.0
    }
}

// Usage
let coffee = BasicCoffee()
print(coffee.cost())  // Output: 5.0

let milkCoffee = MilkDecorator(base: coffee)
print(milkCoffee.cost())  // Output: 7.0

3. Behavioral Design Patterns (Communication Between Objects)

3.1 Observer Pattern

Allows multiple objects to observe changes in another object.

protocol Observer {
    func update(temperature: Double)
}

class WeatherStation {
    var temperature: Double = 0 {
        didSet {
            notifyObservers()
        }
    }
    
    private var observers: [Observer] = []
    
    func addObserver(_ observer: Observer) {
        observers.append(observer)
    }
    
    private func notifyObservers() {
        observers.forEach { $0.update(temperature: temperature) }
    }
}

class DisplayScreen: Observer {
    func update(temperature: Double) {
        print("Temperature updated: \(temperature)°C")
    }
}

// Usage
let weatherStation = WeatherStation()
let display = DisplayScreen()

weatherStation.addObserver(display)
weatherStation.temperature = 30  

// Output: Temperature updated: 30°C

Leave a Reply

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