Skip to content
This repository has been archived by the owner on Jun 19, 2019. It is now read-only.

Error handling #6

Closed
IwanKaramazow opened this issue Jan 10, 2016 · 1 comment
Closed

Error handling #6

IwanKaramazow opened this issue Jan 10, 2016 · 1 comment
Labels

Comments

@IwanKaramazow
Copy link

First of all great work with this library!

This is not really an issue, but more of a design problem.
I typically use channels to coordinate the asynchronous corner of an app.

My question is how do I design an async response (either succes or error) and put it on a channel?
I came up with something like

// renamed `dispatch` to `go`
go {  
       let chans = fetchLogin(user)
       // fetch login calls my api and returns a tuple: fetchlogin(user: User) -> (Chan<Error>, Chan<User>)
       _select {
          _case(chans.0){ err in /*do stuff with the error*/}
          _case(chans.1){ resp in /*do stuff with the success response*/}
      }
}

I have no idea if this is an elegant solution, I'm coming from dynamic languages where I could just put both an error and a valid response on one channel, then I would determine if it was an error or valid response when taking the value from the channel. Here channels are typed, I can't put both on the same channel. I just put them on different channels.

The current 'best practice' with Swift is designing an Enum:

Enum Response<T> {
   case Success(T)
   case Failure(Error)
}

I tried putting such an enum on a Channel, but Swift's type system doesn't accept it.

Any ideas? Maybe I can make it into a recipe and put it in the docs of this repo.

@tidwall
Copy link
Owner

tidwall commented Jan 10, 2016

Thanks for the kind words.

I'm not aware of an official Swift way to handle this issue, but I don't recommend returning multiple channels in a tuple. Though it's possible to make this work, I believe the fewer the channels the cleaner the code.

One approach that I've used many times in the Golang environment in the past is to return a single custom type that contains both the error and response values.

For example, let say you have a User type and a method named login which does work in the background.

class User {
    var id : String = ""
}

enum LoginError: ErrorType {
    case Success
    case Failed
}

struct LoginResponse {
    var user : User
    var error : LoginError
}

func login(username: String, password: String) -> Chan<LoginResponse> {
    let ch = Chan<LoginResponse>()
    dispatch {
        // do some login magic in the background
        let user = User()
        if username != "user" || password != "password"{
            ch <- LoginResponse(user: user, error: .Failed)
            return
        }
        user.id = "1234"
        ch <- LoginResponse(user: user, error: .Success)
    }
    return ch
}

In the foreground, or perhaps from another dispatch routine...

let ch = login("user", password: "password")
let resp = <-ch
if resp!.error != .Success {
    print("login failed", resp!.error)
    return
}
print("login success", resp!.user.id)

I'm sure there are multiple ways to handle this. But this seems the easiest to understand for myself.

Also this example show a way to have an enum in the Channel.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

2 participants