Site icon CodeWithSwift

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
Exit mobile version