Questa pagina è stata tradotta dall'API Cloud Translation.
Switch to English

Leggere e scrivere dati su iOS

Ottieni un FIRDatabaseReference

Per leggere o scrivere dati dal database, è necessaria un'istanza di FIRDatabaseReference :

Swift

var ref: DatabaseReference!

ref = Database.database().reference()

Obiettivo-C

@property (strong, nonatomic) FIRDatabaseReference *ref;

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

Lettura e scrittura di dati

Questo documento tratta le basi della lettura e scrittura dei dati Firebase.

I dati Firebase vengono scritti in un riferimento FIRDatabase e recuperati allegando un listener asincrono al riferimento. Il listener viene attivato una volta per lo stato iniziale dei dati e di nuovo ogni volta che i dati cambiano.

Operazioni di scrittura di base

Per le operazioni di scrittura di base, è possibile utilizzare setValue per salvare i dati in un riferimento specificato, sostituendo i dati esistenti in quel percorso. Puoi utilizzare questo metodo per:

  • Tipi di passaggio che corrispondono ai tipi JSON disponibili come segue:
    • NSString
    • NSNumber
    • NSDictionary
    • NSArray

Ad esempio, puoi aggiungere un utente con setValue come segue:

Swift

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

Obiettivo-C

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

L'utilizzo di setValue in questo modo sovrascrive i dati nella posizione specificata, inclusi eventuali nodi figlio. Tuttavia, puoi comunque aggiornare un bambino senza riscrivere l'intero oggetto. Se desideri consentire agli utenti di aggiornare i loro profili, puoi aggiornare il nome utente come segue:

Swift

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

Obiettivo-C

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

Ascolta gli eventi di valore

Per leggere i dati in un percorso e ascoltare le modifiche, utilizzare i observeEventType:withBlock o observeSingleEventOfType:withBlock di FIRDatabaseReference per osservare FIRDataEventTypeValue eventi FIRDataEventTypeValue .

Tipo di evento Utilizzo tipico
FIRDataEventTypeValue Leggere e ascoltare le modifiche all'intero contenuto di un percorso.

È possibile utilizzare l'evento FIRDataEventTypeValue per leggere i dati in un determinato percorso, poiché esiste al momento dell'evento. Questo metodo viene attivato una volta quando il listener è collegato e di nuovo ogni volta che i dati, inclusi eventuali elementi secondari, cambiano. Alla richiamata dell'evento viene trasmessa snapshot contenente tutti i dati in quella posizione, inclusi i dati figlio. Se non ci sono dati, l'istantanea restituirà false quando si chiama exists() e nil quando si legge la sua proprietà value .

L'esempio seguente mostra un'applicazione di social blogging che recupera i dettagli di un post dal database:

Swift

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

Obiettivo-C

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

Il listener riceve un FIRDataSnapshot che contiene i dati nella posizione specificata nel database al momento dell'evento nella sua proprietà value . È possibile assegnare i valori al tipo nativo appropriato, ad esempio NSDictionary . Se non esistono dati nella posizione, il value è nil .

Leggere i dati una volta

In alcuni casi potresti volere che una richiamata venga chiamata una volta e quindi rimossa immediatamente, ad esempio quando si inizializza un elemento dell'interfaccia utente che non ti aspetti di cambiare. È possibile utilizzare il metodo observeSingleEventOfType per semplificare questo scenario: il callback dell'evento aggiunto si attiva una volta e quindi non si attiva di nuovo.

Ciò è utile per i dati che devono essere caricati una sola volta e non si prevede che cambino frequentemente o che richiedano un ascolto attivo. Ad esempio, l'app di blog negli esempi precedenti utilizza questo metodo per caricare il profilo di un utente quando inizia a creare un nuovo post:

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

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

Aggiornamento o eliminazione dei dati

Aggiorna campi specifici

Per scrivere simultaneamente su elementi updateChildValues specifici di un nodo senza sovrascrivere altri nodi figlio, utilizzare il metodo updateChildValues .

Quando si chiama updateChildValues , è possibile aggiornare i valori figlio di livello inferiore specificando un percorso per la chiave. Se i dati vengono archiviati in più posizioni per una migliore scalabilità, è possibile aggiornare tutte le istanze di tali dati utilizzando il fan-out dei dati . Ad esempio, un'app di social blogging potrebbe voler creare un post e aggiornarlo contemporaneamente al feed delle attività recenti e al feed delle attività dell'utente che pubblica il post. Per fare ciò, l'applicazione di blog utilizza un codice come questo:

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)

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

