Как правильно подписываться на изменения Driver в RxSwift и каковы его особенности?

«Как правильно подписываться на изменения Driver в RxSwift и каковы его особенности?» — вопрос из категории Реактивное программирование, который задают на 10% собеседований IOS Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Driver — специальный тип Observable в RxSwift с гарантиями:

  1. Не вызывает ошибок (error events)
  2. Выполняется на главном потоке (MainScheduler)
  3. Share side effects (share(replay: 1, scope: .whileConnected))

Создание Driver:

// Из Observable
let observable = Observable<String>.create { observer in
    observer.onNext("Data")
    observer.onCompleted()
    return Disposables.create()
}

let driver = observable
    .asDriver(onErrorJustReturn: "Fallback Value")

// Из Subject
let subject = PublishSubject<String>()
let driverFromSubject = subject.asDriver(onErrorJustReturn: "Error")

// С помощью Driver.create
let customDriver = Driver<String>.create { observer in
    observer.onNext("Initial")
    observer.onCompleted()
    return Disposables.create()
}

Подписка на Driver: Используйте drive() вместо subscribe():

// Базовый вариант
driver
    .drive(onNext: { value in
        print("Received: (value)") // Гарантированно на главном потоке
        self.label.text = value     // Безопасно для UI
    })
    .disposed(by: disposeBag)

// Привязка к UI элементам
driver
    .drive(label.rx.text)  // Автоматическая привязка
    .disposed(by: disposeBag)

// Обработка нескольких подписчиков
driver
    .drive(onNext: { value in
        updateView1(value)
    })
    .disposed(by: disposeBag)

driver  // Тот же Driver, side effects shared
    .drive(onNext: { value in
        updateView2(value)
    })
    .disposed(by: disposeBag)

Обработка ошибок в Driver:

// 1. Замена ошибки дефолтным значением
observable
    .asDriver(onErrorJustReturn: "Default")

// 2. Recovery с другого Observable
observable
    .asDriver(onErrorRecover: { error in
        return backupObservable.asDriver(onErrorJustReturn: "Backup")
    })

// 3. Игнорирование ошибок
observable
    .asDriver(onErrorDriveWith: .empty())

Операторы для Driver:

// Все стандартные операторы + специальные
let processedDriver = driver
    .map { $0.uppercased() }          // Transform
    .filter { !$0.isEmpty }           // Filter
    .debounce(.milliseconds(300))     // Throttle
    .distinctUntilChanged()           // Unique values
    .flatMapLatest { query in         // Latest async
        searchAPI(query)
            .asDriver(onErrorJustReturn: [])
    }

Практический пример — поиск:

searchTextField.rx.text.orEmpty
    .asDriver()  // Конвертация в Driver
    .debounce(.milliseconds(500))
    .distinctUntilChanged()
    .flatMapLatest { query in
        return apiService.search(query)
            .asDriver(onErrorJustReturn: [])  // Ошибки → пустой массив
    }
    .drive(tableView.rx.items(cellIdentifier: "Cell")) { index, item, cell in
        cell.configure(with: item)
    }
    .disposed(by: disposeBag)
Ключевые отличия от Observable: Аспект Driver Observable
Поток MainScheduler Любой
Ошибки Не может emit error Может emit error
Side effects Shared Могут повторяться
Использование UI-биндинг Общие операции

Важно: Всегда используйте Driver для привязки к UI-элементам для гарантии работы на главном потоке и корректной обработки ошибок.