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

إذا كنت مستخدم تحليل وتبحث عن حل Backend as Service بديل ، فقد يكون 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 Realtime

قاعدة بيانات Firebase Realtime هي قاعدة بيانات مستضافة على السحابة من NoSQL. يتم تخزين البيانات بتنسيق JSON وتتم مزامنتها في الوقت الفعلي مع كل عميل متصل.

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

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

أشياء

في التحليل ، تقوم بتخزين 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"
    }
    ...
  }
}
لمزيد من التفاصيل ، راجع دليل هيكل قاعدة البيانات الخاصة بك .

قراءة البيانات

في التحليل ، تقرأ البيانات باستخدام معرف كائن تحليل معين ، أو تنفيذ استعلامات باستخدام 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 ، ستحتاج إلى التخطيط لكيفية التعامل مع الفترة التي يحتاج خلالها تطبيقك إلى الكتابة إلى قاعدتي البيانات. اختياراتك هي:

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

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

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

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

كتابة مزدوجة

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

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

مصادقة Firebase

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

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

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

يوفر التحليل فئة مستخدم متخصصة تسمى PFUser والتي تتعامل تلقائيًا مع الوظائف المطلوبة لإدارة حساب المستخدم. PFUser هي فئة فرعية من PFObject ، مما يعني أن بيانات المستخدم متاحة في بيانات التحليل ويمكن توسيعها بحقول إضافية مثل أي 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"];
  }
}];

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

ترحيل الحسابات

لترحيل حسابات المستخدمين من التحليل إلى Firebase ، قم بتصدير قاعدة بيانات المستخدم الخاصة بك إلى ملف JSON أو CSV ، ثم قم باستيراد الملف إلى مشروع Firebase الخاص بك باستخدام الأمر auth:import في Firebase CLI.

أولاً ، قم بتصدير قاعدة بيانات المستخدم الخاصة بك من وحدة تحكم التحليل أو قاعدة البيانات ذاتية الاستضافة. على سبيل المثال ، قد يبدو ملف 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 الخاص بمستخدمي التحليل باعتباره المعرّف المحلي لمستخدمي localId . أيضًا ، يقوم base64 بتشفير قيم bcryptPassword من التحليل واستخدامها في حقل 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 كخوارزمية التجزئة:

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

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

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

Firebase Cloud Messaging

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 ، والتي توفر نفس نموذج المشترك بين الناشر. للتعامل مع الانتقال من التحليل إلى FCM ، يمكنك كتابة إصدار جديد من التطبيق يستخدم Parse SDK لإلغاء الاشتراك من قنوات التحليل و FCM SDK للاشتراك في موضوعات FCM المقابلة.

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

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

باستخدام هذه الإستراتيجية ، يمكنك إرسال رسائل إلى كل من قناة التحليل وموضوع FCM المقابل ، مما يدعم مستخدمي الإصدارين القديم والجديد. عندما يتم ترحيل عدد كافٍ من المستخدمين من الإصدار المحلل فقط للتطبيق ، يمكنك إنهاء هذا الإصدار والبدء في الإرسال باستخدام FCM فقط.

راجع مستندات موضوعات FCM لمعرفة المزيد.

تهيئة Firebase عن بُعد

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

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

لمعرفة المزيد حول Firebase Remote Config ، راجع مقدمة التكوين عن بُعد .

الاختلافات مع تكوين التحليل

باستخدام Parse config ، يمكنك إضافة أزواج مفتاح / قيمة إلى تطبيقك على Parse Config Dashboard ، ثم جلب 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;