Handling UI Interruptions in iOS UI Tests with XCTest

When you are writing UI tests for an iOS it is likely that you will encounter cases where your test needs to interact with an alert presented by the system.

XCTest provides API to handle these cases with addUIInterruptionMonitor(withDescription:handler:) and the corresponding method to remove this monitor on XCTestCase.

If your app does some setup to explain why a permission is required your UI flow may be as follows:

  1. Present a view controller explaining the need for the permission being requested.
  2. The user taps a button to proceed with requesting the permission.
  3. You request authorization from the relevant API, causing the permission prompt alert to be shown by the system.

To test this with a UI test you might do the following:

  1. Navigate to the relevant screen.
  2. Add the UI interruption monitor to handle the expected alert.
  3. Tap the button to grant permission.
  4. Expect the next screen depending on the outcome of the permission grant.

You will likely not reach step 4 with your test initially.

To illustrate why this happens it may be helpful to study the transcript of a passing test case test case:

Test Case '-[SimpleUITestUITests.SimpleUITestUITests testGrantingPermissions]' started.
    t =     0.00s Start Test at 2019-04-25 07:33:34.747
    t =     0.06s Set Up
    t =     0.07s     Open sean.systems.SimpleUITest
    t =     0.10s         Launch sean.systems.SimpleUITest
    t =     1.53s             Wait for accessibility to load
    t =     2.65s             Wait for sean.systems.SimpleUITest to idle
    t =     3.98s Tap "Grant Permission" Button
    t =     3.98s     Wait for sean.systems.SimpleUITest to idle
    t =     4.01s     Find the "Grant Permission" Button
    t =     4.05s         Check for interrupting elements affecting "Grant Permission" Button
    t =     4.05s     Synthesize event
    t =     4.15s     Wait for sean.systems.SimpleUITest to idle
    t =     4.62s Tap Target Application 'sean.systems.SimpleUITest'
    t =     4.62s     Wait for sean.systems.SimpleUITest to idle
    t =     4.65s     Find the Target Application 'sean.systems.SimpleUITest'
    t =     4.67s         Check for interrupting elements affecting "SimpleUITest" Application
    t =     4.67s             Wait for com.apple.springboard to idle
    t =     4.70s             Snapshot accessibility hierarchy for app with pid 3409
    t =     4.75s             Find: Descendants matching type Alert
    t =     4.77s         Handle interrupting element
    t =     4.77s             Find the "Always Allow" Button
    t =     4.77s                 Snapshot accessibility hierarchy for app with pid 3409
    t =     4.82s                 Find: Descendants matching type Alert
    t =     4.82s                 Find: Identity Binding
    t =     4.82s                 Find: Descendants matching type Button
    t =     4.83s                 Find: Elements matching predicate '"Always Allow" IN identifiers'
    t =     4.84s             Tap "Always Allow" Button
    t =     4.84s                 Wait for com.apple.springboard to idle
    t =     4.87s                 Find the "Always Allow" Button
    t =     4.87s                     Snapshot accessibility hierarchy for app with pid 3409
    t =     4.91s                     Find: Descendants matching type Alert
    t =     4.91s                     Find: Identity Binding
    t =     4.91s                     Find: Descendants matching type Button
    t =     4.91s                     Find: Elements matching predicate '"Always Allow" IN identifiers'
    t =     4.91s                     Check for interrupting elements affecting "Always Allow" Button
    t =     4.91s                         Snapshot accessibility hierarchy for app with pid 3409
    t =     4.94s                         Find: Descendants matching type Alert
    t =     4.95s                 Synthesize event
    t =     5.04s                 Wait for com.apple.springboard to idle
    t =     5.10s             Waiting 10.0s for "Allow “SimpleUITest” to access your location?" Alert to not exist
    t =     6.11s                 Checking `Expect predicate `exists == 0` for object "Allow “SimpleUITest” to access your location?" Alert`
    t =     6.11s                     Checking existence of `"Allow “SimpleUITest” to access your location?" Alert`
    t =     6.11s                         Snapshot accessibility hierarchy for app with pid 3409
    t =     6.14s                         Find: Descendants matching type Alert
    t =     6.66s         Check for interrupting elements affecting "SimpleUITest" Application
    t =     6.67s     Synthesize event
    t =     6.75s     Wait for sean.systems.SimpleUITest to idle
    t =     6.78s Waiting 10.0s for "Permission Granted" StaticText to exist
    t =     7.79s     Checking `Expect predicate `exists == 1` for object "Permission Granted" StaticText`
    t =     7.79s         Checking existence of `"Permission Granted" StaticText`
    t =     7.81s Tap "Permission Granted" StaticText
    t =     7.81s     Wait for sean.systems.SimpleUITest to idle
    t =     7.84s     Find the "Permission Granted" StaticText
    t =     7.85s         Check for interrupting elements affecting "Permission Granted" StaticText
    t =     7.86s     Synthesize event
    t =     7.94s     Wait for sean.systems.SimpleUITest to idle
    t =     7.96s Tear Down
