Swift API Design Guidelines: Highlights

The Swift API Design Guidelines could be one of the most important things I’ve found towards writing cleaner Swift code. Have you seen them yet? I hadn’t actually looked at them until I watched the WWDC video, “Swift API Design Guidelines.”. When chatting with a coworker about which WWDC video to watch during some downtime, I’ll admit that this video didn’t really catch my eye. As someone who doesn’t really write much “framework” type code for other people to consume besides those I directly work with, I didn’t immediately relate to the description for the video:

Swift 3 introduces new API Design Guidelines specifically crafted to the unique character of Swift for clear, concise code. This talk will explore the philosophy behind the Swift API Design Guidelines and their application throughout the Swift Standard Library and the Cocoa and Cocoa Touch APIs. See how this API transformation will affect your Swift code and learn how to ensure a smooth transition to Swift 3. Learn how Swift 3 imports Objective-C APIs and how to expose rich Swift interfaces for existing Objective-C libraries.

Well having watched the video, and then read through the actual design guidelines on Swift.org, I’m bought in, and recommend you take a look as well.

The Swift API Design Guidelines is like an instruction manual for how to craft the “words” in your code: how you name variables, parameters, method names. It tells you how to interweave the words in your code with the surrounding documentation. By becoming familiar with the Swift API Design Guidelines, you’ll both write code that is consistent with the community, and the APIs that are being created within the frameworks right out of Apple and the Swift team. It’s a win/win proposition.

Here are the highlights of the Swift API Design Guidelines for me:

The “ed/ing” rule

The presenter in the WWDC video summed this rule up as the “ed/ing” rule (that makes it easy for me to remember). In the Swift.org documentation, it’s referred to as Name functions and methods according to their side-effects in the Strive for Fluent Usage section. The guideline suggests that often, “A mutating method will often have a nonmutating variant with similar semantics, but that returns a new value rather than updating an instance in-place.” According to the ed/ing rule, append ed or ing to the nonmutating version. For example:

Mutating

x.sort()
x.append(y)

Nonmutating

z = x.sorted()
z = x.appending(y)

Omit Needless Words

Boy, if there’s one bad habit that is leftover from Objective-C is really wordy method names, particularly be repeating words. For example, just look at the NSMutableArray Objective-C class documentation and take a peak at how many times the word “object” appears. Methods like this are all over the Objective-C Foundation API:

- (void)addObject:(ObjectType)anObject;
- (void)removeObject:(ObjectType)anObject;

Just look at how many times the word “object” appears in the class documentation for NSMutableArray. As a result, this verbosity made its way into the code that I wrote as well.

Well this is a no no in Swift. The Swift creators are striving for a balance of conciseness AND clarity.

Although Swift code can be compact, it is a non-goal to enable the smallest possible code with the fewest characters. Brevity in Swift code, where it occurs, is a side-effect of the strong type system…

The last part of this section is relevant. It’s the strong type system that allows for the NSMutableArray API to be redesigned as:

func add(_ anObject: AnyObject)
func remove(_ anObject: AnyObject)

Notice how the word “Object” was removed from the method name itself? That’s exactly what is meant as a “needless word” in the guideline “Omit Needless Words.” This is because consumers of this API can rely on the strong type system of Swift to only pass objects to the method that are of the defined type of the parameter. This is just one example where Swift API Design Guideliness lead us to more concise code through omitting needless words. Now that you’re aware of it, you’ll start to see this all over and should even apply it to your own API design.

Also, be sure to check out the sections in the Swift API Design Guidelines for Name variables, parameters, and associated types according to their roles and Compensate for weak type information. Detailed examples are not included here, but those guidelines provide good companion structure how to achieve clarity while also ommitting needless words. Remember, don’t strive for brevity over clarity. Just like the WWDC presenter mentioned, you don’t want to have to constantly be switching to the API docs because the API is so terse.

Grammatical English Phrases

Method and function names should read like grammatical English phrases. I really like this one, and I feel like this is one good habit I’ve carried from my dark days as a Java programmer. This is also covered in the String for Fluent Usage section of the Swift API Design Guidelines. A good sanity check for your method and parameter names is to read the line of code aloud and ask yourself, “Is this grammatically correct English? Is this how I would conversationally say it?” If yes, then you probably are on to a good API design. If not, you should probably reconsider it. Examples right from Swift.org of good API design that grammatically makes sense:

x.insert(y, at: z)          // “x, insert y at z”
x.subViews(havingColor: y)  // “x's subviews having color y”
x.capitalizingNouns()       // “x, capitalizing nouns”

Bad design that doesn’t read aloud well:

x.insert(y, position: z)
x.subViews(color: y)
x.nounCapitalize()

Other Random Cool Stuff

Documentation

In the handful of programming languages that I’ve professionally written, I’ve never come across such a succinct yet well defined set of guidelines for writing code documentation. It’s right at the top of the Fundamentals section of the Swift API Design Guidelines. There are short and sweet points to consider when writing documentation for your Swift code, and also how to make your comments compatible with how Xcode might represent them in generated documentation.

Conventions

The Conventions section is really cool too. There are a lot of one-off suggestions to help clarify everything from when to document Big-Oh efficiency, to how to capitalize your variable and method names.

Wrap Up

I can’t urge you strongly enough to check out the Swift API Design Guidelines on Swift.org. I can tell you that in my opinion it is one actionable thing that you can do to improve your Swift code, today. It’s simple, clear, and instructional manner makes it really easy to understand. Your code will be more consistent with itself, and code written elsewhere in the community. Who wouldn’t want that?

Happy cleaning.

XCTest Target Membership Rule #1

I just had to learn a lesson the hard way, and wanted to pass on to you what I’m going to call the XCTest Target Membership Rule. It took some head banging of mine to discover this rule, and I don’t want you to go through the same pain.

The XCTest Target Membership Rule

The actual implementation files for your app should not be included with the Target Membership for your XCTest target.

xctest target membership

If you were to check the box next to TestStaticTests you would start to see this error in the Console when running your tests:

objc[14049]: Class ViewController is implemented in both <path cut>/TestStatic.app/TestStatic and <path cut>/TestStaticTests.xctest/TestStaticTests. One of the two will be used. Which one is undefined. 

The runtime for XCTest is telling you that it has found the same class defined in two places, the two targets for which you specified the file should be included. And furthermore, you’re also being told that the behavior with regard to which implementation is used will be “undefined.” The funny thing was, I actually found this error message to be misleading. I found that in practice, both copies of the implementation are actually used if you can believe that!

Here’s an in-depth question I posted on StackOverflow documenting the odd behavior I noticed, and included a sample project demonstrating it as well.

