Google is committed to advancing racial equity for Black communities. See how.

Run a Game Loop test

This guide describes how to prepare a Game Loop test for testing in Firebase Test Lab.

About Game Loop tests

It can be hard to automate game testing when gaming apps are built on different UI frameworks. Game Loop tests allow you to integrate your native tests with Test Lab and easily run them on devices you select. A Game Loop test runs your test through your gaming app while simulating the actions of a real player. This guide shows you how to run a Game Loop test, then view and manage your test results in the Firebase console.

Step 1: Register Test Lab’s custom URL scheme

  1. In Xcode, select a project target.

  2. Click the Info tab, then add a new URL type.

  3. In the URL Schemes field, enter firebase-game-loop. You can also register the custom URL scheme by adding it to your project’s Info.plist configuration file anywhere within the <dict> tag:


Your app is now configured to run a test using Test Lab.

Step 2: Optionally configure your app

Run multiple loops

If you plan to run multiple loops (aka scenarios) in your test, you must specify which loops you want to run in your app at launch time.

In your app delegate, override the application(_:open:options:) method:


func application(_app: UIApplication,
               open url: URL
               options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
  let components = URLComponents(url: url, resolvingAgainstBaseURL: true)!
  if components.scheme == "firebase-game-loop" {
      // ...Enter Game Loop Test logic to override application(_:open:options:).
  return true


  • (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary <UIApplicationOpenURLOptionsKey, id> *)options { if ([url.scheme isEqualToString:(@"firebase-game-loop")]) { // ...Enter Game Loop Test logic to override application(_:open:options:). } }

When you run multiple loops in your test, the current loop is passed as a parameter to the URL used to launch the app. You can also obtain the current loop number by parsing the URLComponents object used to fetch the custom URL scheme:


if components.scheme == "firebase-game-loop" {
  // Iterate over all parameters and find the one with the key "scenario".
  let scenarioNum = Int(components.queryItems!.first(where: { $ == "scenario" })!.value!)!
  // ...Write logic specific to the current loop (scenarioNum).


if ([url.scheme isEqualToString:(@"firebase-game-loop")]) {
  // Launch the app as part of a game loop.
  NSURLComponents *components = [NSURLComponents componentsWithURL:url
  for (NSURLQueryItem *item in [components queryItems]) {
      if ([ isEqualToString:@"scenario"]) {
          NSInteger scenarioNum = [item.value integerValue];
          // ...Write logic specific to the current loop (scenarioNum).

End a test early

By default, a Game Loop test continues running until it reaches a timeout of five minutes, even when all the loops have been executed. When the timeout is reached, the test ends and cancels any pending loops. You can speed up your test or end it early by calling Test Lab’s custom URL scheme firebase-game-loop-complete in your app’s AppDelegate. For example:


/// End the loop by calling our custom url scheme.
func finishLoop() {
 let url = URL(string: "firebase-game-loop-complete://")!


  • (void)finishLoop { UIApplication *app = [UIApplication sharedApplication]; [app openURL:[NSURL URLWithString:@"firebase-game-loop-complete://"] options:@{} completionHandler:^(BOOL success) {}]; }

Your Game Loop test terminates the current loop and executes the next loop. When there are no more loops to run, the test ends.

Write custom test results

You can configure your Game Loop test to write custom test results to your device’s file system. This way, when the test starts running, Test Lab stores the result files in a GameLoopsResults directory on your testing device (which you must create yourself). When the test ends, Test Lab moves all files from the GameLoopResults directory to your project's bucket. Keep the following in mind when setting up your test:

  • All result files are uploaded regardless of file type, size, or quantity.

  • Test Lab doesn’t process your test results until all the loops in your test have finished running, so if your test includes multiple loops that write output, make sure you append them to a unique result file or create a result file for each loop. This way, you can avoid overwriting results from a previous loop.

To set up your test to write custom test results:

  1. In your app's Documents directory, create a directory named GameLoopResults.

  2. From anywhere in your app’s code (e.g., your app delegate), add the following:


    /// Write to a results file.
    func writeResults() {
    let text = "Greetings from game loops!"
    let fileName = "results.txt"
    let fileManager = FileManager.default
    do {
    let docs = try fileManager.url(for: .documentDirectory,
                                   in: .userDomainMask,
                                   appropriateFor: nil,
                                   create: true)
    let resultsDir = docs.appendingPathComponent("GameLoopResults")
    try fileManager.createDirectory(
        at: resultsDir,
        withIntermediateDirectories: true,
        attributes: nil)
    let fileURL = resultsDir.appendingPathComponent(fileName)
    try text.write(to: fileURL, atomically: false, encoding: .utf8)
    } catch {
      // ...Handle error writing to file.


    /// Write to a results file.

    • (void)writeResults:(NSString *)message { // Locate and create the results directory (if it doesn't exist already). NSFileManager manager = [NSFileManager defaultManager]; NSURL url = [[manager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject]; NSURL* resultsDir = [url URLByAppendingPathComponent:@"GameLoopResults" isDirectory:YES]; [manager createDirectoryAtURL:resultsDir withIntermediateDirectories:NO attributes:nil error:nil];

      // Write the result message to a text file. NSURL* resultFile = [resultsDir URLByAppendingPathComponent:@"result.txt"]; if ([manager fileExistsAtPath:[resultFile path]]) { // Append to the existing file NSFileHandle *handle = [NSFileHandle fileHandleForWritingToURL:resultFile error:nil]; [handle seekToEndOfFile]; [handle writeData:[message dataUsingEncoding:NSUTF8StringEncoding]]; [handle closeFile]; } else { // Create and write to the file. [message writeToURL:resultFile atomically:NO encoding:NSUTF8StringEncoding error:nil]; } }