الإنشاء باستخدام Firebase Data Connect (لنظام التشغيل iOS أو Swift)

1. نظرة عامة

يرشدك هذا الدرس التطبيقي حول الترميز إلى عملية دمج Firebase Data Connect مع قاعدة بيانات Cloud SQL لإنشاء تطبيق لمراجعة الأفلام على أجهزة iOS باستخدام SwiftUI.

ستتعلّم كيفية ربط تطبيق iOS بقاعدة بيانات Cloud SQL باستخدام Firebase Data Connect، ما يتيح مزامنة البيانات بسلاسة لمراجعات الأفلام.

في نهاية هذا الدرس العملي، سيكون لديك تطبيق iOS عملي يتيح للمستخدمين تصفّح الأفلام ووضع علامة على الأفلام المفضّلة، وكل ذلك مدعوم بقاعدة بيانات Cloud SQL باستخدام إمكانات Firebase Data Connect.

أهداف الدورة التعليمية

سيعلّمك هذا الدرس التطبيقي العملي كيفية:

  • إعداد Firebase Data Connect باستخدام مجموعة محاكي Firebase للحصول على نتائج سريعة
  • تصميم مخطط قاعدة بيانات باستخدام Data Connect وGraphQL
  • أنشئ حزمة تطوير برامج (SDK) متوافقة مع Swift من مخطط قاعدة البيانات وأضِفها إلى تطبيق Swift.
  • نفِّذ مصادقة المستخدم وادمِجها مع Firebase Data Connect لتأمين بيانات المستخدمين.
  • يمكنك استرداد البيانات وتعديلها وحذفها وإدارتها في Cloud SQL باستخدام الاستعلامات والتعديلات المستندة إلى GraphQL.
  • (اختياري) نشر خدمة Data Connect في مرحلة الإنتاج.

المتطلبات الأساسية

  • أحدث إصدار من Xcode
  • عيّنة التعليمات البرمجية في الدرس العملي سيتم تنزيل الرمز النموذجي في إحدى الخطوات الأولى من الدرس العملي.

2. إعداد المشروع النموذجي

إنشاء مشروع Firebase

  1. سجِّل الدخول إلى وحدة تحكّم Firebase باستخدام حسابك على Google.
  2. انقر على الزر لإنشاء مشروع جديد، ثم أدخِل اسم المشروع (على سبيل المثال، Friendly Flix).
  3. انقر على متابعة.
  4. إذا طُلب منك ذلك، راجِع بنود Firebase واقبلها، ثم انقر على متابعة.
  5. (اختياري) فعِّل ميزة "المساعدة المستندة إلى الذكاء الاصطناعي" في وحدة تحكّم Firebase (المعروفة باسم "Gemini في Firebase").
  6. في هذا الدرس العملي، لا تحتاج إلى "إحصاءات Google"، لذا أوقِف خيار "إحصاءات Google".
  7. انقر على إنشاء مشروع، وانتظِر إلى أن يتم توفير مشروعك، ثم انقر على متابعة.

تنزيل الرمز

نفِّذ الأمر التالي لاستنساخ الرمز النموذجي لهذا الدرس العملي. سيؤدي هذا إلى إنشاء دليل باسم codelab-dataconnect-ios على جهازك:

git clone https://github.com/FirebaseExtended/codelab-dataconnect-ios`

إذا لم يكن لديك git على جهازك، يمكنك أيضًا تنزيل الرمز مباشرةً من GitHub.

إضافة إعدادات Firebase

تستخدم حزمة تطوير البرامج (SDK) من Firebase ملف إعداد للربط بمشروعك على Firebase. على منصات Apple، يُطلق على هذا الملف اسم GoogleServices-Info.plist. في هذه الخطوة، عليك تنزيل ملف الإعداد وإضافته إلى مشروع Xcode.

  1. في وحدة تحكّم Firebase، اختَر نظرة عامة على المشروع في شريط التنقّل الأيمن.
  2. انقر على الزر iOS+ لاختيار النظام الأساسي. عندما يُطلب منك إدخال معرّف حزمة Apple، استخدِم com.google.firebase.samples.FriendlyFlix
  3. انقر على تسجيل التطبيق واتّبِع التعليمات لتنزيل ملف GoogleServices-Info.plist.
  4. انقل الملف الذي تم تنزيله إلى الدليل start/FriendlyFlix/app/FriendlyFlix/FriendlyFlix/ للرمز الذي نزّلته للتو، واستبدِل ملف GoogleServices-Info.plist الحالي.
  5. بعد ذلك، انقر على التالي بضع مرات لإكمال إعداد المشروع في وحدة تحكّم Firebase (لست بحاجة إلى إضافة حزمة تطوير البرامج (SDK) إلى التطبيق، لأنّ ذلك قد تمّ إعداده لك في مشروع البداية).
  6. أخيرًا، انقر على متابعة إلى وحدة التحكّم لإنهاء عملية الإعداد.

3- إعداد Data Connect

تثبيت

التثبيت التلقائي

نفِّذ الأمر التالي في دليل codelab-dataconnect-ios/FriendlyFlix:

curl -sL https://firebase.tools/dataconnect | bash

يحاول هذا النص البرمجي إعداد بيئة التطوير لك وتشغيل بيئة تطوير متكاملة مستندة إلى المتصفّح. توفّر بيئة التطوير المتكاملة هذه أدوات، بما في ذلك إضافة VS Code مجمّعة مسبقًا، لمساعدتك في إدارة المخطط وتحديد طلبات البحث وعمليات التعديل التي سيتم استخدامها في تطبيقك، وإنشاء حِزم SDK ذات أنواع محددة بدقة.

بعد تشغيل النص البرمجي، من المفترض أن يتم فتح VS Code تلقائيًا.

