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

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

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

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

Cloud Firestore

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

ビルド

ソースコードを単体で実行可能なソフトウェアへ変換する過程をビルド(build)と呼びます

Xcode

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

Swift

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

Q&A

解決済

1回答

1813閲覧

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

koshira27

総合スコア8

Firebase

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

Cloud Firestore

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

ビルド

ソースコードを単体で実行可能なソフトウェアへ変換する過程をビルド(build)と呼びます

Xcode

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

Swift

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

0グッド

0クリップ

投稿2021/05/31 17:11

環境

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

やりたいこと

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

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

【Firebase×Swift】データ取得とTableView表示(後編)〜取得データの変換とセル表示〜

ToDoのクラスとしてMyTodoクラスを作り、MyTodoクラスからインスタンス(myTodo)を作り、インスタンスの配列を処理することでToDoの追加・削除等の機能を作っていきたいと考えています。

困っていること

MyTodoクラスを元に、タスクを追加するためのインスタンスmyTodoを作成しようとすると、xcodeで

let myTodo = MyTodo()

の箇所で下記のエラーが出てビルドできないです。

Missing argument for parameter 'document' in call

MyTodo() をMyTodo(document: QueryDocumentSnapshot)としたりしましたがエラーは消えず、ハマってしまいこれ以上進めない状況です。対処法等、分かりましたら教えていただけると助かります。
なお、CloudFirestoreとクライアントでデータの送受信ができるところまでは確認できています。

MyTodoクラスの初期化(init)のところがいけないのでしょうか。

コード

swift

