פיתוח באמצעות Firebase Data Connect ‏ (iOS / Swift)

1. סקירה כללית

ב-codelab הזה נסביר איך לשלב את Firebase Data Connect עם מסד נתונים של Cloud SQL כדי ליצור אפליקציה לביקורת סרטים ל-iOS באמצעות SwiftUI

תלמדו איך לחבר את האפליקציה ל-iOS למסד נתונים של Cloud SQL באמצעות Firebase Data Connect, כדי לאפשר סנכרון חלק של נתונים לביקורות על סרטים.

בסיום ה-codelab הזה, תהיה לכם אפליקציית iOS פונקציונלית שמאפשרת למשתמשים לעיין בסרטים ולסמן סרטים כמועדפים, והכול מגובה במסד נתונים של Cloud SQL באמצעות היכולות של Firebase Data Connect.

מה תלמדו

ב-Codelab הזה תלמדו איך:

  • מגדירים את Firebase Data Connect באמצעות חבילת Firebase Emulator כדי לקצר את זמני התגובה.
  • עיצוב סכימת מסד נתונים באמצעות Data Connect ו-GraphQL.
  • יצירת Swift SDK בטוח לטיפוסים מסכמת מסד הנתונים והוספה שלו לאפליקציית Swift.
  • כדי לאבטח את נתוני המשתמשים, כדאי להטמיע אימות משתמשים ולשלב אותו עם Firebase Data Connect.
  • אחזור, עדכון, מחיקה וניהול של נתונים ב-Cloud SQL באמצעות שאילתות ומוטציות שמבוססות על GraphQL.
  • (אופציונלי) מפעילים שירות Data Connect בסביבת הייצור.

דרישות מוקדמות

  • הגרסה האחרונה של Xcode
  • קוד לדוגמה של ה-codelab. אחד השלבים הראשונים ב-codelab הוא הורדה של קוד לדוגמה.

2. הגדרת פרויקט לדוגמה

יצירת פרויקט Firebase

  1. נכנסים למסוף Firebase באמצעות חשבון Google.
  2. לוחצים על הלחצן כדי ליצור פרויקט חדש, ואז מזינים שם לפרויקט (לדוגמה, Friendly Flix).
  3. לוחצים על המשך.
  4. אם מוצגת בקשה לעשות זאת, קוראים ומאשרים את התנאים של Firebase, ואז לוחצים על המשך.
  5. (אופציונלי) מפעילים את העזרה מבוססת-AI במסוף Firebase (שנקראת Gemini ב-Firebase).
  6. ב-codelab הזה לא צריך להשתמש ב-Google Analytics, ולכן משביתים את האפשרות Google Analytics.
  7. לוחצים על יצירת פרויקט, מחכים שהפרויקט יוקצה ולוחצים על המשך.

הורדת הקוד

מריצים את הפקודה הבאה כדי לשכפל את קוד לדוגמה של ה-codelab הזה. תיקייה בשם codelab-dataconnect-ios תיווצר במחשב:

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

אם אין לכם git במחשב, אתם יכולים גם להוריד את הקוד ישירות מ-GitHub.

הוספת הגדרות Firebase

‫Firebase SDK משתמש בקובץ הגדרה כדי להתחבר לפרויקט Firebase. בפלטפורמות של אפל, הקובץ הזה נקרא GoogleServices-Info.plist. בשלב הזה, תורידו את קובץ ההגדרות ותוסיפו אותו לפרויקט Xcode.

  1. במסוף Firebase, בוחרים באפשרות Project Overview (סקירת הפרויקט) בתפריט הניווט שמימין.
  2. לוחצים על הלחצן iOS+‎ כדי לבחור את הפלטפורמה. כשמוצגת בקשה להזנת מזהה החבילה של אפל, משתמשים ב-com.google.firebase.samples.FriendlyFlix
  3. לוחצים על Register app (רישום האפליקציה) ופועלים לפי ההוראות להורדת הקובץ 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

הסקריפט הזה מנסה להגדיר את סביבת הפיתוח בשבילכם ולהפעיל סביבת פיתוח משולבת (IDE) מבוססת-דפדפן. סביבת הפיתוח המשולבת הזו מספקת כלים, כולל תוסף 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. לוחצים על הלחצן Connect a Firebase project (קישור פרויקט Firebase) ובוחרים את הפרויקט שיצרתם קודם במסוף.
  3. לוחצים על הלחצן Run firebase init ופועלים לפי השלבים במסוף המשולב.

