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.
- انقر على الزر تشغيل.
من المفترض أن تظهر لك شاشة "المحادثة الودية" الرئيسية بعد بضع ثوانٍ. من المفترض أن تظهر واجهة المستخدم. في هذه المرحلة، لا يمكنك تسجيل الدخول أو إرسال رسائل أو تلقّيها. سيتم إلغاء التطبيق مع استثناء إلى أن تكمل الخطوة التالية.
4. إنشاء مشروع وحدة تحكُّم Firebase
إنشاء مشروع
من وحدة تحكُّم Firebase، اختَر إضافة مشروع.
أدخِل اسم المشروع FriendlyChat
، ثم انقر على إنشاء مشروع.
ترقية خطة أسعار Firebase
لاستخدام Cloud Storage لبرنامج 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 إلى تطبيقك
في الشاشة الثانية، انقر على Download 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 API بالنيابة عن المستخدمين، عليك تفعيلها.
- انتقِل إلى وحدة تحكُّم Firebase واختَر مشروعك.
- اختَر المصادقة.
- انقر على علامة التبويب طريقة تسجيل الدخول.
- بدِّل مفتاح Google إلى وضع التفعيل (اللون الأزرق)
- اضغط على حفظ في مربع الحوار الناتج.
إذا ظهرت لك أخطاء في وقت لاحق من هذا الدليل التعليمي للترميز تتضمّن الرسالة "CONFIGURATION_NOT_FOUND"، ارجع إلى هذه الخطوة وتحقّق من عملك جيدًا.
التأكُّد من الاعتمادية على مصادقة Firebase
تأكَّد من توفّر تبعيات 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 للسماح للمستخدم بالدخول إلى التطبيق، بعد تسجيل الدخول بنجاح. وإزالة المستمع على 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'
إعداد Cloud Storage لمنصّة Firebase
في ما يلي كيفية إعداد ميزة "التخزين في السحابة الإلكترونية" لبرنامج Firebase في مشروعك على Firebase:
- في اللوحة اليمنى من وحدة تحكّم Firebase، وسِّع الإصدار، ثم اختَر مساحة التخزين.
- انقر على البدء.
- اختَر موقعًا جغرافيًا لحزمتك التلقائية على مساحة التخزين.
يمكن للحِزم فيUS-WEST1
وUS-CENTRAL1
وUS-EAST1
الاستفادة من المستوى "المجاني دائمًا" في Google Cloud Storage. تخضع الحِزم في جميع المواقع الجغرافية الأخرى لأسعار Google 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 لتخزين هذه الصورة مع قاعدة البيانات حتى يتم إرسال هذه الصورة داخل الرسالة.
عدّل طريقة "image PickerController: doFinish PickingMediaWithInfo:" لـ 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 لإنشاء تطبيق محادثة في الوقت الفعلي بسهولة.
المواضيع التي تناولناها
- قاعدة بيانات الوقت الفعلي
- تسجيل الدخول الموحّد
- التخزين