1. نظرة عامة
مرحبًا بك في الدرس التطبيقي حول الترميز FriendlyChat لنظام التشغيل iOS. في هذا الدرس التطبيقي حول الترميز، ستتعرّف على كيفية إنشاء تطبيق محادثة متكامل الميزات في الوقت الفعلي على iOS باستخدام Firebase وميزات Swift الحديثة، مثل SwiftUI وSwift Concurrency وإطار عمل Observation.
أهداف الدورة التعليمية
- إدارة الحالة باستخدام إطار عمل Observation الحديث (
@Observable) - مصادقة المستخدمين باستخدام مصادقة Firebase
- مزامنة رسائل المحادثة في الوقت الفعلي باستخدام Cloud Firestore
- تخزين الصور وعرضها باستخدام مساحة تخزين سحابية لـ Firebase
- الوصول إلى واجهات برمجة تطبيقات Firebase باستخدام ميزات 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التي ستنفّذ فيها ميزات التطبيق باستخدام Firebaseios/swift: مشروع Xcode المكتمل الذي يحتوي على التطبيق النموذجي النهائي
3. إنشاء التطبيق الأولي
أنشئ التطبيق الأولي وشغِّله للتعرّف على واجهة المستخدم:
- في Finder أو Terminal، انتقِل إلى الدليل
ios-starter/swift-starter. - انقر مرّتين على
FriendlyChatSwift.xcodeprojلفتح المشروع في Xcode. - لاحظ أنّ Xcode يحلّ تلقائيًا تبعيات Swift Package Manager في الخلفية (تنزيل الإصدار 12 أو إصدار أحدث من
firebase-ios-sdkمن GitHub). - اختَر محاكي iOS (مثل iPhone 17 Pro) وانقر على الزر تشغيل (cmd+R).
بعد بضع ثوانٍ، ستظهر شاشة تسجيل الدخول إلى FriendlyChat. في هذه المرحلة، تكون أزرار تسجيل الدخول وخلاصات الرسائل عناصر نائبة. ستربط Firebase بالتطبيق في الخطوات التالية.
4. إعداد مشروع Firebase
إنشاء مشروع Firebase جديد
- سجِّل الدخول إلى وحدة تحكُّم Firebase باستخدام حساب Google.
- انقر على الزر لإنشاء مشروع جديد، ثم أدخِل اسم مشروع (مثل
FriendlyChat).
- انقر على متابعة.
- إذا طُلب منك ذلك، راجِع بنود Firebase واقبلها، ثم انقر على متابعة.
- (اختياري) فعِّل المساعدة المستندة إلى الذكاء الاصطناعي في وحدة تحكُّم Firebase (المعروفة باسم "Gemini في Firebase").
- في هذا الدرس التطبيقي حول الترميز، لا تحتاج إلى "إحصاءات Google"، لذا أوقِف خيار "إحصاءات Google".
- انقر على إنشاء مشروع، وانتظِر إلى أن يتم توفير مشروعك، ثم انقر على متابعة.
ترقية خطة التسعير في Firebase
لاستخدام مساحة تخزين سحابية لـ Firebase، يجب أن يكون مشروع Firebase الخاص بك ضمن خطة التسعير الدفع حسب الاستخدام (Blaze)، ما يعني أنّه مرتبط بـ حساب فوترة على السحابة الإلكترونية.
- يتطلّب حساب الفوترة على السحابة الإلكترونية طريقة دفع، مثل بطاقة الائتمان.
- إذا كنت جديدًا على Firebase وGoogle Cloud، تحقَّق مما إذا كنت مؤهلاً للحصول على رصيد بقيمة 300 دولار وحساب فوترة على السحابة الإلكترونية ضمن خطة تجريبية مجانية.
- إذا كنت تُجري هذا الدرس التطبيقي حول الترميز كجزء من فعالية، اسأل المنظِّم عمّا إذا كانت هناك أي أرصدة على السحابة الإلكترونية متاحة.
لترقية مشروعك إلى خطة Blaze، اتّبِع الخطوات التالية:
- في وحدة تحكُّم Firebase، انقر على لـ ترقية خطتك.
- اختَر خطة Blaze. اتّبِع التعليمات الظاهرة على الشاشة لربط حساب فوترة على السحابة الإلكترونية بمشروعك.
إذا احتجت إلى إنشاء حساب فوترة على السحابة الإلكترونية كجزء من هذه الترقية، قد تحتاج إلى الرجوع إلى مسار الترقية في وحدة تحكُّم Firebase لإكمال الترقية.
ربط تطبيقك على iOS
- من شاشة "نظرة عامة على المشروع"، انقر على رمز iOS لتشغيل سير عمل إعداد التطبيق.
- أدخِل رقم تعريف الحزمة:
com.google.firebase.codelab.FriendlyChatSwift - انقر على تسجيل التطبيق.
إضافة GoogleService-Info.plist إلى مشروع Xcode
- انقر على تنزيل GoogleService-Info.plist لحفظ ملف الإعداد.
- في Xcode، اسحب الملف الذي تم تنزيله
GoogleService-Info.plistإلى المجموعة الرئيسيةFriendlyChatSwiftفي "مستكشف المشروع". - عندما يُطلب منك ذلك، تأكَّد من وضع علامة في المربّع نسخ العناصر إذا لزم الأمر واختيار هدف
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، انتقِل إلى الأمان > المصادقة، ثم انقر على البدء.
- انقر على علامة التبويب طريقة تسجيل الدخول.
- انقر على البريد الإلكتروني/كلمة المرور ، ثم انقر على مفتاح تفعيل ، وانقر على حفظ.
تأمين Cloud Firestore باستخدام قواعد المصادقة
بعد ذلك، اطلب من المستخدمين أن يكونوا مصادقين قبل قراءة الرسائل أو كتابتها:
- في وحدة تحكُّم Firebase، انتقِل إلى قواعد البيانات ومساحة التخزين > قاعدة بيانات Firestore، ثم انقر على إنشاء قاعدة بيانات.
- اختَر موقعًا جغرافيًا وانقر على التالي.
- اختَر البدء في وضع الاختبار وانقر على إنشاء.
- انقر على علامة التبويب القواعد ، وعدِّل قواعد الأمان لتصبح على النحو التالي:
تسمح هذه القواعد لأي مستخدم مصادق بقراءة الرسائل أو كتابتها في قاعدة البيانات، وهو أمر مناسب لأغراض التعلّم. مزيد من المعلومات حول قواعد الأمان في مستندات قواعد الأمان.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() لمراقبة تغييرات حالة المصادقة باستخدام AsyncSequence authStateChanges:
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)
}
}
اختبار مصادقة المستخدم
- انقر على الزر تشغيل في Xcode.
- في محاكي التطبيق، انقر على الاشتراك، وأدخِل اسمًا معروضًا وبريدًا إلكترونيًا وكلمة مرور، ثم انقر على الاشتراك.
- من المفترَض أن تتم مصادقتك تلقائيًا والانتقال إلى شاشة المراسلة الفارغة.
6. القراءة من Cloud Firestore
إضافة رسائل نموذجية في وحدة تحكُّم Firebase
املأ قاعدة البيانات برسائل نموذجية:
- في وحدة تحكُّم Firebase، انتقِل إلى قاعدة بيانات Firestore وانقر على علامة التبويب البيانات.
- انقر على بدء المجموعة.
- أدخِل
messagesفي رقم تعريف المجموعة، ثم انقر على التالي. - اترك رقم تعريف المستند مضبوطًا على الإنشاء التلقائي (أو انقر على رقم تعريف تلقائي).
- أضِف الحقول التالية إلى المستند:
text(النوع:string، القيمة:Hello)displayName(النوع:string، القيمة:anonymous)
- انقر على حفظ.
- يمكنك اختياريًا النقر على إضافة مستند لإضافة المزيد من الرسائل النموذجية.
مزامنة الرسائل في FriendlyMessageViewModel.swift
في SwiftUI، يؤدي نموذج @Observable إلى إجراء تعديلات تعريفية على واجهة المستخدم. افتح ViewModels/FriendlyMessageViewModel.swift ونفِّذ startListening() وstopListening() باستخدام AsyncSequence snapshots:
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)
}
اختبار إرسال الرسائل
- انقر على الزر تشغيل في Xcode.
- سجّل الدخول إلى حسابك.
- اكتب رسالة في حقل النص السفلي وانقر على رمز الإرسال.
- شاهد الرسالة تظهر على الفور في المحاكي.
8. تخزين الصور واستلامها
إعداد مساحة تخزين سحابية لـ Firebase
- في وحدة تحكُّم Firebase، انتقِل إلى قواعد البيانات ومساحة التخزين > مساحة التخزين، ثم انقر على البدء.
- اختَر البدء في وضع الاختبار وانقر على التالي.
- اقبل موقع التخزين التلقائي وانقر على تم.
اختيار الصور الأصلية باستخدام PhotosPicker في SwiftUI
يوفر 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 وحزمة تطوير البرامج (SDK) لـ Cloud Storage:
- يتم استرداد عناوين URL العادية للتنزيل عبر HTTP/HTTPS بشكل غير متزامن من خلال
URLSession. - يتم جلب عناوين URL لـ Google Cloud Storage (مثل
gs://...) باستخدامStorage.storage().reference(forURL:).
اختبار رسائل الصور
- انقر على الزر تشغيل في 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 العامة للتنزيل في مساحة تخزين سحابية لـ Firebase