Firebase Authでのサインイン・サインアウトの処理をRxSwiftを使ってイベントとして流せるようにしました。
また、RxSwift3.3.0から Single, Maybe, Completable が使えるようになったので、それらを使って実装してみます。
- Swift 3.1
- RxSwift 3.4.0
サインインの処理をSingleを使って
サインインの処理は Single を使って実装してみます。
Single だと1回だけ success(next)かerrorが流れるのを保証できます。
extension Reactive where Base: FIRAuth {
private static func parseUserResponse(_ observer: @escaping (SingleEvent<FIRUser>) -> Void) -> (FIRUser?, Error?) -> Void {
return { user, error in
if let user = user {
observer(.success(user))
} else if let error = error {
observer(.error(error))
} else {
fatalError()
}
}
}
public static func signIn(withEmail email: String, password: String) -> Single<FIRUser> {
return Single.create { observer in
if let auth = FIRAuth.auth() {
auth.signIn(withEmail: email, password: password, completion: parseUserResponse(observer))
} else {
observer(.error(FIRAuth.AuthError.notInitialized))
}
return Disposables.create()
}
}
public static func signIn(withCustomToken customToken: String) -> Single<FIRUser> {
return Single.create { observer in
if let auth = FIRAuth.auth() {
auth.signIn(withCustomToken: customToken, completion: parseUserResponse(observer))
} else {
observer(.error(FIRAuth.AuthError.notInitialized))
}
return Disposables.create()
}
}
public static func signInAnonymously() -> Single<FIRUser> {
return Single.create { observer in
if let auth = FIRAuth.auth() {
auth.signInAnonymously(completion: parseUserResponse(observer))
} else {
observer(.error(FIRAuth.AuthError.notInitialized))
}
return Disposables.create()
}
}
}
extension FIRAuth {
public enum AuthError: Error {
case notInitialized
}
}
Reactiveのextensionとして実装します。サインインの処理は様々あるのですが、その処理が完了した時のcallbackの引数はどれも同じなので、parseUserResponse
という関数を用意して、userがあれば success を、errorがあれば error を流すように実装します。
また、func
ではなくてstatic func
としているのは、 FIRAuth.auth()
が FIRAuth? 型で、イベントを組む時に意識しないといけないため、それを関数内で処理するようにしています。
基本的にはFIRApp.configure
で正常にsetupできていればnilになることは少ないですが。nilであった場合は、FIRAuth.AuthError.notInitialized
を流すようにしています。
使用例はこのようになります。
FIRAuth.rx.signInAnonymously().subscribe(onSuccess: { user in
print(user)
}, onError: { error in
print(error)
}).addDisposableTo(disposeBag)
そういえば3.4.0からは Single に subscribe(onSuccess:onError)
が追加されたのでsubscribe時の処理が書きやすくなりました。
FIRAuthCredential を使ったサインイン処理は一旦スキップして後ほど。
サインアウトの処理をCompletableを使って
サインアウトの処理は成功時に値を流す必要はなく、完了したかエラーだったかを返せれば良いので Completable を使います。
public static func signOut() -> Completable {
return Completable.create { observer in
if let auth = FIRAuth.auth() {
do {
try auth.signOut()
observer(.completed)
} catch let error {
observer(.error(error))
}
} else {
observer(.error(FIRAuth.AuthError.notInitialized))
}
return Disposables.create()
}
}
使用例はこのようになります。
FIRAuth.rx.signOut().subscribe(onCompleted: {
print("signed out")
}, onError: { error in
print(error)
}).addDisposableTo(disposeBag)
FIRAuthCredentialを用いたサインイン処理
最後にFIRAuthCredentialを用いたサインイン処理ですが、Google,Twitter,Github等の AuthProvider があるので、それらを纏めてより簡単にサインイン処理が行えるようにしてみます。
extension FIRAuth {
public enum AuthProvider {
case emailPassword(String, String) // email, password
case facebook(String) // accessToken
case google(String, String) // IDToken, accessToken
case twitter(String, String) // token, secret
case github(String) // token
var credential: FIRAuthCredential {
switch self {
case .emailPassword(let email, let password):
return FIREmailPasswordAuthProvider.credential(withEmail: email, password: password)
case .facebook(let accessToken):
return FIRFacebookAuthProvider.credential(withAccessToken: accessToken)
case .google(let idToken, let accessToken):
return FIRGoogleAuthProvider.credential(withIDToken: idToken, accessToken: accessToken)
case .twitter(let token, let secret):
return FIRTwitterAuthProvider.credential(withToken: token, secret: secret)
case .github(let token):
return FIRGitHubAuthProvider.credential(withToken: token)
}
}
}
}
extension Reactive where Base: FIRAuth {
public static func signIn(authProvider: FIRAuth.AuthProvider) -> Single<FIRUser> {
return Single.create { observer in
if let auth = FIRAuth.auth() {
auth.signIn(with: authProvider.credential, completion: parseUserResponse(observer))
} else {
observer(.error(FIRAuth.AuthError.notInitialized))
}
return Disposables.create()
}
}
}
FIRAuthの下にAuthProvider
というenumを作り、それぞれのプロバイダに必要な情報を渡してcredentialを作成する処理を実装します。
そのFIRAuth.AuthProvider
を使ってサインインする処理を Single を使って実装すれば完了です。
使用例はこのようになります。
GithubOAuth.authorize()
.flatMap { accessToken -> Single<FIRUser> in
FIRAuth.rx.signIn(authProvider: .github(accessToken))
}.subscribe(onSuccess: { user in
print(user)
}, onError: { error in
print(error)
}).addDisposableTo(disposeBag)
さいごに
新しく増えたSingle, Maybe, Completableの勉強がてら、FirebaseAuthでの処理をrx化してみました。
ソース(全容)は Gistに置いておきます。