עבודה עם רשימות נתונים בפלטפורמות של Apple

קבלת FIRDatabaseReference

כדי לקרוא או לכתוב נתונים במסד הנתונים, צריך מופע של FIRDatabaseReference:

Swift

הערה: מוצר Firebase הזה לא זמין ביעד App Clip.
var ref: DatabaseReference!

ref = Database.database().reference()

Objective-C

הערה: מוצר Firebase הזה לא זמין ביעד App Clip.
@property (strong, nonatomic) FIRDatabaseReference *ref;

self.ref = [[FIRDatabase database] reference];

קריאה וכתיבה של רשימות

הוספה לרשימת נתונים

אפשר להשתמש בשיטה childByAutoId כדי לצרף נתונים לרשימה באפליקציות מרובות משתמשים. השיטה childByAutoId יוצרת מפתח ייחודי בכל פעם שמוסיפים צאצא חדש להפניה שצוינה ב-Firebase. באמצעות המפתחות האלה שנוצרים באופן אוטומטי לכל רכיב חדש ברשימה, כמה לקוחות יכולים להוסיף צאצאים לאותו מיקום בו-זמנית בלי שיהיו התנגשויות בכתיבה. המפתח הייחודי שנוצר על ידי childByAutoId מבוסס על חותמת זמן, ולכן הפריטים ברשימה מסודרים אוטומטית בסדר כרונולוגי.

אפשר להשתמש בהפניה לנתונים החדשים שמוחזרים על ידי השיטה childByAutoId כדי לקבל את הערך של המפתח שנוצר אוטומטית עבור צאצא או להגדיר נתונים עבור צאצא. התקשרות אל getKey בהפניה אל childByAutoId מחזירה את המפתח שנוצר אוטומטית.

אפשר להשתמש במפתחות שנוצרו אוטומטית כדי לפשט את תהליך השטחת מבנה הנתונים. מידע נוסף זמין בדוגמה של פיצול נתונים.

האזנה לאירועים משוכפלים

אירועים שקשורים לילדים מופעלים בתגובה לפעולות ספציפיות שמתבצעות על הילדים של צומת מפעולה כמו הוספה של צומת צאצא חדש באמצעות השיטה childByAutoId או עדכון של צומת צאצא באמצעות השיטה updateChildValues.

סוג האירוע שימוש אופייני
FIRDataEventTypeChildAdded אחזור רשימות של פריטים או האזנה להוספות לרשימה של פריטים. האירוע הזה מופעל פעם אחת לכל צאצא קיים, ואז שוב בכל פעם שמוסיפים צאצא חדש לנתיב שצוין. המאזין מקבל תמונת מצב שמכילה את הנתונים של צאצא חדש.
FIRDataEventTypeChildChanged חיפוש שינויים בפריטים ברשימה. האירוע הזה מופעל בכל פעם שמשנים צומת צאצא. השינויים האלה כוללים שינויים בצאצאים של צומת הילד. תמונת המצב שמועברת למאזין האירועים מכילה את הנתונים המעודכנים של צאצא.
FIRDataEventTypeChildRemoved האזנה להסרה של פריטים מרשימה. האירוע הזה מופעל כשמסירים צאצא מיידי.קובץ ה-snapshot שמועבר לבלוק הקריאה החוזרת מכיל את הנתונים של הצאצא שהוסר.
FIRDataEventTypeChildMoved האזנה לשינויים בסדר הפריטים ברשימה ממוספרת. האירוע הזה מופעל בכל פעם שעדכון גורם לשינוי הסדר של רכיב צאצא. הוא משמש עם נתונים שממוינים לפי queryOrderedByChild או queryOrderedByValue.

כל אחת מהאפשרויות האלה יכולה להיות שימושית להאזנה לשינויים בצומת ספציפי במסד נתונים. לדוגמה, אפליקציה לבלוגים ברשתות חברתיות יכולה להשתמש בשיטות האלה יחד כדי לעקוב אחרי הפעילות בתגובות של פוסט, כמו שמוצג בהמשך:

Swift

הערה: מוצר Firebase הזה לא זמין ביעד App Clip.
// Listen for new comments in the Firebase database
commentsRef.observe(.childAdded, with: { (snapshot) -> Void in
  self.comments.append(snapshot)
  self.tableView.insertRows(
    at: [IndexPath(row: self.comments.count - 1, section: self.kSectionComments)],
    with: UITableView.RowAnimation.automatic
  )
})
// Listen for deleted comments in the Firebase database
commentsRef.observe(.childRemoved, with: { (snapshot) -> Void in
  let index = self.indexOfMessage(snapshot)
  self.comments.remove(at: index)
  self.tableView.deleteRows(
    at: [IndexPath(row: index, section: self.kSectionComments)],
    with: UITableView.RowAnimation.automatic
  )
})

