обрабатывать вложенные наблюдаемые в RxSwift

Я пытаюсь совместить вход в facebook с вызовом отдыха, поэтому, когда пользователь вошел в систему, он должен выполнить вызов аутентификации на сервер, где сервер выполняет вызовы графа, однако я немного смущен тем, как я вставляю звонки с RxSwift? пока у меня есть класс FacebookProvider со следующим методом

func login() -> Observable<String> {
    return Observable.create({ observer in

        let loginManager = LoginManager()

        //LogOut before
        loginManager.logOut()

        //Set Login Method
        loginManager.loginBehavior = .native

        //Login Closure
        loginManager.logIn([ .publicProfile, .userFriends, .email], viewController: self.parentController) { loginResult in
            switch loginResult {
            case .failed(let error):
                print(error)
                observer.onError(FacebookError.NoConnection(L10n.networkError))
            case .cancelled:
                print("User cancelled login.")
            case .success(_, let declinedPermissions, let accessToken):
                print("Logged in!")

                guard declinedPermissions.count > 0 else {
                    observer.onError(FacebookError.DeclinedPermission(L10n.declinedPermission))
                    return
                }

                observer.onNext(accessToken.authenticationToken)
                observer.onCompleted()

            }
        }


        return Disposables.create()
    })

}

Тогда у меня есть LoginViewModel с этой моделью

public func retrieveUserData() -> Observable<User> {
    return Network.provider
                .request(.auth(fbToken: Globals.facebookToken)).retry(5).debug().mapObject(User.self)
}

тогда я в своем UIViewController делаю это

    facebookProvider.validate().subscribe({ [weak self] response in

        switch response {
        case .error(_):
            // User is not logged in push to loginController
            break

        case .next():
            //user is logged in retrieveUserData before proceeding

            self?.loginViewModel.retrieveUserData().subscribe { event in
                switch event {
                case .next(let response):
                    print(response)
                case .error(let error):
                    print(error)
                case .completed:
                    print("completed")
                }
            }.addDisposableTo(self?.disposeBag)


            break
        case .completed:
            //data is retrieved and can now push to app
            break


        }

    }).addDisposableTo(disposeBag)

Подтвердить

public func rx_validate() -> Observable<String> {
    return Observable.create({ observer in
        //Check if AccessToken exist
        if AccessToken.current == nil {
            observer.onError(FacebookError.NotLoggedIn)
        } else {
            observer.onNext(Globals.accessToken)
        }
        observer.onCompleted()
        return Disposables.create()
    })
}

person Peter Pik    schedule 11.01.2017    source источник


Ответы (1)


Вы захотите использовать flatMap

Закрытие, переданное flatMap, вернет наблюдаемое. Затем flatMap позаботится о его распаковке, то есть если замыкание возвращает значение типа Observable<T>, а вы вызываете flatMap для значения типа Observable<U>, результирующий наблюдаемый объект будет Observable<T> (а не Observable<Observable<T>>

В данном конкретном случае код будет выглядеть так:

facebookProvider.validate().flatMap { [weak self] _ in
  return self?.loginViewModel.retrieveUserData()
}.subscribe { event in
  switch event {
    // ...
  }
}.addDisposableTo(disposeBag)

Кстати, вам, вероятно, следует обновить func retrieveUserData(), чтобы принять токен в качестве параметра, а не извлекать его из вашей структуры Globals.

Полученный код будет выглядеть примерно так

public func retrieveUserData(token: String) -> Observable<User> {
    return Network.provider
            .request(.auth(fbToken:  token)).retry(5).debug().mapObject(User.self)
}

в viewController

facebookProvider.validate().flatMap { [weak self] token in
  return self?.loginViewModel.retrieveUserData(token: token)
}.subscribe { event in
  switch event {
    // ...
  }
}.addDisposableTo(disposeBag)
person tomahh    schedule 11.01.2017
comment
почему здесь не используется addDisposableTo(disposeBag)? - person Peter Pik; 11.01.2017
comment
Просто упущение с моей стороны, извините за путаницу. Я обновил ответ. - person tomahh; 11.01.2017
comment
Я добавил функцию validate с незначительным изменением имени, это просто простая функция, проверяющая, сохранен ли уже AccessToken, и тем самым передаю ее дальше, однако это дает мне следующую ошибку Cannot convert value of type (_) -> Observable<User?> to expected argument type (string) -> O - person Peter Pik; 11.01.2017
comment
Я проверил это, и похоже, проблема с [weak self], так как удаление работает. - person Peter Pik; 11.01.2017
comment
И как мне получить событие подписки для validate() в viewController, поскольку нижний код, похоже, ничего не печатает ни в error, ни в next, ни в completed. думаю, это связано с ошибкой в ​​validate() - person Peter Pik; 11.01.2017
comment
Будьте осторожны при удалении [weak self] или [unowned self], потому что это может привести к утечке памяти, как объяснил @tomahh здесь: about-instance-methods-as-param" title="использование себя при замыканиях rxswift, как насчет методов экземпляра в качестве параметра"> stackoverflow.com/questions/40583685/ - person dsapalo; 19.01.2017