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

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

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

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

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

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

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

รับ FIRDatabaseReference

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

SwiftObjective-C

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

ref = Database.database().reference()

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

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

เขียนข้อมูล

เอกสารนี้อธิบายข้อมูลเบื้องต้นเกี่ยวกับการอ่านและเขียนข้อมูล Firebase

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

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

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

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

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

SwiftObjective-C

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

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

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

SwiftObjective-C

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

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

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

SwiftObjective-C

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

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

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

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

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

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

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

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

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

SwiftObjective-C

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

หมายเหตุ: ผลิตภัณฑ์ 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 แบบเรียลไทม์ตามที่แสดงด้านบน

อ่านข้อมูล 1 ครั้งด้วยเครื่องมือตรวจสอบ

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

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

SwiftObjective-C

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

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

SwiftObjective-C

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

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

SwiftObjective-C

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

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

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

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

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

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

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

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

SwiftObjective-C

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

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

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

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

SwiftObjective-C

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

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

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

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

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

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