はじめに
SwiftUIでFirebaseからデータを読み取り、これまで作成したカード内容を更新することを目的とする。
前回までの記事は以下を参考ください。
参考記事
【SwiftUi】TabViewとListの実装とViewのフォルダ管理
開発環境
OSX 10.15.7 (Catalina)
Xcode 12.2.0
CocoaPods 1.10.0
Firestoreへのドキュメントの追加
FirebaseのFirestoreデータベースは以下のように設定しました。
今回は書き込みの機能は設定していないため、直接入力しています。
今後はユーザーが投稿できるよう機能を実装します。
mapImageとuserImageはFireBaseのStorageに保存してある画像のURLを入力しています。
それぞれのファイルをStorageにアップロードします。
必要なURLは"ファイルの場所"にある"アクセストークン"をクリックし、内容をコピーし、Firestoreのそれぞれのフィールドに貼り付けます。
NoSQLの知識が乏しいため、データの構造化がうまくありません。この点は今後学習とともに、現在のデータ構造と変更点が出てくると思います。
現状はFirebaseの使用方法と、SwiftUIの実装方法に焦点を当てます。
参考資料
データベースの構造化
脱RDB脳!Firebase Databse導入のために考えた4つのポイント
Firestoreからのデータの読み取り
datatype
structの実装
Firebaseのデータベースと読み取りと書き出しを行うため、struct
を実装します。
ここで実装したStruct
はgetData
クラスにおいて呼び出されます。
記載内容については読み取り時に、投稿日時を元に並び替えを行う場合はcreatedDate
のみで大丈夫です。
今回は過去の俳人の俳句を投稿しているため、並び替えのためにnow
を定義しました。
struct dataType : Identifiable {
var id : String
var user : String
var haiku : String
var place : String
var likes : String
var mapImage : String
var userImage : String
var stamp : Date
var createdDate : String
var now :Date
}
##getData
クラスの実装
Firebaseデータベースからドキュメントを読み取るための部分です。
特にエラーが出て、実装に時間がかかった部分はTimestampのデータの取り扱いです。
Timestamp firebase Cannot convert value of type 'Timestamp' to expected argument type 'String'
Timestampのデータの変換時にエラーが出てしまい、適切にデータベースからの読み取りができませんでした。
実装にあたっては以下のサイトを参考にしました。
参考資料
Convert timestamp from Firebase to readable date
Firebase FirestoreのTimestamp型のdateへの変換
class getData: ObservableObject {
@Published var datas = [dataType]()
init() {
let db = Firestore.firestore()
db.collection("ikku").order(by: "now", descending: true).addSnapshotListener{ (snap, err) in
if err != nil{
print((err?.localizedDescription)!)
return
}
for i in snap!.documentChanges{
if i.type == .added{
let id = i.document.documentID
let user = i.document.get("userName") as! String
let haiku = i.document.get("haiku") as! String
let place = i.document.get("place") as! String
let likes = i.document.get("likes") as! String
let mapImage = i.document.get("mapImage") as! String
let userImage = i.document.get("userImage") as! String
let now = i.document.get("now") as! Timestamp
let stamp = i.document.get("createdDate") as! Timestamp
let formatterDate = DateFormatter()
formatterDate.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ"
let createdDate = formatterDate.string(from: stamp.dateValue())
DispatchQueue.main.async {
self.datas.append(dataType(id: id, user: user, haiku: haiku, place: place, likes: likes, mapImage: mapImage, userImage: userImage, stamp: stamp.dateValue(), createdDate: createdDate, now: now.dateValue() ))
}
}
}
}
}
}
##ContentRowViewへの実装とデータベースからの情報更新
Firebaseからデータを読み取りにあたって、以下の点を変更しました。
変更前の実装については以下を参考ください。
【SwiftUi】経過時間の表示とListViewのセルデザイン
変更点
1.ContentRowViewにおけるSDWebImageSwiftUIのインポートとvarで定義した変数を空にする。
import SwiftUI
import Firebase
import SDWebImageSwiftUI
struct ContentRowView: View {
var user = ""
var userImage = ""
var haiku = ""
var mapImage = ""
var place = ""
var likes = ""
// ○秒、○日、○年前を表示
var createdDate = ""
static let formatter = RelativeDateTimeFormatter()
var body: some View {
ContentRowView.formatter.locale = Locale(identifier: "ja_JP")
let fmt = ISO8601DateFormatter()
let date1 = fmt.date(from: createdDate)!
let components = Calendar.current.dateComponents(
[.day, .year, .month, .minute, .second],
from: Date(),
to: date1
)
let timeAgo = ContentRowView.formatter.localizedString(from: components)
return VStack {
VStack {
AnimatedImage(url: URL(string: mapImage)!)
.resizable()
//.aspectRatio(contentMode: .fit)
.cornerRadius(12.0, antialiased: /*@START_MENU_TOKEN@*/true/*@END_MENU_TOKEN@*/)
HStack {
Spacer()
Text(place).font(.caption).foregroundColor(.gray)
.padding(.bottom, 5)
}
}
Text(haiku).font(.title3).fontWeight(.bold)
HStack {
Image(systemName: "heart").font(.headline).foregroundColor(Color("pinkColor"))
Text(likes).font(.headline).foregroundColor(Color("pinkColor"))
Spacer()
}.padding(.top, 5)
HStack {
AnimatedImage(url: URL(string: userImage)!)
.resizable()
.frame(width: 30, height: 30)
.clipShape(Circle())
HStack {
Text(user).font(.headline).fontWeight(.light)
+ Text("・").font(.headline).fontWeight(.light)
+ Text(timeAgo).font(.headline).fontWeight(.light)
}
Spacer()
HStack {
Image(systemName: "text.bubble").font(.title).foregroundColor(.gray)
Image(systemName: "heart.fill").font(.title).foregroundColor(Color("pinkColor"))
}
}
Spacer()
}.padding(.top, 8).frame(height: 391)
}
}
2.SDWebImageSwiftUIのインポート
→URLを画像に変換するためのフレームワークを追加
AnimatedImage(url: URL(string: userImage)!)
.resizable()
.frame(width: 30, height: 30)
.clipShape(Circle())
3.HomeViewにおけるObservedObjectの実装
→Firebaseのデータベースからドキュメントを受け取り、ListのそれぞれのRowの情報を更新する。
struct Home: View {
@ObservedObject var observedData = getData()
var body: some View {
List(observedData.datas) { i in
ContentRowView(user: i.user, userImage: i.userImage, haiku: i.haiku, mapImage: i.mapImage, place: i.place, likes: i.likes, createdDate: i.createdDate)
}.environment(\.defaultMinListRowHeight, 391)
}
}
今後実装予定のもの
1.カード部分のLike機能とComment機能の実装
2.mapTabやその他のTab機能の実装
3.Post機能の実装
4.ユーザー登録機能の実装
5.俳人の俳句の登録
以上です。