How to automate the Facebook, Google and Fabric SDK setup for different environments in iOS apps

When building mobile apps, it is very common to integrate Facebook, Google or Facebook SDKs. Managing configurations for these SDKs across different environments can become tedious and error prone. Configurations like setting the app key in info.plist (or GoogleService-Info.plist) sit outside of the scope of the code and cannot be handled programatically. As a consequence, we often end up managing the files manually and … making mistakes.

At iCarAsia, we use 4 environments: stack, staging, prepared and production. Everytime we build for one of these environments we need to change the app keys and URL scheme for Fabric, Facebook and Google. In order to limit the chances of error, we decided to automate the process.
To automate the configurations of these SDK’s following changes are required as per their documentations.

To change the facebook, google and fabric SDK’s settings for different environments following things needs to be modified each time build is required.

Facebook:

Setting the FacebookAppID in the info.plist

Setting the FacebookDisplayName in the info.plist


Facebook_info


Setting the FacebookAppID with fb prefix as the URL scheme in the info.plist

 

Facebook_URL_scheme


Google:

Google has its own GoogleService-Info.plist. its TRACKING_ID needs to be replaced in that file


Google_plist


Fabric:

Setting the APIkey in info.plist under Fabric key.


Fabric


Automation:

To automate this process we are using shell scripts which can easily be integrated in the xcode build process using the Target -> Build phases -> New run script phase


Adding tun script pahse


To invoke any script just place a call to that script file in the shell script placeholder like for example for  facebook it is the folowing code snippet in our project

1
2
# Setup Facebook Kit API Keys
. ${PROJECT_DIR}/FacebookKeyAutomationScripts/Setup_Facebook_API_Keys.sh

which in the xcode project look like this


Facebook script

 

Note: Just make sure this script phase is after the “compile sources”and “copy bundle resources” phase. So that files which needs to be modified are already copied to the target bundle.


Facebook script phase order


Now turning to the actual code (script) which modifies the files,  Since the concept of writing the script is same for every SDK so lets use the example of the facebook for illustration purpose.

Scripts:

To start off we always create two shell script files


Facebooj Script files


  1. Keys file : Defining the keys for different environments
  2. Automation logic file : The script to change the keys in the build depending on the build environment.

1.    Keys file

This files contains all the keys for the different envirnoments. For us its staging, stack, preprod and production.

#!/bin/sh
# Facebook_keys.sh
# iOSConsumerApp
#
# Created by Muhammad Tanveer on 9/17/15.
# Copyright (c) 2015 iCarAsia. All rights reserved.
if [ ${TARGET_NAME} = "iOSConsumerApp" ]; then
the_facebook_production_api_key="XXXXXXXXX"
the_facebook_production_display_name="Carlist - Production"
the_facebook_production_url_scheme="fbXXXXXXXXX"
the_facebook_preprod_api_key="XXXXXXXXX"
the_facebook_preprod_display_name="Carlist - Preprod"
the_facebook_preprod_url_scheme="fbXXXXXXXXX"
the_facebook_staging_api_key="XXXXXXXXX"
the_facebook_staging_display_name="Carlist - staging"
the_facebook_staging_url_scheme="fbXXXXXXXXX"
the_facebook_stack_api_key="XXXXXXXXX"
the_facebook_stack_display_name="Carlist - Stack"
the_facebook_stack_url_scheme="fbXXXXXXXXX"
#indonesia app keys....
elif [ ${TARGET_NAME} = "iOSConsumerApp-ID" ]; then
the_facebook_production_api_key="XXXXXXXXX"
the_facebook_production_display_name="Mobil123 - Production"
the_facebook_production_url_scheme="fbXXXXXXXXX"
the_facebook_preprod_api_key="XXXXXXXXX"
the_facebook_preprod_display_name="Mobil123 - Preprod"
the_facebook_preprod_url_scheme="fbXXXXXXXXX"
the_facebook_staging_api_key="XXXXXXXXX"
the_facebook_staging_display_name="Mobil123 - Staging"
the_facebook_staging_url_scheme="fbXXXXXXXXX"
the_facebook_stack_api_key="XXXXXXXXX"
the_facebook_stack_display_name="Mobil123 - Stack"
the_facebook_stack_url_scheme="XXXXXXXXX"
fi
view rawFacebook_keys.sh hosted with  by GitHub

Another benefit of using this file is that if you have different targets than you can differentiate the keys for different targets. Like we have three different targets for three countries and we use that to differentiate the keys which will be used in the second script.
${TARGET_NAME} is one of the variable available in build settings which can be used to differentiate the targets in xcode project.  For the complete list of available build settings please refer to this apple page.

 

2.   Automation’s logic file

The second phase is to use the keys and perform the required changes according to the SDK requirements. Here is the script to do that

