قابلیت های آفلاین در پلتفرم های اپل

برنامه های Firebase حتی اگر برنامه شما به طور موقت اتصال شبکه خود را قطع کند، کار می کنند. علاوه بر این، Firebase ابزارهایی را برای تداوم داده ها به صورت محلی، مدیریت حضور و مدیریت تأخیر فراهم می کند.

پایداری دیسک

برنامه های Firebase به طور خودکار وقفه های موقت شبکه را کنترل می کنند. داده‌های ذخیره‌شده در حافظه پنهان در حالت آفلاین در دسترس هستند و Firebase هنگام بازیابی اتصال شبکه، هر نوشته‌ای را دوباره ارسال می‌کند.

وقتی پایداری دیسک را فعال می‌کنید، برنامه شما داده‌ها را به صورت محلی در دستگاه می‌نویسد تا برنامه شما بتواند در حالت آفلاین وضعیت را حفظ کند، حتی اگر کاربر یا سیستم عامل برنامه را مجدداً راه‌اندازی کند.

شما می توانید پایداری دیسک را تنها با یک خط کد فعال کنید.

سویفت

توجه: این محصول Firebase در هدف App Clip موجود نیست.
Database.database().isPersistenceEnabled = true

هدف-C

توجه: این محصول Firebase در هدف App Clip موجود نیست.
[FIRDatabase database].persistenceEnabled = YES;

رفتار پایدار

با فعال کردن تداوم، هر داده‌ای که مشتری Firebase Realtime Database در حالت آنلاین همگام‌سازی می‌کند، روی دیسک باقی می‌ماند و به صورت آفلاین در دسترس است، حتی زمانی که کاربر یا سیستم عامل برنامه را مجدداً راه‌اندازی می‌کند. این بدان معنی است که برنامه شما با استفاده از داده های محلی ذخیره شده در حافظه پنهان، همانطور که بصورت آنلاین انجام می شود کار می کند. تماس‌های شنونده برای به‌روزرسانی‌های محلی فعال می‌شوند.

سرویس گیرنده Firebase Realtime Database به طور خودکار یک صف از تمام عملیات نوشتن که در زمانی که برنامه شما آفلاین است انجام می شود نگه می دارد. هنگامی که تداوم فعال است، این صف نیز روی دیسک باقی می ماند تا زمانی که کاربر یا سیستم عامل برنامه را مجددا راه اندازی می کند، همه نوشته های شما در دسترس باشد. هنگامی که برنامه اتصال مجدد را به دست آورد، همه عملیات به سرور Firebase Realtime Database ارسال می شود.

اگر برنامه شما از احراز هویت Firebase استفاده می‌کند، سرویس گیرنده Firebase Realtime Database رمز احراز هویت کاربر را در طول راه‌اندازی مجدد برنامه ادامه می‌دهد. اگر رمز تأیید اعتبار زمانی که برنامه شما آفلاین است منقضی شود، مشتری عملیات نوشتن را متوقف می کند تا زمانی که برنامه شما مجدداً کاربر را تأیید کند، در غیر این صورت ممکن است عملیات نوشتن به دلیل قوانین امنیتی با شکست مواجه شود.

تازه نگه داشتن داده ها

Firebase Realtime Database یک کپی محلی از داده ها را برای شنوندگان فعال همگام و ذخیره می کند. علاوه بر این، می توانید مکان های خاص را همگام نگه دارید.

سویفت

توجه: این محصول Firebase در هدف App Clip موجود نیست.
let scoresRef = Database.database().reference(withPath: "scores")
scoresRef.keepSynced(true)

هدف-C

توجه: این محصول Firebase در هدف App Clip موجود نیست.
FIRDatabaseReference *scoresRef = [[FIRDatabase database] referenceWithPath:@"scores"];
[scoresRef keepSynced:YES];

سرویس گیرنده Firebase Realtime Database به طور خودکار داده ها را در این مکان ها دانلود می کند و حتی اگر مرجع هیچ شنونده فعالی نداشته باشد، آنها را همگام نگه می دارد. می توانید همگام سازی را با خط کد زیر خاموش کنید.

