1. نظرة عامة
مرحبًا بك في برنامج Friendly Chat التعليمي. في هذا الدرس العملي، ستتعلّم كيفية استخدام منصة Firebase لإنشاء تطبيقات iOS. ستنفّذ برنامجًا للدردشة وتراقب أدائه باستخدام Firebase.
ما ستتعلمه
- السماح للمستخدمين بتسجيل الدخول
- مزامنة البيانات باستخدام "قاعدة بيانات Firebase في الوقت الفعلي"
- تخزين الملفات الثنائية في Firebase Storage
المتطلبات
- 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
لاستخدام Cloud Storage for Firebase، يجب أن يكون مشروعك على Firebase ضمن خطة التسعير "الدفع حسب الاستخدام" (Blaze)، ما يعني أنّه مرتبط بحساب فوترة Cloud.
- يتطلّب حساب الفوترة في Cloud طريقة دفع، مثل بطاقة الائتمان.
- إذا كنت حديث العهد باستخدام Firebase وGoogle Cloud، تحقَّق ممّا إذا كنت مؤهَّلاً للحصول على رصيد بقيمة 300 دولار أمريكي وحساب فوترة على 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
استخدِم طريقة "الإعداد" في 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 Authentication نيابةً عن المستخدمين، عليك تفعيلها.
- انتقِل إلى وحدة تحكّم Firebase واختَر مشروعك.
- اختَر المصادقة.
- اختَر علامة التبويب طريقة تسجيل الدخول.
- انقر على مفتاح التبديل Google لتفعيله (اللون الأزرق).
- انقر على حفظ في مربّع الحوار الناتج
إذا ظهرت لك أخطاء لاحقًا في هذا الدرس العملي مع الرسالة "CONFIGURATION_NOT_FOUND"، ارجع إلى هذه الخطوة وتحقّق من عملك.
تأكيد الاعتمادية على Firebase Auth
تأكَّد من توفّر تبعيات "مصادقة Firebase" في الملف 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 Auth للسماح للمستخدم بالدخول إلى التطبيق بعد تسجيل الدخول بنجاح. ويجب إزالة أداة معالجة الأحداث عند إلغاء تهيئة الكائن.
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. تفعيل Realtime Database
استيراد الرسائل
في مشروعك في وحدة تحكّم 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 Realtime Database، سيتمّ إضافة معرّف تلقائي. تكون أرقام التعريف التي يتم إنشاؤها تلقائيًا متسلسلة، ما يضمن إضافة الرسائل الجديدة بالترتيب الصحيح.
عدِّل طريقة "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 Storage
في قسم التبعيات من Podfile
، تأكَّد من تضمين Firebase/Storage
.
Podfile
pod 'Firebase/Storage'
إعداد "التخزين في السحابة الإلكترونية لبرنامج Firebase"
في ما يلي كيفية إعداد "التخزين السحابي لبرنامج Firebase" في مشروعك على Firebase:
- في اللوحة اليمنى من وحدة تحكّم Firebase، وسِّع إنشاء، ثم اختَر مساحة التخزين.
- انقر على البدء.
- اختَر موقعًا جغرافيًا لحزمة Storage التلقائية.
يمكن للحِزم فيUS-WEST1
وUS-CENTRAL1
وUS-EAST1
الاستفادة من الفئة"دائمًا مجانية" في Google Cloud Storage. تخضع الحِزم في جميع المواقع الجغرافية الأخرى لأسعار واستخدام Google Cloud Storage. - انقر على البدء في وضع الاختبار. اقرأ بيان إخلاء المسؤولية عن قواعد الأمان.
في وقت لاحق من هذا الدرس العملي، ستضيف قواعد أمان لحماية بياناتك. لا توزّع تطبيقًا أو تعرضه للجميع بدون إضافة "قواعد الأمان" لحزمة Cloud Storage. - انقر على إنشاء.
ضبط FirebaseStorage
FCViewController.swift
func configureStorage() {
storageRef = Storage.storage().reference()
}
تلقّي الصور في الرسائل الحالية
أضِف رمزًا برمجيًا ينزّل الصور من Firebase Storage.
عدِّل طريقة 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 لإنشاء تطبيق دردشة في الوقت الفعلي بسهولة
المواضيع التي تناولناها
- قاعدة بيانات الوقت الفعلي
- تسجيل الدخول الموحّد
- التخزين