Skip to content

Instantly share code, notes, and snippets.

@marty-suzuki
Last active August 26, 2021 20:34
Show Gist options
  • Save marty-suzuki/96f9d6cd0c4419555b5eac7c4b34cbd6 to your computer and use it in GitHub Desktop.
Save marty-suzuki/96f9d6cd0c4419555b5eac7c4b34cbd6 to your computer and use it in GitHub Desktop.
import Combine
import FirebaseAuth
public struct CombineAuth {
fileprivate let auth: Auth
}
extension CombineAuth {
public enum Error: Swift.Error {
case userNotFound
}
public final class Subscription<S: Subscriber>: Combine.Subscription where S.Input == (Auth, User), S.Failure == Error {
private var subscriber: S?
private let auth: Auth
private let _cancel: (Auth) -> Void
fileprivate init(subscriber: S,
auth: Auth,
addListener: @escaping (Auth, @escaping (Auth, User?) -> Void) -> NSObjectProtocol,
removeListener: @escaping (Auth, NSObjectProtocol) -> Void) {
self.subscriber = subscriber
self.auth = auth
let listener = addListener(auth) { auth, user in
if let user = user {
_ = subscriber.receive((auth, user))
} else {
subscriber.receive(completion: .failure(.userNotFound))
}
}
self._cancel = { removeListener($0, listener) }
}
public func request(_ demand: Subscribers.Demand) {}
public func cancel() {
_cancel(auth)
subscriber = nil
}
}
public struct Publisher: Combine.Publisher {
public typealias Output = (Auth, User)
public typealias Failure = Error
private let auth: Auth
private let addListener: (Auth, @escaping (Auth, User?) -> Void) -> NSObjectProtocol
private let removeListener: (Auth, NSObjectProtocol) -> Void
init(auth: Auth,
addListener: @escaping (Auth, @escaping (Auth, User?) -> Void) -> NSObjectProtocol,
removeListener: @escaping (Auth, NSObjectProtocol) -> Void) {
self.auth = auth
self.addListener = addListener
self.removeListener = removeListener
}
public func receive<S>(subscriber: S) where S : Subscriber, S.Failure == Failure, S.Input == Output {
let subscription = Subscription(subscriber: subscriber,
auth: auth,
addListener: addListener,
removeListener: removeListener)
subscriber.receive(subscription: subscription)
}
}
}
extension Auth {
public var combine: CombineAuth {
return CombineAuth(auth: self)
}
}
extension CombineAuth {
public func stateDidChange() -> AnyPublisher<(Auth, User), Error> {
return Publisher(auth: auth,
addListener: { $0.addStateDidChangeListener($1) },
removeListener: { $0.removeStateDidChangeListener($1) })
.eraseToAnyPublisher()
}
public func idTokenDidChange() -> AnyPublisher<(Auth, User), Error> {
return Publisher(auth: auth,
addListener: { $0.addIDTokenDidChangeListener($1) },
removeListener: { $0.removeIDTokenDidChangeListener($1) })
.eraseToAnyPublisher()
}
}
func doIt() {
class Store {
var user: User?
}
let store = Store()
let cancellable = Auth.auth().combine.stateDidChange()
.map { $1 }
.catch { _ in Just(Optional<User>.none) }
.assign(to: \.user, on: store)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment