Using Fastlane in iOS apps

Introduction

At Shaadi we have various community apps with common source code. Community apps include Marathi Shaadi, Telugu Shaadi, Bengali Shaadi, Punjabi Shaadi and other such apps.
When the code was ready, we had a big task of deploying all these community apps. There were many tasks to be performed for each app. The tasks were :

  1. Registering the app to the app store connect and developer portal
  2. Creating provisioning profiles and adding devices(for testing purposes) for each of these apps
  3. Generating builds
  4. Uploading metadata
  5. Deploying to appstore
  6. Uploading records for InApp purchase

Doing all this manually for each app would take so much time.

We needed some kind of automation for this and thats when we thought of using Fastlane for automating all of this.
Using Fastlane we wrote scripts to automate each of the tasks mentioned above. Further more, we could execute every task for all community apps with a single command. So, with a single command, we could register all the community apps to the app store connect and developer portal at once. And we could do the same for all other steps too.

How we did it :

We created environment files for each of the community app. Every community app has its own configuration settings like scheme, bundle identifier, build configuration, app name, etc. So, every environment file included its own settings. All the environment files will be placed in the same directory where the Fastfile file is. Each environment file is named as “.env.<ConfigId>”
We then created lanes for each of the tasks. And then we created the following lane (execute_for_all_envs) to specifically run any lane for all the environments.

def execute_for_all_envs
    # puts the current directory’s .env. files into an array.
    schemeList = Dir.glob(".env.*”)
    # loops through each .env file.
    schemeList.each do |file|
        # overloads ENV with the current .env file
        Dotenv.overload(file)
        # executes the block following execute_for_all_envs invocation
        yield
    end
end

Registering the app to the app store connect and developer portal :

lane :register_app do
    # using produce commands with various parameters to register the app
end

lane :register_all_apps do
    execute_for_all_envs { register_app }
end

Executing the register_all_apps lane will go through all the environment files and register all the community apps.

Creating provisioning profiles and adding devices(for testing purposes) for each of these apps :

lane :sync_device_info do
    register_devices(
    devices_file: "fastlane/Devicefile"
    )
end

lane :create_profiles do
    # using sigh commands with required parameters to generate provisioning profiles.
end

lane :add_devices_and_update_profiles_for_all_apps do
    sync_device_info
    execute_for_all_envs { create_profiles }
end

Executing the add_devices_and_update_profiles_for_all_apps will register the devices mentioned in Devicefile and recreate/regenerate the provisioning profiles for all the apps.

Generating builds and deploying to appstore :

lane :build_appstore do
    # using gym command with various parameters to generate the build
end

lane :release_appstore do
    ipaPath = build_appstore
    # using deliver command to get the build from ipaPath and deploy it to appstore; this command also uploads the metadata
end

lane :release_all_apps_appstore do
    execute_for_all_envs { release_appstore }
end

Executing release_all_apps_appstore will deploy all the apps to the appstore.

Uploading records for InApp purchase :

Fastlane does not provide any command or action to directly add In-App purchases. We can use Spaceship in our fastlane scripts to do that.
Spaceship is a Ruby library that exposes the Apple Developer Center and App Store Connect API. Link : https://spaceship.airforce/
We had many records to upload as In-App purchases for every community app. These records were available in an excel sheet. We then created a lane to read the records from excel sheet and upload them to app store connect using the Spaceship library.

lane :uploadInAppPurchases do

    require 'roo'

    # using roo library to read data from excel sheet

    fastlane_require 'spaceship'
    Spaceship::Tunes.login("<appleId>")

    # for every record, execute the following lane
        create_iap_nonrenewable(...)

end

lane :create_iap_nonrenewable do |options| # get the details from the options(parameters) . . # use spaceship to find the app and create the In-App purchase for the app fastlane_require 'spaceship' app = Spaceship::Tunes::Application.find(bundleIdentifier) begin app.in_app_purchases.create!( type: Spaceship::Tunes::IAPType::NON_RENEW_SUBSCRIPTION, versions: { "en-US" => { name: displayName, description: desc } }, reference_name: refName, product_id: productId, cleared_for_sale: true, review_notes: "", review_screenshot: screenshot, pricing_intervals: [ { country: "WW", begin_date: nil, end_date: nil, tier: tier } ] ) rescue => e puts e end end

Executing uploadInAppPurchases lane will read all records from given excel sheet and upload them to app store connect.

Conclusion :
So, Fastlane saved us a lot of time. Fastlane is simple and easy to use. It handles all the tedious tasks mentioned above and much more. Apart from tasks mentioned above, we can also capture screenshots, do code-signing, integrate to existing CI servers, etc.