הגדרת יצירת SDK

אחרי שלוחצים על הלחצן Run firebase init, התוסף Firebase Data Connect אמור לאתחל בשבילכם את הספרייה dataconnect.

ב-VS Code, פותחים את הקובץ dataconnect/connector/connector.yaml ומוצאים את הגדרות ברירת המחדל.

כדי לוודא שהקוד שנוצר פועל עם ה-codelab הזה, צריך לעדכן את ההגדרה ולהשתמש בהגדרות הבאות. בפרט, מוודאים שהערך של connectorId מוגדר ל-friendly-flix, וחבילת Swift מוגדרת ל-FriendlyFlixSDK.

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

הסבר על ההגדרות האלה:

  • connectorId – שם ייחודי למחבר הזה.
  • outputDir – הנתיב שבו ערכת ה-SDK של מחבר הנתונים שנוצרה תאוחסן. הנתיב הזה הוא יחסי לספרייה שמכילה את הקובץ connector.yaml.
  • package – שם החבילה שישמש לחבילת Swift שנוצרה.

אחרי ששומרים את הקובץ הזה, Firebase Data Connect יוצר חבילת Swift בשם FriendlyFlixSDK וממקם אותה לצד תיקיית הפרויקט FriendlyFlix.

הפעלת האמולטורים של Firebase

ב-VS Code, עוברים אל תצוגת Firebase ולוחצים על הלחצן הפעלת אמולטורים.

הפעולה הזו תפעיל את Firebase Emulator במסוף המשולב. הפלט אמור להיראות כך:

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

הוספת החבילה שנוצרה לאפליקציית Swift

  1. פתיחת הקובץ FriendlyFlix/app/FriendlyFlix/FriendlyFlix.xcodeproj ב-Xcode
  2. בוחרים באפשרות File > Add Package Dependencies...‎ (קובץ > הוספת תלות בחבילה...)
  3. לוחצים על Add Local... (הוספה מקומית) ואז מוסיפים את חבילת FriendlyFlixSDK מהתיקייה FriendlyFlix/app.
  4. מחכים ש-Xcode יפתור את יחסי התלות של החבילה.
  5. בתיבת הדו-שיח Choose Package Products for FriendlyFlixSDK, בוחרים באפשרות FriendlyFlix בתור היעד ולוחצים על Add Package.

הגדרת האפליקציה ל-iOS לשימוש באמולטור המקומי

  1. פתיחת FriendlyFlixApp.swift. (אפשר להקיש על CMD + Shift + O כדי לפתוח את תיבת הדו-שיח Quick Open, ואז להקליד FriendlyFlixApp כדי למצוא את הקובץ במהירות)
  2. מייבאים את Firebase,‏ Firebase Auth,‏ Firebase Data Connect ואת ה-SDK שנוצר עבור הסכימה
  3. ב-initializer, מגדירים את 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.

ישויות ומערכות יחסים מרכזיות

מודל הנתונים של אפליקציית המעקב אחרי סרטים הזו מורכב מכמה ישויות שתיצרו במהלך ה-codelab הזה. קודם יוצרים את ישויות הליבה, וככל שמטמיעים יותר תכונות, מוסיפים את הישויות שנדרשות לתכונות האלה.

בשלב הזה יוצרים את הסוגים 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 type יוצר קשר של אחד לאחד עם המאפיין Movie type. הוא כולל נתונים נוספים כמו שם הבמאי של הסרט.

מוסיפים את הגדרת הטבלה 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. בצד העליון של הקובץ אמור להופיע לחצן Run (local). לוחצים על האפשרות הזו כדי להוסיף את נתוני הסרט לדוגמה למסד הנתונים.
  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, לוחצים על הלחצן Run כדי להפעיל את האפליקציה ב-iOS Simulator.

אחרי הפעלת האפליקציה, אמור להופיע מסך שנראה כך:

