إمكانات وضع عدم الاتصال بالإنترنت على منصات Apple

تعمل تطبيقات Firebase حتى إذا فقد تطبيقك الاتصال بالشبكة مؤقتًا. بالإضافة إلى ذلك، يوفر Firebase أدوات للاحتفاظ بالبيانات محليًا وإدارة التواجد والتعامل مع وقت الاستجابة.

ثبات القرص

تتعامل تطبيقات Firebase مع الانقطاعات المؤقتة في الشبكة تلقائيًا. تتوفّر البيانات المخزّنة مؤقتًا عندما تكون غير متصل بالإنترنت، ويعيد Firebase إرسال أي عمليات كتابة عند استعادة الاتصال بالشبكة.

عند تفعيل ميزة التثبيت على القرص، يكتب تطبيقك البيانات محليًا على الجهاز حتى يحافظ التطبيق على حالته بلا اتصال بالإنترنت، حتى إذا أعاد المستخدم أو نظام التشغيل تشغيل التطبيق.

يمكنك تفعيل ميزة تثبيت القرص باستخدام سطر واحد فقط من الرمز.

Swift

ملاحظة: لا يتوفّر منتج Firebase هذا في هدف App Clip.
Database.database().isPersistenceEnabled = true

Objective-C

ملاحظة: لا يتوفّر منتج Firebase هذا في هدف App Clip.
[FIRDatabase database].persistenceEnabled = YES;

سلوك الاستمرارية

من خلال تفعيل ميزة الاحتفاظ بالبيانات، تظل أي بيانات يزامنها برنامج قاعدة بيانات Firebase في الوقت الفعلي أثناء الاتصال بالإنترنت على القرص وتصبح متاحة بلا اتصال بالإنترنت، حتى عندما يُعيد المستخدم أو نظام التشغيل تشغيل التطبيق. ويعني هذا أنّ تطبيقك يعمل كما لو كان متصلاً بالإنترنت باستخدام البيانات المحلية المخزَّنة في ذاكرة التخزين المؤقت. سيستمر تنشيط استدعاءات المستمعين للحصول على التحديثات المحلية.

يحتفظ برنامج قاعدة بيانات Firebase في الوقت الفعلي تلقائيًا بقائمة انتظار تضم جميع عمليات الكتابة التي يتم إجراؤها عندما يكون تطبيقك غير متصل بالإنترنت. عند تفعيل ميزة الاحتفاظ بالبيانات، يتم الاحتفاظ بقائمة الانتظار هذه أيضًا على القرص كي تكون جميع عمليات الكتابة متاحة عندما يُعيد المستخدم أو نظام التشغيل تشغيل التطبيق. وعندما يستعيد التطبيق الاتصال، يتم إرسال جميع العمليات إلى خادم قاعدة بيانات Firebase في الوقت الفعلي.

إذا كان تطبيقك يستخدم مصادقة Firebase، يحتفظ عميل قاعدة بيانات Firebase في الوقت الفعلي، بالرمز المميز لمصادقة المستخدم في جميع عمليات إعادة تشغيل التطبيق. إذا انتهت صلاحية الرمز المميّز للمصادقة عندما يكون تطبيقك بلا اتصال بالإنترنت، يوقف العميل عمليات الكتابة مؤقتًا إلى أن يعيد تطبيقك مصادقة المستخدم، وإلّا فقد يتعذّر إتمام عمليات الكتابة بسبب قواعد الأمان.

الحفاظ على إعادة تحميل البيانات

تعمل "قاعدة بيانات Firebase في الوقت الفعلي" على مزامنة نسخة محلية من البيانات وتخزينها للمستمعين النشطين. بالإضافة إلى ذلك، يمكنك مزامنة مواقع جغرافية محددة.

Swift

ملاحظة: لا يتوفّر منتج Firebase هذا في هدف App Clip.
let scoresRef = Database.database().reference(withPath: "scores")
scoresRef.keepSynced(true)

Objective-C

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

يعمل برنامج قاعدة بيانات Firebase في الوقت الفعلي تلقائيًا على تنزيل البيانات في هذه المواقع الجغرافية ويحافظ على مزامنتها حتى إذا لم يكن للمرجع أي مستمعين نشطين. يمكنك إيقاف المزامنة مرة أخرى باستخدام سطر الرمز التالي.

Swift

ملاحظة: لا يتوفّر منتج Firebase هذا في هدف App Clip.
scoresRef.keepSynced(false)

Objective-C

ملاحظة: لا يتوفّر منتج Firebase هذا في هدف App Clip.
[scoresRef keepSynced:NO];

بشكل تلقائي، يتم تخزين 10 ميغابايت من البيانات التي تمت مزامنتها سابقًا مؤقتًا. وهذا سيكون كافيًا لمعظم التطبيقات. وإذا تجاوزت ذاكرة التخزين المؤقت الحجم الذي تم ضبطه، تزيل قاعدة بيانات Firebase في الوقت الفعلي نهائيًا البيانات التي تم استخدامها مؤخرًا. لا تتم الإزالة النهائية للبيانات التي يتم الاحتفاظ بها متزامنة من ذاكرة التخزين المؤقت.