#!/bin/sh
# Setup_Facebook_API_Keys.sh
# iOSConsumerApp
#
# Created by Muhammad Tanveer on 9/17/15.
# Copyright (c) 2015 iCarAsia. All rights reserved.
path_to_info_plist_file="$TARGET_BUILD_DIR/${PRODUCT_NAME}.app/Info.plist"
# Import keys and secrets from a file
. ${PROJECT_DIR}/FacebookKeyAutomationScripts/Facebook_keys.sh
if [ ${CONFIGURATION} = "AppStore" ]; then
echo "Release Build Configuration - Set Facebook API Key to Production"
the_current_facebook_api_key=`/usr/libexec/PlistBuddy -c "Print :FacebookAppID" "$path_to_info_plist_file"`
echo "Current Facebook API Key from Info.plist: $the_current_facebook_api_key"
echo "Facebook Production API Key: $the_facebook_production_api_key"
if [ "$the_current_facebook_api_key" == "$the_facebook_production_api_key" ]
then
# Keys match - do not change
echo "Facebook API Keys match. Will not update"
else
# Keys do not match - will change
echo "Current Facebook API Key is not the same as new API Key, will change"
/usr/libexec/PlistBuddy -x -c "Set :FacebookAppID $the_facebook_production_api_key" "$path_to_info_plist_file"
/usr/libexec/PlistBuddy -x -c "Set :FacebookDisplayName $the_facebook_production_display_name" "$path_to_info_plist_file"
# Assuming the URl scheme for facebook is the first one in url schemes array
/usr/libexec/PlistBuddy -x -c "Set :CFBundleURLTypes:0:CFBundleURLSchemes:0 $the_facebook_production_url_scheme" "$path_to_info_plist_file"
the_updated_facebook_api_key=`/usr/libexec/PlistBuddy -c "Print :FacebookAppID" "$path_to_info_plist_file"`
the_updated_facebook_display_name=`/usr/libexec/PlistBuddy -c "Print :FacebookDisplayName" "$path_to_info_plist_file"`
the_updated_facebook_url_scheme=`/usr/libexec/PlistBuddy -c "Print CFBundleURLTypes:0:CFBundleURLSchemes:0" "$path_to_info_plist_file"`
echo "Facebook API Key set to: $the_updated_facebook_api_key"
echo "Facebook Display Name set to: $the_updated_facebook_display_name"
echo "Facebook URL Scheme set to: $the_updated_facebook_url_scheme"
fi
elif [ ${CONFIGURATION} = "Release" ]; then
echo "AdHoc Build Configuration - Set Facebook API Key to Preprod"
the_current_facebook_api_key=`/usr/libexec/PlistBuddy -c "Print :FacebookAppID" "$path_to_info_plist_file"`
echo "Current Facebook API Key from Info.plist: $the_current_facebook_api_key"
echo "Facebook Preprod API Key: $the_facebook_preprod_api_key"
if [ "$the_current_facebook_api_key" == "$the_facebook_preprod_api_key" ]
then
# Keys match - do not change
echo "Facebook API Keys match. Will not update"
else
# Keys do not match - will change
echo "Current Facebook API Key is not the same as new API Key, will change"
/usr/libexec/PlistBuddy -x -c "Set :FacebookAppID $the_facebook_preprod_api_key" "$path_to_info_plist_file"
/usr/libexec/PlistBuddy -x -c "Set :FacebookDisplayName $the_facebook_preprod_display_name" "$path_to_info_plist_file"
/usr/libexec/PlistBuddy -x -c "Set :CFBundleURLTypes:0:CFBundleURLSchemes:0 $the_facebook_preprod_url_scheme" "$path_to_info_plist_file"
the_updated_facebook_api_key=`/usr/libexec/PlistBuddy -c "Print :FacebookAppID" "$path_to_info_plist_file"`
the_updated_facebook_display_name=`/usr/libexec/PlistBuddy -c "Print :FacebookDisplayName" "$path_to_info_plist_file"`
the_updated_facebook_url_scheme=`/usr/libexec/PlistBuddy -c "Print CFBundleURLTypes:0:CFBundleURLSchemes:0" "$path_to_info_plist_file"`
echo "Facebook API Key set to: $the_updated_facebook_api_key"
echo "Facebook Display Name set to: $the_updated_facebook_display_name"
echo "Facebook URL Scheme set to: $the_updated_facebook_url_scheme"
fi
elif [ ${CONFIGURATION} = "Debug" ]; then
echo "Debug Build Configuration - Set Facebook API Key to Development"
the_current_facebook_api_key=`/usr/libexec/PlistBuddy -c "Print :FacebookAppID" "$path_to_info_plist_file"`
echo "Current Facebook API Key from Info.plist: $the_current_facebook_api_key"
echo "Facebook Development API Key: $the_facebook_staging_api_key"
if [ "$the_current_facebook_api_key" == "$the_facebook_staging_api_key" ]
then
# Keys match - do not change
echo "Facebook API Keys match. Will not update"
else
# Keys do not match - will change
echo "Current Facebook API Key is not the same as new API Key, will change"
/usr/libexec/PlistBuddy -x -c "Set :FacebookAppID $the_facebook_staging_api_key" "$path_to_info_plist_file"
/usr/libexec/PlistBuddy -x -c "Set :FacebookDisplayName $the_facebook_staging_display_name" "$path_to_info_plist_file"
/usr/libexec/PlistBuddy -x -c "Set :CFBundleURLTypes:0:CFBundleURLSchemes:0 $the_facebook_staging_url_scheme" "$path_to_info_plist_file"
the_updated_facebook_api_key=`/usr/libexec/PlistBuddy -c "Print :FacebookAppID" "$path_to_info_plist_file"`
the_updated_facebook_display_name=`/usr/libexec/PlistBuddy -c "Print :FacebookDisplayName" "$path_to_info_plist_file"`
the_updated_facebook_url_scheme=`/usr/libexec/PlistBuddy -c "Print CFBundleURLTypes:0:CFBundleURLSchemes:0" "$path_to_info_plist_file"`
echo "Facebook API Key set to: $the_updated_facebook_api_key"
echo "Facebook Display Name set to: $the_updated_facebook_display_name"
echo "Facebook URL Scheme set to: $the_updated_facebook_url_scheme"
fi
fi

