آزمایشگاه کدنویسی فایربیس iOS: FriendlyChat

۱. مرور کلی

به آزمایشگاه کد FriendlyChat برای iOS خوش آمدید! در این آزمایشگاه کد، یاد خواهید گرفت که چگونه یک برنامه چت بلادرنگ iOS با امکانات کامل با استفاده از Firebase و ویژگی‌های مدرن Swift مانند SwiftUI، Swift Concurrency و چارچوب Observation بسازید.

آنچه یاد خواهید گرفت

  • مدیریت وضعیت با چارچوب مدرن Observation ( @Observable ) .
  • احراز هویت کاربران با استفاده از احراز هویت فایربیس
  • همگام‌سازی پیام‌های چت به صورت بلادرنگ با استفاده از Cloud Firestore
  • تصاویر را با استفاده از فضای ذخیره‌سازی ابری برای فایربیس ذخیره و ارائه دهید.
  • با استفاده از ویژگی‌های مدرن Swift Concurrency به APIهای Firebase دسترسی پیدا کنید.

آنچه نیاز دارید

  • آخرین نسخه Xcode
  • یک شبیه‌ساز iOS یا دستگاه تست فیزیکی
  • یک حساب گوگل برای ایجاد و مدیریت پروژه Firebase شما

۲. کد نمونه را دریافت کنید

مخزن گیت‌هاب را از خط فرمان کلون کنید:

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

مخزن codelab-friendlychat-ios شامل چندین پروژه نمونه است:

  • ios-starter/swift-starter — پروژه اولیه Xcode که شامل اسکلت طرح‌بندی SwiftUI و نظرات TODO است که در آن ویژگی‌های برنامه را با Firebase پیاده‌سازی خواهید کرد.
  • ios/swift — پروژه تکمیل‌شده Xcode به همراه برنامه نمونه نهایی.

۳. اپلیکیشن اولیه را بسازید

برای آشنایی با رابط کاربری، برنامه‌ی آغازین را بسازید و اجرا کنید:

  1. در فایندر یا ترمینال، به دایرکتوری ios-starter/swift-starter بروید.
  2. برای باز کردن پروژه در Xcode، روی FriendlyChatSwift.xcodeproj دوبار کلیک کنید.
  3. توجه داشته باشید که Xcode به طور خودکار وابستگی‌های Swift Package Manager را در پس‌زمینه حل می‌کند ( firebase-ios-sdk نسخه ۱۲+ را از GitHub دانلود کنید).
  4. یک شبیه‌ساز iOS (مثلاً iPhone 17 Pro ) را انتخاب کنید و روی دکمه‌ی اجرا (cmd+R) کلیک کنید.

بعد از چند ثانیه، صفحه ورود به سیستم FriendlyChat ظاهر می‌شود. در این مرحله، دکمه‌های ورود و فیدهای پیام، به صورت موقت نمایش داده می‌شوند. در مراحل بعدی، Firebase را به برنامه متصل خواهید کرد.

۴. یک پروژه Firebase راه‌اندازی کنید

ایجاد یک پروژه جدید فایربیس

  1. با استفاده از حساب گوگل خود وارد کنسول فایربیس شوید.
  2. برای ایجاد یک پروژه جدید، روی دکمه کلیک کنید و سپس نام پروژه را وارد کنید (برای مثال، FriendlyChat ).
  3. روی ادامه کلیک کنید.
  4. در صورت درخواست، شرایط Firebase را مرور و قبول کنید و سپس روی ادامه کلیک کنید.
  5. (اختیاری) دستیار هوش مصنوعی را در کنسول Firebase (با نام "Gemini در Firebase") فعال کنید.
  6. برای این codelab، به گوگل آنالیتیکس نیاز ندارید ، بنابراین گزینه گوگل آنالیتیکس را غیرفعال کنید .
  7. روی ایجاد پروژه کلیک کنید، منتظر بمانید تا پروژه شما آماده شود و سپس روی ادامه کلیک کنید.

طرح قیمت‌گذاری فایربیس خود را ارتقا دهید