Objective-C

הערה: מוצר Firebase הזה לא זמין ביעד App Clip.
// 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];
 }];

האזנה לאירועי ערך

הדרך המומלצת לקרוא רשימות של נתונים היא להאזין לאירועי צאצא, אבל יש מצבים שבהם כדאי להאזין לאירועי ערך בהפניה לרשימה.

צירוף של FIRDataEventTypeValue observer לרשימת נתונים יחזיר את הרשימה כולה כ-DataSnapshot יחיד, שאפשר להשתמש בו בלולאה כדי לגשת לנתונים של כל ילד בנפרד.

גם אם יש רק התאמה אחת לשאילתה, התמונה היא עדיין רשימה, רק שהיא מכילה פריט אחד. כדי לגשת לפריט, צריך להשתמש בלולאה כדי לעבור על התוצאה:

Swift

הערה: מוצר Firebase הזה לא זמין ביעד App Clip.
_commentsRef.observe(.value) { snapshot in
  for child in snapshot.children {
    ...
  }
}

Objective-C

הערה: מוצר Firebase הזה לא זמין ביעד App Clip.
[_commentsRef
              observeEventType:FIRDataEventTypeValue
              withBlock:^(FIRDataSnapshot *snapshot) {
                // Loop over children
                NSEnumerator *children = [snapshot children];
                FIRDataSnapshot *child;
                while (child = [children nextObject]) {
                  // ...
                }
              }];

הדפוס הזה יכול להיות שימושי כשרוצים לאחזר את כל הצאצאים של רשימה בפעולה אחת, במקום להאזין לאירועים נוספים של הוספת צאצא.

מיון וסינון של נתונים

אפשר להשתמש במחלקה Realtime Database FIRDatabaseQuery כדי לאחזר נתונים ממוינים לפי מפתח, לפי ערך או לפי הערך של צאצא. אפשר גם לסנן את התוצאה הממוינת לפי מספר מסוים של תוצאות או לפי טווח של מפתחות או ערכים.

מיון נתונים

כדי לאחזר נתונים ממוינים, צריך קודם לציין אחת משיטות המיון כדי לקבוע את סדר התוצאות:

שיטה שימוש
queryOrderedByKey מיון התוצאות לפי מפתחות צאצא.
queryOrderedByValue מיון התוצאות לפי ערכי הצאצא.
queryOrderedByChild מיון התוצאות לפי הערך של מפתח צאצא ספציפי או נתיב צאצא מוטמע.

אפשר להשתמש רק בשיטה אחת של מיון בכל פעם. הפעלת שיטה של מיון לפי סדר (order-by) מספר פעמים באותה שאילתה גורמת לשגיאה.

בדוגמה הבאה מוצג איך אפשר לאחזר רשימה של הפוסטים המובילים של משתמש, ממוינים לפי מספר הכוכבים:

Swift

הערה: מוצר Firebase הזה לא זמין ביעד App Clip.
// My top posts by number of stars
let myTopPostsQuery = ref.child("user-posts").child(getUid()).queryOrdered(byChild: "starCount")

Objective-C

הערה: מוצר Firebase הזה לא זמין ביעד App Clip.
// My top posts by number of stars
FIRDatabaseQuery *myTopPostsQuery = [[[self.ref child:@"user-posts"]
                                      child:[super getUid]]
                                     queryOrderedByChild:@"starCount"];

השאילתה הזו מאחזרת את הפוסטים של המשתמש מהנתיב במסד הנתונים על סמך מזהה המשתמש, ומסדרת אותם לפי מספר הכוכבים שכל פוסט קיבל. הטכניקה הזו של שימוש במזהים כמפתחות אינדקס נקראת פיצול נתונים. אפשר לקרוא עליה עוד במאמר Structure Your Database (ארגון מסד הנתונים).

הקריאה ל-method‏ queryOrderedByChild מציינת את מפתח הילד שלפיו יסודרו התוצאות. בדוגמה הזו, הפוסטים ממוינים לפי הערך של "starCount" הרכיב הצאצא בכל פוסט. אפשר גם להזמין שאילתות לפי צאצאים מוטמעים, אם יש לכם נתונים שנראים כך:

"posts": {
  "ts-functions": {
    "metrics": {
      "views" : 1200000,
      "likes" : 251000,
      "shares": 1200,
    },
    "title" : "Why you should use TypeScript for writing Cloud Functions",
    "author": "Doug",
  },
  "android-arch-3": {
    "metrics": {
      "views" : 900000,
      "likes" : 117000,
      "shares": 144,
    },
    "title" : "Using Android Architecture Components with Firebase Realtime Database (Part 3)",
    "author": "Doug",
  }
},

במקרה כזה, אפשר להזמין את רכיבי הרשימה לפי ערכים שמוטמעים מתחת למפתח metrics על ידי ציון הנתיב היחסי לצאצא המוטמע בקריאה metrics.queryOrderedByChild

Swift

הערה: מוצר Firebase הזה לא זמין ביעד App Clip.
 
let postsByMostPopular = ref.child("posts").queryOrdered(byChild: "metrics/views")

Objective-C

הערה: מוצר Firebase הזה לא זמין ביעד App Clip.
 
FIRDatabaseQuery *postsByMostPopular = [[ref child:@"posts"] queryOrderedByChild:@"metrics/views"];

מידע נוסף על הסדר של סוגי נתונים אחרים זמין במאמר איך מסודרים נתוני שאילתות.

סינון נתונים

כדי לסנן נתונים, אפשר לשלב כל אחת מהשיטות של הגבלה או טווח עם שיטת מיון כשיוצרים שאילתה.

שיטה שימוש
queryLimitedToFirst מגדיר את המספר המקסימלי של פריטים שיוחזרו מתחילת רשימת התוצאות הממוינת.
queryLimitedToLast מגדיר את המספר המקסימלי של פריטים שיוחזרו מסוף הרשימה הממוינת של התוצאות.
queryStartingAtValue הפונקציה מחזירה פריטים שגדולים מהמפתח או מהערך שצוינו או שווים להם, בהתאם לשיטת המיון שנבחרה.
queryStartingAfterValue החזרת פריטים שגדולים מהמפתח או מהערך שצוינו, בהתאם לשיטת המיון שנבחרה.
queryEndingAtValue הפונקציה מחזירה פריטים שקטנים מהמפתח או מהערך שצוינו או שווים להם, בהתאם לשיטת המיון שנבחרה.
queryEndingBeforeValue הפונקציה מחזירה פריטים שקטנים מהמפתח או מהערך שצוינו, בהתאם לשיטת המיון שנבחרה.
queryEqualToValue הפונקציה מחזירה פריטים ששווים למפתח או לערך שצוינו, בהתאם לשיטת המיון שנבחרה.

בניגוד לשיטות order-by, אפשר לשלב כמה פונקציות limit או range. לדוגמה, אפשר לשלב בין השיטות queryStartingAtValue ו-queryEndingAtValue כדי להגביל את התוצאות לטווח ערכים מסוים.

הגבלת מספר התוצאות

אפשר להשתמש בשיטות queryLimitedToFirst ו-queryLimitedToLast כדי להגדיר מספר מקסימלי של ילדים שיסונכרנו עבור קריאה חוזרת (callback) נתונה. לדוגמה, אם משתמשים ב-queryLimitedToFirst כדי להגדיר מגבלה של 100, מקבלים בהתחלה רק עד 100 קריאות חוזרות של FIRDataEventTypeChildAdded. אם יש לכם פחות מ-100 פריטים שמאוחסנים במסד הנתונים של Firebase, מתבצעת קריאה חוזרת (callback) של FIRDataEventTypeChildAdded לכל פריט.

כשהפריטים משתנים, אתם מקבלים קריאות חוזרות (callback) מסוג FIRDataEventTypeChildAdded לפריטים שנכנסים לשאילתה וקריאות חוזרות מסוג FIRDataEventTypeChildRemoved לפריטים שיוצאים ממנה, כך שהמספר הכולל נשאר 100.

בדוגמה הבאה מוצג אופן האחזור של רשימה של 100 הפוסטים האחרונים של כל המשתמשים באפליקציה לדוגמה לניהול בלוגים:

Swift

הערה: מוצר Firebase הזה לא זמין ביעד App Clip.
// Last 100 posts, these are automatically the 100 most recent
// due to sorting by push() keys
let recentPostsQuery = (ref?.child("posts").queryLimited(toFirst: 100))!

Objective-C