بعد إجراء ذلك مرة واحدة، يمكنك بدء VS Code من خلال تشغيل VS Code في الدليل المحلي:

code .

التثبيت اليدوي

  1. تثبيت Visual Studio Code
  2. تثبيت Node.js
  3. في VS Code، افتح الدليل codelab-dataconnect-ios/FriendlyFlix.
  4. ثبِّت إضافة Firebase Data Connect من Visual Studio Code Marketplace.

تهيئة Data Connect في المشروع

في اللوحة اليمنى، انقر على رمز Firebase لفتح واجهة مستخدم إضافة Data Connect VS Code.

  1. انقر على الزر تسجيل الدخول باستخدام حساب Google. سيتم فتح نافذة متصفّح، اتّبِع التعليمات لتسجيل الدخول إلى الإضافة باستخدام حسابك على Google.
  2. انقر على الزر ربط مشروع Firebase واختَر المشروع الذي أنشأته سابقًا في وحدة التحكّم.
  3. انقر على الزر Run firebase init واتّبِع الخطوات في الوحدة الطرفية المدمجة.

ضبط إعدادات إنشاء حزمة تطوير البرامج (SDK)

بعد النقر على الزر تشغيل عملية الإعداد في Firebase، من المفترض أن تنشئ إضافة Firebase Data Connect دليلاً باسم dataconnect.

في VS Code، افتح الملف dataconnect/connector/connector.yaml وستجد الإعدادات التلقائية.

يُرجى تعديل الإعدادات واستخدام الإعدادات التالية للتأكّد من أنّ الرمز الذي تم إنشاؤه يعمل مع هذا الدرس العملي. على وجه التحديد، تأكَّد من ضبط connectorId على friendly-flix، وحزمة Swift على FriendlyFlixSDK.

connectorId: "friendly-flix"
generate:
  swiftSdk:
    outputDir: "../../app"
    package: "FriendlyFlixSDK"
    observablePublisher: observableMacro

في ما يلي توضيح لهذه الإعدادات:

  • connectorId: اسم فريد لهذا الموصل.
  • outputDir: المسار الذي سيتم فيه تخزين حزمة تطوير البرامج (SDK) التي تم إنشاؤها في Data Connect هذا المسار نسبي إلى الدليل الذي يحتوي على ملف connector.yaml.
  • package: اسم الحزمة التي سيتم استخدامها لحزمة Swift التي تم إنشاؤها

بعد حفظ هذا الملف، ستنشئ أداة Firebase Data Connect حزمة Swift باسم FriendlyFlixSDK لك، وستضعها بجانب مجلد المشروع FriendlyFlix.

بدء محاكيات Firebase

في VS Code، انتقِل إلى طريقة عرض Firebase، ثم انقر على الزر بدء المحاكيات.

سيؤدي ذلك إلى بدء تشغيل محاكي Firebase في الوحدة الطرفية المدمجة. يجب أن تبدو المخرجات على النحو التالي:

npx -y firebase-tools@latest emulators:start --project <your-project-id>

إضافة الحزمة التي تم إنشاؤها إلى تطبيق Swift

  1. فتح FriendlyFlix/app/FriendlyFlix/FriendlyFlix.xcodeproj في Xcode
  2. اختَر ملف > إضافة تبعيات الحزمة...
  3. انقر على إضافة ملف محلي...، ثم أضِف حزمة FriendlyFlixSDK من مجلد FriendlyFlix/app
  4. انتظِر إلى أن يحلّ Xcode التبعيات المرتبطة بالحزمة.
  5. في مربّع الحوار اختيار منتجات الحزمة لـ FriendlyFlixSDK، اختَر FriendlyFlix كهدف، ثم انقر على إضافة حزمة.

ضبط تطبيق iOS لاستخدام المحاكي المحلي

  1. فتح FriendlyFlixApp.swift (يمكنك الضغط على CMD + Shift + O لفتح مربّع الحوار Quick Open، ثم كتابة "FriendlyFlixApp" للعثور على الملف بسرعة)
  2. استيراد Firebase وFirebase Auth وFirebase Data Connect وحزمة تطوير البرامج (SDK) التي تم إنشاؤها للمخطط
  3. في أداة التهيئة، اضبط إعدادات Firebase.
  4. تأكَّد من أنّ DataConnect وFirebase Auth تستخدمان المحاكي المحلي.
import SwiftUI
import os
import Firebase
import FirebaseAuth
import FriendlyFlixSDK
import FirebaseDataConnect

@main
struct FriendlyFlixApp: App {
  ...

  init() {
    FirebaseApp.configure()
    if useEmulator {
      DataConnect.friendlyFlixConnector.useEmulator(port: 9399)
      Auth.auth().useEmulator(withHost: "localhost", port: 9099)
    }

    authenticationService = AuthenticationService()
  }

  ...

}
  1. اختَر iOS Simulator من القائمة المنسدلة الوجهة.
  2. اضغط على CMD+R (أو انقر على الزر تشغيل) في Xcode لتشغيل التطبيق على المحاكي.

4. تحديد المخطط وملء قاعدة البيانات مسبقًا

في هذا القسم، ستحدّد بنية الكيانات الرئيسية في تطبيق الأفلام والعلاقات بينها في مخطط. يتم ربط الكيانات، مثل Movie وMovieMetaData وغيرها، بجداول قواعد البيانات، مع إنشاء علاقات باستخدام توجيهات مخطط GraphQL وFirebase Data Connect.

الكيانات والعلاقات الأساسية

يتألف نموذج البيانات لتطبيق تتبُّع الأفلام هذا من عدة عناصر ستنشئها خلال هذه الدراسة التطبيقية حول الترميز. ستنشئ الكيانات الأساسية أولاً، وعند تنفيذ المزيد من الميزات، ستضيف الكيانات المطلوبة لهذه الميزات.

