1. סקירה כללית
ברוכים הבאים ל-codelab של FriendlyChat ל-iOS! ב-Codelab הזה נסביר איך ליצור אפליקציית צ'אט ל-iOS עם כל התכונות הנדרשות בזמן אמת באמצעות Firebase ותכונות מודרניות של Swift כמו SwiftUI, Swift Concurrency ו-Observation framework.
מה תלמדו
- ניהול מצב באמצעות מסגרת התצפית המודרנית (
@Observable). - אימות משתמשים באמצעות אימות ב-Firebase.
- אפשר לסנכרן הודעות בצ'אט בזמן אמת באמצעות Cloud Firestore.
- אחסון והצגת תמונות באמצעות Cloud Storage for Firebase.
- גישה לממשקי Firebase API באמצעות תכונות מודרניות של Swift Concurrency.
מה צריך
- הגרסה האחרונה של Xcode
- סימולטור iOS או מכשיר בדיקה פיזי
- חשבון Google ליצירה ולניהול של פרויקט Firebase
2. קבלת קוד לדוגמה
משכפלים את המאגר ב-GitHub משורת הפקודה:
$ git clone https://github.com/firebase/codelab-friendlychat-ios
מאגר codelab-friendlychat-ios מכיל כמה פרויקטים לדוגמה:
-
ios-starter/swift-starter– פרויקט Xcode התחלתי שמכיל את השלד של פריסת SwiftUI ותגובותTODOשבהן מטמיעים תכונות של האפליקציה באמצעות Firebase. -
ios/swift– פרויקט Xcode שהושלם עם אפליקציית הדוגמה המוגמרת.
3. יצירת אפליקציה לתחילת הדרך
כדי להכיר את ממשק המשתמש, בונים ומריצים את אפליקציית המתחילים:
- ב-Finder או בטרמינל, עוברים לספרייה
ios-starter/swift-starter. - לוחצים לחיצה כפולה על
FriendlyChatSwift.xcodeprojכדי לפתוח את הפרויקט ב-Xcode. - שימו לב ש-Xcode מטפל אוטומטית ביחסי התלות של Swift Package Manager ברקע (הורדה של
firebase-ios-sdkגרסה 12 ומעלה מ-GitHub). - בוחרים סימולטור iOS (לדוגמה, iPhone 17 Pro) ולוחצים על הלחצן Run (הפעלה) (cmd+R).
אחרי כמה שניות, יופיע מסך הכניסה של FriendlyChat. בשלב הזה, לחצני הכניסה ופידי ההודעות הם placeholders. בשלבים הבאים נסביר איך לקשר את Firebase לאפליקציה.
4. הגדרת פרויקט Firebase
יצירת פרויקט חדש ב-Firebase
- נכנסים למסוף Firebase באמצעות חשבון Google.
- לוחצים על הלחצן כדי ליצור פרויקט חדש, ואז מזינים שם לפרויקט (לדוגמה,
FriendlyChat).
- לוחצים על המשך.
- אם מוצגת בקשה לעשות זאת, קוראים ומאשרים את התנאים של Firebase, ואז לוחצים על המשך.
- (אופציונלי) מפעילים את העזרה מבוססת-AI במסוף Firebase (שנקראת 'Gemini ב-Firebase').
- ב-codelab הזה לא צריך להשתמש ב-Google Analytics, ולכן משביתים את האפשרות של Google Analytics.
- לוחצים על יצירת פרויקט, מחכים שהפרויקט יוקצה ולוחצים על המשך.
שדרוג תוכנית התמחור של Firebase
כדי להשתמש ב-Cloud Storage for Firebase, הפרויקט ב-Firebase צריך להיות בתוכנית התמחור 'תשלום לפי שימוש' (Blaze), כלומר הוא צריך להיות מקושר לחשבון לחיוב ב-Cloud.
- בחשבון לחיוב ב-Cloud צריך להגדיר אמצעי תשלום, כמו כרטיס אשראי.
- אם אתם חדשים ב-Firebase וב-Google Cloud, כדאי לבדוק אם אתם עומדים בדרישות לקבלת קרדיט בשווי 300$ותקופת ניסיון בחינם בחשבון לחיוב ב-Cloud.
- אם אתם משתתפים באירוע שכולל את ה-codelab הזה, כדאי לשאול את מארגן האירוע אם יש קרדיטים ל-Cloud.
כדי לשדרג את הפרויקט לתוכנית Blaze, פועלים לפי השלבים הבאים:
- במסוף Firebase, בוחרים באפשרות שדרוג התוכנית.
- בוחרים בתוכנית Blaze. פועלים לפי ההוראות שמופיעות במסך כדי לקשר חשבון לחיוב ב-Cloud לפרויקט.
אם הייתם צריכים ליצור חשבון לחיוב ב-Cloud כחלק מהשדרוג, יכול להיות שתצטרכו לחזור לתהליך השדרוג במסוף Firebase כדי להשלים את השדרוג.
קישור אפליקציה ל-iOS
- במסך Project Overview (סקירת הפרויקט), לוחצים על הסמל iOS כדי להפעיל את תהליך העבודה להגדרת האפליקציה.
- מזינים את מזהה החבילה:
com.google.firebase.codelab.FriendlyChatSwift - לוחצים על Register app (רישום האפליקציה).
הוספה של GoogleService-Info.plist לפרויקט Xcode
- לוחצים על Download GoogleService-Info.plist כדי לשמור את קובץ ההגדרות.
- ב-Xcode, גוררים את הקובץ
GoogleService-Info.plistשהורד אל הקבוצה הראשיתFriendlyChatSwiftב-Project Navigator. - כשמוצגת בקשה, מוודאים שהתיבה העתקת פריטים אם צריך מסומנת ושנבחר יעד
FriendlyChatSwift, ואז לוחצים על סיום.
הגדרת Firebase ב-FriendlyChatSwiftApp.swift
לבסוף, מאתחלים את Firebase עם הגדרות הפרויקט כשמפעילים את אפליקציית SwiftUI. פותחים את FriendlyChatSwiftApp.swift ומחליפים את הפלייסהולדרים TODO ב-import FirebaseCore וב-FirebaseApp.configure():
import SwiftUI
import FirebaseCore
@main
struct FriendlyChatSwiftApp: App {
init() {
FirebaseApp.configure()
}
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
5. אימות משתמשים
הגדרת אימות במסוף Firebase
לפני שאפשר לאמת משתמשים, צריך קודם להפעיל את ספק האימות שבחרתם במסוף Firebase. במדריך הזה נעשה שימוש באימות באמצעות אימייל וסיסמה:
- במסוף Firebase, עוברים אל Security (אבטחה) > Authentication (אימות), ואז לוחצים על Get started (תחילת העבודה).
- בוחרים בכרטיסייה שיטת הכניסה.
- לוחצים על אימייל/סיסמה, לוחצים על המתג הפעלה ואז על שמירה.
הגנה על Cloud Firestore באמצעות כללי אימות
לאחר מכן, דורשים מהמשתמשים לעבור אימות לפני שהם קוראים או כותבים הודעות:
- במסוף Firebase, עוברים אל Databases & Storage (מסדי נתונים ואחסון) > Firestore Database (מסד נתונים של Firestore), ואז לוחצים על Create database (יצירת מסד נתונים).
- בוחרים מיקום ולוחצים על הבא.
- בוחרים באפשרות התחלה במצב בדיקה ולוחצים על יצירה.
- לוחצים על הכרטיסייה כללים ומעדכנים את כללי האבטחה כך:
הכללים האלה מאפשרים לכל משתמש מאומת לקרוא או לכתוב הודעות במסד הנתונים, וזה מתאים למטרות לימוד. מידע נוסף על כללי אבטחה זמין במסמכי התיעוד בנושא כללי אבטחה.rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { match /messages/{messageId} { allow read, write: if request.auth != null; } } } - לוחצים על פרסום.
הצגת מצב האימות ב-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)
}
}
בדיקת אימות משתמשים
- לוחצים על הלחצן Run (הפעלה) ב-Xcode.
- בסימולטור האפליקציות, לוחצים על הרשמה, מזינים שם לתצוגה, כתובת אימייל וסיסמה, ומקישים על הרשמה.
- האימות אמור להתבצע אוטומטית ותועברו למסך ההודעות הריק.
6. קריאה מ-Cloud Firestore
הוספת הודעות לדוגמה במסוף Firebase
מאכלסים את מסד הנתונים בהודעות לדוגמה:
- במסוף Firebase, עוברים אל Firestore Database ולוחצים על הכרטיסייה Data (נתונים).
- לוחצים על התחלת האוסף.
- מזינים
messagesבמזהה האוסף ולוחצים על הבא. - משאירים את ההגדרה של מזהה המסמך כהגדרה של יצירה אוטומטית (או לוחצים על מזהה אוטומטי).
- מוסיפים את השדות הבאים למסמך:
-
text(סוג:string, ערך:Hello) -
displayName(סוג:string, ערך:anonymous)
-
- לוחצים על שמירה.
- אפשר גם ללחוץ על הוספת מסמך כדי להוסיף עוד הודעות לדוגמה.
סנכרון הודעות ב-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 מאפשרת לקוד האפליקציה למפות מעברים לאנימציות בצורה נקייה. בשלב הבא תכתבו נתונים למסד הנתונים מהאפליקציה כדי לראות את האנימציה הזו בפעולה.
7. שליחת הודעות
הטמעה של 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)
}
בדיקה של שליחת הודעות
- לוחצים על הלחצן Run (הפעלה) ב-Xcode.
- היכנס לחשבונך.
- כותבים הודעה בשדה הטקסט התחתון ומקישים על סמל השליחה.
- ההודעה תופיע באופן מיידי בסימולטור.
8. אחסון וקבלה של תמונות
הגדרת Cloud Storage for Firebase
- במסוף Firebase, עוברים אל Databases & Storage (מסדי נתונים ואחסון) > Storage (אחסון), ואז לוחצים על Get started (תחילת העבודה).
- בוחרים באפשרות הפעלה במצב בדיקה ולוחצים על הבא.
- מאשרים את מיקום האחסון שמוגדר כברירת מחדל ולוחצים על סיום.
בחירת תמונות מקוריות באמצעות 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)
}
}
}
הטמעה של העלאת תמונות
כשבוחרים תמונה, מעלים את נתוני התמונה ל-Cloud Storage ושומרים את כתובת ה-URL הציבורית להורדה ב-Cloud Firestore.
ב-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)")
}
}
שיתוף תמונות באמצעות Cloud Storage
ב-Views/FriendlyMessageImageView.swift, כתובות ה-URL של התמונות נטענות באופן אסינכרוני באמצעות Swift Concurrency ו-Cloud Storage SDK:
- כתובות URL רגילות להורדה מסוג HTTP/HTTPS מאוחזרות באופן אסינכרוני באמצעות
URLSession. - כתובות URL של Google Cloud Storage (לדוגמה,
gs://...) מאוחזרות באמצעותStorage.storage().reference(forURL:).
בדיקה של הודעות עם תמונות
- לוחצים על הלחצן Run (הפעלה) ב-Xcode.
- היכנס לחשבונך.
- מקישים על סמל התמונה, בוחרים תמונה מהספרייה ורואים אותה מועלית ומופיעה בצ'אט.
9. סיכום
כל הכבוד, יצרתם בהצלחה אפליקציית צ'אט ל-iOS בזמן אמת באמצעות Swift ו-Firebase!
מה למדתם
- שילוב של Swift Package Manager (SPM) ב-Xcode עבור
firebase-ios-sdk. - פריסות הצהרתיות של SwiftUI,
PhotosPickerוגלילה אוטומטית שלScrollViewReader. - Swift Concurrency (
async/await) באימות, ב-Cloud Firestore וב-Cloud Storage. - אימות באמצעות אימייל/סיסמה ואימות אנונימי עם מעקב אחר מצב ריאקטיבי.
- האזנה לתמונת מצב בזמן אמת ב-Cloud Firestore וכתיבת מזהים אוטומטית.
- העלאות בינאריות וכתובות URL ציבוריות להורדה ב-Cloud Storage for Firebase.