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:
- Creational Patterns → Deal with object creation.
- Structural Patterns → Focus on class and object composition.
- 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