في هذه الخطوة، ستنشئ النوعَين Movie وMovieMetadata.

فيلم

يحدّد النوع Movie البنية الرئيسية لعنصر الفيلم، بما في ذلك الحقول مثل title وgenre وreleaseYear وrating.

في VS Code، أضِف تعريف النوع Movie إلى dataconnect/schema/schema.gql:

type Movie @table {
  id: UUID! @default(expr: "uuidV4()")
  title: String!
  imageUrl: String!
  releaseYear: Int
  genre: String
  rating: Float
  description: String
  tags: [String]
}

MovieMetadata

يُنشئ النوع MovieMetadata علاقة فردية مع النوع Movie. ويتضمّن بيانات إضافية، مثل مخرج الفيلم.

أضِف تعريف جدول MovieMetadata إلى ملف dataconnect/schema/schema.gql:

type MovieMetadata @table {
  movie: Movie! @ref
  director: String
}

الحقول التي يتم إنشاؤها تلقائيًا والإعدادات التلقائية

يستخدم المخطط تعابير مثل @default(expr: "uuidV4()") لإنشاء معرّفات فريدة وطوابع زمنية تلقائيًا. على سبيل المثال، يتم ملء الحقل id في النوع Movie تلقائيًا بمعرّف فريد عالمي (UUID) عند إنشاء سجلّ جديد.

إدراج بيانات تجريبية للأفلام والبيانات الوصفية للأفلام

بعد تحديد المخطط، يمكنك الآن تعبئة قاعدة البيانات مسبقًا ببيانات وهمية لإجراء الاختبار.

  1. في Finder، انسخ finish/FriendlyFlix/dataconnect/moviedata_insert.gql إلى المجلد start/FriendlyFlix/dataconnect.
  2. في VS Code، افتح dataconnect/moviedata_insert.gql.
  3. تأكَّد من أنّ المحاكيات في إضافة Firebase Data Connect تعمل.
  4. من المفترض أن يظهر لك الزر تشغيل (محلي) في أعلى الملف. انقر على هذا الزر لإدراج بيانات الأفلام التجريبية في قاعدة البيانات.
  5. راجِع نافذة تنفيذ Data Connect للتأكّد من أنّه تمت إضافة البيانات بنجاح.

بعد توفّر البيانات، انتقِل إلى الخطوة التالية لمعرفة كيفية إنشاء طلبات بحث في Data Connect.

5- استرداد الأفلام وعرضها

في هذا القسم، ستنفّذ ميزة لعرض قائمة بالأفلام.

أولاً، ستتعرّف على كيفية إنشاء طلب بحث يستردّ جميع الأفلام من جدول movies. تنشئ أداة Firebase Data Connect رمزًا لحزمة تطوير برامج (SDK) آمنة الأنواع يمكنك استخدامها بعد ذلك لتنفيذ طلب البحث وعرض الأفلام التي تم استردادها في واجهة مستخدم تطبيقك.

تحديد طلب البحث ListMovies

تتم كتابة طلبات البحث في Firebase Data Connect بلغة GraphQL، ما يتيح لك تحديد الحقول التي تريد استردادها. في FriendlyFlix، تتطلّب الشاشات التي تعرض الأفلام الحقول التالية: title وdescription وreleaseYear وrating وimageUrl. بالإضافة إلى ذلك، بما أنّ هذا التطبيق هو تطبيق SwiftUI، ستحتاج إلى id للمساعدة في تحديد هوية عرض SwiftUI.

في VS Code، افتح الملف dataconnect/connector/queries.gql وأضِف طلب البحث ListMovies:

query ListMovies @auth(level: PUBLIC) {
  movies {
    id
    title
    imageUrl
    releaseYear
    genre
    rating
    tags
    description
  }
}

لاختبار طلب البحث الجديد، انقر على الزر تنفيذ (محلي) لتنفيذ طلب البحث على قاعدة البيانات المحلية. يجب أن تظهر قائمة الأفلام من قاعدة البيانات ضمن قسم النتائج في نافذة "تنفيذ Data Connect".

ربط طلب البحث ListMovies بالشاشة الرئيسية للتطبيق

بعد اختبار طلب البحث في "محاكي Data Connect"، يمكنك استدعاء طلب البحث من داخل تطبيقك.

عند حفظ queries.gql، تنشئ أداة Firebase Data Connect الرمز البرمجي المتوافق مع طلب البحث ListMovies في الحزمة FriendlyFlixSDK.

في Xcode، افتح Movie+DataConnect.swift، وأضِف الرمز التالي لربط ListMoviesQuery.Data.Movie بـ Movie:

import FirebaseDataConnect
import FriendlyFlixSDK

extension Movie {
  init(from: ListMoviesQuery.Data.Movie) {
    id = from.id
    title = from.title
    description = from.description ?? ""
    releaseYear = from.releaseYear
    rating = from.rating
    imageUrl = from.imageUrl
  }
}

افتح ملف HomeScreen.swift وعدِّله باستخدام مقتطف الرمز التالي.

import SwiftUI
import FirebaseDataConnect
import FriendlyFlixSDK

struct HomeScreen: View {
  ...

  private var connector = DataConnect.friendlyFlixConnector
  let heroMoviesRef: QueryRefObservation<ListMoviesQuery.Data, ListMoviesQuery.Variables>

  init() {
    heroMoviesRef = connector.listMoviesQuery.ref()
  }
}

extension HomeScreen {
  ...

  private var heroMovies: [Movie] {
    heroMoviesRef.data?.movies.map(Movie.init) ?? []
  }

 private var topMovies: [Movie] {
    heroMoviesRef.data?.movies.map(Movie.init) ?? []
  }

  private var watchList: [Movie] {
    heroMoviesRef.data?.movies.map(Movie.init) ?? []
  }

  ...
}