Background

These past few weeks I’ve been working on an older project, specifically AWeber Stats. If you notice, that app hasn’t been updated since April 2015, that’s a while. As a result a lot of the dependencies were out of date, so one thing the team set out to do was update the dependencies, and CocoaPods seemed like a good place to start. We were previously on version 0.34.1 and planned to update to 1.0.1 (which at the time of this writing is the latest version). We use Ruby Gems to manage the dependency versions for CocoaPods specifically. This allows us to have per-project control of which version of CocoaPods end up being used. This gives us the reassurance that we must explicitly designate the desire to update a given project’s CocoaPod’s version, and thus accordingly test and verify the update.

I move forward with the update, bumping the version of CocoaPods to 1.0.1 as defined in our Gemfile. CocoaPods 1.0 actually defines a new schema for Podfiles so I subsequently migrated the Podfile to the new format, and successfully ran pod install. Everything has gone smoothly up to this point. Then I opened my Xcode workspace and attempt to run our test suite.

100s of failures.

In addition to the failures, I saw many instances of that Console warning from earlier:

...One of the two will be used. Which one is undefined.

Now the thing is, remember, at this point I hadn’t yet discovered the XCTest Target Membership Rule. And as it turns out, nearly every implementation file in the project for the app target, also existed in membership for the test target. And in fact, as I dusted the cobwebs off of my mind, and thought back to the original work on the project, I specifically remember needing to designate those implementation files as having membership in the test target. Specifically, it was errors like this that would appear of the class under test was not part of the test target:

Undefined symbols for architecture x86_64:
  "_OBJC_CLASS_$_MyArrayController", referenced from:
      objc-class-ref in MyArrayControllerTests.o

So where did the need to include that file with the test target go? Why was Xcode now telling me that I shouldn’t include these files with both targets?

Well I never got to the bottom of it. I have two guesses at this point: something either in Xcode, or in CocoaPods changed to modify the behavior how XCTest integrates with the classes under test.

Closing Thoughts

I’m still digging to get to the bottom of this. In the meantime, I will not forget the XCTest Target Membership Rule. A couple other lessons surfaced:

  1. Don’t wait to keep your project up to date – It’s easy to launch an app on the store, or hand it off to a client, and semi-forget about it. The app is live, attracting customers, and functioning well. Just remember, at some point it will either need to be updated or die. And the longer you wait, the harder it will be to bring it up to contemporary standards. It’s much easier to make more frequent small changes, than wait 15 months, return to an unfamiliar code base, and try to bring it up to speed.
  2. Know your tool chain – I’m still not fully aware of what changed between the last app update and today such that such a fundamental piece of project functionality changed. I’m taking this as a kick in the butt to get my head wrapped around third party dependencies being used. And if you can’t do that (for whatever reason – time, complexity, interest, etc.), don’t use them. There are plenty of ways to manually install third party code, or even write it from scratch. Of course other people have already invented the wheel, just don’t blindly use the tools without a foundational understanding of how to troubleshoot them when they go wrong.

Happy cleaning.

Asynchronous Xcode UI Testing

As I continue to delve into Xcode UI tests, I’m starting to discover some of the lesser advertised benefits and drawbacks of the tool. One specific nuance that has caught my eye is how to deal with asynchronous Xcode UI testing. Xcode UI tests are asynchronous in that they simulate end user interaction with your application. End user interaction triggers animation (and in some cases waiting on remote services), and animation doesn’t happen instantaneously. There is a disconnect between the speed at which the lines of code in your tests can be executed, and whether the simulator (where the app under test is located and running) can keep up or not. In most cases, the simulator can’t keep up. As a result, you need to explicitly instruct your tests to wait for the app to catch up in the simulator. There are two ways to do this that I’ve been using.

Smart Waiting

waitForExpectationsWithTimeout(_:handler:)

When performing asynchronous Xcode UI testing, it’s often really useful to be able to verify something has appeared on screen. This way you know that the application is a certain state, and can continue executing a test script. For example, imagine an application with a tab bar, selecting a different tab should show a different view controller. If you were writing a test that relied on features within the second tab of the tab bar, it’s important have some assurances that when you instruct your test to tap the second tab, the tab actually appears. Furthermore, the act of waiting for something on screen can also act as a test in itself. If tapping the second tab doesn’t actually show what you expect, something probably went wrong and the test should fail.

Inspired from Joe Masilotti’s Cheat Sheet for Xcode UI testing, here’s how you can leverage waitForExpectationsWithTimeout(_:handler:) to verify that something appeared on screen:

// 1
let goLabel = self.app.staticTexts["Go!"]
let exists = NSPredicate(format: "exists == true")
expectationForPredicate(exists, evaluatedWithObject: goLabel, handler: nil)

// 2 
app.buttons["Ready, set..."].tap()

// 3
waitForExpectationsWithTimeout(5, handler: nil)
XCTAssert(goLabel.exists)

Here’s an explanation for this code:

  1. Define a NSPredicate to be used in a XCTestExpectation to verify that the Go! button exists in the app.
  2. Tap the Ready, set… button.
  3. Wait for the Go! button to appear.

waitForExpectationsWithTimeout(_:handler:) will repeatedly look for the NSPredicate to be true within the timeout provided, in this case 5 seconds.

This approach to verifying that something exists on the screen is incredibly useful. It’s very much like my old favorite KIF API waitForViewWithAccessibilityLabel. The thing is, it isn’t perfect.

Not Perfect

I’ve observed a number of cases when using this approach has led to false positives that an element “exists” on the screen. Essentially the XCTest UI framework observes that the element exists, but isn’t actually tappable, or in a state that a human-end-user would consider “on the screen.” If seen this most often happen when this technique is used in combination with animations happening in conjunction with the test action. For example, if you have an application that taps a button, which triggers a navigation controller to push a new view controller onto the stack, there’s a relatively slow animation that happens. With the predicate approach laid out above, asynchronous Xcode UI testing will actually identify incoming screen elements as “existing” before they are in their “final resting spot” at the end of the animation. So if you gate further steps of your test on the presence of something on the screen as determined by the XCTest UI framework, you’ll get false positives that the element is on the screen and ready for further interaction. Essentially, Xcode UI tests will tell you, “Yes, that button exists on the screen,” but in reality it isn’t yet “on the screen.” Don’t worry though, there’s a solution, and it’s incredibly dumb.

Dumb Waiting

sleep(1)

In those cases when you need to a little extra pause in your asynchronous Xcode UI testing in response to an asynchronous event like an animation, I’ve found that just using a sleep(1) goes a long way. Essentially this will block your tests from continuing for the specified duration of time, while the application under test is allowed to continue. To update the example code above, I add the sleep(1) in conjunction with the NSPredicate technique as follows:

let goLabel = self.app.staticTexts["Go!"]
let exists = NSPredicate(format: "exists == true")
expectationForPredicate(exists, evaluatedWithObject: goLabel, handler: nil)

app.buttons["Ready, set..."].tap()

waitForExpectationsWithTimeout(5, handler: nil)
XCTAssert(goLabel.exists)
sleep(1)

Notice that the sleep(1) happens right after the NSPredicate is determined to be true. This follows my discovery that Xcode UI tests identify things as “existing” before they are actually on screen. Using the NSPredicate technique with the sleep(1) let’s you verify the presence of specify UI elements, while also giving the app time to complete long running tasks like animations before continuing.

What’s funnier is that this sample code shown on screen at WWDC 2016 caught my eye:

asynchronous Xcode UI testing

Turns out, Apple uses the same technique!

Assertions in Closures

Another useful technique in asynchronous Xcode UI testing is to perform assertions on user-interface elements in response to closure execution. Often, indication of completion of long running calls like web service retrievals will be implemented with closures. I’ve written Xcode UI tests that explicitly call the web service separate from the application in order to cross-check data returned. Basically, I’ll ensure that the data the raw web service returns matches what is in the application. Whether this is your use case or not, you can use this pattern to make assertions in the closure on callback completion. The only requirement is that Xcode UI API calls need to happen on the main thread:

// 1
let expectation = expectationWithDescription("API Request Complete")
var response: Response?

// 2
RequestUpManager().get(id: "3688840") {
  (response: Response?, error: NSError?) -> () in
  
  if let response = response {
    response = response

    // 3
    dispatch_async(dispatch_get_main_queue(),{
      // Verify there is something on screen that matches what the API provided.
      let cell = self.app.cells.elementBoundByIndex(0)
      XCTAssertEqual(cell.staticTexts[response!.position].identifier, "Position")
      expectation.fulfill()
     })
  }
  else {
    // 4
    XCTFail("Unexpected object returned from API")
    expectation.fulfill()
  }
}
// 5
waitForExpectationsWithTimeout(WAIT_TIMEOUT, handler: nil)
  1. Use expectations to keep the test running while the request loads.
  2. Synchronously load data from the API.
  3. Tell the main thread to verify what’s on screen. (This will crash if not done on the main thread).
  4. Explicitly fail the test if unwrapping the optional data returned from the API fails.
  5. Using XCTestExpectations, wait for the asynchronous code to complete.

Wrap Up

These are just a couple little tricks I’ve learned for asynchronous Xcode UI testing when taking my first dive into the API. It’s always nice to get in and try out a tool beyond what’s shown in documentation or videos, you get a much better handle for nuances of what’s available. Have you come up with any solutions for these challenges in asynchronous Xcode UI testing?

Happy cleaning.

How to Disable Tests in Xcode

It’s easy to disable tests in Xcode, but not entirely obvious. A lot of people give Xcode flack in terms of how far behind other IDEs it is with regard to its support for automated testing. I don’t entirely disagree with this. On the other hand, it’s come so far from just five years ago and I’m thankful for that.

Why Disable Tests

You might be wondering, if I’ve spent all this time writing tests, why would I ever want to disable tests in Xcode? I’m not advocating permanently disabling them, instead, I’m advocating disabling them temporarily to speed up your development or to unblock progress. Just this week, I returned to an old project of mine, one that hasn’t had an app store update in over a year. It still uses CocoaPods version 0.33.0, and Swift 1. Needless to say, it needed some work getting it up to speed. (Side note: I have a lot of ammunition for future blog posts on how to NOT let a project get this stale, but we’ll save those for a future camp fire). First, I needed to get the project to actually compile. Second, I needed to get the tests passing. And finally, I needed to update our continuous integration machines and jobs to also compile and successfully run the tests. I had my work cut out for me.

When working with a project this old, sometimes advanced techniques like TDD aren’t immediately useful if you can’t even get the project to simply compile. When working through compilation errors and test failures at a large scale, it’s useful to selectively turn off a subset of the errors or test failures so you can eliminate noise, focus on a piece of the problem, and make progress. You can easily disable tests in Xcode in two ways: disable compilation, and disable test execution. Both of these are scheme changes. Here’s how:

How To Disable Tests

Disable Tests From Building

To simply disable all your tests from building, you can do this in the Scheme editor. From the menu bar, select Product -> Scheme -> Edit Scheme.

In the left hand bar, select Build.

Then in the main pane, you can disable tests from compiling for any action in your project. If you’re project is in a really bad state, you can disable tests from compiling when running your application.

disable tests in Xcode

By disabling any of those checkboxes, when you run your application, anything in the associated targets will not be compiled.

Disable Tests From Running

Similarly, you can use the Scheme Editor for selectively turning tests and off. Still in the Scheme Editor, select Test in the left hand bar. Then, in the main pane, you’ll see each target associated with tests. The checkbox will let you enable or disable the tests from being run when you run tests for your project (Command-U, or Product -> Test).

disable tests in Xcode

Disabling an entire target may not be as granular as you’d like. In fact, it’s pretty coarse. You can expand any of the targets in that list and see specific test classes, and then selectively enable or disable tests by test class. And if that isn’t granular enough, you can drill one level deeper and then selectively turn tests on or off by test method!

disable tests in Xcode

Give it a try.

xcodebuild in Xcode 8

One thing I’m really excited for in Xcode 8 is that xcodebuild, the command line interface for building Xcode projects, was updated with similar functionality. With xcodebuild in Xcode 8 you can selectively pick which tests are built and executed. The use case is a little different for this tool. xcodebuild is often used as part of a continuous integration setup. With the new changes, you no longer need to compile tests to run them. xcodebuild will be able to run tests previously compiled. This means that you can compile your app and tests on one machine, and then distribute the load of testing it across other machines!

disable tests in Xcode

To build you app for testing, but not actually run the tests:

xcodebuild build-for-testing -workspace <path>
                             -scheme <name>
                             -destination <specifier>

Then you can use the produced bundle on different machines to run the tests without compiling! Combine that with the following steps for selectively running tests, and you’ll be able to distribute the execution of your test suite across different machines, in parallel!

To selectively specify which tests to run for xcodebuild:

xcodebuild test -workspace <path>
                -scheme <name>
                -destination <specifier>
                -only-testing:TestBundleA/TestSuiteA/TestCaseA
                -only-testing:TestBundleB/TestSuiteB
                -only-testing:TestBundleC

