Firestoreの公式を見ながらSwiftUIに機能を追加してみた
🤔やってみたいこと
Flutterでは普段はモデルクラスを作って、Firestoreに.add
と.set
しているがSwfitだとうまくいかない💦
公式には、Swiftのライフサイクルと配列の操作まで必要なことは書いてなかったりする。
今回はドキュメントのコードを使いながらデータを追加できるのを試してみようと思う。setData
とaddDocument
で、structをそのまま引数で渡しても使えなかったでどうすればいいのか記事にすることにした。他にも方法はあるみたいですが...
🚀やってみたこと
まずは公式のコードを参考にロジックを考えてみた。Cityという構造体を作ってみる。サンプルのままだとプロパティが多いので、nameだけにした。
enumのCodingKeyは内部実装を見るとこのように書かれておりました。
A type that can be used as a key for encoding and decoding.
エンコードおよびデコードのキーとして使用できるタイプ。
setData
の場合だとこのままでも渡せる。
public struct City: Codable {
let name: String
enum CodingKeys: String, CodingKey {
case name
}
}
こちらが一度だけデータを書き込みして、2回目からは上書きするメソッド。プロフィールとか特定の情報を作るときに使われるのではないでしょうか💁♀️
// use case of setData
func setCity(city: City) async {
do {
try db.collection("cities").document("LA").setData(from: city)
} catch let error {
print("Error writing city to Firestore: \(error)")
}
}
何度でも書き込むことができて、ランダムなIDが生成されるaddDocument
はTodoアプリやChatアプリで使われると思います。この場合は、特定のデータを編集・削除するには、View内で、DocumentIDを取得する必要があります。
structを加工しないと引数として渡せなかったので、Firestore.Encoder().encode(city)
を使う必要がありました。
// use case of addDocument
func addCity(city: City) async {
// Add a new document with a generated id.
do {
let cityData = try Firestore.Encoder().encode(city)
let ref = try await db.collection("cities").addDocument(data: cityData)
print("Document added with ID: \(ref.documentID)")
} catch {
print("Error adding document: \(error)")
}
}
[メソッドを書いた全体のコード]
import FirebaseFirestore
// City Class
class CityRepository : ObservableObject {
// instance of Firestore
let db = Firestore.firestore()
// use case of setData
func setCity(city: City) async {
do {
try db.collection("cities").document("LA").setData(from: city)
} catch let error {
print("Error writing city to Firestore: \(error)")
}
}
// use case of addDocument
func addCity(city: City) async {
// Add a new document with a generated id.
do {
let cityData = try Firestore.Encoder().encode(city)
let ref = try await db.collection("cities").addDocument(data: cityData)
print("Document added with ID: \(ref.documentID)")
} catch {
print("Error adding document: \(error)")
}
}
}
[View側のコード]
入力をして、1度だけ追加か何個でも追加できるボタンを配置してます。これで保存するとデータの保存のされ方が異なると思います。
import SwiftUI
struct CityView: View {
// input value
@State private var name: String = ""
// UserRepository instance
@ObservedObject private var cityRepository = CityRepository()
var body: some View {
VStack {
TextField("City Name", text: $name)
.textFieldStyle(.roundedBorder)
.padding()
Button(action: {
Task {
let city = City(name: name.uppercased())
await cityRepository.setCity(city: city)
}
}) {
Text("setCity")
}
.foregroundColor(.white)
.buttonStyle(.borderedProminent)
.tint(.red)
.padding()
Button(action: {
Task {
let city = City(name: name.uppercased())
await cityRepository.addCity(city: city)
}
}) {
Text("addCity")
}
.foregroundColor(.white)
.buttonStyle(.borderedProminent)
.tint(.blue)
.padding()
}
}
}
#Preview {
CityView()
}
試しに使ってみるとこんな感じになります。setData
だけ変ですが笑
🙂最後に
公式を見ながらやってるのですが、やはりチュートリアルの動画や技術記事を見ないと、足りないコードもあるのでわからなかったですね😇
SwiftUIだと、UIKitより簡単に書けるけど、配列を操作したりページが呼ばれたらロジックを実行するライフサイクルを理解しないといけない場面があるので簡単ではないですね。
足りなかったコードだとこれかな。そのとのきによってパターンは異なりますね。
- Task {}
- CodingKey
- Firestore.Encoder().encode(city)
Discussion