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

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

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

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

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

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

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

รับ 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 เพื่ออ่านข้อมูลในเส้นทางที่ระบุได้ ตามที่มีอยู่ ณ เวลาที่เกิดเหตุการณ์ ระบบจะทริกเกอร์วิธีการนี้ครั้งเดียวเมื่อมีการแนบผู้ฟัง และอีกครั้งทุกครั้งที่มีการเปลี่ยนแปลงข้อมูลรวมถึงรายการย่อย Callback ของเหตุการณ์จะส่งผ่าน 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;
  // ...
}];

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

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

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

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

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

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

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

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

อ่านข้อมูลครั้งเดียวกับผู้สังเกตการณ์

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

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 เดียวได้

ปลดผู้ฟังออก

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

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

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

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

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

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

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

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

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

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

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

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