Atelier de programmation Firebase pour iOS : FriendlyChat

1. Présentation

Bienvenue dans l'atelier de programmation FriendlyChat pour iOS. Dans cet atelier de programmation, vous apprendrez à créer une application de chat iOS en temps réel complète à l'aide de Firebase et de fonctionnalités Swift modernes telles que SwiftUI, Swift Concurrency et le framework Observation.

Points abordés

  • Gérer l'état avec le framework Observation moderne (@Observable).
  • Authentifier les utilisateurs à l'aide de Firebase Authentication.
  • Synchroniser les messages de chat en temps réel à l'aide de Cloud Firestore.
  • Stocker et diffuser des images à l'aide de Cloud Storage for Firebase.
  • Accéder aux API Firebase à l'aide des fonctionnalités modernes de Swift Concurrency.

Ce dont vous avez besoin

  • La dernière version de Xcode
  • Un simulateur iOS ou un appareil de test physique
  • Un compte Google pour créer et gérer votre projet Firebase

2. Obtenir l'exemple de code

Clonez le dépôt GitHub à partir de la ligne de commande :

$ git clone https://github.com/firebase/codelab-friendlychat-ios

Le dépôt codelab-friendlychat-ios contient plusieurs exemples de projets :

  • ios-starter/swift-starter : projet Xcode de démarrage contenant le squelette de mise en page SwiftUI et les commentaires TODO dans lesquels vous implémenterez les fonctionnalités de l'application avec Firebase.
  • ios/swift : projet Xcode terminé avec l'exemple d'application final.

3. Créer l'application de démarrage

Créez et exécutez l'application de démarrage pour vous familiariser avec l'interface utilisateur :

  1. Dans le Finder ou le terminal, accédez au répertoire ios-starter/swift-starter.
  2. Double-cliquez sur FriendlyChatSwift.xcodeproj pour ouvrir le projet dans Xcode.
  3. Notez qu'Xcode résout automatiquement les dépendances de Swift Package Manager en arrière-plan (téléchargement de firebase-ios-sdk v12+ à partir de GitHub).
  4. Sélectionnez un simulateur iOS (par exemple, iPhone 17 Pro), puis cliquez sur le bouton Exécuter (cmd+R).

Après quelques secondes, l'écran de connexion FriendlyChat s'affiche. À ce stade, les boutons de connexion et les flux de messages sont des espaces réservés. Vous allez associer Firebase à l'application lors des étapes suivantes.

4. Configurer un projet Firebase

Créer un projet Firebase

  1. Connectez-vous à la console Firebase avec votre compte Google.
  2. Cliquez sur le bouton pour créer un projet, puis saisissez un nom de projet (par exemple, FriendlyChat).
  3. Cliquez sur Continuer.
  4. Si vous y êtes invité, lisez et acceptez les conditions d'utilisation de Firebase, puis cliquez sur Continuer.
  5. (Facultatif) Activez l'assistance IA dans la console Firebase (appelée "Gemini dans Firebase").
  6. Pour cet atelier de programmation, vous n'avez pas besoin de Google Analytics. Par conséquent, désactivez l'option Google Analytics.
  7. Cliquez sur Créer un projet, attendez que votre projet soit provisionné, puis cliquez sur Continuer.

Passer à un forfait Firebase supérieur

Pour utiliser Cloud Storage for Firebase, votre projet Firebase doit être associé au forfait Blaze avec paiement à l'usage, ce qui signifie qu'il est associé à un compte de facturation Cloud.

  • Un compte de facturation Cloud nécessite un mode de paiement, comme une carte de crédit.
  • Si vous débutez avec Firebase et Google Cloud, vérifiez si vous êtes éligible à un crédit de 300 $ et à un compte de facturation Cloud d'essai sans frais.
  • Si vous suivez cet atelier de programmation dans le cadre d'un événement, demandez à l'organisateur si des crédits Cloud sont disponibles.

Pour passer votre projet au forfait Blaze, procédez comme suit :

  1. Dans la console Firebase, sélectionnez pour passer à un forfait supérieur.
  2. Sélectionnez le forfait Blaze. Suivez les instructions à l'écran pour associer un compte de facturation Cloud à votre projet.
    Si vous avez dû créer un compte de facturation Cloud dans le cadre de cette mise à niveau, vous devrez peut-être revenir au processus de mise à niveau dans la console Firebase pour terminer la mise à niveau.

