Odczyt i zapis danych na platformach Apple

(Opcjonalnie) Prototypowanie i testowanie w Pakiecie emulatorów lokalnych Firebase

Zanim opowiemy o tym, jak Twoja aplikacja odczytuje dane w bazie danych czasu rzeczywistego i zapisy w niej, przedstawimy zestaw narzędzi, które można wykorzystać do prototypowania i testowania Bazy danych czasu rzeczywistego funkcje: Pakiet emulatorów lokalnych Firebase. Jeśli testujesz inne dane optymalizowanie reguł zabezpieczeń lub wyszukiwanie opłacalny sposób interakcji z backendem, który daje możliwość pracy lokalnej bez wdrażania aktywnych usług może być świetnym pomysłem.

Emulator Bazy danych czasu rzeczywistego jest częścią Pakietu emulatorów lokalnych, który umożliwia aplikacji interakcję z treścią i konfiguracją emulowanej bazy danych a także opcjonalnie emulowane zasoby projektu (funkcje, inne bazy danych, i reguły zabezpieczeń).

Aby użyć emulatora Bazy danych czasu rzeczywistego, wystarczy kilka kroków:

  1. Dodajesz wiersz kodu do konfiguracji testowej aplikacji, aby połączyć się z emulatorem.
  2. Uruchomienie firebase emulators:start w katalogu głównym projektu lokalnego.
  3. Wykonywanie wywołań z prototypowego kodu aplikacji za pomocą platformy Bazy danych czasu rzeczywistego pakietu SDK lub interfejsu API REST Realtime Database.

Dostępny jest szczegółowy instrukcja korzystania z Bazy danych czasu rzeczywistego i Cloud Functions. Zapoznaj się też z wprowadzeniem do Pakietu emulatorów lokalnych.

Pobieranie odniesienia do bazy danych FIRDatabase

Aby móc odczytywać lub zapisywać dane z bazy danych, musisz mieć instancję FIRDatabaseReference:

Swift

Uwaga: ta usługa Firebase nie jest dostępna w celu wycinka aplikacji.
var ref: DatabaseReference!

ref = Database.database().reference()

Objective-C

Uwaga: ta usługa Firebase nie jest dostępna w celu wycinka aplikacji.
@property (strong, nonatomic) FIRDatabaseReference *ref;

self.ref = [[FIRDatabase database] reference];

Zapisywanie danych

Ten dokument zawiera podstawowe informacje o odczytywaniu i zapisywaniu danych Firebase.

Dane Firebase są zapisywane w odwołaniu Database i pobierane przez przez dodanie detektora asynchronicznego do pliku referencyjnego. Detektor zostaje aktywowany raz dla początkowego stanu danych i ponownie po każdej zmianie danych.

Podstawowe operacje zapisu

W przypadku podstawowych operacji zapisu możesz użyć funkcji setValue, aby zapisać dane w określonym zastępując wszystkie istniejące dane w tej ścieżce. Możesz użyć tej metody, aby:

  • Typy kart odpowiadające dostępnym typom JSON:
    • NSString
    • NSNumber
    • NSDictionary
    • NSArray

Użytkownik z adresem setValue możesz np. dodać w ten sposób:

Swift

Uwaga: ta usługa Firebase nie jest dostępna w celu wycinka aplikacji.
self.ref.child("users").child(user.uid).setValue(["username": username])

Objective-C

Uwaga: ta usługa Firebase nie jest dostępna w celu wycinka aplikacji.
[[[self.ref child:@"users"] child:authResult.user.uid]
    setValue:@{@"username": username}];

Użycie tego parametru setValue spowoduje zastąpienie danych w określonej lokalizacji, łącznie z węzłami podrzędnymi. Nadal możesz jednak zaktualizować dziecko bez cały obiekt jest tworzony na nowo. Jeśli chcesz zezwolić użytkownikom na aktualizowanie swoich profili możesz zmienić nazwę użytkownika w następujący sposób:

Swift

Uwaga: ta usługa Firebase nie jest dostępna w celu wycinka aplikacji.
self.ref.child("users/\(user.uid)/username").setValue(username)

Objective-C

Uwaga: ta usługa Firebase nie jest dostępna w celu wycinka aplikacji.
[[[[_ref child:@"users"] child:user.uid] child:@"username"] setValue:username];

Odczyt danych

Odczytuj dane przez nasłuchiwanie zdarzeń wartości

