LoginSignup
3
9

More than 3 years have passed since last update.

【SwiftUI】Firebaseからデータを読み取り、ListViewのRowを更新する

Last updated at Posted at 2020-11-22

はじめに

SwiftUIでFirebaseからデータを読み取り、これまで作成したカード内容を更新することを目的とする。

前回までの記事は以下を参考ください。

参考記事
【SwiftUi】TabViewとListの実装とViewのフォルダ管理

開発環境

OSX 10.15.7 (Catalina)
Xcode 12.2.0
CocoaPods 1.10.0

Firestoreへのドキュメントの追加

FirebaseのFirestoreデータベースは以下のように設定しました。
今回は書き込みの機能は設定していないため、直接入力しています。
今後はユーザーが投稿できるよう機能を実装します。
Haiku_-_Cloud_Firestore_-_Firebase_コンソール.png

mapImageとuserImageはFireBaseのStorageに保存してある画像のURLを入力しています。
それぞれのファイルをStorageにアップロードします。
必要なURLは"ファイルの場所"にある"アクセストークン"をクリックし、内容をコピーし、Firestoreのそれぞれのフィールドに貼り付けます。
Haiku_-_Storage_-_Firebase_コンソール.png

NoSQLの知識が乏しいため、データの構造化がうまくありません。この点は今後学習とともに、現在のデータ構造と変更点が出てくると思います。
現状はFirebaseの使用方法と、SwiftUIの実装方法に焦点を当てます。

参考資料
データベースの構造化
脱RDB脳!Firebase Databse導入のために考えた4つのポイント

Firestoreからのデータの読み取り

datatypestructの実装

Firebaseのデータベースと読み取りと書き出しを行うため、structを実装します。
ここで実装したStructgetDataクラスにおいて呼び出されます。
記載内容については読み取り時に、投稿日時を元に並び替えを行う場合は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で定義した変数を空にする。

ContentRowView.swift
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の情報を更新する。

home.swift
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)
    }
}

iPhone_11_–_14_2.png

今後実装予定のもの

1.カード部分のLike機能とComment機能の実装
2.mapTabやその他のTab機能の実装
3.Post機能の実装
4.ユーザー登録機能の実装
5.俳人の俳句の登録

以上です。

3
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
9