تم إنشاء طلب البحث listMoviesQuery() بواسطة "أداة ربط البيانات" عند حفظ queries.gql. للاطّلاع على تنفيذ Swift، راجِع ملف FriendlyFlixOperations.swift في حزمة FriendlyFlixSDK.

تشغيل التطبيق

في Xcode، انقر على الزر تشغيل لتشغيل التطبيق في محاكي iOS.

بعد تشغيل التطبيق، من المفترض أن تظهر لك شاشة على النحو التالي:

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

6. عرض الصورة الرئيسية وأهم الأفلام

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

في الوقت الحالي، يسترد طلب البحث ListMovies جميع الأفلام. لتحسين العرض في هذه الأقسام، عليك الحدّ من عدد الأفلام التي يعرضها كل طلب بحث. لا يوفّر التنفيذ الحالي لطلب البحث ListMovies بعد دعمًا مدمجًا للحدّ من النتائج، لذا ستضيف في هذا القسم إمكانية الحدّ من النتائج وترتيبها.

تحسين طلب البحث ListMovies

افتح queries.gql وعدِّل ListMovies على النحو التالي لإتاحة إمكانية الترتيب والحدّ:

query ListMovies(
  $orderByRating: OrderDirection
  $orderByReleaseYear: OrderDirection
  $limit: Int
) @auth(level: PUBLIC) {
  movies(
    orderBy: [{ rating: $orderByRating }, { releaseYear: $orderByReleaseYear }]
    limit: $limit
  ) {
    id
    title
    description
    releaseYear
    rating
    imageUrl
  }
}

سيسمح لك ذلك بالحدّ من عدد الأفلام التي يعرضها طلب البحث، وترتيب مجموعة النتائج حسب التقييم وسنة الإصدار.

بعد حفظ هذا الملف، ستعيد أداة Firebase Data Connect تلقائيًا إنشاء الرمز البرمجي داخل FriendlyFlixSDK. في الخطوة التالية، يمكنك تعديل الرمز في HomeScreen.swift للاستفادة من هذه الميزات الإضافية.

استخدام طلب البحث المحسّن في واجهة المستخدم

ارجع إلى Xcode لإجراء التغييرات المطلوبة على HomeScreen.swift.

أولاً، عدِّل heroMoviesRef لجلب أحدث 3 أفلام تم إصدارها:

struct HomeScreen {
  ...

  init() {
    heroMoviesRef = connector.listMoviesQuery
      .ref { optionalVars in
        optionalVars.limit = 3
        optionalVars.orderByReleaseYear = .DESC
      }

  }
}

بعد ذلك، أعدّ مرجع استعلام آخر لأفضل الأفلام، واضبط الفلتر على الأفلام الخمسة الأعلى تقييمًا:

struct HomeScreen {
  ...

  let topMoviesRef: QueryRefObservation<ListMoviesQuery.Data, ListMoviesQuery.Variables>

  init() {
    heroMoviesRef = ...

    topMoviesRef = connector.listMoviesQuery
      .ref { optionalVars in
        optionalVars.limit = 5
        optionalVars.orderByRating = .DESC
      }
  }
}

أخيرًا، عدِّل السمة المحسوبة التي تربط نتيجة طلب البحث هذا بواجهة المستخدم:

extension HomeScreen {
  ...

  private var topMovies: [Movie] {
    topMoviesRef.data?.movies.map(Movie.init) ?? []
  }

}

أمثلة واقعية

أعِد تشغيل التطبيق مرة أخرى للاطّلاع على أحدث 3 أفلام في قسم "البطاقة الرئيسية"، والأفلام الـ 5 الأعلى تقييمًا في قسم "أهم الأفلام":

7. عرض تفاصيل الأفلام والممثلين

يمكن للمستخدم الآن تصفُّح الأفلام. عند النقر على بطاقة فيلم، ستظهر بعض التفاصيل حول الفيلم، ولكن قد تكون لاحظت أنّ التفاصيل لا تتضمّن قدرًا معيّنًا من التفاصيل.

ويرجع ذلك إلى أنّنا لم نطلب سوى التفاصيل التي نحتاج إليها لعرض قسم "الجزء الرئيسي" وقسم "أبرز الأفلام"، وهي عنوان الفيلم ووصف قصير وعنوان URL للصورة.

في صفحة تفاصيل الفيلم، نريد عرض المزيد من المعلومات حول الفيلم. في هذا القسم، ستعمل على تحسين التطبيق ليتمكّن من عرض الممثلين في الفيلم وأي مراجعات في صفحة التفاصيل.

لإجراء ذلك، عليك تنفيذ ما يلي:

  • تحسين المخطط لتوفير معلومات عن الممثلين في الأفلام والمراجعات
  • كتابة طلبات بحث في Firebase Data Connect لاسترداد تفاصيل حول فيلم معيّن
  • عرض النتائج على شاشة تفاصيل الفيلم

تحسين المخطط

في VS Code، افتح dataconnect/schema/schema.gql وأضِف تعريفات المخطط لكلّ من Actor وMovieActor.

## Actors
## An actor can participate in multiple movies; movies can have multiple actors
## Movie - Actors (or vice versa) is a many to many relationship
type Actor @table {
  id: UUID!
  imageUrl: String!
  name: String! @col(name: "name", dataType: "varchar(30)")
}

## Join table for many-to-many relationship for movies and actors
## The 'key' param signifies the primary key(s) of this table
## In this case, the keys are [movieId, actorId], the generated fields of the reference types [movie, actor]
type MovieActor @table(key: ["movie", "actor"]) {
  ## @ref creates a field in the current table (MovieActor) that holds the primary key of the referenced type
  ## In this case, @ref(fields: "id") is implied
  movie: Movie!
  ## movieId: UUID! <- this is created by the implied @ref, see: implicit.gql

  actor: Actor!
  ## actorId: UUID! <- this is created by the implied  @ref, see: implicit.gql

  role: String! ## "main" or "supporting"
}

