העבר את אפליקציית Parse iOS שלך ל-Firebase

אם אתה משתמש Parse שמחפש פתרון אחורי כשירות חלופי, Firebase עשוי להיות הבחירה האידיאלית עבור אפליקציית iOS שלך.

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

גוגל ניתוח נתונים

Google Analytics הוא פתרון למדידת אפליקציה בחינם המספק תובנות לגבי השימוש באפליקציה ומעורבות המשתמשים. Analytics משתלב בין תכונות Firebase ומספק לך דיווח בלתי מוגבל עבור עד 500 אירועים שונים שתוכל להגדיר באמצעות Firebase SDK.

עיין במסמכים של Google Analytics למידע נוסף.

אסטרטגיית הגירה מוצעת

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

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

השוואת קוד

נתח אנליטיקה

// Start collecting data
[PFAnalytics trackAppOpenedWithLaunchOptions:launchOptions];

NSDictionary *dimensions = @{
  // Define ranges to bucket data points into meaningful segments
  @"priceRange": @"1000-1500",
  // Did the user filter the query?
  @"source": @"craigslist",
  // Do searches happen more often on weekdays or weekends?
  @"dayType": @"weekday"
};
// Send the dimensions to Parse along with the 'search' event
[PFAnalytics trackEvent:@"search" dimensions:dimensions];

גוגל ניתוח נתונים

// Obtain the AppMeasurement instance and start collecting data
[FIRApp configure];

// Send the event with your params
[FIRAnalytics logEventWithName:@"search" parameters:@{
  // Define ranges to bucket data points into meaningful segments
  @"priceRange": @"1000-1500",
  // Did the user filter the query?
  @"source": @"craigslist",
  // Do searches happen more often on weekdays or weekends?
  @"dayType": @"weekday"
}];

מסד נתונים בזמן אמת של Firebase

מסד הנתונים בזמן אמת של Firebase הוא מסד נתונים המתארח בענן NoSQL. הנתונים מאוחסנים כ-JSON ומסונכרנים בזמן אמת לכל לקוח מחובר.

עיין במסמכים של Firebase Realtime Database למידע נוסף.

הבדלים עם נתוני ניתוח

חפצים

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

כל נתוני Firebase Realtime Database מאוחסנים כאובייקטי JSON, ואין מקבילה עבור PFObject ; אתה פשוט כותב לעץ ה-JSON ערכי סוגים התואמים לסוגי ה-JSON הזמינים.

להלן דוגמה כיצד תוכל לשמור את הציונים הגבוהים למשחק.

לְנַתֵחַ
PFObject *gameScore = [PFObject objectWithClassName:@"GameScore"];
gameScore[@"score"] = @1337;
gameScore[@"playerName"] = @"Sean Plott";
gameScore[@"cheatMode"] = @NO;
[gameScore saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
  if (succeeded) {
    // The object has been saved.
  } else {
    // There was a problem, check error.description
  }
}];
Firebase
// Create a reference to the database
FIRDatabaseReference *ref = [[FIRDatabase database] reference];
NSString *key = [[ref child:@"scores"] childByAutoId].key;
NSDictionary *score = @{@"score": @1337,
                        @"playerName": @"Sean Plott",
                        @"cheatMode": @NO};
[key setValue:score withCompletionBlock:^(NSError *error,  FIRDatabaseReference *ref) {
  if (error) {
    // The object has been saved.
  } else {
    // There was a problem, check error.description
  }
}];
לפרטים נוספים, עיין במדריך קריאה וכתיבה של נתונים על פלטפורמות אפל .

יחסים בין נתונים

PFObject יכול להיות קשר עם PFObject אחר: כל אובייקט יכול להשתמש באובייקטים אחרים כערכים.

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

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

לְנַתֵחַ
// Create the author
PFObject *myAuthor = [PFObject objectWithClassName:@"Author"];
myAuthor[@"name"] = @"Grace Hopper";
myAuthor[@"birthDate"] = @"December 9, 1906";
myAuthor[@"nickname"] = @"Amazing Grace";

// Create the post
PFObject *myPost = [PFObject objectWithClassName:@"Post"];
myPost[@"title"] = @"Announcing COBOL, a New Programming Language";

// Add a relation between the Post and the Author
myPost[@"parent"] = myAuthor;

