Как реализовать аутентификацию Firebase в MVVM- C RxSwift - PullRequest
2 голосов
/ 25 марта 2020

Я пытаюсь реализовать приложение MVVM- C rx swift.

У меня есть виртуальная машина для моего контроллера представления регистрации с именем пользователя и паролем в качестве субъектов поведения. У меня также есть обработчик firebase, внедренный в VM. Как лучше всего передать результат регистрации обратно в V C.

код моей виртуальной машины:

класс 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
        }
    }
}

}

мой V C код:

класс CreateV C: 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)

}

}

1 Ответ

1 голос
/ 26 марта 2020

Я делаю мои модели представления как функцию, и многое зависит от того, что именно вы хотите сделать с результатом, но вот пример кода, который может вам помочь:

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)

...