Aby odczytać dane na ścieżce i nasłuchiwać zmian, użyj funkcji observeEventType:withBlock z FIRDatabaseReference do obserwacji Wydarzenia: FIRDataEventTypeValue.

Typ zdarzenia Typowe zastosowanie
FIRDataEventTypeValue Odczyt i nasłuchiwanie zmian w całej zawartości ścieżki.

Za pomocą zdarzenia FIRDataEventTypeValue możesz odczytywać dane w danej ścieżce, aktualny w momencie wystąpienia zdarzenia. Ta metoda jest wyzwalana jednorazowo, gdy za każdym razem, gdy dane, w tym dzieci, zmian. Wywołanie zwrotne zdarzenia jest przekazywane w postaci snapshot zawierającej wszystkie dane w lokalizacji, w tym danych podrzędnych. Jeśli nie ma danych, zrzut zwróci false, gdy wywołujesz exists() i nil po odczytaniu jego właściwości value.

W poniższym przykładzie pokazano, jak aplikacja do blogowania społecznościowego pobiera szczegóły posta z bazy danych:

Swift

Uwaga: ta usługa Firebase nie jest dostępna w celu wycinka aplikacji.
refHandle = postRef.observe(DataEventType.value, with: { snapshot in
  // ...
})

Objective-C

Uwaga: ta usługa Firebase nie jest dostępna w celu wycinka aplikacji.
_refHandle = [_postRef observeEventType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot * _Nonnull snapshot) {
  NSDictionary *postDict = snapshot.value;
  // ...
}];

Detektor odbiera obiekt FIRDataSnapshot zawierający dane w określonym miejscu znajduje się w bazie danych w chwili wystąpienia zdarzenia we właściwości value. Ty mogą przypisać wartości do odpowiedniego typu natywnego, np. NSDictionary. Jeśli w danej lokalizacji nie ma danych, value ma wartość nil.

Odczytaj dane raz

Odczyt raz za pomocą getData()

Pakiet SDK służy do zarządzania interakcjami z serwerami baz danych niezależnie od tego, czy jest online lub offline.

Ogólnie do odczytu wartości zdarzeń należy używać opisanych powyżej technik aby otrzymywać powiadomienia o aktualizacjach danych z backendu. Te techniki zmniejszają wykorzystanie zasobów i płatności, są zoptymalizowane, aby zapewniać użytkownikom korzystają z internetu i offline.

Jeśli dane są potrzebne tylko raz, możesz użyć funkcji getData(), aby uzyskać zrzut z bazy danych. Jeśli z jakiegoś powodu getData() nie może zwrócić wartość serwera, klient sprawdzi pamięć podręczną w lokalnej pamięci masowej i zwróci błąd , jeśli nadal nie możesz znaleźć wartości.

Ten przykład pokazuje, jak pobrać nazwę użytkownika widoczną publicznie z bazy danych:

Swift

Uwaga: ta usługa Firebase nie jest dostępna w celu wycinka aplikacji.
do {
  let snapshot = try await ref.child("users/\(uid)/username").getData()
  let userName = snapshot.value as? String ?? "Unknown"
} catch {
  print(error)
}

Objective-C

Uwaga: ta usługa Firebase nie jest dostępna w celu wycinka aplikacji.
NSString *userPath = [NSString stringWithFormat:@"users/%@/username", uid];
[[ref child:userPath] getDataWithCompletionBlock:^(NSError * _Nullable error, FIRDataSnapshot * _Nonnull snapshot) {
  if (error) {
    NSLog(@"Received an error %@", error);
    return;
  }
  NSString *userName = snapshot.value;
}];

Niepotrzebne użycie getData() może zwiększyć wykorzystanie przepustowości i doprowadzić do utraty wydajności. Można to zapobiec za pomocą detektora w czasie rzeczywistym, powyżej.

Jednorazowe odczytywanie danych z obserwatorem

W niektórych przypadkach możesz chcieć, aby zwracana była wartość z lokalnej pamięci podręcznej natychmiast, bez sprawdzania aktualizacji na serwerze. W tych można użyć funkcji observeSingleEventOfType, aby pobrać dane z do pamięci podręcznej dysku lokalnego.

Jest to przydatne w przypadku danych, które trzeba wczytać tylko raz i w pewnym stopniu często się zmieniają lub wymagają aktywnego słuchania. Na przykład aplikacja do blogowania w poprzednich przykładach korzysta z tej metody do wczytywania profilu użytkownika, gdy rozpocznij tworzenie nowego posta:

Swift