برای استفاده از فضای ذخیره‌سازی ابری برای فایربیس، پروژه فایربیس شما باید در طرح قیمت‌گذاری پرداخت در محل (Blaze) باشد، به این معنی که به یک حساب پرداخت ابری متصل باشد.

  • یک حساب Cloud Billing به یک روش پرداخت، مانند کارت اعتباری، نیاز دارد.
  • اگر در استفاده از فایربیس و گوگل کلود تازه‌کار هستید، بررسی کنید که آیا واجد شرایط دریافت اعتبار ۳۰۰ دلاری و یک حساب کاربری رایگان ابری هستید یا خیر.
  • اگر این codelab را به عنوان بخشی از یک رویداد انجام می‌دهید، از برگزارکننده خود بپرسید که آیا امکان استفاده از فضای ابری (Cloud credits) وجود دارد یا خیر.

برای ارتقاء پروژه خود به طرح Blaze، مراحل زیر را دنبال کنید:

  1. در کنسول Firebase، گزینه ارتقاء پلن خود را انتخاب کنید.
  2. طرح Blaze را انتخاب کنید. دستورالعمل‌های روی صفحه را دنبال کنید تا یک حساب Cloud Billing به پروژه شما متصل شود.
    اگر به عنوان بخشی از این ارتقا نیاز به ایجاد یک حساب Cloud Billing داشتید، ممکن است لازم باشد برای تکمیل ارتقا، به روند ارتقا در کنسول Firebase برگردید.

برنامه iOS خود را وصل کنید

  1. از صفحه نمای کلی پروژه، روی آیکون iOS کلیک کنید تا گردش کار راه‌اندازی برنامه اجرا شود.
  2. شناسه بسته را وارد کنید:
    com.google.firebase.codelab.FriendlyChatSwift
    
  3. روی ثبت برنامه کلیک کنید.

GoogleService-Info.plist را به پروژه Xcode خود اضافه کنید.

  1. برای ذخیره فایل پیکربندی، روی «دانلود GoogleService-Info.plist» کلیک کنید.
  2. در Xcode، فایل دانلود شده GoogleService-Info.plist را به گروه اصلی FriendlyChatSwift در Project Navigator بکشید (drag) و رها کنید.
  3. وقتی از شما خواسته شد، مطمئن شوید که گزینه‌ی «کپی موارد در صورت نیاز» تیک خورده باشد و هدف FriendlyChatSwift انتخاب شده باشد، سپس روی «پایان» کلیک کنید.

پیکربندی فایربیس در FriendlyChatSwiftApp.swift

در نهایت، هنگام اجرای برنامه SwiftUI، Firebase را با پیکربندی پروژه خود مقداردهی اولیه کنید. FriendlyChatSwiftApp.swift را باز کنید و متغیرهای TODO را با import FirebaseCore و FirebaseApp.configure() جایگزین کنید:

import SwiftUI
import FirebaseCore

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

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

۵. احراز هویت کاربران

پیکربندی احراز هویت در کنسول Firebase

قبل از اینکه بتوانید کاربران را احراز هویت کنید، ابتدا باید ارائه دهنده احراز هویت مورد نظر خود را در کنسول Firebase فعال کنید. در این آموزش از احراز هویت ایمیل/رمز عبور استفاده شده است:

  1. در کنسول Firebase، به Security > Authentication بروید، سپس روی Get started کلیک کنید.
  2. برگه روش ورود را انتخاب کنید.
  3. روی ایمیل/رمز عبور کلیک کنید، روی کلید فعال‌سازی کلیک کنید و ذخیره را بزنید.

ایمن‌سازی Cloud Firestore با قوانین احراز هویت

