6

I am implementing a library with a Login function using Swift Combine.

With the following code, I am already able to login, serialize the response to an object AuthenticationResult, and return a Publisher with a User or ApiError as a result.

As part of the response (AuthenticationResult object), I am also getting an accessToken from the API. I would like to save that token locally as part of this stream. But I would like that to be done internally in the library, so that the response from the login function does not change.

Is there a way to handle this type of situations in the stream, to handle the event but then to continue with the stream to return an User?

func authenticate(username: String, password: String) -> AnyPublisher<User, ApiError> {
    let parameters: [String: Any] = [
        "username": username,
        "pw": password
    ]

    var request = URLRequest(endpoint: Endpoint.login)
    request.addValue("application/json", forHTTPHeaderField: "Content-Type")
    request.httpBody = try? JSONSerialization.data(withJSONObject: parameters, options: [])

    return URLSession.shared.dataTaskPublisher(for: request)
        .map { $0.data }
        .decode(type: AuthenticationResult.self, decoder: JSONDecoder())
        .map { $0.user }
    // Here I would like to do something like: .handleAndContinue { self.save($0.accessToken }
        .mapError { _ in ApiError.loginError }
        .receive(on: RunLoop.main)
        .eraseToAnyPublisher()
}

Or is there any other way to handle this type of situations with Swift Combine?

StuckOverFlow
  • 851
  • 6
  • 15

1 Answers1

8

That can be done with:

        .handleEvents(receiveOutput: {
            self.save($0.accessToken)
        })
StuckOverFlow
  • 851
  • 6
  • 15
  • 3
    No, `.handleEvents` is a debugging call. I think it would be better to say `.map { self.save($0.accessToken); return $0 }`. – matt Apr 11 '20 at 01:03
  • 7
    why do you think it's a debugging tool? – Rufat Mirza Oct 07 '20 at 15:51
  • 4
    @matt it's not a debugging tool. It's how you differentiate pure side effect (`handleEvents`) from pure transformation (`map`). Using `map` for side effect is a code smell. – Michał Klimczak Dec 29 '20 at 14:54
  • 5
    @MichałK the debugging characterization comes from Apple: https://developer.apple.com/documentation/combine/publishers – matt Dec 29 '20 at 18:25
  • I can't find anything about debugging under that link. Nor under docs about handleEvents: https://developer.apple.com/documentation/combine/publishers/handleevents. – Michał Klimczak Dec 30 '20 at 08:32
  • 1
    Looks like @matt is right. You'll find `handleEvents` in the Debugging section [here](https://developer.apple.com/documentation/combine/publisher). Also have a look at the Discussion section [here](https://developer.apple.com/documentation/combine/publisher/handleevents(receivesubscription:receiveoutput:receivecompletion:receivecancel:receiverequest:)). That said, it doesn't mean you can't use it for non-debugging purposes, though. – Lars Jul 25 '21 at 13:15