Brian Gilham

Engineering leader, husband, and father

ios

Snapshot Tests + Blinking Cursors

It was Friday morning and I was already annoyed. One of our snapshot tests was failing. Randomly. Totally fine one minute, failure city the next. The snapshot was for a fairly benign login form. Present the view controller, call becomeFirstResponder on the first text field. What could possibly go wrong? Nothing in the test class seemed amiss. The build server was functioning normally. The test passed on my machine. Ready to give up, hours later, it finally dawned on me.

It was Friday morning and I was already annoyed. One of our snapshot tests was failing. Randomly. Totally fine one minute, failure city the next.

The snapshot was for a fairly benign login form. Present the view controller, call becomeFirstResponder on the first text field. What could possibly go wrong?

Nothing in the test class seemed amiss. The build server was functioning normally. The test passed on my machine. Ready to give up, hours later, it finally dawned on me.

The cursor. The damn blinking cursor.

For those not familiar, calling becomeFirstResponder on a UITextField immediately opens the keyboard and places a blinking cursor inside the text field. As it turns out, our snapshot test had been recorded when the cursor was visible. Well, maybe the build server was running a bit slower than usual, but that day the test ran when the cursor had blinked off. It was sheer luck we hadn’t run into it before.

Head, meet desk.


How to Disable App Transport Security

(Originally published at FiveMinuteWatchKit.com) 06/11/15: Steven Peterson has posted an update, showing how to explicitly define per-domain exceptions to ATS. The method described below should only be used as a last resort. If you eagerly fired up iOS 9 and watchOS 2 yesterday you may have noticed something strange, at least if your app relies on NSURLSession. In iOS 9, Apple is introducing App Transport Security. ATS enforces best practices during network calls, including the use of HTTPS.

(Originally published at FiveMinuteWatchKit.com)

06/11/15: Steven Peterson has posted an update, showing how to explicitly define per-domain exceptions to ATS. The method described below should only be used as a last resort.

If you eagerly fired up iOS 9 and watchOS 2 yesterday you may have noticed something strange, at least if your app relies on NSURLSession.

In iOS 9, Apple is introducing App Transport Security. ATS enforces best practices during network calls, including the use of HTTPS.

From the docs:

ATS prevents accidental disclosure, provides secure default behavior, and is easy to adopt. You should adopt ATS as soon as possible, regardless of whether you’re creating a new app or updating an existing one. If you’re developing a new app, you should use HTTPS exclusively. If you have an existing app, you should use HTTPS as much as you can right now, and create a plan for migrating the rest of your app as soon as possible.

If, however, your app relies on a non-HTTPS API outside of your control, this is problematic.

While the documentation also mentions:

App Transport Security (ATS) lets an app add a declaration to its Info.plist file that specifies the domains with which it needs secure communication.

It currently fails to describe the Info.plist keys needed to specify exceptions to ATS.

After a lot of searching last night, Steven Peterson found one solution: disabling ATS entirely. Simply include the following in your Info.plist file:

Obviously this is not ideal.

He also turned up two other keys: NSExceptionDomains and NSIncludesSubdomains. However, neither one of us has been able to figure out how to successfully use them.

With any luck the documentation will be updated in short order and we can update our apps to use ATS properly. Until then, Steven’s solution does just the trick.


Improve the Accessibility of Images in Your WatchKit App

(Originally published at FiveMinuteWatchKit.com, before the release of watchOS 2) While browsing through the WatchKit Framework Reference recently, I noticed an interesting class: WKAccessibilityImageRegion. Intrigued — and unable to find anyone else who had tried it out — I put together a quick example. Essentially, WKAccessibilityImageRegion allows you to add accessibility labels to specific parts of an image. Let’s say you have a photo of a group of people. By default, WatchKit allows setting the usual accessibility properties for the image as whole: label, hint, value, and traits.

(Originally published at FiveMinuteWatchKit.com, before the release of watchOS 2)