Uwaga: ta usługa Firebase nie jest dostępna w celu wycinka aplikacji.
let userID = Auth.auth().currentUser?.uid
ref.child("users").child(userID!).observeSingleEvent(of: .value, with: { snapshot in
  // Get user value
  let value = snapshot.value as? NSDictionary
  let username = value?["username"] as? String ?? ""
  let user = User(username: username)

  // ...
}) { error in
  print(error.localizedDescription)
}

Objective-C

Uwaga: ta usługa Firebase nie jest dostępna w celu wycinka aplikacji.
NSString *userID = [FIRAuth auth].currentUser.uid;
[[[_ref child:@"users"] child:userID] observeSingleEventOfType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot * _Nonnull snapshot) {
  // Get user value
  User *user = [[User alloc] initWithUsername:snapshot.value[@"username"]];

  // ...
} withCancelBlock:^(NSError * _Nonnull error) {
  NSLog(@"%@", error.localizedDescription);
}];

Aktualizowanie lub usuwanie danych

Zaktualizuj określone pola

Jednoczesne zapisywanie do określonych elementów podrzędnych węzła bez zastępowania innych węzłów podrzędnych, użyj metody updateChildValues.

Wywołując updateChildValues, możesz zaktualizować wartości podrzędne niższego poziomu przez i podaj ścieżkę dostępu do klucza. Jeśli dane są przechowywane w wielu lokalizacjach na potrzeby skalowania możesz zaktualizować wszystkie wystąpienia tych danych za pomocą funkcji rozpowszechnianie danych. Na przykład plik aplikacja do obsługi blogów społecznościowych może utworzyć posta i jednocześnie zaktualizować go kanał ostatniej aktywności i kanał aktywności użytkownika publikującego. W tym celu aplikacja do tworzenia blogów używa takiego kodu:

Swift

Uwaga: ta usługa Firebase nie jest dostępna w celu wycinka aplikacji.
guard let key = ref.child("posts").childByAutoId().key else { return }
let post = ["uid": userID,
            "author": username,
            "title": title,
            "body": body]
let childUpdates = ["/posts/\(key)": post,
                    "/user-posts/\(userID)/\(key)/": post]
ref.updateChildValues(childUpdates)

Objective-C

Uwaga: ta usługa Firebase nie jest dostępna w celu wycinka aplikacji.
NSString *key = [[_ref child:@"posts"] childByAutoId].key;
NSDictionary *post = @{@"uid": userID,
                       @"author": username,
                       @"title": title,
                       @"body": body};
NSDictionary *childUpdates = @{[@"/posts/" stringByAppendingString:key]: post,
                               [NSString stringWithFormat:@"/user-posts/%@/%@/", userID, key]: post};
[_ref updateChildValues:childUpdates];

W tym przykładzie użyto metody childByAutoId do utworzenia w węźle posta posta zawierającego posty dla: wszystkich użytkowników w organizacji /posts/$postid i jednocześnie pobieraj klucz za pomocą polecenia getKey() Za pomocą klucza można utworzyć drugi wpis w posty na blogu /user-posts/$userid/$postid.

Korzystając z tych ścieżek, można wprowadzać zmiany w wielu lokalizacjach jednocześnie drzewo JSON z pojedynczym wywołaniem updateChildValues, jak w tym przykładzie utworzy nowy post w obu lokalizacjach. Równoczesne aktualizacje dokonywane w ten sposób są niepodzielne: wszystkie aktualizacje zakończą się sukcesem, albo wszystkie aktualizacje kończą się niepowodzeniem.

Dodaj blok ukończenia

Jeśli chcesz się dowiedzieć, kiedy Twoje dane zostały zatwierdzone, możesz dodać możesz użyć tej funkcji. Zarówno setValue, jak i updateChildValues przyjmują opcjonalny blok ukończenia, który jest wywoływany po zatwierdzeniu zapisu w funkcji w bazie danych. Ten detektor może być przydatny do śledzenia danych, które zostały zapisane, a które dane są nadal synchronizowane. Jeśli połączenie się nie udało, detektor przekazuje obiekt błędu wskazujący przyczynę błędu.

Swift

Uwaga: ta usługa Firebase nie jest dostępna w celu wycinka aplikacji.
do {
  try await ref.child("users").child(user.uid).setValue(["username": username])
  print("Data saved successfully!")
} catch {
  print("Data could not be saved: \(error).")
}

Objective-C