First we define the path to the info.plist of the target at line 9 . Then import the keys files defined in step 1 to this scope so that we can use it.

Next we see if the current configuration for the project is “AppStore”. If it is indeed the AppStore build than we get he current key using the helpful utility plistbuddywhich is available on mac to modify and access the plist files.

We than compare the current key with the facebook production key. If the keys matches than there is no need to change anything as the last configuration scheme for the build process was same as the current one as keys are already set properly otherwise we have to update the FacebookAppID, FacebookDisplayName and URL scheme values which is done from line 30-33.

Note:

Important thing to mention is this case URL scheme is the first element in the URL schemes array. If your URL scheme for is at second or any other position use that position like “:positionIndex:” insetead of “:0:” in the script.

After that to confirm that the values are changed correctly we get the latest values from the plist and print it to console.

You can confirm the values from the Report Navigator (cmd +8) of the xcode project.


Script Output

 

The script below line 67 is same as the explained for with different keys.

Same kind of scripts can be written for fabric and google. For you reference the links of these scripts for our app are added at the bottom.

There is one extra step for fabric  that is to upload the dsym build to the fabric which can be added as another build phase to run another script. The script for that is

#!/bin/sh
# Run_Crashlytics.sh
# iOSConsumerApp
#
# Created by Muhammad Tanveer on 9/17/15.
# Copyright (c) 2015 iCarAsia. All rights reserved.
# Import keys and secrets from a file
. ${PROJECT_DIR}/Crashlytics_build_phase_run_scripts/Crashlytics_keys.sh
if [ ${CONFIGURATION} = "AppStore" ]; then
echo "Running Crashlytics for this build"
echo "Will upload to production Organization"
"${PODS_ROOT}/Fabric/Fabric.framework/run" $the_crashlytics_production_api_key $the_crashlytics_production_build_secret
elif [ ${CONFIGURATION} = "Release" ]; then
echo "Running Crashlytics for this build"
echo "Will upload to development Organization"
"${PODS_ROOT}/Fabric/Fabric.framework/run" $the_crashlytics_development_api_key $the_crashlytics_development_build_secret
elif [ ${CONFIGURATION} = "Debug" ]; then
echo "Not Running Crashlytics for this build"
else
echo "Not Running Crashlytics for this build"
fi
view rawRun_Crashlytics.sh hosted with  by GitHub

Which just invokes the fabric run command as they have described in their docs. We just don’t run it for the development build as we don’t want to upload the DSYM file for crashes during the development. But for QA and production builds it get uploaded automatically.

The gists of other scripts can be found at the following links.

Facebook_keys.sh , Setup_Facebook_API_Keys.sh

GoogleAnalytics_keys.shSetup_GoogleAnalytics_API_Keys.sh

Crashlytics_keys.shSetup_Crashlytics_API_Keys.sh

Conclusion:

Automating the key change process for different environments and countries had a great effect on our workflow. We can now work with confidence, knowing the right keys will be used for each environment. It also saved us significant time on the tedious and error prone task of configuring SDKs.

We will now work on automating the build distribution process for QA and Apple. So that, for instance when QA needs a specific build for a specific country, the correct app is built and sent with one command.