در مرحله بعد، از کاربران بخواهید قبل از خواندن یا نوشتن پیام‌ها، احراز هویت شوند:

  1. در کنسول Firebase، به Databases & Storage > Firestore Database بروید، سپس روی Create database کلیک کنید.
  2. یک مکان را انتخاب کنید و روی «بعدی» کلیک کنید.
  3. گزینه «شروع در حالت آزمایشی» را انتخاب کنید و روی «ایجاد» کلیک کنید.
  4. برگه قوانین را انتخاب کنید و قوانین امنیتی را به شرح زیر به‌روزرسانی کنید:
    rules_version = '2';
    
    service cloud.firestore {
      match /databases/{database}/documents {
        match /messages/{messageId} {
          allow read, write: if request.auth != null;
        }
      }
    }
    
    این قوانین به هر کاربر احراز هویت شده اجازه می‌دهد پیام‌ها را در پایگاه داده بخواند یا بنویسد، که برای اهداف یادگیری مناسب است. برای کسب اطلاعات بیشتر در مورد قوانین امنیتی، به مستندات قوانین امنیتی مراجعه کنید.
  5. روی انتشار کلیک کنید.

مشاهده وضعیت احراز هویت در UserViewModel.swift

در SwiftUI، مشاهده‌ی وضعیت احراز هویت Firebase به صورت واکنشی تضمین می‌کند که رابط کاربری شما به راحتی بین صفحه‌ی ورود به سیستم و رابط چت جابجا شود.

ViewModels/UserViewModel.swift را باز کنید و TODO را در init() جایگزین کنید تا تغییرات وضعیت احراز هویت را با استفاده از authStateChanges AsyncSequence مشاهده کنید:

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

پیاده‌سازی ورود، ثبت‌نام و خروج با Swift Concurrency

در UserViewModel.swift ، کامنت‌های TODO باقی‌مانده را با متدهای احراز هویت مدرن async / await جایگزین کنید:

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

تست احراز هویت کاربر

  1. روی دکمه‌ی Run در Xcode کلیک کنید.
  2. در شبیه‌ساز برنامه، روی «ثبت‌نام» کلیک کنید، نام نمایشی، ایمیل و رمز عبور را وارد کنید و روی «ثبت‌نام» ضربه بزنید.
  3. شما باید به طور خودکار احراز هویت شده و به صفحه پیام‌رسانی خالی هدایت شوید!

۶. از Cloud Firestore بخوانید

پیام‌های نمونه را در کنسول Firebase اضافه کنید

پایگاه داده را با پیام‌های نمونه پر کنید:

  1. در کنسول Firebase، به Firestore Database بروید و تب Data را انتخاب کنید.
  2. روی شروع جمع‌آوری کلیک کنید.
  3. messages مربوط به شناسه مجموعه را وارد کنید، سپس روی بعدی کلیک کنید.
  4. شناسه سند را روی تولید خودکار بگذارید (یا روی شناسه خودکار کلیک کنید).
  5. فیلدهای زیر را به سند اضافه کنید:
    • text (نوع: string ، مقدار: Hello )
    • displayName (نوع: string ، مقدار: anonymous )
  6. روی ذخیره کلیک کنید.
  7. در صورت تمایل، برای افزودن پیام‌های نمونه بیشتر، روی «افزودن سند» کلیک کنید.

همگام‌سازی پیام‌ها در FriendlyMessageViewModel.swift

در SwiftUI، یک مدل @Observable به‌روزرسانی‌های رابط کاربری اعلانی را هدایت می‌کند. ViewModels/FriendlyMessageViewModel.swift را باز کنید و startListening() و stopListening() را با استفاده از 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()
  }

نحوه رندر و اسکرول خودکار پیام‌ها توسط SwiftUI

در Views/ContentView.swift ، توجه کنید که چگونه ScrollViewReader و LazyVStack حباب‌های چت را رندر می‌کنند و هر زمان که messages.count تغییر کند، به طور خودکار به جدیدترین پیام اسکرول می‌کنند:

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

با پیوند دادن مستقیم وضعیت پایگاه داده به وضعیت رابط کاربری از طریق مدل نمایش، SwiftUI به کد برنامه اجازه می‌دهد تا انتقال‌ها را به طور تمیز به انیمیشن‌ها نگاشت کند. در مرحله بعد، داده‌ها را از برنامه به پایگاه داده می‌نویسید تا این انیمیشن را در عمل مشاهده کنید.

۷. ارسال پیام