Connecter votre application iOS

  1. Dans l'écran "Vue d'ensemble du projet", cliquez sur l'icône iOS pour lancer le processus de configuration de l'application.
  2. Saisissez l'ID du bundle :
    com.google.firebase.codelab.FriendlyChatSwift
    
  3. Cliquez sur Enregistrer l'application.

Ajouter GoogleService-Info.plist à votre projet Xcode

  1. Cliquez sur Télécharger GoogleService-Info.plist pour enregistrer le fichier de configuration.
  2. Dans Xcode, faites glisser le fichier GoogleService-Info.plist téléchargé dans le groupe principal FriendlyChatSwift du navigateur de projet.
  3. Lorsque vous y êtes invité, assurez-vous que l'option Copier les éléments si nécessaire est cochée et que la cible FriendlyChatSwift est sélectionnée, puis cliquez sur Terminer.

Configurer Firebase dans FriendlyChatSwiftApp.swift

Enfin, initialisez Firebase avec la configuration de votre projet au lancement de votre application SwiftUI. Ouvrez FriendlyChatSwiftApp.swift et remplacez les espaces réservés TODO par import FirebaseCore et FirebaseApp.configure() :

import SwiftUI
import FirebaseCore

@main
struct FriendlyChatSwiftApp: App {
  init() {
    FirebaseApp.configure()
  }

  var body: some Scene {
    WindowGroup {
      ContentView()
    }
  }
}

5. Authentifier les utilisateurs

Configurer l'authentification dans la console Firebase

Avant de pouvoir authentifier les utilisateurs, vous devez d'abord activer le fournisseur d'authentification de votre choix dans la console Firebase. Ce tutoriel utilise l'authentification par adresse e-mail/mot de passe :

  1. Dans la console Firebase, accédez à Sécurité > Authentification, puis cliquez sur Premiers pas.
  2. Sélectionnez l'onglet Méthode de connexion.
  3. Cliquez sur Adresse e-mail/Mot de passe, activez le bouton Activer, puis cliquez sur Enregistrer.

Sécuriser Cloud Firestore avec des règles d'authentification

Ensuite, demandez aux utilisateurs de s'authentifier avant de lire ou d'écrire des messages :

  1. Dans la console Firebase, accédez à Bases de données et stockage > Base de données Firestore, puis cliquez sur Créer une base de données.
  2. Sélectionnez un emplacement, puis cliquez sur Suivant.
  3. Sélectionnez Démarrer en mode test , puis cliquez sur Créer.
  4. Sélectionnez l'onglet Règles, puis mettez à jour les règles de sécurité comme suit :
    rules_version = '2';
    
    service cloud.firestore {
      match /databases/{database}/documents {
        match /messages/{messageId} {
          allow read, write: if request.auth != null;
        }
      }
    }
    
    Ces règles permettent à tout utilisateur authentifié de lire ou d'écrire des messages dans la base de données, ce qui est adapté à des fins d'apprentissage. Pour en savoir plus sur les règles de sécurité, consultez la documentation sur les règles de sécurité.
  5. Cliquez sur Publier.

Observer l'état de l'authentification dans UserViewModel.swift

Dans SwiftUI, l'observation réactive de l'état de Firebase Auth garantit que votre interface utilisateur passe en douceur de l'écran de connexion à l'interface de chat.

Ouvrez ViewModels/UserViewModel.swift et remplacez le TODO dans init() pour observer les changements d'état d'authentification à l'aide de authStateChanges AsyncSequence :

  init() {
    authTask = Task {
      for await user in Auth.auth().authStateChanges {
        self.user = user
      }
    }
  }

Implémenter la connexion, l'inscription et la déconnexion avec Swift Concurrency

