SOLID Principles in Swift: Theory and Practice from A to Z

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Introduction: Why SOLID is the Foundation of Quality Swift Code

Every iOS/macOS developer sooner or later faces a situation where the code becomes "spaghetti": changes in one place break the entire application, and testing turns into a nightmare. This is where SOLID principles come to the rescue — five fundamental rules of object-oriented design formulated by Robert Martin.

In this article, we will break down each SOLID principle using Swift examples, show how violating them leads to problems, and provide ready-made solutions. You will learn to write code that is easy to maintain, extend, and test.



1. Single Responsibility Principle (SRP)

Theory

SRP states: a class should have only one reason to change. In other words, each class is responsible for exactly one functionality.



SRP Violation (Bad Code)

class UserManager {    func createUser(name: String, email: String) {        // Create user        saveToDatabase(user: User(name: name, email: email))        sendWelcomeEmail(to: email)        logEvent("User created: \\(name)")    }        private func saveToDatabase(user: User) { /* ... */ }    private func sendWelcomeEmail(to email: String) { /* ... */ }    private func logEvent(_ message: String) { /* ... */ }}

Here, UserManager is responsible for creation, saving, sending emails, and logging — four reasons to change.



Fix (Good Code)

class UserService {    private let repository: UserRepository    private let emailService: EmailService    private let logger: Logger        init(repository: UserRepository, emailService: EmailService, logger: Logger) {        self.repository = repository        self.emailService = emailService        self.logger = logger    }        func createUser(name: String, email: String) {        let user = User(name: name, email: email)        repository.save(user)        emailService.sendWelcome(to: email)        logger.log("User created: \\(name)")    }}

class UserRepository { func save(_ user: User) { /* saving only */ }}class EmailService { func sendWelcome(to email: String) { /* sending only */ }}class Logger { func log(_ message: String) { /* logging only */ }}

Now each class has a single responsibility, making testing and maintenance easier.



2. Open/Closed Principle (OCP)

Theory

Classes should be open for extension but closed for modification. New functionality is added through inheritance or protocols, without touching existing code.



OCP Violation

class PaymentProcessor {    func process(payment: Any, type: String) {        if type == "card" {            // Process card        } else if type == "paypal" {            // Process PayPal        } // When adding a new type — modify this class    }}


Fix Using Protocols

protocol PaymentMethod {    func process(amount: Double)}

class CardPayment: PaymentMethod { func process(amount: Double) { /* process card */ }}

class PayPalPayment: PaymentMethod { func process(amount: Double) { /* process PayPal */ }}

class PaymentProcessor { func process(payment: PaymentMethod, amount: Double) { payment.process(amount: amount) }}

// Add a new type without modifying existing codeclass ApplePayPayment: PaymentMethod { func process(amount: Double) { /* process Apple Pay */ }}

Now, to add a new payment method, simply create a new class that implements the PaymentMethod protocol.



3.

Blogs

Book Recommendations