Clean Code на Swift: принципы и примеры
Чистый код (Clean Code) — это не просто модный термин, а философия разработки, которая напрямую влияет на скорость создания фич, количество багов и удовольствие от работы. Для Swift-разработчиков, работающих над iOS и macOS приложениями, особенно важно писать код, который легко читать и поддерживать, ведь проекты живут годами, а команды меняются. В этой статье мы разберём основные принципы Clean Code применительно к Swift и рассмотрим конкретные примеры.
1. Именование: говорите на языке предметной области
Правильное именование — основа читаемого кода. Имена переменных, функций и классов должны отвечать на вопросы "что?" и "зачем?". Избегайте сокращений и однобуквенных имён (кроме i в коротких циклах).
Плохой пример:
func calc(a: Int, b: Int) -> Int {
return a * b + 10
}
Хороший пример:
func calculateTotalPriceWithShipping(itemPrice: Int, shippingCost: Int) -> Int {
return itemPrice + shippingCost
}
Для булевых переменных используйте префиксы is, has, should:
var isUserLoggedIn: Bool = false
var hasCompletedOnboarding: Bool = true
Имена классов должны быть существительными ( UserManager, NetworkService ), а функции — глаголами или глагольными фразами ( fetchData(), validateEmail() ).
2. Функции: маленькие и с одной ответственностью
Каждая функция должна делать ровно одну вещь и делать её хорошо. Идеальная длина функции — 10-20 строк. Если функция превышает 30 строк — разбивайте её на более мелкие.
Плохой пример: функция, которая и валидирует, и сохраняет, и отправляет уведомление.
func processUser(_ user: User) {
// Валидация
guard !user.name.isEmpty else { return }
guard user.age > 18 else { return }
// Сохранение
CoreDataManager.shared.save(user)
// Уведомление
NotificationCenter.default.post(name: .userDidRegister, object: nil)
}
Хороший пример: разделение на три отдельные функции.
func processUser(_ user: User) {
guard isValid(user) else { return }
saveUser(user)
notifyUserRegistration()
}
private func isValid(_ user: User) -> Bool {
return !user.name.isEmpty && user.age > 18
}
private func saveUser(_ user: User) {
CoreDataManager.shared.save(user)
}
private func notifyUserRegistration() {
NotificationCenter.default.post(name: .userDidRegister, object: nil)
}
Также избегайте большого количества параметров. Если их больше трёх — объедините в структуру.
struct SearchConfig {
let query: String
let category: String?
let page: Int
let pageSize: Int
}
func search(with config: SearchConfig) -> [Result] { ... }
3. SOLID в Swift: практические примеры
Принципы SOLID — краеугольный камень чистого кода в объектно-ориентированных языках. Рассмотрим два ключевых принципа на примерах Swift.
Принцип единственной ответственности (SRP)
Класс должен иметь только одну причину для изменения. Не смешивайте логику сети и UI.
// Нарушение SRP
class UserViewController: UIViewController {
func fetchUser() {
URLSession.shared.dataTask(with: URL(string: "...")!) { ... }
}
func updateUI() { ... }
}
// Исправление
class UserNetworkService {
func fetchUser(completion: (User) -> Void) { ... }
}
class UserViewController: UIViewController {
let networkService = UserNetworkService()
func loadUser() {
networkService.fetchUser { [weak self] user in
DispatchQueue.main.async { self?.updateUI(with: user) }
}
}
}
Принцип подстановки Лисков (LSP)
Объекты подклассов должны быть взаимозаменяемы с объектами базового класса без нарушения логики.
class Bird {
func fly() { print("Flying") }
}
class Penguin: Bird {
override func fly() {
fatalError("Penguins can't fly!") // Нарушение LSP!
}
}
// Правильный подход: протокол Flyable
protocol Flyable {
func fly()
}
class Bird { }
class Sparrow: Bird, Flyable {
func fly() { print("Sparrow flying") }
}
class Penguin: Bird { } // Не реализует Flyable
4. Обработка ошибок и опционалы
Swift — язык с мощной системой типов, и чистый код должен использовать это преимущество. Избегайте force unwrapping (!) и голых try!.
Плохой пример:
let url = URL(string: "https://api.example.com")!
let data = try! Data(contentsOf: url)
let json = try! JSONSerialization.jsonObject(with: data)
Хороший пример:
enum AppError: Error {
case invalidURL
case networkError(Error)
case parsingError
}
func fetchData(from urlString: String) -> Result<Any, AppError> {
guard let url = URL(string: urlString) else {
return .failure(.invalidURL)
}
do {
let data = try Data(contentsOf: url)
let json = try JSONSerialization.jsonObject(with: data)
return .success(json)
} catch {
return .failure(.networkError(error))
}
}
Для опционалов используйте guard let или if let, а не !. Это делает код безопаснее и явно показывает намерения.
// Плохо
let name = optionalName!
// Хорошо
guard let name = optionalName else { return }
// или
if let name = optionalName {
print(name)
}
Заключение
Clean Code на Swift — это не набор жёстких правил, а набор практик, которые делают ваш код более предсказуемым, тестируемым и удобным для совместной работы. Начните с малого: переименуйте пару нечитаемых переменных, разбейте большую функцию на несколько маленьких, замените force unwrapping на безопасную обработку. Со временем эти привычки станут автоматическими, и качество вашего кода вырастет в разы. Помните: код пишется для людей, а не для компьютера. Чистый код — это проявление уважения к себе и своим коллегам.