سویفت

توجه: این محصول Firebase در هدف App Clip موجود نیست.
scoresRef.keepSynced(false)

هدف-C

توجه: این محصول Firebase در هدف App Clip موجود نیست.
[scoresRef keepSynced:NO];

به طور پیش‌فرض، 10 مگابایت از داده‌های همگام‌سازی شده قبلی در حافظه پنهان ذخیره می‌شود. این باید برای اکثر برنامه ها کافی باشد. اگر حافظه پنهان از اندازه پیکربندی شده خود بیشتر شود، Firebase Realtime Database داده هایی را که اخیراً کمتر استفاده شده اند را پاک می کند. داده هایی که همگام نگه داشته می شوند از حافظه پنهان پاک نمی شوند.

پرس و جو داده ها به صورت آفلاین

Firebase Realtime Database داده های بازگشتی از یک درخواست را برای استفاده در حالت آفلاین ذخیره می کند. برای پرس و جوهایی که در حالت آفلاین ساخته می شوند، Firebase Realtime Database برای داده های بارگیری شده قبلی به کار خود ادامه می دهد. اگر داده‌های درخواستی بارگیری نشده باشند، Firebase Realtime Database داده‌ها را از حافظه پنهان محلی بارگیری می‌کند. هنگامی که اتصال شبکه دوباره در دسترس است، داده ها بارگیری می شوند و پرس و جو را منعکس می کنند.

به عنوان مثال، این کد برای چهار مورد آخر در Firebase Realtime Database جستجو می کند

سویفت

توجه: این محصول Firebase در هدف App Clip موجود نیست.
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")")
}

هدف-C

توجه: این محصول Firebase در هدف App Clip موجود نیست.
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);
    }];

فرض کنید که کاربر ارتباط خود را قطع می کند، آفلاین می شود و برنامه را دوباره راه اندازی می کند. در حالی که هنوز آفلاین است، برنامه دو مورد آخر را از یک مکان جستجو می کند. این عبارت با موفقیت دو مورد آخر را برمی گرداند زیرا برنامه هر چهار مورد را در جستار بالا بارگیری کرده است.

سویفت

توجه: این محصول Firebase در هدف App Clip موجود نیست.
scoresRef.queryOrderedByValue().queryLimited(toLast: 2).observe(.childAdded) { snapshot in
  print("The \(snapshot.key) dinosaur's score is \(snapshot.value ?? "null")")
}

هدف-C

توجه: این محصول Firebase در هدف App Clip موجود نیست.
[[[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 ابتدایی آورده شده است:

سویفت

توجه: این محصول Firebase در هدف App Clip موجود نیست.
let presenceRef = Database.database().reference(withPath: "disconnectmessage");
// Write a string when this client loses connection
presenceRef.onDisconnectSetValue("I disconnected!")

هدف-C

توجه: این محصول Firebase در هدف App Clip موجود نیست.
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 به درستی متصل شده است:

سویفت

توجه: این محصول Firebase در هدف App Clip موجود نیست.
presenceRef.onDisconnectRemoveValue { error, reference in
  if let error = error {
    print("Could not establish onDisconnect event: \(error)")
  }
}

هدف-C

توجه: این محصول Firebase در هدف App Clip موجود نیست.
[presenceRef onDisconnectRemoveValueWithCompletionBlock:^(NSError *error, FIRDatabaseReference *reference) {
  if (error != nil) {
    NSLog(@"Could not establish onDisconnect event: %@", error);
  }
}];

یک رویداد onDisconnect را نیز می توان با فراخوانی .cancel() لغو کرد:

سویفت

توجه: این محصول Firebase در هدف App Clip موجود نیست.
presenceRef.onDisconnectSetValue("I disconnected")
// some time later when we change our minds
presenceRef.cancelDisconnectOperations()

هدف-C

توجه: این محصول Firebase در هدف App Clip موجود نیست.
[presenceRef onDisconnectSetValue:@"I disconnected"];
// some time later when we change our minds
[presenceRef cancelDisconnectOperations];

تشخیص وضعیت اتصال

برای بسیاری از ویژگی های مربوط به حضور، برای برنامه شما مفید است که بداند چه زمانی آنلاین یا آفلاین است. Firebase Realtime Database یک مکان ویژه در /.info/connected ارائه می دهد که هر بار که وضعیت اتصال سرویس گیرنده Firebase Realtime Database تغییر می کند، به روز می شود. در اینجا یک مثال است:

سویفت

توجه: این محصول Firebase در هدف App Clip موجود نیست.
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")
  }
})