You can also specify tests to be skipped:

xcodebuild test -workspace <path>
                -scheme <name>
                -destination <specifier>
                -skip-testing:TestBundleA/TestSuiteA/TestCaseA

Don’t Abuse It

With great power comes great responsibility. Please don’t disable tests in Xcode, either from running or compiling, and leave it that way. That’s like driving without a seatbelt. Only do it if you are working in effort to improve your project. I know that working with a dusty and stale project isn’t the only time that it may be useful to disable tests in Xcode. I’m sure you’ll find your own reasons. I’d love to hear what they are.

Happy cleaning.

Xcode UI Tests Review

After writing Xcode UI tests for the first time today, immediately some things formed my initial impression. I wanted to pass along my Xcode UI tests review here on cleanswifter.com in effort to help educate others. For at least the pass year, if not longer, I’ve been using KIF for writing my functional UI tests. I love KIF. KIF is a third party, open source tool. That brings both benefits and drawbacks. Right off the bat, due to changes in the iOS 10/Xcode 8 Accessibility Inspector, KIF was broken in the beta versions of our tools. It was quickly fixed, and the impact was low, but it does make you wonder about what’s in store for the future when there are less people available to maintain it? This wasn’t the only thing that drove me towards Xcode UI tests for this project, but it was certainly a concern. I still love KIF and will continue to use it on other projects for the foreseeable future.

Background

Xcode UI tests build on an older tool from Apple called UI Automation. UI Automation enabled the iOS developer to write UI tests for their apps, and it was officially supported by Apple. “Officially supported” is certainly a loaded term, especially with UI Automation. The documentation was pretty bad. While there was some light guides on developing UI Automation tests, the actual API documentation had very little information. Combine that with the fact that the tests were written in JavaScript and run with Instruments (outside of Xcode), I was never happy with the tool.

Then last year, in WWDC 2015, Apple announced a reimagined solution for writing UI tests, Xcode UI tests. Here’s my Xcode UI tests review.

Pros

Officially Supported by Apple

This shouldn’t be underplayed. I’d live through a lot of crappy documentation just to use an officially supported tool. Apple officially supports Xcode UI tests, and that means a lot to me. It means you get WWDC videos on the subject. You get subtle integration with Xcode that only Apple could have access to. It means that your tests are less likely to break in future versions of iOS and Xcode.

Tests Written In Swift

Unlike UI Automation tests, Xcode UI tests can be written in either Objective-C or Swift. This could be the single best improvement. This means that you get everything from code completion, to compile-time syntax checking, to full Xcode integration.

Integration with Xcode

It was really annoying with UI Automation that you needed to switch to Instruments just to run your UI tests. Furthermore, JavaScript support in Xcode is pretty bad too, so one was left searching for a good editor experience when writing tests. Xcode UI tests change this. Now, the tests are first-class citizens right in the same IDE that we are using to write our actual application code. There’s even a “New File template” for Xcode UI tests.

Xcode UI Tests Review

XCTestCase subclasses

Buliding on the integration with Xcode, Xcode UI tests subclass XCTestCase just like unit tests. This means the full API of XCTest is available. Methods like XCTestAssertEqual and XCTestAssertTrue can be leveraged for writing your tests. Additionally, things like Scheme integration into your Test action enable execution of your Xcode UI tests when running tests, right from Xcode. Also, all those others niceties of running unit tests and the wonderful keyboard shortcuts can also be used with your Xcode UI tests.

Xcode UI Tests Review

UI Recording

UI Recording for Xcode UI tests is pretty cool. You can click a “Record” button in Xcode, and your app will launch in the simulator. Then, as you tap and navigate through your app, actual code will be generated in a test reflecting the path through your app that you took. It’s not perfect though, and more importantly, it doesn’t actually generate any assertions. Also, to prevent your test from being really brittle, you do need to consider breaking your tests up into cohesive chunks, and not one giant test.

Cons

Xcode UI tests aren’t without their flaws.

Documentation Is Still Light

The documentation for Xcode UI tests is still light. It’s not as bad as UI Automation was, but it’s still not perfect. KIF has been around for years. There are tons of stackoverflow.com questions and answers for it, the GitHub repository is mature, and you’ll find plenty of tutorials. I still can’t find an official piece of web-hosted API documentation for Xcode UI tests.

See the end of the article for a list of resources that I’ve been using as reference for Xcode UI testing.

Slow

Obviously all UI tests are going to be slow, regardless of whatever platform and tools you are using. Something uniquely slow jumps out at me about Xcode UI tests though. As a developer, you are required to call XCUIApplication().launch() from your tests when you want the app to launch. Each test must call this. The common pattern from what I’ve seen is to place the call to XCUIApplication().launch() in setUp(). setUp() is an overridden method from the XCTestCase base class, and is executed by the XCTest framework before each test. The thing is, XCUIApplication().launch() is REALLY SLOW. It triggers a fresh launch of your app. Now I guess this is useful from the context of resetting state in your app, but if you aren’t careful about structuring your tests, you could inadvertently introduce a lot of overhead in the speed of your tests.

iOS9 and Up

One of the main advantages of a suite of automated tests, regardless of the platform or type of tests, is to be able to run those tests in all places that your code base is supported. If you are writing a web application, this might be different browsers at different screen resolutions and window sizes. In iOS development, you can exponential benefit from running the same suite of tests across different devices of different form factors and different iOS versions. Unfortunately, Xcode UI tests put a major hamstring in this, in that they will only run on devices with iOS 9 and greater. To me, this is a big deal. We don’t all have the luxury of requiring the minimum iOS version to be the latest on the market. At the time of this post, iOS 9 is the latest iOS version, so that means I can’t even run my automated UI test suite on one version back, iOS 8. There have been so many times in my testing career in which UI tests identify and/or verify a bug that is present on one iOS version and not another. I’m really disappointed that Xcode UI tests don’t work on anything older than iOS 9. I guess I just have to hope for my applications, we’ll soon be able to require iOS 9 and greater.

XCUIElement != UIView

One of the most awesome things about KIF is the overlap between the KIF APIs and UIKit. When writing KIF tests, there are many methods like:

func waitForViewWithAccessibilityLabel(label: String!) -> UIView!

It’s so useful to have a UIView returned. This can be cast to more specialized subclasses that actually represent the object returned. And once you do that, you have full access to all the methods available on that object as defined in the actual code. Code like this is possible:

let saveButton = tester().waitForViewWithAccessibilityLabel("Save") as! UIButton
XCTAssertEqual(UIColor.blue(), saveButton.currentTitleColor)