الاستعلام عن البيانات بلا اتصال بالإنترنت

تخزِّن "قاعدة بيانات Firebase في الوقت الفعلي" البيانات التي يتم عرضها من طلب بحث لاستخدامها بلا اتصال بالإنترنت. بالنسبة إلى طلبات البحث التي تم إنشاؤها أثناء عدم الاتصال بالإنترنت، تواصل "قاعدة بيانات Firebase في الوقت الفعلي" العمل مع البيانات التي تم تحميلها سابقًا. إذا لم يتم تحميل البيانات المطلوبة، تحمِّل قاعدة بيانات Firebase في الوقت الفعلي البيانات من ذاكرة التخزين المؤقت على الجهاز. عندما يصبح الاتصال بالشبكة متاحًا مرة أخرى، يتم تحميل البيانات وستظهر طلب البحث.

على سبيل المثال، يبحث هذا الرمز عن آخر أربعة عناصر في قاعدة بيانات Firebase في الوقت الفعلي للنتائج.

Swift

ملاحظة: لا يتوفّر منتج 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")")
}

Objective-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);
    }];

لنفترض أنّ المستخدم فقد الاتصال بالإنترنت وانقطع الاتصال بالإنترنت ثم أعاد تشغيل التطبيق. عندما يكون التطبيق غير متصل بالإنترنت، يطلب التطبيق آخر عنصرين من الموقع الجغرافي نفسه. سيعرض هذا الاستعلام آخر عنصرين بنجاح لأن التطبيق حمّل العناصر الأربعة جميعها في الاستعلام أعلاه.

Swift

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

Objective-C

ملاحظة: لا يتوفّر منتج Firebase هذا في هدف App Clip.
[[[scoresRef queryOrderedByValue] queryLimitedToLast:2]
    observeEventType:FIRDataEventTypeChildAdded withBlock:^(FIRDataSnapshot *snapshot) {
      NSLog(@"The %@ dinosaur's score is %@", snapshot.key, snapshot.value);
    }];

في المثال السابق، يعرض عميل قاعدة بيانات Firebase في الوقت الفعلي الأحداث "المضافة التابعة" للأحداث التي سجّل نوعين من الديناصورات من حيث النتائج، وذلك باستخدام ذاكرة التخزين المؤقت الثابتة. ولن يرفع هذا الحدث الحدث "value"، لأنّ التطبيق لم ينفّذ طلب البحث هذا على الإنترنت.

إذا طلب التطبيق العناصر الستة الأخيرة بلا اتصال بالإنترنت، سيحصل على الفور على أحداث "إضافة تابعة" للعناصر الأربعة المخزّنة مؤقتًا. عندما يعود الجهاز إلى الاتصال بالإنترنت، يتزامن برنامج قاعدة بيانات Firebase في الوقت الفعلي مع الخادم ويحصل على آخر حدثَي "إضافة ثانوي" و"قيمة" للتطبيق.

التعامل مع المعاملات بلا إنترنت

يتم وضع أي معاملات يتم إجراؤها عندما يكون التطبيق بلا اتصال بالإنترنت في قائمة الانتظار. وبعد أن يستعيد التطبيق الاتصال بالشبكة، يتم إرسال المعاملات إلى خادم قاعدة بيانات الوقت الفعلي.

إدارة التواجد

في تطبيقات الوقت الفعلي، غالبًا ما يكون من المفيد رصد حالات اتصال العملاء وقطع الاتصال بهم. على سبيل المثال، يمكنك وضع علامة "غير متصل بالإنترنت" على المستخدم عند قطع اتصال العميل به.

يوفر عملاء قاعدة بيانات Firebase مجموعات أولية بسيطة يمكنك استخدامها للكتابة في قاعدة البيانات عندما ينقطع اتصال العميل بخوادم قاعدة بيانات Firebase. يتم إجراء هذه التحديثات سواء انقطع الاتصال بالإنترنت لدى العميل أم لا، لذا يمكنك الاعتماد عليها لتنظيف البيانات حتى في حال انقطاع الاتصال أو تعطُّل العميل. يمكن تنفيذ جميع عمليات الكتابة عند قطع الاتصال، بما في ذلك الإعداد والتحديث والإزالة.

في ما يلي مثال بسيط على كتابة البيانات عند قطع الاتصال باستخدام أساس onDisconnect:

Swift

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

Objective-C

ملاحظة: لا يتوفّر منتج Firebase هذا في هدف App Clip.
FIRDatabaseReference *presenceRef = [[FIRDatabase database] referenceWithPath:@"disconnectmessage"];
// Write a string when this client loses connection
[presenceRef onDisconnectSetValue:@"I disconnected!"];

طريقة عمل ميزة on Connect

