Lire et écrire des données sur les plates-formes Apple

(Facultatif) Prototyper et tester avec Firebase Local Emulator Suite

Avant d'aborder la façon dont votre application lit et écrit dans Realtime Database, Découvrons un ensemble d'outils que vous pouvez utiliser pour prototyper et tester Realtime Database fonctionnalité: Firebase Local Emulator Suite. Si vous testez différentes données en optimisant vos règles de sécurité, ou en tentant d'identifier moyen rentable d'interagir avec le backend, en étant capable de travailler localement sans déployer de services en temps réel peut être une excellente idée.

Un émulateur Realtime Database fait partie de Local Emulator Suite, qui permet à votre application d'interagir avec le contenu et la configuration de votre base de données émulée, et éventuellement les ressources de projet émulées (fonctions, autres bases de données, et règles de sécurité).

L'utilisation de l'émulateur Realtime Database ne nécessite que quelques étapes:

  1. Ajoutez une ligne de code à la configuration de test de votre application pour vous connecter à l'émulateur.
  2. À partir de la racine du répertoire local de votre projet, exécutez firebase emulators:start.
  3. Effectuer des appels à partir du code du prototype de votre application à l'aide d'une plate-forme Realtime Database SDK comme d'habitude, ou à l'aide de l'API REST Realtime Database.

Un tutoriel détaillé impliquant Realtime Database et Cloud Functions est disponible. Vous pouvez également consulter l'introduction de Local Emulator Suite.

Obtenir une référence de base de données FIR

Pour lire ou écrire des données depuis la base de données, vous avez besoin d'une instance de FIRDatabaseReference:

Swift

Remarque:Ce produit Firebase n'est pas disponible sur la cible de l'extrait d'application.
var ref: DatabaseReference!

ref = Database.database().reference()

Objective-C

Remarque:Ce produit Firebase n'est pas disponible sur la cible de l'extrait d'application.
@property (strong, nonatomic) FIRDatabaseReference *ref;

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

Écrire des données

Ce document présente les principes de base de la lecture et de l'écriture de données Firebase.

Les données Firebase sont écrites dans une référence Database et récupérées en attachant un écouteur asynchrone à la référence. L'écouteur est déclenché une fois pour l'état initial des données et à nouveau chaque fois que les données changent.

Opérations d'écriture de base

Pour les opérations d'écriture de base, vous pouvez utiliser setValue pour enregistrer des données dans un en remplaçant toutes les données existantes de ce chemin. Vous pouvez utiliser cette méthode pour:

  • Types de cartes correspondant aux types JSON disponibles, comme suit:
    • NSString
    • NSNumber
    • NSDictionary
    • NSArray

Par exemple, vous pouvez ajouter un utilisateur avec setValue comme suit:

Swift

Remarque:Ce produit Firebase n'est pas disponible sur la cible de l'extrait d'application.
self.ref.child("users").child(user.uid).setValue(["username": username])

Objective-C

Remarque:Ce produit Firebase n'est pas disponible sur la cible de l'extrait d'application.
[[[self.ref child:@"users"] child:authResult.user.uid]
    setValue:@{@"username": username}];

L'utilisation de setValue de cette manière écrase les données à l'emplacement spécifié, y compris les nœuds enfants. Toutefois, vous pouvez toujours mettre à jour un élément enfant à réécrire l'objet entier. Si vous souhaitez autoriser les utilisateurs à mettre à jour leur profil, vous pouvez modifier le nom d'utilisateur comme suit :

Swift

Remarque:Ce produit Firebase n'est pas disponible sur la cible de l'extrait d'application.
self.ref.child("users/\(user.uid)/username").setValue(username)

Objective-C

Remarque:Ce produit Firebase n'est pas disponible sur la cible de l'extrait d'application.
[[[[_ref child:@"users"] child:user.uid] child:@"username"] setValue:username];

Lire des données

Lire les données en écoutant les événements de valeur