Uwaga: ta usługa Firebase nie jest dostępna w celu wycinka aplikacji.
[[[_ref child:@"users"] child:user.uid] setValue:@{@"username": username} withCompletionBlock:^(NSError *error, FIRDatabaseReference *ref) {
  if (error) {
    NSLog(@"Data could not be saved: %@", error);
  } else {
    NSLog(@"Data saved successfully.");
  }
}];

Usuń dane

Najprostszym sposobem usunięcia danych jest wywołanie funkcji removeValue w odniesieniu do lokalizacji danych.

Możesz również usunąć, określając nil jako wartość innego zapisu operacji, na przykład setValue lub updateChildValues. Tej metody możesz używać, za pomocą updateChildValues, aby usunąć wiele elementów podrzędnych w pojedynczym wywołaniu interfejsu API.

Odłącz detektory

Obserwatorzy nie zatrzymują automatycznie synchronizacji danych, gdy opuścisz stronę ViewController Jeśli obserwator nie zostanie prawidłowo usunięty, będzie nadal synchronizować się do pamięci lokalnej. Jeśli obserwator nie jest już potrzebny, usuń go, przesuwając powiązany element FIRDatabaseHandle z metodą removeObserverWithHandle.

Gdy dodasz blok wywołania zwrotnego do pliku referencyjnego, zwrócony zostanie element FIRDatabaseHandle. Za pomocą tych uchwytów można usunąć blok wywołania zwrotnego.

Jeśli do odniesienia do bazy danych dodano wielu detektorów, każdy z nich jest wywoływane po wywołaniu zdarzenia. Aby zatrzymać synchronizację danych w tej lokalizacji, musisz usunąć wszystkich obserwatorów w danej lokalizacji, wywołując funkcję removeAllObservers .

Wywoływanie removeObserverWithHandle lub removeAllObservers przez słuchacza nie mogą automatycznie usuwać detektorów zarejestrowanych w węzłach podrzędnych; musisz też śledzić te odniesienia lub nicki i je usunąć.

Zapisywanie danych jako transakcji

Podczas pracy z danymi, które mogły zostać uszkodzone w wyniku równoczesnej pracy takich jak liczniki przyrostowe, możesz użyć funkcji operacji transakcji. Należy przekazać tej operacji dwa argumenty: funkcję aktualizacji i opcjonalny zakończenia. Funkcja aktualizacji przyjmuje bieżący stan danych jako argument i zwraca nowy pożądany stan, który chcesz zapisać.

Na przykład w przykładowej aplikacji do blogowania społecznościowego użytkownicy mogą oznaczanie postów gwiazdką i usuwanie gwiazdek oraz śledzenie liczby przyznanych gwiazdek w następujący sposób:

Swift

Uwaga: ta usługa Firebase nie jest dostępna w celu wycinka aplikacji.
ref.runTransactionBlock({ (currentData: MutableData) -> TransactionResult in
  if var post = currentData.value as? [String: AnyObject],
    let uid = Auth.auth().currentUser?.uid {
    var stars: [String: Bool]
    stars = post["stars"] as? [String: Bool] ?? [:]
    var starCount = post["starCount"] as? Int ?? 0
    if let _ = stars[uid] {
      // Unstar the post and remove self from stars
      starCount -= 1
      stars.removeValue(forKey: uid)
    } else {
      // Star the post and add self to stars
      starCount += 1
      stars[uid] = true
    }
    post["starCount"] = starCount as AnyObject?
    post["stars"] = stars as AnyObject?

    // Set value and report transaction success
    currentData.value = post

    return TransactionResult.success(withValue: currentData)
  }
  return TransactionResult.success(withValue: currentData)
}) { error, committed, snapshot in
  if let error = error {
    print(error.localizedDescription)
  }
}

Objective-C

Uwaga: ta usługa Firebase nie jest dostępna w celu wycinka aplikacji.
[ref runTransactionBlock:^FIRTransactionResult * _Nonnull(FIRMutableData * _Nonnull currentData) {
  NSMutableDictionary *post = currentData.value;
  if (!post || [post isEqual:[NSNull null]]) {
    return [FIRTransactionResult successWithValue:currentData];
  }

  NSMutableDictionary *stars = post[@"stars"];
  if (!stars) {
    stars = [[NSMutableDictionary alloc] initWithCapacity:1];
  }
  NSString *uid = [FIRAuth auth].currentUser.uid;
  int starCount = [post[@"starCount"] intValue];
  if (stars[uid]) {
    // Unstar the post and remove self from stars
    starCount--;
    [stars removeObjectForKey:uid];
  } else {
    // Star the post and add self to stars
    starCount++;
    stars[uid] = @YES;
  }
  post[@"stars"] = stars;
  post[@"starCount"] = @(starCount);

  // Set value and report transaction success
  currentData.value = post;
  return [FIRTransactionResult successWithValue:currentData];
} andCompletionBlock:^(NSError * _Nullable error,
                       BOOL committed,
                       FIRDataSnapshot * _Nullable snapshot) {
  // Transaction completed
  if (error) {
    NSLog(@"%@", error.localizedDescription);
  }
}];

