Codelab iOS Firebase: FriendlyChat

1. Ringkasan

Selamat datang di codelab FriendlyChat untuk iOS. Dalam codelab ini, Anda akan mempelajari cara membuat aplikasi iOS chat real-time berfitur lengkap menggunakan Firebase dan fitur Swift modern seperti SwiftUI, Swift Concurrency, dan framework Observation.

Yang akan Anda pelajari

  • Kelola status dengan framework Observasi modern (@Observable).
  • Mengautentikasi pengguna menggunakan Firebase Authentication.
  • Menyinkronkan pesan chat secara real time menggunakan Cloud Firestore.
  • Simpan dan sajikan gambar menggunakan Cloud Storage for Firebase.
  • Akses Firebase API menggunakan fitur Swift Concurrency modern.

Yang Anda butuhkan

  • Xcode versi terbaru
  • Simulator iOS atau perangkat pengujian fisik
  • Akun Google untuk membuat dan mengelola project Firebase Anda

2. Mendapatkan kode contoh

Buat clone repositori GitHub dari command line:

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

Repositori codelab-friendlychat-ios berisi beberapa project contoh:

  • ios-starter/swift-starter — Project Xcode awal yang berisi kerangka tata letak SwiftUI dan komentar TODO tempat Anda akan menerapkan fitur aplikasi dengan Firebase.
  • ios/swift — Project Xcode yang telah selesai dengan aplikasi contoh yang sudah selesai.

3. Membangun aplikasi awal

Bangun dan jalankan aplikasi awal untuk memahami antarmuka pengguna:

  1. Di Finder atau Terminal, buka direktori ios-starter/swift-starter.
  2. Klik dua kali FriendlyChatSwift.xcodeproj untuk membuka project di Xcode.
  3. Perhatikan bahwa Xcode secara otomatis menyelesaikan dependensi Swift Package Manager di latar belakang (mendownload firebase-ios-sdk v12+ dari GitHub).
  4. Pilih Simulator iOS (misalnya, iPhone 17 Pro), lalu klik tombol Run (cmd+R).

Setelah beberapa detik, layar login FriendlyChat akan muncul. Pada tahap ini, tombol login dan feed pesan adalah placeholder. Anda akan menghubungkan Firebase ke aplikasi pada langkah-langkah berikutnya.

4. Menyiapkan project Firebase

Buat project Firebase baru

  1. Login ke Firebase console menggunakan Akun Google Anda.
  2. Klik tombol untuk membuat project baru, lalu masukkan nama project (misalnya, FriendlyChat).
  3. Klik Lanjutkan.
  4. Jika diminta, tinjau dan setujui persyaratan Firebase, lalu klik Continue.
  5. (Opsional) Aktifkan bantuan AI di Firebase console (disebut "Gemini di Firebase").
  6. Untuk codelab ini, Anda tidak memerlukan Google Analytics, jadi nonaktifkan opsi Google Analytics.
  7. Klik Buat project, tunggu hingga project Anda disediakan, lalu klik Lanjutkan.

Mengupgrade paket harga Firebase Anda

Untuk menggunakan Cloud Storage for Firebase, project Firebase Anda harus menggunakan paket harga bayar sesuai penggunaan (Blaze), yang berarti project tersebut ditautkan ke akun Penagihan Cloud.

  • Akun Penagihan Cloud memerlukan metode pembayaran, seperti kartu kredit.
  • Jika Anda baru menggunakan Firebase dan Google Cloud, periksa apakah Anda memenuhi syarat untuk mendapatkan kredit sebesar$300 dan akun Penagihan Cloud Uji Coba Gratis.
  • Jika Anda mengerjakan codelab ini sebagai bagian dari acara, tanyakan kepada penyelenggara apakah ada kredit Cloud yang tersedia.

Untuk mengupgrade project Anda ke paket Blaze, ikuti langkah-langkah berikut:

  1. Di Firebase console, pilih upgrade your plan.
  2. Pilih paket Blaze. Ikuti petunjuk di layar untuk menautkan akun Penagihan Cloud ke project Anda.
    Jika perlu membuat akun Penagihan Cloud sebagai bagian dari upgrade ini, Anda mungkin perlu kembali ke alur upgrade di Firebase console untuk menyelesaikan upgrade.