יכול להיות שתשימו לב שמוצגת אותה רשימה בכל האזורים באפליקציה (האזור המרכזי, הסרטים המובילים ורשימת הצפייה). הסיבה לכך היא שאתם משתמשים באותה שאילתה לכל התצוגות האלה. בקטעים הבאים תטמיעו שאילתות בהתאמה אישית.

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. בצד העליון של הקובץ אמור להופיע לחצן Run (local). לוחצים על האפשרות הזו כדי להוסיף את נתוני הסרט לדוגמה למסד הנתונים.
  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, לוחצים על הלחצן Run כדי להפעיל את האפליקציה ב-iOS Simulator.

אחרי שהאפליקציה מופעלת, מקישים על כרטיס של סרט כדי להציג את פרטי הסרט. הוא אמור להיראות כך:

8. הטמעה של אימות משתמשים

בשלב הזה, האפליקציה מציגה מידע על סרטים ושחקנים שלא מותאם אישית. בשלבים הבאים תטמיעו תכונות שמשייכות נתונים למשתמש שמחובר לחשבון שלו. תתחילו בכך שתאפשרו למשתמשים להוסיף סרטים לרשימת הצפייה האישית שלהם.

כדי להטמיע את התכונה 'רשימת מעקב', צריך קודם לקבוע את זהות המשתמש. כדי להפעיל את התכונה הזו, צריך לשלב את Firebase Authentication, כדי לאפשר למשתמשים להיכנס לאפליקציה.

יכול להיות שכבר ראיתם את לחצן האווטאר של המשתמש בפינה השמאלית העליונה של מסך הבית. הקשה על האפשרות הזו תוביל אתכם למסך שבו המשתמשים יכולים להירשם או להיכנס באמצעות כתובת האימייל והסיסמה שלהם.

אחרי שהמשתמש נכנס לחשבון, האפליקציה צריכה לשמור את הפרטים החיוניים שלו, בעיקר את מזהה המשתמש הייחודי ואת שם המשתמש שהוא בחר.

הפעלת אימות ב-Firebase

במסוף Firebase של הפרויקט, עוברים לקטע Authentication (אימות) ומפעילים את Firebase Authentication. לאחר מכן, מפעילים את ספק האימות Email/Password.

בתיקיית הפרויקט המקומי, מחפשים את firebase.json ומעדכנים אותו באופן הבא כדי להפעיל את האמולטור של Firebase Authentication.

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

אחרי זה, צריך להפסיק את פעולת Firebase Emulator ולהפעיל אותו מחדש כדי שהשינוי ייכנס לתוקף.

הטמעה של handler לאימות

בקטע הבא תטמיעו את הלוגיקה שמקשרת בין אימות המשתמש לבין מסד הנתונים. התהליך כולל יצירה של אמצעי לטיפול באימות, שמחכה לכניסות מוצלחות.

אחרי שהמשתמש עובר אימות, ה-handler הזה מפעיל באופן אוטומטי את היצירה של החשבון התואם שלו במסד הנתונים שלכם.

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

זהו רכיב handler כללי לאימות שמאפשר להשתמש ב-onSignUp כדי לרשום סגירה שתופעל כשהמשתמש יתחבר לחשבון.

בתוך הסגירה הזו, אפשר ליצור חשבון משתמש חדש במסד הנתונים. אבל לפני שתוכלו לעשות את זה, תצטרכו ליצור מוטציה שתאפשר לכם ליצור או לעדכן משתמשים חדשים במסד הנתונים.

הוספת ישות מסוג User לסכימה

הסוג 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 Emulator כדי לוודא ש-Firebase Authentication Emulator פועל.
  • ב-Xcode, לוחצים על הלחצן Run כדי להפעיל את האפליקציה ב-iOS Simulator.
  • לוחצים על סמל הדמות בפינה השמאלית העליונה של המסך.
  • עוברים לתהליך הרשמה ונרשמים לאפליקציה.

לאחר מכן, שולחים שאילתה למסד הנתונים כדי לוודא שהאפליקציה יצרה חשבון משתמש חדש בשביל המשתמש:

  • ב-VS Code, פותחים את dataconnect/schema/schema.gql ולוחצים על Read data (קריאת נתונים) בישות User.
  • ייווצר קובץ שאילתה חדש בשם User_read.gql
  • לוחצים על Run local כדי לראות את כל המשתמשים בטבלת המשתמשים.
  • בחלונית Data Connect Execution (הפעלת חיבור נתונים), אמור להופיע עכשיו חשבון של המשתמש שאיתו נרשמתם

