درس تطبيقي حول الترميز في Firebase على نظام التشغيل iOS Swift

1- نظرة عامة

2efe6805ef369641.png

مرحبًا بك في الدرس التطبيقي حول ترميز 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- إنشاء تطبيق المبتدئين

2f4c98d858c453fe.png

لإنشاء تطبيق المبتدئين:

  1. في نافذة طرفية، انتقِل إلى دليل android_studio_مجلد.pngios-starter/swift-starter من نموذج تنزيل الرمز البرمجي.
  2. السباق pod install --repo-update
  3. افتح ملف FriendlyChatSwift.xcworkspace لفتح المشروع في Xcode.
  4. انقر على الزر 98205811bbed9d74.pngتشغيل.

من المفترض أن تظهر الشاشة الرئيسية "محادثة ودية" بعد بضع ثوانٍ. من المفترض أن تظهر واجهة المستخدم. ومع ذلك، لا يمكنك في هذه المرحلة تسجيل الدخول أو إرسال رسائل أو استقبالها. سيتم إيقاف التطبيق مع استثناء حتى تكمل الخطوة التالية.

4. إنشاء مشروع وحدة تحكُّم Firebase

إنشاء مشروع

من وحدة تحكُّم Firebase، اختَر إضافة مشروع.

اتصل بالمشروع FriendlyChat، ثم انقر على إنشاء مشروع.

لقطة شاشة من 2015-11-06 14:13:39.png

ربط تطبيق iOS

  1. من شاشة "نظرة عامة على المشروع" في مشروعك الجديد، انقر على إضافة Firebase إلى تطبيق iOS.
  2. أدخِل معرِّف الحزمة باسم "com.google.firebase.codelab.FriendlyChatSwift".
  3. أدخِل مُعرّف App Store على النحو التالي "123456".
  4. انقر على تسجيل التطبيق.

إضافة ملف GoogleService-Info.plist إلى تطبيقك

في الشاشة الثانية، انقر على تنزيل GoogleService-Info.plist لتنزيل ملف إعداد يحتوي على جميع بيانات Firebase الوصفية اللازمة لتطبيقك. انسخ هذا الملف إلى تطبيقك وأضِفه إلى الهدف FriendlyChatSwift.

يمكنك الآن النقر على الرمز "x" في أعلى يسار النافذة المنبثقة لإغلاقها - مع تخطي الخطوتين 3 و4 - حيث ستنفذ هاتين الخطوتين هنا.

19d59efb213ddbdc.png

استيراد وحدة 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 بالنيابة عن المستخدمين، عليك تفعيلها.

  1. انتقِل إلى وحدة تحكُّم Firebase واختَر مشروعك.
  2. اختَر المصادقة.
  3. انقر على علامة التبويب طريقة تسجيل الدخول.
  4. بدِّل مفتاح Google إلى وضع التفعيل (اللون الأزرق)
  5. اضغط على حفظ في مربع الحوار الناتج.

إذا ظهرت لك أخطاء لاحقًا في هذا الدرس التطبيقي حول الترميز مع الرسالة "CONFIGURATION_NOT_FOUND"، يمكنك الرجوع إلى هذه الخطوة والتحقّق مرة أخرى من عملك.

التأكُّد من الاعتمادية على مصادقة Firebase

تأكَّد من وجود اعتماديات مصادقة Firebase في ملف Podfile.

ملف Podfile

pod 'Firebase/Auth'

إعداد ملف Info.plist لتسجيل الدخول إلى Google

