Funkcje offline na platformach Apple

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

Trwałość dysku

Aplikacje Firebase automatycznie obsługują tymczasowe przerwy w działaniu 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 trwałość dysku, aplikacja zapisuje dane lokalnie na urządzeniu, dzięki czemu aplikacja może utrzymywać stan w trybie offline, nawet jeśli użytkownik lub system operacyjny ponownie uruchomi aplikację.

Trwałość dysku możesz włączyć za pomocą jednego wiersza kodu.

Swift

Uwaga: ta usługa Firebase nie jest dostępna w celu wycinka aplikacji.
Database.database().isPersistenceEnabled = true

Objective-C

Uwaga: ta usługa Firebase nie jest dostępna w celu wycinka aplikacji.
[FIRDatabase database].persistenceEnabled = YES;

Zachowanie trwałości

Jeśli włączysz trwałość, wszystkie dane, które klient Bazy danych czasu rzeczywistego Firebase będą synchronizowane, gdy będą w trybie online, pozostaną na dysku i będą dostępne offline, nawet gdy użytkownik lub system operacyjny ponownie uruchomi aplikację. Oznacza to, że aplikacja będzie działać tak samo jak w trybie online, używając lokalnych danych przechowywanych w pamięci podręcznej. Wywołania zwrotne detektora będą nadal uruchamiane w przypadku aktualizacji lokalnych.

Klient Bazy danych czasu rzeczywistego Firebase automatycznie przechowuje kolejkę wszystkich operacji zapisu wykonywanych, gdy aplikacja jest offline. Gdy trwałość jest włączona, kolejka jest również zachowywana na dysku, dzięki czemu wszystkie zapisy są dostępne po ponownym uruchomieniu aplikacji przez użytkownika lub system operacyjny. Gdy aplikacja odzyska połączenie, wszystkie operacje są wysyłane na serwer Bazy danych czasu rzeczywistego Firebase.

Jeśli Twoja aplikacja korzysta z uwierzytelniania Firebase, klient Bazy danych czasu rzeczywistego Firebase będzie utrzymywać token uwierzytelniania użytkownika po każdym ponownym uruchomieniu aplikacji. Jeśli token uwierzytelniania wygaśnie, gdy aplikacja będzie w trybie offline, klient wstrzyma operacje zapisu do czasu ponownego uwierzytelnienia użytkownika w aplikacji. W przeciwnym razie operacje zapisu mogą się nie powieść ze względu na reguły zabezpieczeń.

Dbanie o aktualność danych

Baza danych czasu rzeczywistego Firebase synchronizuje i przechowuje lokalną kopię danych dla aktywnych detektorów. Dodatkowo możesz synchronizować określone lokalizacje.

Swift

Uwaga: ta usługa Firebase nie jest dostępna w celu wycinka aplikacji.
let scoresRef = Database.database().reference(withPath: "scores")
scoresRef.keepSynced(true)

Objective-C

Uwaga: ta usługa Firebase nie jest dostępna w celu wycinka aplikacji.
FIRDatabaseReference *scoresRef = [[FIRDatabase database] referenceWithPath:@"scores"];
[scoresRef keepSynced:YES];

Klient Bazy danych czasu rzeczywistego Firebase automatycznie pobiera dane z tych lokalizacji i synchronizuje je, nawet jeśli plik referencyjny nie ma aktywnych detektorów. Synchronizację możesz wyłączyć z powrotem, korzystając z następującego wiersza kodu.

Swift

Uwaga: ta usługa Firebase nie jest dostępna w celu wycinka aplikacji.
scoresRef.keepSynced(false)

Objective-C

Uwaga: ta usługa Firebase nie jest dostępna w celu wycinka aplikacji.
[scoresRef keepSynced:NO];

Domyślnie w pamięci podręcznej przechowywane jest 10 MB wcześniej zsynchronizowanych danych. To powinno wystarczyć w większości aplikacji. Jeśli pamięć podręczna przekroczy skonfigurowany rozmiar, Baza danych czasu rzeczywistego Firebase trwale usunie dane, które były ostatnio używane. Zsynchronizowane dane nie są trwale usuwane z pamięci podręcznej.

Wysyłanie zapytań o dane offline

Baza danych czasu rzeczywistego Firebase przechowuje dane zwrócone z zapytania do użytku w trybie offline. W przypadku zapytań utworzonych w trybie offline Baza danych czasu rzeczywistego Firebase działa z danymi wczytywanymi wcześniej. Jeśli żądane dane nie zostały wczytane, Baza danych czasu rzeczywistego Firebase wczytuje dane z lokalnej pamięci podręcznej. Gdy połączenie sieciowe będzie ponownie dostępne, dane wczytają się i będą odzwierciedlały zapytanie.

Na przykład ten kod wysyła zapytania dotyczące ostatnich 4 elementów w Bazie danych czasu rzeczywistego Firebase z wynikami

Swift