This code is really useful because once you have a handle on the UIView, you can access so many APIs to verify behavior.

Unfortunately, there is no such intersection in Xcode UI tests. In Xcode UI tests, the object returned from similar method calls is a XCUIElement, and there’s no way to translate this to UIView (that I’ve found). As such, you are limited to the limited API available through Xcode UI tests and XCUIElement – none of which translates to a UIKit equivalent.

Final Thoughts

Despite the drawbacks, I’m still excited to use Xcode UI tests and I hope this Xcode UI tests review conveys that. It’s always fun to learn something new, and I think there are plenty of positives to using the tool. Have you used Xcode UI tests? What’s your Xcode UI tests review?

Happy cleaning.

Useful Resources

“What’s New In Swift” WWDC Session Highlights

I just finished watching What’s New in Swift from WWDC 2016 and wanted to pass on the highlights for you. “What’s New in Swift” is great session that I highly recommend you also watch for yourself. There are a couple things that jump out at me that I feel are worth writing about.

Goals For Swift 3

Unless you’re hiding under a rock, you’ll know that the big news in Swift is the upcoming release of Swift 3. This presentation kicks off immediately covering the goals for Swift 3:

  • Develop an open community
  • Portability to new platforms
  • Get core fundamentals into shape
  • Optimize Swift for awesomeness

Granted some of these are a little “soft” and not well defined, I really like the first goal. On stage, Apple did not hold back in describing that Swift 3 will be the result of total community involvement with the languages evolution – which is exactly the desired intention. This totally hits home for me. Even though I haven’t been an active participant in the language’s evolution, I’m a firm believer that the open source software model leads to better software, and for something as critical as the language that I could be using for the foreseeable future, I’m happy that it’s being evolved in such an open model.

Swift Adoption at Apple

This might be one of the first times that I’ve seen or heard Apple formally recognize and describe major uses of Swift internally in their own development. It was surprisingly extensive, and refreshing to hear them actually admit to it. Swift has been out almost two years now, and for me, one of the signs I’ve been looking for to represent Swift’s maturity is precisely this: Apple’s adoption of the language in their own products, and they finally hit the nail on the head for me. Here are some highlights:

Apple Products With Lots Of Swift

  • New Music app for iOS
  • Console app in Sierra
  • Dock app
  • Picture in Picture in Sierra (actually 100% Swift)
  • New documentation viewer in Xcode (actually 100% Swift)
  • Swift Playgrounds for iOS (actually 100% Swift)

The presenter then went on to talk further about the Dock with some really revealing information. Internally, there’s actually a lot of stuff I consider general “OS functionality” that Apple actually classifies as the Dock app. Can you believe all this is included in the Dock codebase?

  • Dock bar at the bottom
  • Mission Control
  • Command-Tab Application Switcher
  • Stacks
  • LaunchPad
  • Spaces
  • Dashboard
  • Some of Notification System

What’s interesting about the Dock app is that a significant amount of Swift code was written for it in the El Capitan release, 10s of thousand lines of Swift code out of a total of 200k lines of code total. This is interesting because it represents a case study of a non-trivial amount of code that will be migrated from Swift 2.2 to Swift 3.0, and Apple did just this. The migration resulted with about 15% less actual code! Not to mention that the engineers like the safety features of Swift as well as the more articulate code.

How To Contribute To Swift

Have you seen swift.org yet? It’s the home of Swift on the web, besides the actual repositories on GitHub. There is a ton of information on the site about everything from migrating to Swift 3, to information on how to get started with contributing to the language. It’s a lot to digest. During this WWDC session, What’s New in Swift, an extremely simple outline was provided what contributing to the Swift language looks like:

  1. Socialize your idea idea on the mailing list.
  2. Once critical mass is achieved, the idea becomes a formal proposal on GitHub.
  3. Formal review of the proposal happens once the pull request for the proposal merges into the repository. Formal review happens on the mailing list, out in the open.
  4. Core team arbitrates a decision. The rationale for the decision is always documented, regardless of whether the proposal was accepted or rejected.

All proposals, past and present, can be found in the swift-evolution repository.

Swift 3 Source Compatibility

The #1 goal for Swift 3, according to Chris Lattner, is API compatibility. This is no small task though, because decisions now will affect developers for years to come since the API will become stable. As a result, naming guidelines are carefully considered and many tweaks are made as a result. Swift 3 APIs should:

  • Strive for clarity, not terseness or verbosity
  • Capture essential information
  • Omit redundant info/boilerplate

A direct result of this is the libdispatch renaming that I wrote about a couple weeks ago. Additionally, one of the weirdest and hardest things to get used to in Objective-C for me (a prior Java developer) was it’s incredible verbosity for the sake of verbosity. Chris Latter provides a bunch of examples on stage of this new clarity, and I’m loving it.

Here’s two such examples:

array.insert(1, atIndex: 0)

becomes

array.insert(1, at: 0)

and

if url.fileURL {}

becomes

if url.isFileURL {}

I can’t wait.

There is a lot more information in this session, “What’s New in Swift”, especially some in-depth discussion of improvements and cleaning-up of Swift 3 syntax, as well as some information on tool improvements when using Swift.

One of the closing statements of “What’s New in Swift” recognized that Swift 2.3 will be a total interim step towards Swift 3, most notably, Apple recommends getting Swift 3 migration into your project plans. And certain tools like the Swift Playgrounds iOS app and the Thread Sanitizer already require Swift 3. Luckily Xcode 8 comes with a nifty converter.

Did you watch this session, “What’s new in Swift”? If so, what are your impressions?

Happy cleaning.

Three New Xcode 8 Testing Features

I’m in the middle of watching the Platforms State of the Union from WWDC 2016, and there were three new Xcode 8 testing features announced for Xcode 8 that are so exciting for me. They are all related to automated testing. Did you catch them? They were:

  1. Test crash logs are captured.
  2. xcodebuild can now run with pre-built tests.
  3. Indexing tests is now 50x faster.

Capturing Crash Logs

Have you ever ran a test, unit or UI, that triggered an app crash? It just happened to me today. I bug when animating a UIPresentationController surfaced on iOS8. Unfortunately, Xcode 7 doesn’t capture crash logs when a test triggers a crash. Instead, you have to go back, re-run the test several times, set some breakpoints, and hone in on the code that caused the crash. Luckily, this is changing in Xcode 8 as crash logs will automatically be captured when a test crashes. This means you’ll instantly know the offending line of code that caused the crash, and will be able to fix it that much faster, and move on to building other pieces of your app.

Running with Pre-Built Tests

