All Articles

Open app in multiple simulators with a single command

I was interested in opening an app in multiple simulators without having to manually run it via xcode, but there seems to be no straightforward way of doing it. We are devs, we don’t want to go through the hassle of changing simulator each time to try the app in different sizes.

I implemented this using derived data. If there’s already a build for the simulator architecture, just run that build for other simulators.

Sample

The following code works with Fastlane installed:

DEFAULT_SIMULATORS = ['iPhone 5s', 'iPhone 8', 'iPhone X', 'iPhone Xʀ']
PROJECT_PATH = # Project path.
SCHEME = ... # (Your scheme)
CONFIGURATION = ... # (usually Debug or Release)
APP_ID = # Your app id. i.e: com.company.app
APP_NAME = # The output name of your app.

platform :ios do

  desc "Build the app"
  lane :build do
    simulator_sdk_path = sh("xcrun --show-sdk-path --sdk iphonesimulator").to_s.strip()
    sh("xcodebuild -configuration #{CONFIGURATION} -scheme #{SCHEME} -project #{PROJECT_PATH} -sdk #{simulator_sdk_path} build -UseModernBuildSystem=NO")
  end

  # Before running this lane, ensure that you built the app in Xcode first.
  # Example specifying simulators: fastlane run_multiple_simulators simulators:"iPhone 5s, iPhone 8"
  desc 'Run App in multiple simulators'
  lane :run_multiple_simulators do |options|
    simulators = options[:simulators].nil? ? DEFAULT_SIMULATORS : options[:simulators].split(',').map { |device| device.strip }
    derivedDataQuery = sh "xcodebuild -project #{PROJECT_PATH} -scheme #{SCHEME} -showBuildSettings | grep -m 1 \'BUILD_DIR\' | grep -oEi \"\/.*\" | tail -1"
    derived_data_path = derivedDataQuery.split.last + "/Debug-iphonesimulator/#{APP_NAME}/"

    if !File.exist?(derived_data_path)
      UI.error("#{derived_data_path} doesn\'t exist! Building app...")
      build
    end

    # Launch simulator app
    sh('open /Applications/Xcode.app/Contents/Developer/Applications/Simulator.app/')

    # First boot all simulators because it takes some seconds
    simulators.each do |simulator|
      begin
        sh "xcrun simctl list | grep Booted | grep -w \'#{simulator}\' "
      rescue
        sh "xcrun simctl boot \'#{simulator}\'"
      end
    end

    # Install and launch the app
    simulators.each do |simulator|
      sh("xcrun simctl install \'#{simulator}\' #{derived_data_path}")
      sh("xcrun simctl launch \'#{simulator}\' #{APP_ID}")
    end

  end
end

To run it with default simulators:

fastlane run_multiple_simulators

To run it with specific simulators:

fastlane run_multiple_simulators simulators:"iPhone 5s, iPhone 8"

To get a list of available simulators:

xcrun simctl list | grep com.apple.CoreSimulator

Video

Published May 1, 2019

Software Engineer, Mentor and Philosopher