質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
Firebase

Firebaseは、Googleが提供するBasSサービスの一つ。リアルタイム通知可能、並びにアクセス制御ができるオブジェクトデータベース機能を備えます。さらに認証機能、アプリケーションのログ解析機能などの利用も可能です。

Cloud Firestore

Cloud Firestore は、自動スケーリングと高性能を実現し、アプリケーション開発を簡素化するように構築された NoSQLドキュメントデータベースです。

非同期処理

非同期処理とは一部のコードを別々のスレッドで実行させる手法です。アプリケーションのパフォーマンスを向上させる目的でこの手法を用います。

Xcode

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

Q&A

0回答

633閲覧

Firestore のドキュメント受信処理が成功してからTableViewに行を追加したい

koshira27

総合スコア8

Firebase

Firebaseは、Googleが提供するBasSサービスの一つ。リアルタイム通知可能、並びにアクセス制御ができるオブジェクトデータベース機能を備えます。さらに認証機能、アプリケーションのログ解析機能などの利用も可能です。

Cloud Firestore

Cloud Firestore は、自動スケーリングと高性能を実現し、アプリケーション開発を簡素化するように構築された NoSQLドキュメントデータベースです。

非同期処理

非同期処理とは一部のコードを別々のスレッドで実行させる手法です。アプリケーションのパフォーマンスを向上させる目的でこの手法を用います。

Xcode

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

0グッド

0クリップ

投稿2021/06/25 15:18

編集2021/06/26 15:51

環境

言語:Swift 5.4
開発環境;xcode 12.5
クラウドデータベース:Cloud Firestore

やりたいこと

Swift×FirestoreでToDoリストアプリを作っています。

最終的には、マルチデバイスでどのデバイスとも同期できるようにしたいと思い、
Firestoreと連携するTableViewのアプリを作成しています。

困っていること

以前下記の質問をした際に、

Firestoreと連携するクラスをインスタンス化しようとすると「 Missing argument for parameter 'document' in call」のエラーが出る

回答を頂いた方からのコメントに、「テーブルに行を追加するのは、Firestore の処理が成功してからの方が良い」と頂き、確かにコンソールのログを見ると、非同期のfunc tableView(_:numberOfRowsInSection:)がテーブル行問合せを何度もしているので、下記の順番で処理をさせたいと考えています。

■初回起動時
firebaseのデータ取得処理

func tableView(:numberOfRowsInSection:)

func tableView(
:cellForRowAt:)

■初回起動後
func tableView(:numberOfRowsInSection:)と
func tableView(
:cellForRowAt:)は一般的なtableViewでの使われ方

ネット等で調べて、順番に処理をさせるには、クロージャの@escapingの書き方が使えるとわかりましたが、下記の点で行き詰まっていますので対処法を教えてもらえないでしょうか。

①func tableView(:numberOfRowsInSection:)とfunc tableView(:cellForRowAt:)でクロージャの@escapingの書き方がわからない。tableViewの引数がいろいろつきすぎてて書き方が不明。(また、そもそもfunc tableView(:numberOfRowsInSection:)とfunc tableView(:cellForRowAt:)でクロージャの@escapingをする方法が適切なのか)

②初回起動時にクロージャの@escapingで順番に処理することができても、初回起動後にfunc tableView(:numberOfRowsInSection:)とfunc tableView(:cellForRowAt:)はこれまで通りの処理になるようにしたい場合どうしたら良いのか。

