อ่านและเขียนข้อมูลบนแพลตฟอร์ม Apple

จัดทุกอย่างให้เป็นระเบียบอยู่เสมอด้วยคอลเล็กชัน บันทึกและจัดหมวดหมู่เนื้อหาตามค่ากำหนดของคุณ

(ไม่บังคับ) สร้างต้นแบบและทดสอบด้วย Firebase Local Emulator Suite

ก่อนที่จะพูดถึงวิธีที่แอปของคุณอ่านและเขียนไปยัง Realtime Database เราขอแนะนำชุดเครื่องมือที่คุณสามารถใช้เพื่อสร้างต้นแบบและทดสอบการทำงานของฐานข้อมูลเรียลไทม์: Firebase Local Emulator Suite หากคุณกำลังลองใช้โมเดลข้อมูลต่างๆ ปรับกฎความปลอดภัยให้เหมาะสม หรือหาวิธีโต้ตอบกับแบ็คเอนด์ที่คุ้มค่าที่สุด ความสามารถในการทำงานแบบโลคัลโดยไม่ต้องปรับใช้บริการที่ใช้งานจริงอาจเป็นแนวคิดที่ดี

โปรแกรมจำลองฐานข้อมูลเรียลไทม์เป็นส่วนหนึ่งของ Local Emulator Suite ซึ่งช่วยให้แอปของคุณสามารถโต้ตอบกับเนื้อหาและการกำหนดค่าฐานข้อมูลจำลองของคุณ ตลอดจนเลือกทรัพยากรโครงการจำลองของคุณ (ฟังก์ชัน ฐานข้อมูลอื่นๆ และกฎความปลอดภัย)

การใช้โปรแกรมจำลองฐานข้อมูลเรียลไทม์เกี่ยวข้องกับขั้นตอนเพียงไม่กี่ขั้นตอน:

  1. การเพิ่มบรรทัดโค้ดในการกำหนดค่าการทดสอบของแอปเพื่อเชื่อมต่อกับโปรแกรมจำลอง
  2. จากรูทของไดเร็กทอรีโปรเจ็กต์ในเครื่องของคุณ ให้รัน firebase emulators:start
  3. ทำการเรียกจากโค้ดต้นแบบของแอปโดยใช้ SDK ของแพลตฟอร์ม Realtime Database ตามปกติ หรือใช้ Realtime Database REST API

คำแนะนำโดยละเอียดเกี่ยวกับฐานข้อมูลเรียลไทม์และฟังก์ชันคลาวด์ มีให้ใช้งาน คุณควรดูที่การ แนะนำ Local Emulator Suite

รับ FIRDatabaseReference

ในการอ่านหรือเขียนข้อมูลจากฐานข้อมูล คุณต้องมีอินสแตนซ์ของ FIRDatabaseReference :

สวิฟต์

หมายเหตุ: ผลิตภัณฑ์ Firebase นี้ไม่มีอยู่ในเป้าหมาย App Clip
var ref: DatabaseReference!

ref = Database.database().reference()

วัตถุประสงค์-C

หมายเหตุ: ผลิตภัณฑ์ Firebase นี้ไม่มีอยู่ในเป้าหมาย App Clip
@property (strong, nonatomic) FIRDatabaseReference *ref;

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

เขียนข้อมูล

เอกสารนี้ครอบคลุมพื้นฐานการอ่านและเขียนข้อมูล Firebase

ข้อมูล Firebase ถูกเขียนไปยังการอ้างอิง Database และดึงข้อมูลโดยการแนบตัวฟังแบบอะซิงโครนัสกับการอ้างอิง ผู้ฟังจะถูกกระตุ้นหนึ่งครั้งสำหรับสถานะเริ่มต้นของข้อมูล และอีกครั้งทุกครั้งที่มีการเปลี่ยนแปลงข้อมูล

การดำเนินการเขียนขั้นพื้นฐาน

สำหรับการดำเนินการเขียนขั้นพื้นฐาน คุณสามารถใช้ setValue เพื่อบันทึกข้อมูลไปยังการอ้างอิงที่ระบุ โดยแทนที่ข้อมูลที่มีอยู่ที่เส้นทางนั้น คุณสามารถใช้วิธีนี้เพื่อ:

  • ประเภทการส่งผ่านที่สอดคล้องกับประเภท JSON ที่มีดังต่อไปนี้:
    • NSString
    • NSNumber
    • NSDictionary
    • NSArray

ตัวอย่างเช่น คุณสามารถเพิ่มผู้ใช้ด้วย setValue ดังนี้:

สวิฟต์