عند إنشاء عملية onDisconnect()، تظهر العملية على خادم "قاعدة بيانات Firebase في الوقت الفعلي". يفحص الخادم الأمان للتأكّد من أنّ المستخدم يمكنه تنفيذ حدث الكتابة المطلوب، ويبلغ تطبيقك إذا كان غير صالح. ويراقب الخادم بعد ذلك الاتصال. وفي حال انتهاء مهلة الاتصال في أي وقت، أو إغلاق الخادم بشكل نشط من قِبل برنامج قاعدة البيانات في الوقت الفعلي، يتحقّق الخادم من الأمان مرة ثانية (للتأكّد من أن العملية لا تزال صالحة)، ثم يستدعي الحدث.

يمكن لتطبيقك استخدام عملية الاستدعاء في عملية الكتابة لضمان إرفاق onDisconnect بشكل صحيح:

Swift

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

Objective-C

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

يمكن أيضًا إلغاء حدث "onDisconnect" من خلال الاتصال بالرقم .cancel():

Swift

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

Objective-C

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

اكتشاف حالة الاتصال

وبالنسبة إلى العديد من الميزات المتعلّقة بالحضور، من المفيد أن يعرف تطبيقك ما إذا كان متصلاً بالإنترنت أو غير متصل بالإنترنت. توفّر "قاعدة بيانات Firebase في الوقت الفعلي" موقعًا خاصًا في /.info/connected يتم تعديله في كل مرة تتغيّر فيها حالة اتصال عميل "قاعدة بيانات Firebase" في الوقت الفعلي. وفي ما يلي مثال لذلك:

Swift

ملاحظة: لا يتوفّر منتج 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")
  }
})

Objective-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 هي قيمة منطقية لا تتم مزامنتها بين عملاء قاعدة بيانات الوقت الفعلي، لأنّ القيمة تعتمد على حالة البرنامج. بعبارة أخرى، إذا قرأ أحد البرامج /.info/connected على أنّه خطأ، لا يضمن ذلك أنّ برنامجًا منفصلاً سيقرأ أيضًا القيمة "خطأ".

وقت الاستجابة

الطوابع الزمنية للخادم

توفّر خوادم "قاعدة بيانات Firebase في الوقت الفعلي" آلية لإدراج الطوابع الزمنية التي تم إنشاؤها على الخادم كبيانات. وتوفّر هذه الميزة، بالإضافة إلى onDisconnect، طريقة سهلة لتدوين الوقت الذي تم فيه قطع اتصال برنامج قاعدة البيانات في الوقت الفعلي بشكل موثوق:

Swift

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

Objective-C

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

انحراف الساعة

إنّ السمة firebase.database.ServerValue.TIMESTAMP أكثر دقة وتفضّلها لمعظم عمليات القراءة/الكتابة، غير أنّه قد يكون مفيدًا في بعض الأحيان تقدير انحراف ساعة العميل في ما يتعلق بخوادم "قاعدة بيانات Firebase في الوقت الفعلي". يمكنك إرفاق طلب معاودة الاتصال بالموقع /.info/serverTimeOffset للحصول على القيمة بالمللي ثانية التي يضيفها عملاء قاعدة بيانات Firebase في الوقت الفعلي إلى الوقت المحلي لإعداد التقارير (الوقت الفعلي بالمللي ثانية) لتقدير وقت الخادم. يُرجى العِلم أنّ دقة هذه المعادلة يمكن أن تتأثر بوقت استجابة الشبكة، وبالتالي فهي مفيدة في المقام الأول لرصد تناقضات كبيرة (أكبر من ثانية واحدة) في وقت الساعة.

Swift

ملاحظة: لا يتوفّر منتج 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)")
  }
})

Objective-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);
}];

نموذج تطبيق التواجد

ومن خلال الجمع بين عمليات قطع الاتصال ومراقبة حالة الاتصال والطوابع الزمنية للخادم، يمكنك إنشاء نظام تواجد المستخدم. في هذا النظام، يخزِّن كل مستخدم البيانات في موقع قاعدة بيانات للإشارة إلى ما إذا كان برنامج قاعدة البيانات في الوقت الفعلي متصلاً بالإنترنت أم لا. يضبط العملاء هذا الموقع الجغرافي على "صحيح" عند الاتصال بالإنترنت وعلى طابع زمني عند قطع الاتصال. ويشير هذا الطابع الزمني إلى آخر مرة كان فيها المستخدم المحدّد متصلاً بالإنترنت.

تجدر الإشارة إلى أنّ تطبيقك يجب أن يضيف عمليات قطع الاتصال إلى قائمة الانتظار قبل أن يتم وضع علامة على المستخدم على الإنترنت، لتجنُّب حدوث أي حالات تعارض في حال فقدان اتصال العميل بالشبكة قبل إرسال الأمرَين إلى الخادم.

إليك نظام بسيط لتواجد المستخدم:

Swift

ملاحظة: لا يتوفّر منتج 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())
})

Objective-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]];
  }
}];