Prueba Cloud Firestore: Descubre la base de datos flexible y escalable de Firebase y Google Cloud Platform. Obtén más información sobre Cloud Firestore.

Lee y escribe datos en iOS

Obtén una FIRDatabaseReference

Para leer y escribir en la base de datos, necesitas una instancia de FIRDatabaseReference:

Swift

var ref: DatabaseReference!

ref = Database.database().reference()

Objective-C

@property (strong, nonatomic) FIRDatabaseReference *ref;

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

Lectura y escritura de datos

Este documento abarca los aspectos básicos de la lectura y la escritura de datos en Firebase.

Los datos de Firebase se escriben en una referencia de FIRDatabase y para recuperarlos se debe adjuntar un agente de escucha asíncrono a la referencia. El agente de escucha se activa una vez para el estado inicial de los datos y otra vez cuando los datos cambian.

Operaciones básicas de escritura

Para ejecutar operaciones de escritura básicas, puedes usar setValue para guardar datos en una referencia que especifiques y reemplazar los datos existentes en esa ruta de acceso. Puedes usar este método para:

  • Pasar tipos que corresponden a los tipos disponibles de JSON de la siguiente manera:
    • NSString
    • NSNumber
    • NSDictionary
    • NSArray

Por ejemplo, puedes agregar un usuario con setValue como se muestra a continuación:

Swift

self.ref.child("users").child(user.uid).setValue(["username": username])

Objective-C

[[[self.ref child:@"users"] child:authResult.user.uid]
    setValue:@{@"username": username}];

Si usas setValue de esta forma, se sobrescriben los datos en la ubicación especificada, incluidos los nodos secundarios. Sin embargo, es posible actualizar un elemento secundario sin volver a escribir el objeto entero. Si deseas permitir que los usuarios actualicen sus perfiles, podrías actualizar el nombre de usuario de la siguiente forma:

Swift

self.ref.child("users/\(user.uid)/username").setValue(username)

Objective-C

[[[[_ref child:@"users"] child:user.uid] child:@"username"] setValue:username];

Detecta eventos de valores

Para leer datos en una ruta de acceso y escuchar para detectar cambios, usa el método observeEventType:withBlock o el método observeSingleEventOfType:withBlock de FIRDatabaseReference para observar eventos FIRDataEventTypeValue.

Tipo de evento Uso común
FIRDataEventTypeValue Lee y detecta cambios en todo el contenido de una ruta.

Puedes usar el evento FIRDataEventTypeValue para leer los datos de una ruta de acceso determinada, en el estado en que se encontraban en el momento del evento. Este método se activa cuando se adjunta el agente de escucha y se vuelve a activar cada vez que cambian los datos (incluidos los de segundo nivel). La devolución de llamada del evento recibe una snapshot que contiene todos los datos de dicha ubicación, incluidos los datos secundarios. Si no hay datos, la instantánea mostrará el valor false cuando llames a la función exists() y nil cuando leas su propiedad value.

El siguiente ejemplo demuestra una aplicación de blogs sociales que recupera los detalles de una publicación de la base de datos:

Swift

refHandle = postRef.observe(DataEventType.value, with: { (snapshot) in
  let postDict = snapshot.value as? [String : AnyObject] ?? [:]
  // ...
})

Objective-C

_refHandle = [_postRef observeEventType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot * _Nonnull snapshot) {
  NSDictionary *postDict = snapshot.value;
  // ...
}];

El agente de escucha recibe una FIRDataSnapshot que contiene los datos de la ubicación especificada en el momento del evento en su propiedad value. Puedes asignar los valores al tipo nativo apropiado, como NSDictionary. Si no hay datos en la ubicación, el value es nil.

Lee los datos una sola vez

En algunos casos, es posible que desees obtener una devolución de llamada una vez y después se lo quite de inmediato (p. ej., cuando inicializas un elemento de IU que no tienes previsto modificar). Puedes usar el método observeSingleEventOfType para simplificar esta situación: la devolución de llamada del evento se activa una vez y luego no se vuelve a activar.

Esto resulta útil para los datos que solo se deben cargar una vez y que, según lo previsto, no cambiarán con frecuencia ni necesitarán una escucha activa. Por ejemplo, en la app de blogs de los ejemplos anteriores se usa este método para cargar el perfil de un usuario cuando este comienza a crear una publicación nueva:

Swift

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

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

Actualización o eliminación de datos

Actualiza campos específicos

Para escribir de forma simultánea en elementos secundarios específicos de un nodo sin sobrescribir otros nodos secundarios, usa el método updateChildValues.

Cuando llamas a updateChildValues, puedes especificar una ruta de acceso de la clave para actualizar valores secundarios de nivel inferior. Si se almacenan datos en varias ubicaciones para obtener un escalamiento mejor, puedes actualizar todas las instancias de esos datos mediante la distribución de datos. Por ejemplo, es posible que una app social de blogs desee crear una publicación y que esta aparezca en forma simultánea con el feed de actividad reciente y en el feed de actividad de las entradas del usuario. Para ello, la aplicación de blogs usa código similar al siguiente:

Swift

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

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

