Что можно использовать вместо Singleton для улучшения тестируемости и отладки
Что проверяют
Понимаете ли вы, чем именно мешает Singleton: не самим фактом одного экземпляра, а скрытой зависимостью и общим состоянием, которое переживает тесты. Следом обычно просят показать, как протестировать класс, который сейчас ходит в NetworkManager.shared.
Как отвечать
Основная замена это внедрение зависимостей. Объект получает сервис снаружи и знает о нём только протокол:
- через инициализатор: самый честный вариант, зависимости видны в сигнатуре. Чтобы не ломать существующий код, можно дать значение по умолчанию:
init(session: URLSessionProtocol = URLSession.shared); - через свойство или параметр метода: когда зависимость нужна точечно или объект создаёт система, как
UIViewControllerиз сториборда; - композиционный корень: один экземпляр сервиса никуда не девается, но создаётся в
AppилиSceneDelegateи раздаётся вниз. Единственность обеспечивает сборка, а неstatic let shared; - окружение SwiftUI:
EnvironmentValuesс макросом@Entryили.environment(_:)для@Observable-объектов, в превью и тестах туда кладётся заглушка.
Из библиотек стоит назвать Factory, Swinject, swift-dependencies от Point-Free, а для больших проектов Needle, который проверяет граф зависимостей на этапе компиляции.
Полезно добавить про Swift 6: глобальный static var shared в строгом режиме конкурентности не собирается, а static let требует Sendable-типа или изоляции актором. Это ещё один повод отказаться от общего изменяемого состояния.
Где спотыкаются
Заменяют Singleton на Service Locator и считают задачу решённой. Глобальный локатор тоже прячет зависимости, просто в другом месте, и тест всё так же зависит от того, что в нём зарегистрировано.
Говорят, что Singleton всегда зло. URLSession.shared и UserDefaults.standard вполне нормальны, проблема в том, что код обращается к ним напрямую, а не через протокол.
Называют Dagger среди DI-фреймворков для iOS. Это инструмент из мира Android и Java, в Swift-проектах его нет, и такая оговорка выдаёт ответ, выученный по чужой шпаргалке.