Join us for Firebase Summit on November 10, 2021. Tune in to learn how Firebase can help you accelerate app development, release with confidence, and scale with ease. Register

קרא וכתוב נתונים בפלטפורמות של אפל

(אופציונלי) אב טיפוס ובדיקה עם חבילת אמולטור מקומית של Firebase

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

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

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

  1. הוספת שורה של קוד לתצורת הבדיקה של האפליקציה שלך כדי להתחבר לאמולטור.
  2. מן השורש של ספריית הפרויקט המקומית, פועל firebase emulators:start .
  3. ביצוע שיחות מקוד האב -טיפוס של האפליקציה שלך באמצעות SDK של פלטפורמת מסד נתונים בזמן אמת כרגיל, או באמצעות ממשק ה- REST API של מסד הנתונים בזמן אמת.

מפורטת בהדרכה המעורבת מסד זמן אמת ותפקידי ענן נגישה. כמו כן כדאי להעיף מבט לעבר הקדמת Suite Emulator המקומית .

קבל הפניה FIRDatabase

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

מָהִיר

var ref: DatabaseReference!

ref = Database.database().reference()

Objective-C

@property (strong, nonatomic) FIRDatabaseReference *ref;

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

כתוב נתונים

מסמך זה עוסק ביסודות הקריאה והכתיבה של נתוני Firebase.

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

פעולות כתיבה בסיסיות

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

  • סוגי מעבר המתאימים לסוגי JSON הזמינים כדלקמן:
    • NSString
    • NSNumber
    • NSDictionary
    • NSArray

למשל, אתה יכול להוסיף משתמש עם setValue כדלקמן:

מָהִיר

self.ref.child("users").child(user.uid).setValue(["username": username])

Objective-C

[[[self.ref child:@"users"] child:authResult.user.uid]
    setValue:@{@"username": username}];

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

מָהִיר

self.ref.child("users/\(user.uid)/username").setValue(username)

Objective-C

[[[[_ref child:@"users"] child:user.uid] child:@"username"] setValue:username];

קרא נתונים

קרא נתונים על ידי האזנה לאירועי ערך

כדי לקרוא נתונים על נתיב ולהקשיב לשינויים, להשתמש observeEventType:withBlock של FIRDatabaseReference להתבונן FIRDataEventTypeValue אירועים.

סוג אירוע שימוש אופייני
FIRDataEventTypeValue קרא והקשב לשינויים בתוכן הנתיב כולו.

אתה יכול להשתמש FIRDataEventTypeValue האירוע לקרוא את הנתונים על נתיב נתון, כפי שהוא קיים בעת האירוע. שיטה זו מופעלת פעם אחת כשהמאזין מצורף ושוב בכל פעם שהנתונים, כולל ילדים, משתנים. התקשרות האירוע מועברת snapshot המכיל את כל הנתונים במיקום זה, לרבות נתונים ילד. אם אין נתונים, תמונת המצב תחזור false כאשר אתה קורא exists() ו nil כאשר אתה קורא שלה value רכוש.

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

מָהִיר

refHandle = postRef.observe(DataEventType.value, with: { snapshot in
  // ...
})

Objective-C

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

המאזין מקבל FIRDataSnapshot המכיל את הנתונים במיקום שצוין באתר בעת האירוע שלה value הרכוש. ניתן להקצות ערכי לסוג ילידי המתאים, כגון NSDictionary . אם לא קיימים נתוני המיקום, את value הוא nil .

קרא נתונים פעם אחת

קרא פעם אחת באמצעות getData ()

ה- SDK נועד לנהל אינטראקציות עם שרתי מסדי נתונים בין אם האפליקציה שלך מקוונת או לא מקוונת.

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

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

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

מָהִיר

ref.child("users/\(uid)/username").getData(completion:  { error, snapshot in
  guard error == nil else {
    print(error!.localizedDescription)
    return;
  }
  let userName = snapshot.value as? String ?? "Unknown";
});

Objective-C

NSString *userPath = [NSString stringWithFormat:@"users/%@/username", uid];
[[ref child:userPath] getDataWithCompletionBlock:^(NSError * _Nullable error, FIRDataSnapshot * _Nonnull snapshot) {
  if (error) {
    NSLog(@"Received an error %@", error);
    return;
  }
  NSString *userName = snapshot.value;
}];

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

קרא נתונים פעם אחת עם משקיף

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

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

מָהִיר

let userID = Auth.auth().currentUser?.uid
ref.child("users").child(userID!).observeSingleEvent(of: .value, with: { snapshot in
  // Get user value
  let value = snapshot.value as? NSDictionary
  let username = value?["username"] as? String ?? ""
  let user = User(username: username)

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

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);
}];

עדכון או מחיקה של נתונים

עדכן שדות ספציפיים

כדי לכתוב בו זמנית לילדים ספציפיים של צומת מבלי להחליף צומת ילד אחרים, להשתמש updateChildValues השיטה.

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

מָהִיר

guard let key = ref.child("posts").childByAutoId().key else { return }
let post = ["uid": userID,
            "author": username,
            "title": title,
            "body": body]
let childUpdates = ["/posts/\(key)": post,
                    "/user-posts/\(userID)/\(key)/": post]
ref.updateChildValues(childUpdates)

Objective-C

NSString *key = [[_ref child:@"posts"] childByAutoId].key;
NSDictionary *post = @{@"uid": userID,
                       @"author": username,
                       @"title": title,
                       @"body": body};
