قم بترحيل تطبيق Parse iOS الخاص بك إلى Firebase

إذا كنت من مستخدمي Parse وتبحث عن حل بديل للواجهة الخلفية كخدمة، فقد يكون Firebase هو الخيار الأمثل لتطبيق iOS الخاص بك.

يصف هذا الدليل كيفية دمج خدمات معينة في تطبيقك. للحصول على تعليمات إعداد Firebase الأساسية، راجع دليل إعداد iOS+ .

تحليلات كوكل

يعد Google Analytics حلاً مجانيًا لقياس التطبيقات يوفر نظرة ثاقبة حول استخدام التطبيق وتفاعل المستخدم. يتكامل برنامج Analytics عبر ميزات Firebase ويوفر لك تقارير غير محدودة لما يصل إلى 500 حدث مختلف يمكنك تحديده باستخدام Firebase SDK.

راجع مستندات Google Analytics لمعرفة المزيد.

استراتيجية الهجرة المقترحة

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

بالنسبة للأحداث المخصصة وخصائص المستخدم، يمكنك استخدام استراتيجية الكتابة المزدوجة باستخدام كل من Parse 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 Realtime هي قاعدة بيانات مستضافة على السحابة NoSQL. يتم تخزين البيانات بتنسيق JSON ومزامنتها في الوقت الفعلي لكل عميل متصل.

راجع مستندات قاعدة بيانات Firebase Realtime لمعرفة المزيد.

الاختلافات مع تحليل البيانات

أشياء

في Parse، تقوم بتخزين PFObject ، أو فئة فرعية منه، تحتوي على أزواج قيمة المفتاح من البيانات المتوافقة مع JSON. البيانات غير مخططة، مما يعني أنك لا تحتاج إلى تحديد المفاتيح الموجودة على كل PFObject .

يتم تخزين جميع بيانات قاعدة بيانات Firebase Realtime ككائنات 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، يتم التعبير عن العلاقات بشكل أفضل باستخدام هياكل البيانات المسطحة التي تقسم البيانات إلى مسارات منفصلة، ​​بحيث يمكن تنزيلها بكفاءة في مكالمات منفصلة.

فيما يلي مثال لكيفية تنظيم العلاقة بين المنشورات في تطبيق التدوين ومؤلفيها.

تحليل
// 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);
}];
لمزيد من التفاصيل حول الأنواع المتاحة من مستمعي الأحداث وكيفية ترتيب البيانات وتصفيتها، راجع دليل قراءة البيانات وكتابتها على منصات Apple .

استراتيجية الهجرة المقترحة

أعد التفكير في بياناتك

تم تحسين قاعدة بيانات Firebase Realtime لمزامنة البيانات بالمللي ثانية عبر جميع العملاء المتصلين، ويختلف هيكل البيانات الناتج عن تحليل البيانات الأساسية. وهذا يعني أن الخطوة الأولى في عملية الترحيل هي مراعاة التغييرات التي تتطلبها بياناتك، بما في ذلك:

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

ترحيل البيانات الخاصة بك

بعد أن تقرر كيفية تنظيم بياناتك في Firebase، يتعين عليك التخطيط لكيفية التعامل مع الفترة التي يحتاج تطبيقك خلالها إلى الكتابة إلى قاعدتي البيانات. اختياراتك هي:

مزامنة الخلفية

في هذا السيناريو، لديك إصداران من التطبيق: الإصدار القديم الذي يستخدم Parse والإصدار الجديد الذي يستخدم Firebase. تتم معالجة عمليات المزامنة بين قاعدتي البيانات بواسطة Parse Cloud Code (Parse to Firebase)، حيث تستمع التعليمات البرمجية الخاصة بك إلى التغييرات على Firebase ومزامنة تلك التغييرات مع Parse. قبل أن تتمكن من البدء في استخدام الإصدار الجديد، يجب عليك:

  • قم بتحويل بيانات التحليل الموجودة لديك إلى بنية Firebase الجديدة، واكتبها في قاعدة بيانات Firebase Realtime.
  • اكتب وظائف Parse Cloud Code التي تستخدم Firebase REST API للكتابة إلى تغييرات قاعدة بيانات Firebase Realtime التي تم إجراؤها في تحليل البيانات بواسطة العملاء القدامى.
  • قم بكتابة ونشر التعليمات البرمجية التي تستمع إلى التغييرات على Firebase ومزامنتها مع قاعدة بيانات Parse.

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

كتابة مزدوجة

