前提
こちらの記事の続きでFirebaseAuthを用いてログイン機能を実装します。
参考
導入手順などは公式サイトの手順通りに行いました。
GitHub
実装
アプリ起動時にFirebase.configure()を呼びます。
import SwiftUI
import Firebase
@main
struct SwiftUI_MVVM_LoginApp: App {
init() {
FirebaseApp.configure()
}
var body: some Scene {
WindowGroup {
NavigationView {
ContentView(viewModel: ContentViewModel(firebaseAuthService: FirebaseAuthService.shared))
}
}
}
}
FirebaseAuth用のServiceを作成します。
シングルトンで作ってみました。
addStateDidChangeListenerのクロージャーで受け取ったuserを保持しておけば
ログイン中のユーザー情報をこの常にこのServiceから取得することもできます。
import FirebaseAuth
protocol IFirebaseAuthService: AnyObject {
func addStateDidChangeListener(completion: @escaping (Bool) -> Void)
func removeStateDidChangeListener()
func signIn(email: String?, password: String?, completion: @escaping (Error?) -> Void)
func signUp(email: String?, password: String?, completion: @escaping (Error?) -> Void)
func signOut()
}
final class FirebaseAuthService: IFirebaseAuthService {
public static let shared = FirebaseAuthService()
private var handler: AuthStateDidChangeListenerHandle?
private init() {}
deinit {
removeStateDidChangeListener()
}
func addStateDidChangeListener(completion: @escaping (Bool) -> Void) {
handler = Auth.auth().addStateDidChangeListener { (auth, user) in
if let _ = user {
completion(true)
} else {
completion(false)
}
}
}
func removeStateDidChangeListener() {
if let handler = handler {
Auth.auth().removeStateDidChangeListener(handler)
}
}
func signUp(email: String?, password: String?, completion: @escaping (Error?) -> Void) {
guard let email = email, let password = password else {
completion(nil)
return
}
Auth.auth().createUser(withEmail: email, password: password) { authResult, error in
completion(error)
}
}
func signIn(email: String?, password: String?, completion: @escaping (Error?) -> Void) {
guard let email = email, let password = password else {
completion(nil)
return
}
Auth.auth().signIn(withEmail: email, password: password) { authResult, error in
completion(error)
}
}
func signOut() {
do {
try Auth.auth().signOut()
}
catch(let error) {
debugPrint(error.localizedDescription)
}
}
}
ContentViewのViewModelを作成して、ログイン中かどうかのフラグを持たせています。
import FirebaseAuth
import Combine
final class ContentViewModel: ObservableObject {
@Published var isLogin: Bool = false
private let firebaseAuthService: IFirebaseAuthService
// Input: Viewで発生するイベントをViewModelで検知するためのもの
let didTapLogoutButton = PassthroughSubject<Void, Never>()
// cancellable
private var cancellables = Set<AnyCancellable>()
init(firebaseAuthService: IFirebaseAuthService) {
self.firebaseAuthService = firebaseAuthService
firebaseAuthService.addStateDidChangeListener(completion: { [weak self] in
self?.isLogin = $0
})
didTapLogoutButton
.sink(receiveValue: {
firebaseAuthService.signOut()
})
.store(in: &cancellables)
}
}
あとはログイン画面や新規登録画面でFirebaseAuthServiceのメソッドを呼ぶだけです
ログイン処理
didTapLoginButton
.sink(receiveValue: { [weak self] in
firebaseAuthService.signIn(email: self?.email, password: self?.password, completion: {
if let error = $0 {
self?.isShowError = (true, error.localizedDescription)
}
})
})
.store(in: &cancellables)
新規登録処理
didTapSignUpButton
.sink(receiveValue: { [weak self] in
firebaseAuthService.signUp(email: self?.email, password: self?.password, completion: { [weak self] in
if let error = $0 {
self?.isShowError = (true, error.localizedDescription)
}
})
})
.store(in: &cancellables)