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

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

ماندگاری دیسک

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

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

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

Swift

توجه: این محصول Firebase در هدف «کلیپ برنامه» دردسترس نیست.
Database.database().isPersistenceEnabled = true

Objective-C

توجه: این محصول Firebase در هدف «کلیپ برنامه» دردسترس نیست.
[FIRDatabase database].persistenceEnabled = YES;

رفتار پایداری

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

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

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

به‌روز نگه داشتن داده‌ها

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

Swift

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

Objective-C

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

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

Swift

توجه: این محصول Firebase در هدف «کلیپ برنامه» دردسترس نیست.
scoresRef.keepSynced(false)

Objective-C

توجه: این محصول Firebase در هدف «کلیپ برنامه» دردسترس نیست.
[scoresRef keepSynced:NO];

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

درحال پُرسمان داده‌ها به‌صورت آفلاین

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

برای مثال، این کد برای چهار مورد آخر در Firebase Realtime Database امتیازها پُرسمان می‌کند

Swift

توجه: این محصول Firebase در هدف «کلیپ برنامه» دردسترس نیست.
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

توجه: این محصول Firebase در هدف «کلیپ برنامه» دردسترس نیست.
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

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

Objective-C

توجه: این محصول Firebase در هدف «کلیپ برنامه» دردسترس نیست.
[[[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

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

Objective-C

توجه: این محصول Firebase در هدف «کلیپ برنامه» دردسترس نیست.
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

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

Objective-C

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

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

Swift

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

Objective-C

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

درحال تشخیص وضعیت اتصال

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

Swift

توجه: این محصول Firebase در هدف «کلیپ برنامه» دردسترس نیست.
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

توجه: این محصول Firebase در هدف «کلیپ برنامه» دردسترس نیست.
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

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

Objective-C

توجه: این محصول Firebase در هدف «کلیپ برنامه» دردسترس نیست.
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

توجه: این محصول Firebase در هدف «کلیپ برنامه» دردسترس نیست.
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

توجه: این محصول Firebase در هدف «کلیپ برنامه» دردسترس نیست.
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

توجه: این محصول Firebase در هدف «کلیپ برنامه» دردسترس نیست.
// 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

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