إضافة بيانات وهمية للممثلين

بعد تعديل المخطط، يمكنك الآن ملء قاعدة البيانات بمزيد من البيانات التجريبية للاختبار.

  1. في Finder، انسخ finish/FriendlyFlix/dataconnect/moviededetails_insert.gql إلى المجلد start/FriendlyFlix/dataconnect.
  2. في VS Code، افتح dataconnect/moviededetails_insert.gql.
  3. تأكَّد من أنّ المحاكيات في إضافة Firebase Data Connect تعمل.
  4. من المفترض أن يظهر لك الزر تشغيل (محلي) في أعلى الملف. انقر على هذا الزر لإدراج بيانات الأفلام التجريبية في قاعدة البيانات.
  5. راجِع نافذة تنفيذ Data Connect للتأكّد من أنّه تمت إضافة البيانات بنجاح.

بعد توفّر البيانات، انتقِل إلى الخطوة التالية لتحديد طلب البحث الذي سيتم استخدامه لجلب تفاصيل الفيلم.

تحديد طلب البحث GetMovieById

في VS Code، افتح الملف dataconnect/connector/queries.gql وأضِف طلب البحث GetMovieById:

## Get movie by id
query GetMovieById($id: UUID!) @auth(level: PUBLIC) {
  movie(id: $id) {
    id
    title
    imageUrl
    releaseYear
    genre
    rating
    description
    tags
    metadata: movieMetadatas_on_movie {
      director
    }
    mainActors: actors_via_MovieActor(where: { role: { eq: "main" } }) {
      id
      name
      imageUrl
    }
    supportingActors: actors_via_MovieActor(
      where: { role: { eq: "supporting" } }
    ) {
      id
      name
      imageUrl
    }
  }
}

ربط طلب البحث GetMovieById بـ MovieDetailsView

في Xcode، افتح ملف MovieDetailsView.swift وعدِّل السمة المحسوبة movieDetails لتتطابق مع الرمز التالي:

import NukeUI
import SwiftUI
import FirebaseDataConnect
import FriendlyFlixSDK

@MainActor
struct MovieDetailsView: View {
  private var movie: Movie

  private var movieDetails: MovieDetails? {
    DataConnect.friendlyFlixConnector
      .getMovieByIdQuery
      .ref(id: movie.id)
      .data?.movie.map { movieDetails in
        MovieDetails(
          title: movieDetails.title,
          description: movieDetails.description ?? "",
          releaseYear: movieDetails.releaseYear,
          rating: movieDetails.rating ?? 0,
          imageUrl: movieDetails.imageUrl,
          mainActors: movieDetails.mainActors.map { mainActor in
            MovieActor(id: mainActor.id,
                       name: mainActor.name,
                       imageUrl: mainActor.imageUrl)
          },
          supportingActors: movieDetails.supportingActors.map{ supportingActor in
            MovieActor(id: supportingActor.id,
                       name: supportingActor.name,
                       imageUrl: supportingActor.imageUrl)
          },
          reviews: []
        )
      }
  }

  public init(movie: Movie) {
    self.movie = movie
  }
}

تشغيل التطبيق

في Xcode، انقر على الزر تشغيل لتشغيل التطبيق على محاكي iOS.

بعد تشغيل التطبيق، انقر على بطاقة فيلم لعرض تفاصيل الفيلم. يجب أن يظهر على النحو التالي:

8. تنفيذ مصادقة المستخدم

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

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

ربما لاحظت زرّ صورة المستخدم الرمزية في أعلى يسار الشاشة الرئيسية. سيؤدي النقر على هذا الزر إلى نقلك إلى شاشة يمكن للمستخدمين من خلالها الاشتراك أو تسجيل الدخول باستخدام عنوان بريدهم الإلكتروني وكلمة المرور.

بعد أن يسجّل المستخدم الدخول بنجاح، يجب أن يخزِّن تطبيقك تفاصيله الأساسية، وأهمها رقم تعريف المستخدم الفريد واسم المستخدم الذي اختاره.

تفعيل خدمة "مصادقة Firebase"

في "وحدة تحكّم Firebase" لمشروعك، انتقِل إلى قسم "المصادقة" وفعِّل خدمة "مصادقة Firebase". بعد ذلك، فعِّل مقدّم مصادقة البريد الإلكتروني/كلمة المرور.

في مجلد المشروع المحلي، ابحث عن firebase.json وعدِّله على النحو التالي لتفعيل محاكي Firebase Authentication.

{
  "emulators": {
    "dataconnect": {
    },
    "auth": {
    }
  },
  "dataconnect": {
    "source": "dataconnect"
  }
}

بعد ذلك، عليك إيقاف محاكي Firebase وإعادة تشغيله لتفعيل التغيير.

تنفيذ معالج مصادقة

في القسم التالي، ستنفّذ المنطق الذي يربط مصادقة المستخدم بقاعدة البيانات. يتضمّن ذلك إنشاء معالج مصادقة يستمع إلى عمليات تسجيل الدخول الناجحة.

بعد مصادقة المستخدم، سيؤدي هذا المعالج تلقائيًا إلى إنشاء حسابه المقابل في قاعدة البيانات.

في Xcode، افتح الملف AuthenticationService.swift وأضِف الرمز التالي:

import Foundation
import Observation
import os
import FirebaseAuth

enum AuthenticationState {
  case unauthenticated
  case authenticating
  case authenticated
}

@Observable
class AuthenticationService {
  private let logger = Logger(subsystem: "FriendlyFlix", category: "auth")

  var presentingAuthenticationDialog = false
  var presentingAccountDialog = false