หมายเหตุ: ผลิตภัณฑ์ Firebase นี้ไม่มีอยู่ในเป้าหมาย App Clip
self.ref.child("users").child(user.uid).setValue(["username": username])

วัตถุประสงค์-C

หมายเหตุ: ผลิตภัณฑ์ Firebase นี้ไม่มีอยู่ในเป้าหมาย App Clip
[[[self.ref child:@"users"] child:authResult.user.uid]
    setValue:@{@"username": username}];

การใช้ setValue ด้วยวิธีนี้จะเขียนทับข้อมูลที่ตำแหน่งที่ระบุ รวมถึงโหนดย่อยใดๆ อย่างไรก็ตาม คุณยังสามารถอัปเดตรายการย่อยได้โดยไม่ต้องเขียนวัตถุใหม่ทั้งหมด หากคุณต้องการอนุญาตให้ผู้ใช้อัปเดตโปรไฟล์ คุณสามารถอัปเดตชื่อผู้ใช้ได้ดังนี้:

สวิฟต์

หมายเหตุ: ผลิตภัณฑ์ Firebase นี้ไม่มีอยู่ในเป้าหมาย App Clip
self.ref.child("users/\(user.uid)/username").setValue(username)

วัตถุประสงค์-C

หมายเหตุ: ผลิตภัณฑ์ Firebase นี้ไม่มีอยู่ในเป้าหมาย App Clip
[[[[_ref child:@"users"] child:user.uid] child:@"username"] setValue:username];

อ่านข้อมูล

อ่านข้อมูลโดยการฟังเหตุการณ์ที่มีคุณค่า

หากต้องการอ่านข้อมูลที่เส้นทางและฟังการเปลี่ยนแปลง ให้ใช้ observeEventType:withBlock ของ FIRDatabaseReference เพื่อสังเกตเหตุการณ์ FIRDataEventTypeValue

ประเภทเหตุการณ์ การใช้งานทั่วไป
FIRDataEventTypeValue อ่านและฟังการเปลี่ยนแปลงเนื้อหาทั้งหมดของเส้นทาง

คุณสามารถใช้เหตุการณ์ FIRDataEventTypeValue เพื่ออ่านข้อมูลที่เส้นทางที่กำหนดได้ เนื่องจากเหตุการณ์นั้นมีอยู่ ณ เวลาที่เกิดเหตุการณ์ เมธอดนี้ถูกกระตุ้นหนึ่งครั้งเมื่อแนบ Listener และอีกครั้งทุกครั้งที่ข้อมูล รวมถึงลูก เปลี่ยนแปลง การเรียกกลับเหตุการณ์จะถูกส่งผ่านส snapshot ชอตที่มีข้อมูลทั้งหมดในตำแหน่งนั้น รวมถึงข้อมูลย่อย หากไม่มีข้อมูล สแน็ปช็อตจะคืนค่า false เมื่อคุณเรียกการ exists() และ nil เมื่อคุณอ่านคุณสมบัติ value ของมัน

ตัวอย่างต่อไปนี้สาธิตแอปพลิเคชันบล็อกโซเชียลที่ดึงรายละเอียดของโพสต์จากฐานข้อมูล:

สวิฟต์

หมายเหตุ: ผลิตภัณฑ์ Firebase นี้ไม่มีอยู่ในเป้าหมาย App Clip
refHandle = postRef.observe(DataEventType.value, with: { snapshot in
  // ...
})

วัตถุประสงค์-C

หมายเหตุ: ผลิตภัณฑ์ Firebase นี้ไม่มีอยู่ในเป้าหมาย App Clip
_refHandle = [_postRef observeEventType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot * _Nonnull snapshot) {
  NSDictionary *postDict = snapshot.value;
  // ...
}];

ผู้ฟังได้รับ FIRDataSnapshot ที่มีข้อมูลในตำแหน่งที่ระบุในฐานข้อมูล ณ เวลาที่เกิดเหตุการณ์ในคุณสมบัติ value คุณสามารถกำหนดค่าให้กับประเภทเนทีฟที่เหมาะสม เช่น NSDictionary หากไม่มีข้อมูลที่ตำแหน่งนั้น value จะเป็น nil

อ่านข้อมูลครั้งเดียว

อ่านครั้งเดียวโดยใช้ getData()

SDK ออกแบบมาเพื่อจัดการการโต้ตอบกับเซิร์ฟเวอร์ฐานข้อมูล ไม่ว่าแอปของคุณจะออนไลน์หรือออฟไลน์