في هذا السيناريو، تكتب إصدارًا جديدًا من التطبيق يستخدم كلاً من Firebase وParse، باستخدام Parse Cloud Code لمزامنة التغييرات التي أجراها العملاء القدامى من Parse Data إلى Firebase Realtime Database. عندما يتم ترحيل عدد كافٍ من الأشخاص من إصدار التحليل فقط للتطبيق، يمكنك إزالة رمز التحليل من إصدار الكتابة المزدوجة.

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

مصادقة Firebase

يمكن لمصادقة Firebase مصادقة المستخدمين باستخدام كلمات المرور وموفري الهوية الموحدة المشهورين مثل Google وFacebook وTwitter. كما يوفر أيضًا مكتبات واجهة المستخدم لتوفير الاستثمار الكبير المطلوب لتنفيذ تجربة مصادقة كاملة لتطبيقك والحفاظ عليها عبر جميع الأنظمة الأساسية.

راجع مستندات مصادقة Firebase لمعرفة المزيد.

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

يوفر Parse فئة مستخدم متخصصة تسمى PFUser والتي تتعامل تلقائيًا مع الوظائف المطلوبة لإدارة حساب المستخدم. PFUser هو فئة فرعية من PFObject ، مما يعني أن بيانات المستخدم متاحة في Parse Data ويمكن توسيعها بحقول إضافية مثل أي PFObject آخر.

يمتلك FIRUser مجموعة ثابتة من الخصائص الأساسية - معرف فريد وعنوان بريد إلكتروني أساسي واسم وعنوان URL للصورة - مخزنة في قاعدة بيانات مستخدم منفصلة للمشروع؛ يمكن للمستخدم تحديث هذه الخصائص. لا يمكنك إضافة خصائص أخرى إلى كائن FIRUser مباشرة؛ بدلاً من ذلك، يمكنك تخزين الخصائص الإضافية في قاعدة بيانات Firebase Realtime.

فيما يلي مثال لكيفية تسجيل مستخدم وإضافة حقل رقم هاتف إضافي.

تحليل
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، مع تحديد bcrypt كخوارزمية التجزئة:

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

ترحيل بيانات المستخدم

إذا كنت تقوم بتخزين بيانات إضافية للمستخدمين، فيمكنك ترحيلها إلى Firebase Realtime Database باستخدام الاستراتيجيات الموضحة في قسم ترحيل البيانات . إذا قمت بترحيل الحسابات باستخدام التدفق الموضح في قسم ترحيل الحسابات ، فستحتوي حسابات Firebase الخاصة بك على نفس معرفات حسابات Parse الخاصة بك، مما يسمح لك بسهولة ترحيل وإعادة إنتاج أي علاقات يتم تحديدها بواسطة معرف المستخدم.

المراسلة السحابية من Firebase

يعد Firebase Cloud Messaging (FCM) أحد حلول المراسلة عبر الأنظمة الأساسية التي تتيح لك تسليم الرسائل والإشعارات بشكل موثوق دون أي تكلفة. مؤلف الإشعارات عبارة عن خدمة مجانية مبنية على Firebase Cloud Messaging والتي تتيح إشعارات المستخدم المستهدفة لمطوري تطبيقات الأجهزة المحمولة.

راجع مستندات Firebase Cloud Messaging لمعرفة المزيد.

الاختلافات مع تحليل الإخطارات

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

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

استراتيجية الهجرة المقترحة

ترحيل الرموز المميزة للجهاز

بينما يستخدم Parse الرموز المميزة لجهاز APN لاستهداف عمليات التثبيت للإشعارات، يستخدم FCM الرموز المميزة لتسجيل FCM المعينة للرموز المميزة لجهاز APN. ما عليك سوى إضافة FCM SDK إلى تطبيق Apple الخاص بك وسيقوم بجلب رمز FCM المميز تلقائيًا .

ترحيل القنوات إلى موضوعات FCM

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

على سبيل المثال، إذا كان المستخدم الخاص بك مشتركًا في موضوع "العمالقة"، فستفعل شيئًا مثل:

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

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

يمكن أن يكون Firebase Remote Config مفيدًا جدًا أثناء عمليات الترحيل في الحالات التي تريد فيها اختبار حلول مختلفة وتكون قادرًا على تحويل المزيد من العملاء ديناميكيًا إلى موفر مختلف. على سبيل المثال، إذا كان لديك إصدار من تطبيقك يستخدم كلاً من 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، فيمكنك نشر إصدار جديد من التطبيق يستخدم كلا حزمتي SDK حتى يتم ترحيل عدد كافٍ من المستخدمين من إصدار Parse فقط.

مقارنة الكود

تحليل

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