/
ConfirmCodeViewModel.swift
120 lines (89 loc) · 3.97 KB
/
ConfirmCodeViewModel.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
//
// ConfirmCodeViewModel.swift
// LoginSample
//
// Created by Igor Vedeneev on 02.02.2021.
//
import Foundation
import RxSwift
import RxSwiftExt
import RxCocoa
let codeTimerLimit = 3
enum AuthResult {
case success
case needPersonalData
}
protocol ConfirmCodeViewModelProtocol {
/// Введенный пользователем код для подтверждения
var code: AnyObserver<String> { get }
/// Пользователь нажал на "отправить повторно"
var getNewCode: AnyObserver<Void> { get }
/// Результат подтверждения кода
var didAuthorize: Driver<AuthResult> { get }
/// Один индикатор на все запросы на этом экране
var isLoading: Driver<Bool> { get }
/// Ошибки из всех запросов на этом экране
var errors: Driver<String> { get }
/// Таймер отправки нового кода
var newCodeTimer: Driver<Int> { get }
/// Запросили новый код при нажатии на "отправить заново"
var didRequestNewCode: Driver<Void> { get }
}
final class ConfirmCodeViewModel: ConfirmCodeViewModelProtocol {
let code: AnyObserver<String>
let getNewCode: AnyObserver<Void>
let didAuthorize: Driver<AuthResult>
let isLoading: Driver<Bool>
let errors: Driver<String>
let newCodeTimer: Driver<Int>
let didRequestNewCode: Driver<Void>
private let disposeBag = DisposeBag()
init(token: String,
phone: String,
codeLength: Int = 4,
resendCodeTimeout: Int = 3,
authService: AuthServiceProtocol = AuthService())
{
let _codeSubject = PublishSubject<String>()
code = _codeSubject.asObserver()
let _getCode = PublishSubject<Void>()
getNewCode = _getCode.asObserver()
let codeObservable = _codeSubject.asObservable()
let validCodeObservable = codeObservable.filter { $0.count == codeLength }
// запрос нового кода
let fetchNewCode = _getCode.asObservable()
.flatMapLatest {
authService.loginByPhone(phone: phone).materialize()
}
.share()
let _newCode = fetchNewCode.elements().mapTo(Void())
didRequestNewCode = _newCode.asDriver(onErrorJustReturn: ())
let codeEvents = validCodeObservable
.flatMapLatest { (code) in
authService.confirmCode(code: code, token: token).materialize()
}
.share()
didAuthorize = codeEvents.elements().asDriver(onErrorJustReturn: .needPersonalData)
let _timer = BehaviorRelay<Int>(value: codeTimerLimit)
newCodeTimer = _timer.asDriver()
Observable.merge(validCodeObservable.mapTo(Void()), _newCode)
.startWith(Void())
.flatMap { _ -> Observable<Int> in
Observable.interval(.seconds(1), scheduler: MainScheduler.instance)
.map { resendCodeTimeout - $0 - 1 }
.take(while: { $0 >= 0 })
}
.bind(to: _timer)
.disposed(by: disposeBag)
let isLoadingRelay = BehaviorRelay<Bool>(value: false)
isLoading = isLoadingRelay.asDriver()
codeEvents.mapTo(false).bind(to: isLoadingRelay).disposed(by: disposeBag)
validCodeObservable.mapTo(true).bind(to: isLoadingRelay).disposed(by: disposeBag)
_getCode.asObservable().mapTo(true).bind(to: isLoadingRelay).disposed(by: disposeBag)
fetchNewCode.mapTo(false).bind(to: isLoadingRelay).disposed(by: disposeBag)
errors = codeEvents.errors()
.merge(with: fetchNewCode.errors())
.compactMap { ($0 as? ErrorType)?.localizedDescription }
.asDriver(onErrorJustReturn: "")
}
}