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

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

لمحة عن هذا الدرس التطبيقي حول الترميز

subjectتاريخ التعديل الأخير: أبريل 7, 2025
account_circleتأليف: Peter Friese

1. نظرة عامة

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

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

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

ما ستتعرّف عليه

سيعلّمك هذا الدليل التعليمي كيفية:

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

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

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

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

إنشاء مشروع على Firebase

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

تنزيل الرمز

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

git clone https://github.com/peterfriese/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

بعد النقر على الزر Run firebase init (تشغيل Firebase init)، من المفترض أن تهيئ إضافة 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 لفتح مربّع الحوار الفتح السريع، ثم كتابة 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" من القائمة المنسدلة الوجهة.
  2. اضغط على CMD+R (أو انقر على الزر Run) في Xcode لتشغيل التطبيق على المحاكي.

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

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

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

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

في هذه الخطوة، عليك إنشاء النوعَين 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. راجِع المحطة الطرفية تنفيذ "ربط البيانات" للتأكّد من إضافة البيانات بنجاح.

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

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
  }
}

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

ربط طلب البحث 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، انقر على الزر Run (تشغيل) لتشغيل التطبيق في "محاكي 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
      }

  }
}

بعد ذلك، يمكنك إعداد مرجع استعلام آخر لأبرز الأفلام، وضبط الفلتر على أهم 5 أفلام حسب التقييم:

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. راجِع محطة تنفيذ "ربط البيانات" للتأكّد من إضافة البيانات بنجاح.

بعد إضافة البيانات، انتقِل إلى الخطوة التالية لتحديد طلب البحث من أجل جلب تفاصيل الفيلم.

تحديد طلب البحث 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، انقر على الزر Run (تشغيل) لتشغيل التطبيق على "محاكي iOS".

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

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

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

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

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

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

تفعيل ميزة "مصادقة 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" وأعِد تشغيله للتأكّد من تشغيل "محاكي مصادقة Firebase".
  • في Xcode، انقر على الزر Run (تشغيل) لتشغيل التطبيق على "محاكي iOS".
  • انقر على رمز الصورة الرمزية في أعلى يسار الشاشة.
  • انتقِل إلى مسار الاشتراك وانشئ حسابًا في التطبيق.

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

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

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، انقر على الزر Run (تشغيل) لتشغيل التطبيق على "محاكي 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

ملاحظة مهمة: إذا كانت هذه هي المرة الأولى التي يتم فيها نشر مخطّط في مشروعك، ستؤدي هذه العملية إلى إنشاء مثيل PostgreSQL على Cloud SQL، ما قد يستغرق 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 وسيطة التشغيل.