如何在 MVVM-C RxSwift 中实现 firebase 身份验证

Posted

技术标签:

【中文标题】如何在 MVVM-C RxSwift 中实现 firebase 身份验证【英文标题】:How to implement firebase auth into MVVM-C RxSwift 【发布时间】:2020-03-25 12:27:07 【问题描述】:

我正在尝试实现一个 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)

    

【问题讨论】:

VC想对注册结果做什么? 【参考方案1】:

我将我的视图模型作为一个函数来做,很大程度上取决于你想对结果做什么,但这里有一些示例代码可能会对你有所帮助:

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)

【讨论】:

感谢这极大地帮助了我理解 VM 和 VC 应该如何通信。它也给了我一些关于我需要复习的事情的指示,但总的来说我确实理解你的实现:D 非常感谢!

以上是关于如何在 MVVM-C RxSwift 中实现 firebase 身份验证的主要内容,如果未能解决你的问题,请参考以下文章

传递数据 MVVM 和 RxSwift

使用 Observable 绑定 BehaviorRelay - RxSwift

在 RxSwift 中设置 TextField 字符的限制 [重复]

RxSwift 从一个创建多个 Observable

IOS Rxswift 使用 Kingfisher 预取 cell Image

在 Firestore 查询中实现 OR - Firebase firestore