โดยทั่วไป คุณควรใช้เทคนิคเหตุการณ์มูลค่าที่อธิบายไว้ด้านบนเพื่ออ่านข้อมูลเพื่อรับการแจ้งเตือนการอัปเดตข้อมูลจากแบ็กเอนด์ เทคนิคเหล่านั้นลดการใช้งานและการเรียกเก็บเงินของคุณ และได้รับการปรับปรุงเพื่อให้ผู้ใช้ของคุณได้รับประสบการณ์ที่ดีที่สุดเมื่อพวกเขาออนไลน์และออฟไลน์

หากคุณต้องการข้อมูลเพียงครั้งเดียว คุณสามารถใช้ getData() เพื่อรับภาพรวมของข้อมูลจากฐานข้อมูล หาก getData() ไม่สามารถส่งคืนค่าเซิร์ฟเวอร์ด้วยเหตุผลใดๆ ก็ตาม ไคลเอนต์จะตรวจสอบแคชหน่วยเก็บข้อมูลในเครื่องและส่งกลับข้อผิดพลาดหากยังไม่พบค่า

ตัวอย่างต่อไปนี้สาธิตการดึงชื่อผู้ใช้ที่เปิดเผยต่อสาธารณะของผู้ใช้ในครั้งเดียวจากฐานข้อมูล:

สวิฟต์

หมายเหตุ: ผลิตภัณฑ์ Firebase นี้ไม่มีอยู่ในเป้าหมาย App Clip
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";
});

วัตถุประสงค์-C

หมายเหตุ: ผลิตภัณฑ์ Firebase นี้ไม่มีอยู่ในเป้าหมาย App Clip
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 เพื่อรับข้อมูลจากดิสก์แคชในเครื่องได้ทันที

วิธีนี้มีประโยชน์สำหรับข้อมูลที่จำเป็นต้องโหลดเพียงครั้งเดียวและไม่ได้คาดว่าจะเปลี่ยนแปลงบ่อยหรือต้องการการฟังอย่างกระตือรือร้น ตัวอย่างเช่น แอปบล็อกในตัวอย่างก่อนหน้านี้ใช้วิธีนี้เพื่อโหลดโปรไฟล์ของผู้ใช้เมื่อพวกเขาเริ่มเขียนโพสต์ใหม่:

สวิฟต์

หมายเหตุ: ผลิตภัณฑ์ Firebase นี้ไม่มีอยู่ในเป้าหมาย App Clip
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)
}

วัตถุประสงค์-C

หมายเหตุ: ผลิตภัณฑ์ Firebase นี้ไม่มีอยู่ในเป้าหมาย App Clip
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 ​​คุณสามารถอัพเดตค่าลูกระดับล่างได้โดยระบุพาธสำหรับคีย์ หากข้อมูลถูกจัดเก็บไว้ในหลายตำแหน่งเพื่อให้ปรับขนาดได้ดีขึ้น คุณสามารถอัปเดตอินสแตนซ์ทั้งหมดของข้อมูลนั้นโดยใช้ การกระจายข้อมูล ตัวอย่างเช่น แอปโซเชียลบล็อกอาจต้องการสร้างโพสต์และอัปเดตเป็นฟีดกิจกรรมล่าสุดและฟีดกิจกรรมของผู้ใช้ที่โพสต์พร้อมกัน ในการทำเช่นนี้ แอปพลิเคชันบล็อกจะใช้โค้ดดังนี้:

สวิฟต์

หมายเหตุ: ผลิตภัณฑ์ Firebase นี้ไม่มีอยู่ในเป้าหมาย App Clip
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)

วัตถุประสงค์-C

หมายเหตุ: ผลิตภัณฑ์ Firebase นี้ไม่มีอยู่ในเป้าหมาย App Clip
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 ​​ใช้บล็อกการทำให้สมบูรณ์ซึ่งเป็นทางเลือกซึ่งถูกเรียกเมื่อการเขียนถูกคอมมิตไปยังฐานข้อมูล ผู้ฟังนี้มีประโยชน์ในการติดตามว่าข้อมูลใดได้รับการบันทึกและข้อมูลใดบ้างที่ยังคงซิงโครไนซ์อยู่ หากการโทรไม่สำเร็จ ผู้ฟังจะถูกส่งผ่านออบเจกต์ข้อผิดพลาดเพื่อระบุว่าเหตุใดจึงเกิดความล้มเหลว

สวิฟต์

หมายเหตุ: ผลิตภัณฑ์ Firebase นี้ไม่มีอยู่ในเป้าหมาย App Clip
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!")
  }
}

วัตถุประสงค์-C