There’s no way in Xcode 7 to provide a pre-built bundle of compiled tests to be executed against a new instance of your app. This is changing in Xcode 8 with the new xcodebuild. You’ll be able to specify a pre-compiled bundle of tests to be run against a freshly-compiled instance of your app. This has the potential to vastly reduce your compile times. By default (at least in Xcode 7), all of your tests will go into the same target. Each time you need to run your tests, your entire app and all the tests will be recompiled. This isn’t always efficient, especially if there are pieces of your app that haven’t changed. With this new feature of Xcode 8, you’ll be able segregate these tests on their own, so that they can still be run, but they don’t have to be compiled. Awesome.

Faster Test Indexing

Xcode 8 testing features

In Xcode 7, it’s really frustrating when I open a project in Xcode, and I have to wait several minutes for Xcode to finish “Indexing” my tests before I can run them. Have you experienced this? Seeing that I’m only working with a couple projects at a time, and the number of my tests creeps up slowly as I work on the projects, there really isn’t a huge cliff where performance all of a sudden drops off. Instead, one day, it will catch my eye that I noticed the indexing took longer than usual, or got in my way from actually performing a build. Then I wait. And eventually I can resume what I intended to do.

I was happy to hear that in Xcode 8, Apple has vastly improved the performance of this indexing process. In the Platforms State of the Union, it was stated that there is a 50x speed increase when tests are indexed. I can’t wait to experience this in my real projects during my day to day development.

It’s been great to watch how Apple’s support of automated testing has evolved over the years, and these three new Xcode 8 testing features continue that trend. You can measure tangible jumps in automated testing support with each WWDC since the iPhone’s original introduction. I can’t wait to see what else the rest of the week holds with regards to further enhancements to testing in Xcode 8.

Happy cleaning.

Xcode Extensions- Your Life Will Change

Did you get a chance to watch the WWDC 2016 Keynote or Platforms State of the Union today? Or were you lucky enough to be there in person? I wanted to bring to your attention what I believe will be one of the most impactful changes to your life as a developer, Xcode Extensions. In fact, Apple didn’t actually even mention it aloud during the keynote. You had to be closely watching the slides. Xcode Extensions appeared in the slide that Craig Federighi presented that was tag-cloud like in showing off all the new developer features available for iOS10. You can see it at minute mark 100:48. Xcode Extensions are also featured prominently on the What’s New page for Xcode 8:

xcode extensions

Later in the day, Xcode Extensions got a solid shoutout and description during the Platforms State of the Union, which you can see at minute mark 37:38.

Third Party Extensions Have Been Hard

Overall, during the keynote, I was a little surprised by the lack of new APIs available to iOS developers that were presented during this keynote. So when this slide appeared highlighting the new features available to iOS developers, I paused the video to scour each bullet. I’ve always loved customizing my development environment, and I’ve been a Alcatraz user for years. Alcatraz is a third-party extension (or plugin) manager for Xcode plugins. You can get anything from different source code highlighting templates, to extensions that significantly change the behavior of Xcode (usually in good ways). Unfortunately, creating Xcode extensions up until now has not been supported by Apple. I had the honor of working with Derek Selander on his monster 3-part raywenderlich.com tutorial on creating your own Xcode Plugin (I tech edited the article), and it revealed just how hard it is to create an Xcode extension, up until now. It’s a huge pain in the butt, and is really hard to do. You end up decompiling assembly code, running multiple instances of Xcode, and end up guessing a lot. And then, when new versions of Xcode are released, it’s possible that your plugin won’t work and will need to be updated.

Officially Supported Xcode Extensions Will Be Awesome

Other IDEs like Android Studio or Eclipse have APIs available for easily creating plugins. I’m assuming that Xcode Extensions are going to be just that, a way for developers to easily create Xcode extensions or plugins, through a Apple-supported API. Looking at the WWDC schedule, now that it’s been declassified, session 414 on Thursday is titled “Using and Extending the Xcode Source Editor.” It’s description contains the following:

We’ll also show you how to add commands to the source editor with new Xcode Extensions that you can distribute on the Mac App Store.

xcode extensions

During the Platforms State of the Union, the following were described as examples of Xcode extension functionality:

  • Content addition and deletion
  • Content transformation
  • Content selection
  • Pasteboard modification
  • In-file navigation

I was happy to see that Xcode extensions will be supported anywhere Xcode 8 runs – on both macOS Sierra and El Capitan. And furthermore, to be able to actually SELL THEM- something which isn’t possible with today’s backwards way of reverse engineering to create your own Xcode extension, will be awesome.

I think this will have a profound effect on our lives as developers. JetBrains and Reveal will no longer need to create standalone apps to help developers. Instead, they’ll be able to provide their awesome 3rd party functionality and features right within Xcode. I can’t wait for Xcode extensions.

Signed Libraries

Remember the hacked version of Xcode called XcodeGhost that was being distributed on third party download sites? Hackers had created a malicious version of Xcode that would inject malicious code into your iOS apps. Luckily, this only ever made it onto 3rd party download sites, which you shouldn’t use anyway, but people still do use because of slow overseas download speeds. The technique used to create Xcode extensions prior to Xcode 8 enabled this type of malicious hackery of Xcode. Luckily, with changes to Xcode 8, this doesn’t look like it will be possible any longer.

Xcode is now secured by System Integrity Protection, which means only trusted libraries and extensions can interact with the IDE and your code.

I interpret this to mean that the “old” way to hacking your own plugins into Xcode will no longer work, and you’ll instead have to play within the boundaries defined for official Xcode extensions.

WWDC week is so exciting. I’ll be going through videos each day and finding exciting and new APIs, features, and SDK changes to relay to you here, right on cleanswifter.com. What are you finding most exciting?

Happy cleaning.

Amazon Device Farm XCTest Tutorial

Amazon Device Farm has been on my radar for some time now. It offers the ability to remotely test your iOS apps on physical devices that are located somewhere else. Remember my post about cattle vs pets, and how your continuous integration box should not be a pet? I wanted to test out how well Amazon Device Farm XCTest actually worked, and report back here on my experience so you know how to do it as well.

Extensive List of Supported Devices

Not only does moving your automated testing to the cloud move you away from owning a “pet” build box, but it is also a cost effective way of testing your application across a large number of devices and OS versions. You won’t need to go buying old iOS devices on eBay. Amazon Device Farm supports all the way back to iOS 7. Ever accidentally upgrade your old iOS8 test device, only to realize that you can’t downgrade? With cloud based testing, you no longer need to worry about that. Amazon Device Farm supports many devices and OS versions, across iOS AND Android. You can take a look at the actual list of supported devices here.

The Goal

