🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Firebase

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

TableView

TableView(UITableView)とは、リスト形式で表示するコントロールで、ほとんどのアプリに使用されています。画面を「行」に分けて管理し、一般的には各行をタップした際に詳細画面に移動します。

iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

Xcode

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

Swift

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

Q&A

解決済

4回答

965閲覧

画面遷移に伴うFirebaseのバグ【iOS】

vlb

総合スコア109

Firebase

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

TableView

TableView(UITableView)とは、リスト形式で表示するコントロールで、ほとんどのアプリに使用されています。画面を「行」に分けて管理し、一般的には各行をタップした際に詳細画面に移動します。

iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

Xcode

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

Swift

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

1グッド

0クリップ

投稿2019/10/05 13:33

編集2019/11/17 11:30

やりたいこと

__FirstView__から__SecondView__へ画面遷移した時に,__Firebase__の値を更新し,その値を__Firebase__から読み取って__SecondView__で表示する.

これまでの流れ

  1. 新しいプロジェクトを作成する.
  2. __Firebase__とアプリを接続する.
  3. 写真1のようにオブジェクトを配置する.
  4. コード1コード2を記述する.
  5. 写真3のように__Realtime Database__を構成する.
  6. プロジェクトを実行すると__FirestView__では__print(Array)__が実行されるが,__SecondView__へ画面遷移を行うと,__SecondView__では__print("error")__が実行される.

コード1

ViewController.swift

Swift

