Harness the Full Power of Custom Xcode Shell Scripts

Pre-made scripts are cool, but writing your own Xcode shell scrips opens up a world of possibilities for making developers’ lives easier. We demonstrate this on one potential use case – checking the SSL pinning certificate.

If you’re an iOS developer, chances are you use some sort of a bash command on our Xcode projects on a daily basis, even if you’re unaware of it. If you are using Cocoapods, just check the Build Phases of your project. You will find the Run Script Phase named Check Pods Manifest.lock

If you are using Universal Frameworks in your project, you will also have some sort of a Strip unused architecture script. These pre-made scripts are cool, but Xcode shell scripts can make our lives easier in many different ways. So let’s dive in and see how we can utilize them.

Reduce the hassle with custom Xcode shell scripts

Writing our own Xcode shell scripts opens up many possibilities. We’ll illustrate this on a use case we’ve chosen as our example: checking the SSL pinning certificate validity date.

Use case: SSL Pinning certificate check

We often want to make our apps more secure, especially those that involve sensitive user data (i.e., mobile banking applications). One of the most common ways of doing that is by using SSL pinning. As it says in the blog post,

[when] pinning public keys from certificates, you can either pin the certificates themselves (in which case you do a byte-per-byte data comparison) or just compare the public keys in the certificates.

For the sake of this example, let’s say we are using a certificate check rather than a public key. Considering that certificates usually have an expiration time of two years, you’ll have to track the validity date so your app doesn’t suddenly stop working. 

The expiration date is easy to miss. It’s a common scenario that everyone goes crazy a couple of days before the expiration because a new certificate is issued, and the app has to include it. 

In this case, there is little time to create an app update and review the testing required for the certificate update. What’s more, that kind of test could result in a couple of hours of downtime for the users as their app is still not ready for the new certificate. This is bad UX and can bring about negative comments from the community. So, let’s see how a custom script can help us out here.

Certificate validity check script

To check the certificate’s validity, we will use two main components in our script:

  • openssl — used for converting our certificate from .der into the .pem and checking date validity;
  • osascript — UNIX-based command for running AppleScripts.

In your project, go to the Build Phases and add the New Run Script Phase named Check Certificates Validity.

Screenshot showing a window that selects New Run Script Phase in our example for creating an Xcode shell script for SSL Pinning certificate validity check.

You can find the entire script in this code snippet

Let’s dive in:

	# 1 Create cache file for storing last shown date
...
PROJECT_CACHE=~/.cache/$(basename -- $(find . -name *.xcworkspace)).txt

# 2 Variables
CERTIFICATES="${PROJECT_DIR}/MyProject/SupportingFiles/Certificates/"
REMIND_IN=2 # 2 months
LAST_CHECK=$(head -n 1 $PROJECT_CACHE)
...

# 3 Check validity of each certificate
for cert in $(find "$CERTIFICATES" -name '*.der')
do 
# 4 Convert to .pem for validity date check
...
openssl x509 -inform der -in $cert -out $TEMP_CERT 
# 5 Check validity
if openssl x509 -checkend $REMINDER_TIME -noout -in $TEMP_CERT; then
echo "Certificate $CERT_NAME is still valid"
else
EXPIRY_DATE=($(openssl x509 -enddate -noout -in $TEMP_CERT|cut -d= -f 2))
# Create message
fi
done

# 6 Alert user

CERTIFICATE_WARNING="The following certificates expire in $REMIND_IN..."
OSASCRIPT_MESSAGE=$(printf 'tell app "Xcode" to display dialog "%s" with title "Certificates validity" with icon caution buttons {"OK"} default button "OK"' "$CERTIFICATE_WARNING")
osascript -e "$OSASCRIPT_MESSAGE"

Let’s take it step by step:

1

This script will be triggered every time we build the project, so we will store the date of the last check in the cache and show the alert only on the first app build of the day, as long as we have an expiring certificate in the project.

2

We should define some global variables, such as a path to the certificates, a temporary directory for converted certificates, reminder time, etc.

3

After the temporary folder is created, we will iterate through the certificates in our project.

4

Every certificate should be converted from binary .der format to the .cer format since we cannot read the date from binary data.

5

Check if the date comes after the 2-month period. If it does, it’s all good, but if the certificate’s expiration date falls within this time period, its name will be appended to the list of certificates that require updating.

6

If there are some certificates with validity warnings, and this is the first time in the day when we should be alerted, we will assemble the command for osascript. Since the AppleScript format is intended to be human-readable for easy use, our command will basically be the following:

	tell app "Xcode" to 
    display dialog "%s" with title "Certificates validity" 
    with icon caution 
    buttons {"OK"} 
    default button "OK"

7

Run AppleScript, save today’s date in the cache and remove the temporarily created certificates from the project.

End result

AppleScript is executed by a custom Xcode shell script. Our build will be paused as long as this alert stays active. Once the alert is dismissed, the build will continue, so you can’t miss it.

AppleScript is executed by a custom Xcode shell script. The alert shows Certificates validity.

Get creative with Xcode shell scripts

As you can see, a few simple lines of code can make our projects as predictive and as secure as we want our apps to be.

This was just one example of the power of Xcode shell scripts. Combining them, you could create much more automatizations in your projects than you think. For example, you could create universal frameworks, strip unused architectures, use one main .plist file that is re-written with different data depending on the scheme you are running, make #TODO show as a warning, etc. 

With a little creativity, you can get Xcode to make your life and coding much easier.