1- نظرة عامة
مرحبًا بك في الدرس التطبيقي حول ترميز Friendly Chat. في هذا الدرس التطبيقي حول الترميز، ستتعلم كيفية استخدام نظام Firebase الأساسي لإنشاء تطبيقات iOS. ستُنفذ برنامج دردشة وتراقب أدائه باستخدام Firebase.
المعلومات التي ستطّلع عليها
- السماح للمستخدمين بتسجيل الدخول
- مزامنة البيانات باستخدام قاعدة بيانات Firebase في الوقت الفعلي.
- تخزين الملفات الثنائية في تخزين Firebase.
المتطلبات
- Xcode
- CocoaPods
- جهاز اختباري يعمل بالإصدار 8.0 من نظام التشغيل iOS أو إصدار أحدث أو مُحاكي
كيف ستستخدم هذا البرنامج التعليمي؟
كيف تقيّم تجربتك في إنشاء تطبيقات iOS؟
2- الحصول على الرمز النموذجي
استنسِخ مستودع GitHub من سطر الأوامر.
$ git clone https://github.com/firebase/codelab-friendlychat-ios
3- إنشاء تطبيق المبتدئين
لإنشاء تطبيق المبتدئين:
- في نافذة طرفية، انتقِل إلى دليل
ios-starter/swift-starter
من نموذج تنزيل الرمز البرمجي. - السباق
pod install --repo-update
- افتح ملف FriendlyChatSwift.xcworkspace لفتح المشروع في Xcode.
- انقر على الزر تشغيل.
من المفترض أن تظهر الشاشة الرئيسية "محادثة ودية" بعد بضع ثوانٍ. من المفترض أن تظهر واجهة المستخدم. ومع ذلك، لا يمكنك في هذه المرحلة تسجيل الدخول أو إرسال رسائل أو استقبالها. سيتم إيقاف التطبيق مع استثناء حتى تكمل الخطوة التالية.
4. إنشاء مشروع وحدة تحكُّم Firebase
إنشاء مشروع
من وحدة تحكُّم Firebase، اختَر إضافة مشروع.
اتصل بالمشروع FriendlyChat
، ثم انقر على إنشاء مشروع.
ربط تطبيق 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، اختَر "قاعدة البيانات في الوقت الفعلي" (Realtime Database)، ثم انقر على علامة التبويب "القواعد" (القواعد). بعد ذلك، عدِّل القواعد لتبدو كما يلي:
{
"rules": {
"messages": {
".read": "auth != null",
".write": "auth != null"
}
}
}
لمزيد من المعلومات حول كيفية عمل ذلك (بما في ذلك المستندات حول متغير "auth") راجع مستندات الأمان في Firebase.
ضبط واجهات برمجة تطبيقات المصادقة
قبل أن يتمكّن تطبيقك من الوصول إلى واجهات Firebase Authentication API بالنيابة عن المستخدمين، عليك تفعيلها.
- انتقِل إلى وحدة تحكُّم Firebase واختَر مشروعك.
- اختَر المصادقة.
- انقر على علامة التبويب طريقة تسجيل الدخول.
- بدِّل مفتاح Google إلى وضع التفعيل (اللون الأزرق)
- اضغط على حفظ في مربع الحوار الناتج.
إذا ظهرت لك أخطاء لاحقًا في هذا الدرس التطبيقي حول الترميز مع الرسالة "CONFIGURATION_NOT_FOUND"، يمكنك الرجوع إلى هذه الخطوة والتحقّق مرة أخرى من عملك.
التأكُّد من الاعتمادية على مصادقة Firebase
تأكَّد من وجود اعتماديات مصادقة 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 للسماح للمستخدم بالدخول إلى التطبيق، بعد تسجيل الدخول بنجاح. وإزالة المستمع على Deinit.
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، اختَر عنصر قاعدة البيانات في شريط التنقّل الأيمن. في القائمة الكاملة لقاعدة البيانات، اختَر Import JSON (استيراد JSON). تصفّح للوصول إلى ملف initial_messages.json
في دليل Friendlychat، وحدده ثم انقر على الزر استيراد. سيؤدي هذا إلى استبدال أي بيانات موجودة حاليًا في قاعدة البيانات الخاصة بك. يمكنك أيضًا تحرير قاعدة البيانات مباشرة، باستخدام رمزي + الأخضر والأحمر لإضافة عناصر وإزالتها.
بعد استيراد قاعدة البيانات الخاصة بك، يجب أن تبدو كما يلي:
التأكيد على تبعية قاعدة بيانات Firebase
في مجموعة الموارد التابعة لملف Podfile
، تأكَّد من تضمين Firebase/Database
.
ملف Podfile
pod 'Firebase/Database'
مزامنة الرسائل الحالية
إضافة رمز يعمل على مزامنة الرسائل المضافة حديثًا مع واجهة مستخدم التطبيق.
الرمز الذي تضيفه في هذا القسم:
- قم بتهيئة قاعدة بيانات Firebase وإضافة مستمع للتعامل مع التغييرات التي يتم إجراؤها على قاعدة البيانات.
- عدِّل "
DataSnapshot
" ليتم عرض الرسائل الجديدة.
عدّل "deinit" و"ConfigureDatabase" و"tableView:cellForRow indexPath" في أداة FCViewController: الطرق؛ استبدله بالتعليمة البرمجية المحددة أدناه:
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- إرسال الرسائل
تنفيذ ميزة "إرسال الرسائل"
إرسال القيم إلى قاعدة البيانات. عند استخدام طريقة الإرسال لإضافة بيانات إلى قاعدة بيانات 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" وتأكَّد من تفعيل مساحة التخزين باستخدام "gs://PROJECTID.appspot.com". نطاق
إذا ظهرت لك نافذة التفعيل بدلاً من ذلك، انقر على "البدء". لتفعيلها باستخدام القواعد التلقائية
ضبط 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 لتخزين هذه الصورة مع قاعدة البيانات حتى يتم إرسال هذه الصورة داخل الرسالة.
تعديل FCViewController's "image PickerController: deFinish PickingMediaWithInfo:" الطريقة؛ استبدله بالتعليمة البرمجية المحددة أدناه:
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 لإنشاء تطبيق دردشة في الوقت الفعلي بسهولة.
النقاط التي تناولناها
- قاعدة بيانات الوقت الفعلي
- تسجيل الدخول الموحّد
- التخزين