Code Monkey home page Code Monkey logo

Comments (8)

twittemb avatar twittemb commented on May 19, 2024 1

Hi @yudinm

In order to help you efficiently, It would be great if I could see a bigger part of your code base ... but I assume it is not open source :-( so I will try to give you some insights:

Why VC lifecycle matters ?
The only reason RxFlow "listens" to your ViewControllers lifecycle is to be able to restrain the effects of a Stepper to the VC (and the Flow) that is currently displayed. To do so, RxFlow needs to know if the VC associated to the Stepper that has triggered a new Step is displayed or not, and if yes -> the Step can be given to the Flow, if not -> the Step is ignored.

RxFlow does this to avoid performing a navigation coming from a VC that is not displayed (for instance from a VC that is lower than the current VC in your navigation stack), because that could lead to UIKit errors (by making a VC present a another VC whereas it is not actually the currently displayed VC).

RxFlow also performs such checks at a Flow level.

Handling user connection state

I personally had to deal with "user authentication state" in previous applications. In that case, I like to have a "UserState" value exposed through a Rx Observable by a low level layer (perhaps a UserService or something). This UserState can be an enum representing the current user, for instance:

enum UserState {
    case unauthenticated
    case authenticated (User)
}

If for some reason, a request returns a code 401, you could mutate the UserState to ".unauthenticated". As it is an Observable, you could react to this mutation.

For instance, the Stepper associated with the root Flow could subscribe to this UserState Observable and and trigger a MyStep.auth step if the state equals .unauthenticated. In that case the root Flow could display a login popup to authenticate the user. Doing so, your app is able to prompt the user to login each time the token is expired.

Could this match your requirements ?

from rxflow.

twittemb avatar twittemb commented on May 19, 2024

Hi @yudinm

I Have a few questions:

  • In the TaskViewModel, the network call is done in the init function ?
  • Is there an error message or it is just not working ?

My take on this -> the MySteps.auth step is triggered too soon (because the network call is done in the init function of the TaskViewModel) and is catched by your Flow whereas the animation is not finished yet. Then your Flow triggers a new navigateXXX function (because of the MySteps.auth step) and then UIKit cannot display the auth screen because of the animation is not finished on the previous screen.

You should try to put your network call in a dedicated function in your viewModel and call this function in the viewDidLoad of the taskViewController.

Keep me in touch.

from rxflow.

yudinm avatar yudinm commented on May 19, 2024

Thank you for quick answer.

In the TaskViewModel, the network call is done in the init function ?
No. Network call done in viewDidLoad.

Is there an error message or it is just not working ?
No error. Just did not go to coordinator.rx.willNavigate/didNavigate and so on. Also i try to subscribe on .step in network error handler and see that:

// animated: true
401 Unauthorized!
step: NoneStep()
step: auth
// animated: false
step: NoneStep()
did navigate to flow=TaskFlow and step=auth
did navigate to flow=DashboardFlow and step=auth
did navigate to flow=AppFlow and step=auth
will navigate to flow=LoginFlow and step=login
did navigate to flow=LoginFlow and step=login
step: auth

Checked calls in VC. Found, that network call triggered by let viewWillAppear = rx.sentMessage(#selector(UIViewController.viewWillAppear(_:))).mapToVoid().asDriverOnErrorJustComplete(). Fixed by replacing viewWillAppear to viewDidAppear.

However, problem confuse me. Why Stepper or Coordinator work depends from animation and VC state? In my case we have access token that sends in http headers.

  1. User get tasks list (first network call)
  2. Access token invalidates on server
  3. User taps on task item. Its pushes details VC (second network call)
  4. Getting network error with status code 401

On this error we want navigate user back to login flow. That kind of error can perhaps in very different cases: on background requests, when user quickly taps on different items and switch screens. How we can assured redirect user to login (or something other) flow without any knowledge about current view state, transitions?

from rxflow.

twittemb avatar twittemb commented on May 19, 2024

@yudinm how is it going with yours flows ?

from rxflow.

yudinm avatar yudinm commented on May 19, 2024

Hi. Sorry, appeared tasks not related to the Flows.

With VC lifecycle – it's clear.

With connection state we'll use your way, probably. Thank you.

For now we just added extension to ObservableType:

import RxSwift

protocol ErrorHandler {
    func handle(_ error: Error)
}

extension ObservableType {
    func handleError(_ errorHandler: ErrorHandler) -> Observable<E> {
        return Observable.create({ observer in
            let subscription = self.subscribe { e in
                switch e {
                case .next(let value):
                    observer.on(.next(value))
                case .error(let error):
                    errorHandler.handle(error)
                    observer.on(.error(error))
                case .completed:
                    observer.on(.completed)
                }
            }
            
            return subscription
        })
    }
}

And in ViewModels implement protocol:

extension TaskListViewModel : ErrorHandler {
    func handle(_ error: Error) {
        guard let error = error as? MoyaError else { return }
        guard case .statusCode(let response) = error else { return }
        
        if response.statusCode == 401 {
            print("\(response.statusCode) Unauthorized!")
            self.services.preferencesService.invalidateUser()
            self.step.accept(Steps.auth)
        } else {
            print("Error statusCode: \(response.statusCode)")
        }
    }
}

Anyway error handling is a future task in current app.

I would like to add, that ViewControllers as a Stepper mixed with ViewModels as a Stepper produces some confusion. In our case we have agreed to use only ViewModels as a Steppers.

Thank you. Good stuff.

from rxflow.

twittemb avatar twittemb commented on May 19, 2024

@yudinm

Thanks for your feedback. You're totally right about ViewModels being the best candidate for Stepper.

If i may make a suggestion, it feels like your extension to ObservableType is doing something very similar to the built-in do(onError) function. Is there a reason you don't use it ?

from rxflow.

twittemb avatar twittemb commented on May 19, 2024

@yudinm

Can I close this issue ?

Thanks.

from rxflow.

yudinm avatar yudinm commented on May 19, 2024

Yes. Thanks! You may close.

We like to make extension, because we do not want include "big" block in chain. We will call .handleError(self) in chain. And implement enum of error handlers somewhere outside of ViewModel.

        let tasks = input.trigger.flatMapLatest { _ -> Driver<[TaskListViewModelItem]> in
            return apiClient.getTasks()
                .asObservable()
                .handleError(self)
                .map{ return $0.map(TaskListViewModelItem.init) }
                .asObservable()
                .trackActivity(activityIndicator)
                .asDriver(onErrorJustReturn: [])
        }

from rxflow.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.