Dans UserViewModel.swift, remplacez les commentaires TODO restants par des méthodes d'authentification async/await modernes :

  func signIn(email: String, password: String) async {
    errorMessage = nil
    if email.isEmpty || password.isEmpty {
      showError("Please enter both email and password.")
      return
    }
    do {
      try await Auth.auth().signIn(withEmail: email, password: password)
    } catch {
      showError(error.localizedDescription)
    }
  }

  func signUp(email: String, password: String, displayName: String) async {
    errorMessage = nil
    if email.isEmpty || password.isEmpty {
      showError("Please enter both email and password.")
      return
    }
    do {
      let result =
        try await Auth.auth().createUser(withEmail: email, password: password)
      let changeRequest = result.user.createProfileChangeRequest()
      changeRequest.displayName = displayName.isEmpty ? email : displayName
      try await changeRequest.commitChanges()
      self.user = Auth.auth().currentUser
    } catch {
      showError(error.localizedDescription)
    }
  }

  func updateDisplayName(_ displayName: String) async {
    guard let currentUser = Auth.auth().currentUser else { return }
    do {
      let changeRequest = currentUser.createProfileChangeRequest()
      changeRequest.displayName = displayName
      try await changeRequest.commitChanges()
      self.user = Auth.auth().currentUser
    } catch {
      showError(error.localizedDescription)
    }
  }

  func signOut() {
    do {
      try Auth.auth().signOut()
    } catch {
      showError(error.localizedDescription)
    }
  }

Tester l'authentification des utilisateurs

  1. Cliquez sur le bouton Exécuter dans Xcode.
  2. Dans le simulateur d'application, cliquez sur S'inscrire, saisissez un nom à afficher, une adresse e-mail et un mot de passe, puis appuyez sur S'INSCRIRE.
  3. Vous devriez être automatiquement authentifié et redirigé vers l'écran de messagerie vide.

6. Lire à partir de Cloud Firestore

Ajouter des exemples de messages dans la console Firebase

Remplissez la base de données avec des exemples de messages :

  1. Dans la console Firebase, accédez à Base de données Firestore , puis sélectionnez l'onglet Données.
  2. Cliquez sur Commencer une collection.
  3. Saisissez messages pour l'ID de collection, puis cliquez sur Suivant.
  4. Laissez l'ID de document défini sur la génération automatique (ou cliquez sur ID automatique).
  5. Ajoutez les champs suivants au document :
    • text (type : string, valeur : Hello)
    • displayName (type : string, valeur : anonymous)
  6. Cliquez sur Enregistrer.
  7. Vous pouvez également cliquer sur Ajouter un document pour ajouter d'autres exemples de messages.

Synchroniser les messages dans FriendlyMessageViewModel.swift

Dans SwiftUI, un modèle @Observable génère des mises à jour déclaratives de l'interface utilisateur. Ouvrez ViewModels/FriendlyMessageViewModel.swift et implémentez startListening() et stopListening() à l'aide de snapshots AsyncSequence :

  func startListening() {
    stopListening()
    listenerTask = Task {
      let db = Firestore.firestore()
      do {
        for try await snapshot in db.collection("messages").snapshots {
          self.messages = snapshot.documents.compactMap { document in
            try? document.data(as: FriendlyMessage.self)
          }
        }
      } catch {
        print("Error listening for messages: \(error)")
      }
    }
  }

  func stopListening() {
    listenerTask?.cancel()
    listenerTask = nil
    messages.removeAll()
  }

Comment SwiftUI affiche et fait défiler automatiquement les messages

Dans Views/ContentView.swift, notez comment ScrollViewReader et LazyVStack affichent les bulles de chat et font défiler automatiquement le dernier message chaque fois que messages.count change :

ScrollViewReader { scrollViewReader in
  ScrollView {
    LazyVStack(spacing: 12) {
      ForEach(messageViewModel.messages) { message in
        FriendlyMessageView(friendlyMessage: message)
          .id(message.id)
      }
    }
    .padding(.horizontal)
    .onChange(of: messageViewModel.messages.count) { _, count in
      guard count > 0,
            let lastId = messageViewModel.messages.last?.id else { return }
      withAnimation(.easeInOut) {
        scrollViewReader.scrollTo(lastId, anchor: .bottom)
      }
    }
  }
}

En associant l'état de la base de données directement à l'état de l'interface utilisateur via le modèle de vue, SwiftUI permet au code de l'application de mapper clairement les transitions aux animations. Lors de l'étape suivante, vous écrirez des données dans la base de données à partir de l'application pour observer cette animation en action.

7. Envoyer des messages

Implémenter sendMessage avec Swift Concurrency

Lorsque vous ajoutez un document à une collection à l'aide de addDocument(), Cloud Firestore génère un ID unique pour chaque message de chat.