הערה: מוצר Firebase הזה לא זמין ביעד App Clip.
// Last 100 posts, these are automatically the 100 most recent
// due to sorting by push() keys
FIRDatabaseQuery *recentPostsQuery = [[self.ref child:@"posts"] queryLimitedToFirst:100];

סינון לפי מפתח או ערך

אפשר להשתמש ב-queryStartingAtValue,‏ queryStartingAfterValue,‏ queryEndingAtValue,‏ queryEndingBeforeValue ו-queryEqualToValue כדי לבחור נקודות שרירותיות להתחלה, לסיום ולשוויון של שאילתות. המאפיין הזה יכול להיות שימושי כשמבצעים חלוקה לעמודים של נתונים או כשמחפשים פריטים עם צאצאים שיש להם ערך ספציפי.

איך נתוני השאילתות מסודרים

בקטע הזה מוסבר איך הנתונים ממוינים לפי כל אחת מהשיטות של order-by במחלקה FIRDatabaseQuery.

queryOrderedByKey

כשמשתמשים ב-queryOrderedByKey כדי למיין את הנתונים, הנתונים מוחזרים בסדר עולה לפי מפתח.

  1. הילדים עם מפתח שאפשר לנתח כמספר שלם בן 32 ביט מופיעים ראשונים, בסדר עולה.
  2. אחריהם מופיעים ילדים עם ערך מחרוזת כמפתח, ממוינים בסדר עולה לפי סדר מילוני.

queryOrderedByValue

כשמשתמשים ב-queryOrderedByValue, הילדים מסודרים לפי הערך שלהם. קריטריוני המיון זהים לאלה שמופיעים ב-queryOrderedByChild, אלא שהערך של הצומת משמש במקום הערך של מפתח צאצא שצוין.

queryOrderedByChild

כשמשתמשים ב-queryOrderedByChild, נתונים שמכילים את מפתח הצאצא שצוין מסודרים באופן הבא:

  1. ילדים עם ערך nil למפתח הילד שצוין מופיעים ראשונים.
  2. אחריהם מופיעים רכיבי צאצא עם הערך false עבור מפתח הצאצא שצוין. אם לכמה צאצאים יש ערך של false, הם ממוינים לקסיקוגרפית לפי מפתח.
  3. אחריהם מופיעים רכיבי צאצא עם הערך true עבור מפתח הצאצא שצוין. אם לכמה צאצאים יש ערך של true, הם ממוינים לפי מפתח בסדר לקסיקוגרפי.
  4. אחריהם מופיעים ילדים עם ערך מספרי, ממוינים בסדר עולה. אם לכמה צאצאים יש את אותו ערך מספרי בצומת הצאצא שצוין, הם ממוינים לפי מפתח.
  5. מחרוזות מופיעות אחרי מספרים וממוינות בסדר עולה לפי סדר מילוני. אם לכמה צאצאים יש את אותו ערך בצומת הצאצא שצוין, הם מסודרים לפי מפתח בסדר לקסיקוגרפי.
  6. האובייקטים מופיעים בסוף וממוינים בסדר עולה לפי מפתח, בסדר לקסיקוגרפי.

ניתוק של listeners

הצופים לא מפסיקים אוטומטית את סנכרון הנתונים כשעוזבים את ViewController. אם לא מסירים את האובייקט מסוג Observer בצורה נכונה, הוא ממשיך לסנכרן נתונים לזיכרון המקומי וישמור את כל האובייקטים שנתפסו בסגירת ה-event handler, מה שעלול לגרום לדליפות זיכרון. כשאין יותר צורך ב-observer, אפשר להסיר אותו באמצעות העברת FIRDatabaseHandle המשויך לשיטה removeObserverWithHandle.

כשמוסיפים בלוק של callback להפניה, מוחזרת התוצאה FIRDatabaseHandle. אפשר להשתמש בנקודות האחיזה האלה כדי להסיר את בלוק הקריאה החוזרת.

אם נוספו כמה listeners להפניה למסד נתונים, כל אחד מהם נקרא כשמופעל אירוע. כדי להפסיק את סנכרון הנתונים במיקום מסוים, צריך להסיר את כל הצופים במיקום באמצעות הקריאה לשיטה removeAllObservers.

התקשרות אל removeObserverWithHandle או אל removeAllObservers ב-listener לא מסירה באופן אוטומטי listeners שרשומים בצמתי הצאצא שלו. צריך גם לעקוב אחרי ההפניות או ה-handles האלה כדי להסיר אותם.

השלבים הבאים