// This will save both myAuthor and myPost
[myPost saveInBackground];
Firebase
// Create a reference to the database
FIRDatabaseReference *ref = [[FIRDatabase database] reference];

// Create the author
NSString *myAuthorKey = @"ghopper";
NSDictionary *author = @{@"name": @"Grace Hopper",
                         @"birthDate": @"December 9, 1906",
                         @"nickname": @"Amazing Grace"};
// Save the author
[[ref child:myAuthorKey] setValue:author]

// Create and save the post
NSString *key = [[ref child:@"posts"] childByAutoId].key;
NSDictionary *post = @{@"author": myAuthorKey,
                       @"title": @"Announcing COBOL, a New Programming Language"};
[key setValue:post]

פריסת הנתונים הבאה היא התוצאה.

{
  // Info about the authors
  "authors": {
    "ghopper": {
      "name": "Grace Hopper",
      "date_of_birth": "December 9, 1906",
      "nickname": "Amazing Grace"
    },
    ...
  },
  // Info about the posts: the "author" fields contains the key for the author
  "posts": {
    "-JRHTHaIs-jNPLXOQivY": {
      "author": "ghopper",
      "title": "Announcing COBOL, a New Programming Language"
    }
    ...
  }
}
לפרטים נוספים, עיין במדריך מבנה את מסד הנתונים שלך .

קריאת נתונים

ב-Parse אתה קורא נתונים באמצעות המזהה של אובייקט Parse ספציפי, או ביצוע שאילתות באמצעות PFQuery .

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

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

לְנַתֵחַ
PFQuery *query = [PFQuery queryWithClassName:@"GameScore"];
[query whereKey:@"playerName" equalTo:@"Dan Stemkoski"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
  if (!error) {
    for (PFObject *score in objects) {
      NSString *gameScore = score[@"score"];
      NSLog(@"Retrieved: %@", gameScore);
    }
  } else {
    // Log details of the failure
    NSLog(@"Error: %@ %@", error, [error userInfo]);
  }
}];
Firebase
// Create a reference to the database
FIRDatabaseReference *ref = [[FIRDatabase database] reference];

// This type of listener is not one time, and you need to cancel it to stop
// receiving updates.
[[[[ref child:@"scores"] queryOrderedByChild:@"playerName"] queryEqualToValue:@"Dan Stemkoski"]
    observeEventType:FIRDataEventTypeChildAdded withBlock:^(FIRDataSnapshot *snapshot) {
  // This will fire for each matching child node.
  NSDictionary *score = snapshot.value;
  NSString gameScore = score[@"score"];
  NSLog(@"Retrieved: %@", gameScore);
}];
לפרטים נוספים על סוגים זמינים של מאזינים לאירועים ועל אופן ההזמנה והסינון של נתונים, עיין במדריך קריאה וכתיבה של נתונים על פלטפורמות אפל .

אסטרטגיית הגירה מוצעת

שקול מחדש את הנתונים שלך

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

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

העבר את הנתונים שלך

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

סנכרון ברקע

בתרחיש זה, יש לך שתי גרסאות של האפליקציה: הגרסה הישנה המשתמשת ב-Parse וגרסה חדשה המשתמשת ב-Firebase. סנכרון בין שני מסדי הנתונים מטופל על ידי Parse Cloud Code (Parse to Firebase), כאשר הקוד שלך מאזין לשינויים ב-Firebase ומסנכרן את השינויים הללו עם Parse. לפני שתוכל להתחיל להשתמש בגרסה החדשה, עליך:

  • המר את נתוני הניתוח הקיימים שלך למבנה Firebase החדש, וכתוב אותם אל Firebase Realtime Database.
  • כתוב פונקציות Parse Cloud Code המשתמשות ב-Firebase REST API כדי לכתוב ל-Firebase Realtime Database שינויים שבוצעו ב-Perse Data על ידי לקוחות ישנים.
  • כתוב ופריסה קוד שמאזין לשינויים ב-Firebase ומסנכרן אותם למסד הנתונים של Parse.

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

כתיבה כפולה

בתרחיש זה, אתה כותב גרסה חדשה של האפליקציה שמשתמשת גם ב-Firebase וגם ב-Parse, תוך שימוש ב-Parse Cloud Code כדי לסנכרן שינויים שבוצעו על ידי לקוחות ישנים מ-Perse Data ל-Firebase Realtime Database. כאשר מספיק אנשים עברו מגרסת ה-Parse בלבד של האפליקציה, תוכלו להסיר את קוד ה-Parse מגרסת הכתיבה הכפולה.

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