Dans ViewModels/FriendlyMessageViewModel.swift, remplacez le TODO dans sendMessage(text:imageUrl:) par une méthode async throws :

  func sendMessage(text: String?, imageUrl: String?) async throws {
    guard let currentUser = Auth.auth().currentUser else { return }
    let message = FriendlyMessage(
      text: text,
      displayName: currentUser.displayName ?? currentUser.email ?? "Anonymous",
      imageUrl: imageUrl,
      userId: currentUser.uid
    )
    let db = Firestore.firestore()
    _ = try db.collection("messages").addDocument(from: message)
  }

Tester l'envoi de messages

  1. Cliquez sur le bouton Exécuter dans Xcode.
  2. Connectez-vous à votre compte.
  3. Saisissez un message dans le champ de texte en bas, puis appuyez sur l'icône Envoyer.
  4. Regardez le message s'afficher instantanément dans votre simulateur.

8. Stocker et recevoir des images

Configurer Cloud Storage for Firebase

  1. Dans la console Firebase, accédez à Bases de données et stockage > Stockage, puis cliquez sur Premiers pas.
  2. Sélectionnez Démarrer en mode test , puis cliquez sur Suivant.
  3. Acceptez l'emplacement de stockage par défaut, puis cliquez sur OK.

Sélection d'images native avec SwiftUI PhotosPicker

SwiftUI moderne (iOS 16+) fournit une sélection de photos native via PhotosPicker à partir de import PhotosUI. Aucune passerelle de délégué UIKit ni aucun wrapper UIImagePickerController ne sont requis.

Dans Views/FooterView.swift, observez comment PhotosPicker lie les images sélectionnées :

PhotosPicker(selection: $selectedItem, matching: .images) {
  Image(systemName: "photo.on.rectangle.angled")
    .font(.system(size: 26))
    .foregroundStyle(.blue)
    .accessibilityLabel("Select photo")
}
.onChange(of: selectedItem) { _, newItem in
  Task {
    if let data = try? await newItem?.loadTransferable(type: Data.self) {
      await uploadAndSendImage(data: data)
    }
  }
}

Implémenter l'importation d'images

Lorsqu'une photo est sélectionnée, importez les données de l'image dans Cloud Storage et enregistrez l'URL de téléchargement publique dans Cloud Firestore.

Dans Views/FooterView.swift, remplacez le TODO dans uploadAndSendImage(data:) :

  private func uploadAndSendImage(data: Data) async {
    guard let uid = Auth.auth().currentUser?.uid else { return }
    isUploading = true
    defer {
      isUploading = false
      selectedItem = nil
    }
    do {
      let filename = "\(uid)/\(UUID().uuidString).jpg"
      let storageRef =
        Storage.storage().reference().child("images").child(filename)
      let metadata = StorageMetadata()
      metadata.contentType = "image/jpeg"

      _ = try await storageRef.putDataAsync(data, metadata: metadata)
      let downloadURL = try await storageRef.downloadURL()
      try await viewModel.sendMessage(
        text: nil, imageUrl: downloadURL.absoluteString)
    } catch {
      print("Error uploading image: \(error.localizedDescription)")
    }
  }

Partage d'images avec Cloud Storage

Dans Views/FriendlyMessageImageView.swift, les URL des images sont chargées de manière asynchrone à l'aide de Swift Concurrency et du SDK Cloud Storage :

  • Les URL de téléchargement HTTP/HTTPS standards sont récupérées de manière asynchrone via URLSession.
  • Les URL Google Cloud Storage (par exemple, gs://...) sont récupérées à l'aide de Storage.storage().reference(forURL:).

Tester les messages d'image

  1. Cliquez sur le bouton Exécuter dans Xcode.
  2. Connectez-vous à votre compte.
  3. Appuyez sur l'icône Photo, choisissez une image dans la bibliothèque, puis regardez-la s'importer et s'afficher dans le chat.

9. Conclusion

Félicitations, vous avez créé une application de chat iOS en temps réel à l'aide de Swift et de Firebase.

Connaissances acquises

  • Intégration de Swift Package Manager (SPM) Xcode pour firebase-ios-sdk.
  • Mises en page déclaratives SwiftUI , PhotosPicker et défilement automatique ScrollViewReader.
  • Swift Concurrency (async/await) dans l'authentification, Cloud Firestore et Cloud Storage.
  • Authentification par adresse e-mail/mot de passe et anonyme avec observation réactive de l'état.
  • Écoute des instantanés en temps réel et écriture automatique des ID avec Cloud Firestore.
  • Importations binaires et URL de téléchargement publiques avec Cloud Storage for Firebase.

En savoir plus