NSDictionary *childUpdates = @{[@"/posts/" stringByAppendingString:key]: post,
                               [NSString stringWithFormat:@"/user-posts/%@/%@/", userID, key]: post};
[_ref updateChildValues:childUpdates];

דוגמה זו משתמשת childByAutoId ליצור פוסט ב הצומת המכיל הודעות לכל המשתמשים בדומיין /posts/$postid ובמקביל לאחזר את המפתח עם getKey() . המפתח לאחר מכן ניתן להשתמש כדי ליצור ערך שני הודעות של המשתמש ב /user-posts/$userid/$postid .

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

הוסף בלוק השלמה

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

מָהִיר

ref.child("users").child(user.uid).setValue(["username": username]) {
  (error:Error?, ref:DatabaseReference) in
  if let error = error {
    print("Data could not be saved: \(error).")
  } else {
    print("Data saved successfully!")
  }
}

Objective-C

[[[_ref child:@"users"] child:user.uid] setValue:@{@"username": username} withCompletionBlock:^(NSError *error, FIRDatabaseReference *ref) {
  if (error) {
    NSLog(@"Data could not be saved: %@", error);
  } else {
    NSLog(@"Data saved successfully.");
  }
}];

מחק נתונים

הדרך הפשוטה ביותר נתונים המחיקה היא להתקשר removeValue על התייחסות למיקום של הנתונים.

אתה גם יכול למחוק ידי ציון nil כערך לעוד פעולת הכתיבה כגון setValue או updateChildValues . אתה יכול להשתמש בטכניקה זו עם updateChildValues למחוק ילדים מרובים בשיחת API יחידה.

נתק מאזינים

משקיפים לא להפסיק סנכרון אוטומטי של נתונים כאשר אתה משאיר ViewController . אם צופה אינו מוסר כראוי, הוא ממשיך לסנכרן נתונים לזיכרון מקומי. כאשר משקיף אינו נחוץ עוד, להסיר אותו על ידי העברת הקשורים FIRDatabaseHandle אל removeObserverWithHandle השיטה.

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

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

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

שמור נתונים כעסקאות

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

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

מָהִיר

ref.runTransactionBlock({ (currentData: MutableData) -> TransactionResult in
  if var post = currentData.value as? [String: AnyObject],
    let uid = Auth.auth().currentUser?.uid {
    var stars: [String: Bool]
    stars = post["stars"] as? [String: Bool] ?? [:]
    var starCount = post["starCount"] as? Int ?? 0
    if let _ = stars[uid] {
      // Unstar the post and remove self from stars
      starCount -= 1
      stars.removeValue(forKey: uid)
    } else {
      // Star the post and add self to stars
      starCount += 1
      stars[uid] = true
    }
    post["starCount"] = starCount as AnyObject?
    post["stars"] = stars as AnyObject?

    // Set value and report transaction success
    currentData.value = post

    return TransactionResult.success(withValue: currentData)
  }
  return TransactionResult.success(withValue: currentData)
}) { error, committed, snapshot in
  if let error = error {
    print(error.localizedDescription)
  }
}

Objective-C

[ref runTransactionBlock:^FIRTransactionResult * _Nonnull(FIRMutableData * _Nonnull currentData) {
  NSMutableDictionary *post = currentData.value;
  if (!post || [post isEqual:[NSNull null]]) {
    return [FIRTransactionResult successWithValue:currentData];
  }

  NSMutableDictionary *stars = post[@"stars"];
  if (!stars) {
    stars = [[NSMutableDictionary alloc] initWithCapacity:1];
  }
  NSString *uid = [FIRAuth auth].currentUser.uid;
  int starCount = [post[@"starCount"] intValue];
  if (stars[uid]) {
    // Unstar the post and remove self from stars
    starCount--;
    [stars removeObjectForKey:uid];
  } else {
    // Star the post and add self to stars
    starCount++;
    stars[uid] = @YES;
  }
  post[@"stars"] = stars;
  post[@"starCount"] = @(starCount);

  // Set value and report transaction success
  currentData.value = post;
  return [FIRTransactionResult successWithValue:currentData];
} andCompletionBlock:^(NSError * _Nullable error,
                       BOOL committed,
                       FIRDataSnapshot * _Nullable snapshot) {
  // Transaction completed
  if (error) {
    NSLog(@"%@", error.localizedDescription);
  }
}];

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

תוספות בצד השרת האטומי

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

מָהִיר

let updates = [
  "posts/\(postID)/stars/\(userID)": true,
  "posts/\(postID)/starCount": ServerValue.increment(1),
  "user-posts/\(postID)/stars/\(userID)": true,
  "user-posts/\(postID)/starCount": ServerValue.increment(1)
] as [String : Any]
Database.database().reference().updateChildValues(updates);

Objective-C

NSDictionary *updates = @{[NSString stringWithFormat: @"posts/%@/stars/%@", postID, userID]: @TRUE,
                        [NSString stringWithFormat: @"posts/%@/starCount", postID]: [FIRServerValue increment:@1],
                        [NSString stringWithFormat: @"user-posts/%@/stars/%@", postID, userID]: @TRUE,
                        [NSString stringWithFormat: @"user-posts/%@/starCount", postID]: [FIRServerValue increment:@1]};
[[[FIRDatabase database] reference] updateChildValues:updates];

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

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

עבודה עם נתונים לא מקוונים

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

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

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

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

נידבר יותר על התנהגות לא מקוונת למידע נוסף על מקוון ויכולות מחוברות .

הצעדים הבאים