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

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

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

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

Swift

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

Q&A

解決済

1回答

922閲覧

swift 配列にFirebaseの値格納したい

ysdyk05

総合スコア13

Firebase

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

Swift

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

0グッド

0クリップ

投稿2020/10/21 05:31

CloudFirebaseに入っているStringg型のデータを配列に格納したいです。
getSelectedQuiz関数内で配列に格納してprintで配列に入っていることは確認できたのですが、
viewDidLoad内でgetSelectedQuizを呼び出した後にpurint(quizArray)しても空の状態で表示されてしまいます。
どなたか解決方法をご教授ください

swift

1 2import UIKit 3import FSCalendar 4import CalculateCalendarLogic 5import FirebaseCore 6import FirebaseFirestore 7 8class GameViewController: UIViewController { 9 private var homeViewController: HomeViewController! 10 var quizArray = [String]() 11 var answerArray = [String]() 12 var commentaryArray = [String]() 13 var db: Firestore! 14 @IBOutlet weak var quiz: UITextView! 15 override func viewDidLoad() { 16 super.viewDidLoad() 17 let settings = FirestoreSettings() 18 Firestore.firestore().settings = settings 19 db = Firestore.firestore() 20 getSelectedQuiz() 21 print(quizArray) 22 } 23 24 // 追加した問題を取得 25 func getSelectedQuiz() { 26 var answer = "" 27 var commentary = "" 28 var quiz = "" 29 var events = "" 30 db.collection(HomeViewController.userName) 31 .getDocuments() { [self] (QuerySnapshot, err) in 32 if let err = err { 33 print("Error getting documents: (err)") 34 35 } else { 36 MotherScheduleViewController.countEvent = 0 37 for document in QuerySnapshot!.documents { 38 let sample: Dictionary = document.data() 39 answer = sample["answer"] as! String 40 commentary = sample["commentary"] as! String 41 quiz = sample["quiz"] as! String 42 self.answerArray.append(answer) 43 self.commentaryArray.append(commentary) 44 self.quizArray.append(quiz) 45 print(self.answerArray) 46 print(self.commentaryArray) 47 print(self.quizArray) 48 MotherScheduleViewController.documentID[MotherScheduleViewController.countEvent] = document.documentID 49 MotherScheduleViewController.schedule[MotherScheduleViewController.countEvent] = events 50 MotherScheduleViewController.countEvent+=1 51 } 52 } 53 } 54 } 55} 56

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

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

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

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

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

guest

回答1

0

ベストアンサー

Swift

1 override func viewDidLoad() { 2 super.viewDidLoad() 3 let settings = FirestoreSettings() 4 Firestore.firestore().settings = settings 5 db = Firestore.firestore() 6 getSelectedQuiz() 7 print(quizArray) 8 }

この中で、getSelectedQuiz() を実行しているにもかかわらず、print(quizArray)で表示される値が空である、というのが質問の趣旨かと思います。

getSelectedQuiz() で行っている処理を抽象的に書くと、

// 追加した問題を取得 func getSelectedQuiz() { // (1)db から値を取得する処理はすぐに返答が返らない処理 db.collection(HomeViewController.userName) .getDocuments() { [self] (QuerySnapshot, err) in // (2)このブロックの処理は db から値が取得されたあと、つまり一定時間後に実行される // ここの処理も一定時間後「非同期に」処理される self.answerArray.append(answer) self.commentaryArray.append(commentary) self.quizArray.append(quiz) } // (3)ここ以降は db から値が取得される前に実行される }

という具合に処理されます。

つまり、(1) でクエリを実行しても、すぐに実行が(3)に移るわけではなく、実際には(3)以降が先に処理されて、その後(2)が実行されるという流れになります。つまり、(2)の実行は非同期で行われており、いつ処理が完了するかは未定である、ということです。

なので、print(quizArray)で値を表示させても、思ったように値を取得できないという状態になってしまいます。

解決方法はいくつか考えられますが、一般的なのは db から値を取得したあとに、その値を使った処理を実行させる方法だと思います(全体の構成によっては他の方法も考えられますので、あくまでも一般的な方法の一つです)。

対策ですが、直近の質問だと下記の質問が近いかと思います。

ほかにも、Observer パターンや Delegate パターンなども考えられるので、必要に応じて使い分けていただければと思います。

##追記

Swift

1// String型の空配列を用意する 2var answers: [String] = [] 3// 途中の処理 4for document in QuerySnapshot!.documents { 5 let sample: Dictionary = document.data() 6 // ここで一旦受け取り 7 let answer = sample["answer"] as! String 8 commentary = sample["commentary"] as! String 9 quiz = sample["quiz"] as! String 10 11 // いったん配列に入れる 12 answers.append(answer) 13} 14 15// 最後に配列をまとめてクロージャに渡す 16DispatchQueue.main.async { 17 completion(answers) 18}

非同期で実行されるべきクロージャを for-in 内で何度も呼び出していることが悪さの原因かもしれません(競合が起きている可能性があるため)。
たとえば、一度仮の配列に answer を入れ、for-in の外で一度に処理する、という感じで記述してみてはいかがでしょうか。

投稿2020/10/21 09:21

編集2020/10/21 12:07
TsukubaDepot

総合スコア5086

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

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

ysdyk05

2020/10/21 09:55

ご回答ありがとうございます。なんとか下記のようにした所、空の配列では無くなりました for document in QuerySnapshot!.documents { let sample: Dictionary = document.data() answer = sample["answer"] as! String commentary = sample["commentary"] as! String quiz = sample["quiz"] as! String print(answer) DispatchQueue.main.async { completion(answer) } override func viewDidLoad() { super.viewDidLoad() let settings = FirestoreSettings() Firestore.firestore().settings = settings db = Firestore.firestore() getSelectedQuiz() { answer in self.quizArray.append(answer) print(self.quizArray) } しかし、print(self.quizArray)の出力結果が["2", "2"]となっています。本来であれば["1", "2"]と出力されるはずでしたが、後ろの2が2度呼ばれているようです。 何故でしょうか....何度もすみません、よろしければ教えていただけないでしょうか
TsukubaDepot

2020/10/21 12:08

非同期処理が連続して何度も呼び出されていることが問題かもしれません。 クロージャ内部で行っている追加処理そのものは何度も呼び出す必要がありませんので、内部で追記し、最後に一括してクロージャに渡すようにしてみてはいかがでしょうか。 詳しくは回答本文に追記しましたので、参考にしていただければと思います。
ysdyk05

2020/10/22 01:05

ご回答ありがとうございます。 1 func getSelectedQuiz(completion: @escaping (String) -> () ) { 2 var answer = "" 3 var commentary = "" 4 var quiz = "" 5 var events = "" 6 var answers: [String] = [] 7 db.collection(HomeViewController.userName) 8 .getDocuments() { [self] (QuerySnapshot, err) in 9 if let err = err { 10 print("Error getting documents: (err)") 11 } else { 12 MotherScheduleViewController.countEvent = 0 13 for document in QuerySnapshot!.documents { 14 let sample: Dictionary = document.data() 15 answer = sample["answer"] as! String 16 answers.append(answer) 17 } 18 DispatchQueue.main.async{ 19 completion(answers) 20 } 21 } 22 } 23 } 6行目でvar answers: [String] = []を宣言し、19行目でcompletion(answers)を追加した所、19行目のanswersでCannot convert value of type '[String]' to expected argument type 'String'というエラーが発生しました。6行目をvar answers = String()にするとエラーは無くなるのですが、 getSelectedQuiz() { answers in self.quizArray.append(answers) print(self.quizArray) } print(self.quizArray)の出力結果が["12"]となってしまいます。求める結果は["1","2"]です。 このエラーについて解決方法を教えていただけないでしょうか。何度もすみません。。
TsukubaDepot

2020/10/22 01:17

getSelectedQuiz(completion: @escaping (String) -> () ) と、クロージャに渡すデータの型が String になっていますから、ここを [String] と文字列の配列(Array<String>)に変更しないといけないと思います。 文字列、ではなく文字列の配列として一括処理していますから、全体の整合性を合わせてあげないと、今回のような型エラーが発生してしまいますのでご確認いただけますでしょうか。
ysdyk05

2020/10/22 02:02

ご回答ありがとうございます 引き続きいろいろ試してみた所、 func getSelectedQuiz(completion: @escaping (String) -> () ) { ➡️func getSelectedAnswer(completion: @escaping ([String]) -> () ) { self.quizArray.append(answers) ➡️self.quizArray.append(contentsOf: answers) とした所解決することができました。 何度も親身にご回答していただき本当にありがとうございました。 また機会がありましたらよろしくお願い致します.....笑
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問