แอปพลิเคชัน Firebase จะทำงานได้แม้แอปของคุณจะสูญเสียเครือข่ายชั่วคราว การเชื่อมต่อ นอกจากนี้ Firebase ยังมีเครื่องมือสำหรับ การยืนยันข้อมูลภายในเครื่อง ในการจัดการการตรวจหาบุคคลในบ้าน และการจัดการกับเวลาในการตอบสนอง
ความต่อเนื่องของดิสก์
แอป Firebase จะจัดการการหยุดชะงักของเครือข่ายชั่วคราวโดยอัตโนมัติ ข้อมูลแคชจะใช้งานได้ขณะที่ออฟไลน์และ Firebase จะส่งการเขียนซ้ำ เมื่อการเชื่อมต่อเครือข่ายกลับมาทำงานอีกครั้ง
เมื่อคุณเปิดใช้การคงอยู่ของดิสก์ แอปจะเขียนข้อมูลภายในเครื่องไปยัง อุปกรณ์ของคุณเพื่อให้แอปรักษาสถานะขณะออฟไลน์ได้แม้ว่าผู้ใช้จะ หรือระบบปฏิบัติการรีสตาร์ทแอป
คุณเปิดใช้การคงอยู่ของดิสก์ได้ด้วยโค้ดเพียงบรรทัดเดียว
Swift
Database.database().isPersistenceEnabled = true
Objective-C
[FIRDatabase database].persistenceEnabled = YES;
พฤติกรรมอย่างต่อเนื่อง
เมื่อเปิดใช้การถาวร ข้อมูลทั้งหมดที่ไคลเอ็นต์ฐานข้อมูลเรียลไทม์ของ Firebase จะซิงค์ขณะที่ออนไลน์ยังคงอยู่ที่ดิสก์และพร้อมใช้งานแบบออฟไลน์ แม้ว่าผู้ใช้หรือระบบปฏิบัติการจะรีสตาร์ทแอปก็ตาม ซึ่งหมายความว่า แอปพลิเคชันจะทำงานเช่นเดียวกับที่ออนไลน์โดยใช้ข้อมูลในตัวเครื่องที่จัดเก็บในแคช Callback ของผู้ฟังจะยังคงเริ่มทำงานต่อไปเพื่ออัปเดตในพื้นที่
ไคลเอ็นต์ฐานข้อมูลเรียลไทม์ของ Firebase จะเก็บคิวของรายการทั้งหมดโดยอัตโนมัติ เขียนการดำเนินการที่เกิดขึ้นขณะที่แอปของคุณออฟไลน์ เมื่อเปิดใช้การถาวร คิวนี้จะคงอยู่ในดิสก์ด้วย ของการเขียนจะพร้อมใช้งานเมื่อผู้ใช้หรือระบบปฏิบัติการ รีสตาร์ทแอป เมื่อแอปกลับมาเชื่อมต่อได้อีกครั้ง ระบบจะส่งการดำเนินการไปยังเซิร์ฟเวอร์ฐานข้อมูลเรียลไทม์ของ Firebase
หากแอปของคุณใช้ การตรวจสอบสิทธิ์ของ Firebase ไคลเอ็นต์ฐานข้อมูลเรียลไทม์ของ Firebase จะยังคงตรวจสอบสิทธิ์ของผู้ใช้อยู่ โทเค็นในการรีสตาร์ทแอป หากโทเค็นการตรวจสอบสิทธิ์หมดอายุขณะที่แอปของคุณออฟไลน์อยู่ ไคลเอ็นต์จะหยุดชั่วคราว เขียนการดำเนินการจนกว่าแอปจะตรวจสอบสิทธิ์ผู้ใช้อีกครั้ง มิฉะนั้น การดำเนินการเขียนอาจล้มเหลวเนื่องจากกฎความปลอดภัย
อัปเดตข้อมูลให้ใหม่อยู่เสมอ
ฐานข้อมูลเรียลไทม์ของ Firebase จะซิงค์และจัดเก็บสำเนาข้อมูล ข้อมูลของผู้ฟังที่ฟังอยู่ นอกจากนี้ คุณสามารถเก็บสถานที่ตั้ง ซิงค์กัน
Swift
let scoresRef = Database.database().reference(withPath: "scores") scoresRef.keepSynced(true)
Objective-C
FIRDatabaseReference *scoresRef = [[FIRDatabase database] referenceWithPath:@"scores"]; [scoresRef keepSynced:YES];
ไคลเอ็นต์ฐานข้อมูลเรียลไทม์ของ Firebase จะดาวน์โหลดข้อมูลโดยอัตโนมัติที่ ตำแหน่งเหล่านี้และทำให้ข้อมูลตรงกัน แม้ว่าข้อมูลอ้างอิงจะไม่มี ผู้ฟังที่ฟังอยู่ คุณสามารถปิดการซิงค์ข้อมูลอีกครั้งได้ด้วย บรรทัดโค้ดต่อไปนี้
Swift
scoresRef.keepSynced(false)
Objective-C
[scoresRef keepSynced:NO];
โดยค่าเริ่มต้น ระบบจะแคชข้อมูลที่ซิงค์ไว้ก่อนหน้านี้ 10 MB ควรเป็น เพียงพอสำหรับแอปพลิเคชันส่วนใหญ่ หากแคชมีขนาดใหญ่กว่าขนาดที่กำหนดค่าไว้ ฐานข้อมูลเรียลไทม์ของ Firebase จะลบข้อมูลที่ใช้ล่าสุดออกอย่างถาวร โดยข้อมูลที่ซิงค์ไว้จะไม่ถูกลบออกจากแคชถาวร
การค้นหาข้อมูลแบบออฟไลน์
ฐานข้อมูลเรียลไทม์ของ Firebase จัดเก็บข้อมูลที่แสดงผลจากการค้นหาเพื่อใช้งาน เมื่อออฟไลน์ สำหรับการค้นหาที่สร้างขึ้นขณะออฟไลน์ ฐานข้อมูลเรียลไทม์ของ Firebase จะยังทำงานต่อไปสำหรับข้อมูลที่โหลดก่อนหน้านี้ หากข้อมูลที่ขอไม่โหลด ฐานข้อมูลเรียลไทม์ของ Firebase จะโหลดขึ้นมา จากแคชในเครื่อง เมื่อการเชื่อมต่อเครือข่ายใช้งานได้อีกครั้ง จะโหลดข้อมูลและจะแสดงการค้นหา
ตัวอย่างเช่น โค้ดนี้จะสอบถามถึงพารามิเตอร์ 4 รายการในฐานข้อมูลคะแนนแบบเรียลไทม์ของ Firebase
Swift
let scoresRef = Database.database().reference(withPath: "scores") scoresRef.queryOrderedByValue().queryLimited(toLast: 4).observe(.childAdded) { snapshot in print("The \(snapshot.key) dinosaur's score is \(snapshot.value ?? "null")") }
Objective-C
FIRDatabaseReference *scoresRef = [[FIRDatabase database] referenceWithPath:@"scores"]; [[[scoresRef queryOrderedByValue] queryLimitedToLast:4] observeEventType:FIRDataEventTypeChildAdded withBlock:^(FIRDataSnapshot *snapshot) { NSLog(@"The %@ dinosaur's score is %@", snapshot.key, snapshot.value); }];
สมมติว่าผู้ใช้ขาดการเชื่อมต่อ ออฟไลน์ และรีสตาร์ทแอป ขณะที่ออฟไลน์ แอปจะค้นหาสำหรับ 2 รายการสุดท้ายจาก สถานที่เดียวกัน การค้นหานี้จะแสดง 2 รายการสุดท้ายได้สำเร็จ เพราะแอปโหลดขึ้นมาทั้ง 4 รายการในการค้นหาด้านบน
Swift
scoresRef.queryOrderedByValue().queryLimited(toLast: 2).observe(.childAdded) { snapshot in print("The \(snapshot.key) dinosaur's score is \(snapshot.value ?? "null")") }
Objective-C
[[[scoresRef queryOrderedByValue] queryLimitedToLast:2] observeEventType:FIRDataEventTypeChildAdded withBlock:^(FIRDataSnapshot *snapshot) { NSLog(@"The %@ dinosaur's score is %@", snapshot.key, snapshot.value); }];
ในตัวอย่างก่อนหน้านี้ ไคลเอ็นต์ฐานข้อมูลเรียลไทม์ของ Firebase ระดมทุน "เพิ่มเด็ก" เหตุการณ์สำหรับไดโนเสาร์ 2 ตัวที่ทำคะแนนสูงสุดโดยใช้ แคชที่เก็บไว้ แต่จะไม่เพิ่ม "ค่า" เนื่องจากแอป ไม่เคยดำเนินการค้นหานั้นขณะที่ออนไลน์
หากแอปจะขอรายการ 6 รายการล่าสุดขณะออฟไลน์ "เพิ่มเด็ก" เหตุการณ์ของทั้ง 4 รายการที่แคชไว้ได้ทันที เมื่อ อุปกรณ์กลับมาออนไลน์อีกครั้ง โดยไคลเอ็นต์ฐานข้อมูลเรียลไทม์ของ Firebase จะซิงค์ กับเซิร์ฟเวอร์ แล้วส่ง "เพิ่มเด็ก" 2 คนสุดท้าย และ "value" กิจกรรมของแอป
การจัดการธุรกรรมแบบออฟไลน์
ธุรกรรมที่เกิดขึ้นขณะที่แอปออฟไลน์จะอยู่ในคิว เมื่อแอปกลับมาเชื่อมต่อเครือข่ายได้ ระบบจะส่งธุรกรรมไปยัง เซิร์ฟเวอร์ Realtime Database
การจัดการการตรวจหาบุคคล
ในแอปพลิเคชันแบบเรียลไทม์ การตรวจหาว่าไคลเอ็นต์เมื่อใด เชื่อมต่อและยกเลิกการเชื่อมต่อ ตัวอย่างเช่น คุณอาจ ต้องการทำเครื่องหมายผู้ใช้ว่า "ออฟไลน์" เมื่อลูกค้ายกเลิกการเชื่อมต่อ
ไคลเอ็นต์ฐานข้อมูลของ Firebase ระบุค่าพื้นฐานง่ายๆ ซึ่งคุณสามารถใช้เพื่อ เขียนลงในฐานข้อมูลเมื่อไคลเอ็นต์ยกเลิกการเชื่อมต่อจากฐานข้อมูล Firebase เซิร์ฟเวอร์ การอัปเดตเหล่านี้จะเกิดขึ้นไม่ว่าไคลเอ็นต์จะตัดการเชื่อมต่ออย่างชัดเจนหรือไม่ คุณจึงไว้วางใจให้ล้างข้อมูลได้แม้การเชื่อมต่อจะหลุด หรือไคลเอ็นต์ขัดข้อง การดำเนินการเขียนทั้งหมด รวมถึงการตั้งค่า การอัปเดตและการนำออก สามารถดำเนินการได้เมื่อยกเลิกการเชื่อมต่อ
ต่อไปนี้เป็นตัวอย่างง่ายๆ ของการเขียนข้อมูลเมื่อยกเลิกการเชื่อมต่อโดยใช้
onDisconnect
พื้นฐาน:
Swift
let presenceRef = Database.database().reference(withPath: "disconnectmessage"); // Write a string when this client loses connection presenceRef.onDisconnectSetValue("I disconnected!")
Objective-C
FIRDatabaseReference *presenceRef = [[FIRDatabase database] referenceWithPath:@"disconnectmessage"]; // Write a string when this client loses connection [presenceRef onDisconnectSetValue:@"I disconnected!"];
วิธีการทำงานของการยกเลิกการเชื่อมต่อ
เมื่อคุณสร้างการดำเนินการ onDisconnect()
อยู่บนเซิร์ฟเวอร์ฐานข้อมูลเรียลไทม์ของ Firebase เซิร์ฟเวอร์จะตรวจสอบความปลอดภัยเพื่อ
ตรวจสอบว่าผู้ใช้สามารถทำกิจกรรมการเขียนตามที่ขอได้ แล้วแจ้งข้อมูล
ในแอปของคุณหากแอปไม่ถูกต้อง จากนั้นเซิร์ฟเวอร์
ติดตามดูการเชื่อมต่อ หากการเชื่อมต่ออินเทอร์เน็ตหมดเวลา หรือ
ที่ไคลเอ็นต์ Realtime Database ปิดอย่างต่อเนื่อง เซิร์ฟเวอร์จะตรวจสอบความปลอดภัย
ครั้งที่ 2 (เพื่อให้มั่นใจว่าการดำเนินการยังคงถูกต้อง) แล้วจึงเรียกใช้
กิจกรรมนั้น
แอปของคุณสามารถใช้ Callback ในการดำเนินการเขียน
เพื่อตรวจสอบว่าการแนบ onDisconnect
ถูกต้อง
Swift
presenceRef.onDisconnectRemoveValue { error, reference in if let error = error { print("Could not establish onDisconnect event: \(error)") } }
Objective-C
[presenceRef onDisconnectRemoveValueWithCompletionBlock:^(NSError *error, FIRDatabaseReference *reference) { if (error != nil) { NSLog(@"Could not establish onDisconnect event: %@", error); } }];
คุณยกเลิกกิจกรรมของ onDisconnect
ได้โดยโทรหา .cancel()
ดังนี้
Swift
presenceRef.onDisconnectSetValue("I disconnected") // some time later when we change our minds presenceRef.cancelDisconnectOperations()
Objective-C
[presenceRef onDisconnectSetValue:@"I disconnected"]; // some time later when we change our minds [presenceRef cancelDisconnectOperations];
กำลังตรวจหาสถานะการเชื่อมต่อ
สำหรับฟีเจอร์มากมายที่เกี่ยวข้องกับการตรวจหาบุคคลในบ้าน ความสามารถนี้มีประโยชน์สำหรับแอปของคุณ
เพื่อรู้ว่าออนไลน์หรือออฟไลน์เมื่อใด ฐานข้อมูลเรียลไทม์ของ Firebase
ระบุสถานที่พิเศษที่ /.info/connected
ซึ่ง
จะอัปเดตทุกครั้งที่สถานะการเชื่อมต่อของไคลเอ็นต์ฐานข้อมูลเรียลไทม์ของ Firebase
การเปลี่ยนแปลง มีตัวอย่างดังต่อไปนี้
Swift
let connectedRef = Database.database().reference(withPath: ".info/connected") connectedRef.observe(.value, with: { snapshot in if snapshot.value as? Bool ?? false { print("Connected") } else { print("Not connected") } })
Objective-C
FIRDatabaseReference *connectedRef = [[FIRDatabase database] referenceWithPath:@".info/connected"]; [connectedRef observeEventType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot *snapshot) { if([snapshot.value boolValue]) { NSLog(@"connected"); } else { NSLog(@"not connected"); } }];
/.info/connected
เป็นค่าบูลีนที่ไม่ใช่
ซิงค์ระหว่างไคลเอ็นต์ Realtime Database แล้วเนื่องจากค่านี้
ขึ้นอยู่กับสถานะของไคลเอ็นต์ กล่าวคือ หากลูกค้ารายหนึ่ง
อ่านว่า /.info/connected
เป็นเท็จ นี่ไม่ใช่
ให้รับประกันว่าไคลเอ็นต์แยกต่างหากจะอ่าน false ด้วย
การจัดการเวลาในการตอบสนอง
การประทับเวลาของเซิร์ฟเวอร์
เซิร์ฟเวอร์ฐานข้อมูลเรียลไทม์ของ Firebase มีกลไกในการแทรก
การประทับเวลาที่สร้างขึ้นบนเซิร์ฟเวอร์ในรูปแบบข้อมูล ฟีเจอร์นี้เมื่อใช้ร่วมกับ
onDisconnect
ให้วิธีง่ายๆ ในการจดบันทึกอย่างน่าเชื่อถือ
เวลาที่ไคลเอ็นต์ Realtime Database ยกเลิกการเชื่อมต่อ
Swift
let userLastOnlineRef = Database.database().reference(withPath: "users/morgan/lastOnline") userLastOnlineRef.onDisconnectSetValue(ServerValue.timestamp())
Objective-C
FIRDatabaseReference *userLastOnlineRef = [[FIRDatabase database] referenceWithPath:@"users/morgan/lastOnline"]; [userLastOnlineRef onDisconnectSetValue:[FIRServerValue timestamp]];
เอียงนาฬิกา
ในขณะที่ firebase.database.ServerValue.TIMESTAMP
นั้นมีอะไรมากกว่านั้น
ถูกต้อง และเหมาะสำหรับการดำเนินการอ่าน/เขียนส่วนใหญ่
อาจมีประโยชน์ในการคาดประมาณค่าความเอียงของนาฬิกาของลูกค้า
ในเซิร์ฟเวอร์ฐานข้อมูลเรียลไทม์ของ Firebase คุณ
สามารถแนบ Callback ไปยังตำแหน่ง /.info/serverTimeOffset
เพื่อรับค่าเป็นมิลลิวินาทีที่ไคลเอ็นต์ฐานข้อมูลเรียลไทม์ของ Firebase นั้นๆ
เพิ่มลงในเวลาที่รายงานในท้องถิ่น (เวลา Epoch ในหน่วยมิลลิวินาที) เพื่อประมาณการ
เวลาของเซิร์ฟเวอร์ โปรดทราบว่าความแม่นยำของออฟเซ็ตนี้อาจได้รับผลกระทบจาก
เวลาในการตอบสนองของเครือข่าย และ มีประโยชน์สำหรับการค้นพบ
เวลาของนาฬิกามีความคลาดเคลื่อนมาก (> 1 วินาที)
Swift
let offsetRef = Database.database().reference(withPath: ".info/serverTimeOffset") offsetRef.observe(.value, with: { snapshot in if let offset = snapshot.value as? TimeInterval { print("Estimated server time in milliseconds: \(Date().timeIntervalSince1970 * 1000 + offset)") } })
Objective-C
FIRDatabaseReference *offsetRef = [[FIRDatabase database] referenceWithPath:@".info/serverTimeOffset"]; [offsetRef observeEventType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot *snapshot) { NSTimeInterval offset = [(NSNumber *)snapshot.value doubleValue]; NSTimeInterval estimatedServerTimeMs = [[NSDate date] timeIntervalSince1970] * 1000.0 + offset; NSLog(@"Estimated server time: %0.3f", estimatedServerTimeMs); }];
แอปตัวอย่างการตรวจหาบุคคล
ผสานรวมการดำเนินการยกเลิกการเชื่อมต่อเข้ากับการตรวจสอบสถานะการเชื่อมต่อและ การประทับเวลาของเซิร์ฟเวอร์ คุณสามารถสร้างระบบตรวจหาบุคคลในบ้านของผู้ใช้ได้ ในระบบนี้ ผู้ใช้แต่ละคนจัดเก็บข้อมูลที่ตำแหน่งฐานข้อมูลเพื่อระบุว่ามีการ ไคลเอ็นต์ Realtime Database ออนไลน์อยู่ ลูกค้าตั้งค่าตำแหน่งนี้เป็น "จริง" เมื่อ ผู้ใช้กลับมาออนไลน์และมีการประทับเวลาเมื่อยกเลิกการเชื่อมต่อ การประทับเวลานี้ ระบุเวลาล่าสุดที่ผู้ใช้ที่ระบุออนไลน์
โปรดทราบว่าแอปของคุณควรจัดคิวการดำเนินการยกเลิกการเชื่อมต่อก่อนที่ผู้ใช้จะ ที่ระบุทางออนไลน์ เพื่อหลีกเลี่ยงเงื่อนไขการแข่งขันในกรณีที่ลูกค้า การเชื่อมต่อเครือข่ายขาดหายก่อนที่จะส่งทั้งสองคำสั่งไปยังเซิร์ฟเวอร์
ต่อไปนี้เป็นระบบการตรวจหาผู้ใช้แบบง่าย:
Swift
// since I can connect from multiple devices, we store each connection instance separately // any time that connectionsRef's value is null (i.e. has no children) I am offline let myConnectionsRef = Database.database().reference(withPath: "users/morgan/connections") // stores the timestamp of my last disconnect (the last time I was seen online) let lastOnlineRef = Database.database().reference(withPath: "users/morgan/lastOnline") let connectedRef = Database.database().reference(withPath: ".info/connected") connectedRef.observe(.value, with: { snapshot in // only handle connection established (or I've reconnected after a loss of connection) guard snapshot.value as? Bool ?? false else { return } // add this device to my connections list let con = myConnectionsRef.childByAutoId() // when this device disconnects, remove it. con.onDisconnectRemoveValue() // The onDisconnect() call is before the call to set() itself. This is to avoid a race condition // where you set the user's presence to true and the client disconnects before the // onDisconnect() operation takes effect, leaving a ghost user. // this value could contain info about the device or a timestamp instead of just true con.setValue(true) // when I disconnect, update the last time I was seen online lastOnlineRef.onDisconnectSetValue(ServerValue.timestamp()) })
Objective-C
// since I can connect from multiple devices, we store each connection instance separately // any time that connectionsRef's value is null (i.e. has no children) I am offline FIRDatabaseReference *myConnectionsRef = [[FIRDatabase database] referenceWithPath:@"users/morgan/connections"]; // stores the timestamp of my last disconnect (the last time I was seen online) FIRDatabaseReference *lastOnlineRef = [[FIRDatabase database] referenceWithPath:@"users/morgan/lastOnline"]; FIRDatabaseReference *connectedRef = [[FIRDatabase database] referenceWithPath:@".info/connected"]; [connectedRef observeEventType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot *snapshot) { if([snapshot.value boolValue]) { // connection established (or I've reconnected after a loss of connection) // add this device to my connections list FIRDatabaseReference *con = [myConnectionsRef childByAutoId]; // when this device disconnects, remove it [con onDisconnectRemoveValue]; // The onDisconnect() call is before the call to set() itself. This is to avoid a race condition // where you set the user's presence to true and the client disconnects before the // onDisconnect() operation takes effect, leaving a ghost user. // this value could contain info about the device or a timestamp instead of just true [con setValue:@YES]; // when I disconnect, update the last time I was seen online [lastOnlineRef onDisconnectSetValue:[FIRServerValue timestamp]]; } }];