ios - 如何在 MVVM-C RxSwift 中实现 firebase 身份验证
问题描述
我正在尝试实现一个 MVVM-C rx swift 应用程序。
我的注册视图控制器有一个虚拟机,用户名和密码作为行为主体。我还有一个注入 VM 的 firebase 处理程序。将注册结果传回 VC 的最佳方式是什么?
我的虚拟机代码:
class CreateVM {
let firebase: FirebaseHandler
let email: String
var password = BehaviorSubject<String>(value: "")
var confirmPassword = BehaviorSubject<String>(value: "")
var shouldHideButton: Observable<Bool> {
return Observable.combineLatest(password.asObservable(), confirmPassword.asObservable()) { pass, confPass in
!(pass.count >= 5 && pass == confPass)
}
}
init(firebase: FirebaseHandler, email: String) {
self.firebase = firebase
self.email = email
}
func submit() {
let pass = try! password.value()
firebase.createWithEmail(email: email, password: pass) { (result) in
switch result {
case .success(let uid):
print(uid, "created")
//handle successful creation
case .failure(let err):
print("failed with error:", err)
//handler error
}
}
}
}
我的 VC 代码:
class CreateVC: UIViewController, Storyboarded {
@IBOutlet weak var createButton: Rounded!
@IBOutlet weak var passwordEntry: UITextField!
@IBOutlet weak var confirmPasswordEntry: UITextField!
weak var coordinator: AuthCoordinator?
var displayName: String!
var viewModel: CreateVM!
let disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
bindUI()
}
func bindUI() {
passwordEntry.rx.text.orEmpty.bind(to: viewModel.password).disposed(by: disposeBag)
confirmPasswordEntry.rx.text.orEmpty.bind(to: viewModel.confirmPassword).disposed(by: disposeBag)
viewModel.shouldHideButton.bind(to: createButton.rx.isHidden).disposed(by: disposeBag)
createButton.rx.tap.bind { [unowned self] _ in
self.viewModel.submit()
}.disposed(by: disposeBag)
}
}
解决方案
我将我的视图模型作为一个函数来做,很大程度上取决于你想要对结果做什么,但这里有一些示例代码可能会对你有所帮助:
struct CreateInput {
let password: Observable<String>
let confirm: Observable<String>
let submit: Observable<Void>
}
struct CreateOutput {
let displayName: String
let shouldHideButton: Observable<Bool>
let signUpResult: Observable<Result<Int, Error>>
}
func createVM(firebase: FirebaseHandler, email: String) -> (CreateInput) -> CreateOutput {
return { input in
let shouldHideButton = Observable.combineLatest(input.password, input.confirm) { $0.count < 5 || $0 != $1 }
let credentials = Observable.combineLatest(Observable.just(email), input.password) { (email: $0, password: $1) }
let signUpResult = input.submit
.withLatestFrom(credentials)
.flatMapLatest {
firebase.create(email: $0.email, password: $0.password)
}
return CreateOutput(
displayName: email,
shouldHideButton: shouldHideButton,
signUpResult: signUpResult
)
}
}
extension FirebaseHandler {
func create(email: String, password: String) -> Observable<Result<Int, Error>> {
Observable.create { observer in
self.createWithEmail(email: email, password: password) { (result) in
observer.onNext(result)
observer.onCompleted()
}
return Disposables.create()
}
}
}
final class CreateViewController: UIViewController {
@IBOutlet weak var displayNameLabel: UILabel!
@IBOutlet weak var createButton: UIButton!
@IBOutlet weak var passwordEntry: UITextField!
@IBOutlet weak var confirmPasswordEntry: UITextField!
var bindUI: (CreateInput) -> CreateOutput = { _ in fatalError() } // assign `createVM(firebase: myFirebaseHandler, email: "myEmail")` to this before it loads.
private let disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
let input = CreateInput(
password: passwordEntry.rx.text.orEmpty.asObservable(),
confirm: confirmPasswordEntry.rx.text.orEmpty.asObservable(),
submit: createButton.rx.tap.asObservable()
)
let output = bindUI(input)
displayNameLabel.text = output.displayName
output.shouldHideButton
.bind(to: createButton.rx.isHidden)
.disposed(by: disposeBag)
output.signUpResult
.bind { result in
switch result {
case .success(let uid):
print("uid:", uid)
case .failure(let error):
print("error:", error.localizedDescription)
}
}
.disposed(by: disposeBag)
}
}
如果高阶函数让您感到紧张,那么您可以将其包装在一个类型中:
struct CreateVM {
struct Input {
let password: Observable<String>
let confirm: Observable<String>
let submit: Observable<Void>
}
struct Output {
let displayName: String
let shouldHideButton: Observable<Bool>
let signUpResult: Observable<Result<Int, Error>>
}
let firebase: FirebaseHandler
let email: String
func bind(_ input: Input) -> Output {
let shouldHideButton = Observable.combineLatest(input.password, input.confirm) { $0.count < 5 || $0 != $1 }
let credentials = Observable.combineLatest(Observable.just(email), input.password) { (email: $0, password: $1) }
let signUpResult = input.submit
.withLatestFrom(credentials)
.flatMapLatest { [unowned firebase] in
firebase.create(email: $0.email, password: $0.password)
}
return Output(
displayName: email,
shouldHideButton: shouldHideButton,
signUpResult: signUpResult
)
}
}
然后您的视图控制器将具有一个属性:
var viewModel: CreateVM!
并使用以下命令构建输出:
let output = viewModel.bind(input)
推荐阅读
- html - HTML:不一致的网格面板高度?
- android - 即使我使用 handler.postDelayed 也会立即显示视图
- sql - 在表中进行交叉连接时会出现问题
- flutter - 悬停时颤振 DragTarget
- java - Spring Boot 和 Jquery 从前端的 hashmap 访问值
- java - 如何使用我的粒子发射器传递模式?
- wordpress - 如何使用 .htaccess 访问 404 页面
- xcode - how can I use facebook sdk swift 4.1 branch for ios
- scala - Simple play/scala benchmark, rendering a view versus a raw text output comparison
- java - How can I display dishName according to the searched price after sorting in jtable java?