(ไม่บังคับ) สร้างต้นแบบและทดสอบกับ Firebase Local Emulator Suite
ก่อนที่จะพูดถึงวิธีที่แอปของคุณอ่านและเขียนไปยัง Realtime Database เรามาแนะนำชุดเครื่องมือที่คุณสามารถใช้สร้างต้นแบบและทดสอบ Realtime Database กัน ฟังก์ชัน: Firebase Local Emulator Suite หากคุณกำลังลองใช้ข้อมูลอื่น โมเดล, การเพิ่มประสิทธิภาพของกฎความปลอดภัย หรือการทำงานเพื่อค้นหา วิธีที่คุ้มค่าในการโต้ตอบกับระบบหลังบ้าน ทำให้สามารถทำงานในองค์กรได้ โดยไม่ต้องใช้บริการแบบสดอาจเป็นความคิดที่ดี
โปรแกรมจำลอง Realtime Database เป็นส่วนหนึ่งของ Local Emulator Suite ซึ่ง ทำให้แอปของคุณสามารถโต้ตอบกับเนื้อหาและการกำหนดค่าฐานข้อมูลที่จำลอง รวมทั้งทรัพยากรโครงการจำลอง (ฟังก์ชัน ฐานข้อมูลอื่นๆ และกฎความปลอดภัย)
การใช้โปรแกรมจำลอง Realtime Database มีเพียงไม่กี่ขั้นตอนดังนี้
- การเพิ่มบรรทัดโค้ดลงในการกำหนดค่าการทดสอบของแอปเพื่อเชื่อมต่อกับโปรแกรมจำลอง
- จากรูทของไดเรกทอรีโปรเจ็กต์ในเครื่อง โดยเรียกใช้
firebase emulators:start
- การเรียกใช้จากโค้ดต้นแบบของแอปโดยใช้แพลตฟอร์ม Realtime Database SDK ตามปกติ หรือใช้ Realtime Database REST API
สามารถดูคำแนะนำแบบทีละขั้นเกี่ยวกับ Realtime Database และ Cloud Functions โดยละเอียดได้ นอกจากนี้ คุณควรดูLocal Emulator Suite ข้อมูลเบื้องต้นด้วย
รับ FIRDatabaseReference
หากต้องการอ่านหรือเขียนข้อมูลจากฐานข้อมูล คุณต้องมีอินสแตนซ์ของ FIRDatabaseReference
ดังนี้
Swift
var ref: DatabaseReference! ref = Database.database().reference()
Objective-C
@property (strong, nonatomic) FIRDatabaseReference *ref; self.ref = [[FIRDatabase database] reference];
เขียนข้อมูล
เอกสารนี้ครอบคลุมพื้นฐานการอ่านและการเขียนข้อมูล Firebase
ข้อมูล Firebase เขียนไปยังข้อมูลอ้างอิง Database
และดึงข้อมูลโดย
การแนบ Listener แบบไม่พร้อมกันลงในข้อมูลอ้างอิง โปรแกรมฟังจะทริกเกอร์ 1 ครั้งสําหรับสถานะเริ่มต้นของข้อมูล และทริกเกอร์อีกครั้งเมื่อใดก็ตามที่ข้อมูลมีการเปลี่ยนแปลง
การดำเนินการเขียนพื้นฐาน
สำหรับการดำเนินการเขียนพื้นฐาน คุณสามารถใช้ setValue
เพื่อบันทึกข้อมูลไปยัง
โดยแทนที่ข้อมูลที่มีอยู่ในเส้นทางนั้น คุณใช้วิธีการนี้เพื่อทำสิ่งต่อไปนี้ได้
- ประเภทพาสที่สอดคล้องกับประเภท JSON ที่พร้อมใช้งานมีดังนี้
NSString
NSNumber
NSDictionary
NSArray
เช่น เพิ่มผู้ใช้ด้วย setValue
ดังนี้
Swift
self.ref.child("users").child(user.uid).setValue(["username": username])
Objective-C
[[[self.ref child:@"users"] child:authResult.user.uid] setValue:@{@"username": username}];
การใช้ setValue
ในลักษณะนี้จะเขียนทับข้อมูลในตำแหน่งที่ระบุ
รวมถึงโหนดย่อย อย่างไรก็ตาม คุณยังอัปเดตรายการย่อยได้โดยไม่ต้อง
การเขียนออบเจ็กต์ใหม่ทั้งหมด หากต้องการอนุญาตให้ผู้ใช้อัปเดตโปรไฟล์ของตนเอง
คุณสามารถอัปเดตชื่อผู้ใช้ได้ดังนี้
Swift
self.ref.child("users/\(user.uid)/username").setValue(username)
Objective-C
[[[[_ref child:@"users"] child:user.uid] child:@"username"] setValue:username];
อ่านข้อมูล
อ่านข้อมูลโดยการฟังเหตุการณ์มูลค่า
ในการอ่านข้อมูลที่เส้นทางและฟังการเปลี่ยนแปลง ให้ใช้
observeEventType:withBlock
จาก FIRDatabaseReference
เพื่อสังเกตการณ์
FIRDataEventTypeValue
กิจกรรม
ประเภทเหตุการณ์ | การใช้งานทั่วไป |
---|---|
FIRDataEventTypeValue |
อ่านและรับฟังการเปลี่ยนแปลงเนื้อหาทั้งหมดของเส้นทาง |
คุณสามารถใช้เหตุการณ์ FIRDataEventTypeValue
เพื่ออ่านข้อมูลในเส้นทางที่ระบุ
ตามที่มีอยู่ ณ เวลาที่เกิดเหตุการณ์ เมธอดนี้จะถูกเรียกใช้ครั้งเดียวเมื่อ
Listener จะแนบอยู่และอีกครั้งทุกครั้งที่
ข้อมูล รวมถึงเด็กด้วย
การเปลี่ยนแปลง จะมีการส่ง snapshot
ที่มีข้อมูลทั้งหมดในตำแหน่งนั้น รวมถึงข้อมูลย่อย ไปยังการเรียกกลับเหตุการณ์ หากไม่มีข้อมูล สแนปชอตจะแสดงfalse
เมื่อคุณเรียกใช้ exists()
และ nil
เมื่อคุณอ่านพร็อพเพอร์ตี้ value
ตัวอย่างต่อไปนี้แสดงแอปพลิเคชันการเขียนบล็อกโซเชียลที่เรียก รายละเอียดของโพสต์จากฐานข้อมูล:
Swift
refHandle = postRef.observe(DataEventType.value, with: { snapshot in // ... })
Objective-C
_refHandle = [_postRef observeEventType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot * _Nonnull snapshot) { NSDictionary *postDict = snapshot.value; // ... }];
Listener จะได้รับ FIRDataSnapshot
ที่มีข้อมูลตามที่ระบุ
ตำแหน่งในฐานข้อมูล ณ เวลาที่เกิดเหตุการณ์ในพร็อพเพอร์ตี้ value
คุณ
กำหนดค่าให้กับประเภทเนทีฟที่เหมาะสมได้ เช่น NSDictionary
หากไม่มีข้อมูลที่อยู่ที่ตำแหน่งดังกล่าว value
จะเป็น nil
อ่านข้อมูลครั้งเดียว
อ่านครั้งเดียวโดยใช้ getData()
SDK ออกแบบมาเพื่อจัดการการโต้ตอบกับเซิร์ฟเวอร์ฐานข้อมูล ไม่ว่าจะเป็น ออนไลน์หรือออฟไลน์
โดยทั่วไป คุณควรใช้เทคนิคเหตุการณ์มูลค่าที่อธิบายไว้ข้างต้นในการอ่าน เพื่อรับการแจ้งเตือนเมื่อมีการอัปเดตข้อมูลจากแบ็กเอนด์ เทคนิคเหล่านั้น ลดการใช้งานและการเรียกเก็บเงินของคุณ และได้รับการเพิ่มประสิทธิภาพเพื่อช่วยให้ผู้ใช้ ขณะออนไลน์และออฟไลน์
หากต้องการข้อมูลเพียงครั้งเดียว คุณสามารถใช้ getData()
เพื่อดูภาพรวมของ
จากฐานข้อมูล หาก getData()
ไม่สามารถแสดงผล
ค่าของเซิร์ฟเวอร์ ไคลเอ็นต์จะตรวจสอบแคชที่จัดเก็บในตัวเครื่อง และแสดงผลข้อผิดพลาด
หากยังไม่พบค่า
ตัวอย่างต่อไปนี้แสดงการดึงข้อมูลชื่อผู้ใช้ที่เปิดเผยต่อสาธารณะของผู้ใช้ เพียงครั้งเดียวจากฐานข้อมูล:
Swift
do { let snapshot = try await ref.child("users/\(uid)/username").getData() let userName = snapshot.value as? String ?? "Unknown" } catch { print(error) }
Objective-C
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
เพื่อรับข้อมูลจาก
ดิสก์แคชในเครื่องทันที
วิธีนี้เป็นประโยชน์สําหรับข้อมูลที่ต้องโหลดเพียงครั้งเดียวและไม่คาดว่าจะโหลด เปลี่ยนบ่อยหรือต้องใช้การฟังอย่างต่อเนื่อง เช่น แอปการเขียนบล็อก ในตัวอย่างก่อนหน้านี้ใช้วิธีนี้เพื่อโหลดโปรไฟล์ของผู้ใช้ เริ่มเขียนโพสต์ใหม่:
Swift
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
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
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
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
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
[[[_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
เมื่อคุณเพิ่มบล็อก Callback ไปยังข้อมูลอ้างอิง ระบบจะแสดงผล FIRDatabaseHandle
คุณใช้แฮนเดิลเหล่านี้เพื่อนำการบล็อก Callback ออกได้
ถ้ามีการเพิ่ม Listener หลายรายการไปยังการอ้างอิงฐานข้อมูล Listener แต่ละรายจะถูก
ถูกเรียกเมื่อเกิดเหตุการณ์ หากต้องการหยุดซิงค์ข้อมูลในตำแหน่งนั้น คุณต้องนำผู้สังเกตการณ์ทั้งหมดในตำแหน่งนั้นออกโดยเรียกใช้เมธอด removeAllObservers
การเรียก removeObserverWithHandle
หรือ removeAllObservers
ในการฟัง
ไม่นำ Listener ที่ลงทะเบียนในโหนดย่อยออกโดยอัตโนมัติ คุณยังต้อง
ติดตามข้อมูลอ้างอิงหรือแฮนเดิลเหล่านั้นเพื่อนำออก
บันทึกข้อมูลเป็นธุรกรรม
เมื่อทํางานกับข้อมูลที่อาจเสียหายจากการแก้ไขพร้อมกัน เช่น ตัวนับที่เพิ่มขึ้น คุณสามารถใช้การดำเนินการธุรกรรม คุณจะส่งอาร์กิวเมนต์ 2 รายการให้กับการดำเนินการนี้ ได้แก่ ฟังก์ชันอัปเดตและฟังก์ชันการติดต่อกลับเมื่อดำเนินการเสร็จสิ้น (ไม่บังคับ) ฟังก์ชันอัปเดตจะใช้สถานะปัจจุบันของข้อมูลเป็น อาร์กิวเมนต์ และแสดงสถานะใหม่ที่ต้องการเขียน
เช่น ในตัวอย่างแอปการเขียนบล็อกโซเชียล คุณสามารถอนุญาตให้ผู้ใช้ ติดดาวและเลิกติดดาวโพสต์ และติดตามจำนวนดาวที่โพสต์ได้รับ ดังนี้
Swift
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
[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
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
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 จะซิงค์ข้อมูลนั้นกับฐานข้อมูลระยะไกล เซิร์ฟเวอร์และไคลเอ็นต์อื่นๆ อย่าง "สุดความสามารถ" พื้นฐาน
ด้วยเหตุนี้ การเขียนทั้งหมดลงในฐานข้อมูลจึงทริกเกอร์เหตุการณ์ในเครื่องทันทีก่อนที่จะมีการเขียนข้อมูลลงในเซิร์ฟเวอร์ ซึ่งหมายความว่าแอปของคุณจะยัง ตอบสนองตามอุปกรณ์โดยไม่คำนึงถึงเวลาในการตอบสนองหรือการเชื่อมต่อของเครือข่าย
เมื่อเชื่อมต่ออินเทอร์เน็ตอีกครั้งแล้ว แอปจะได้รับชุด เหตุการณ์เพื่อให้ไคลเอ็นต์ซิงค์กับสถานะเซิร์ฟเวอร์ปัจจุบันโดยไม่ต้อง เขียนโค้ดที่กำหนดเอง
เราจะพูดคุยเพิ่มเติมเกี่ยวกับพฤติกรรมออฟไลน์ใน ดูข้อมูลเพิ่มเติมเกี่ยวกับความสามารถในการทำงานแบบออนไลน์และออฟไลน์