هدف-C

توجه: این محصول Firebase در هدف App Clip موجود نیست.
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 بخواند، این تضمینی نیست که یک کلاینت جداگانه نیز false را بخواند.

رسیدگی به تاخیر

مهر زمانی سرور

سرورهای Firebase Realtime Database مکانیزمی را برای درج مُهرهای زمانی تولید شده روی سرور به عنوان داده فراهم می کنند. این ویژگی، همراه با onDisconnect ، راه آسانی را برای یادداشت‌برداری مطمئن از زمان قطع ارتباط مشتری Realtime Database ارائه می‌کند:

سویفت

توجه: این محصول Firebase در هدف App Clip موجود نیست.
let userLastOnlineRef = Database.database().reference(withPath: "users/morgan/lastOnline")
userLastOnlineRef.onDisconnectSetValue(ServerValue.timestamp())

هدف-C

توجه: این محصول Firebase در هدف App Clip موجود نیست.
FIRDatabaseReference *userLastOnlineRef = [[FIRDatabase database] referenceWithPath:@"users/morgan/lastOnline"];
[userLastOnlineRef onDisconnectSetValue:[FIRServerValue timestamp]];

کج بودن ساعت

در حالی که firebase.database.ServerValue.TIMESTAMP بسیار دقیق تر است و برای اکثر عملیات خواندن/نوشتن ارجح است، گاهی اوقات تخمین انحراف ساعت مشتری با توجه به سرورهای Firebase Realtime Database می تواند مفید باشد. می‌توانید یک تماس برگشتی را به مکان /.info/serverTimeOffset متصل کنید تا مقداری را که مشتریان Firebase Realtime Database بر حسب میلی‌ثانیه به زمان گزارش شده محلی (زمان دوره به میلی‌ثانیه) برای تخمین زمان سرور اضافه می‌کنند، به دست آورید. توجه داشته باشید که دقت این افست می‌تواند تحت‌تاثیر تأخیر شبکه قرار گیرد، و بنابراین اساساً برای کشف اختلافات بزرگ (> 1 ثانیه) در زمان ساعت مفید است.

سویفت

توجه: این محصول Firebase در هدف App Clip موجود نیست.
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)")
  }
})

هدف-C

توجه: این محصول Firebase در هدف App Clip موجود نیست.
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 آنلاین است یا نه. مشتریان زمانی که آنلاین می‌شوند، این مکان را روی true و هنگام قطع ارتباط یک مهر زمانی تنظیم می‌کنند. این مهر زمان آخرین باری را که کاربر مورد نظر آنلاین بوده است نشان می دهد.

توجه داشته باشید که برنامه شما باید قبل از اینکه کاربر به صورت آنلاین علامت گذاری شود، عملیات قطع اتصال را در صف قرار دهد تا در صورت قطع شدن اتصال شبکه مشتری قبل از ارسال هر دو دستور به سرور، از هرگونه شرایط مسابقه جلوگیری شود.

در اینجا یک سیستم حضور کاربر ساده وجود دارد:

سویفت

توجه: این محصول Firebase در هدف App Clip موجود نیست.
// 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())
})

هدف-C

توجه: این محصول Firebase در هدف App Clip موجود نیست.
// 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]];
  }
}];