عليك إضافة مخطط عنوان URL مخصّص إلى مشروع XCode.

  1. فتح إعدادات المشروع: انقر مرّتين على اسم المشروع في العرض التدرّجي الأيمن. حدد تطبيقك من القسم "استهدافات"، ثم حدد علامة التبويب "المعلومات"، ووسع القسم "أنواع عناوين URL".
  2. انقر على الزر + وأضف مخطط عنوان URL لمعرِّف العميل المعكوس. للعثور على هذه القيمة، افتح ملف التهيئة GoogleService-Info.plist، وابحث عن المفتاح REVERSED_CLIENT_ID. انسخ قيمة هذا المفتاح، والصقها في مربع مخططات عناوين URL في صفحة التهيئة. اترك الحقول الأخرى فارغة.
  3. عند الانتهاء، يجب أن تبدو التهيئة مشابهة لما يلي (ولكن مع القيم الخاصة بالتطبيق):

1b54d5bd2f4f1448.png

ضبط 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)")
    }
  }

اختبار قراءة الرسائل كمستخدم مسجّل الدخول

  1. انقر على الزر 98205811bbed9d74.pngتشغيل.
  2. من المفترَض أن يتم توجيهك فورًا إلى شاشة تسجيل الدخول. انقر على زر "تسجيل الدخول بحساب Google".
  3. من المفترض أن يتم نقلك بعد ذلك إلى شاشة المراسلة إذا سارت الأمور على ما يرام.

6- تفعيل قاعدة بيانات الوقت الفعلي

2efe6805ef369641.png

استيراد الرسائل

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

20ccf4856b715b4c.png

بعد استيراد قاعدة البيانات الخاصة بك، يجب أن تبدو كما يلي:

f3e0367f1c9cd187.png

التأكيد على تبعية قاعدة بيانات 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
  }

اختبار مزامنة الرسائل

  1. انقر على الزر 98205811bbed9d74.pngتشغيل.
  2. انقر على زر تسجيل الدخول للبدء للانتقال إلى نافذة الرسائل.
  3. يمكنك إضافة رسائل جديدة مباشرةً في "وحدة تحكُّم Firebase" من خلال النقر على رمز + الأخضر بجانب "الرسائل". الإدخال وإضافة كائن مثل ما يلي: f9876ffc8b316b14.png
  4. تأكَّد من ظهورها في واجهة مستخدم 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)
  }

اختبار إرسال الرسائل

  1. انقر على الزر 98205811bbed9d74.pngتشغيل.
  2. انقر على تسجيل الدخول للانتقال إلى نافذة الرسائل.
  3. اكتب رسالة ثم انقر على "إرسال". يجب أن تكون الرسالة الجديدة مرئية في واجهة مستخدم التطبيق وفي وحدة تحكُّم Firebase.

8- تخزين الصور واستلامها

تأكيد الاعتماد على مساحة التخزين في Firebase

في مجموعة الموارد التابعة لـ Podfile، تأكَّد من تضمين Firebase/Storage.

ملف Podfile

pod 'Firebase/Storage'

تفعيل مساحة تخزين Firebase في لوحة البيانات

انتقِل إلى "وحدة تحكُّم Firebase" وتأكَّد من تفعيل مساحة التخزين باستخدام "gs://PROJECTID.appspot.com". نطاق

b0438b37a588bcee.png

إذا ظهرت لك نافذة التفعيل بدلاً من ذلك، انقر على "البدء". لتفعيلها باستخدام القواعد التلقائية

c290bbebff2cafa7.png

ضبط 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])
      }
    }
  }

اختبار إرسال رسائل الصور واستلامها

  1. انقر على الزر 98205811bbed9d74.pngتشغيل.
  2. انقر على تسجيل الدخول للانتقال إلى نافذة الرسائل.
  3. انقر على "إضافة صورة" لاختيار صورة. يجب أن تكون الرسالة الجديدة التي تحتوي على الصورة مرئية في واجهة مستخدم التطبيق وفي وحدة تحكُّم Firebase.

10- تهانينا

لقد استخدمت Firebase لإنشاء تطبيق دردشة في الوقت الفعلي بسهولة.

النقاط التي تناولناها

  • قاعدة بيانات الوقت الفعلي
  • تسجيل الدخول الموحّد
  • التخزين

مزيد من المعلومات