🔑
SwiftUI × Firebase Authで認証機能を追加する ~実装編~(2/2)
1. はじめに
本記事は、以下の記事の続きになります。
2. 認証機能を実装
今回は以下の構成で作成しようと思います。
認証用クラス(AuthViewModel)を作成する
AuthViewModel.swift
import SwiftUI
import FirebaseAuth
import GoogleSignIn
import FirebaseCore
class AuthViewModel: ObservableObject {
@Published var user: User?
@Published var isAuthenticated: Bool = false
@Published var errorMessage: String = ""
init() {
checkUserState()
}
func checkUserState() {
if let user = Auth.auth().currentUser {
self.user = user
self.isAuthenticated = true
} else {
self.isAuthenticated = false
self.user = nil
}
}
// メールとパスワードでログイン
func signInWithEmail(email: String, password: String) {
Auth.auth().signIn(withEmail: email, password: password) { [weak self] result, error in
guard let self = self else { return }
if let error = error {
self.errorMessage = error.localizedDescription
return
}
if let user = result?.user {
self.user = user
self.isAuthenticated = true
}
}
}
// メールとパスワードで新規登録
func signUpWithEmail(email: String, password: String) {
Auth.auth().createUser(withEmail: email, password: password) { [weak self] result, error in
guard let self = self else { return }
if let error = error {
self.errorMessage = error.localizedDescription
return
}
if let user = result?.user {
self.user = user
self.isAuthenticated = true
}
}
}
// Googleアカウントでログイン
func signInWithGoogle() {
guard let clientID = FirebaseApp.app()?.options.clientID else { return }
let config = GIDConfiguration(clientID: clientID)
guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
let rootViewController = windowScene.windows.first?.rootViewController else {
return
}
GIDSignIn.sharedInstance.signIn(
withPresenting: rootViewController,
hint: nil,
additionalScopes: []
) { [weak self] result, error in
guard let self = self else { return }
if let error = error {
self.errorMessage = error.localizedDescription
return
}
guard let user = result?.user,
let idToken = user.idToken?.tokenString else {
return
}
let credential = GoogleAuthProvider.credential(
withIDToken: idToken,
accessToken: user.accessToken.tokenString
)
// Firebaseにログイン
Auth.auth().signIn(with: credential) { result, error in
if let error = error {
self.errorMessage = error.localizedDescription
return
}
if let user = result?.user {
self.user = user
self.isAuthenticated = true
}
}
}
}
// ログアウト
func signOut() {
do {
try Auth.auth().signOut()
self.isAuthenticated = false
self.user = nil
} catch let error {
self.errorMessage = error.localizedDescription
}
}
}
認証画面(LoginView/EmailAuthView)を作成する
LoginView.swift
import SwiftUI
struct LoginView: View {
@EnvironmentObject var authViewModel: AuthViewModel
@State private var showingEmailAuth = false
var body: some View {
VStack(spacing: 20) {
Text("認証アプリ")
.font(.largeTitle)
.fontWeight(.bold)
.padding(.top, 50)
Spacer()
VStack(spacing: 15) {
Button(action: {
showingEmailAuth = true
}) {
HStack {
Image(systemName: "envelope.fill")
.foregroundColor(.white)
Text("メールで認証")
.foregroundColor(.white)
.fontWeight(.semibold)
}
.frame(maxWidth: .infinity)
.padding()
.background(Color.blue)
.cornerRadius(10)
}
Button(action: {
authViewModel.signInWithGoogle()
}) {
HStack {
Image(systemName: "g.circle.fill")
.foregroundColor(.white)
Text("Googleで認証")
.foregroundColor(.white)
.fontWeight(.semibold)
}
.frame(maxWidth: .infinity)
.padding()
.background(Color.red)
.cornerRadius(10)
}
}
.padding(.horizontal, 30)
Spacer()
if !authViewModel.errorMessage.isEmpty {
Text(authViewModel.errorMessage)
.foregroundColor(.red)
.padding()
}
}
.sheet(isPresented: $showingEmailAuth) {
EmailAuthView()
.environmentObject(authViewModel)
}
}
}
EmailAuthView.swift
import SwiftUI
struct EmailAuthView: View {
@EnvironmentObject var authViewModel: AuthViewModel
@Environment(\.presentationMode) var presentationMode
@State private var email = ""
@State private var password = ""
@State private var isSignUp = false
var body: some View {
NavigationView {
VStack(spacing: 20) {
Text(isSignUp ? "アカウント作成" : "ログイン")
.font(.largeTitle)
.fontWeight(.bold)
.padding(.top, 30)
VStack(spacing: 15) {
TextField("メールアドレス", text: $email)
.autocapitalization(.none)
.keyboardType(.emailAddress)
.padding()
.background(Color.gray.opacity(0.1))
.cornerRadius(10)
SecureField("パスワード", text: $password)
.padding()
.background(Color.gray.opacity(0.1))
.cornerRadius(10)
}
.padding(.horizontal, 30)
.padding(.top, 20)
Button(action: {
if isSignUp {
authViewModel.signUpWithEmail(email: email, password: password)
} else {
authViewModel.signInWithEmail(email: email, password: password)
}
if authViewModel.isAuthenticated {
presentationMode.wrappedValue.dismiss()
}
}) {
Text(isSignUp ? "登録する" : "ログインする")
.foregroundColor(.white)
.fontWeight(.semibold)
.frame(maxWidth: .infinity)
.padding()
.background(Color.blue)
.cornerRadius(10)
.padding(.horizontal, 30)
}
Button(action: {
isSignUp.toggle()
}) {
Text(isSignUp ? "既にアカウントをお持ちの方はこちら" : "アカウントをお持ちでない方はこちら")
.foregroundColor(.blue)
}
Spacer()
if !authViewModel.errorMessage.isEmpty {
Text(authViewModel.errorMessage)
.foregroundColor(.red)
.padding()
}
}
.navigationBarItems(leading: Button("キャンセル") {
presentationMode.wrappedValue.dismiss()
})
}
}
}
ContentView.swift
import SwiftUI
struct ContentView: View {
@StateObject private var authViewModel = AuthViewModel()
var body: some View {
Group {
if authViewModel.isAuthenticated {
MainView()
.environmentObject(authViewModel)
} else {
LoginView()
.environmentObject(authViewModel)
}
}
.onAppear {
authViewModel.checkUserState()
}
}
}
エントリーポイント(YourApp)を作成する
AuthApp.swift
import SwiftUI
import Firebase
import GoogleSignIn
@main
struct AuthApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var delegate
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
class AppDelegate: NSObject, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
FirebaseApp.configure()
return true
}
// Google認証のためのURLハンドリング
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
return GIDSignIn.sharedInstance.handle(url)
}
}
メイン画面(MainView)を作成する
MainView.swift
import SwiftUI
struct MainView: View {
@EnvironmentObject var authViewModel: AuthViewModel
var body: some View {
VStack {
Text("Hello World")
.font(.largeTitle)
.fontWeight(.bold)
.padding()
Button(action: {
authViewModel.signOut()
}) {
Text("ログアウト")
.foregroundColor(.white)
.padding()
.background(Color.red)
.cornerRadius(10)
}
.padding()
}
}
}
動作確認
画面は以下のようになります。
また、一度ログインすればアプリを再起動しても直接メイン画面に遷移します。
Firebase管理画面上から見ると…
3. まとめ
今回はSwiftUIとFirebase Authを使ってiOSアプリに認証機能を追加しました。
便利なライブラリが用意されているため、容易に実装が可能であることがわかっていただけたかと思います。本記事により、皆さんのアプリ開発の幅が広がることを願います。
関連記事
Discussion