قدرات غير متصلة بالإنترنت على منصات Apple

تنظيم صفحاتك في مجموعات يمكنك حفظ المحتوى وتصنيفه حسب إعداداتك المفضّلة.

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

ثبات القرص

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

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

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

سويفت

ملاحظة: منتج Firebase هذا غير متاح في هدف مقطع التطبيق.
Database.database().isPersistenceEnabled = true

ج موضوعية

ملاحظة: منتج Firebase هذا غير متاح في هدف مقطع التطبيق.
[FIRDatabase database].persistenceEnabled = YES;

سلوك الإصرار

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

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

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

تحديث البيانات باستمرار

تقوم قاعدة بيانات Firebase Realtime بمزامنة وتخزين نسخة محلية من البيانات للمستمعين النشطين. بالإضافة إلى ذلك ، يمكنك الاحتفاظ بمواقع محددة متزامنة.

سويفت

ملاحظة: منتج Firebase هذا غير متاح في هدف مقطع التطبيق.
let scoresRef = Database.database().reference(withPath: "scores")
scoresRef.keepSynced(true)

ج موضوعية

ملاحظة: منتج Firebase هذا غير متاح في هدف مقطع التطبيق.
FIRDatabaseReference *scoresRef = [[FIRDatabase database] referenceWithPath:@"scores"];
[scoresRef keepSynced:YES];

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

سويفت

ملاحظة: منتج Firebase هذا غير متاح في هدف مقطع التطبيق.
scoresRef.keepSynced(false)

ج موضوعية

ملاحظة: منتج Firebase هذا غير متاح في هدف مقطع التطبيق.
[scoresRef keepSynced:NO];

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

الاستعلام عن البيانات دون اتصال

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

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

سويفت

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

ج موضوعية

ملاحظة: منتج 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);
    }];

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

سويفت

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

ج موضوعية

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

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

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

التعامل مع المعاملات دون اتصال

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

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

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

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

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

سويفت

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

ج موضوعية

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

كيف يعمل قطع الاتصال

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

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

سويفت

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

ج موضوعية

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

يمكن أيضًا إلغاء حدث onDisconnect عن طريق استدعاء .cancel() :

سويفت

ملاحظة: منتج Firebase هذا غير متاح في هدف مقطع التطبيق.
presenceRef.onDisconnectSetValue("I disconnected")
// some time later when we change our minds
presenceRef.cancelDisconnectOperations()

ج موضوعية

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

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

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

سويفت

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

ج موضوعية

ملاحظة: منتج 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 آلية لإدراج الطوابع الزمنية التي تم إنشاؤها على الخادم كبيانات. توفر هذه الميزة ، جنبًا إلى جنب مع onDisconnect ، طريقة سهلة لتدوين الوقت الذي تم فيه فصل عميل Realtime Database بشكل موثوق:

سويفت

ملاحظة: منتج Firebase هذا غير متاح في هدف مقطع التطبيق.
let userLastOnlineRef = Database.database().reference(withPath: "users/morgan/lastOnline")
userLastOnlineRef.onDisconnectSetValue(ServerValue.timestamp())

ج موضوعية

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

انحراف ساعة

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

سويفت

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

ج موضوعية

ملاحظة: منتج 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 متصل بالإنترنت أم لا. يقوم العملاء بتعيين هذا الموقع على "صحيح" عند الاتصال بالإنترنت والطابع الزمني عند قطع الاتصال. يشير هذا الطابع الزمني إلى آخر مرة كان فيها المستخدم متصلاً بالإنترنت.

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

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

سويفت

ملاحظة: منتج 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 let connected = snapshot.value as? Bool, connected 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())
})

ج موضوعية

ملاحظة: منتج 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]];
  }
}];