Użycie transakcji zapobiega nieprawidłowemu liczeniu gwiazdek w przypadku większej liczby Użytkownicy oznaczali gwiazdką tego samego posta w tym samym czasie lub klient miał nieaktualne dane. wartość zawarta w klasie FIRMutableData jest początkowo ostatnią ostatnią klasą klienta znanej wartości ścieżki lub nil, jeśli jej nie ma. Serwer porównuje bieżącej wartości i akceptuje transakcję, jeśli pasują do wartości, czy też ją odrzuca. W przypadku odrzucenia transakcji serwer zwraca bieżącą wartość klientowi, co powoduje ponowne uruchomienie transakcji zaktualizowanej wartości. Powtarza się, dopóki transakcja nie zostanie zaakceptowana lub nie pojawi się zbyt wiele wszystkich prób.

Atomic przyrosty po stronie serwera

W powyższym przypadku użycia zapisujemy w bazie danych 2 wartości: identyfikator użytkownik, który oznacza post lub oznaczenie gwiazdką posta, i zwiększona liczba gwiazdek. Jeśli że użytkownik oznaczy post gwiazdką, możemy użyć przyrostu atomowego zamiast transakcji.

Swift

Uwaga: ta usługa Firebase nie jest dostępna w celu wycinka aplikacji.
let updates = [
  "posts/\(postID)/stars/\(userID)": true,
  "posts/\(postID)/starCount": ServerValue.increment(1),
  "user-posts/\(postID)/stars/\(userID)": true,
  "user-posts/\(postID)/starCount": ServerValue.increment(1)
] as [String : Any]
Database.database().reference().updateChildValues(updates)

Objective-C

Uwaga: ta usługa Firebase nie jest dostępna w celu wycinka aplikacji.
NSDictionary *updates = @{[NSString stringWithFormat: @"posts/%@/stars/%@", postID, userID]: @TRUE,
                        [NSString stringWithFormat: @"posts/%@/starCount", postID]: [FIRServerValue increment:@1],
                        [NSString stringWithFormat: @"user-posts/%@/stars/%@", postID, userID]: @TRUE,
                        [NSString stringWithFormat: @"user-posts/%@/starCount", postID]: [FIRServerValue increment:@1]};
[[[FIRDatabase database] reference] updateChildValues:updates];

Ten kod nie korzysta z operacji transakcji, więc nie pobiera automatycznie w przypadku wystąpienia konfliktu aktualizacji uruchom ponownie system. Ponieważ jednak operacja przyrostu odbywa się bezpośrednio na serwerze bazy danych, nie występuje konflikt.

Jeśli chcesz wykrywać i odrzucać konflikty dotyczące aplikacji, na przykład w postach, które zostały już wcześniej oznaczone gwiazdką, należy napisać niestandardowy dla konkretnego przypadku użycia.

Praca z danymi w trybie offline

Jeśli klient utraci połączenie sieciowe, aplikacja będzie nadal działać .

Każdy klient połączony z bazą danych Firebase ma własną wersję wewnętrzną wszystkich aktywnych danych. Podczas zapisywania danych są one zapisywane w tej wersji lokalnej . Klient Firebase następnie synchronizuje te dane ze zdalną bazą danych. z serwerami Google i innymi klientami w ramach „najlepszych starań”, podstaw.

W rezultacie wszystkie zapisy w bazie danych wywołują zdarzenia lokalne natychmiast, przed jakiekolwiek dane są zapisywane na serwerze. Oznacza to, że aplikacja pozostaje niezależnie od opóźnienia sieciowego czy połączenia.

Po przywróceniu połączenia aplikacja otrzyma odpowiedni zestaw zdarzeń, aby klient synchronizował się z bieżącym stanem serwera bez konieczności napisać dowolny niestandardowy kod.

Więcej informacji o zachowaniu użytkowników offline Więcej informacji o możliwościach pracy online i offline

Następne kroki