Hubungkan aplikasi iOS Anda

  1. Dari layar Ringkasan Project, klik ikon iOS untuk meluncurkan alur kerja penyiapan aplikasi.
  2. Masukkan ID Paket:
    com.google.firebase.codelab.FriendlyChatSwift
    
  3. Klik Daftarkan aplikasi.

Tambahkan GoogleService-Info.plist ke project Xcode Anda

  1. Klik Download GoogleService-Info.plist untuk menyimpan file konfigurasi.
  2. Di Xcode, seret file GoogleService-Info.plist yang didownload ke grup utama FriendlyChatSwift di Project Navigator.
  3. Jika diminta, pastikan Salin item jika perlu dicentang dan target FriendlyChatSwift dipilih, lalu klik Selesai.

Mengonfigurasi Firebase di FriendlyChatSwiftApp.swift

Terakhir, lakukan inisialisasi Firebase dengan konfigurasi project Anda saat aplikasi SwiftUI diluncurkan. Buka FriendlyChatSwiftApp.swift dan ganti placeholder TODO dengan import FirebaseCore dan FirebaseApp.configure():

import SwiftUI
import FirebaseCore

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

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

5. Mengautentikasi pengguna

Mengonfigurasi autentikasi di Firebase console

Sebelum dapat mengautentikasi pengguna, Anda harus mengaktifkan penyedia auth pilihan Anda terlebih dahulu di Firebase console. Tutorial ini menggunakan autentikasi Email/Sandi:

  1. Di Firebase console, buka Security > Authentication, lalu klik Get started.
  2. Pilih tab metode login.
  3. Klik Email/Sandi, klik tombol aktif/nonaktif Aktifkan, lalu klik Simpan.

Mengamankan Cloud Firestore dengan aturan autentikasi

Selanjutnya, minta pengguna untuk melakukan autentikasi sebelum membaca atau menulis pesan:

  1. Di Firebase console, buka Databases & Storage > Firestore Database, lalu klik Create database.
  2. Pilih lokasi, lalu klik Berikutnya.
  3. Pilih Start in test mode, lalu klik Create.
  4. Pilih tab Aturan, lalu perbarui aturan keamanan menjadi:
    rules_version = '2';
    
    service cloud.firestore {
      match /databases/{database}/documents {
        match /messages/{messageId} {
          allow read, write: if request.auth != null;
        }
      }
    }
    
    Aturan ini memungkinkan pengguna yang terautentikasi membaca atau menulis pesan di database, yang cocok untuk tujuan pembelajaran. Pelajari lebih lanjut aturan keamanan di dokumentasi aturan keamanan.
  5. Klik Publikasikan.

Mengamati status autentikasi di UserViewModel.swift

Di SwiftUI, mengamati status Firebase Auth secara reaktif memastikan UI Anda beralih dengan lancar antara layar login dan antarmuka chat.

Buka ViewModels/UserViewModel.swift dan ganti TODO di init() untuk mengamati perubahan status autentikasi menggunakan authStateChanges AsyncSequence:

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

Mengimplementasikan login, pendaftaran, dan logout dengan Swift Concurrency

Di UserViewModel.swift, ganti komentar TODO yang tersisa dengan metode Autentikasi async/await modern:

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

Menguji autentikasi pengguna

  1. Klik tombol Run di Xcode.
  2. Di simulator aplikasi, klik Sign Up, masukkan Nama Tampilan, Email, dan Sandi, lalu ketuk SIGN UP.
  3. Anda akan otomatis diautentikasi dan diarahkan ke layar pesan kosong.

6. Membaca dari Cloud Firestore

Menambahkan contoh pesan di Firebase console

Isi database dengan pesan contoh:

  1. Di Firebase console, buka Firestore Database dan pilih tab Data.
  2. Klik Mulai koleksi.
  3. Masukkan messages untuk Collection ID, lalu klik Next.
  4. Biarkan Document ID disetel untuk dibuat secara otomatis (atau klik Auto-ID).
  5. Tambahkan kolom berikut ke dokumen:
    • text (jenis: string, nilai: Hello)
    • displayName (jenis: string, nilai: anonymous)
  6. Klik Save.
  7. Jika perlu, klik Tambahkan dokumen untuk menambahkan lebih banyak contoh pesan.