Questo esempio utilizza childByAutoId per creare un post nel nodo contenente post per tutti gli utenti in /posts/$postid e contemporaneamente recuperare la chiave con getKey() . La chiave può quindi essere utilizzata per creare una seconda voce nei post /user-posts/$userid/$postid in /user-posts/$userid/$postid .

Usando questi percorsi, puoi eseguire aggiornamenti simultanei a più posizioni nella struttura JSON con una singola chiamata a updateChildValues , ad esempio il modo in cui questo esempio crea il nuovo post in entrambe le posizioni. Gli aggiornamenti simultanei effettuati in questo modo sono atomici: tutti gli aggiornamenti hanno esito positivo o tutti gli aggiornamenti non riescono.

Aggiungi un blocco di completamento

Se vuoi sapere quando i tuoi dati sono stati salvati, puoi aggiungere un blocco di completamento. Sia setValue che updateChildValues accettano un blocco di completamento facoltativo che viene chiamato quando la scrittura è stata updateChildValues nel database. Questo listener può essere utile per tenere traccia di quali dati sono stati salvati e quali dati sono ancora sincronizzati. Se la chiamata non ha avuto successo, all'ascoltatore viene passato un oggetto di errore che indica il motivo dell'errore.

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!")
  }
}

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

Elimina i dati

Il modo più semplice per eliminare i dati è chiamare removeValue su un riferimento alla posizione di tali dati.

È inoltre possibile eliminare specificando nil come valore per un'altra operazione di scrittura come setValue o updateChildValues . È possibile utilizzare questa tecnica con updateChildValues per eliminare più elementi updateChildValues in una singola chiamata API.

Stacca gli ascoltatori

Gli osservatori non interrompono automaticamente la sincronizzazione dei dati quando esci da ViewController . Se un osservatore non viene rimosso correttamente, continua a sincronizzare i dati nella memoria locale. Quando un osservatore non è più necessario, rimuoverlo passando il FIRDatabaseHandle associato al metodo removeObserverWithHandle .

Quando aggiungi un blocco di callback a un riferimento, viene restituito un FIRDatabaseHandle . Questi handle possono essere utilizzati per rimuovere il blocco di callback.

Se più listener sono stati aggiunti a un riferimento al database, ogni listener viene chiamato quando viene generato un evento. Per interrompere la sincronizzazione dei dati in quella posizione, è necessario rimuovere tutti gli osservatori in una posizione chiamando il metodo removeAllObservers .

La chiamata di removeObserverWithHandle o di removeAllObservers su un listener non rimuove automaticamente i listener registrati sui suoi nodi figlio; è inoltre necessario tenere traccia di tali riferimenti o handle per rimuoverli.

Salva i dati come transazioni

Quando si lavora con dati che potrebbero essere danneggiati da modifiche simultanee, come i contatori incrementali, è possibile utilizzare un'operazione di transazione . Si danno a questa operazione due argomenti: una funzione di aggiornamento e un callback di completamento opzionale. La funzione di aggiornamento accetta lo stato corrente dei dati come argomento e restituisce il nuovo stato desiderato che si desidera scrivere.

Ad esempio, nell'app di social blogging di esempio, potresti consentire agli utenti di aggiungere e rimuovere post da Speciali e tenere traccia di quante stelle ha ricevuto un post come segue:

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

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

L'utilizzo di una transazione impedisce che il conteggio delle stelle non sia corretto se più utenti aggiungono a Speciali lo stesso post contemporaneamente o se il cliente ha dati obsoleti. Il valore contenuto nella classe FIRMutableData è inizialmente l'ultimo valore noto del client per il percorso o nil se non ce n'è. Il server confronta il valore iniziale con il suo valore corrente e accetta la transazione se i valori corrispondono o la rifiuta. Se la transazione viene rifiutata, il server restituisce il valore corrente al client, che esegue nuovamente la transazione con il valore aggiornato. Questo si ripete fino a quando la transazione non viene accettata o sono stati effettuati troppi tentativi.

Scrivi dati offline

Se un client perde la connessione di rete, l'app continuerà a funzionare correttamente.

Ogni client connesso a un database Firebase mantiene la propria versione interna di tutti i dati attivi. Quando i dati vengono scritti, vengono scritti prima in questa versione locale. Il client Firebase quindi sincronizza i dati con i server di database remoti e con altri client in base al "massimo impegno".

Di conseguenza, tutte le scritture sul database attivano immediatamente gli eventi locali, prima che i dati vengano scritti sul server. Ciò significa che la tua app rimane reattiva indipendentemente dalla latenza di rete o dalla connettività.

Una volta ristabilita la connettività, l'app riceve il set di eventi appropriato in modo che il client si sincronizzi con lo stato del server corrente, senza dover scrivere alcun codice personalizzato.

Prossimi passi