😇
SwiftUI signInAnonymously
Tips💡
SwiftUIで、匿名認証を実装したい。ログインしたら、画面も切り替えたい。状態管理も必要。ViewModelでやることになると思う。
メモ書き程度なので参考までに...
こちらが公式
今回は匿名認証を使う:
Auth.auth().signInAnonymously { authResult, error in
// ...
}
ログインを維持するには、このメソッドを使う:
handle = Auth.auth().addStateDidChangeListener { auth, user in
// ...
}
ロジックを作る
authStateChange
のようなものがあるようだ。
import FirebaseAuth
class AuthService {
let auth = Auth.auth()
// Authの非同期版signInAnonymouslyメソッドを追加
func signInAnonymously() async throws -> AuthDataResult {
return try await withCheckedThrowingContinuation { continuation in
// signInAnonymouslyメソッドを呼び出す
auth.signInAnonymously { result, error in
// 結果をハンドリング してcontinuationをresumeする
if let error = error {
// エラーがある場合はエラーを投げる
continuation.resume(throwing: error)
} else if let result = result {
// 結果がある場合は結果を返す
continuation.resume(returning: result)
} else {
// それ以外の場合は不明なエラーを投げる
continuation.resume(throwing: NSError(domain: "AuthError", code: 0, userInfo: [NSLocalizedDescriptionKey: "Unknown error occurred"]))
}
}
}
}
func signOut() async throws {
do {
try await auth.signOut()
} catch {
throw error
}
}
}
状態管理をする。これがないと、非同期処理のエラーも出るようだ。 SwiftUIでは、必ずViewModel使う気がする。 ロジック直接書くとViewModelではないが😅
import SwiftUI
import FirebaseAuth
import OSLog
class GestStartPageViewModel: ObservableObject {
@Published var isAuthenticated = false
let auth = Auth.auth()
// Logger
let logger = Logger()
let authService = AuthService()
// アプリの起動時に認証状態をチェックする
init() {
observeAuthChanges()
}
//
private func observeAuthChanges() {
auth.addStateDidChangeListener { [weak self] _, user in
DispatchQueue.main.async {
self?.isAuthenticated = user != nil
}
}
}
// ゲストログインをするメソッドを呼び出す
func gestUserLogin() async {
do {
try await authService.signInAnonymously()
} catch {
// Loggerを使用してエラーをログに記録
logger.error("ゲストログインに失敗しました: \(error.localizedDescription)")
}
}
// Sign Out
func gestSignOut() async {
do {
try await authService.signOut()
} catch {
logger.error("sign out error: \(error.localizedDescription)")
}
}
}
ログイン画面はボタン1個でも作れます。匿名認証なので😅
ログインしたいページを指定すれば、認証が通った後に、画面が切り替わります。
import SwiftUI
struct GestStartPage: View {
@ObservedObject var vm: GestStartPageViewModel
@State private var isButtonTapped = false // ログインボタンがタップされたかの状態
@State private var isLoggedIn = false // ログイン状態
var body: some View {
NavigationView {
Color.customPink.edgesIgnoringSafeArea(.all)
.overlay(Group {
VStack {
Button(action: {
Task {
await vm.gestUserLogin()
// ログイン成功時にisLoggedInをtrueに設定
self.isLoggedIn = true
}
}, label: {
Text("利用を始める")
.frame(width: 278, height: 85)
})
.padding()
.accentColor(Color.white)
.background(Color.customPurple)
// ログイン成功時の画面遷移
.fullScreenCover(isPresented: $isLoggedIn) {
// ここに遷移先のビューを指定
ContentView()
}
})
}
}
}
// GestStartPageのプレビュー
struct GestStartPage_Previews: PreviewProvider {
static var previews: some View {
let viewModel = GestStartPageViewModel()
GestStartPage(vm: viewModel)
}
}
ログインページはこんな感じでいいかな...
import SwiftUI
import Firebase
struct ContentView: View {
let vm = GestStartPageViewModel()
var body: some View {
VStack {
Button {
Task {
await vm.gestSignOut()
}
} label: {
Text("sign out")
}
}
}
#Preview {
ContentView()
}
アプリのエントリーポイントでログインしているかいないかの、分岐処理を追加したら、それぽい動きをします。ローディングとかも入れたら良いのかも?
import SwiftUI
import FirebaseCore
class AppDelegate: NSObject, UIApplicationDelegate {
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
FirebaseApp.configure()
return true
}
}
@main
struct SmapleApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var delegate
@StateObject var vm = GestStartPageViewModel()
var body: some Scene {
WindowGroup {
if vm.isAuthenticated {
ContentView()
} else {
GestStartPage(vm: vm)
}
}
}
}
Discussion