While browsing through the WatchKit Framework Reference recently, I noticed an interesting class: WKAccessibilityImageRegion. Intrigued — and unable to find anyone else who had tried it out — I put together a quick example.

Essentially, WKAccessibilityImageRegion allows you to add accessibility labels to specific parts of an image. Let’s say you have a photo of a group of people.

Tim Cook, Jony Ive, Dave Grohl, and Nathan Mendel

By default, WatchKit allows setting the usual accessibility properties for the image as whole: label, hint, value, and traits. By employing WKAccessibilityImageRegion, however, we can call out each person individually.

Let’s define an area around Tim Cook’s face. We simply create a new WKAccessibilityImageRegion object, set the frame and label, and add it to the group using setAccessibilityImageRegions:.

(Note: In this example, I’ve set the above photo as a background image on a group. While the documentation claims otherwise, I’ve been unable to get this working with a standalone WKInterfaceImage)

Voila! With one simple change, Voiceover not only calls out the image as a whole, but identifies Tim Cook within it.

Please excuse my hairy arm.

Whether you are working with static images, or generating images dynamically, WKAccessibilityImageRegion offers a simple way to improve the accessibility of the images in your WatchKit app.


Quick WKInterfaceImage Tips

(Originally published at FiveMinuteWatchKit.com, before the release of watchOS 2) Now that users everywhere are firing up their brand-new Apple Watch, I’ve been extremely busy working on updating a few apps. But I wanted to take a moment and share two quick image-related tips with you. Rounded Corners I see one question being asked on Stack Overflow again and again. How the heck do I round the corners on a WKInterfaceImage?

(Originally published at FiveMinuteWatchKit.com, before the release of watchOS 2)

Now that users everywhere are firing up their brand-new Apple Watch, I’ve been extremely busy working on updating a few apps. But I wanted to take a moment and share two quick image-related tips with you.

Rounded Corners

I see one question being asked on Stack Overflow again and again. How the heck do I round the corners on a WKInterfaceImage? It’s easy. Like many UI tricks in WatchKit, the secret is WKInterfaceGroup.

Simply nest your image inside a group sized to fit its content and set the cornerRadius on the group as desired. The group will clip your image, giving you the appearance of rounded corners on the image itself.

Why nest a WKInterfaceImage inside the group, instead of setting the group’s backgroundImage? That leads me to my second tip.

Image Placeholders

In many apps, developers opt to lazy-load their images. From a performance standpoint, this is a wise choice. Many apps, however, simply show a large gap in the content until the image loads. This can be confusing to users. At best, they guess something might load there eventually. At worst, your layout appears broken.

The solution, then, is to display some sort of placeholder for the image. In many of the apps I’ve worked on we accomplish this by nesting a WKInterfaceImage inside a WKInterfaceGroup and using the group’s backgroundImage to display a placeholder graphic or animation. Once the image inside the group is set, it completely covers the background image.

It’s an extremely simple technique, but it’s proven to be a huge help for users.


Submitting Your WatchKit App

(Originally published at FiveMinuteWatchKit.com, before the release of watchOS 2) Now that submissions are open for WatchKit extensions, many developers are running into issues and bugs that didn’t present themselves when working in the simulator. This post is an attempt to aggregate tips and solutions to common problems. Of course, be sure to check out Apple’s official guidelines. Many of these tips are gleaned from posts in the dev forums and my own experience submitting a WatchKit app.

(Originally published at FiveMinuteWatchKit.com, before the release of watchOS 2)

Now that submissions are open for WatchKit extensions, many developers are running into issues and bugs that didn’t present themselves when working in the simulator. This post is an attempt to aggregate tips and solutions to common problems. Of course, be sure to check out Apple’s official guidelines.

Many of these tips are gleaned from posts in the dev forums and my own experience submitting a WatchKit app. I’ll update the list as time goes on. Have something to add? Give me a shout.

iTunes Connect

  • The section for uploading your Apple Watch screenshots and app icon will only appear once you have uploaded your first WatchKit build.

