Автономные возможности на платформах Apple

Приложения Firebase работают даже в случае временной потери сетевого соединения. Кроме того, Firebase предоставляет инструменты для локального сохранения данных, управления присутствием и обработки задержек.

Сохранение данных на диске

Приложения Firebase автоматически обрабатывают временные перебои в сети. Кэшированные данные доступны в автономном режиме, и Firebase повторно отправляет все данные для записи при восстановлении сетевого соединения.

При включении сохранения данных на диске ваше приложение записывает данные локально на устройство, что позволяет ему сохранять состояние в автономном режиме, даже если пользователь или операционная система перезапустят приложение.

Включить сохранение данных на диск можно всего одной строкой кода.

Быстрый

Примечание: Этот продукт Firebase недоступен в целевом приложении App Clip.
Database.database().isPersistenceEnabled = true

Objective-C

Примечание: Этот продукт Firebase недоступен в целевом приложении App Clip.
[FIRDatabase database].persistenceEnabled = YES;

Настойчивое поведение

Включение функции сохранения данных позволяет клиенту Firebase Realtime Database синхронизировать любые данные, которые он обычно синхронизирует в онлайн-режиме, на диск, делая их доступными в автономном режиме, даже после перезапуска приложения пользователем или операционной системой. Это означает, что ваше приложение будет работать так же, как и в онлайн-режиме, используя локальные данные, хранящиеся в кэше. Обратные вызовы слушателей будут продолжать срабатывать при локальных обновлениях.

The Firebase Realtime Database client automatically keeps a queue of all write operations that are performed while your app is offline. When persistence is enabled, this queue is also persisted to disk so all of your writes are available when the user or operating system restarts the app. When the app regains connectivity, all of the operations are sent to the Firebase Realtime Database server.

Если ваше приложение использует аутентификацию Firebase , клиент Firebase Realtime Database сохраняет токен аутентификации пользователя после перезапуска приложения. Если срок действия токена аутентификации истекает, пока ваше приложение находится в автономном режиме, клиент приостанавливает операции записи до тех пор, пока ваше приложение не проверит пользователя повторно; в противном случае операции записи могут завершиться неудачей из-за правил безопасности.

Поддержание актуальности данных

Firebase Realtime Database синхронизирует и хранит локальную копию данных для активных слушателей. Кроме того, вы можете синхронизировать данные в определенных местах.

Быстрый

Примечание: Этот продукт 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 Realtime Database автоматически загружает данные в указанные места и поддерживает их синхронизацию, даже если у ссылки нет активных слушателей. Вы можете отключить синхронизацию с помощью следующей строки кода.

Быстрый

Примечание: Этот продукт Firebase недоступен в целевом приложении App Clip.
scoresRef.keepSynced(false)

Objective-C

Примечание: Этот продукт Firebase недоступен в целевом приложении App Clip.
[scoresRef keepSynced:NO];

По умолчанию кэшируется 10 МБ ранее синхронизированных данных. Этого должно быть достаточно для большинства приложений. Если размер кэша превышает заданный размер, Firebase Realtime Database удаляет данные, которые использовались реже всего. Данные, которые синхронизируются, из кэша не удаляются.

Запрос данных в автономном режиме

The Firebase Realtime Database stores data returned from a query for use when offline. For queries constructed while offline, the Firebase Realtime Database continues to work for previously loaded data. If the requested data hasn't loaded, the Firebase Realtime Database loads data from the local cache. When network connectivity is available again, the data loads and will reflect the query.

Например, этот код запрашивает последние четыре элемента из 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")")
}

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

Предположим, пользователь теряет соединение, переходит в офлайн-режим и перезапускает приложение. Находясь в офлайн-режиме, приложение запрашивает последние два элемента из того же местоположения. Этот запрос успешно вернет последние два элемента, поскольку приложение загрузило все четыре элемента из предыдущего запроса.

Быстрый

Примечание: Этот продукт 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 Realtime Database генерирует события «дочерний элемент добавлен» для двух динозавров с наивысшим баллом, используя сохраненный кэш. Однако он не будет генерировать событие «значение», поскольку приложение никогда не выполняло этот запрос в режиме онлайн.

Если приложение запросит последние шесть элементов в автономном режиме, оно сразу же получит события «дочерние элементы добавлены» для четырех кэшированных элементов. Когда устройство снова подключится к сети, клиент Firebase Realtime Database синхронизируется с сервером и получит последние два события «дочерние элементы добавлены» и событие «значение» для приложения.

Обработка транзакций в автономном режиме

Все транзакции, выполняемые в автономном режиме приложения, ставятся в очередь. Как только приложение восстанавливает сетевое соединение, транзакции отправляются на сервер Realtime Database .

Управление присутствием

В приложениях, работающих в режиме реального времени, часто бывает полезно отслеживать подключение и отключение клиентов. Например, может потребоваться пометить пользователя как «оффлайн», когда его клиент отключается.

Firebase Database clients provide simple primitives that you can use to write to the database when a client disconnects from the Firebase Database servers. These updates occur whether the client disconnects cleanly or not, so you can rely on them to clean up data even if a connection is dropped or a client crashes. All write operations, including setting, updating, and removing, can be performed upon a disconnection.

Вот простой пример записи данных при отключении с использованием примитива onDisconnect :

Быстрый

Примечание: Этот продукт 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!"];

Как работает onDisconnect

When you establish an onDisconnect() operation, the operation lives on the Firebase Realtime Database server. The server checks security to make sure the user can perform the write event requested, and informs your app if it is invalid. The server then monitors the connection. If at any point the connection times out, or is actively closed by the Realtime Database client, the server checks security a second time (to make sure the operation is still valid) and then invokes the event.

Ваше приложение может использовать функцию обратного вызова при операции записи, чтобы убедиться, что onDisconnect было установлено корректно:

Быстрый

Примечание: Этот продукт 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() :

Быстрый

Примечание: Этот продукт 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 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")
  }
})

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 является логическим значением, которое не синхронизируется между клиентами 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())

Objective-C

Примечание: Этот продукт Firebase недоступен в целевом приложении App Clip.
FIRDatabaseReference *userLastOnlineRef = [[FIRDatabase database] referenceWithPath:@"users/morgan/lastOnline"];
[userLastOnlineRef onDisconnectSetValue:[FIRServerValue timestamp]];

Смещение тактовой частоты

While firebase.database.ServerValue.TIMESTAMP is much more accurate, and preferable for most read/write operations, it can occasionally be useful to estimate the client's clock skew with respect to the Firebase Realtime Database 's servers. You can attach a callback to the location /.info/serverTimeOffset to obtain the value, in milliseconds, that Firebase Realtime Database clients add to the local reported time (epoch time in milliseconds) to estimate the server time. Note that this offset's accuracy can be affected by networking latency, and so is useful primarily for discovering large (> 1 second) discrepancies in clock time.

Быстрый

Примечание: Этот продукт 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);
}];

Пример приложения "Присутствие"

By combining disconnect operations with connection state monitoring and server timestamps, you can build a user presence system. In this system, each user stores data at a database location to indicate whether or not a Realtime Database client is online. Clients set this location to true when they come online and a timestamp when they disconnect. This timestamp indicates the last time the given user was online.

Обратите внимание, что ваше приложение должно ставить операции отключения в очередь до того, как пользователь будет отмечен как находящийся в сети, чтобы избежать состояний гонки в случае потери сетевого соединения клиента до того, как обе команды будут отправлены на сервер.

Вот простая система определения присутствия пользователя:

Быстрый

Примечание: Этот продукт 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]];
  }
}];