Test Case '-[SimpleUITestUITests.SimpleUITestUITests testGrantingPermissions]' passed (8.166 seconds).

First the Grant Permission button is tapped, causing the iOS permission request alert to be presented.

Next, the app is tapped. This causes the interruption monitor to inspect the alert and perform the actions defined in the handler — because the alert is blocking interaction with the app.

Notice also that the interruption monitor is only consulted after the UI test runner matches an element to interact with. If you attempt to match an element which is only expected to be present after the permission is granted or denied your test will not succeed.

Thus this presents two options for ensuring the interruption handler is executed during a UI test case:

  • Tap on an existing element (such as the button causing the alert's presentation)
  • Tap anywhere on the screen (this should succeeded in any case but may be less desirable)

Configuration: Xcode 10.2 (10E125)

The Least Exciting Features in iOS 10

Every year, some more so than others, Apple overwhelms us with a deluge of new features, APIs, platforms even, at WWDC.
It can be hard to contain your excitement for the new and shiny, as well as figuring out what actually matters for your day to day development.
In this post I’d like to take a slightly different approach and consider some of the underappreciated news from WWDC and how this impacts iOS and all of Apple’s platforms.

Foundation

Foundation is the workhorse library underpinning most high-level development on Apple’s platforms. Hence, its release notes are required reading once the first developer seeds are released.

These typically feature both fundamental changes in behaviour throughout the SDK, such as the grand renaming by applying the Swift API Design Guidelines (SE-0023) and the introduction of Swift value types (SE-0069) where appropriate as well as more subtle adjustments and improvements.

Often incremental improvements to Objective-C are noted here too, such as string enumerations and class properties.

Occasionally, Foundation may end up sherlocking common third-party libraries like with the introduction of the ISO 8601 date formatter or date range formatters, while also adding new functionality such as support for measurements and units as well as delivering general performance improvements in key infrastructure like NSUserDefaults, KVO/KVC and friends.

More Release Notes

Platform-specific release notes such as for iOS include details that are typically more specific to certain use cases, but can be helpful to point out pitfalls when building with a new SDK and showstoppers which may well be a good reason not to install beta releases on your primary devices.

For example, once linked with the iOS 10 SDK, attempting to call [someObject valueForKey:nil]; will result in a crash, as opposed to the previous undefined behaviour (which includes crashing of course).

Unified Documentation

You may recall the increasing multiplication of documentation over the past years as watchOS and tvOS were added as development platforms. Such as, 4 pages claiming to document NSObject.

This has finally come to an end, as documentation for corresponding symbols has been unified in a single API reference across all platforms, drastically streamlining browsing documentation.

However the old documentation library interface has not been entirely abandoned yet. You will still find long-form guides, sample code, references etc. here.

Unified Logging and Activity Tracing (Session 721)

iOS 10 and macOS Sierra bring with them an entirely overhauled unified logging subsystem.

While NSLog and other existing APIs will continue to work, these are in fact patched through to the new os_log APIs.

In particular, os_log now supports multiple additional views on logs, based on subsystem, category and severity. Log levels are configurable when viewing logs, and low priority logs are only kept in memory, saving disk space for log histories. Even your console log output benefits from this, as by default the log category is now included in messages there.

The entirely rewritten log Console (Applications ▸ Utilities) reflects these changes and the improvement filtering options they entail.

Additionally, os_log integrates with improved activity tracing, allowing logging to be tied to activities, which can in fact be nested and visualized using the new Console utility. This can help make flows and their related logging clearer. For example, a web framework may have nested activities for the various stages of request processing.

Unfortunately, as of Beta 3, os_log is not entirely complete yet. While it has powerful configuration options on macOS, the format for configuration profiles to configure behaviour on iOS has not been released, and the logging methods are inaccessible from Swift for the time being.

Instrumentation: App Startup Time (Session 406)

While improved logging can bring better insights of a process’ runtime performance, what happens before main() is called is often more of a mystery, but is often an issue when facing long launch times or more seriously, operating system level bugs which are hard to track down.

This session helps provide an overview of how binaries — be they executables, dylibs etc.—are structured and loaded as well as providing details of how your choices can impact the amount of time needed for this. Amongst other aspects, the DYLD_PRINT_STATISTICS environment variable is demonstrated as a means to gain insights prior to main() being called.

Instruments (Sessions 411 and 418)

Instruments is a crucial tool for tracing application behaviour at runtime. Two sessions focused on how to use instruments more effectively.

At a lower level, System Trace, can be useful to squeeze out micro-second level optimisations, particularly for real-time tasks or graphics rendering.

The time profiler is more broadly useful to find hotspots worth investigating or tracking down what is causing unexpectedly high resource usage in an app. It has gained some new tricks this release, so watching the session covering it is certainly worthwhile.


What’s worth knowing?

This piece should provide an example of the less prominently advertised parts of WWDC and Apple’s software releases.

Knowing the Foundations we stand on, their release notes, where to find documentation as well as effective logging and instrumentation practices provide the bedrock for development work.

EmptyCollection

In a world 🌏 driven by Entropy Harnesses sometimes your collections 📁 can feel cold ❄️ and heartless 💙⃠.

If you share this feeling, you may want to let EmptyCollection into your heart and collections.
EmptyCollection provides trivial conformance to Collection (a matter which has been considered previously on these pages).

This is achieved by using an integer index type, a trivial generator struct dubbed EmptyGenerator, defining startIndex == endIndex, returning a count of 0 and disallowing subscripting.

This seems rather straightforward, so where is this put to use?
The file header provides a brief motivating example:

Sometimes an operation is best expressed in terms of some other, larger operation where one of the parameters is an empty collection.

For example RangeReplaceable uses EmptyCollections to write a number of removal operations in terms of replaceRange, as does Array, while Sequence uses an EmptyCollection if a prefix of length 0 is requested.

So, the next time you need a truly empty collection, consider choosing EmptyCollection for your API.

Under the Hood

As mentioned in the inaugural post, the goal of Swift: Shuffle All (S:SA henceforth) is to explore the Swift standard library in a random fashion.
At the time, an external source of entropy was used to choose the first topic for discussion. However, as this can't always be relied on, a more reliable method of harnessing entropy to enable serendipity needs to be found.

The entropy harness that was developed is outlined briefly in the remainder of this post.

Big Nerd Ranch's Freddy is used rather than NSJSONSerialization to allow for more fluent JSON parsing. Numerous other options exist, but are beyond the scope of this post.

Fortunately SwiftDoc exposes an API, avoiding reliance on a third-party index.
A plain NSURLSession data task is used to query the endpoint which returns all SwiftDoc entry URLs.
Assuming a good response, this is parsed as JSON and checked for a top-level dictionary containing the name and URL of SwiftDoc entries.
This dictionary is mangled into a dictionary keyed by category and its associated entries — based on the assumption that entry URLs contain their category.
Some categories of limited interest are trimmed (at time of writing these were 404 and operators).

Finally a random category and entry within this category are chosen. This requires some interaction between arc4random_uniform which returns integers and the opaque index type used by Dictionary.
The result is presented as a string literal (only suitable for REPL or Playground use for the time being).

Finally, the data task is resumed as these are not scheduled for execution automatically.

Having outlined the internals of the entropy harness, upcoming posts will benefit from the fruits of its labour.

`struct Set { var first { get } }`

Artist's Impression

Artist's Impression

Welcome to Swift: Shuffle All.

While some of us may have fond memories of these friendly words greeting users in iOS 7's Music app, these days we must turn elsewhere to appreciate a random ordering of collections.

Swift: Shuffle All is intended as one option to satisfy this need by bringing together Nate Cook's SwiftDoc.org and a source of random indexes (RandomAccessIndexType anyone?) as a means to explore Swift's standard library and built-ins in a more serendipitous fashion.

Rather than hoping for a conference talk to mention that API you always wanted to know about:

entropy may well do a better job of surfacing it before you need it.

While so far no well-defined method exists for choosing subject matter from SwiftDoc.org, Chris kindly provided inspiration for this initial exploration:

Investigating the protocol hierarchy for Set, or rather: Set<Element : Hashable> is revealing. Conforming to CollectionType places several requirements on Set, in particular providing a Generator for interation and Index for representing collection indices.

CollectionType thus also requires that a Set provides an instance variable defined as:

So far, so good. However this doesn't tell us how first element is determined when iterating.

On the other hand, we now have a place to go and find these details: the Swift source at https://github.com/apple/swift An initial search for struct Set doesn't yield any results in Swift source files, however a HashedCollections.swift.gyb file looks promising.

Further investigation reveals these are parsed by a tool included in the repository dubbed Generate Your Boilerplate. Clearly some tooling work has been done to ease Swift development.

Back to HashedCollections.swift.gyb. Running this through

utils/gyb stdlib/public/core/HashedCollections.swift.gyb > HashedCollections.swift

inflates a somewhat larger Swift source file.

This seems to contain two rather similar clusters of types, one for Dictionaries and another for Sets. In fact, much of this was investigated in Ankit Aggarwal's Exploring Swift Dictionary Implementation(Although they don't seem to have shown their hand about where this information was gleaned from.)

Both Dictionary and Set share their HashedContainerStorageHeader while a Set consists only of keys as opposed to a Dictionary which must store keys and values.

Since buckets for keys are looked up by their hashValue, this suggests a Set's first object will depend on its hashValue. Well, almost. As the hashValue is squeezed to fit the header bitmap which is sized based on the Set's capacity, a sufficiently large capacity for the range of hashValues must be choosen to allow this sorting to take place.

Have a look at this Playground page to experiment with this behaviour. Simply drag the page into an existing playground to get started.