React Made Native Easy

  Edit

iOS build scripts/setup

iOS builds via command line are much more complex as compared to Android.

Note: Builds work only for Mac users. Since Apple requires that all the builds be made on Xcode itself, iOS apps can only be built on a Mac machine.

These are the few pre-requisites to build a release ipa file.

  • BUILD_NAME - The name that will be used by testers to identify the build, for example: '1.1.1', '1.0-alpha', etc.
  • BUILD_NUMBER - A unique integer number identifying the build. This is used by iOS to identify which build is the updated build. This should be an integer number. For example: 1, 111, 111, etc.
  • IOS_APP_ID - This is the unique app identifier which is used to identify the app uniquely in the App Store or it can be used to identify if the build is dev, preprod or prod. App ids may look like this: com.app.notetaker-dev, com.app.notetaker-alpha.
  • IOS_CERTIFICATE - This is the certificate file used to sign the app.
  • IOS_CERTIFICATE_KEY - This is the password used while creating the certificate.
  • IOS_PROVISION_PROFILE - This is the provision profile needed to build the app. This file mentions the capabilities/devices that are allowed to run the app.
  • IOS_EXPORT_OPTIONS_PLIST - This is the options file needed to specify parameters for the build.
  • IOS_SCHEME - The scheme which should be used to build the IPA. Typically, we will have different schemes per environment. For example, we can have a local, a preprod and a production scheme.
  • IOS_CONFIGURATION - This is the setting which specifies if the build is DEBUG or RELEASE.
  • PROJECT_NAME - This is the name of the project. For example, if your project name inside ios folder says SampleProject.xcworkspace or SampleProject.xcodeproj, then PROJECT_NAME=SampleProject .

Release variants

Ideally, every app has three release variants just like a typical backend application:

  • Dev build - The app which connects to the staging/dev backend environment. This can also have additional libraries like TestFairy.
  • Pre-Prod build - The app which points to the preprod backend environment. This is usually very similar, if not identical to the production app.
  • Prod build - The final IPA which should be released to the App Store.

Hence, we would need three different schemes for three different variants.

A typical build process in iOS would have the following steps:

  1. Getting the certificates and provisioning profiles from the Apple developer account.
  2. Adding the certificate to the default keychain and placing the provisioning profile at the location ~/Library/MobileDevice/Provisioning\ Profiles/
  3. Archiving the project - Think of it as an executable zip of the project that can be run using Xcode.
  4. Exporting the IPA - Think of it as exporting the archive to a format recognized by an iPhone.

