← Назад к вопросам

Как организовать код, чтобы не использовать combine

3.0 Senior🔥 102 комментариев
#Архитектура и паттерны#Многопоточность и асинхронность

Комментарии (2)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как организовать код без использования Combine

В современной iOS-разработке есть несколько подходов для организации реактивного кода без использования Combine. Рассмотрим основные альтернативы, их преимущества и недостатки.

Основные альтернативы Combine

1. Closures и Completion Handlers

Наиболее базовый подход, используемый с первых версий iOS.

class DataService {
    func fetchData(completion: @escaping (Result<Data, Error>) -> Void) {
        URLSession.shared.dataTask(with: url) { data, response, error in
            if let error = error {
                completion(.failure(error))
            } else if let data = data {
                completion(.success(data))
            }
        }.resume()
    }
}

// Использование
let service = DataService()
service.fetchData { result in
    switch result {
    case .success(let data):
        self.processData(data)
    case .failure(let error):
        self.handleError(error)
    }
}

Преимущества:

  • Простота понимания для начинающих разработчиков
  • Минимальные требования к версии iOS
  • Прямой контроль над потоком выполнения

Недостатки:

  • Пирамида смерти (pyramid of doom) при цепочке вызовов
  • Сложность с отменой операций
  • Проблемы с утечками памяти при неправильном использовании [weak self]

2. Delegate Pattern

Классический подход Apple для асинхронных операций.

protocol DataServiceDelegate: AnyObject {
    func dataService(_ service: DataService, didReceive data: Data)
    func dataService(_ service: DataService, didFailWith error: Error)
}

class DataService {
    weak var delegate: DataServiceDelegate?
    
    func fetchData() {
        URLSession.shared.dataTask(with: url) { [weak self] data, _, error in
            guard let self = self else { return }
            
            if let error = error {
                self.delegate?.dataService(self, didFailWith: error)
            } else if let data = data {
                self.delegate?.dataService(self, didReceive: data)
            }
        }.resume()
    }
}

Преимущества:

  • Четкое разделение ответственности
  • Поддержка нескольких делегатов через паттерн наблюдателя
  • Стандартный подход для многих iOS API

Недостатки:

  • Большой boilerplate-код
  • Сложность при работе с цепочками вызовов
  • Потенциальные проблемы с циклами ссылок

3. NotificationCenter

Глобальная система событий для слабо связанных компонентов.

extension Notification.Name {
    static let dataLoaded = Notification.Name("DataLoaded")
    static let dataLoadingFailed = Notification.Name("DataLoadingFailed")
}

class DataService {
    func fetchData() {
        URLSession.shared.dataTask(with: url) { data, _, error in
            if let error = error {
                NotificationCenter.default.post(
                    name: .dataLoadingFailed,
                    object: error
                )
            } else if let data = data {
                NotificationCenter.default.post(
                    name: .dataLoaded,
                    object: data
                )
            }
        }.resume()
    }
}

// Подписка в ViewController
class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(handleDataLoaded(_:)),
            name: .dataLoaded,
            object: nil
        )
    }
    
    @objc private func handleDataLoaded(_ notification: Notification) {
        guard let data = notification.object as? Data else { return }
        // Обработка данных
    }
}

Преимущества:

  • Полная декомпозиция компонентов
  • Возможность широковещательных уведомлений
  • Простота добавления новых подписчиков

Недостатки:

  • Отсутствие типизации (использование Any)
  • Сложность отладки из-за глобальности
  • Потенциальные проблемы с производительностью

4. Custom Observable/Reactive Implementation

Создание собственной минимальной реактивной системы.

class Observable<T> {
    typealias Observer = (T) -> Void
    
    private var observers: [Observer] = []
    
    var value: T {
        didSet {
            notifyObservers()
        }
    }
    
    init(_ value: T) {
        self.value = value
    }
    
    func bind(observer: @escaping Observer) {
        observers.append(observer)
        observer(value)
    }
    
    private func notifyObservers() {
        observers.forEach { observer in
            observer(value)
        }
    }
}

// Использование
class ViewModel {
    var data: Observable<[Item]> = Observable([])
    
    func loadData() {
        // Загрузка данных...
        data.value = loadedItems
    }
}

class ViewController: UIViewController {
    let viewModel = ViewModel()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        viewModel.data.bind { [weak self] items in
            self?.tableView.reloadData()
        }
    }
}

Преимущества:

  • Полный контроль над реализацией
  • Минимальные зависимости
  • Оптимизация под конкретные нужды проекта

Недостатки:

  • Необходимость самостоятельно реализовывать сложные операторы
  • Отсутствие стандартизации
  • Риск создания неоптимальных решений

Рекомендации по выбору подхода

  1. Для простых проектов используйте closures - это минималистично и достаточно
  2. Для модульных архитектур с четкими границами подойдет delegate pattern
  3. Для глобальных событий в приложении (настройки темы, авторизация) используйте NotificationCenter
  4. Для кастомных реактивных систем создавайте свою минимальную реализацию Observable
  5. Рассмотрите Third-Party решения если нужна полноценная реактивность:
    • RxSwift - полный аналог Rx, но с большим порогом входа
    • ReactiveSwift - более легковесная альтернатива
    • Async/Await (iOS 13+) - современный подход от Apple

Архитектурные паттерны для управления состоянием

Для комплексного управления состоянием без Combine рассмотрите:

  1. Redux-подобные архитектуры с централизованным store
  2. MVVM с Observables через собственную реализацию биндингов
  3. Координаторы/Роутеры для управления навигацией и зависимостями

Каждый подход имеет свои компромиссы между сложностью реализации, производительностью и поддерживаемостью кода. Выбор зависит от конкретных требований проекта, команды и целевых версий iOS.

Как организовать код, чтобы не использовать combine | PrepBro