import UIKit import Firebase //Firebase仕様 import FirebaseFirestoreSwift //Firebase仕様 var todoList = [MyTodo]() //MyTodo配列クラスをインスタンス化してtodoListプロパティ配列生成 //************************************************* // ViewControllerクラス //************************************************* class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate { let db = Firestore.firestore() //************************************************* //UITableViewのoutlet定義(行を追加した時の通知等で、UITableViewに対して操作を行うために必要となる) //************************************************* @IBOutlet weak var tableView: UITableView! //------------------------------------------------- //初回ロード処理 //------------------------------------------------- override func viewDidLoad() { super.viewDidLoad() print("ログ_viewdidload:start、時間:(logTime.formatSimple())") //データを取得 readFirestoreDocuments(completion: { // rd.readFirestoreDocuments(MyTodo().todoId, completion: { //tableViewを更新 self.tableView.reloadData() }) } //------------------------------------------------- //Firestoreからのデータ受信処理 //------------------------------------------------- func readFirestoreDocuments(completion: @escaping ()->()) { print("ログ_readFirestoreDocuments:プロシージャ@escaping開始、時間:(logTime.formatSimple())") //++++++++++++++++++++++++++++++++++++++++++++ //Firestoreからドキュメントのデータを取得して、todoListのデータ型に変換し配列の各要素に入れる //++++++++++++++++++++++++++++++++++++++++++++ //Firestoreからドキュメントのデータ取得。querySnapshotにドキュメントデータが配列になって入っている print("ログ_readFirestoreDocuments:Getting documents is start、時間:(logTime.formatSimple())") db.collection("todo").getDocuments() { (querySnapshot, err) in //Firestoreからドキュメントが取得できているか、取得したデータの内容の確認ログ if let err = err { print("ログ_readFirestoreDocuments:Error getting documents(err)、時間:(logTime.formatSimple())") } else { //データ取得の成功、データの中身のログ出力 print("ログ_readFirestoreDocuments:Getting documents is success!、時間:(logTime.formatSimple())") for document in querySnapshot!.documents{ print("ログ_readFirestoreDocuments:documentの中身を確認(document.data())、時間:(logTime.formatSimple())") } } print("ログ_readFirestoreDocuments:todoListの中身を確認(MyTodoクラスの型への変換前):(todoList)、時間:(logTime.formatSimple())") todoList = querySnapshot!.documents.map { //querySnapshotの各ドキュメントデータから要素を一つ一つ取り出して、MyTodoのデータ型に変換してtodoList配列の全ての要素に入れる(ドキュメントのデータをMyTodoのデータ型に変換したデータはdataとしてreturnで配列に戻し、各ドキュメントで繰り返す) // document in let data = MyTodo(document: document) document in let data = MyTodo(document: document) return data } print("ログ_readFirestoreDocuments:todoListの行数確認(MyTodoクラスの型への変換後):(todoList.count)、時間:(logTime.formatSimple())") //読み出し完了時のクロージャを呼び出す completion() } } //************************************************* // +ボタンをタップした時の処理(タスク入力オブジェクトの作成・処理) //************************************************* @IBAction func tapAddButton(_ sender: Any) { // タスク入力オブジェクトとなるアラートダイアログを生成 let alertController = UIAlertController( title: "ToDo追加", message: "ToDoを入力してください", preferredStyle: UIAlertController.Style.alert ) // テキストエリアを追加 alertController.addTextField(configurationHandler: nil) //************************************************* //OKボタンの配置、ボタン押下時の処理 //************************************************* // OKボタンを追加 let okAction = UIAlertAction( title: "OK", style: UIAlertAction.Style.default ){ (action: UIAlertAction) in // OKボタンがタップされたときの処理(ToDoの配列に入力値を挿入。テーブルの先頭行に挿入する) if let textField = alertController.textFields?.first { let myTodo = MyTodo()//MyTodoクラスをインスタンス化してmyTodo定数を生成 myTodo.todoTitle = textField.text! todoList.insert(myTodo, at: 0) print("ログ_func tapAddButton:新たなToDoとして入力された言葉:(myTodo.todoTitle!)") // テーブルに行を追加 self.tableView.insertRows( at: [IndexPath(row: 0, section: 0)], with: UITableView.RowAnimation.right ) let ref = self.db.collection("todo").document() print("ログ_func tapAddButton:作られたドキュメントのID:(ref.documentID)") myTodo.todoId = ref.documentID ref.setData([ "todoId": ref.documentID, "todoTitle": myTodo.todoTitle!, "todoDone": false ]) { err in if let err = err { print("ログ_func tapAddButton:Error adding document: (err)") } else { print("ログ_func tapAddButton:Document added with ID:(ref.documentID)") } } } } // OKボタンがタップされたときの処理 alertController.addAction(okAction) //************************************************* //キャンセルボタンの配置、ボタン押下時の処理 //************************************************* // CANCELボタンがタップされたときの処理 let cancelButton = UIAlertAction( title: "CANCEL", style: UIAlertAction.Style.cancel, handler: nil ) // CANCELボタンを追加 alertController.addAction(cancelButton) // アラートダイアログを表示 present(alertController, animated: true, completion: nil) } //************************************************* // テーブルの行数返却 //************************************************* // テーブルの行数を返却する func tableView( _ tableView: UITableView, numberOfRowsInSection section: Int ) -> Int { // Todoの配列の長さを返却する print("ログ_tableView_numberOfRowsInSection:配列の長さは(todoList.count)、時間:(logTime.formatSimple())") return todoList.count } //************************************************* // テーブルのセルの表示(テーブルの行ごとにセルを返却) //************************************************* // テーブルの行ごとのセルを返却する func tableView( _ tableView: UITableView, cellForRowAt indexPath: IndexPath ) -> UITableViewCell { // Storyboardで指定したtodoCell識別子を利用して再利用可能なセルを取得する let cell = tableView.dequeueReusableCell( withIdentifier: "todoCell", for: indexPath ) // 行番号に合ったToDoの情報を取得 let myTodo = todoList[indexPath.row] // print("ログ_tableView_cellForRowAt:myTodoの中身は(todoList[indexPath.row])") print("ログ_tableView_cellForRowAt:行番号に合ったToDoは(String(describing: myTodo.todoTitle))、時間:(logTime.formatSimple())") // セルのラベルにToDoのタイトルをセット cell.textLabel?.text = myTodo.todoTitle // セルのチェックマーク状態をセット if myTodo.todoDone { // チェックあり cell.accessoryType = UITableViewCell.AccessoryType.checkmark } else { // チェックなし cell.accessoryType = UITableViewCell.AccessoryType.none } return cell } } //************************************************* // MyTodoクラス //************************************************* //class MyTodo: NSObject, NSSecureCoding { class MyTodo: NSObject{ //Firebase仕様(NSCodingプロトコルを削除) //ドキュメントID var todoId: String? // ToDoのタイトル var todoTitle: String? // ToDoを完了したかどうかを表すフラグ var todoDone: Bool = false //取得したデータを当てはめていくための型(MyTodoのデータ型) init(document: QueryDocumentSnapshot) { self.todoId = document.documentID self.todoTitle = document.data()["todoTitle"] as? String self.todoDone = document.data()["todoDone"] as! Bool } override init() { } }

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだ回答がついていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問