Screenshots

  • Ensure your screenshots are 312x390px.
  • Screenshots should depict only your app’s interface — and use the entire space to do so.
  • Do not frame your screenshots in an Apple Watch “frame” or image.
  • Do not refer to your Apple Watch app in your iPhone app screenshots.
  • Do not add additional text/promo material to your Watch screenshots.
  • Despite the earlier requirement for screenshots to be taken only on device, you can now submit screenshots from the simulator. To do so, simply hit ⌘ + S or File > Save Screen Shot. The resulting images will be saved to your desktop, by default. Thanks to reader Jeff Rames for prompting this tip.

App Icons

  • Ensure your app icon does not include an alpha channel. This will result in an automatic rejection during validation and produces a cryptic error message for some.
  • If you encounter the error file names must match pattern “*@x.png”, ensure your Watch app icon is contained in an asset catalog included with the Watch app target. You cannot share an asset catalog between your iPhone app and your Watch app.
  • Make sure your icon does not contain a black background. This will cause it to blend into the background on the phone and has been a source of rejections for some.

Deployment Targets

  • While your iPhone app may support versions of iOS lower than 8.2, your WatchKit extension’s deployment target must be 8.2 or higher.
  • If you employ a framework in your WatchKit extension, your iPhone app’s deployment target must be 8.0 or higher. This is due to your WatchKit extension being bundled with your host app.

Build Process

  • If your build process happens outside of Xcode, or you have a custom build script, be sure your final zipped IPA follows the conventions described in this dev forums post.

Version & Build Numbers, Bundle Identifiers

  • Ensure the build and version number for your iPhone app, WatchKit extension, and Watch app are exactly the same.
  • Your WatchKit extension’s bundle identifier should use the bundle identifier for your iPhone app as a prefix. For example: if your iPhone app’s bundle identifier is com.company.AppName, your WatchKit extension’s bundle identifier should be something along the lines of com.company.AppName.watchkitextension.
  • David Olesch, iOS Lead at Jackrabbit Mobile, adds: “also make sure your app target and watch app target have the same display name. I got rejected because I forgot the other.”
  • An Tran adds: Ensure your WatchKit app does not include “WatchKit” or “Apple Watch” in the name.

Provisioning

  • Remember that your WatchKit extension requires its own app ID and provisioning profile.
  • From the ever-awesome Nick Arnott: If your WatchKit app fails to code sign with Xcode 6.3, you now need a profile for the WatchKit app in addition to the WatchKit extension.

App Store Description

  • If you reference the Apple Watch in your App Store description, be sure to follow Apple’s guidelines for capitalization and such. A few developers have faced rejections due to not following the guidelines.
  • As a quick reference, Apple Watch should always be written in English with an uppercase A and an uppercase W. It should not be UPPERCASE, lowercase, or use the Apple logo in place of the word “Apple”.

Performance

  • Ensure, as much as possible, that your app feels responsive in the simulator. If it feels at all sluggish in the simulator, it will be doubly so on device. At least one developer has been rejected for this.
  • If you are making use of openParentApplication:reply: I strongly suggest you follow the advice given in this post. In my testing with an actual Watch, openParentApplication:reply: was extremely unreliable without using the tips in that post. Another Watch labs participant has confirmed that to be the case for them as well. At least one developer has had their app rejected due to openParentApplication:reply: calls being killed on device before completion.

Swift

  • If you use Swift in your iPhone app, be sure to set the “Embedded Content Contains Swift” build setting to NO for your frameworks and extensions and YES for your iPhone app target.

App

  • Duplicating the functionality of a clock face, or displaying the time in a way that could be confused with one, will result in a rejection. Judging by the posts in the developer forums, this rule was supposed to appear in the HIG but was missed. It will be added soon.
  • Looking at this post in the dev forums, it appears your Watch app cannot exceed 50mb in size.

How to use Handoff in your WatchKit app