Let's get started

  1. Place the provisioning profile at scripts/ios/profile/XYZ.mobileprovision
  2. Place the certificate at scripts/ios/certs/ABC.p12
  3. Place an exportOptions file at scripts/ios/exportOptions/exportOptions-dev.plist Typically, an exportOptions file looks like this :

    scripts/ios/exportOptions/exportOptions-dev.plist

     <?xml version="1.0" encoding="UTF-8"?>
     <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
     <plist version="1.0">
     <dict>
         <key>compileBitcode</key>
         <false/>
         <key>method</key>
         <string>enterprise</string>
         <key>teamID</key>
         <string>ABC1234DA</string>
         <key>uploadBitcode</key>
         <true/>
         <key>uploadSymbols</key>
         <true/>
         <key>manifest</key>
         <dict>
             <key>appURL</key>
             <string>null</string>
             <key>displayImageURL</key>
             <string>null</string>
             <key>fullSizeImageURL</key>
             <string>null</string>
         </dict>
     </dict>
     </plist>
    

    Make sure you put all the above files in the gitignore.

  4. Create the script: The below script will create a new keychain ios-build and will store the certificate in the keychain. Also, it will make ios-build the default keychain so that Xcode picks up the certificate from it. Then it will copy the provisioning profile to the correct directory so that Xcode can pick it up.

    scripts/ios/keychain.sh

    #!/bin/bash
    
    set -e
    cur_dir=`dirname $0`
    
    #Check if ios-build keychain exists
    export keychainCount=`security list-keychains | grep -E 'ios-build' -c`
    
    if [ $keychainCount == 0 ] ; then
     echo "Create ios-build keychain"
     # Create a custom keychain
     security create-keychain -p "ios-build-password" ios-build.keychain
    fi
    # Add it to the list
    security list-keychains -d user -s ios-build.keychain
    
    echo "Making the ios-build keychain default, so xcodebuild will use it for signing"
    security default-keychain -s ios-build.keychain
    
    echo "Unlocking the ios-build keychain"
    security unlock-keychain -p "ios-build-password" ios-build.keychain
    
    # Set keychain timeout to 1 hour for long builds
    # see http://www.egeek.me/2013/02/23/jenkins-and-xcode-user-interaction-is-not-allowed/
    security set-keychain-settings -t 3600 -l ~/Library/Keychains/ios-build.keychain
    
    echo "Importing $IOS_CERTIFICATE to keychain"
    security import $cur_dir/certs/$IOS_CERTIFICATE -k ~/Library/Keychains/ios-build.keychain -P $IOS_CERTIFICATE_KEY -T "/usr/bin/codesign" -A
    
    #Mac OS Sierra https://stackoverflow.com/questions/39868578/security-codesign-in-sierra-keychain-ignores-access-control-settings-and-ui-p
    security set-key-partition-list -S apple-tool:,apple: -s -k "ios-build-password" ios-build.keychain
    
    # Put the provisioning profile in place
    echo "Copying $IOS_PROVISION_PROFILE in place"
    mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
    cp "$cur_dir/profile/$IOS_PROVISION_PROFILE" ~/Library/MobileDevice/Provisioning\ Profiles/
    
  5. Create the script that does the build. This script will run the xcodebuild to first archive the project. Then it will generate the IPA file which can be used for installing the app onto an iPhone.

    scripts/ios/builder.sh

    #!/bin/bash
    
    set -e
    cur_dir=`dirname $0`
    
    WORKING_DIR=`pwd`;
    cd $cur_dir/../../ios
    echo "Setting version to ${BUILD_NUMBER}, ${BUILD_NAME}"
    xcrun agvtool new-version -all ${BUILD_NUMBER}
    xcrun agvtool new-marketing-version ${BUILD_NAME}
    cd $WORKING_DIR
    
    echo "Archiving the project"
    xcodebuild clean archive PRODUCT_BUNDLE_IDENTIFIER=${IOS_APP_ID} -project $cur_dir/../../ios/${PROJECT_NAME}.xcodeproj -scheme $IOS_SCHEME -configuration $IOS_CONFIGURATION -derivedDataPath $cur_dir/../../ios/build -archivePath $cur_dir/../../ios/build/Products/${PROJECT_NAME}.xcarchive
    
    # or if you are not using xcodeproj and are using xcworkspace to build.. use the below code:
    
    # echo "Archiving the project"
    # xcodebuild clean archive PRODUCT_BUNDLE_IDENTIFIER=${IOS_APP_ID} -workspace $cur_dir/../../ios/${PROJECT_NAME}.xcworkspace -scheme $IOS_SCHEME -configuration $IOS_CONFIGURATION -derivedDataPath $cur_dir/../../ios/build -archivePath $cur_dir/../../ios/build/Products/${PROJECT_NAME}.xcarchive
    
    #SIGN
    # Issue : "No applicable devices found."
    # Fix: https://stackoverflow.com/questions/39634404/xcodebuild-exportarchive-no-applicable-devices-found
    unset GEM_HOME
    unset GEM_PATH
    
    echo "Export archive to create IPA file using $IOS_EXPORT_OPTIONS_PLIST"
    xcodebuild -exportArchive -archivePath $cur_dir/../../ios/build/Products/${PROJECT_NAME}.xcarchive -exportOptionsPlist $cur_dir/../../scripts/ios/exportOptions/$IOS_EXPORT_OPTIONS_PLIST -exportPath $cur_dir/../../ios/build/Products/IPA
    
    echo "IPA will be found at $cur_dir/../../ios/build/Products/IPA/$IOS_SCHEME.ipa"
    

Executing the scripts

  1. Run the keychain.sh to setup the certificate and provision profile like this: IOS_CERTIFICATE='ABC.p12' IOS_CERTIFICATE_KEY='PASSWORD' IOS_PROVISION_PROFILE='ABC.mobileprovision' sh scripts/ios/keychain.sh

  2. Now, run the build script to build the ipa file like this: PROJECT_NAME='NoteTaker' IOS_APP_ID='com.notetaker.app.ios' BUILD_NUMBER=11 BUILD_NAME=1.1.1 IOS_SCHEME='local' IOS_CONFIGURATION='RELEASE' IOS_EXPORT_OPTIONS_PLIST='exportOptions-dev.plist' sh ./scripts/ios/builder.sh

The build should take a couple of minutes and you can find the final ipa file at ios/build/Products/IPA/

The code till here can be found on the branch chapter/11/11.2