9. ניהול הסרטים המועדפים

בקטע הזה של ה-codelab, תטמיעו אינטראקציות של משתמשים באפליקציה לביקורת סרטים, ובאופן ספציפי תאפשרו למשתמשים לנהל את הסרטים המועדפים שלהם. סרטים שמסומנים כמועדפים יופיעו בקטע 'רשימת צפייה' באפליקציה.

שיפור הסכימה כדי לתמוך במועדפים

הסוג FavoriteMovie הוא טבלת הצטרפות שמטפלת בקשרים מסוג many-to-many בין משתמשים לבין הסרטים המועדפים עליהם. בכל טבלה יש קישור בין 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. מטמיעים את ה-method‏ 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 Simulator.
  2. אחרי שהאפליקציה מופעלת, מקישים על כרטיס של סרט כדי להציג את פרטי הסרט.
  3. מקישים על סמל הלב כדי לסמן את הסרט כמועדף. הלב אמור להיות מלא.
  4. חוזרים על הפעולה הזו לגבי כמה סרטים.
  5. עוברים לכרטיסייה 'ספרייה'. עכשיו אמורה להופיע רשימה של כל הסרטים שסימנתם כמועדפים.

10. מזל טוב

הוספת בהצלחה את Firebase Data Connect לאפליקציית iOS! עכשיו אתם יודעים מהם השלבים העיקריים שנדרשים כדי להגדיר את Data Connect, ליצור שאילתות ושינויים ולטפל באימות משתמשים.

אופציונלי: פריסה בסביבת הייצור

עד עכשיו נעשה באפליקציה הזו שימוש רק ב-Firebase Emulators. אם רוצים ללמוד איך פורסים את האפליקציה הזו לפרויקט Firebase אמיתי, ממשיכים לשלב הבא.

11. (אופציונלי) פריסת האפליקציה

עד עכשיו האפליקציה הזו הייתה מקומית לחלוטין, וכל הנתונים נכללים בחבילת Firebase Emulator Suite. בקטע הזה נסביר איך להגדיר את פרויקט Firebase כדי שהאפליקציה הזו תפעל בסביבת פרודקשן.

הפעלת אימות ב-Firebase

  1. במסוף Firebase, עוברים לקטע אימות ולוחצים על תחילת העבודה.
  2. עוברים לכרטיסייה שיטת הכניסה .
  3. בקטע של ספקי הזהויות המקוריים, בוחרים באפשרות 'אימייל/סיסמה'.
  4. מפעילים את הספק Email/Password (אימייל/סיסמה) ולוחצים על Save (שמירה).

הפעלת Firebase Data Connect

חשוב: אם זו הפעם הראשונה שאתם פורסים סכימה בפרויקט, התהליך הזה ייצור מופע Cloud SQL PostgreSQL, שיכול להימשך כ-15 דקות. לא תוכלו לפרוס עד שמופע Cloud SQL יהיה מוכן וישולב עם Firebase Data Connect.

1. בממשק המשתמש של התוסף Firebase Data Connect VS Code, לוחצים על Deploy to production (פריסה לייצור). 2. יכול להיות שתצטרכו לבדוק שינויים בסכימה ולאשר שינויים שעלולים לגרום נזק. תתבקשו: – לבדוק את השינויים בסכימה באמצעות firebase dataconnect:sql:diff – כשיהיו שינויים שתרצו להחיל, תוכלו להחיל אותם באמצעות התהליך שהתחיל ב-firebase dataconnect:sql:migrate

המכונה שלכם ב-Cloud SQL ל-PostgreSQL תעודכן עם הסכימה והנתונים הסופיים שנפרסו. אפשר לעקוב אחרי הסטטוס במסוף Firebase.

עכשיו אפשר ללחוץ על 'הפעלה (ייצור)' בחלונית Firebase Data Connect, בדיוק כמו שעשיתם עם האמולטורים המקומיים, כדי להוסיף נתונים לסביבת הייצור.

לפני שמריצים שוב את אפליקציית iOS, צריך לוודא שהיא מתחברת למופע הייצור של הפרויקט:

  1. פותחים את התפריט Product (מוצר) > Scheme (סכימה) > Edit Scheme…‎ (עריכת הסכימה…).
  2. בקטע Run, מבטלים את הסימון של ארגומנט ההשקה -useEmulator YES.