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

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

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

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

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

  1. การเพิ่มบรรทัดโค้ดลงในการกำหนดค่าทดสอบของแอปเพื่อเชื่อมต่อกับโปรแกรมจำลอง
  2. จากรากของไดเร็กทอรีโปรเจ็กต์ในเครื่องของคุณ ให้รัน firebase emulators:start
  3. โทรออกจากโค้ดต้นแบบของแอปโดยใช้ Realtime Database Platform SDK ตามปกติ หรือใช้ 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 และดึงข้อมูลโดยการแนบ Listener แบบอะซิงโครนัสเข้ากับการอ้างอิง Listener จะถูกทริกเกอร์หนึ่งครั้งสำหรับสถานะเริ่มต้นของข้อมูล และอีกครั้งทุกครั้งที่ข้อมูลมีการเปลี่ยนแปลง

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

สำหรับการดำเนินการเขียนขั้นพื้นฐาน คุณสามารถใช้ 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
do {
  let snapshot = try await ref.child("users/\(uid)/username").getData()
  let userName = snapshot.value as? String ?? "Unknown"
} catch {
  print(error)
}

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

สวิฟท์

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

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

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

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

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

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

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

สวิฟท์

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

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

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

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

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

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

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