אימות Firebase

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

עיין במסמכי האימות של Firebase למידע נוסף.

הבדלים עם ניתוח אישור

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

ל- FIRUser יש קבוצה קבועה של מאפיינים בסיסיים - מזהה ייחודי, כתובת דוא"ל ראשית, שם וכתובת URL של תמונה - המאוחסנת במסד הנתונים של המשתמשים של פרויקט נפרד; מאפיינים אלה יכולים להיות מעודכנים על ידי המשתמש. לא ניתן להוסיף מאפיינים אחרים לאובייקט FIRUser ישירות; במקום זאת, תוכל לאחסן את המאפיינים הנוספים במסד הנתונים שלך בזמן אמת של Firebase.

להלן דוגמה כיצד תוכל לרשום משתמש ולהוסיף שדה מספר טלפון נוסף.

לְנַתֵחַ
PFUser *user = [PFUser user];
user.username = @"my name";
user.password = @"my pass";
user.email = @"email@example.com";

// other fields can be set just like with PFObject
user[@"phone"] = @"415-392-0202";

[user signUpInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
  if (!error) {
    // Hooray! Let them use the app now.
  } else {
    // Something went wrong
    NSString *errorString = [error userInfo][@"error"];
  }
}];
Firebase
[[FIRAuth auth] createUserWithEmail:@"email@example.com"
                           password:@"my pass"
                         completion:^(FIRUser *_Nullable user, NSError *_Nullable error) {
  if (!error) {
    FIRDatabaseReference *ref = [[FIRDatabase database] reference];
    [[[[ref child:@"users"] child:user.uid] child:@"phone"] setValue:@"415-392-0202"
  } else {
    // Something went wrong
    NSString *errorString = [error userInfo][@"error"];
  }
}];

אסטרטגיית הגירה מוצעת

העבר חשבונות

כדי להעביר חשבונות משתמש מ-Parse ל-Firebase, ייצא את מסד הנתונים של המשתמשים שלך לקובץ JSON או CSV, ולאחר מכן ייבא את הקובץ לפרויקט Firebase שלך ​​באמצעות הפקודה auth:import של Firebase CLI.

ראשית, ייצא את מסד הנתונים של המשתמש שלך ממסוף Parse או מסד הנתונים המתארח בעצמך. לדוגמה, קובץ JSON שיוצא ממסוף Parse עשוי להיראות כך:

{ // Username/password user
  "bcryptPassword": "$2a$10$OBp2hxB7TaYZgKyTiY48luawlTuYAU6BqzxJfpHoJMdZmjaF4HFh6",
  "email": "user@example.com",
  "username": "testuser",
  "objectId": "abcde1234",
  ...
},
{ // Facebook user
  "authData": {
    "facebook": {
      "access_token": "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
      "expiration_date": "2017-01-02T03:04:05.006Z",
      "id": "1000000000"
    }
  },
  "username": "wXyZ987654321StUv",
  "objectId": "fghij5678",
  ...
}

לאחר מכן, הפוך את הקובץ המיוצא לפורמט הנדרש על ידי Firebase CLI. השתמש ב- objectId של משתמשי Parse שלך ​​בתור ה- localId של משתמשי Firebase שלך. כמו כן, base64 מקודד את ערכי bcryptPassword מ- Parse והשתמש בהם בשדה passwordHash . לדוגמה:

{
  "users": [
    {
      "localId": "abcde1234",  // Parse objectId
      "email": "user@example.com",
      "displayName": "testuser",
      "passwordHash": "JDJhJDEwJE9CcDJoeEI3VGFZWmdLeVRpWTQ4bHVhd2xUdVlBVTZCcXp4SmZwSG9KTWRabWphRjRIRmg2",
    },
    {
      "localId": "fghij5678",  // Parse objectId
      "displayName": "wXyZ987654321StUv",
      "providerUserInfo": [
        {
          "providerId": "facebook.com",
          "rawId": "1000000000",  // Facebook ID
        }
      ]
    }
  ]
}

לבסוף, ייבא את הקובץ שעבר טרנספורמציה עם Firebase CLI, תוך ציון bcrypt בתור אלגוריתם ה-hash:

firebase auth:import account_file.json --hash-algo=BCRYPT

העבר נתוני משתמש

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

Firebase Cloud Messaging

Firebase Cloud Messaging (FCM) הוא פתרון העברת הודעות חוצה פלטפורמות המאפשר לך להעביר באופן אמין הודעות והתראות ללא עלות. ה-Notifications composer הוא שירות ללא עלות הבנוי על Firebase Cloud Messaging המאפשר הודעות משתמש ממוקדות למפתחי אפליקציות לנייד.

עיין במסמכי Firebase Cloud Messaging למידע נוסף.

הבדלים עם ניתוח הודעות דחיפה

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

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

אסטרטגיית הגירה מוצעת

העברת אסימוני מכשיר

בעוד ש-Parse משתמש באסימוני מכשירי APN כדי למקד להתקנות להתראות, FCM משתמש באסימוני רישום של FCM הממופים לאסימוני מכשירי APN. פשוט הוסף את FCM SDK לאפליקציית Apple שלך ​​והיא תביא אסימון FCM באופן אוטומטי .

העברת ערוצים לנושאי FCM

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

לדוגמה, אם המשתמש שלך רשום לנושא "ענקים", תעשה משהו כמו:

PFInstallation *currentInstallation = [PFInstallation currentInstallation];
[currentInstallation removeObject:@"Giants" forKey:@"channels"];
[currentInstallation saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
  if (succedeed) {
    [[FIRMessaging messaging] subscribeToTopic:@"/topics/Giants"];
  } else {
    // Something went wrong unsubscribing
  }
}];

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