Este ejemplo usa childByAutoId para crear una entrada en el nodo que contiene las entradas de todos los usuarios en /posts/$postid y, al mismo tiempo, recupera la clave con getKey(). Esta clave se puede usar para crear una segunda entrada en las publicaciones de usuarios en /user-posts/$userid/$postid.

Con estas rutas de acceso, puedes hacer actualizaciones simultáneas en varias ubicaciones del árbol JSON con una única llamada a updateChildValues, de manera similar a este ejemplo en el que se crea la publicación nueva en ambas ubicaciones. Las actualizaciones simultáneas que se hacen de esta forma son atómicas: todas las actualizaciones se aplican correctamente o todas fallan.

Agrega un bloque de finalización

Si quieres saber en qué momento se confirma la escritura de los datos, puedes agregar un bloque de finalización. Los métodos setValueupdateChildValues admiten un bloque de finalización opcional que se llama cuando la escritura se confirma en la base de datos. El agente de escucha es útil para mantener un registro de los datos que se guardan y de los que siguen en sincronización. Si la llamada no funciona correctamente, el agente de escucha recibirá un objeto de error que indicará el motivo.

Swift

ref.child("users").child(user.uid).setValue(["username": username]) {
  (error:Error?, ref:DatabaseReference) in
  if let error = error {
    print("Data could not be saved: \(error).")
  } else {
    print("Data saved successfully!")
  }
}

Objective-C

[[[_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.");
  }
}];

Borra datos

La forma más sencilla de borrar datos es llamar a removeValue en una referencia a la ubicación de los datos.

Otra manera de borrar es especificar nil como valor para otra operación de escritura, como setValue o updateChildValues. Puedes usar esta técnica con updateChildValues para borrar varios elementos secundarios en una única llamada de API.

Desvincula agentes de escucha

Los observadores no detienen de forma automática la sincronización de datos cuando dejas un ViewController. Si un observador no se quita de forma apropiada, la sincronización de datos continúa en la memoria local. Cuando ya no necesites un observador, pasa el FIRDatabaseHandle asociado al método removeObserverWithHandle para quitarlo.

Cuando agregas un bloque de devoluciones de llamada a una referencia, se muestra FIRDatabaseHandle. Estos controladores se pueden usar para quitar el bloque de devoluciones de llamada.

Si se agregaron varios agentes de escucha a la referencia de una base de datos, cada agente de escucha se llama cuando surge un evento. Para detener la sincronización de datos en esa ubicación, debes quitar todos los observadores de la ubicación a través del método removeAllObservers.

Llamar a removeObserverWithHandle o a removeAllObservers en un agente de escucha no quita de manera automática los agentes de escucha registrados en estos nodos secundarios; también debes hacer un seguimiento de esas referencias o controladores para quitarlos.

Guarda datos como transacciones

Cuando trabajas con datos que se podrían dañar si se hacen cambios simultáneos (por ejemplo, contadores incrementales) puedes usar una operación de transacción. Esta operación acepta dos argumentos: una función de actualización y una devolución de llamada opcional de finalización. La función de actualización toma el estado actual de los datos como argumento y genera el nuevo estado que deseas escribir.

Por ejemplo, en la app social de blogs que mencionamos antes, podrías permitir que los usuarios agreguen o quiten estrellas en las entradas y llevar un seguimiento de cuántas estrellas recibió una entrada de la siguiente forma:

Swift

ref.runTransactionBlock({ (currentData: MutableData) -> TransactionResult in
  if var post = currentData.value as? [String : AnyObject], let uid = Auth.auth().currentUser?.uid {
    var stars: Dictionary<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

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

Si usas una transacción, evitas que el recuento de estrellas sea incorrecto en caso de que varios usuarios agreguen estrellas a la publicación al mismo tiempo, o el cliente tenga datos inactivos. Por lo general, el valor que contiene inicialmente la clase FIRMutableData es el último valor conocido del cliente para la ruta de acceso, o nil si no hay uno. El servidor compara el valor inicial con el valor actual y acepta la transacción si los valores coinciden. De lo contrario, la rechaza. Si se rechaza la transacción, el servidor muestra el valor actual al cliente, que vuelve a ejecutar la transacción con el valor actualizado. Esto se repite hasta que se acepte la transacción o hasta que se registren demasiados intentos.

Escribe datos sin conexión

Si un cliente pierde la conexión de red, la app continúa funcionando de manera correcta.

Todos los clientes conectados a una base de datos de Firebase mantienen su propia versión interna de los datos activos. Cuando se escriben datos, se hace primero en esta versión local. Después, el cliente de Firebase sincroniza esos datos con los servidores de bases de datos remotas y con otros clientes según el “mejor esfuerzo”.

Como resultado, todas las operaciones de escritura en la base de datos activan eventos locales al instante, antes de que se escriban datos en el servidor. Esto significa que la app conserva la capacidad de respuesta, sin importar la latencia o el estado de conexión de la red.

Cuando se restablece la conexión, la app recibe el conjunto de eventos adecuado, de manera que el cliente se sincroniza con el estado actual del servidor sin tener que escribir código personalizado.

Próximos pasos

Enviar comentarios sobre…

Firebase Realtime Database
¿Necesitas ayuda? Visita nuestra página de asistencia.