(Originally published at FiveMinuteWatchKit.com, before the release of watchOS 2) Considered by many to be an optional feature in their iOS/OSX apps, I believe Handoff will play an integral role in our WatchKit apps. Need the user to sign in before they can use your Watch app? Handoff. Want to present content a big too long for the Watch? Handoff. Need specific OS-level permissions for your Watch app? Asking the user to Handoff to this phone and pop open an interface specifically asking for/setting those permissions up.

(Originally published at FiveMinuteWatchKit.com, before the release of watchOS 2)

Considered by many to be an optional feature in their iOS/OSX apps, I believe Handoff will play an integral role in our WatchKit apps.

Need the user to sign in before they can use your Watch app? Handoff. Want to present content a big too long for the Watch? Handoff. Need specific OS-level permissions for your Watch app? Asking the user to Handoff to this phone and pop open an interface specifically asking for/setting those permissions up. When I see developers complain about WatchKit’s limitations, 99% of the time I think their problems could be easily solved by handing off to the iPhone and presenting custom UI.

Let’s take a look at how to implement it.

Give me your hand

To broadcast your app’s current activity, simply call updateUserActivity:userInfo:webpageURL: any time during the execution of your WKInterfaceController’s code. You should not create an NSUserActivityobject, as you would on iOS or OSX.

The updateUserActivity parameter requires an NSString describing the type of activity you’d like to broadcast in reverse-DNS format, by convention. It cannot be nil or an empty string.

userInfo expects an NSDictionary, as you might expect. This is where you can send along any data or state information the iPhone might need to pick up what the user is doing on the Watch. The contents of userInfo must be one of the following types: NSArray, NSData, NSDate, NSDictionary, NSNull, NSNumber, NSSet, or NSString.

Finally, you can optionally specify webpageURL to let the receiving iPhone continue your activity in Safari. Any URL scheme other than http or https throws an exception.

Invalidation

An activity will be broadcasted until the OS decides to kill it, you call updateUserActivity:userInfo:webpageURL: with a new activity, or call invalidateUserActivity to manually invalidate it.

Glances & Notifications

It isn’t obvious as first, but you can also call updateUserActivity:userInfo:webpageURL: from your Glance and custom notification interfaces. If you specify an activity that is recognized by the phone, it will allow the user to continue it there. But you can also “continue” the activity in your WatchKit app.

When your app is launched from a Glance or custom notification interface, any activity broadcasted from those interfaces will be passed to your root interface controller in the handleUserActivity: method.

If your app uses a page-based interface, handleUserActivity: will be called for each controller that is part of the initial interface. You don’t need to register the activity type in your WatchKit app for this to work. When overriding this method, don’t call super.

Simulator

As usual, the iOS simulator does not support testing Handoff. Unless you have received an invite to one of the developer labs and can test on actual hardware, you’ll have to hope for the best or wait for the Watch to be released.


How to debug an iOS app while the associated WatchKit app is running

(Originally published at FiveMinuteWatchKit.com, before the release of watchOS 2) Since the introduction of openParentApplication:reply: I’ve seen developers struggle to debug their iOS app while running a WatchKit app in the simulator. If you haven’t worked with extensions before, the solution may not be obvious. Here’s how to pull it off: Build & run your iOS app in the simulator. Wait for it to finish launching, then hit the stop button in Xcode.

(Originally published at FiveMinuteWatchKit.com, before the release of watchOS 2)

Since the introduction of openParentApplication:reply: I’ve seen developers struggle to debug their iOS app while running a WatchKit app in the simulator. If you haven’t worked with extensions before, the solution may not be obvious. Here’s how to pull it off:

  1. Build & run your iOS app in the simulator.
  2. Wait for it to finish launching, then hit the stop button in Xcode.
  3. Switch your active target to your WatchKit app, build, and run it.
  4. When the Watch app has finished launching, tap your iOS app’s icon in the main simulator window.
  5. In Xcode’s menu bar select Debug > Attach to Process.
  6. Select your iOS app from the list. Chances are you’ll find it under Likely Targets.

