Converting a callback into an Effect

I converted a callback from a firebase register createUser function into an Effect like this:

  func register(email: String, password: String) -> Effect<User, RegisterApiError> {
      Future<User, RegisterApiError> { promise in
        Auth.auth().createUser(withEmail: email, password: password) { authResult, error in
          if let error = error {
            promise(.failure(RegisterApiError(errorName: error.localizedDescription)))
          }
          else if let name = authResult?.user.displayName, let email = authResult?.user.email  {
            let user = User(name: name,
                            email: email)
            promise(.success(user))
          }
        }
      }
      .eraseToAnyPublisher()
      .eraseToEffect()
  }

I feel like I must not be understanding how to use Effects -- is there a better way to do this?

2 Likes

Yes, there is a better way. But first: you have a logic error. The Future initializer runs its argument immediately. But an Effect should not do anything until you subscribe to its publisher! You need to wrap the Future in a Deferred publisher so that the closure doesn't get called until there's a subscriber.

Also, you don't need to use eraseToAnyPublisher before eraseToEffect, because eraseToEffect is a Publisher extension so you can use it directly on Deferred or (unfortunately, in this case) Future.

Anyway, here's the better way: Effect has a static future method that applies Deferred, Future, and eraseToEffect for you. Something like this should work:

func register(email: String, password: String) -> Effect<User, RegisterApiError> {
    return .future { promise in
        Auth.auth().createUser(withEmail: email, password: password) { authResult, error in
            if let error = error {
                promise(.failure(RegisterApiError(errorName: error.localizedDescription)))
            }
            else if let name = authResult?.user.displayName, let email = authResult?.user.email  {
                let user = User(name: name,
                                email: email)
                promise(.success(user))
            }
        }
    }
}
2 Likes

Beautiful! That makes perfect sense, thank you.