The goal is to evaluate and document getting a project executing Amazon Device Farm XCTest runs. I’m going to reuse my sample project from my CocoaHeads presentation, you can find it here. The project actually has three test targets setup: unit tests with XCTest, KIF tests, and FBSnapshotTestCase tests. My primary goal here get Amazon Device Farm XCTest running for unit tests. Getting the other two test targets running would be icing on the cake.

Running Amazon Device Farm XCTest Cases

TLDR

If you’ve gone through Amazon’s documentation, and are still stuck, there are two key things that I discovered that made this work for me:

  1. Properly create the .ipa file – I achieved this by Archive -> Save for Adhoc Deployment, etc.
  2. Properly create and find the .xctest folder – I had a hard time finding the .xctest folder to zip up. It’s buried in your Derived Data folder. I used the command line find command to locate it. Also, Amazon Device Farm requires you to include any class under test in the test bundle as well.

More details on those two steps later, so read on!

Overview of Amazon Device Farm

Amazon Device Farm breaks down your work into Projects and Runs. Amazon describes a Project as:

A project in Device Farm represents a logical workspace in Device Farm that contains runs, one run for each test of a single app against one or more devices. Projects enable you to organize workspaces in whatever way you choose. For example, there can be one project per app title, or there can be one project per platform. You can create as many projects as you need.

You basically have the freedom to use Projects as wide or as narrow as you want. I’m imagine a Project to be analogous to a Jenkins job. Just like a Jenkins job, a Project will consist of any number of Runs, which are basically just an execution of a given test script.

Access Your Account

To use Amazon Device Farm, you’ll need a Amazon Web Services account. Follow the instructions here for creating an account and setting it up. It’s a little more involved than just registering. You can actually create additional users within your AWS account to help segregate identities and access management, and it’s recommended that you do this to be used with Amazon Device Farm.

Once you have your account setup, sign in to the Amazon Device Farm at https://console.aws.amazon.com/devicefarm.

Create Your Project

Now that you’re logged in, you’ll need to create your first Project.

amazon device farm xctest

For this simple trial, I reused my Xcode project name for my Amazon Device Farm project name, “CocoaHeadsTestingPresentation”.

amazon device farm xctest

After you’ve created your project, you’ll see it in the list of projects:

amazon device farm xctest

Create Your Run

Select your project from the project list, and you’ll see a page that will eventually show your test runs. Since you have no test runs at this point, the page is empty. Now, you’ll create your first Run. Select Create a new run.

amazon device farm xctest

On Step 1, select the button that shows the Android and Apple icons to indicate that you are going to create a run for a native app.

Prepare And Upload Your Build

Now you need to prepare your application for execution within the Amazon Device Farm XCTest Run. This was a little tricky, and under documented in Amazon’s documentation, so I think this part will really help you. Jump over to Xcode, and Archive a build. Archiving a build will inherently enforce you to make sure you are building for device, as this is also a requirement of running Amazon Device Farm XCTest cases, since they actually run on devices. Once your build has finished archiving, select the build and **Export…* it from Xcode:

amazon device farm xctest

Select Save for Ad Hoc Deployment:

amazon device farm xctest

Ensure that the same identify as your build settings is used for code signing:

amazon device farm xctest

And then ensure that you Export one app for all compatible devices:

amazon device farm xctest

After selecting Next, follow through another couple prompts selecting the default options, and specify a location for your .ipa file and Xcode will code sign your application and create an .ipa file that will be uploaded to Amazon Device Farm. Go back into your browser that was left off on Step 1 of Create a new run in your first Amazon Device Farm project. Click Upload and navigate to your new .ipa file, and upload it.

You’ll know it was successful when Amazon Device Farm shows you information about the .ipa file:

amazon device farm xctest

Click Next step.

Specify and upload your XCTests

On Step 2, select XCTest as the test type, and then you’ll need to upload your XCTests. This was the hardest part of the process to get right, and also the most under-documented in Amazon’s documentation. Here’s a couple things to tweak and double check in Xcode to make sure you are setup:

  • Unit tests will compile with each build – This should be the default setting, but it’s worth double checking. Open the Build action settings for the scheme in the Scheme Editor. Verify that in the Run column, your test targets are checked. This means that when you type Command-B or even run your app, your tests will be compiled too. amazon device farm xctest
  • SUT Classes are included with the test target – In order for your classes under test (aka SUT) to be available within the test bundle, you need to ensure they are included with test target membership. In my project, WelcomeViewController.swift was a class under test, so I needed to actually add it to the test target membership since this isn’t technically required in a Swift world with the @testable annotation for importing modules. amazon device farm xctest
  • Build tests for devices – Your XCTest bundle must be built for device, not simulator. Select a device, rather than a simulator. amazon device farm xctest

Now that you are all setup, build your tests with Command-B. Now, you need to find the .xctest folder, compress it, and then upload it. I used a find command from Terminal to find it:

CocoaHeadsTestingPresentation|⇒ find . -type d -name '*.xctest'
./Carthage/Checkouts/ios-snapshot-test-case/DerivedData/FBSnapshotTestCase/Build/Products/Release-iphoneos/FBSnapshotTestCase iOS Tests.xctest
./Carthage/Checkouts/ios-snapshot-test-case/DerivedData/FBSnapshotTestCase/Build/Products/Release-iphonesimulator/FBSnapshotTestCase iOS Tests.xctest
./DerivedData/CocoaHeadsTestingPresentation/Build/Products/Debug-iphoneos/CocoaHeadsTestingPresentation.app/PlugIns/CocoaHeadsTestingPresentationTests.xctest
./DerivedData/CocoaHeadsTestingPresentation/Build/Products/Debug-iphoneos/CocoaHeadsTestingPresentation.app/PlugIns/SnapshotTests.xctest
./DerivedData/CocoaHeadsTestingPresentation/Build/Products/Debug-iphoneos/CocoaHeadsTestingPresentation.app/PlugIns/UserInterfaceTests.xctest
./DerivedData/CocoaHeadsTestingPresentation/Build/Products/Debug-iphonesimulator/CocoaHeadsTestingPresentation.app/PlugIns/CocoaHeadsTestingPresentationTests.xctest
./DerivedData/CocoaHeadsTestingPresentation/Build/Products/Debug-iphonesimulator/CocoaHeadsTestingPresentation.app/PlugIns/SnapshotTests.xctest
./DerivedData/CocoaHeadsTestingPresentation/Build/Products/Debug-iphonesimulator/CocoaHeadsTestingPresentation.app/PlugIns/UnitTestBundle2.xctest
./DerivedData/CocoaHeadsTestingPresentation/Build/Products/Debug-iphonesimulator/CocoaHeadsTestingPresentation.app/PlugIns/UserInterfaceTests.xctest
CocoaHeadsTestingPresentation|⇒ 

