1. نظرة عامة

مرحبًا بك في درس Friendly Chat التطبيقي حول الترميز. في هذا الدرس التطبيقي حول الترميز، ستتعلّم كيفية استخدام منصة Firebase لإنشاء تطبيقات iOS. ستنفّذ برنامجًا للدردشة وتراقب أدائه باستخدام Firebase.
ما ستتعلمه
- السماح للمستخدمين بتسجيل الدخول
- مزامنة البيانات باستخدام "قاعدة بيانات Firebase في الوقت الفعلي"
- تخزين الملفات الثنائية في مساحة تخزين Firebase
المتطلبات
- Xcode
- CocoaPods
- جهاز اختبار يعمل بنظام التشغيل iOS 8.0 أو إصدار أحدث أو محاكي
كيف ستستخدم هذا البرنامج التعليمي؟
كيف تقيّم تجربتك في إنشاء تطبيقات iOS؟
2. الحصول على الرمز النموذجي
استنسِخ مستودع GitHub من سطر الأوامر.
$ git clone https://github.com/firebase/codelab-friendlychat-ios
3- إنشاء التطبيق الأوّلي

لإنشاء تطبيق البداية، اتّبِع الخطوات التالية:
- في نافذة المحطة الطرفية، انتقِل إلى الدليل