Uwaga: ta usługa Firebase nie jest dostępna w celu wycinka aplikacji.
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: ta usługa Firebase nie jest dostępna w celu wycinka aplikacji.
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ę. Gdy nadal jesteś offline, aplikacja wysyła zapytanie o ostatnie 2 elementy z tego samego miejsca. To zapytanie zwróci ostatnie 2 elementy, ponieważ aplikacja wczytała wszystkie 4 elementy z powyższego zapytania.

Swift

Uwaga: ta usługa Firebase nie jest dostępna w celu wycinka aplikacji.
scoresRef.queryOrderedByValue().queryLimited(toLast: 2).observe(.childAdded) { snapshot in
  print("The \(snapshot.key) dinosaur's score is \(snapshot.value ?? "null")")
}

Objective-C

Uwaga: ta usługa Firebase nie jest dostępna w celu wycinka aplikacji.
[[[scoresRef queryOrderedByValue] queryLimitedToLast:2]
    observeEventType:FIRDataEventTypeChildAdded withBlock:^(FIRDataSnapshot *snapshot) {
      NSLog(@"The %@ dinosaur's score is %@", snapshot.key, snapshot.value);
    }];

W poprzednim przykładzie klient Bazy danych czasu rzeczywistego Firebase generuje zdarzenia „dodane przez dziecko” w przypadku najwyżej punktowanych 2 dinozaurów, używając zapisanej pamięci podręcznej. Nie spowoduje to jednak wywołania zdarzenia „value”, ponieważ aplikacja nigdy nie wykonała tego zapytania w trybie online.

Jeśli aplikacja poprosi o ostatnie 6 elementów w trybie offline, otrzyma od razu zdarzenia „dodane przez wydawcę” dla tych 4 elementów w pamięci podręcznej. Gdy urządzenie znów będzie online, klient Bazy danych czasu rzeczywistego Firebase zsynchronizuje się z serwerem i pobierze 2 ostatnie zdarzenia „dodano element podrzędny” i „wartość” aplikacji.

Obsługa transakcji offline

Wszystkie transakcje wykonane, gdy aplikacja jest offline, są umieszczane w kolejce. Gdy aplikacja odzyska połączenie sieciowe, transakcje są wysyłane do serwera Bazy danych czasu rzeczywistego.

Zarządzanie obecnością

W aplikacjach działających w czasie rzeczywistym często przydaje się możliwość wykrywania, kiedy klienty łączą się i rozłączają. Możesz na przykład oznaczyć użytkownika jako użytkownika offline, gdy jego klient się rozłączy.

Klienty bazy danych Firebase udostępniają proste elementy podstawowe, których można używać do zapisywania w bazie danych, gdy klient odłączy się od serwerów bazy danych Firebase. Aktualizacje te są przeprowadzane niezależnie od tego, czy klient się rozłącza, czy też nie. Dzięki temu możesz mieć pewność, że dane zostaną wyczyszczone nawet w przypadku utraty połączenia lub awarii klienta. Wszystkie operacje zapisu, w tym ustawianie, aktualizowanie i usuwanie, mogą być wykonywane po odłączeniu.

Oto prosty przykład zapisywania danych po odłączeniu za pomocą elementu podstawowego onDisconnect:

Swift

Uwaga: ta usługa Firebase nie jest dostępna w celu wycinka aplikacji.
let presenceRef = Database.database().reference(withPath: "disconnectmessage");
// Write a string when this client loses connection
presenceRef.onDisconnectSetValue("I disconnected!")

Objective-C

Uwaga: ta usługa Firebase nie jest dostępna w celu wycinka aplikacji.
FIRDatabaseReference *presenceRef = [[FIRDatabase database] referenceWithPath:@"disconnectmessage"];
// Write a string when this client loses connection
[presenceRef onDisconnectSetValue:@"I disconnected!"];

Jak onOdłącz działa?

Gdy utworzysz operację onDisconnect(), będzie ona działać na serwerze Bazy danych czasu rzeczywistego Firebase. Serwer sprawdza zabezpieczenia, by upewnić się, że użytkownik może wykonać żądane zdarzenie zapisu, i informuje aplikację, jeśli to zdarzenie jest nieprawidłowe. Serwer będzie monitorować połączenie. Jeśli w którymkolwiek momencie połączenie zostanie przekroczone lub zostanie aktywnie zamknięte przez klienta Bazy danych czasu rzeczywistego, serwer jeszcze raz sprawdzi zabezpieczenia (aby upewnić się, że operacja jest nadal prawidłowa), a następnie wywoła zdarzenie.

Aplikacja może używać wywołania zwrotnego podczas zapisu, aby sprawdzić, czy onDisconnect został prawidłowo podłączony:

Swift

Uwaga: ta usługa Firebase nie jest dostępna w celu wycinka aplikacji.
presenceRef.onDisconnectRemoveValue { error, reference in
  if let error = error {
    print("Could not establish onDisconnect event: \(error)")
  }
}

Objective-C