หมายเหตุ: ผลิตภัณฑ์ Firebase นี้ไม่มีอยู่ในเป้าหมาย App Clip
[[[_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 บนผู้ฟังไม่ได้ลบผู้ฟังที่ลงทะเบียนบนโหนดย่อยโดยอัตโนมัติ คุณต้องติดตามการอ้างอิงหรือหมายเลขอ้างอิงเหล่านั้นเพื่อลบออก

บันทึกข้อมูลเป็นธุรกรรม

เมื่อทำงานกับข้อมูลที่อาจเสียหายจากการแก้ไขพร้อมกัน เช่น ตัวนับส่วนเพิ่ม คุณสามารถใช้ การดำเนินการธุรกรรม ได้ คุณให้อาร์กิวเมนต์สองข้อแก่การดำเนินการนี้: ฟังก์ชันอัปเดตและการเรียกกลับที่สมบูรณ์ซึ่งเป็นทางเลือก ฟังก์ชันอัปเดตใช้สถานะปัจจุบันของข้อมูลเป็นอาร์กิวเมนต์และส่งคืนสถานะใหม่ที่คุณต้องการเขียน

ตัวอย่างเช่น ในตัวอย่างแอปโซเชียลบล็อก คุณสามารถอนุญาตให้ผู้ใช้ติดดาวและเลิกติดดาวโพสต์ และติดตามจำนวนดาวที่โพสต์ได้รับดังนี้:

สวิฟต์

หมายเหตุ: ผลิตภัณฑ์ Firebase นี้ไม่มีอยู่ในเป้าหมาย App Clip
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)
  }
}

วัตถุประสงค์-C

หมายเหตุ: ผลิตภัณฑ์ Firebase นี้ไม่มีอยู่ในเป้าหมาย App Clip
[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 หากไม่มีเลย เซิร์ฟเวอร์จะเปรียบเทียบค่าเริ่มต้นกับมูลค่าปัจจุบันและยอมรับการทำธุรกรรมหากค่าตรงกันหรือปฏิเสธ หากธุรกรรมถูกปฏิเสธ เซิร์ฟเวอร์จะส่งกลับค่าปัจจุบันไปยังไคลเอ็นต์ ซึ่งจะเรียกใช้ธุรกรรมอีกครั้งด้วยมูลค่าที่อัปเดต สิ่งนี้จะเกิดขึ้นซ้ำๆ จนกว่าธุรกรรมจะได้รับการยอมรับหรือมีการพยายามหลายครั้งเกินไป

Atomic ฝั่งเซิร์ฟเวอร์ที่เพิ่มขึ้น

ในกรณีการใช้งานข้างต้น เรากำลังเขียนค่าสองค่าลงในฐานข้อมูล: ID ของผู้ใช้ที่ติดดาว/เลิกติดดาวโพสต์ และจำนวนดาวที่เพิ่มขึ้น หากเราทราบอยู่แล้วว่าผู้ใช้กำลังแสดงโพสต์ เราสามารถใช้การดำเนินการเพิ่มปรมาณูแทนธุรกรรมได้

สวิฟต์

หมายเหตุ: ผลิตภัณฑ์ Firebase นี้ไม่มีอยู่ในเป้าหมาย App Clip
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);

วัตถุประสงค์-C

หมายเหตุ: ผลิตภัณฑ์ Firebase นี้ไม่มีอยู่ในเป้าหมาย App Clip
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 จะซิงโครไนซ์ข้อมูลนั้นกับเซิร์ฟเวอร์ฐานข้อมูลระยะไกลและกับไคลเอนต์อื่นๆ บนพื้นฐาน "ความพยายามอย่างดีที่สุด"

ด้วยเหตุนี้ การเขียนทั้งหมดไปยังฐานข้อมูลจะทริกเกอร์เหตุการณ์ในเครื่องทันที ก่อนที่ข้อมูลใดๆ จะถูกเขียนไปยังเซิร์ฟเวอร์ ซึ่งหมายความว่าแอปของคุณยังคงตอบสนองโดยไม่คำนึงถึงเวลาแฝงของเครือข่ายหรือการเชื่อมต่อ

เมื่อสร้างการเชื่อมต่อใหม่แล้ว แอปของคุณจะได้รับชุดเหตุการณ์ที่เหมาะสมเพื่อให้ไคลเอนต์ซิงค์กับสถานะเซิร์ฟเวอร์ปัจจุบัน โดยไม่ต้องเขียนโค้ดที่กำหนดเองใดๆ

เราจะพูดถึงพฤติกรรมออฟไลน์เพิ่มเติมใน เรียนรู้เพิ่มเติมเกี่ยวกับความสามารถออนไลน์และออฟไลน์

ขั้นตอนถัดไป