Menyinkronkan pesan di FriendlyMessageViewModel.swift

Di SwiftUI, model @Observable mendorong update UI deklaratif. Buka ViewModels/FriendlyMessageViewModel.swift dan terapkan startListening() dan stopListening() menggunakan 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()
  }

Cara SwiftUI merender dan men-scroll pesan secara otomatis

Di Views/ContentView.swift, perhatikan cara ScrollViewReader dan LazyVStack merender balon chat dan otomatis men-scroll ke pesan terbaru setiap kali messages.count berubah:

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

Dengan mengikat status database langsung ke status UI melalui model tampilan, SwiftUI memungkinkan kode aplikasi memetakan transisi ke animasi dengan jelas. Pada langkah berikutnya, Anda akan menulis data ke database dari aplikasi untuk mengamati animasi ini.

7. Mengirim pesan

Menerapkan sendMessage dengan Swift Concurrency

Saat Anda menambahkan dokumen ke koleksi menggunakan addDocument(), Cloud Firestore akan membuat ID unik untuk setiap pesan chat.

Di ViewModels/FriendlyMessageViewModel.swift, ganti TODO di sendMessage(text:imageUrl:) dengan metode 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)
  }

Menguji pengiriman pesan

  1. Klik tombol Run di Xcode.
  2. Login ke akun Anda.
  3. Ketik pesan di kolom teks bawah, lalu ketuk ikon Kirim.
  4. Lihat pesan yang langsung muncul di simulator Anda.

8. Menyimpan dan menerima gambar

Menyiapkan Cloud Storage for Firebase

  1. Di Firebase console, buka Databases & Storage > Storage, lalu klik Get started.
  2. Pilih Start in test mode, lalu klik Next.
  3. Setujui lokasi penyimpanan default, lalu klik Selesai.

Pemilihan gambar native dengan SwiftUI PhotosPicker

SwiftUI modern (iOS 16+) menyediakan pemilihan foto native melalui PhotosPicker dari import PhotosUI—tidak memerlukan jembatan delegasi UIKit atau wrapper UIImagePickerController.

Di Views/FooterView.swift, amati cara PhotosPicker mengikat gambar yang dipilih:

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

Menerapkan upload gambar

Saat foto dipilih, upload data gambar ke Cloud Storage dan simpan URL download publik di Cloud Firestore.

Di Views/FooterView.swift, ganti TODO di 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)")
    }
  }

Berbagi gambar dengan Cloud Storage

Di Views/FriendlyMessageImageView.swift, URL gambar dimuat secara asinkron menggunakan Swift Concurrency dan Cloud Storage SDK:

  • URL download HTTP/HTTPS standar diambil secara asinkron melalui URLSession.
  • URL Google Cloud Storage (misalnya, gs://...) diambil menggunakan Storage.storage().reference(forURL:).

Menguji pesan gambar

  1. Klik tombol Run di Xcode.
  2. Login ke akun Anda.
  3. Ketuk ikon Foto, pilih gambar dari galeri, lalu lihat gambar tersebut diupload dan muncul dalam percakapan.

9. Kesimpulan

Selamat, Anda telah berhasil membuat aplikasi chat iOS real-time menggunakan Swift dan Firebase.

Yang telah Anda pelajari

  • Integrasi Xcode Swift Package Manager (SPM) untuk firebase-ios-sdk.
  • Tata letak deklaratif SwiftUI, PhotosPicker, dan ScrollViewReader yang dapat di-scroll otomatis.
  • Swift Concurrency (async/await) di Authentication, Cloud Firestore, dan Cloud Storage.
  • Autentikasi Email/Sandi & Anonim dengan pengamatan status reaktif.
  • Pemrosesan snapshot real-time Cloud Firestore dan penulisan ID otomatis.
  • Upload biner dan URL download publik Cloud Storage for Firebase.

Pelajari lebih lanjut