Pour lire les données d'un chemin et écouter les modifications, utilisez la fonction observeEventType:withBlock sur FIRDatabaseReference à observer FIRDataEventTypeValue événements.

Type d'événement Utilisation habituelle
FIRDataEventTypeValue Lire et écouter les modifications apportées à l'intégralité du contenu d'un chemin d'accès

Vous pouvez utiliser l'événement FIRDataEventTypeValue pour lire les données à un chemin donné. telles qu'elles existaient au moment de l'événement. Cette méthode est déclenchée une fois "Listener" est attaché et à nouveau chaque fois que les données, y compris les enfants, des modifications. Le rappel d'événement reçoit un snapshot contenant toutes les données à cette la localisation, y compris les données des enfants. En l'absence de données, l'instantané renvoie false lorsque vous appelez exists() et nil lorsque vous lisez sa propriété value.

L'exemple suivant illustre une application de blog sur les réseaux sociaux qui récupère les détails d'un article à partir de la base de données:

Swift

Remarque:Ce produit Firebase n'est pas disponible sur la cible de l'extrait d'application.
refHandle = postRef.observe(DataEventType.value, with: { snapshot in
  // ...
})

Objective-C

Remarque:Ce produit Firebase n'est pas disponible sur la cible de l'extrait d'application.
_refHandle = [_postRef observeEventType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot * _Nonnull snapshot) {
  NSDictionary *postDict = snapshot.value;
  // ...
}];

L'écouteur reçoit un FIRDataSnapshot contenant les données au niveau emplacement dans la base de données au moment de l'événement dans sa propriété value. Toi peut attribuer les valeurs au type natif approprié, tel que NSDictionary. Si aucune donnée n'existe à l'emplacement, value est nil.

Lire les données une fois

Lire une fois avec getData()

Le SDK est conçu pour gérer les interactions avec les serveurs de base de données, que votre l'application est en ligne ou hors connexion.

En règle générale, vous devez utiliser les techniques d'événements de valeur décrites ci-dessus pour lire les données et être averti des mises à jour des données à partir du backend. Ces techniques de réduire votre utilisation et votre facturation, et sont optimisés pour offrir à vos utilisateurs tant en ligne que hors connexion.

Si vous n'avez besoin des données qu'une seule fois, vous pouvez utiliser getData() pour obtenir un instantané de de la base de données. Si, pour une raison quelconque, getData() ne parvient pas à renvoyer le "server", le client va vérifier le cache du stockage local et renvoyer une erreur si la valeur est toujours introuvable.

L'exemple suivant illustre la récupération du nom d'utilisateur public d'un utilisateur une seule fois depuis la base de données:

Swift

Remarque:Ce produit Firebase n'est pas disponible sur la cible de l'extrait d'application.
do {
  let snapshot = try await ref.child("users/\(uid)/username").getData()
  let userName = snapshot.value as? String ?? "Unknown"
} catch {
  print(error)
}

Objective-C

Remarque:Ce produit Firebase n'est pas disponible sur la cible de l'extrait d'application.
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;
}];

Une utilisation inutile de getData() peut augmenter l'utilisation de la bande passante et entraîner une perte de données qui peuvent être évitées en utilisant un écouteur en temps réel, comme illustré ci-dessus.

Lire les données une fois avec un observateur

Dans certains cas, vous souhaiterez peut-être que la valeur du cache local soit renvoyée immédiatement, au lieu de rechercher une valeur mise à jour sur le serveur. Dans ces vous pouvez utiliser observeSingleEventOfType pour obtenir les données le cache du disque local.

Cela est utile pour les données qui ne doivent être chargées qu'une seule fois et qui ne devraient pas changer fréquemment ni nécessiter une écoute active. Par exemple, l'application de blog utilise cette méthode pour charger le profil d'un utilisateur commencez à rédiger un nouveau message:

Swift