  var authenticationState: AuthenticationState = .unauthenticated
  var user: User?
  private var authenticationListener: AuthStateDidChangeListenerHandle?

  init() {
    authenticationListener = Auth.auth().addStateDidChangeListener { auth, user in
      if let user {
        self.authenticationState = .authenticated
        self.user = user
      } else {
        self.authenticationState = .unauthenticated
      }
    }
  }

  private var onSignUp: ((User) -> Void)?
  public func onSignUp(_ action: @escaping (User) -> Void) {
    onSignUp = action
  }

  func signInWithEmailPassword(email: String, password: String) async throws {
    try await Auth.auth().signIn(withEmail: email, password: password)
    authenticationState = .authenticated
  }

  func signUpWithEmailPassword(email: String, password: String) async throws {
    try await Auth.auth().createUser(withEmail: email, password: password)

    if let onSignUp, let user = Auth.auth().currentUser {
      logger
        .debug(
          "User signed in \(user.displayName ?? "(no fullname)") with email \(user.email ?? "(no email)")"
        )
      onSignUp(user)
    }

    authenticationState = .authenticated
  }

  func signOut() throws {
    try Auth.auth().signOut()
    authenticationState = .unauthenticated
  }
}

هذا معالج مصادقة عام يتيح لك استخدام onSignUp لتسجيل إغلاق سيتم استدعاؤه عند تسجيل دخول المستخدم.

داخل هذا الإغلاق، يمكنك بعد ذلك إنشاء حساب مستخدم جديد في قاعدة البيانات. ولكن قبل أن تتمكّن من إجراء ذلك، عليك إنشاء عملية تغيير تتيح لك إنشاء مستخدمين جدد أو تعديلهم في قاعدة البيانات.

إضافة كيان "مستخدم" إلى المخطط

يحدّد النوع User كيان مستخدم. يمكن للمستخدمين التفاعل مع الأفلام من خلال ترك مراجعات أو إضافة الأفلام إلى قائمة المفضّلة.

في VS Code، افتح الملف dataconnect/schema/schema.gql وأضِف تعريف الجدول User التالي:

## Users
## A user can leave reviews for movies
## user-reviews is a one to many relationship, movie-reviews is a one to many relationship, movie:user is a many to many relationship
type User @table {
  id: String! @col(name: "user_auth")
  username: String! @col(name: "username", dataType: "varchar(50)")
}

تحديد عملية تغيير لإدراج مستخدم أو تعديله

في VS Code، افتح الملف dataconnect/connector/mutations.gql وأضِف عملية التعديل UpsertUser:

mutation UpsertUser($username: String!) @auth(level: USER) {
  user_upsert(
    data: {
      id_expr: "auth.uid"
      username: $username
    }
  )
}

إنشاء مستخدم جديد بعد تسجيل الدخول بنجاح

في Xcode، افتح FriendlyFlixApp.swift وأضِف الرمز التالي إلى أداة التهيئة:

@main
struct FriendlyFlixApp: App {

  ...

  init() {
    ...
    authenticationService = AuthenticationService()
    authenticationService?.onSignUp { user in
      let userName = String(user.email?.split(separator: "@").first ?? "(unknown)")
      Task {
        try await DataConnect.friendlyFlixConnector
          .upsertUserMutation.execute(username: userName)
      }
    }
  }

  var body: some Scene {
    ...
  }
}

يستخدم هذا الرمز upsertUserMutation Firebase Data Connect الذي تم إنشاؤه لك لإدراج مستخدم جديد (أو تعديل مستخدم حالي باستخدام المعرّف نفسه) كلما سجّل مستخدم اشتراكه بنجاح باستخدام Firebase Authentication.

أمثلة واقعية

للتأكّد من أنّ هذه الميزة تعمل، عليك أولاً الاشتراك في تطبيق iOS باتّباع الخطوات التالية:

  • إذا لم يسبق لك ذلك، أوقِف المحاكي وأعِد تشغيله للتأكّد من أنّ محاكي Firebase Authentication يعمل.
  • في Xcode، انقر على الزر تشغيل لتشغيل التطبيق على محاكي iOS.
  • انقر على رمز الأفاتار في أعلى يسار الشاشة.
  • انتقِل إلى مسار الاشتراك واشترك في التطبيق.

بعد ذلك، أرسِل طلب بحث إلى قاعدة البيانات للتأكّد من أنّ التطبيق قد أنشأ حساب مستخدم جديدًا:

  • في VS Code، افتح dataconnect/schema/schema.gql وانقر على قراءة البيانات في الكيان User.
  • سيؤدي ذلك إلى إنشاء ملف طلب بحث جديد باسم User_read.gql.
  • انقر على التشغيل محليًا للاطّلاع على جميع المستخدمين في جدول المستخدمين.
  • في لوحة "تنفيذ Data Connect"، من المفترض أن يظهر الآن حساب للمستخدم الذي سجّلت الاشتراك به للتو

9. إدارة الأفلام المفضّلة

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

تحسين المخطط لتوفير إمكانية إضافة المحتوى إلى المفضّلة

النوع FavoriteMovie هو جدول ربط يعالج علاقات متعدد إلى متعدد بين المستخدمين وأفلامهم المفضّلة. يربط كل جدول User بـ Movie.

انسخ مقتطف الرمز وألصقه في ملف dataconnect/schema/schema.gql:

type FavoriteMovie
  @table(name: "FavoriteMovies", singular: "favorite_movie", plural: "favorite_movies", key: ["user", "movie"]) {
  ## @ref is implicit
  user: User!
  movie: Movie!
}

تحديد عمليات التغيير لإضافة العناصر المفضّلة وإزالتها