ios-starter/swift-starterمن عملية تنزيل الرمز النموذجي. - السباق
pod install --repo-update - افتح الملف FriendlyChatSwift.xcworkspace لفتح المشروع في Xcode.
- انقر على الزر
تشغيل.
من المفترض أن تظهر الشاشة الرئيسية لتطبيق Friendly Chat بعد بضع ثوانٍ. يجب أن تظهر واجهة المستخدم. ومع ذلك، لا يمكنك في هذه المرحلة تسجيل الدخول أو إرسال رسائل أو تلقّيها. سيتم إيقاف التطبيق مع ظهور خطأ إلى أن تُكمل الخطوة التالية.
4. إعداد مشروع Firebase
إنشاء مشروع Firebase جديد
- سجِّل الدخول إلى وحدة تحكّم Firebase باستخدام حساب Google.
- انقر على الزر لإنشاء مشروع جديد، ثم أدخِل اسم المشروع (على سبيل المثال،
FriendlyChat).
- انقر على متابعة.
- إذا طُلب منك ذلك، راجِع بنود Firebase واقبلها، ثم انقر على متابعة.
- (اختياري) فعِّل ميزة "المساعدة المستندة إلى الذكاء الاصطناعي" في وحدة تحكّم Firebase (المعروفة باسم "Gemini في Firebase").
- في هذا الدرس العملي، لا تحتاج إلى "إحصاءات Google"، لذا أوقِف خيار "إحصاءات Google".
- انقر على إنشاء مشروع، وانتظِر إلى أن يتم توفير مشروعك، ثم انقر على متابعة.
ترقية خطة أسعار Firebase
لاستخدام مساحة تخزين سحابية لـ Firebase، يجب أن يكون مشروع Firebase الخاص بك ضمن خطة التسعير "الدفع حسب الاستخدام" (Blaze)، ما يعني أنّه مرتبط بحساب فوترة على Cloud.
- يتطلّب حساب الفوترة في Cloud طريقة دفع، مثل بطاقة الائتمان.
- إذا كنت حديث العهد باستخدام Firebase وGoogle Cloud، تحقّق ممّا إذا كنت مؤهَّلاً للحصول على رصيد بقيمة 300 دولار أمريكي وحساب فوترة في Cloud ضمن "الفترة التجريبية المجانية".
- إذا كنت تجري هذا الدرس التطبيقي حول الترميز كجزء من حدث، اسأل المنظّم عمّا إذا كانت هناك أي أرصدة Cloud متاحة.
لترقية مشروعك إلى خطة Blaze، اتّبِع الخطوات التالية:
- في "وحدة تحكّم Firebase"، اختَر ترقية خطتك.
- اختَر خطة Blaze. اتّبِع التعليمات الظاهرة على الشاشة لربط حساب فوترة على Cloud بمشروعك.
إذا كان عليك إنشاء حساب فوترة على Cloud كجزء من عملية الترقية هذه، قد تحتاج إلى الرجوع إلى مسار الترقية في وحدة تحكّم Firebase لإكمال عملية الترقية.
ربط تطبيق iOS
- من شاشة "نظرة عامة على المشروع" لمشروعك الجديد، انقر على إضافة Firebase إلى تطبيق iOS.
- أدخِل معرّف الحزمة، مثل "
com.google.firebase.codelab.FriendlyChatSwift". - أدخِل معرّف App Store على النحو التالي: "
123456". - انقر على تسجيل التطبيق.
إضافة ملف GoogleService-Info.plist إلى تطبيقك
في الشاشة الثانية، انقر على تنزيل ملف GoogleService-Info.plist لتنزيل ملف إعدادات يحتوي على جميع البيانات الوصفية اللازمة في Firebase لتطبيقك. انسخ هذا الملف إلى تطبيقك وأضِفه إلى هدف FriendlyChatSwift.
يمكنك الآن النقر على "x" في أعلى يسار النافذة المنبثقة لإغلاقها، وتخطّي الخطوتَين 3 و4، لأنّك ستنفّذ هاتين الخطوتَين هنا.
استيراد وحدة Firebase
ابدأ بالتأكّد من استيراد الوحدة Firebase.
AppDelegate.swift وFCViewController.swift
import Firebase
إعداد Firebase في AppDelegate
استخدِم طريقة "configure" في FirebaseApp داخل الدالة application:didFinishLaunchingWithOptions لضبط خدمات Firebase الأساسية من ملف .plist.
AppDelegate.swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions
launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
FirebaseApp.configure()
GIDSignIn.sharedInstance().delegate = self
return true
}
5- تحديد هوية المستخدمين
استخدام القواعد لحصر الوصول على المستخدمين الذين تم إثبات ملكيتهم
سنضيف الآن قاعدة تتطلّب المصادقة قبل قراءة أي رسائل أو كتابتها. لإجراء ذلك، نضيف القواعد التالية إلى عنصر بيانات الرسائل. من قسم "قاعدة البيانات" في وحدة تحكّم Firebase، اختَر "قاعدة بيانات الوقت الفعلي"، ثم انقر على علامة التبويب "القواعد". بعد ذلك، عدِّل القواعد لتصبح على النحو التالي:
{
"rules": {
"messages": {
".read": "auth != null",
".write": "auth != null"
}
}
}
لمزيد من المعلومات حول طريقة عمل ذلك (بما في ذلك المستندات المتعلقة بالمتغيّر "auth")، يُرجى الاطّلاع على مستندات الأمان في Firebase.
ضبط واجهات برمجة التطبيقات الخاصة بالمصادقة
قبل أن يتمكّن تطبيقك من الوصول إلى واجهات برمجة التطبيقات الخاصة بخدمة مصادقة Firebase نيابةً عن المستخدمين، عليك تفعيلها.
- انتقِل إلى وحدة تحكّم Firebase واختَر مشروعك.
- انقر على المصادقة.
- اختَر علامة التبويب طريقة تسجيل الدخول.
- انقر على مفتاح التبديل Google لتفعيله (اللون الأزرق)
- انقر على حفظ في مربّع الحوار الناتج
إذا ظهرت لك أخطاء لاحقًا في هذا الدرس التطبيقي حول الترميز مع الرسالة "CONFIGURATION_NOT_FOUND"، ارجع إلى هذه الخطوة وتحقّق من عملك.
تأكيد الاعتمادية على Firebase Auth
تأكَّد من توفّر تبعيات Firebase Auth في الملف Podfile.
Podfile
pod 'Firebase/Auth'
إعداد ملف Info.plist لميزة "تسجيل الدخول باستخدام حساب Google"
عليك إضافة مخطّط URL مخصّص إلى مشروع XCode.
- افتح إعدادات مشروعك: انقر مرّتين على اسم المشروع في العرض التدرّجي على يمين الصفحة. اختَر تطبيقك من قسم "الاستهدافات"، ثم انقر على علامة التبويب "المعلومات"، ووسِّع قسم "أنواع عناوين URL".
- انقر على الزر +، وأضِف مخطط URL لمعرّف العميل المعكوس. للعثور على هذه القيمة، افتح ملف الإعداد GoogleService-Info.plist وابحث عن المفتاح REVERSED_CLIENT_ID. انسخ قيمة هذا المفتاح والصِقها في مربّع "مخططات عناوين URL" في صفحة الإعدادات. اترك الحقول الأخرى فارغة.
- عند الانتهاء، من المفترض أن تبدو إعداداتك مشابهة لما يلي (ولكن مع القيم الخاصة بتطبيقك):

