Funkcje offline na platformach Apple

Aplikacje Firebase działają nawet wtedy, gdy aplikacja tymczasowo utraci połączenie z siecią. Firebase udostępnia też narzędzia do utrwalania danych lokalnie, zarządzania obecnością i obsługi opóźnień.

Utrwalanie na dysku

Aplikacje Firebase automatycznie obsługują tymczasowe przerwy w połączeniu z siecią. Dane w pamięci podręcznej są dostępne w trybie offline, a Firebase ponownie wysyła wszystkie zapisy po przywróceniu połączenia z siecią.

Gdy włączysz utrwalanie na dysku, aplikacja będzie zapisywać dane lokalnie na urządzeniu, dzięki czemu będzie mogła zachować stan w trybie offline, nawet jeśli użytkownik lub system operacyjny ponownie uruchomi aplikację.

Utrwalanie na dysku możesz włączyć za pomocą tylko 1 wiersza kodu.

Swift

Uwaga: ten produkt Firebase nie jest dostępny w przypadku celu App Clip.
Database.database().isPersistenceEnabled = true

Objective-C

Uwaga: ten produkt Firebase nie jest dostępny w przypadku celu App Clip.
[FIRDatabase database].persistenceEnabled = YES;

Zachowanie utrwalania

Gdy włączysz utrwalanie, wszystkie dane, które klient Firebase Realtime Database synchronizuje w trybie online, będą utrwalane na dysku i dostępne w trybie offline, nawet jeśli użytkownik lub system operacyjny ponownie uruchomi aplikację. Oznacza to, że aplikacja będzie działać tak jak w trybie online, korzystając z danych lokalnych przechowywanych w pamięci podręcznej. Wywołania zwrotne odbiornika będą nadal wywoływane w przypadku aktualizacji lokalnych.

Klient Firebase Realtime Database automatycznie utrzymuje kolejkę wszystkich operacji zapisu wykonywanych, gdy aplikacja jest w trybie offline. Gdy utrwalanie jest włączone, ta kolejka jest też utrwalana na dysku, dzięki czemu wszystkie zapisy są dostępne, gdy użytkownik lub system operacyjny ponownie uruchomi aplikację. Gdy aplikacja odzyska połączenie, wszystkie operacje zostaną wysłane na serwer Firebase Realtime Database.

Jeśli aplikacja korzysta z uwierzytelniania Firebase, klient Firebase Realtime Database utrwala token uwierzytelniania użytkownika podczas ponownego uruchamiania aplikacji. Jeśli token uwierzytelniania wygaśnie, gdy aplikacja jest w trybie offline, klient wstrzyma operacje zapisu do czasu ponownego uwierzytelnienia użytkownika przez aplikację. W przeciwnym razie operacje zapisu mogą się nie powieść z powodu reguł zabezpieczeń.

Odświeżanie danych

Firebase Realtime Database synchronizuje i przechowuje lokalną kopię danych dla aktywnych odbiorników. Możesz też synchronizować określone lokalizacje in sync.

Swift

Uwaga: ten produkt Firebase nie jest dostępny w przypadku celu App Clip.
let scoresRef = Database.database().reference(withPath: "scores")
scoresRef.keepSynced(true)

Objective-C

Uwaga: ten produkt Firebase nie jest dostępny w przypadku celu App Clip.
FIRDatabaseReference *scoresRef = [[FIRDatabase database] referenceWithPath:@"scores"];
[scoresRef keepSynced:YES];

Klient Firebase Realtime Database automatycznie pobiera dane z tych lokalizacji i synchronizuje je, nawet jeśli odwołanie nie ma aktywnych odbiorników. Synchronizację możesz wyłączyć za pomocą tego wiersza kodu.

Swift

Uwaga: ten produkt Firebase nie jest dostępny w przypadku celu App Clip.
scoresRef.keepSynced(false)

Objective-C

Uwaga: ten produkt Firebase nie jest dostępny w przypadku celu App Clip.
[scoresRef keepSynced:NO];

Domyślnie w pamięci podręcznej przechowywane jest 10 MB wcześniej zsynchronizowanych danych. Powinno to wystarczyć w przypadku większości aplikacji. Jeśli pamięć podręczna przekroczy skonfigurowany rozmiar, Firebase Realtime Database usunie dane, które były używane najrzadziej. Dane, które są synchronizowane, nie są usuwane z pamięci podręcznej.

Wykonywanie zapytań o dane w trybie offline

Firebase Realtime Database przechowuje dane zwrócone przez zapytanie do użycia w trybie offline. W przypadku zapytań utworzonych w trybie offline, Firebase Realtime Database nadal działa na podstawie wcześniej wczytanych danych. Jeśli żądane dane nie zostały wczytane, Firebase Realtime Database wczytuje dane z lokalnej pamięci podręcznej. Gdy połączenie z siecią zostanie przywrócone, dane zostaną wczytane i będą odzwierciedlać zapytanie.

