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

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

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

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

การใช้โปรแกรมจำลอง Realtime Database มีเพียงไม่กี่ขั้นตอน ดังนี้

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

มีคำแนะนำแบบทีละขั้นตอนโดยละเอียดเกี่ยวกับ Realtime Database และ Cloud Functions นอกจากนี้ คุณควรอ่านLocal Emulator Suite ข้อมูลเบื้องต้นด้วย

รับ 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];

เขียนข้อมูล

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

ระบบจะเขียนข้อมูล Firebase ไปยังDatabaseการอ้างอิงและเรียกข้อมูลโดย แนบ Listener แบบอะซิงโครนัสกับการอ้างอิง ระบบจะเรียกใช้ Listener 1 ครั้งสำหรับสถานะเริ่มต้นของข้อมูล และอีกครั้งเมื่อใดก็ตามที่ข้อมูลมีการเปลี่ยนแปลง

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

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

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

เช่น คุณเพิ่มผู้ใช้ที่มี setValue ได้ดังนี้

Swift

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

Objective-C

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

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

Swift

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

Objective-C

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

อ่านข้อมูล

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

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

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

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

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

Swift

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

Objective-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() ไม่สามารถแสดงค่าเซิร์ฟเวอร์ได้ไม่ว่าจะด้วยเหตุผลใดก็ตาม ไคลเอ็นต์จะตรวจสอบแคชในพื้นที่เก็บข้อมูลและแสดงข้อผิดพลาด หากยังไม่พบค่า

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

Swift

หมายเหตุ: ผลิตภัณฑ์ Firebase นี้ไม่พร้อมใช้งานในเป้าหมาย App Clip
do {
  let snapshot = try await ref.child("users/\(uid)/username").getData()
  let userName = snapshot.value as? String ?? "Unknown"
} catch {
  print(error)
}

Objective-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() โดยไม่จำเป็นอาจเพิ่มการใช้แบนด์วิดท์และทำให้ประสิทธิภาพลดลง ซึ่งป้องกันได้โดยใช้เครื่องมือฟังแบบเรียลไทม์ตามที่แสดง ด้านบน

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

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

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

Swift

หมายเหตุ: ผลิตภัณฑ์ 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)
}

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

Swift

หมายเหตุ: ผลิตภัณฑ์ 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)

Objective-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() จากนั้นจะใช้คีย์เพื่อสร้างรายการที่ 2 ในโพสต์ของผู้ใช้ที่ /user-posts/$userid/$postid ได้

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

เพิ่มบล็อกการทำเครื่องหมายว่าเสร็จสิ้น

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

Swift

หมายเหตุ: ผลิตภัณฑ์ Firebase นี้ไม่พร้อมใช้งานในเป้าหมาย App Clip
do {
  try await ref.child("users").child(user.uid).setValue(["username": username])
  print("Data saved successfully!")
} catch {
  print("Data could not be saved: \(error).")
}

Objective-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 ครั้งเดียว

ยกเลิกการเชื่อมต่อ Listener

ผู้สังเกตการณ์จะไม่หยุดซิงค์ข้อมูลโดยอัตโนมัติเมื่อคุณออกจาก ViewController หากไม่ได้นำ Observer ออกอย่างถูกต้อง Observer จะยังคงซิงค์ ข้อมูลไปยังหน่วยความจำในเครื่องต่อไป เมื่อไม่ต้องการใช้ Observer แล้ว ให้นำออกโดยส่งFIRDatabaseHandleที่เกี่ยวข้องไปยังเมธอด removeObserverWithHandle

เมื่อเพิ่มบล็อกการโทรกลับในการอ้างอิง ระบบจะแสดงผล FIRDatabaseHandle คุณใช้แฮนเดิลเหล่านี้เพื่อนำการบล็อกการเรียกกลับออกได้

หากมีการเพิ่ม Listener หลายรายการลงในการอ้างอิงฐานข้อมูล ระบบจะเรียกใช้ Listener แต่ละรายการเมื่อมีการเรียกเหตุการณ์ หากต้องการหยุดซิงค์ข้อมูลในตำแหน่งนั้น คุณต้องนำผู้สังเกตการณ์ทั้งหมดในตำแหน่งออกโดยเรียกใช้เมธอด removeAllObservers

การเรียกใช้ removeObserverWithHandle หรือ removeAllObservers ใน Listener จะไม่นำ Listener ที่ลงทะเบียนในโหนดย่อยออกโดยอัตโนมัติ คุณต้องติดตามการอ้างอิงหรือแฮนเดิลเหล่านั้นเพื่อนำออกด้วย

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

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

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

Swift

หมายเหตุ: ผลิตภัณฑ์ 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)
  }
}

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

การเพิ่มฝั่งเซิร์ฟเวอร์แบบอะตอม

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

Swift

หมายเหตุ: ผลิตภัณฑ์ 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)

Objective-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 จะซิงโครไนซ์ข้อมูลดังกล่าวกับเซิร์ฟเวอร์ฐานข้อมูลระยะไกล และกับไคลเอ็นต์อื่นๆ โดยพยายามอย่างเต็มที่

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

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

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

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