iOS でのデータの取得

このドキュメントでは、データの取得に関する基本と、Firebase データの並べ替えとフィルタリングの方法について説明します。

Firebase データは、非同期リスナーを FIRDatabase 参照にアタッチして取得します。リスナーはデータの初期状態で 1 回トリガーされます。さらに、データが変更されると、そのたびに再トリガーされます。

イベントをリッスンする

データを取得する次の種類のイベントがないかリッスンすることが可能です。

イベントの種類 一般的な使用方法
FIRDataEventTypeValue パスのコンテンツ全体に対する変更の読み取りとリッスンを行います。
FIRDataEventTypeChildAdded アイテムのリストを取得するか、アイテムのリストへの追加がないかリッスンします。 FIRDataEventTypeChildChangedFIRDataEventTypeChildRemoved と併用して、リストに対する変更をモニタリングすることをおすすめします。
FIRDataEventTypeChildChanged リスト内のアイテムに対する変更がないかリッスンします。FIRDataEventTypeChildAddedFIRDataEventTypeChildRemoved と併用して、リストに対する変更をモニタリングします。
FIRDataEventTypeChildRemoved リストから削除されるアイテムがないかリッスンします。FIRDataEventTypeChildAddedFIRDataEventTypeChildChanged と併用して、リストに対する変更をモニタリングします。
FIRDataEventTypeChildMoved 並べ替えリストの項目順変更をリッスンします。 FIRDataEventTypeChildMoved イベントは常に、(現在の order-by メソッドに基づく)並べ替え変更が原因の FIRDataEventTypeChildChanged イベントに後続します。

イベント リスナーを追加するには、observeEventType メソッドを使用して、イベントの種類とコールバック ブロックを指定します。

value イベント

FIRDataEventTypeValue イベントを使用して、特定のパスにあるデータを、イベントのときに存在していたとおりに読み取ることができます。このメソッドはリスナーがアタッチされたときに 1 回トリガーされます。さらに、データ(子も含む)が変更されると、そのたびに再びトリガーされます。イベントのコールバックには、その場所にあるすべてのデータ(子のデータも含む)を含んでいる snapshot が渡されます。データが存在しない場合、返される snapshotvaluenil です。

次の例は、データベースから投稿の詳細を取得するソーシャルブログ アプリケーションを示しています。

Objective-C

_refHandle = [_postRef observeEventType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot * _Nonnull snapshot) {
  NSDictionary *postDict = snapshot.value;
  // ...
}];

Swift

refHandle = postRef.observeEventType(FIRDataEventType.Value, withBlock: { (snapshot) in
  let postDict = snapshot.value as! [String : AnyObject]
  // ...
})

リスナーは FIRDataSnapshot を受信します。その value プロパティには、イベントのときにデータベース内の指定された場所にあったデータが含まれています。この値は、NSDictionary など、適切なネイティブ型に割り当てることができます。 その場所にデータが存在しない場合、valuenil です。

child イベント

childByAutoId メソッドによる新しい子の追加や、updateChildValues メソッドによる子の更新など、ノードの子に対して発生した特定の操作に応じて、child イベントがトリガーされます。

FIRDataEventTypeChildAdded イベントは通常、Firebase データベース内のアイテムのリストを取得するために使用します。FIRDataEventTypeChildAdded イベントは既存の子ごとに 1 回トリガーされます。さらに、指定されたパスに新しい子が追加されると、そのたびに再びトリガーされます。リスナーには、新しい子のデータを含んでいるスナップショットが渡されます。

子ノードが修正されると、そのたびに FIRDataEventTypeChildChanged イベントがトリガーされます。この修正には、子ノードの子孫に対する修正も含まれます。このイベントはアイテムのリストに対する変更に応答するために通常、FIRDataEventTypeChildAdded イベントや FIRDataEventTypeChildRemoved イベントと組み合わせて使用します。イベント リスナーに渡されるスナップショットには、子の更新済みデータが含まれています。

FIRDataEventTypeChildRemoved イベントは直接の子が削除されるとトリガーされます。 これは通常、FIRDataEventTypeChildAdded イベントや FIRDataEventTypeChildChanged イベントと組み合わせて使用します。コールバック ブロックに渡されるスナップショットには、削除された子のデータが含まれています。

FIRDataEventTypeChildMoved イベントは、子の再並べ替えが起きる更新によって FIRDataEventTypeChildChanged イベントがトリガーされると、そのたびにトリガーされます。このイベントは、queryOrderedByChild または queryOrderedByValue で並べ替えられたデータに使用されます。

それぞれのイベントを組み合わせると、データベース内の特定のノードに対する変更をリッスンする場合に役立つことがあります。たとえば、ソーシャルブログ アプリでは、以下に示すように、これらのメソッドを組み合わせて使用して投稿のコメントでのアクティビティをモニタリングできます。

Objective-C

// Listen for new comments in the Firebase database
[_commentsRef
              observeEventType:FIRDataEventTypeChildAdded
              withBlock:^(FIRDataSnapshot *snapshot) {
                [self.comments addObject:snapshot];
                [self.tableView insertRowsAtIndexPaths:@[
                  [NSIndexPath indexPathForRow:[self.comments count] - 1 inSection:kSectionComments]
                ]
                                      withRowAnimation:UITableViewRowAnimationAutomatic];
              }];
