# iOS native app automation testing

{% hint style="info" %}
Appfigurate doesn't currently support automation testing watchOS, Flutter or React Native apps.
{% endhint %}

Appfigurate can change the configuration of an iOS app being automation tested using `XCTestCase`.

## Examples

An example UI testing bundle `AppfigurateExampleUITests` is available in both [Objective-C example](/getting-started/examples.md#objective-c-example) and [Swift example](/getting-started/examples.md#swift-example) GitHub repositories. To test, ensure `AppfigurateExample (ObjC/Swift iOS)` is selected as the scheme, then long tap the run button to show more options and tap `Test` (⌘U).

<img src="/files/vmjBATBxJq73nMFF8CfA" alt="" data-size="original">

## Testing your app

### Add APLConfiguration subclass to UI testing bundle

Tap on your [<mark style="color:blue;">`APLConfiguration`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html) subclass in the project navigator. In File inspector ‣ Target membership, tick on your UI testing bundle.

![](/files/QUvP0jLVa6Jr494ZlZC3)

### Ensure allowInvalidSignatures returns YES/true

In your [<mark style="color:blue;">`APLConfiguration`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html) subclass, confirm that your [<mark style="color:blue;">`allowInvalidSignatures`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html#/c:objc\(cs\)APLConfiguration\(im\)allowInvalidSignatures) method returns `YES`/`true` when running automation tests. (Test schemes are by default run with a DEBUG build).

{% tabs %}
{% tab title="Swift" %}

```swift
@objcMembers class ExampleConfiguration: APLConfiguration {
...
  override func allowInvalidSignatures() -> Bool {
    return !ENCRYPTED()
  }
...
```

{% endtab %}

{% tab title="Objective-C" %}

```objectivec
@implementation ExampleConfiguration
...
- (BOOL) allowInvalidSignatures {
#if DEBUG
    return YES;
#else
    return NO;
#endif
}
...
```

{% endtab %}
{% endtabs %}

### Add an UIInterruptionMonitor

When the configuration is applied to the application on launch, the standard Appfigurate 'Configuration applied' alert is displayed. It is recommended you add an `UIInterruptionMonitor` to your `XCTestCase` to automatically dismiss this alert. A good place to do this in the `setUp` method.

{% tabs %}
{% tab title="Swift" %}

```swift
addUIInterruptionMonitor(withDescription: "Appfigurate") { (element) -> Bool in
  if (element.elementType == .alert) {
    if (element.buttons["OK"].exists) {
      element.buttons["OK"].tap()
      return true
    } else if (element.buttons["Ignore"].exists) {
      element.buttons["Ignore"].tap()
      return true
    }
  }
  return false
}
```

{% endtab %}

{% tab title="Objective-C" %}

```objectivec
[self addUIInterruptionMonitorWithDescription: @"Appfigurate" handler: ^(XCUIElement *element) {
    if (element.elementType == XCUIElementTypeAlert) {
        if ([element.buttons[@"OK"] exists]) {
            [element.buttons[@"OK"] tap];
            return YES;
        } else if ([element.buttons[@"Ignore"] exists]) {
            [element.buttons[@"Ignore"] tap];
            return YES;
        }
    }
    return NO;
}];
```

{% endtab %}
{% endtabs %}

### Create instance of APLConfiguration subclass

* Get an instance of your [<mark style="color:blue;">`APLConfiguration`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html) subclass in your `XCTestCase`. A good place to do this in the `setUp` method.
* Set the properties required to be applied to your app to allow it to be tested correctly.

{% tabs %}
{% tab title="Swift" %}

```swift
let c = APLConfiguration.shared() as! ExampleConfiguration
c.boolean = false
c.string_Textfield = "thursday"
```

{% endtab %}

{% tab title="Objective-C" %}

<pre class="language-objectivec"><code class="lang-objectivec"><strong>ExampleConfiguration* c = (ExampleConfiguration*) [APLConfiguration sharedConfiguration];
</strong><strong>c.boolean = NO;
</strong><strong>c.string_Textfield = @"thursday";
</strong></code></pre>

{% endtab %}
{% endtabs %}

### Apply configuration to XCUIApplication

* Apply the result of [<mark style="color:blue;">`APLConfiguration`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html) [<mark style="color:blue;">`automationLaunchArguments`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html#/c:objc\(cs\)APLConfiguration\(im\)automationLaunchArguments) method to `XCUIApplication launchArguments` property.
* Launch your app.

{% tabs %}
{% tab title="Swift" %}

```swift
let app = XCUIApplication()
app.launchArguments = c.automationLaunchArguments()
app.launch()
```

{% endtab %}

{% tab title="Objective-C" %}

```objectivec
XCUIApplication* app = [XCUIApplication new];
app.launchArguments = [c automationLaunchArguments];
[app launch];
```

{% endtab %}
{% endtabs %}

Once your app is launched, the configuration you set in [Create instance of APLConfiguration subclass ](#create-instance-of-aplconfiguration-subclass)will be applied before executing each test case.

### Additional automation launch methods

See also [<mark style="color:blue;">`automationLaunchArgumentsReset`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html#/c:objc\(cs\)APLConfiguration\(im\)automationLaunchArgumentsReset) and [<mark style="color:blue;">`automationLaunchArgumentsWithAction`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html#/c:objc\(cs\)APLConfiguration\(im\)automationLaunchArgumentsWithAction) methods of [<mark style="color:blue;">`APLConfiguration`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html).

## Applying configuration at runtime to the app under test

You can apply configuration to the app after it has launched using the [<mark style="color:blue;">`automationSendConfiguration`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html#/c:objc\(cs\)APLConfiguration\(im\)automationSendConfiguration), [<mark style="color:blue;">`automationSendConfigurationReset`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html#/c:objc\(cs\)APLConfiguration\(im\)automationSendConfigurationReset) and [<mark style="color:blue;">`automationSendConfigurationWithAction`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html#/c:objc\(cs\)APLConfiguration\(im\)automationSendConfigurationWithAction:) methods of [<mark style="color:blue;">`APLConfiguration`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html). You can read the configuration from the app under test using the [<mark style="color:blue;">`automationSendReadConfiguration`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html#/c:objc\(cs\)APLConfiguration\(im\)automationSendReadConfiguration) method.

{% tabs %}
{% tab title="Swift" %}

```swift
func testSendConfig() {
  app.launchArguments = c.automationLaunchArgumentsReset()
  app.launch()
  c.boolean = true
  c.automationSendConfiguration()
  ...
```

{% endtab %}

{% tab title="Objective-C" %}

```objectivec
- (void)testSendConfig {
    app.launchArguments = [c automationLaunchArgumentsReset];
    [app launch];
    c.boolean = YES;
    [c automationSendConfiguration];
    ...
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
Under the hood, the [<mark style="color:blue;">`automationSendConfiguration`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html#/c:objc\(cs\)APLConfiguration\(im\)automationSendConfiguration), [<mark style="color:blue;">`automationSendConfigurationReset`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html#/c:objc\(cs\)APLConfiguration\(im\)automationSendConfigurationReset), [<mark style="color:blue;">`automationSendConfigurationWithAction`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html#/c:objc\(cs\)APLConfiguration\(im\)automationSendConfigurationWithAction:) and [<mark style="color:blue;">`automationSendReadConfiguration`</mark>](https://www.electricbolt.co.nz/api/Classes/APLConfiguration.html#/c:objc\(cs\)APLConfiguration\(im\)automationSendReadConfiguration) methods use the [<mark style="color:blue;">`APLAutomationSendMessage`</mark>](https://www.electricbolt.co.nz/api/Functions.html#/c:@F@APLAutomationSendMessage) and [<mark style="color:blue;">`APLAutomationMessageReceivedBlock`</mark>](https://www.electricbolt.co.nz/api/Functions.html#/c:@F@APLAutomationMessageReceivedBlock) functions introduced in the [next section](#invoking-functionality-in-the-app-under-test-at-runtime).
{% endhint %}

## Invoking functionality in the app under test at runtime

Appfigurate allows you to invoke functionality in the app under test, at runtime, from your `XCTestCase`. Example use cases:

* Setting mocked HTTP responses for HTTP requests in the app.
* Share mocked objects between the `XCTestCase` and app.
* Read and set the internal state of the app.

### Instrument the app under test

Use the [<mark style="color:blue;">`APLAutomationMessageReceivedBlock`</mark>](https://www.electricbolt.co.nz/api/Functions.html#/c:@F@APLAutomationMessageReceivedBlock) function in the app under test to declare a callback. The callback will be invoked anytime a message is received from the `XCTestCase`. Best practice is to wrap the function in `#if DEBUG` to ensure it's not included in an App Store build.

{% tabs %}
{% tab title="Swift" %}
App under test example

```swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
  APLApplicationDidFinishLaunchingWithOptions(launchOptions)
        
#if DEBUG
  APLAutomationMessageReceivedBlock { message, plist in
    let window = UIApplication.shared.windows.filter {$0.isKeyWindow}.first!
            
    if (message == "SetDarkMode") {
      let bool = plist as! Bool
      window.overrideUserInterfaceStyle = bool ? .dark : .light
    } else if message == "GetDarkMode" {
      let bool = window.traitCollection.userInterfaceStyle == .dark
      return bool
    }
    return nil
  }
#endif

  return true
}
```

{% endtab %}

{% tab title="Objective-C" %}
App under test example

```objectivec
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    APLApplicationDidFinishLaunchingWithOptions(launchOptions);
    
#if DEBUG
    APLAutomationMessageReceivedBlock(^id _Nullable(NSString * _Nonnull message, id  _Nullable plist) {
        UIWindow* window;
        for (window in [UIApplication sharedApplication].windows) {
            if ([window isKeyWindow]) {
                break;
            }
        }
            
        if ([message isEqualToString: @"SetDarkMode"]) {
            window.overrideUserInterfaceStyle = [plist boolValue] ? UIUserInterfaceStyleDark : UIUserInterfaceStyleLight;
        } else if ([message isEqualToString: @"GetDarkMode"]) {
            return [NSNumber numberWithBool: window.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark];
        }
        return nil;
    });
#endif
    return YES;
}
```

{% endtab %}
{% endtabs %}

### Invoke functionality from your XCTestCase

Use the [<mark style="color:blue;">`APLAutomationSendMessage`</mark>](https://www.electricbolt.co.nz/api/Functions.html#/c:@F@APLAutomationSendMessage) function in your `XCTestCase` to send a message and wait for a reply from the app under test.

{% tabs %}
{% tab title="Swift" %}
XCTestCase example

<pre class="language-swift"><code class="lang-swift"><strong>func testSendMessageToApplicationUnderTest() {
</strong>  app.launchArguments = c.automationLaunchArgumentsReset()
  app.launch()
  APLAutomationSendMessage("SetDarkMode", true, 3.0)
  XCTAssertTrue(APLAutomationSendMessage("GetDarkMode", nil, 3.0) as! Bool)
}
</code></pre>

{% endtab %}

{% tab title="Objective-C" %}
XCTestCase example

```objectivec
- (void)testSendMessageToApplicationUnderTest {
    app.launchArguments = [config automationLaunchArgumentsReset];
    [app launch];
    APLAutomationSendMessage(@"SetDarkMode", @YES, 3.0);
    XCTAssertTrue([APLAutomationSendMessage(@"GetDarkMode", nil, 3.0) boolValue]);
}
```

{% endtab %}
{% endtabs %}

{% hint style="success" %}
The [<mark style="color:blue;">`APLAutomationSendMessage`</mark>](https://www.electricbolt.co.nz/api/Functions.html#/c:@F@APLAutomationSendMessage) and [<mark style="color:blue;">`APLAutomationMessageReceivedBlock`</mark>](https://www.electricbolt.co.nz/api/Functions.html#/c:@F@APLAutomationMessageReceivedBlock) functions are compatible with both iOS Simulators and physical iOS devices.
{% endhint %}

{% hint style="info" %}
The [<mark style="color:blue;">`APLAutomationSendMessage`</mark>](https://www.electricbolt.co.nz/api/Functions.html#/c:@F@APLAutomationSendMessage) `plist` parameter accepts any object that is [property list compatible](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/PropertyLists/Introduction/Introduction.html#//apple_ref/doc/uid/10000048i), or `nil`. This also applies to the result returned from [<mark style="color:blue;">`APLAutomationMessageReceivedBlock`</mark>](https://www.electricbolt.co.nz/api/Functions.html#/c:@F@APLAutomationMessageReceivedBlock). The size of the property list once serialized, must be less than 65535 bytes, otherwise an [<mark style="color:blue;">`AppfigurateLibraryException`</mark>](https://www.electricbolt.co.nz/api/Constants.html#/c:@AppfigurateLibraryException) will be thrown from the `XCTestCase`. In practice, keep your property lists to just a few hundred bytes. Larger property lists will result in significant transmission time (tens of seconds).
{% endhint %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.electricbolt.co.nz/automation-testing/ios-native-app-automation-testing.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