If you take a look in the Debug Navigator (⌘ + 6), you’ll notice the debugger is now attached to both your iOS app and the WatchKit extension. You can click on each target in the navigator window to select which console output you’d like to view. If Xcode encounters a breakpoint it will automatically switch to the correct target.

Keep in mind that you’ll have to repeat this process each time you re-run your WatchKit app.


One weird trick to “fix” openParentApplication:reply:

(Originally published at FiveMinuteWatchKit.com, before the release of watchOS 2) A quick perusal of the Apple Developer Forums shows more than a few developers have experienced strange bugs when calling openParentApplication:reply: in a WKInterfaceController. Most often, it seems, when making multiple requests in quick succession. In an app I worked on recently we were hitting all sorts of strange behaviour. Sometimes openParentApplication:reply: would mysteriously be called twice. Every once in a while the reply block would fail to ever be called — despite being certain the delegate was functioning correctly and completing work in a background task.

(Originally published at FiveMinuteWatchKit.com, before the release of watchOS 2)

A quick perusal of the Apple Developer Forums shows more than a few developers have experienced strange bugs when calling openParentApplication:reply: in a WKInterfaceController. Most often, it seems, when making multiple requests in quick succession.

In an app I worked on recently we were hitting all sorts of strange behaviour. Sometimes openParentApplication:reply: would mysteriously be called twice. Every once in a while the reply block would fail to ever be called — despite being certain the delegate was functioning correctly and completing work in a background task.

Frustrating, to say the least.

However, I’ve discovered a solution that has made communication with the iPhone rock-solid. Essentially you have to begin — and end, after two seconds — an empty background task right at the beginning of the delegate method. It should be the absolute first thing you do. Afterward, kick off a background task for the real work.

Here’s an example:

The theory is the bogus background task prevents the OS from killing your app immediately. I honestly couldn’t tell you for sure.

All I know is it’s made openParentApplication:reply: far more reliable.


How to round the corners of a WKInterfaceImage

(Originally published at FiveMinuteWatchKit.com, before the release of watchOS 2) The Human Interface Guidelines recommend using black as the background colour across your entire app. Why? It allows the background to blend in with the bezel surrounding the display, giving the illusion there isn’t a bezel at all. In my experience, rounding the corners of your full-width images can help this illusion. A quick look at the WKInterfaceImage class reference, however, might make you think this isn’t possible.

(Originally published at FiveMinuteWatchKit.com, before the release of watchOS 2)

The Human Interface Guidelines recommend using black as the background colour across your entire app. Why? It allows the background to blend in with the bezel surrounding the display, giving the illusion there isn’t a bezel at all.

In my experience, rounding the corners of your full-width images can help this illusion. A quick look at the WKInterfaceImage class reference, however, might make you think this isn’t possible. Where’s the setCornerRadius: property?

It isn’t possible. Not directly, anyway.

Poking around, you’ll notice that WKInterfaceGroup does have a setCornerRadius: method. Place your WKInterfaceImage inside a group, set the corner radius, and you’ll notice it clips the corners of your image beautifully.


Chronicons Site Header

Just a small portion of the new design for http://chronicons.com. The site does a much better job of explaining what the icon set is, who it’s for, and why it’s beneficial. I’m psyched.


Chronicons Edition #1

For my first shot on Dribbble I’m excited to debut an icon set I’ve been working on for the last few months called Chronicons. They’re designed and tested specifically for the Apple Watch.

I’ll be releasing the full set on March 4th, but I wanted to share a tidbit here first. They’re fully compliant with the HIG and I hope they’ll be as helpful for others as they have been for me.

I’d appreciate any feedback you all may have and I look forward to participating a bunch more on Dribbble as time goes on.

UPDATE: Chronicons Edition #1 is now available! Check it out at http://chronicons.com. $5 off for a limited time.