В
Swift есть встроенные механизмы, которые делают отладку быстрее и чище. Они не требуют подключения дебаггера, не заставляют вас ставить брейкпоинты и часами шагать по каждой строчке кода. Сеньоры используют их постоянно, а джуны часто проходят мимо, потому что привыкли к одному только принту. Давайте разберем пять таких инструментов.
assert - ловим логические ошибки на этапе разработки:
assert проверяет условие и, если оно ложно, останавливает выполнение программы. Работает только в дебаг-сборке - в релизе эти проверки просто вырезаются.
Допустим, вы пишете функцию, которая вычисляет квадрат числа. Вы точно знаете, что ноль сюда передавать нельзя. Добавляете проверку:
func calculateSquare(x: Double) -> Double {
assert(x != 0, "Число не может быть нулем")
return x * x
}
Если кто-то случайно вызовет
calculateSquare(x: 0), приложение упадет с сообщением об ошибке. И вы сразу увидите проблему, а не будете полдня выяснять, почему результат не сошелся.
Важный момент: assert не подходит для проверки пользовательского ввода. Его задача - ловить ошибки в вашей логике, те самые ситуации, которые не могут произойти, но почему-то происходят.
#file, #function, #line - добавляем контекст в логи:
Обычный
print выводит просто сообщение. Чтобы понять, откуда оно пришло, приходится лезть в код. Есть способ лучше - использовать литералы
#file,
#function и
#line. Они подставляют имя файла, функции и номер строки прямо во время компиляции.
Оборачиваем
print в удобную функцию:
func log(_ message: String,
file: String = #file,
function: String = #function,
line: Int = #line) {
print("[\(file):\(line)] \(function) - \(message)")
}
Теперь вместо
print("Ошибка") пишем
log("Ошибка"), и получаем в консоли что-то вроде:
[ViewController.swift:42] updateUser() - Ошибка
Никакого копирования имен функций и номеров строк руками. Все само подставляется.
CustomDebugStringConvertible - настраиваем вывод своих типов:
Когда вы пишете
print(user),
Swift выводит стандартное представление:
User(name: "Artem", age: 33, role: "Admin"). Для маленьких структур ок, но когда полей много - в консоли каша.
Протокол
CustomDebugStringConvertible позволяет переопределить этот вывод:
extension User: CustomDebugStringConvertible {
var debugDescription: String {
"👤 \(name), роль: \(role)"
}
}
Теперь
print(user) покажет: "👤 Artem, роль: Admin". Коротко и понятно. Часто используемые типы данных можно снабдить такими расширениями - и отладка станет намного приятнее.
Mirror - смотрим на свойства объекта без документации:
Иногда нужно быстро посмотреть, какие поля у объекта и какие у них значения. Например, вы работаете с чужим кодом или отлаживаете сложную модель.
Mirror - это инструмент рефлексии, который позволяет заглянуть внутрь любого экземпляра:
func showProperties(_ value: T) {
let mirror = Mirror(reflecting: value)
for child in mirror.children {
print("\(child.label ?? "unknown"): \(child.value)")
}
}
Передаете любую структуру или класс:
let user = User(name: "Artem", age: 33, role: "Admin")
showProperties(user)
В консоли:
name: Artem
age: 33
role: Admin
Mirror работает даже со
SwiftUI вьюхами - можно посмотреть, какие
@State у них внутри. Полезная штука, когда нет документации или лень подключать дебаггер.
dump - печатаем сложные структуры с отступами:
Если у вас вложенный объект внутри другого объекта, обычный
print выдаст что-то нечитаемое. Функция
dump() решает эту проблему. Она рекурсивно печатает все содержимое, сохраняя иерархию.
Пример:
struct User {
let name: String
let age: Int
let address: Address
}
struct Address {
let city: String
let street: String
}
let user = User(name: "Artem", age: 33, address: Address(city: "Moscow", street: "Tverskaya"))
dump(user)
В консоли увидите:
User
- name: "Artem"
- age: 33
address: Address
- city: "Moscow"
- street: "Tverskaya"
dump показывается тип каждого значения и его вложенность. Особенно удобно для отладки массивов, словарей и
Core Data объектов.
Вывод:
Сеньоры используют эти пять инструментов не потому, что они умнее, а потому что так быстрее.
assert, литералы
#file,
#function и
#line, протокол
CustomDebugStringConvertible, тип
Mirror и функция
dump встроены в стандартную библиотеку
Swift и доступны без подключения дополнительных зависимостей. Перестаньте терять часы на однообразный print и просто начните применять то, что уже есть под рукой. Отладка станет чище, а логи - понятнее.