برنامههای Firebase حتی اگر اتصال شبکه برنامهتان موقتاً قطع شود، کار میکنند. علاوهبراین، Firebase ابزارهایی برای ماندگاری دادهها بهصورت محلی، مدیریت حضور، و رسیدگی به تأخیر ارائه میدهد.
پایداری دیسک
برنامههای Firebase بهطور خودکار وقفههای موقت شبکه را مدیریت میکنند. دادههای ذخیرهشده در حافظه پنهان درحالت آفلاین دردسترس است و Firebase هرگونه نوشتن را پساز بازیابی اتصال شبکه دوباره ارسال میکند.
وقتی پایداری دیسک را فعال میکنید، برنامهتان دادهها را بهصورت محلی در دستگاه مینویسد تا برنامهتان بتواند وضعیت را درحالت آفلاین حفظ کند، حتی اگر کاربر یا سیستمعامل برنامه را بازراهاندازی کند.
میتوانید پایداری دیسک را فقط با یک خط کد فعال کنید.
Swift
Database.database().isPersistenceEnabled = true
Objective-C
[FIRDatabase database].persistenceEnabled = YES;
رفتار پایداری
با فعال کردن پایداری، هر دادهای که کلاینت Firebase Realtime Database درحین آنلاین بودن همگامسازی میکند در دیسک باقی میماند و بهصورت آفلاین دردسترس است، حتی زمانی که کاربر یا سیستمعامل برنامه را بازراهاندازی میکند. این یعنی برنامه شما بااستفاده از دادههای محلی ذخیرهشده در حافظه نهان، همانطور که در حالت آنلاین کار میکند، کار میکند. فراخوانیهای شنونده همچنان برای بهروزرسانیهای محلی فعال خواهند بود.
کارخواه Firebase Realtime Database بهطور خودکار صفی از همه عملیات نوشتن را که درحین آفلاین بودن برنامه انجام میشود نگه میدارد. وقتی پایداری فعال باشد، این صف در دیسک نیز پایدار میماند تا همه نوشتههایتان هنگام بازراهاندازی برنامه توسط کاربر یا سیستمعامل دردسترس باشد. وقتی برنامه دوباره متصل میشود، همه عملیات به سرور Firebase Realtime Database ارسال میشود.
اگر برنامهتان از اصالتسنجی Firebase استفاده میکند، کارخواه Firebase Realtime Database کد اصالتسنجی کاربر را در سراسر بازراهاندازیهای برنامه حفظ میکند. اگر توکن اصالتسنجی درحالیکه برنامهتان آفلاین است منقضی شود، کارخواه عملیات نوشتن را موقتاً متوقف میکند تا برنامهتان دوباره اصالتسنجی کاربر را انجام دهد، درغیراینصورت ممکن است عملیات نوشتن بهدلیل قوانین امنیتی ناموفق باشد.
بهروز نگه داشتن دادهها
Firebase Realtime Database نسخه محلی دادهها را برای شنوندگان فعال همگامسازی و ذخیره میکند. بهعلاوه، میتوانید مکانهای خاصی را همگامسازی کنید.
Swift
let scoresRef = Database.database().reference(withPath: "scores") scoresRef.keepSynced(true)
Objective-C
FIRDatabaseReference *scoresRef = [[FIRDatabase database] referenceWithPath:@"scores"]; [scoresRef keepSynced:YES];
کارخواه Firebase Realtime Database بهطور خودکار دادهها را در این مکانها بارگیری میکند و آن را همگامسازیشده نگه میدارد، حتی اگر مرجع شنونده فعالی نداشته باشد. با خط کد زیر میتوانید همگامسازی را دوباره خاموش کنید.
Swift
scoresRef.keepSynced(false)
Objective-C
[scoresRef keepSynced:NO];
بهطور پیشفرض، ۱۰ مگابایت از دادههای قبلاً همگامسازیشده در حافظه پنهان ذخیره میشود. این مقدار باید برای اکثر برنامهها کافی باشد. اگر حافظه نهان از اندازه پیکربندیشدهاش بزرگتر شود، Firebase Realtime Database دادههایی را که اخیراً کمتر استفاده شدهاند پاک میکند. دادههایی که همگامسازی میشوند از حافظه نهان پاک نمیشوند.
درحال پُرسمان دادهها بهصورت آفلاین
Firebase Realtime Database دادههای برگشتی از پُرسمان را برای استفاده در حالت آفلاین ذخیره میکند. برای پُرسمانهایی که درحالت آفلاین ساخته شدهاند، Firebase Realtime Database همچنان برای دادههایی که قبلاً بار شدهاند کار میکند. اگر دادههای درخواستشده بار نشده باشد، Firebase Realtime Database دادهها را از حافظه نهان محلی بار میکند. وقتی اتصال شبکه دوباره دردسترس قرار بگیرد، دادهها بار میشود و پُرسمان را منعکس میکند.
برای مثال، این کد چهار مورد آخر را در Firebase Realtime Database امتیازها جستجو میکند
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); }];
فرض کنید کاربر اتصالش را ازدست میدهد، آفلاین میشود، و برنامه را بازراهاندازی میکند. درحالیکه هنوز آفلاین است، برنامه دو مورد آخر را از همان مکان پُرسمان میکند. این پُرسمان دو مورد آخر را باموفقیت برمیگرداند زیرا برنامه هر چهار مورد را در پُرسمان بالا بار کرده بود.
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 Realtime Database بااستفاده از حافظه نهان پایدار، رویدادهای «فرزند اضافه شد» را برای دو دایناسور با بالاترین امتیاز ایجاد میکند. اما رویداد «مقدار» را ایجاد نخواهد کرد، زیرا برنامه هرگز آن پُرسمان را درحالت آنلاین اجرا نکرده است.
اگر برنامه درحالت آفلاین شش مورد آخر را درخواست کند، رویدادهای «فرزند اضافه شد» را برای چهار مورد ذخیرهشده در حافظه نهان بلافاصله دریافت میکند. وقتی دستگاه دوباره آنلاین میشود، کارخواه Firebase Realtime Database با سرور همگامسازی میشود و دو رویداد نهایی «فرزند اضافه شد» و «مقدار» را برای برنامه دریافت میکند.
مدیریت تراکنشها بهصورت آفلاین
هر تراکنشی که درحین آفلاین بودن برنامه انجام شود، در صف قرار میگیرد. وقتی برنامه دوباره به شبکه متصل شد، تراکنشها به سرور 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
وقتی عملیات onDisconnect() را برقرار میکنید، عملیات در سرور Firebase Realtime Database قرار میگیرد. سرور امنیت را بررسی میکند تا
مطمئن شود کاربر میتواند رویداد نوشتن درخواستشده را انجام دهد، و اگر نامعتبر باشد
به برنامهتان اطلاع میدهد. سپس سرور
اتصال را پایش میکند. اگر در هر نقطهای اتصال قطع شود یا بهطور فعال توسط کارخواه Realtime Database بسته شود، سرور امنیت را برای بار دوم بررسی میکند (تا مطمئن شود که عملیات همچنان معتبر است) و سپس رویداد را فراخوانی میکند.
برنامهتان میتواند از فراخوانی برگشتی در عملیات نوشتن استفاده کند
تا مطمئن شود 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 Realtime Database
مکان ویژهای را در /.info/connected ارائه میدهد که
هر بار که وضعیت اتصال مشتری Firebase Realtime Database
تغییر میکند، بهروز میشود. در اینجا مثالی آورده شده است:
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 را بهعنوان نادرست بخواند، این
تضمینی نیست که کلاینت جداگانهای نیز آن را نادرست بخواند.
تأخیر در رسیدگی
مُهرهای زمان سرور
سرورهای Firebase Realtime Database مکانیزمی برای درج مُهرهای زمان تولیدشده در سرور بهعنوان داده ارائه میدهند. این ویژگی، در ترکیب با
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 Realtime Database مفید باشد. میتوانید
یک فراخوان برگشتی به مکان /.info/serverTimeOffset پیوست کنید
تا مقدار را به میلیثانیه دریافت کنید، که مشتریان Firebase Realtime Database
به زمان گزارششده محلی (زمان مبدأ به میلیثانیه) اضافه میکنند تا
زمان سرور را تخمین بزنند. توجه داشته باشید که دقت این انحراف میتواند تحتتأثیر تأخیر شبکه قرار گیرد، بنابراین عمدتاً برای کشف اختلافات بزرگ (بیشتر از ۱ ثانیه) در زمان ساعت مفید است.
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]]; } }];