Remember, you want the bundle built for device, not simulator. That means you’ll use the one in the directory Debug-iphoneos, not Debug-iphonesimulator. The directory you need to compress to a zip file is:

./DerivedData/CocoaHeadsTestingPresentation/Build/Products/Debug-iphoneos/CocoaHeadsTestingPresentation.app/PlugIns/CocoaHeadsTestingPresentationTests.xctest

Compress it as a standard zip file (you can use Finder for this), and Upload it back in the Amazon Device Farm browser.

amazon device farm xctest

You’ll know the upload was successful when you see details about the XCTest zip file:

amazon device farm xctest

Click Next step to select your devices for the run.

Select Devices

One thing that annoyed me with setting up my first Run for an Amazon Device Farm XCTest was that when I finally got to the screen to select which devices to execute my Run on, the default list contained five devices, none of which were supported by my app. I would have rather’d some smarts on Amazon’s part to look at the lowest iOS version supported by my app, and then only show me “Top Devices” for that minimum iOS version.

Since the suggested list didn’t work for me, the first thing to do was Create a new device pool, which is what you should click to do that:

amazon device farm xctest

On the resulting screen, it’s obvious enough to select devices for a new “pool” – just make sure you select devices compatible with your app’s minimum version, because you won’t be prevented from doing that.

Once you create a pool of compatible devices, you’ll see a message about “100% Compatibility”

amazon device farm xctest

Select Next step to specify initial device state.

Specify Device State

In Step 4, you can specify other initial aspects of the device’s initial state, like other apps to load, a geographic location, or which locale to use:

amazon device farm xctest

I didn’t change any of these values from the default for my app.

Next, click Review and start run. The next page reviews your Run’s settings, and allows you to start it. Click “Confirm and start run” to start it.

Boom, that’s it! At this point, you’re basically done. If all is configured correctly, your Amazon Device Farm XCTest will be running. Congrats!

amazon device farm xctest

Observations of Amazon Device Farm XCTest

Here’s a couple observations from going through this setup for Amazon Device Farm XCTest.

Setup Is Too Manual

Next, I really need to investigate automating this. There’s no way I would frequently go through this in order to run my tests. It’s so much faster to run it from Xcode locally. There’s just too much clicking through prompts. Ideally, I want to connect this with my continuous integration server, so that’s the next thing I’ll look into.

Tests Are Slow

Just the simple act of running tests is really slow. For my sample project, that is totally bare bones, with ONE XCTest unit test, it took just over three minutes to run in Amazon Device Farm. That’s compared to like one second locally in Xcode. I’d like to try this with a larger code base and observe how long it takes. I hope the duration doesn’t lengthen with the size of the project or number of tests, but it probably will. At this point, I don’t think I’d put such long running tests in the middle of my code review workflow, but rather integrate these as nightly builds across a large number of devices.

Documentation Lacked Key Steps

The hardest part of getting this setup was getting the precise build settings right, both for the app binary ipa itself, and the .xctest bundle as well. Amazon totally glossed over this in their documentation, which really prompted me to write this article. The good news is that in Googling around, I noticed Amazon support employees actually answering questions on Stack Overflow, and also participating in Amazon’s own dedicated form for supporting Amazon Device Farm.

Wrap Up

I noticed that there is a command line reference for Amazon Device Farm. In order to integrate Amazon Device Farm XCTest with my continuous integration procedures, I know I need to test this with more complicated projects, and also automate it. I’ll do that in a future post.

Happy cleaning.

Asynchronous iOS Unit Test Tutorial

By default, iOS unit tests in Xcode execute just like any other method, from top to bottom, in serial order. This is fine most of the time. Occasionally though, you’ll find the need to write a unit test for asynchronous code. And with the prevalence of closures in Swift, writing an asynchronous iOS unit test will become even more common place.

The Method To Test

Consider this method under test:

class Parser {

  func parse(toParse: String, success: () -> Void, failure: () -> Void) {
    // code omitted
  }

}

It requires some imagination, but envision that this is some kind of complicated parsing routine that takes a long amount of time to complete. Depending on the outcome of parsing the input, either a closure success() or ‘failure()` (that are provided to the method) are guaranteed to be called at some asynchronous, non-deterministic point in the future.

Why This Is Hard To Test

At the core of this method, an input string will be parsed, probably on a different thread. In some cases that will pass, and in some cases that will fail. We need to figure out how to write tests to verify that.

Initially, one might think to try a test like this:

func testParse_Succeeds() {
  let toTest = Parser()
  toTest.parse("Something that will parse", success: {
    // do nothing, test will pass
  }) { 
    // if failure parsing, fail test
    XCTFail()
  }
}

The problem with this approach, is that assuming the long running code in parse(_:success:failure) is executed on another thread, `testParse_Succeeds()’ will likely complete before the parsing actually completes, thus never giving the test the chance to perform the actual verification. This will result in false positives, with no way to actually see the test fail.

asynchronous iOS unit test

The Solution – Expectations

There’s a really cool API provided in an XCTestCase extension that makes testing asynchronous code possible.

public func expectationWithDescription(description: String) -> XCTestExpectation

and

public func waitForExpectationsWithTimeout(timeout: NSTimeInterval, handler: XCWaitCompletionHandler?)

Here’s how you can use this with the previous example to verify the asynchronous code:

func testParse_Succeeds() {
  // 1
  let expectation = expectationWithDescription("Parsing Succeeds")
  let toTest = Parser()
  toTest.parse("Something that will parse", success: {
      // 2
      expectation.fulfill()
    }) { 
      // 3
      XCTFail()
  }

  // 4
  waitForExpectationsWithTimeout(1.0) { (_) -> Void in
  }
}

Looking at this line by line:

  1. Create an expectation for parsing to succeed
  2. When parsing succeeds, mark the expectation as fulfilled (and optionally perform any other verification)
  3. Explicitly fail the test if parsing does not succeed
  4. Tell XCTest to wait 1.0 second for the expectation to be fulfilled, or otherwise fail the test. (Good thing the timeout is configurable).

That’s it, now you can write an asynchronous iOS unit test!

Getting Cleaner

Now you know how to write an asynchronous iOS unit test. Take a minute to try out these sweet extensions on XCTestCase. I think you’ll find a lot of creative uses for them. Keep in mind, it doesn’t necessarily need to be long running code that needs this solution, but rather any code that is going to execute in an asynchronous fashion. Do you unit test your web service API calls?

Happy cleaning.

Other References: