Resetting the Xcode Simulator for UI tests
Tags: xcode, simulator, swift, tdd
Published: 2020-01-14 21:50:32

---
title: Resetting the Xcode Simulator for UI tests
published: true
description: a method to reset the Xcode simulator between runs
tags: xcode, simulator, swift, tdd
---

One of the major issues we encounter with running UI tests for an iOS project, is the simulator's inability to completely clear its slate between runs.

## The problem we're trying to solve

Say, you're implementing a login functionality and, like a good citizen, you're storing the credentials in the keychain. Now, for unit and integration tests this won't pose a problem, as it can be easily mocked. But for UI tests, the app is tested as a self-enclosed entity, from outside, without access to internals. And the simulator doesn't clear the keychain between tests.

Almost as bad as Xcode running tests in alphabetical order! 😱

## The Solution — app launch arguments

When starting an app as part of the UI tests, one can pass in launch arguments:

```swift
func testExample() {
  let app = XCUIApplication()
  app.launchArguments = ["--Reset"]
  app.launch()
}
```

To understand how to use these launch arguments, we're going to have a look at how `UIApplicationMain` works first.

## UIApplicationMain

If you've come to Swift from other programming languages, or even if you did iOS development with Objective-C before, you might have noticed that there is no `main.swift` file that is used as the entry point — like the main.m in Objective-C. There is however a `@UIApplicationMain` attribute in the `AppDelegate.swift` file, that serves the same purpose. Moreover, it's actually possible to also use a "main" entry file.

To be able to observe the new launch parameter, " --Reset", we'll need to change things a bit so we can react to it.

Let's delete `@UIApplicationMain` and create a `main.swift` file, besides `AppDelegate.swift`, with the following content:

```swift
import Foundation
import UIKit

_ = autoreleasepool {
  _ = UIApplicationMain( CommandLine.argc,
      UnsafeMutableRawPointer(CommandLine.unsafeArgv).bindMemory(to:
          UnsafeMutablePointer?.self, capacity: Int(CommandLine.argc)),
      nil, NSStringFromClass(AppDelegate.self)
  )
}
```

This simply calls `UIApplicationMain` which is the entry point to create the application object and the application delegate and set up the event cycle.

This now gives us the opportunity to perform tasks before launching the app; eg: calling a method to reset the keychain:

```
_ = autoreleasepool {
  if ProcessInfo().arguments.contains("--Reset") {
    AppReset.resetKeychain()
  }
  _ = UIApplicationMain( CommandLine.argc,
      UnsafeMutableRawPointer(CommandLine.unsafeArgv).bindMemory(to:
          UnsafeMutablePointer?.self, capacity: Int(CommandLine.argc)),
      nil, NSStringFromClass(AppDelegate.self)
  )
}
```

## The AppReset

To reset the keychain I've created a simple `resetKeychain` method, enclosed into an enum, simply for namespacing:

```
enum AppReset {
  static func resetKeychain() {
      let secClasses = [
          kSecClassGenericPassword as String,
          kSecClassInternetPassword as String,
          kSecClassCertificate as String,
          kSecClassKey as String,
          kSecClassIdentity as String
      ]
      for secClass in secClasses {
          let query = [kSecClass as String: secClass]
          SecItemDelete(query as CFDictionary)
      }
  }
}
```

## All together now

![main.swift](https://thepracticaldev.s3.amazonaws.com/i/40r6f03ptgfqa1xj3okd.png)


Here is all the code in main.swift :

```swift
import Foundation
import UIKit

enum AppReset {
  static func resetKeychain() {
      let secClasses = [
          kSecClassGenericPassword as String,
          kSecClassInternetPassword as String,
          kSecClassCertificate as String,
          kSecClassKey as String,
          kSecClassIdentity as String
      ]
      for secClass in secClasses {
          let query = [kSecClass as String: secClass]
          SecItemDelete(query as CFDictionary)
      }
  }
}

_ = autoreleasepool {
  if ProcessInfo().arguments.contains("--Reset") {
      AppReset.resetKeychain()
  }
  _ = UIApplicationMain(
      CommandLine.argc,
      UnsafeMutableRawPointer(CommandLine.unsafeArgv).bindMemory(to:
          UnsafeMutablePointer?.self, capacity: Int(CommandLine.argc)),
      nil,
      NSStringFromClass(AppDelegate.self)
  )
}
```

## One more thing

When using a reset mechanism for UI tests, I tend to create helper methods to launch the app with different arguments:

```swift
class SomeUITests: XCTestCase {
      
  override func setUp() {
      super.setUp()
      continueAfterFailure = false
  }
  
  func launch() {
      XCUIApplication().launch()
  }
  
  func launchWithReset() {
      let app = XCUIApplication()
      app.launchArguments = ["--Reset"]
      app.launch()
  }
  
  func testExample() {
      launchWithReset()
  }
  
}
```

That's it!


# Happy Testing! 🤓