1import UIKit 2import Firebase 3import FirebaseFirestoreSwift 4 5 6class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate { 7 8 //************************************************* 9 //ToDoのテキストを格納する配列の定義 10 //************************************************* 11 var todoList = [MyTodo]() 12 let db = Firestore.firestore() 13 14 //************************************************* 15 //UITableViewのoutlet定義 16 //************************************************* 17 @IBOutlet weak var tableView: UITableView! 18 19 //************************************************* 20 //画面の初回ロード時の処理 21 //************************************************* 22 override func viewDidLoad() { 23 super.viewDidLoad() 24 print("ログ_viewdidload:start") 25 26 27 //++++++++++++++++++++++++++++++++++++++++++++ 28 //Firestoreからドキュメントのデータを取得して、todoListのデータ型に変換し配列の各要素に入れる 29 //++++++++++++++++++++++++++++++++++++++++++++ 30 //Firestoreからドキュメントのデータ取得。 31 print("ログ_viewdidload:Getting documents is start") 32 db.collection("todo").getDocuments() { (querySnapshot, err) in 33 if let err = err { 34 print("ログ_viewdidload:Error getting documents(err)") 35 } else { 36 print("ログ_viewdidload:Getting documents is success!") 37 for document in querySnapshot!.documents{ 38 print("ログ_viewdidload:documentの中身を確認(document.data())") 39 } 40 } 41 42 self.todoList = querySnapshot!.documents.map { 43 document in let data = MyTodo(document: document) 44 return data 45 } 46 } 47 } 48 49 //************************************************* 50 // +ボタンをタップした時の処理(タスク入力オブジェクトの作成・処理) 51 //************************************************* 52 53 @IBAction func tapAddButton(_ sender: Any) { 54 55 // タスク入力オブジェクトとなるアラートダイアログを生成 56 let alertController = UIAlertController( 57 title: "ToDo追加", 58 message: "ToDoを入力してください", 59 preferredStyle: UIAlertController.Style.alert 60 ) 61 62 // テキストエリアを追加 63 alertController.addTextField(configurationHandler: nil) 64 65 //************************************************* 66 //OKボタンの配置、ボタン押下時の処理 67 //************************************************* 68 // OKボタンを追加 69 let okAction = UIAlertAction( 70 title: "OK", 71 style: UIAlertAction.Style.default 72 ){ 73 (action: UIAlertAction) in 74 // OKボタンがタップされたときの処理(ToDoの配列に入力値を挿入。テーブルの先頭行に挿入する) 75 if let textField = alertController.textFields?.first { 76 77 let myTodo = MyTodo() //<--ココ! MyTodoクラスをインスタンス化してmyTodo定数を生成したい 78 myTodo.todoTitle = textField.text! 79 self.todoList.insert(myTodo, at: 0) 80 81 // テーブルに行を追加 82 self.tableView.insertRows( 83 at: [IndexPath(row: 0, section: 0)], 84 with: UITableView.RowAnimation.right 85 ) 86 87 //追加したToDoについて、Firestoreのtodoコレクションに新規のtodoドキュメントを追加 88 let ref: DocumentReference? = nil 89 self.db.collection("todo").addDocument(data: [ 90 "todoTitle": myTodo.todoTitle!, 91 "todoDone": false 92 ]) { err in 93 if let err = err { 94 print("Error adding document: (err)") 95 } else { 96 print("Document added with ID: (ref!.documentID)") 97 } 98 } 99 } 100 } 101 102 // OKボタンがタップされたときの処理 103 alertController.addAction(okAction) 104 105 //************************************************* 106 //キャンセルボタンの配置、ボタン押下時の処理 107 //************************************************* 108 // CANCELボタンがタップされたときの処理 109 let cancelButton = UIAlertAction( 110 title: "CANCEL", 111 style: UIAlertAction.Style.cancel, 112 handler: nil 113 ) 114 115 // CANCELボタンを追加 116 alertController.addAction(cancelButton) 117 // アラートダイアログを表示 118 present(alertController, animated: true, completion: nil) 119 } 120 121 122 //************************************************* 123 // テーブルの行数返却 124 //************************************************* 125 // テーブルの行数を返却する 126 127 128 func tableView( 129 _ tableView: UITableView, 130 numberOfRowsInSection section: Int 131 ) -> Int { 132 // Todoの配列の長さを返却する 133 print("ログ_tableView_numberOfRowsInSection:配列の長さは(todoList.count)") 134 135 return todoList.count 136 } 137 138 //************************************************* 139 // テーブルのセルの表示(テーブルの行ごとにセルを返却) 140 //************************************************* 141 // テーブルの行ごとのセルを返却する 142 func tableView( 143 _ tableView: UITableView, 144 cellForRowAt indexPath: IndexPath 145 ) -> UITableViewCell { 146 // Storyboardで指定したtodoCell識別子を利用して再利用可能なセルを取得する 147 let cell = tableView.dequeueReusableCell( 148 withIdentifier: "todoCell", 149 for: indexPath 150 ) 151 // 行番号に合ったToDoの情報を取得 152 let myTodo = todoList[indexPath.row] 153 print("ログ_tableView_cellForRowAt:myTodoの中身は(todoList[indexPath.row])") 154 print("ログ_tableView_cellForRowAt:行番号に合ったToDoは(String(describing: myTodo.todoTitle))") 155 156 // セルのラベルにToDoのタイトルをセット 157 cell.textLabel?.text = myTodo.todoTitle 158 159 160 // セルのチェックマーク状態をセット 161 if myTodo.todoDone { 162 // チェックあり 163 cell.accessoryType = UITableViewCell.AccessoryType.checkmark 164 } else { 165 // チェックなし 166 cell.accessoryType = UITableViewCell.AccessoryType.none 167 } 168 169 return cell 170 } 171 172 173 174 //************************************************* 175 // セルをタップした時の処理(チェックを入れたり外したり) 176 //************************************************* 177 // セルをタップしたときの処理 178 func tableView( 179 _ tableView: UITableView, 180 didSelectRowAt indexPath: IndexPath 181 ) { 182 let myTodo = todoList[indexPath.row] 183 184 if myTodo.todoDone { 185 // 完了済みの場合は未完了に変更 186 myTodo.todoDone = false 187 } else { 188 // 未完の場合は完了済みに変更 189 myTodo.todoDone = true 190 } 191 192 // セルの状態を変更(特定の行だけリロードする) 193 tableView.reloadRows( 194 at: [indexPath], 195 with: UITableView.RowAnimation.none 196 ) 197 198 let ref: DocumentReference? = nil //Firestoreでドキュメントを指定するため、タップしたドキュメントIDを取得 199 myTodo.todoId = ref!.documentID 200 201 //フラグを変更したToDoについて、Firestoreのtodoコレクションの該当するtodoドキュメントのフラグを変更 202 db.collection("todo").document(myTodo.todoId!).updateData([ 203 "todoDone": myTodo.todoDone, 204 ]) { err in 205 if let err = err { 206 print("Error updating document: (err)") 207 } else { 208 print("Document successfully updated") 209 } 210 } 211 212 } 213} 214//************************************************* 215// MyTodoクラスを作成 216//************************************************* 217 218class MyTodo: NSObject{ 219 var todoId: String? 220 var todoTitle: String? 221 var todoDone: Bool = false 222 223 //取得したデータをFirestoreのデータ型に当てはめていくための型(MyTodoのデータ型) 224 init(document: QueryDocumentSnapshot) { 225 self.todoId = document.documentID 226 self.todoTitle = document.data()["todoTitle"] as? String 227 self.todoDone = document.data()["todoDone"] as! Bool 228 } 229}

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

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

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

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

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

hoshi-takanori

2021/05/31 17:38

MyTodo は引数がない init を持たないのでエラーになります。 init() { // 中身は空で良い } を追加すると動くと思いますが、todoId を持たない MyTodo ができるのは良くない気が…。
koshira27

2021/06/01 13:32

回答ありがとうございます。 init(document: QueryDocumentSnapshot) { self.todoId = document.documentID self.todoTitle = document.data()["todoTitle"] as? String self.todoDone = document.data()["todoDone"] as! Bool } 上記部分を頂いたコメントの通り下記のようにするとビルドされるまではいくようになりました。 init() { // 中身は空で良い } ただし、上記にすると、ビルドはできるがFirestoreからとってきたデータがtableViewに表示されなくなってしまいます。 質問に記載した参照ページによると、init(document: の処理は、Firestoreからとってきたドキュメントをテーブルビューに表示できるデータ型(クラスの型))へ変換する処理とのことです。 実際にinit() {}に変更してコンソールでログを確認して、Firestoreからdocumentを取り込めても、 todoList配列への配置でデータが取り込まれなくなっています。 (反対に、init(document: とするとコンソールのログでデータが取り込まれていることが確認できます) 実は頂いた修正は既に試していました。試したのですが、init() {}だとビルドはできるがtableViewに表示されず、init(document: だとエラーがでてビルドできず。どちらも作りたいものが作れず、私の知識ではにっちもさっちもいかない状況になっているのが今の状況です。 おそらく、init(){}の箇所でデータ型の変換をするのではなく、 別の場所でデータ型の変換ができればビルドが通り、データ変換もできるようになると推測しています。 (ただしその具体的な方法がわからない・・・)。それとも何か根本的に間違っているのか・・・・やっぱり分からず・・・。 その他コメント頂いたtodoIdを持たないMyTodoができる件については、 とりあえず動くものができてからデータモデルを整備していこうと考えています。
hoshi-takanori

2021/06/02 09:19

init(document: QueryDocumentSnapshot) はそのままで、引数なしの init() を追加しましたか?
koshira27

2021/06/02 13:29

追加のコメントありがとうございます。 init(document: QueryDocumentSnapshot) はそのままで、引数なしの init() を追加したところ。ビルドできて追加したデータがtabeViewに表示されるようになりました! 私の中で、クラスにinit(イニシャライザ)は一つしか設定できないと思い込んでいたのですが、複数のイニシャライザを定義できるんですね。複数のイニシャライザを定義することについて少しネットで調べたところ、イニシャライザを定義した後はインスタンス生成時はそれぞれinitメソッドで定義した引数と一致する型と値を指定する必要があるのですね。 4日くらいハマっていたので大分助かりました。ありがとうございます!
koshira27

2021/06/02 13:32

hoshi-takanoriさんをベストアンサーにしたいと思いますので、回答欄に最初のアドバイス等記載いただけると幸いです。
guest

回答1

0

ベストアンサー

一つのクラス (や構造体) には引数の異なる複数のイニシャライザを定義できますので、既存の init(document: QueryDocumentSnapshot) はそのままで、引数なしの init() を追加すると良いでしょう。

この際、let myTodo = MyTodo() として生成したものは todoId や todoTitle の型がオプショナルであるため、初期値が nil になることに注意してください。

diff

1 class MyTodo: NSObject{ 2 var todoId: String? 3 var todoTitle: String? 4 var todoDone: Bool = false 5 6+ init() { 7+ } 8 9 //取得したデータをFirestoreのデータ型に当てはめていくための型(MyTodoのデータ型) 10 init(document: QueryDocumentSnapshot) { 11 self.todoId = document.documentID 12 self.todoTitle = document.data()["todoTitle"] as? String 13 self.todoDone = document.data()["todoDone"] as! Bool 14 } 15 }

ちなみに、新規生成したドキュメントの id を取得する方法はこんな感です。また、テーブルに行を追加するのは、Firestore の処理が成功してからの方が良いような。
参考: swift - How to get document id after creation Firestore ios? - Stack Overflow

diff

1- let ref: DocumentReference? = nil 2- self.db.collection("todo").addDocument(data: [ 3+ let ref = self.db.collection("todo").document() 4+ print(ref.documentID) 5+ ref.setData([ 6 "todoTitle": myTodo.todoTitle!, 7 "todoDone": false 8 ]) { err in // 以下同様

投稿2021/06/02 18:47

編集2021/06/02 18:51
hoshi-takanori

総合スコア7895

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

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

koshira27

2021/06/03 00:43

ありがとうございました! 複数のイニシャライザを定義できることについて勉強になりました。 新規生成したドキュメントの id を取得することについてもありがとうございます。本課題が解決してちょうど修正に取り組み始めたところなのでとても助かります。頂いたコメントとサンプルコードを参考に取り組んでみたいと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問