برنامههای 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]]; } }];