// Listen for deleted comments in the Firebase database
[_commentsRef
 observeEventType:FIRDataEventTypeChildRemoved
 withBlock:^(FIRDataSnapshot *snapshot) {
   int index = [self indexOfMessage:snapshot];
   [self.comments removeObjectAtIndex:index];
   [self.tableView deleteRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:index inSection:kSectionComments]]
                         withRowAnimation:UITableViewRowAnimationAutomatic];
 }];

Swift

// Listen for new comments in the Firebase database
commentsRef.observeEventType(.ChildAdded, withBlock: { (snapshot) -> Void in
  self.comments.append(snapshot)
  self.tableView.insertRowsAtIndexPaths([NSIndexPath(forRow: self.comments.count-1, inSection: self.kSectionComments)], withRowAnimation: UITableViewRowAnimation.Automatic)
})
// Listen for deleted comments in the Firebase database
commentsRef.observeEventType(.ChildRemoved, withBlock: { (snapshot) -> Void in
  let index = self.indexOfMessage(snapshot)
  self.comments.removeAtIndex(index)
  self.tableView.deleteRowsAtIndexPaths([NSIndexPath(forRow: index, inSection: self.kSectionComments)], withRowAnimation: UITableViewRowAnimation.Automatic)
})

リスナーのデタッチ

ViewController から離れても、オブザーバーはデータの同期を自動的に停止するわけではありません。オブザーバーが適切に削除されないと、オブザーバーによってデータがローカルメモリに同期され続けます。オブザーバーが不要になったときは、関連付けられている FIRDatabaseHandleremoveObserverWithHandle メソッドに渡してオブザーバーを削除してください。

参照にコールバック ブロックを追加すると、FIRDatabaseHandle が返されます。 これらのハンドルを使用して、コールバック ブロックを削除できます。

データベース参照に複数のリスナーが追加されている場合、イベントが発生したときにそれぞれのリスナーが呼び出されます。その場所でのデータの同期を停止するために、removeAllObservers メソッドを呼び出して、その場所のすべてのオブザーバーを削除する必要があります。

リスナーの removeObserverWithHandle または removeAllObservers を呼び出しても、その子ノードに登録されているリスナーが自動的に削除されるわけではありません。これらを削除するには、これらの参照またはハンドルも追跡する必要があります。

データの 1 回読み取り

変更を想定していない UI 要素を初期化するときなど、コールバックを 1 回呼び出してから直ちに削除したい場合があります。 observeSingleEventOfType メソッドを使用して、このシナリオを簡素化できます。追加されたイベント コールバックは 1 回トリガーされ、その後再びトリガーされることはありません。

これは 1 回読み込む必要があるだけで頻繁な変更やアクティブなリッスンを行うことは想定していないデータに対して有用です。たとえば、前の例にあるブログアプリでは、このメソッドを使用して、ユーザーが新しい投稿を作成し始めたときにユーザーのプロフィールを読み込んでいます。

Objective-C

NSString *userID = [FIRAuth auth].currentUser.uid;
[[[_ref child:@"users"] child:userID] observeSingleEventOfType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot * _Nonnull snapshot) {
  // Get user value
  User *user = [[User alloc] initWithUsername:snapshot.value[@"username"]];

  // ...
} withCancelBlock:^(NSError * _Nonnull error) {
  NSLog(@"%@", error.localizedDescription);
}];

Swift

let userID = FIRAuth.auth()?.currentUser?.uid
ref.child("users").child(userID!).observeSingleEventOfType(.Value, withBlock: { (snapshot) in
  // Get user value
  let username = snapshot.value!["username"] as! String
  let user = User.init(username: username)

  // ...
  }) { (error) in
    print(error.localizedDescription)
}

データの並べ替えとフィルタリング

Realtime Database の FIRDatabaseQuery クラスを使用して、キー、値、または子の値で並べ替えられたデータを取得できます。また、並べ替えられた結果を、特定の個数の結果またはある範囲のキーや値にフィルタリングできます。

データを並べ替える

並べ替えられたデータを取得するには、最初にいずれかの order-by メソッドを指定して、結果の並べ替え方法を決定します。

メソッド 使用方法
queryOrderedByKey 子キーで結果を並べ替えます。
queryOrderedByValue 子の値で結果を並べ替えます。
queryOrderedByChild 指定した子キーの値で結果を並べ替えます。

同時に使用できる order-by メソッドは 1 つだけです。同じクエリで order-by メソッドを複数回呼び出すとエラーがスローされます。

次の例では、スターの数で並べ替えられたユーザーの上位の投稿のリストを取得する方法について示しています。

Objective-C

// My top posts by number of stars
FIRDatabaseQuery *myTopPostsQuery = [[[self.ref child:@"user-posts"]
                                      child:[super getUid]]
                                     queryOrderedByChild:@"starCount"];

Swift

// My top posts by number of stars
let myTopPostsQuery = (ref.child("user-posts").child(getUid())).queryOrderedByChild("starCount")