قبل أن يتمكّن التطبيق من عرض الأفلام المفضّلة لدى المستخدم، عليه تحديد الأفلام التي يفضّلها. لتحقيق ذلك، عليك أولاً إضافة عمليتَي تغيير لوضع علامة على فيلم كأحد الأفلام المفضّلة لدى المستخدم، أو لإزالته من الأفلام المفضّلة مرة أخرى، على التوالي.

  1. في VS Code، افتح mutations.gql في dataconnect/connector/mutations.gql
  2. أضِف عمليات التغيير التالية للتعامل مع إضافة الأفلام إلى قائمة المفضّلة:
## Add a movie to the user's favorites list
mutation AddFavoritedMovie($movieId: UUID!) @auth(level: USER) {
  favorite_movie_upsert(data: { userId_expr: "auth.uid", movieId: $movieId })
}

## Remove a movie from the user's favorites list
mutation DeleteFavoritedMovie($movieId: UUID!) @auth(level: USER) {
  favorite_movie_delete(key: { userId_expr: "auth.uid", movieId: $movieId })
}

ربط عمليات التغيير بواجهة مستخدم تطبيقك

يمكن للمستخدمين وضع علامة على فيلم كفيلم مفضّل من خلال النقر على رمز القلب في شاشة تفاصيل الفيلم.

لربط التغييرات التي أنشأتها للتو بواجهة مستخدم التطبيق، عليك إجراء التغييرات التالية في MovieCardView:

  1. استيراد FriendlyFlixSDK وإعداد الموصّل
import NukeUI
import os
import SwiftUI
import FirebaseDataConnect
import FriendlyFlixSDK

struct MovieCardView: View {
  private let logger = Logger(subsystem: "FriendlyFlix", category: "moviecard")
  @Environment(\.dismiss) private var dismiss
  private var connector = DataConnect.friendlyFlixConnector

  ...
}
  1. نفِّذ الطريقة toggleFavourite. سيتم طلبها عندما ينقر المستخدم على رمز القلب في MovieCardView:
struct MovieCardView {

  ...

  private func toggleFavourite() {
    Task {
      if isFavourite {
        let _ = try await connector.deleteFavoritedMovieMutation.execute(movieId: movie.id)
      } else {
        let _ = try await connector.addFavoritedMovieMutation.execute(movieId: movie.id)
      }
    }
  }
}

سيؤدي ذلك إلى تعديل حالة الفيلم الحالي في قاعدة البيانات. الخطوة الأخيرة التي لم يتم تنفيذها هي التأكّد من أنّ حالة واجهة المستخدم تنعكس على النحو المطلوب.

تحديد طلب بحث لمعرفة ما إذا تم وضع علامة "مفضّل" على فيلم

  1. في VS Code، افتح queries.gql في dataconnect/connector.
  2. أضِف طلب البحث التالي لمعرفة ما إذا كان فيلم معيّن مصنّفًا كفيلم مفضّل:
query GetIfFavoritedMovie($movieId: UUID!) @auth(level: USER) {
  favorite_movie(key: { userId_expr: "auth.uid", movieId: $movieId }) {
    movieId
  }
}
  1. في Xcode، أنشئ مرجعًا لطلب البحث GetIfFavoritedMovie ونفِّذ السمة المحسوبة التي تحدّد ما إذا كان الفيلم المعروض على MovieCardView هذا مصنّفًا كفيلم مفضّل للمستخدم الحالي.
struct MovieCardView: View {

  ...

  public init(showDetails: Bool, movie: Movie) {
    self.showDetails = showDetails
    self.movie = movie

    isFavouriteRef = connector.getIfFavoritedMovieQuery.ref(movieId: movie.id)
  }

  // MARK: - Favourite handling

  private let isFavouriteRef: QueryRefObservation<
    GetIfFavoritedMovieQuery.Data,
    GetIfFavoritedMovieQuery.Variables
  >
  private var isFavourite: Bool {
    isFavouriteRef.data?.favorite_movie?.movieId != nil
  }

  ...

}
  1. عدِّل الرمز في toggleFavourite لتنفيذ طلب البحث كلما نقر المستخدم على الزر. يضمن ذلك أنّ السمة المحسوبة isFavourite تعرض دائمًا القيمة الصحيحة.
  private func toggleFavourite() {
    Task {
      if isFavourite {
        ...
      }

      let _ = try await isFavouriteRef.execute()
    }
  }

استرداد الأفلام المفضّلة

كخطوة أخيرة لهذه الميزة، عليك تنفيذ عملية استرجاع الأفلام المفضّلة لدى المستخدم لكي يتمكّن من الاطّلاع عليها في قائمة المشاهدة.

  1. في VS Code، افتح queries.gql في dataconnect/connector/queries.gql وألصِق الاستعلام التالي:
## Get favorite movies by user ID
query GetUserFavoriteMovies @auth(level: USER) {
  user(id_expr: "auth.uid") {
    favoriteMovies: favorite_movies_on_user {
      movie {
        id
        title
        genre
        imageUrl
        releaseYear
        rating
        description
      }
    }
  }
}

يتم عرض قائمة الأفلام المفضّلة لدى المستخدم على LibraryScreen. يجب ألا تعرض هذه الشاشة البيانات إلا إذا كان المستخدم مسجّلاً الدخول، لذا عليك أولاً ربط حالة المصادقة في الشاشة بميزة AuthenticationService في التطبيق.

  1. إضافة رمز إلى خريطة من FavoriteMovieFavoriteMovies إلى Movie إلى Movie+DataConnect.swift:
import FirebaseDataConnect
import FriendlyFlixSDK

extension Movie {

  ...

  init(from: GetUserFavoriteMoviesQuery.Data.User.FavoriteMovieFavoriteMovies) {
    id = from.movie.id
    title = from.movie.title
    description = from.movie.description ?? ""
    releaseYear = from.movie.releaseYear
    rating = from.movie.rating
    imageUrl = from.movie.imageUrl
  }
}
  1. في Xcode، افتح LibraryScreen، ثم عدِّل isSignedIn على النحو التالي:
struct LibraryScreen: View {
  ...

  private var isSignedIn: Bool {
    authenticationService.user != nil
  }

}
  1. بعد ذلك، استورِد Firebase Data Connect وFriendlyFlixSDK، واحصل على مرجع لطلب البحث GetUserFavoriteMovies:
import SwiftUI
import FirebaseDataConnect
import FriendlyFlixSDK

struct LibraryScreen {

 ...

  private var connector = DataConnect.friendlyFlixConnector

  ...

  init() {
    watchListRef = connector.getUserFavoriteMoviesQuery.ref()
  }

  private let watchListRef: QueryRefObservation<
    GetUserFavoriteMoviesQuery.Data,
    GetUserFavoriteMoviesQuery.Variables
  >
  private var watchList: [Movie] {
    watchListRef.data?.user?.favoriteMovies.map(Movie.init) ?? []
  }

  ...

}


  1. تأكَّد من تنفيذ طلب البحث watchListRef عند ظهور طريقة العرض:
extension LibraryScreen: View {
  var body: some View {
    ...
            MovieListSection(namespace: namespace, title: "Watch List", movies: watchList)
              .onAppear {
                Task {
                  try await watchListRef.execute()
                }
  ...

أمثلة واقعية

يمكنك الآن تشغيل التطبيق وتجربة ميزة "المفضّلة" التي نفّذتها للتو. في ما يلي بعض النقاط التي يجب أخذها في الاعتبار:

  • التأكّد من أنّ محاكي Firebase قيد التشغيل
  • تأكَّد من إضافة بيانات تجريبية للأفلام وتفاصيلها
  • التأكّد من أنّك اشتركت كمستخدم
  1. في Xcode، انقر على الزر تشغيل لتشغيل التطبيق على محاكي iOS.
  2. بعد تشغيل التطبيق، انقر على بطاقة فيلم لعرض تفاصيل الفيلم.
  3. انقر على رمز القلب لوضع علامة "مفضّلة" على الفيلم. يجب أن يصبح القلب بلون خالص.
  4. كرِّر هذه العملية لفيلمين.
  5. انتقِل إلى علامة التبويب "المكتبة". من المفترض أن تظهر الآن قائمة بجميع الأفلام التي أضفتها إلى قائمة الأفلام المفضّلة.

10. تهانينا

تهانينا، لقد نجحت في إضافة Firebase Data Connect إلى تطبيق على iOS. أنت الآن تعرف الخطوات الأساسية المطلوبة لإعداد Data Connect وإنشاء طلبات بحث وعمليات تغيير البيانات والتعامل مع مصادقة المستخدم.

اختياري: النشر في الإصدار العلني

حتى الآن، لم يستخدم هذا التطبيق سوى محاكيات Firebase. إذا أردت معرفة كيفية نشر هذا التطبيق في مشروع حقيقي على Firebase، انتقِل إلى الخطوة التالية.

11. (اختياري) نشر تطبيقك

حتى الآن، كان هذا التطبيق محليًا بالكامل، حيث يتم تضمين جميع البيانات في "مجموعة أدوات المحاكاة" من Firebase. في هذا القسم، ستتعرّف على كيفية إعداد مشروعك على Firebase لكي يعمل هذا التطبيق في مرحلة الإنتاج.

تفعيل خدمة "مصادقة Firebase"

  1. في وحدة تحكّم Firebase، انتقِل إلى قسم المصادقة وانقر على البدء.
  2. انتقِل إلى علامة التبويب طريقة تسجيل الدخول .
  3. اختَر خيار "البريد الإلكتروني/كلمة المرور" من قسم مقدّمي الخدمات الأصليين.
  4. فعِّل موفّر خدمة "البريد الإلكتروني/كلمة المرور"، ثم انقر على حفظ.

تفعيل Firebase Data Connect

ملاحظة مهمة: إذا كانت هذه هي المرة الأولى التي تنشر فيها مخططًا في مشروعك، ستؤدي هذه العملية إلى إنشاء مثيل Cloud SQL PostgreSQL، وقد يستغرق ذلك حوالي 15 دقيقة. لن تتمكّن من النشر إلى أن يصبح مثيل Cloud SQL جاهزًا ومدمجًا مع Firebase Data Connect.

1. في واجهة مستخدم إضافة Firebase Data Connect VS Code، انقر على نشر إلى مرحلة الإنتاج. 2. قد تحتاج إلى مراجعة تغييرات المخطط والموافقة على التعديلات التي قد تؤدي إلى فقدان البيانات. سيُطلب منك إجراء ما يلي: - مراجعة تغييرات المخطط باستخدام firebase dataconnect:sql:diff - تطبيق التغييرات باستخدام المسار الذي بدأته firebase dataconnect:sql:migrate عندما تكون راضيًا عن التغييرات

سيتم تعديل آلة Cloud SQL الافتراضية التي تستخدم PostgreSQL باستخدام المخطط والبيانات النهائية التي تم نشرها. يمكنك تتبُّع الحالة في وحدة تحكّم Firebase.

يمكنك الآن النقر على "تشغيل" (في بيئة الإنتاج) في لوحة Firebase Data Connect، تمامًا كما فعلت مع المحاكيات المحلية، لإضافة بيانات إلى بيئة الإنتاج.

قبل تشغيل تطبيق iOS مرة أخرى، تأكَّد من أنّه يتصل بنسخة الإصدار العلني من مشروعك:

  1. افتح قائمة المنتج (Product) > المخطّط (Scheme) > تعديل المخطّط… (Edit Scheme…‎).
  2. في قسم تشغيل، أزِل العلامة من مربّع وسيطة التشغيل -useEmulator YES.