1import UIKit 2import Firebase 3 4class ViewController: UIViewController { 5 6 var ref: DatabaseReference! 7 var value: Int = 0 8 9 10 11 override func viewDidLoad() { 12 super.viewDidLoad() 13 14 self.ref = Database.database().reference() 15 self.ref.observeSingleEvent(of: .value) { (DataSnapshot) in 16 guard let Array = DataSnapshot.value as? [String: Int] else { 17 print("error") 18 return 19 } 20 print(Array) //["値": 3] 21 } 22 } 23 24 25 26 @IBAction func increment(_ sender: UIButton) { 27 self.value += 1 28 } 29 30 31 32 override func viewWillDisappear(_ animated: Bool) { 33 super.viewWillDisappear(animated) 34 35 self.ref.child("値").runTransactionBlock { (MutableData) -> TransactionResult in 36 if let value = MutableData.value as? Int { 37 MutableData.value = value + self.value 38 self.value = 0 39 } 40 return TransactionResult.success(withValue: MutableData) 41 } 42 } 43}

コード2

SecondViewController.swift

Swift

1import UIKit 2import Firebase 3 4class SecondViewController: UIViewController { 5 6 var ref: DatabaseReference! 7 8 @IBOutlet weak var label: UILabel! 9 10 11 12 override func viewDidLoad() { 13 super.viewDidLoad() 14 } 15 16 17 18 override func viewDidAppear(_ animated: Bool) { 19 super.viewDidAppear(animated) 20 21 self.ref = Database.database().reference() 22 self.ref.observeSingleEvent(of: .value) { (DataSnapshot) in 23 guard let Array = DataSnapshot.value as? [String: Int] else { 24 print("error") //error 25 self.label.text = "error" 26 return 27 } 28 print(Array) 29 if let value = Array["値"] { 30 self.label.text = String(value) 31 } 32 } 33 } 34}

写真1

Main.storyboard
イメージ説明

写真2

構成
イメージ説明

写真3

Firebase
イメージ説明

写真4

デバイス上
イメージ説明

写真5

実際のアプリに近づけた構成
__detail__で値を増やし,__detail__から__Second__へ画面遷移をした際に__Firebase__への書き込みと書き出しを行う.
その際に問題が発生する.
イメージ説明

環境

xcode : ver11.0
Swift : ver5.1
iOS : ver13.1.2

最後に

お力をお貸しください.宜しくお願い致します.
(分かり難所があれば言ってください)

dja👍を押しています

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

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

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

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

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

guest

回答4

0

自己解決

解決ならずでした.
一旦ここでお開きとします.

投稿2019/10/23 15:00

vlb

総合スコア109

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

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

0

そもそも質問のタイトルに「Firebaseのバグ」と書いているのが
この質問の心証を悪くしていると思います。
「Firebaseのバグ」だというのは、Firebaseが悪いと言っています。
そうじゃなくて、Firebaseを正しく使うことができないあなたのコードのバグでしょ。
Firebaseに責任をなすりつけるなよと思います。

それで、今回の件ですが、SecondViewControllerのviewDidAppearの
タイミングでobserveSingleEventを使ってデータ取得するのではなくて、
SecondViewControllerのviewDidLoadで、observeメソッドを使って
データの更新を常に監視するようあらかじめ仕掛けておけば、
別の場所で実施したトランザクション更新が確定したタイミングで
適宜最新データを取得してラベルを更新できると思います。

swift

1import UIKit 2import Firebase 3 4class SecondViewController: UIViewController { 5 6 var ref: DatabaseReference! 7 8 @IBOutlet weak var label: UILabel! 9 10 override func viewDidLoad() { 11 super.viewDidLoad() 12 self.ref = Database.database().reference() 13 self.ref.observe(.value) { (DataSnapshot) in 14 guard let Array = DataSnapshot.value as? [String: Int] else { 15 print("error") //error 16 return 17 } 18 print(Array) 19 if let value = Array["値"] { 20 self.label.text = String(value) 21 } 22 } 23 } 24}

投稿2019/10/21 17:12

TakeOne

総合スコア6299

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

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

vlb

2019/10/22 11:58

上記のコードですとエラーは起こりませんでしたが,Databaseの内容が変更されるたびにDatabaseからデータを勝手に受信するので,Firebaseとの接続回数が著しく増える気がするのですが,実際はどうなのでしょうか. 実際に,ある端末(Aとおく)でSecondViewを開き,別の端末(Bとおく)で値を増やすと,Aは画面遷移等なくても自動で値が更新されてしまいます.
TakeOne

2019/10/22 15:42

実際にどうなのかは、この値がどのような用途でどれくらいの頻度で更新されるのか知らない人にはわからない話です。それはあなたが調べて考える話です。問題があるなら、viewWillAppearのタイミングで監視を開始してviewWillDisappearで監視を停止させることで画面表示中だけ監視するとか、viewWillAppearのタイミングで監視を開始して更新データを1回受け取ったらそれで監視を停止させるとか、工夫してコントロールすればいい話です。
vlb

2019/10/23 13:26

override func viewDidDisappear(_ animated: Bool) { super.viewDidDisappear(animated) self.ref.keepSynced(true) self.ref.child("値").runTransactionBlock { (MutableData) -> TransactionResult in if let value = MutableData.value as? Int { MutableData.value = value + self.value self.value = 0 } return TransactionResult.success(withValue: MutableData) } self.ref.keepSynced(false) } 上記のように self.ref.keepSynced()で監視を限定的にしましたが,エラーが表示されます
TakeOne

2019/10/23 14:44 編集

keepSyncedは、監視をしていない時もサーバー上の更新データを自動的にダウンロードするオプションですが、それをトランザクション更新の前後で呼び出して一体何の意味があるのかさっぱりわかりません。あなたがやりたいことに最後まで付き合うつもりはないので、新しいことをやって新しいエラーが出て自分で解決できなくて誰かに助けを求めたいのだったら、新しい質問を立てたらどうですか?
vlb

2019/10/27 12:15

ではどのようにしたら画面表示中だけ監視できるのでしょうか
TakeOne

2019/10/27 14:54 編集

「viewWillAppearのタイミングで監視を開始してviewWillDisappearで監視を停止させることで画面表示中だけ監視する」って具体的に書いてるのに、全く読んでないか、意味がわからないんでしょうね。「解決ならず」であきらめたみたいだからどうでもいいですけど。
vlb

2019/10/28 14:03

ですから,「viewWillAppearのタイミングで監視を開始してviewWillDisappearで監視を停止させることで画面表示中だけ監視する」にはどのようにしたらいいのかわからないので質問しているのですが.
guest

0

画面遷移に伴うFirebaseのバグ【iOS】

Bug Report から Google へバグ報告をしてください。

ただ、質問内容からは以下が不明なので、適切に追記して報告すると良いです。
・どの仕様に対してどのようにおかしな挙動をしているか?(該当する Document の箇所を示すのが良いかと)
・再現のための最小限のコードの提示

あと、興味本位ですが、上記が整理できれば、質問に追記をしていただけると知的好奇心が満たされます。

投稿2019/10/06 06:53

退会済みユーザー

退会済みユーザー

総合スコア0

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

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

vlb

2019/10/21 13:33

返信遅くなりました. 質問をもう少しわかりやすくしてみました. 実際に実装してみていただけると幸いです. わかりにくい箇所があれば言ってください.
guest

0

まず、問題は切り分けましょう。
今回の問題点は、「TableViewに1月が表示されない」ということでいいですか?

tableViewfirebaseからの取得を下記のようにprintしてみてはどうですか?

Swift

1self.ref.observeSingleEvent(of: .value) { (DataSnapshot) in 2 let namesArray = DataSnapshot.value as! [String: Int] 3 // 下記を追加 4 print(namesArray) 5 self.rank = namesArray.sorted {$0.value > $1.value} 6 self.list.reloadData() 7}

もし、それでも1月が取得できていないのでしたら、取得の方法(self.ref)がおかしいということで、
再度googleの公式ドキュメントを見てみる必要があると思います。

以下、2019/10/26修正

よくわかりませんが、どうしてもそのようなタイミングがいいということみたいですね。
TabBarをクリックした時にしたいということなので、
TabBarをcustom classでラップして、
その条件の時に値を書き換えるようにする必要があります。

ここを参考に書いて見ました。

タイミングの取得はバッチしできることをサンプルを作成して確認しております。

Swift

1import UIKit 2 3class CustomTabBarController: UITabBarController, UITabBarControllerDelegate { 4 5 override func viewDidLoad() { 6 super.viewDidLoad() 7 self.delegate = self 8 } 9 10 func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool { 11 let naviCon = tabBarController.viewControllers![0] as! UINavigationController 12 let presentView = naviCon.visibleViewController 13 if presentView is ADetailViewController { 14 switch viewController { 15 case is BViewController: 16 print("From ADetail, show B") 17 // ここに値を書き換えるトランザクションを記入し、トランザクションがかえってきたら、tableViewにreloadをかけること 18 return true 19 default: 20 print("From else, show B") 21 return true 22 } 23 } 24 return true 25 } 26}

イメージ説明

投稿2019/10/05 15:35

編集2019/10/26 02:40
hameji

総合スコア1380

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

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

vlb

2019/10/06 02:09

>>まず、問題は切り分けましょう。 すいません.私は問題を1つしかしていないと思うのですが,何と何を分けるのでしょうか. >>今回の問題点は、「TableViewに1月が表示されない」ということでいいですか? 仰る通りです. >>tableViewfirebaseからの取得を下記のようにprintしてみてはどうですか? 「問題点」のところに記載があるように,print()でLogを確認しても1月がありません. >>googleの公式ドキュメントを見てみる必要があると思います。 公式のドキュメントは再度確認しておりますが,今回の問題に対する解決策となりそうな記事がなかったため,teratailに質問している次第です. 質問に対する回答が,「公式ドキュメントを見る必要がある」だとお粗末な気がします. 宜しく御願い致します.
hameji

2019/10/06 03:41 編集

質問内容は分解できると思います。 1. データの更新(インクリメント?)と、2. 取得です。 取得の仕方は間違っていないと思われるのですが、 データの更新はひとまず置いといて、きちんと動くか確認が必要です。 viewcontroller.swiftのviewdidloadで同様に取得、 printを記入して見て、結果を見て見て下さい。 きちんと取得できるのではないかと思います。 ということは、更新作業が悪さをしていると推測します。 なので、質問が「tableviewに1月がきちんと表示されない」だけであれば、 更新コードを削除して下さいでも、回答としては成り立ちます。 気になるのはデータの更新のタイミングです。 ViewControllerのviewWillDisappearとTableViewControllerのviewDidLoadのタイミングが 合ってないのではないかと推測されます。 どちらにもprint(何か)を記入、完了の{}の中にもprintを記入して、 タイミングの確認をして下さい。 transactionとなっているように非同期通信なので、 1月データの更新途中にデータの取得が行われてて、 1月はロックがかかってて取得できず、表示されないのではないですか? 解決策は更新のタイミング変えるか、 完了のタイミングを受け取ってから表示するです。 ちなみに、善意の回答者に対して、「お粗末だと思います。」というのは、 いかがなものかと思いますよ。 下記のcoco_bauerさんも書いてますが、自分の質問の仕方を棚に上げ 上から目線で、目的の答えを得られないから批判だけをするのは お粗末以上に、言葉も出ません。 だから、週間-12, 個人の全体評価もマイナスなんだと思います。 (そんな評価の人ここで他に見たことないです、よっぽどひどい人ということになりますよ)
coco_bauer

2019/10/06 03:03

質問には問題点として、以下の3つを挙げられていると思うのですが、「私は問題を1つしかしていない」というのであれば、どれが本当の質問ですか? 残り2つは無意味なのですよね? ・1月がCellに表示されない. ・インクリメントする月を変えても,そのインクリメントした月が表示されない. ・(print()でLogを監視しても表示がない)
vlb

2019/10/06 04:12 編集

>>質問内容は分解できると思います。 1. データの更新(インクリメント?)と、2. 取得です。 ⇓⇓ 問題点として挙げているのは「1月がCellに表示されない」であって,データ(インクリメント)が更新されないとは言っていません.
vlb

2019/10/06 04:12 編集

>>質問には問題点として、以下の3つを挙げられていると思うのですが、 「私は問題を1つしかしていない」というのであれば、どれが本当の質問 ですか? 残り2つは無意味なのですよね? ・1月がCellに表示されない. ・インクリメントする月を変えても,そのインクリメントした月が表示さ れない. ・(print()でLogを監視しても表示がない) ⇓⇓ 問題点はインクリメントした月のデータが取得できないという状況です. (質問では例として1月の値をインクリメントしただけ.) 下の2つは質問ではなく補足説明です.
vlb

2019/10/06 04:19

>>viewcontroller.swiftのviewdidloadで同様に取得、 printを記入して見て、結果を見て見て下さい。 ↓↓ ViewController.swiftに下記を追加 ///////////////////////////////// override func viewDidLoad() { super.viewDidLoad() self.ref = Database.database().reference().child("This") self.ref.observeSingleEvent(of: .value) { (DataSnapshot) in let namesArray = DataSnapshot.value as! [String: Int] print(namesArray) } } //////////////////////////////// Logを下記に記載 ["1月": 32, "3月": 30, "2月": 15, "4月": 50] ViewControllerでは取得できています.
vlb

2019/10/06 04:29

>>気になるのはデータの更新のタイミングです。 ViewControllerのviewWillDisappearとTableViewControllerのviewDidLoadのタイミングが 合ってないのではないかと推測されます。 どちらにもprint(何か)を記入、完了の{}の中にもprintを記入して、 タイミングの確認をして下さい。 ↓↓ ViewControllerのviewWillDisappearとTableViewControllerのviewDidLoadではTableViewControllerのviewDidLoadが先です. ViewControllerで「viewWillDisappear」「viewDidDisappear」, TableViewControllerで「viewDidLoad」「viewWillAppear」「viewDidAppear」 の組み合わせで試していますが,同じ問題が発生します.
vlb

2019/10/06 05:12 編集

>>transactionとなっているように非同期通信なので、 1月データの更新途中にデータの取得が行われてて、 1月はロックがかかってて取得できず、表示されないのではないですか? ↓↓ その可能性は私も考えております. しかし,そこが問題で,ViewControllerからTableViewControllerへ画面遷移を行なったタイミングでデータの更新とCellの表示を行いたいので,どのようにすれば解決に至るか考えている次第です. また,TableViewControllerに更新ボタンを設けて,画面遷移が完全に終わった後にTableViewをreloadさせると,バグは発生しません. 宜しくお願い致します.
hameji

2019/10/06 15:40 編集

情報は小出しにせずに最初から全部出してください。 全く違う状況になってますが…。 結局表示のコードには問題がなく、 データの更新の部分に問題ありですよね。 transactionが返ってくるとこで、 tableviewにreloadをかける必要があります。 observerを使う、delegateを使う もしくは直接指定 (storyboard idからviewを取得して、tableviewにreloadをかける) でもも行えると思います。 しかし、根本問題として、 viewcontrollerを表示し、tableViewに遷移せずに アプリを終了した場合はどうするつもりでしょうか? 自分なら、viewcontrollerだけの表示で数えないなら、 tableViewcontrollerで値の更新にしますし、(同じviewでreloadしやすい) viewcontrollerを表示した時は遷移しなくても数えたいなら、 viewcontrollerのviewdidLoadでデータを書き換えるようにすると思います。 そうすれば、タイミングの問題は出にくいのではないかと思います。
vlb

2019/10/20 14:40

>>情報は小出しにせずに最初から全部出してください。 ↓ すいません.バグに関する情報は最小限にする必要があると思いこのようにしております. アプリの全貌が知りたいとのことでしたので,追加追加の情報になりました. 問題自体は最初の質問の通りです.
vlb

2019/10/20 14:47

>>しかし、根本問題として、 viewcontrollerを表示し、tableViewに遷移せずに アプリを終了した場合はどうするつもりでしょうか? ↓ その問題についても考えていて,アプリが強制終了する際に実行するメソッド等がないかも探しているところです.また,userdefaultsなどを使っても解決できるのではないかと考えています.
vlb

2019/10/20 15:40

>>transactionが返ってくるとこで、 tableviewにreloadをかける必要があります。 observerを使う、delegateを使う もしくは直接指定 (storyboard idからviewを取得して、tableviewにreloadをかける) でもも行えると思います。 ↓ 実際のアプリに近い構成は写真5のような感じになっているのですが,FirestViewのButtonを押すとDetailViewに遷移し,DetailViewからタブの切り替えでSecondViewに遷移した際に上記の問題が発生します. DetailViewからの遷移先が,BackボタンでFirestViewへ行くか,タブの切り替えでSecondViewに行くかの2通りあるので,タブの切り替えでSecondViewへ遷移した時だけtransactionやobserverを使って通知するにはどのようにしたら良いのか考えている次第です. 具体的な方法やコードを知っていましたらご教授願います.
vlb

2019/11/17 11:32

2019/10/26修正につきましてはありがとうございます. 特定の画面遷移を判断できる方法があるのですね. そこでなのですが // ここに値を書き換えるトランザクションを記入し、トランザクションがかえってきたら、tableViewにreloadをかけること の部分をどのように書けばいいのかわからない次第です. 教えていただけますでしょうか.
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問