このクエリは、ユーザー ID に基づいてデータベース内のパスからユーザーの投稿を取得しています。ユーザー ID は、それぞれの投稿が受けたスターの数で並べ替えられています。このように ID をインデックス キーとして使用する手法はデータのファンアウトと呼ばれています。詳細については、データベースの構造化をご覧ください。

queryOrderedByChild メソッドの呼び出しでは、結果を並べ替えるための子キーを指定します。ここでは、投稿はそれぞれの投稿の子 "starCount" の値で並べ替えられます。他の種類のデータを並べ替える方法の詳細については、クエリデータが並べ替えられる仕組みをご覧ください。

データのフィルタリング

データをフィルタリングするには、クエリの構築時に、制限メソッドまたは範囲メソッドのいずれかを order-by メソッドと組み合わせることができます。

メソッド 使用方法
queryLimitedToFirst 並べ替えられた結果リストの先頭から返すアイテムの最大数を設定します。
queryLimitedToLast 並べ替えられた結果リストの末尾から返すアイテムの最大数を設定します。
queryStartingAtValue 選択した order-by メソッドに応じて、指定したキーまたは値以上のアイテムを返します。
queryEndingAtValue 選択した order-by メソッドに応じて、指定したキーまたは値以下のアイテムを返します。
queryEqualToValue 選択した order-by メソッドに応じて、指定したキーまたは値に等しいアイテムを返します。

order-by メソッドとは異なり、複数の制限関数または範囲関数を組み合わせることができます。 たとえば、queryStartingAtValue メソッドと queryEndingAtValue メソッドを組み合わせて、結果を特定の範囲の値に制限することができます。

結果の個数を制限する

queryLimitedToFirst メソッドと queryLimitedToLast メソッドを使用して、特定のコールバックに対して同期する子の最大数を設定できます。たとえば、queryLimitedToFirst を使用して上限 100 を設定した場合は、初期状態で最大 100 個の FIRDataEventTypeChildAdded コールバックのみを受け取ります。Firebase データベースに保存されているアイテムが 100 個よりも少ない場合、それぞれのアイテムに対する FIRDataEventTypeChildAdded コールバックが発生します。

アイテムの変更に応じて、クエリに加わったアイテムに対する FIRDataEventTypeChildAdded コールバックと、クエリから外れたアイテムに対する FIRDataEventTypeChildRemoved コールバックを受け取り、合計数が 100 に保たれます。

次の例は、サンプルのブログアプリで全ユーザーの最新 100 件の投稿のリストを取得する方法を示しています。

Objective-C

// Last 100 posts, these are automatically the 100 most recent
// due to sorting by push() keys
FIRDatabaseQuery *recentPostsQuery = [[self.ref child:@"posts"] queryLimitedToFirst:100];

Swift

// Last 100 posts, these are automatically the 100 most recent
// due to sorting by push() keys
let recentPostsQuery = (ref?.child("posts").queryLimitedToFirst(100))!

キーまたは値によるフィルタリング

queryStartingAtValuequeryEndingAtValuequeryEqualToValue を使用して、クエリ用に任意の始点、終点、同値点を選択できます。これは、データを改ページするときや、特定の値を持つ子があるアイテムを検索するときに役立ちます。

クエリデータが並べ替えられる仕組み

このセクションでは、FIRDatabaseQuery クラスのそれぞれの order-by メソッドでデータがどのように並べ替えられるのかを説明します。

queryOrderedByKey

queryOrderedByKey を使用してデータを並べ替えると、キーで昇順にデータが返されます。

  1. 32 ビット整数として解析できるキーを持つ子が最初に来ます。ただし、昇順に並べ替えられます。
  2. 文字列値をキーとして持つ子が次に来ます。ただし、辞書順で昇順に並べ替えられます。

queryOrderedByValue

queryOrderedByValue を使用すると、子がその値で並べ替えられます。並べ替えの条件は、 指定した子キーの値の代わりにノードの値が使用されるという点を除いて、queryOrderedByChild の場合と同じです。

queryOrderedByChild

queryOrderedByChild を使用すると、指定した子キーを含むデータが次のように並べ替えられます。

  1. 指定した子キーに nil 値を持つ子が最初に来ます。
  2. 指定した子キーに false 値を持つ子が次に来ます。複数の子が値 false を持つ場合、キーの辞書順で並べ替えられます。
  3. 指定した子キーに true 値を持つ子が次に来ます。複数の子が値 true を持つ場合、キーで辞書順に並べ替えられます。
  4. 数値を持つ子が次に来ます。ただし、昇順に並べ替えられます。複数の子が指定した子ノードに同じ数値を持つ場合、キーで並べ替えられます。
  5. 文字列は数値の後に来て、辞書順で昇順に並べ替えられます。複数の子が指定した子ノードに同じ値を持つ場合、キーで辞書順に並べ替えられます。
  6. オブジェクトが最後に来て、キー名の辞書順で昇順に並べ替えられます。

次のステップ

フィードバックを送信...

ご不明な点がありましたら、Google のサポートページをご覧ください。