Remarque:Ce produit Firebase n'est pas disponible sur la cible de l'extrait d'application.
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

Remarque : Ce produit Firebase n'est pas disponible sur la cible App Clip.
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);
}];

Mettre à jour ou supprimer des données

Mettre à jour des champs spécifiques

Pour écrire simultanément sur des enfants spécifiques d'un nœud sans écraser d'autres les nœuds enfants, utilisez la méthode updateChildValues.

Lorsque vous appelez updateChildValues, vous pouvez mettre à jour les valeurs enfants de niveau inférieur en spécifiant un chemin d'accès pour la clé. Si les données sont stockées dans plusieurs emplacements vous pouvez mettre à jour toutes les instances de ces données distribution ramifiée des données. Par exemple, un application de blog social peut vouloir créer un article et le mettre à jour simultanément pour le flux d'activité récente et le flux d'activité de l'utilisateur ayant publié la publication. Pour ce faire, de blog utilise un code de ce type:

Swift

Remarque:Ce produit Firebase n'est pas disponible sur la cible de l'extrait d'application.
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

Remarque:Ce produit Firebase n'est pas disponible sur la cible de l'extrait d'application.
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];

Cet exemple utilise childByAutoId pour créer un post dans le nœud contenant des posts pour tous les utilisateurs de /posts/$postid et récupèrent simultanément la clé avec getKey() La clé peut ensuite être utilisée pour créer une deuxième entrée dans le posts à /user-posts/$userid/$postid.

Ces chemins d'accès vous permettent d'effectuer des mises à jour simultanées de plusieurs emplacements dans l'arborescence JSON avec un seul appel à updateChildValues, comme dans cet exemple crée le post aux deux emplacements. Les mises à jour simultanées effectuées de cette manière sont atomiques : toutes les mises à jour réussissent ou toutes échouent.

Ajouter un bloc de fin de lecture

Si vous souhaitez savoir quand vos données ont été validées, vous pouvez ajouter un bloc de finalisation. setValue et updateChildValues sont tous deux d'achèvement qui est appelé lorsque l'écriture a été validée dans le base de données. Cet écouteur peut être utile pour suivre les données qui ont été enregistrées et celles qui sont encore en cours de synchronisation. Si l'appel a échoué, L'écouteur reçoit un objet d'erreur indiquant la raison de l'échec.

Swift

Remarque:Ce produit Firebase n'est pas disponible sur la cible de l'extrait d'application.
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

Remarque : Ce produit Firebase n'est pas disponible sur la cible App Clip.
[[[_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.");
  }
}];

Supprimer des données

Le moyen le plus simple de supprimer des données consiste à appeler removeValue au niveau d'une référence à l'emplacement de ces données.

Vous pouvez également supprimer en spécifiant nil comme valeur pour une autre opération d'écriture, telle que setValue ou updateChildValues. Vous pouvez utiliser cette technique avec updateChildValues pour supprimer plusieurs enfants dans un seul appel d'API.

Dissocier les écouteurs

Les observateurs n'arrêtent pas automatiquement la synchronisation des données lorsque vous quittez un ViewController Si un observateur n'est pas correctement supprimé, il continue de se synchroniser en mémoire locale. Lorsqu'un observateur n'est plus nécessaire, supprimez-le en transmettant le FIRDatabaseHandle associé à la méthode removeObserverWithHandle.

Lorsque vous ajoutez un bloc de rappel à une référence, un FIRDatabaseHandle est renvoyé. Ces identifiants peuvent être utilisés pour supprimer le bloc de rappel.

Si plusieurs écouteurs ont été ajoutés à une référence de base de données, chaque écouteur est appelé lorsqu'un événement est déclenché. Pour arrêter la synchronisation des données à cet emplacement, vous devez supprimer tous les observateurs à un emplacement en appelant la méthode removeAllObservers.

