Firebase Realtime Database
を使ったデータ保存・読み込み処理を、Swiftのasync/await
でシンプルに実装する方法を紹介します。
UserProfileの構造
まずはUserProfile
の構造体を定義します。
struct UserProfile: Codable, Sendable {
var nickName: String
var startDate: Double // Unixタイムスタンプ(秒単位)
var follow: [String]
init(nickName: String = "未設定", follow: [String] = [], startDate: Double = Date().timeIntervalSince1970) {
self.nickName = nickName
self.follow = follow
self.startDate = startDate
}
}
説明
-
nickName
:ユーザー名を保存。 -
startDate
:ユーザー開始日をUnixタイムスタンプで保存。 -
follow
:フォローしているユーザーのIDリスト。
Firebaseからプロフィールを非同期に取得する
Firebase Realtime Database
からUserProfile
を取得する関数です。
@MainActor
class ProfilePostModel: ObservableObject {
var profile: UserProfile = UserProfile()
// 非同期にFirebaseからプロフィールをロードする関数
func loadProfileFromDatabase(id: String) async throws -> UserProfile? {
let ref = Database.database().reference()
let userRef = ref.child("users").child(id)
do {
// Firebaseから非同期でデータを取得
let snapshot = try await getValueAsync(ref: userRef)
guard let value = snapshot as? [String: Any] else {
print("データが存在しないか、フォーマットが不正です")
return nil
}
// 取得したデータをUserProfileにデコード
let jsonData = try JSONSerialization.data(with: value)
let profile = try JSONDecoder().decode(UserProfile.self, from: jsonData)
print("プロフィールのロードに成功しました: \(profile)")
return profile
} catch {
print("プロフィールのロードに失敗しました: \(error.localizedDescription)")
throw error
}
}
// Firebaseから値を取得する補助関数
func getValueAsync(ref: DatabaseReference) async throws -> Any? {
try await withCheckedThrowingContinuation { continuation in
ref.observeSingleEvent(of: .value, with: { snapshot in
Task { @MainActor in
continuation.resume(returning: snapshot.value)
}
}, withCancel: { error in
continuation.resume(throwing: error)
})
}
}
}
説明
-
loadProfileFromDatabase
:指定したid
に対応するプロフィールデータを取得し、UserProfile
として返します。 -
getValueAsync
:observeSingleEvent
をasync/await
でラップして、非同期処理に変換しています。
Firebaseにプロフィールを非同期に保存する
次に、UserProfile
をFirebaseに非同期に保存する関数です。
@MainActor
class ProfilePostModel: ObservableObject {
var profile: UserProfile = UserProfile()
// 非同期にFirebaseにプロフィールを保存する関数
func saveProfileToDatabase() async {
let ref = Database.database().reference()
do {
let userRef = ref.child("users").child(profile.nickName)
let profileData = try JSONEncoder().encode(profile)
if let profileDictionary = try JSONSerialization.jsonObject(with: profileData) as? [String: Any] {
try await setValueAsync(ref: userRef, value: profileDictionary)
} else {
print("プロフィールデータの変換に失敗しました")
}
print("プロフィールを保存しました")
} catch {
print("プロフィールの保存に失敗しました: \(error.localizedDescription)")
}
}
// FirebaseのsetValueメソッドをasync/awaitでラップする補助関数
func setValueAsync(ref: DatabaseReference, value: Any) async throws {
try await withCheckedThrowingContinuation { continuation in
ref.setValue(value) { error, _ in
if let error = error {
continuation.resume(throwing: error)
} else {
continuation.resume()
}
}
}
}
}
説明
-
saveProfileToDatabase
:UserProfile
をFirebase Realtime Database
に保存します。 -
setValueAsync
:FirebaseのsetValue
メソッドをasync/await
でラップし、非同期処理を簡潔に実装します。
まとめ
async/await
を使うことで、Firebaseとの非同期通信が非常にシンプルになりました。コールバックベースのコードから脱却でき、可読性が高まり、メンテナンスがしやすくなります。