🛋️

SwiftUIでFirestoreを使ってみた

2023/11/12に公開

Overview

iOSにFirebaseのSDKを追加して、Cloud Firestoreを使ってみるのをやってみた。
https://firebase.google.com/docs/ios/setup?hl=ja

GUIでこれを追加する

https://github.com/firebase/firebase-ios-sdk

追加するパッケージを選ぶときに、Swiftとついたものにチエックをつける

今回は、SwiftUIを使うので、こちらの公式ドキュメントを参考に必要な機能を追加して、保存するだけの機能を作ってみようと思います。
https://firebase.google.com/docs/firestore/quickstart?hl=ja

// Add a new document with a generated ID
var ref: DocumentReference? = nil
ref = db.collection("users").addDocument(data: [
  "first": "Ada",
  "last": "Lovelace",
  "born": 1815
]) { err in
  if let err = err {
    print("Error adding document: \(err)")
  } else {
    print("Document added with ID: \(ref!.documentID)")
  }
}

summary

作業手順は動画にしてみました
https://youtu.be/Kh8kkoZY8oA

そのまま作るといい感じのコードではなかったので、MVVMぽくしてみました。なってるか疑問ですが😅

🔧Firebaseと接続する設定をする

@mainのアノテーションがついているファイルに、Firebaseと接続する設定を追加する。

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 FirebaseTutorialApp: App {
    @UIApplicationDelegateAdaptor(AppDelegate.self) var delegate
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

📁Modelディレクトリを作成する

Userというstructを作成する。これは他の言語でいうところのモデルクラスですね。

import FirebaseFirestore

struct User {
    var id: String
    var name: String
    var createdAt: Timestamp
}

📁ViewModelディレクトリを作成する

ViewとModelのデータのやりとりをするViewModelを作成する。

import FirebaseFirestore

class UserViewModel: ObservableObject {
    private var db = Firestore.firestore()

    func saveUser(name: String, completion: @escaping (Error?) -> Void) {
        let docRef = db.collection("users").document()
        
        let user = User(id: docRef.documentID, name: name, createdAt: Timestamp())
        
        docRef.setData([
            "id": user.id,
            "name": user.name,
            "createdAt": user.createdAt
        ]) { error in
            completion(error)
        }
    }
}

📁Viewディレクトリを作成する

入力フォームとして使うUIを作成する。

import SwiftUI
import FirebaseFirestore

struct ContentView: View {
    @State private var name: String = ""
    @ObservedObject private var viewModel = UserViewModel()

    var body: some View {
        VStack {
            TextField("Name", text: $name)
                .textFieldStyle(RoundedBorderTextFieldStyle())
                .padding()

            Button("Save") {
                viewModel.saveUser(name: name) { error in
                    if let error = error {
                        print("Error: \(error.localizedDescription)")
                    } else {
                        print("User saved successfully.")
                    }
                }
            }
        }
    }
}

データを保存してみよう!

このように追加されていたら、OK!

thoughts

今回は、SwiftUIでFirestoreを使ってみました!
データを追加するだけでしたが、これがUIkitだとすごくむずかしい😅
私は、昔やってみたときできなかったですね🤪

取得したデータを表示できるコードも書いてみました。こんな感じです。

Userモデルには、Identifiableが必要なので追加!

import FirebaseFirestore

// リスト内の各アイテムを一意に識別できるように、Identifiableに準拠させる
struct User: Identifiable {
    var id: String
    var name: String
    var createdAt: Timestamp
}

ViewModelにFirestoreからデータを取得するメソッドを追加

import FirebaseFirestore

class UserViewModel: ObservableObject {
    private var db = Firestore.firestore()
    // struct Userをデータ型に使う
    @Published var users: [User] = []

    // データの追加をするメソッド
    func saveUser(name: String, completion: @escaping (Error?) -> Void) {
        let docRef = db.collection("users").document()
        
        let user = User(id: docRef.documentID, name: name, createdAt: Timestamp())
        
        docRef.setData([
            "id": user.id,
            "name": user.name,
            "createdAt": user.createdAt
        ]) { error in
            completion(error)
        }
    }
    
    // データを取得して表示するメソッド
    func fetchUsers() {
            db.collection("users").getDocuments { snapshot, error in
                if let error = error {
                    print("Error getting documents: \(error)")
                } else {
                    self.users = snapshot?.documents.map {
                        User(id: $0.documentID,
                             name: $0.data()["name"] as? String ?? "",
                             createdAt: $0.data()["createdAt"] as? Timestamp ?? Timestamp())
                    } ?? []
                }
            }
        }
}

Firestoreから取得したデータを表示するメソッドを追加する

import SwiftUI
import FirebaseFirestore

struct ContentView: View {
    @State private var name: String = ""
    @ObservedObject private var viewModel = UserViewModel()
    
    var body: some View {
        VStack {
            TextField("Name", text: $name)
                .textFieldStyle(RoundedBorderTextFieldStyle())
                .padding()
            
            Button("Save") {
                viewModel.saveUser(name: name) { error in
                    if let error = error {
                        print("Error: \(error.localizedDescription)")
                    } else {
                        print("User saved successfully.")
                        viewModel.fetchUsers()
                    }
                }
            }
            
            List(viewModel.users) { user in
                VStack(alignment: .leading) {
                    Text(user.name)
                    Text("ID: \(user.id)")
                    // 他のユーザー情報の表示
                }
            }
        }
        .onAppear {
            viewModel.fetchUsers()
        }
    }
}

データの取得に成功していればこのようなUIが表示されるはずです。

Discussion