עיין במסמכים בנושאי FCM למידע נוסף.

תצורה מרחוק של Firebase

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

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

למידע נוסף על Firebase Remote Config, עיין במבוא של Remote Config .

הבדלים עם ניתוח תצורת

עם Parse config אתה יכול להוסיף צמדי מפתח/ערך לאפליקציה שלך ב-Parse Config Dashboard, ולאחר מכן להביא את PFConfig בלקוח. כל מופע PFConfig שאתה מקבל הוא תמיד בלתי ניתן לשינוי. כאשר אתה מאחזר PFConfig חדש בעתיד מהרשת, הוא לא ישנה אף מופע PFConfig קיים, אלא יצור מופע חדש ויהפוך אותו לזמין באמצעות currentConfig .

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

אסטרטגיית הגירה מוצעת

אתה יכול לעבור ל-Firebase Remote Config על-ידי העתקת צמדי המפתח/ערך של תצורת ה-Parse שלך ​​למסוף Firebase, ולאחר מכן פריסת גרסה חדשה של האפליקציה המשתמשת ב-Firebase Remote Config.

אם ברצונך להתנסות גם ב-Parse Config וגם ב-Firebase Remote Config, תוכל לפרוס גרסה חדשה של האפליקציה שמשתמשת בשני ה-SDKs עד שדי משתמשים יעברו מהגרסת Parse only.

השוואת קוד

לְנַתֵחַ

[PFConfig getConfigInBackgroundWithBlock:^(PFConfig *config, NSError *error) {
  if (!error) {
    NSLog(@"Yay! Config was fetched from the server.");
  } else {
    NSLog(@"Failed to fetch. Using Cached Config.");
    config = [PFConfig currentConfig];
  }

  NSString *welcomeMessage = config[@"welcomeMessage"];
  if (!welcomeMessage) {
    NSLog(@"Falling back to default message.");
    welcomeMessage = @"Welcome!";
  }
}];

Firebase

FIRRemoteConfig remoteConfig = [FIRRemoteConfig remoteConfig];
// Set defaults from a plist file
[remoteConfig setDefaultsFromPlistFileName:@"RemoteConfigDefaults"];

[remoteConfig fetchWithCompletionHandler:^(FIRRemoteConfigFetchStatus status, NSError *error) {
  if (status == FIRRemoteConfigFetchStatusSuccess) {
    NSLog(@"Yay! Config was fetched from the server.");
    // Once the config is successfully fetched it must be activated before newly fetched
    // values are returned.
    [self.remoteConfig activateFetched];
  } else {
    NSLog(@"Failed to fetch. Using last fetched or default.");
  }
}];

// ...

// When this is called, the value of the latest fetched and activated config is returned;
// if there's none, the default value is returned.
NSString welcomeMessage = remoteConfig[@"welcomeMessage"].stringValue;