XCUITest an iOS app phone call


Recently (2017-10) I talked with a an iOS developer.
In his company's app, the user is able to start a phone call.
Currently they test some of this function manually.

I decided to create an automated test.
Programmatically starting a call was easy, ending the call took more work!

XCUITest recorder

In Xcode I started the UI test recorder and manually operated the iPhone.
I started my app, made a phone call and ended it.
Unfortunately the recorded test didn't play to completion.
I edited the test script to reference "mobilephone.app", that didn't work.

I tried several alternative approaches before I found a simple solution via springboard app.

XCUITest with Springboard to tap End call button

Recently Apple added ability to XCUITest multiple apps.

Test Flow

XCUITest Code Snippet

let app = XCUIApplication()
let springboardApp = XCUIApplication(bundleIdentifier: "com.apple.springboard")


let appCallButton = app.buttons["Call Now"]
if appCallButton.waitForExistence(timeout: 10) {



    let delayBeforeEndCallSeconds: UInt32 = 12

    springboardApp.buttons["End call"].tap()

Alternative approaches to end call


A test can use CXCallObserver delegate method callObserver(_ callObserver: callChanged:)
Then the test can verify that a call hasConnected or hasEnded.
But it's only an observer, doesn't have a method to end a call.

XCUITest Code Snippet

Set test expectations and delegate.

// make expectations properties so that delegate methods can reference them
weak var expectCallHasConnected: XCTestExpectation?
weak var expectCallHasEnded: XCTestExpectation?

func testCallTapped() {

    expectCallHasConnected = self.expectation(description: "expect call hasConnected")
    expectCallHasEnded = self.expectation(description: "expect call hasEnded")

    let callObserver = CXCallObserver()
    callObserver.setDelegate(self, queue: nil)

Then the test taps buttons to start and end call.

In callback, fulfill expectations.

extension PhoneyUITests: CXCallObserverDelegate {

    func callObserver(_ callObserver: CXCallObserver, callChanged call: CXCall) {
        // This callback comes from iOS.
        // It accurately represents that a phone call connected or ended.
        if call.isOutgoing && call.hasConnected && !call.hasEnded {
            print("outgoing call hasConnected")
            if expectCallHasConnected != nil {
                // set nil to avoid error from calling multiple times
                // https://jeremywsherman.com/blog/2016/03/19/xctestexpectation-gotchas/#kaboom-calling-twice
                expectCallHasConnected = nil
        if call.isOutgoing && call.hasEnded {
            print("outgoing call hasEnded")
            if self.expectCallHasEnded != nil {
                // set nil to avoid error from calling multiple times
                expectCallHasEnded = nil


I briefly tried using CXProvider in the test to end a call.
It didn't work, maybe because the call was started by the Phoney.app, not the test.

Accessibility Switch Control

This approach is very general, could be applied to many actions.
I may prototype it later.
For more info see Using a Raspberry Pi for iPhone Switch Control

Raspberry Pi and headphone breakout switch

A user can end a phone call by clicking the headphone switch.
A program can end a phone call by closing a headphone breakout switch.
I made a prototype.
For more info see Using a Raspberry Pi to end an iPhone phone call

Potential improvements



Phoney is an iOS application to experiment with testing phone calls.

master branch

Uses XCUITest with Springboard to tap End call button.

request_web_service_end_call branch

Uses XCUITest to make an http request to an external web server, asks it to end call by closing switch.

XCUITest phone call with help from springboard


Build a universal translator (Raspberry Pi)


Speech Synthesis on the Raspberry Pi