העברת ניתוח אפליקציית iOS ל-Firebase

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

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

Google Analytics

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

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

הצעה לאסטרטגיית העברה

שימוש בספקים שונים של ניתוח נתונים הוא תרחיש נפוץ שניתן ליישם בקלות Google Analytics פשוט מוסיפים אותו לאפליקציה כדי ליהנות מאירועים וממאפייני משתמשים 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];

Google Analytics

// 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 Realtime Database

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

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

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

Objects

בקטע 'ניתוח' אתם מאחסנים 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
  }
}];
פרטים נוספים זמינים במדריך קריאה וכתיבה של נתונים בפלטפורמות של Apple.

הקשרים בין נתונים

ל-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, אפשר לאחזר נתונים על ידי צירוף אוזן אסינכרוני להפניה של מסד נתונים. ה-listener מופעל פעם אחת למצב הראשוני של ��נתונים ופעם נוספת כשהנתונים משתנים, כך שלא צריך להוסיף קוד כדי לקבוע אם הנתונים השתנו.

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

ניתוח
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);
}];
פרטים נוספים על הסוגים הזמינים של פונקציות event listener ואיך לסדר ולסנן נתונים, קריאה וכתיבה של נתונים בפלטפורמות של Apple מותאמת אישית.

הצעה לאסטרטגיית העברה

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

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

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

העברת הנתונים

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

סנכרון ברקע

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

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

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

כתיבה כפולה

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

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

Firebase Authentication

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

מידע נוסף זמין במסמכי העזרה של Firebase Authentication.

הבדלים ביחס לאימות של Parse

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

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

הדוגמה הבאה היא של איך לרשום משתמש ולהוסיף עוד שדה של מספר טלפון.

ניתוח
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 ב-CLI של Firebase הפקודה.

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

{ // 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 ערכים מ-Anse ולהשתמש בהם בפונקציה 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
        }
      ]
    }
  ]
}

בשלב האחרון, מייבאים את הקובץ שהומר באמצעות ה-CLI של Firebase, ומציינים bcrypt בתור אלגוריתם הגיבוב:

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

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

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

Firebase Cloud Messaging

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

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

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

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

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

הצעה לאסטרטגיית העברה

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

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

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

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

לדוגמה, אם המשתמש רשום לנושא 'Giants', צריך לבצע פעולה כמו:

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

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

לצפייה FCM מסמכי נושאים למידע נוסף.

Firebase Remote Config

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

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

מידע נוסף על Firebase Remote Config זמין ב הקדמה אחת (Remote Config).

הבדלים ביחס ל-Parse Config

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

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

הצעה לאסטרטגיית העברה

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

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

השוואה בין קודים

ניתוח

[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;