Na przykład ten kod wysyła zapytanie o 4 ostatnie elementy w bazie danych czasu rzeczywistego Firebase zawierającej wyniki:Firebase Realtime Database

Swift

Uwaga: ten produkt Firebase nie jest dostępny w przypadku celu 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

Uwaga: ten produkt Firebase nie jest dostępny w przypadku celu 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);
    }];

Załóżmy, że użytkownik traci połączenie, przechodzi w tryb offline i ponownie uruchamia aplikację. W trybie offline aplikacja wysyła zapytanie o 2 ostatnie elementy z tej samej lokalizacji. To zapytanie zwróci 2 ostatnie elementy ponieważ aplikacja wczytała wszystkie 4 elementy w zapytaniu powyżej.

Swift

Uwaga: ten produkt Firebase nie jest dostępny w przypadku celu App Clip.
scoresRef.queryOrderedByValue().queryLimited(toLast: 2).observe(.childAdded) { snapshot in
  print("The \(snapshot.key) dinosaur's score is \(snapshot.value ?? "null")")
}

Objective-C

Uwaga: ten produkt Firebase nie jest dostępny w przypadku celu App Clip.
[[[scoresRef queryOrderedByValue] queryLimitedToLast:2]
    observeEventType:FIRDataEventTypeChildAdded withBlock:^(FIRDataSnapshot *snapshot) {
      NSLog(@"The %@ dinosaur's score is %@", snapshot.key, snapshot.value);
    }];

W poprzednim przykładzie klient Firebase Realtime Database wywołuje zdarzenia „child added” (dodano element podrzędny) dla 2 dinozaurów z najwyższym wynikiem, korzystając z utrwalonej pamięci podręcznej. Nie wywoła jednak zdarzenia „value” (wartość), ponieważ aplikacja nigdy nie wykonała tego zapytania w trybie online.

Jeśli aplikacja poprosi o 6 ostatnich elementów w trybie offline, od razu otrzyma „child added” zdarzenia dla 4 elementów w pamięci podręcznej. Gdy urządzenie wróci do trybu online, klient Firebase Realtime Database zsynchronizuje się z serwerem i otrzyma 2 ostatnie zdarzenia „child added” oraz zdarzenie „value” dla aplikacji.

Obsługa transakcji w trybie offline

Wszystkie transakcje wykonywane, gdy aplikacja jest w trybie offline, są umieszczane w kolejce. Gdy aplikacja odzyska połączenie z siecią, transakcje zostaną wysłane na serwer Realtime Database.

Zarządzanie obecnością

W aplikacjach czasu rzeczywistego często przydaje się wykrywanie, kiedy klienci łączą się i rozłączają. Możesz na przykład oznaczyć użytkownika jako „offline”, gdy jego klient się rozłączy.

Klienci Bazy danych Firebase udostępniają proste elementy pierwotne, których możesz używać do zapisywania w bazie danych, gdy klient rozłączy się z serwerami Bazy danych Firebase serwerów. Te aktualizacje występują niezależnie od tego, czy klient rozłączy się prawidłowo, czy nie, więc możesz na nich polegać w zakresie zwalniania miejsca na dane, nawet jeśli połączenie zostanie przerwane lub klient ulegnie awarii. Po rozłączeniu można wykonywać wszystkie operacje zapisu, w tym ustawianie, aktualizowanie i usuwanie.

Oto prosty przykład zapisywania danych po rozłączeniu za pomocą elementu pierwotnego onDisconnect:

Swift

Uwaga: ten produkt Firebase nie jest dostępny w przypadku celu App Clip.
let presenceRef = Database.database().reference(withPath: "disconnectmessage");
// Write a string when this client loses connection
presenceRef.onDisconnectSetValue("I disconnected!")

Objective-C

Uwaga: ten produkt Firebase nie jest dostępny w przypadku celu App Clip.
FIRDatabaseReference *presenceRef = [[FIRDatabase database] referenceWithPath:@"disconnectmessage"];
// Write a string when this client loses connection
[presenceRef onDisconnectSetValue:@"I disconnected!"];

Jak działa funkcja onDisconnect

Gdy ustanowisz operację onDisconnect(), będzie ona działać na serwerze Firebase Realtime Database. Serwer sprawdza zabezpieczenia, aby upewnić się, że użytkownik może wykonać żądane zdarzenie zapisu, i informuje aplikację, jeśli jest ono nieprawidłowe. Następnie serwer monitoruje połączenie. Jeśli w dowolnym momencie połączenie zostanie przerwane lub zostanie aktywnie zamknięte przez klienta Realtime Database, serwer ponownie sprawdzi zabezpieczenia (aby upewnić się, że operacja jest nadal prawidłowa), a następnie wywoła zdarzenie.

Aplikacja może użyć wywołania zwrotnego w operacji zapisu aby upewnić się, że onDisconnect została prawidłowo dołączona:

Swift