پیاده‌سازی sendMessage با Swift Concurrency

وقتی با استفاده از addDocument() یک سند را به یک مجموعه اضافه می‌کنید، Cloud Firestore برای هر پیام چت یک شناسه منحصر به فرد ایجاد می‌کند.

در ViewModels/FriendlyMessageViewModel.swift ، دستور TODO در sendMessage(text:imageUrl:) را با یک متد 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)
  }

تست ارسال پیام

  1. روی دکمه‌ی Run در Xcode کلیک کنید.
  2. وارد حساب کاربری خود شوید.
  3. در قسمت پایین صفحه، یک پیام تایپ کنید و روی آیکون ارسال (Send) ضربه بزنید.
  4. ببینید که پیام فوراً در شبیه‌ساز شما ظاهر می‌شود!

۸. ذخیره و دریافت تصاویر

راه‌اندازی فضای ذخیره‌سازی ابری برای فایربیس

  1. در کنسول فایربیس، به مسیر Databases & Storage > Storage بروید، سپس روی Get started کلیک کنید.
  2. گزینه «شروع در حالت آزمایشی» را انتخاب کرده و روی «بعدی» کلیک کنید.
  3. محل ذخیره‌سازی پیش‌فرض را بپذیرید و روی «انجام شد» کلیک کنید.

انتخاب تصویر بومی با SwiftUI PhotosPicker

SwiftUI مدرن (iOS 16+) انتخاب عکس بومی را از طریق PhotosPicker از طریق import PhotosUI فراهم می‌کند - بدون نیاز به پل‌های نماینده UIKit یا پوشش‌دهنده‌های UIImagePickerController !

در Views/FooterView.swift ، مشاهده کنید که چگونه PhotosPicker تصاویر انتخاب شده را به هم متصل می‌کند:

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

پیاده‌سازی آپلود تصویر

وقتی عکسی انتخاب شد، داده‌های تصویر را در فضای ابری آپلود کنید و آدرس اینترنتی دانلود عمومی را در فضای ابری فایراستور ذخیره کنید.

در Views/FooterView.swift ، عبارت TODO در 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)")
    }
  }

اشتراک‌گذاری تصویر با فضای ذخیره‌سازی ابری

در Views/FriendlyMessageImageView.swift ، آدرس‌های اینترنتی تصاویر به صورت غیرهمزمان با استفاده از Swift Concurrency و Cloud Storage SDK بارگذاری می‌شوند:

  • URL های استاندارد دانلود HTTP/HTTPS به صورت غیرهمزمان از طریق URLSession بازیابی می‌شوند.
  • آدرس‌های اینترنتی فضای ذخیره‌سازی ابری گوگل (برای مثال، gs://... ) با استفاده از Storage.storage().reference(forURL:) واکشی می‌شوند.

تست پیام‌های تصویری

  1. روی دکمه‌ی Run در Xcode کلیک کنید.
  2. وارد حساب کاربری خود شوید.
  3. روی نماد عکس ضربه بزنید، یک تصویر از کتابخانه انتخاب کنید و آپلود و نمایش آن در چت را تماشا کنید!

۹. نتیجه‌گیری

تبریک می‌گویم، شما با موفقیت یک برنامه چت iOS بلادرنگ با استفاده از Swift و Firebase ساختید!

آنچه آموخته‌اید

  • یکپارچه‌سازی Xcode با مدیریت بسته‌های Swift (SPM) برای firebase-ios-sdk .
  • طرح‌بندی‌های اعلانی SwiftUI ، پیمایش خودکار PhotosPicker و ScrollViewReader .
  • همزمانی Swift ( async / await ) در سراسر احراز هویت، Cloud Firestore و Cloud Storage.
  • ایمیل/رمز عبور و احراز هویت ناشناس با مشاهده‌ی وضعیت واکنشی.
  • شنود لحظه‌ای اسنپ‌شات در کلود فایراستور و نوشتن خودکار شناسه.
  • فضای ذخیره‌سازی ابری برای آپلودهای باینری فایربیس و URLهای دانلود عمومی.

بیشتر بدانید