Appeler removeObserverWithHandle ou removeAllObservers sur un écouteur ne pas supprimer automatiquement les écouteurs enregistrés sur ses nœuds enfants ; vous devez également gardez une trace de ces références ou identifiants pour les supprimer.

Enregistrer les données en tant que transactions

lorsque vous travaillez avec des données susceptibles d'être corrompues par des modifications, telles que des compteurs incrémentiels, vous pouvez utiliser un opération de transaction. Vous donnez à cette opération deux arguments: une fonction de mise à jour et un argument facultatif d'achèvement. La fonction de mise à jour prend l'état actuel des données un argument et renvoie le nouvel état souhaité que vous souhaitez écrire.

Dans l'exemple d'application de blog sur les réseaux sociaux, vous pourriez autoriser les utilisateurs activer ou désactiver le suivi de posts et suivre le nombre d'étoiles qu'ils ont reçues ; comme suit:

Swift

Remarque:Ce produit Firebase n'est pas disponible sur la cible de l'extrait d'application.
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

Remarque:Ce produit Firebase n'est pas disponible sur la cible de l'extrait d'application.
[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);
  }
}];

L'utilisation d'une transaction permet d'éviter que le nombre d'étoiles soit incorrect si plusieurs les utilisateurs ont ajouté le même message à leurs favoris en même temps ou le client avait des données obsolètes. La valeur contenue dans la classe FIRMutableData est initialement la dernière valeur connue du client pour le chemin d'accès, ou nil en l'absence de valeur. Le serveur compare la valeur initiale à sa valeur actuelle, et accepte la transaction si les valeurs correspondent, ou la rejette. Si la transaction est refusée, le serveur renvoie la valeur actuelle au client, qui exécute à nouveau la transaction avec le paramètre la valeur mise à jour. Ce processus se répète jusqu'à ce que la transaction soit acceptée ou qu'un trop grand nombre de transactions soient acceptées. ont été effectuées.

Incréments atomiques côté serveur

Dans le cas d'utilisation ci-dessus, nous écrivons deux valeurs dans la base de données: l'identifiant l'utilisateur qui a activé ou désactivé le suivi du post, ainsi que le nombre d'étoiles incrémenté. Si nous savons déjà que l'utilisateur ajoute le post à ses favoris, nous pouvons utiliser une opération d'incrément atomique au lieu d'une transaction.

Swift

Remarque:Ce produit Firebase n'est pas disponible sur la cible de l'extrait d'application.
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

Remarque : Ce produit Firebase n'est pas disponible sur la cible App Clip.
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];

Ce code n'utilise pas d'opération de transaction. Par conséquent, il n'obtient pas automatiquement réexécuté en cas de mise à jour incompatible. Toutefois, comme l'opération d'incrémentation se produit directement sur le serveur de base de données, il n'y a aucun risque de conflit.

Si vous souhaitez détecter et rejeter les conflits propres à une application, tels qu'un conflit activer le suivi d'un post qu'il a déjà suivi, vous devez écrire des règles de sécurité pour ce cas d'utilisation.

Utiliser les données hors connexion

Si un client perd sa connexion réseau, votre application continuera de fonctionner correctement.

Chaque client connecté à une base de données Firebase gère sa propre version interne de toutes les données actives. Lorsque les données sont écrites, elles sont écrites dans cette version locale en premier. Le client Firebase synchronise ensuite ces données avec la base de données distante. serveurs et avec d'autres clients, de la manière à la base.

Par conséquent, toutes les écritures dans la base de données déclenchent immédiatement des événements locaux, avant toutes les données sont écrites sur le serveur. Cela signifie que votre application quelle que soit la latence ou la connectivité du réseau.

Une fois la connectivité rétablie, votre application reçoit l'ensemble d'événements approprié pour que le client se synchronise avec l'état actuel du serveur, sans avoir à écrire de code personnalisé.

Nous reviendrons sur le comportement hors connexion dans la section En savoir plus sur les fonctionnalités en ligne et hors connexion.

Étapes suivantes