Uwaga: ten produkt Firebase nie jest dostępny w przypadku celu App Clip.
presenceRef.onDisconnectRemoveValue { error, reference in
  if let error = error {
    print("Could not establish onDisconnect event: \(error)")
  }
}

Objective-C

Uwaga: ten produkt Firebase nie jest dostępny w przypadku celu App Clip.
[presenceRef onDisconnectRemoveValueWithCompletionBlock:^(NSError *error, FIRDatabaseReference *reference) {
  if (error != nil) {
    NSLog(@"Could not establish onDisconnect event: %@", error);
  }
}];

Zdarzenie onDisconnect można też anulować, wywołując metodę .cancel():

Swift

Uwaga: ten produkt Firebase nie jest dostępny w przypadku celu App Clip.
presenceRef.onDisconnectSetValue("I disconnected")
// some time later when we change our minds
presenceRef.cancelDisconnectOperations()

Objective-C

Uwaga: ten produkt Firebase nie jest dostępny w przypadku celu App Clip.
[presenceRef onDisconnectSetValue:@"I disconnected"];
// some time later when we change our minds
[presenceRef cancelDisconnectOperations];

Wykrywanie stanu połączenia

W przypadku wielu funkcji związanych z obecnością przydaje się, gdy aplikacja wie, czy jest online, czy offline. Firebase Realtime Database udostępnia specjalną lokalizację /.info/connected, która jest aktualizowana za każdym razem, gdy zmienia się stan połączenia klienta Firebase Realtime Database. Oto przykład:

Swift

Uwaga: ten produkt Firebase nie jest dostępny w przypadku celu 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

Uwaga: ten produkt Firebase nie jest dostępny w przypadku celu 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 to wartość logiczna, która nie jest synchronizowana między klientami Realtime Database ponieważ zależy od stanu klienta. Innymi słowy, jeśli jeden klient odczyta wartość /.info/connected jako false, nie ma gwarancji, że inny klient też odczyta wartość false.

Obsługa opóźnień

Sygnatury czasowe serwera

Serwery Firebase Realtime Database udostępniają mechanizm wstawiania sygnatur czasowych generowanych na serwerze jako danych. Ta funkcja w połączeniu z onDisconnect, ułatwia niezawodne zapisywanie czasu, w którym klient Realtime Database się rozłączył:

Swift

Uwaga: ten produkt Firebase nie jest dostępny w przypadku celu App Clip.
let userLastOnlineRef = Database.database().reference(withPath: "users/morgan/lastOnline")
userLastOnlineRef.onDisconnectSetValue(ServerValue.timestamp())

Objective-C

Uwaga: ten produkt Firebase nie jest dostępny w przypadku celu App Clip.
FIRDatabaseReference *userLastOnlineRef = [[FIRDatabase database] referenceWithPath:@"users/morgan/lastOnline"];
[userLastOnlineRef onDisconnectSetValue:[FIRServerValue timestamp]];

Rozbieżność czasu

Chociaż firebase.database.ServerValue.TIMESTAMP jest znacznie dokładniejsza i preferowana w przypadku większości operacji odczytu i zapisu, czasami może się przydać oszacowanie rozbieżności czasu klienta w stosunku do serwerów Firebase Realtime Database. Aby uzyskać wartość w milisekundach, którą klienci Firebase Realtime Database dodają do lokalnego czasu (czas epoki w milisekundach), aby oszacować czas serwera, możesz dołączyć wywołanie zwrotne do lokalizacji /.info/serverTimeOffset. Pamiętaj, że na dokładność tego przesunięcia może wpływać opóźnienie sieci, dlatego jest ono przydatne głównie do wykrywania dużych (> 1 sekunda) rozbieżności w czasie.

Swift

Uwaga: ten produkt Firebase nie jest dostępny w przypadku celu 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

Uwaga: ten produkt Firebase nie jest dostępny w przypadku celu 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);
}];

Przykładowa aplikacja do zarządzania obecnością

Łącząc operacje rozłączania z monitorowaniem stanu połączenia i sygnaturami czasowymi serwera, możesz utworzyć system obecności użytkowników. W tym systemie, każdy użytkownik przechowuje dane w lokalizacji bazy danych, aby wskazać, czy klient Realtime Database jest online. Gdy klienci przechodzą do trybu online, ustawiają tę lokalizację na true , a gdy się rozłączają, ustawiają sygnaturę czasową. Ta sygnatura czasowa wskazuje, kiedy dany użytkownik był ostatnio online.

Pamiętaj, że aplikacja powinna umieścić operacje rozłączania w kolejce, zanim użytkownik zostanie oznaczony jako online, aby uniknąć sytuacji wyścigu, gdy połączenie sieciowe klienta zostanie utracone, zanim oba polecenia zostaną wysłane na serwer.

Oto prosty system obecności użytkowników:

Swift

Uwaga: ten produkt Firebase nie jest dostępny w przypadku celu 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

Uwaga: ten produkt Firebase nie jest dostępny w przypadku celu 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]];
  }
}];