ضبط clientID لميزة "تسجيل الدخول باستخدام حساب Google"
بعد إعداد Firebase، يمكننا استخدام clientID لإعداد ميزة "تسجيل الدخول بحساب Google" داخل طريقة "didFinishLaunchingWithOptions:".
AppDelegate.swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions
launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
FirebaseApp.configure()
GIDSignIn.sharedInstance().clientID = FirebaseApp.app()?.options.clientID
GIDSignIn.sharedInstance().delegate = self
return true
}
إضافة معالج تسجيل الدخول
بعد اكتمال عملية تسجيل الدخول باستخدام حساب Google بنجاح، استخدِم الحساب للمصادقة مع Firebase.
AppDelegate.swift
func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error?) {
if let error = error {
print("Error \(error)")
return
}
guard let authentication = user.authentication else { return }
let credential = GoogleAuthProvider.credential(withIDToken: authentication.idToken,
accessToken: authentication.accessToken)
Auth.auth().signIn(with: credential) { (user, error) in
if let error = error {
print("Error \(error)")
return
}
}
}
تسجيل دخول المستخدم تلقائيًا بعد ذلك، أضِف متتبِّعًا إلى Firebase المصادقة للسماح للمستخدم بالدخول إلى التطبيق بعد تسجيل الدخول بنجاح. ويجب إزالة أداة المعالجة عند إيقاف التطبيق.
SignInViewController.swift
override func viewDidLoad() {
super.viewDidLoad()
GIDSignIn.sharedInstance().uiDelegate = self
GIDSignIn.sharedInstance().signInSilently()
handle = Auth.auth().addStateDidChangeListener() { (auth, user) in
if user != nil {
MeasurementHelper.sendLoginEvent()
self.performSegue(withIdentifier: Constants.Segues.SignInToFp, sender: nil)
}
}
}
deinit {
if let handle = handle {
Auth.auth().removeStateDidChangeListener(handle)
}
}
تسجيل الخروج
إضافة طريقة تسجيل الخروج
FCViewController.swift
@IBAction func signOut(_ sender: UIButton) {
let firebaseAuth = Auth.auth()
do {
try firebaseAuth.signOut()
dismiss(animated: true, completion: nil)
} catch let signOutError as NSError {
print ("Error signing out: \(signOutError.localizedDescription)")
}
}
اختبار قراءة الرسائل بصفتك مستخدمًا مسجّلاً الدخول
- انقر على الزر
تشغيل. - من المفترض أن يتم توجيهك على الفور إلى شاشة تسجيل الدخول. انقر على زر "تسجيل الدخول باستخدام حساب Google".
- بعد ذلك، من المفترض أن يتم توجيهك إلى شاشة المراسلة إذا سارت الأمور على ما يرام.
6. تفعيل قاعدة بيانات الوقت الفعلي

استيراد الرسائل
في مشروعك في وحدة تحكّم Firebase، انقر على العنصر قاعدة البيانات في شريط التنقّل الأيمن. في القائمة الكاملة لقاعدة البيانات، اختَر استيراد JSON. انتقِل إلى ملف initial_messages.json في دليل friendlychat، واختَره، ثم انقر على الزر استيراد. سيؤدي هذا الإجراء إلى استبدال أي بيانات حالية في قاعدة البيانات. يمكنك أيضًا تعديل قاعدة البيانات مباشرةً، باستخدام علامة الجمع الخضراء وعلامة الضرب الحمراء لإضافة عناصر وإزالتها.

بعد الاستيراد، من المفترض أن تبدو قاعدة البيانات على النحو التالي:

تأكيد الاعتماد على قاعدة بيانات Firebase
في قسم التبعيات من الملف Podfile، تأكَّد من تضمين Firebase/Database.
Podfile
pod 'Firebase/Database'
مزامنة الرسائل الحالية
أضِف رمزًا برمجيًا يتيح مزامنة الرسائل المُضافة حديثًا مع واجهة مستخدم التطبيق.
سيؤدي الرمز الذي تضيفه في هذا القسم إلى ما يلي:
- إعداد قاعدة بيانات Firebase وإضافة متتبِّع لمعالجة التغييرات التي يتم إجراؤها على قاعدة البيانات.
- يجب تحديث
DataSnapshotحتى تظهر الرسائل الجديدة.
عدِّل طرق FCViewController "deinit" و"configureDatabase" و"tableView:cellForRow indexPath:" واستبدلها بالرمز البرمجي المحدّد أدناه:
FCViewController.swift
deinit {
if let refHandle = _refHandle {
self.ref.child("messages").removeObserver(withHandle: _refHandle)
}
}
func configureDatabase() {
ref = Database.database().reference()
// Listen for new messages in the Firebase database
_refHandle = self.ref.child("messages").observe(.childAdded, with: { [weak self] (snapshot) -> Void in
guard let strongSelf = self else { return }
strongSelf.messages.append(snapshot)
strongSelf.clientTable.insertRows(at: [IndexPath(row: strongSelf.messages.count-1, section: 0)], with: .automatic)
})
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Dequeue cell
let cell = self.clientTable.dequeueReusableCell(withIdentifier: "tableViewCell", for: indexPath)
// Unpack message from Firebase DataSnapshot
let messageSnapshot = self.messages[indexPath.row]
guard let message = messageSnapshot.value as? [String: String] else { return cell }
let name = message[Constants.MessageFields.name] ?? ""
let text = message[Constants.MessageFields.text] ?? ""
cell.textLabel?.text = name + ": " + text
cell.imageView?.image = UIImage(named: "ic_account_circle")
if let photoURL = message[Constants.MessageFields.photoURL], let URL = URL(string: photoURL),
let data = try? Data(contentsOf: URL) {
cell.imageView?.image = UIImage(data: data)
}
return cell
}
اختبار مزامنة الرسائل
- انقر على الزر
تشغيل. - انقر على الزر تسجيل الدخول لبدء المراسلة للانتقال إلى نافذة الرسائل.
- أضِف رسائل جديدة مباشرةً في وحدة تحكّم Firebase من خلال النقر على رمز علامة الجمع الخضراء بجانب الإدخال "الرسائل" وإضافة عنصر مثل ما يلي:

- تأكَّد من ظهورها في واجهة مستخدم Friendly-Chat.
7. إرسال الرسائل
تنفيذ وظيفة "إرسال رسالة"
إرسال القيم إلى قاعدة البيانات عند استخدام طريقة push لإضافة بيانات إلى قاعدة بيانات Firebase في الوقت الفعلي، سيتمّ إضافة معرّف تلقائي. تكون أرقام التعريف التي يتم إنشاؤها تلقائيًا متسلسلة، ما يضمن إضافة الرسائل الجديدة بالترتيب الصحيح.
عدِّل طريقة "sendMessage:" في FCViewController، واستبدِلها بالرمز المحدّد أدناه:
FCViewController.swift
func sendMessage(withData data: [String: String]) {
var mdata = data
mdata[Constants.MessageFields.name] = Auth.auth().currentUser?.displayName
if let photoURL = Auth.auth().currentUser?.photoURL {
mdata[Constants.MessageFields.photoURL] = photoURL.absoluteString
}
// Push data to Firebase Database
self.ref.child("messages").childByAutoId().setValue(mdata)
}
اختبار إرسال الرسائل
- انقر على الزر
تشغيل. - انقر على تسجيل الدخول للانتقال إلى نافذة الرسائل.
- اكتب رسالة وانقر على "إرسال". من المفترض أن تظهر الرسالة الجديدة في واجهة مستخدم التطبيق وفي "وحدة تحكّم Firebase".
8. تخزين الصور واستلامها
تأكيد الاعتمادية على "مساحة تخزين Firebase"
في قسم التبعيات في ملف Podfile، تأكَّد من تضمين Firebase/Storage.
Podfile
pod 'Firebase/Storage'
إعداد مساحة تخزين سحابية لـ Firebase
في ما يلي كيفية إعداد مساحة تخزين سحابية لـ Firebase في مشروع Firebase:
- في اللوحة اليمنى من وحدة تحكّم Firebase، وسِّع إنشاء، ثم اختَر مساحة التخزين.
- انقر على البدء.
- اختَر موقعًا جغرافيًا لحزمة التخزين التلقائية.
يمكن للحِزم فيUS-WEST1وUS-CENTRAL1وUS-EAST1الاستفادة من الفئة"دائمًا مجانية" في Google Cloud Storage. تخضع الحِزم في جميع المواقع الجغرافية الأخرى لأسعار واستخدام Google Cloud Storage. - انقر على بدء التشغيل في وضع الاختبار. اقرأ بيان إخلاء المسؤولية حول قواعد الأمان.
في وقت لاحق من هذا الدرس العملي، ستضيف قواعد أمان لحماية بياناتك. لا توزّع تطبيقًا أو تعرضه للجميع بدون إضافة "قواعد الأمان" لحزمة Cloud Storage. - انقر على إنشاء.
ضبط FirebaseStorage
FCViewController.swift
func configureStorage() {
storageRef = Storage.storage().reference()
}
تلقّي الصور في الرسائل الحالية
أضِف رمزًا برمجيًا ينزّل الصور من مساحة تخزين Firebase.
عدِّل طريقة FCViewController "tableView: cellForRowAt indexPath:" واستبدلها بالرمز البرمجي المحدّد أدناه:
FCViewController.swift
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Dequeue cell
let cell = self.clientTable .dequeueReusableCell(withIdentifier: "tableViewCell", for: indexPath)
// Unpack message from Firebase DataSnapshot
let messageSnapshot: DataSnapshot! = self.messages[indexPath.row]
guard let message = messageSnapshot.value as? [String:String] else { return cell }
let name = message[Constants.MessageFields.name] ?? ""
if let imageURL = message[Constants.MessageFields.imageURL] {
if imageURL.hasPrefix("gs://") {
Storage.storage().reference(forURL: imageURL).getData(maxSize: INT64_MAX) {(data, error) in
if let error = error {
print("Error downloading: \(error)")
return
}
DispatchQueue.main.async {
cell.imageView?.image = UIImage.init(data: data!)
cell.setNeedsLayout()
}
}
} else if let URL = URL(string: imageURL), let data = try? Data(contentsOf: URL) {
cell.imageView?.image = UIImage.init(data: data)
}
cell.textLabel?.text = "sent by: \(name)"
} else {
let text = message[Constants.MessageFields.text] ?? ""
cell.textLabel?.text = name + ": " + text
cell.imageView?.image = UIImage(named: "ic_account_circle")
if let photoURL = message[Constants.MessageFields.photoURL], let URL = URL(string: photoURL),
let data = try? Data(contentsOf: URL) {
cell.imageView?.image = UIImage(data: data)
}
}
return cell
}
9. إرسال رسائل صور
تنفيذ ميزة "تخزين الصور وإرسالها"
حمِّل صورة من المستخدم، ثم زامن عنوان URL الخاص بمساحة تخزين هذه الصورة مع قاعدة البيانات ليتم إرسال هذه الصورة داخل الرسالة.
عدِّل طريقة "imagePickerController: didFinishPickingMediaWithInfo:" في FCViewController، واستبدلها بالرمز المحدّد أدناه:
FCViewController.swift
func imagePickerController(_ picker: UIImagePickerController,
didFinishPickingMediaWithInfo info: [String : Any]) {
picker.dismiss(animated: true, completion:nil)
guard let uid = Auth.auth().currentUser?.uid else { return }
// if it's a photo from the library, not an image from the camera
if #available(iOS 8.0, *), let referenceURL = info[UIImagePickerControllerReferenceURL] as? URL {
let assets = PHAsset.fetchAssets(withALAssetURLs: [referenceURL], options: nil)
let asset = assets.firstObject
asset?.requestContentEditingInput(with: nil, completionHandler: { [weak self] (contentEditingInput, info) in
let imageFile = contentEditingInput?.fullSizeImageURL
let filePath = "\(uid)/\(Int(Date.timeIntervalSinceReferenceDate * 1000))/\((referenceURL as AnyObject).lastPathComponent!)"
guard let strongSelf = self else { return }
strongSelf.storageRef.child(filePath)
.putFile(from: imageFile!, metadata: nil) { (metadata, error) in
if let error = error {
let nsError = error as NSError
print("Error uploading: \(nsError.localizedDescription)")
return
}
strongSelf.sendMessage(withData: [Constants.MessageFields.imageURL: strongSelf.storageRef.child((metadata?.path)!).description])
}
})
} else {
guard let image = info[UIImagePickerControllerOriginalImage] as? UIImage else { return }
let imageData = UIImageJPEGRepresentation(image, 0.8)
let imagePath = "\(uid)/\(Int(Date.timeIntervalSinceReferenceDate * 1000)).jpg"
let metadata = StorageMetadata()
metadata.contentType = "image/jpeg"
self.storageRef.child(imagePath)
.putData(imageData!, metadata: metadata) { [weak self] (metadata, error) in
if let error = error {
print("Error uploading: \(error)")
return
}
guard let strongSelf = self else { return }
strongSelf.sendMessage(withData: [Constants.MessageFields.imageURL: strongSelf.storageRef.child((metadata?.path)!).description])
}
}
}
اختبار إرسال رسائل الصور واستلامها
- انقر على الزر
تشغيل. - انقر على تسجيل الدخول للانتقال إلى نافذة الرسائل.
- انقر على رمز "إضافة صورة" لاختيار صورة. يجب أن تظهر الرسالة الجديدة التي تتضمّن الصورة في واجهة مستخدم التطبيق وفي "وحدة تحكّم Firebase".
10. تهانينا!
استخدَمت Firebase لإنشاء تطبيق دردشة في الوقت الفعلي بسهولة.
المواضيع التي تناولناها
- قاعدة بيانات الوقت الفعلي
- تسجيل الدخول الموحّد
- التخزين