Uwaga: ta usługa Firebase nie jest dostępna w celu wycinka aplikacji.
[presenceRef onDisconnectRemoveValueWithCompletionBlock:^(NSError *error, FIRDatabaseReference *reference) {
  if (error != nil) {
    NSLog(@"Could not establish onDisconnect event: %@", error);
  }
}];

Wydarzenie onDisconnect można też anulować, dzwoniąc pod numer .cancel():

Swift

Uwaga: ta usługa Firebase nie jest dostępna w celu wycinka aplikacji.
presenceRef.onDisconnectSetValue("I disconnected")
// some time later when we change our minds
presenceRef.cancelDisconnectOperations()

Objective-C

Uwaga: ta usługa Firebase nie jest dostępna w celu wycinka aplikacji.
[presenceRef onDisconnectSetValue:@"I disconnected"];
// some time later when we change our minds
[presenceRef cancelDisconnectOperations];

Wykrywam stan połączenia

W przypadku wielu funkcji związanych z obecnością aplikacja może wiedzieć, czy jest online czy offline. Baza danych czasu rzeczywistego Firebase udostępnia specjalną lokalizację pod adresem /.info/connected, która jest aktualizowana za każdym razem, gdy zmienia się stan połączenia klienta Bazy danych czasu rzeczywistego Firebase. Oto przykład:

Swift

Uwaga: ta usługa Firebase nie jest dostępna w celu wycinka aplikacji.
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: ta usługa Firebase nie jest dostępna w celu wycinka aplikacji.
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 Bazy danych czasu rzeczywistego, ponieważ zależy od stanu klienta. Inaczej mówiąc, jeśli jeden klient odczyta /.info/connected jako fałsz, nie ma gwarancji, że inny klient również odczyta ten parametr.

Czas oczekiwania na obsługę

Sygnatury czasowe serwera

Serwery Bazy danych czasu rzeczywistego Firebase udostępniają mechanizm wstawiania sygnatur czasowych wygenerowanych na serwerze jako dane. Ta funkcja w połączeniu z funkcją onDisconnect pozwala w łatwy sposób zanotować moment, w którym klient Bazy danych czasu rzeczywistego rozłączył się:

Swift

Uwaga: ta usługa Firebase nie jest dostępna w celu wycinka aplikacji.
let userLastOnlineRef = Database.database().reference(withPath: "users/morgan/lastOnline")
userLastOnlineRef.onDisconnectSetValue(ServerValue.timestamp())

Objective-C

Uwaga: ta usługa Firebase nie jest dostępna w celu wycinka aplikacji.
FIRDatabaseReference *userLastOnlineRef = [[FIRDatabase database] referenceWithPath:@"users/morgan/lastOnline"];
[userLastOnlineRef onDisconnectSetValue:[FIRServerValue timestamp]];

Zniekształcenie zegara

Chociaż metoda firebase.database.ServerValue.TIMESTAMP jest znacznie dokładniejsza i zalecana w przypadku większości operacji odczytu i zapisu, czasami może się przydać do oszacowania zniekształcenia zegara klienta w odniesieniu do serwerów Bazy danych czasu rzeczywistego Firebase. Możesz dołączyć wywołanie zwrotne do lokalizacji /.info/serverTimeOffset, aby uzyskać (w milisekundach) wartość, którą klienty Bazy danych czasu rzeczywistego Firebase dodają do lokalnego czasu zgłaszanego (w milisekundach) w celu oszacowania czasu serwera. Pamiętaj, że opóźnienie sieciowe może mieć wpływ na dokładność przesunięcia, dlatego jest ono przydatne głównie przy wykrywaniu dużych (powyżej 1 sekundy) rozbieżności w czasie zegara.

Swift

Uwaga: ta usługa Firebase nie jest dostępna w celu wycinka aplikacji.
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: ta usługa Firebase nie jest dostępna w celu wycinka aplikacji.
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 Obecność

Łącząc operacje rozłączania z monitorowaniem stanu połączenia i sygnaturami czasowymi serwera, możesz utworzyć system wykrywania obecności użytkowników. W tym systemie każdy użytkownik przechowuje dane w lokalizacji bazy danych, aby wskazać, czy klient Bazy danych czasu rzeczywistego jest online. Klienty ustawiają tę lokalizację na Prawda, gdy są online, oraz sygnaturę czasową po rozłączeniu. Ta sygnatura czasowa wskazuje, kiedy dany użytkownik ostatnio był online.

Pamiętaj, że aplikacja powinna kolejkować operacje odłączania, zanim użytkownik zostanie oznaczony jako online. Pozwoli to uniknąć wyścigów w przypadku utraty połączenia sieciowego klienta przed wysłaniem obu poleceń na serwer.

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

Swift

Uwaga: ta usługa Firebase nie jest dostępna w celu wycinka aplikacji.
// 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: ta usługa Firebase nie jest dostępna w celu wycinka aplikacji.
// 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]];
  }
}];