Search code examples
iosobjective-ciphoneswiftreactive-cocoa

How to handle invalid token using ReactiveCocoa


I wonder what is the correct way to handle invalid token from server.

  1. The first solution came up from my mind is to fire up an event in ViewModel layer, listen that in rootViewController navigation to login page.

  2. The second solution (first solution works but I really don't like event, I would like to use signals instead of event), add an authenticationViewModel in rootViewController, subscribe invalidToken (or call it logout signal in view layer) signal in rootViewController, trigger signal in api calls.

I would like to ask, Is there any better way to handle this?


Solution

  • The example is based on RAC3 and I don't know if is the best solution but this is with what I came out after experimenting a bit.

    I store the authentication token in the View Model as a MutableProperty and in the View Controller I observe the valid and invalid token.

    Something like this:

    View Model:

    class LoginViewModel {
    
        let username = MutableProperty<String>("")
        let password = MutableProperty<String>("")
        let authToken = MutableProperty<String>("")
        let invalidToken = MutableProperty<String>("")
        private let _inputValid = MutableProperty(false)
    
        var action:Action<AnyObject?, String, NSError>?
        var cocoaAction:CocoaAction?
    
        init() {
           let validation: Signal<String, NoError> -> Signal<Bool, NoError> = map { string in
               return count(string) > 0
           }
    
           _inputValid <~ combineLatest(
               username.producer
                  |> validation,
               password.producer
                  |> validation)
            |> map({ user, pass in return user && pass })
    
            action = Action(enabledIf:_inputValid) { _ in
                return LoginService().login(self.username.value, password: self.password.value) }
            cocoaAction = CocoaAction(action!, input: nil)
            action!.values.observe(next: { self.authToken.put($0) })
            action!.errors.observe(next: { self.invalidToken.put($0)})
       }
    }
    

    In the ViewController you can observe the authToken and invalidToken:

    viewModel.authToken.producer
        |> filter { count($0) > 0 }
        |> start(next: {
            _ in
                // do something in case token is ok 
        })
    
    viewModel.invalidToken.producer
        |> filter { count($0) > 0 }
        |> start(next: {
            _ in
                // do something in case token is NOT ok 
        })
    

    And if your username and password are textfields you can map the input to the correspondent view model like this

    viewModel.username <~ userNameTextField.rac_textSignal().toSignalProducer() |> map { $0 as? String ?? "" } |> catch { _ in SignalProducer<String, NoError>.empty }
    viewModel.password <~ passwordTextField.rac_textSignal().toSignalProducer() |> map { $0 as? String ?? "" } |> catch { _ in SignalProducer<String, NoError>.empty }