Haptic Feedback Done Correctly

The latest iPhones take vibration feedback to a new level.

About a year ago, I wrote on my secondary blog at length about Apple's decision to remove the 3.5mm headphone jack from the iPhone, as well as a follow-up post about what this removal made possible in the iPhone 7. I'll spare you the details of the full post, the TLDR was this: Apple's new 'Taptic Engine' was an incredibly significant feat of engineering, and promised to re-define 3D Touch as it first existed on the iPhone 6s — if developers would get on board.

How much progress have we made a year later? The answer is kind of a mixed bag: While many popular apps have been updated to take advantage of the new APIs, the bulk of apps from small developers have not been. This is probably because the second-gen Taptic Engine was only available on Apple's flagship iPhone 7 and in order for mobile haptics to true realize their potential, 100% adoption is a must. 

Now that Apple's new Taptic Engine is available on 1/2 the devices in the iPhone lineup, I'd imagine that more and more people start taking advantage of these new APIs. In this post, I'm going to explain the best ways that one might go about doing that.

Understanding Haptic Feedback

Before I begin explaining the differences in vibration hardware and the different capabilities of the latest iPhone devices, I thought I'd begin with a brief history of vibration hardware in Apple's lineup. For a more detailed account of this history, see my other post on this subject.

Before 3D Touch

In the early days of the iPhone Android wars, one of the biggest initial criticisms of iOS was it's general lack of haptic feedback — a feature which causes your phone to vibrate when a control is interacted with. This criticism disappeared quickly as it became obvious that without incredibly precise & directional vibration, haptic feedback was more of a nuisance that it was a user experience enhancement. Apple's documentation actively argued against developers incorporating vibration as a form of haptic feedback, and the API's for triggering a vibration showed it — vibrations were  like sound effects in iOS, making Apple's intention for the feature abundantly clear: this is *not* haptic feedback. Don't use it that way.

After 3D Touch

When Apple's first generation Taptic Engine was release as part of 3D Touch on the iPhone 6s, things began to change a little. 3D Touch interactions added depth to iOS, and Apple needed a good way to differentiate between a long press and a hard press. Thus: the Taptic Engine was born, which allowed for shorter, more precise vibrations. Despite this, not much has changed for developers: this first-gen Taptic Engine was only for use by 3D Touch, and not really for anything else. Apple's vibration API didn't change much — a few more sound effects were added to correspond to the newly available shorter vibrations, but the message was still the same: don't use these vibrations in place of actual haptic feedback, which is *not* a sound effect.

After iPhone 7

When the second generation Taptic Engine was released with the iPhone 7, that's when things truly changed. The second generation Taptic Engine was an industry first: a vibration motor more precise and directional than any before it. This iPhone could produce normal vibrations, but it could also produce many other kinds of feedback. Apple made use of this new Taptic Engine all over iOS 10, and I can't really explain these effects to you in writing — you just need to try then in person. Interacting with the iPhone 7 felt truly futuristic, as if the digital controls you're interacting with were also physical. It made 3D Touch go from a boring additional feature to an intriguing UI possibility, if only it were implemented consistently and correctly.

Implementing Haptic Feedback

UIFeedbackGenerator

Apple provides 3 kinds of haptic feedback, each of which is controlled via subclass of UIFeedbackGenerator:

  1. UIImpactFeedbackGenerator, which is used "to indicate that an impact has occurred. For example, you might trigger impact feedback when a user interface object collides with something or snaps into place". Think of the notification tray in iOS 10.
  2. UISelectionFeedbackGenerator, which is used "to indicate a change in selection". Think of say, the UIDatePicker Control, which slightly "taps" the user when a new selection is made.
  3. UINotificationFeedbackGenerator, which is used "to indicate successes, failures, and warnings". Think pre-pompts, modal alerts, and in-app banner notifications.

Each of these three subclasses have different purposes, and it's important that you match up your use case with the definitions above. Otherwise, you may end up confusing your user who probably has pre-concieved subliminal notions about what these vibrations mean, as they're used all through out iOS. If you haven't already, take a look at the feedback section of the Human Interface Guidelines, which go into great detail about which kinds of feedback to use and when.

Checking for Availability

Unfortunately, there is no official API to determine whether UIFeedbackGenerator APIs actually work on a given device. You won't get a run-time error on an older device — the vibrations simply won't fire. If you don't mind dabbling in private APIs, take a look at this stack overflow post, but note that these unofficial APIs could change at anytime without official definitions, and that using them in production might get your app rejected from the App Store.

Sending Feedback

Sending feedback is pretty straight forward:

  1. Instantiate the feedback object, and configure it as needed. Each of the three feedback generator classes provide designated initializers, and allow you to customize the feedback with specific styles and weights. See the documentation for each class for more specific information on initializers and customization options.
  2. Send Feedback. Each of the three feedback subclasses has its own instance method for sending feedback to the user. See the documentation of each class for more information.

Taptic Engine State

One of the way's the Taptic Engine is able to send such precise notifications is because of its built-in state and power management system. The Taptic Engine can send near instant feedback when it's ready, but it also regularly goes into an idle state to persevere battery power. You don't have any control over how and when this happens, nor can you actually tell whether or not the Taptic Engine is idle or not — Apple does all of this for you. As such, there are a few things you should take into account:

  1. Call -prepare on feedback generator objects before you send feedback. If you plan on sending a lot of feedback back-to-back, or are trying to align your feedback with other UI cues like alerts or sound effects, remember to call the -prepare method on your feedback generator objects. This method wakes up the Taptic Engine incase it was idle, and ensures minimal latency between your feedback message and the actual vibration. Note that -prepare doesn't keep the Taptic Engine awake for ever — it can slip back into idle if no feedback is sent, so don't call it too early. Sending -prepare to a feedback generator object when the Taptic Engine is already active doesn't do anything, but you have no way of knowing whether it is idle or not at any given moment.
  2. Don't retain unnecessary references to UIFeedbackGenerator objects. Keeping the Taptic Engine on and ready for action uses battery power. As such, iOS uses the number of feedback generator objects in memory, among other things, to decide how and when the Taptic Engine should switch to its idle state. Don't maintain references to feedback generator objects if you don't need to use them again really soon — you'll waste a lot of unnecessary battery power

Don't substitute UIFeedbackGenerator with older APIs

Remember that UIFeedbackGenerator only works on devices with the second generation Taptic Engine. As of this writing, that list is limited to the iPhone 7, the iPhone 8/8 Plus, and the upcoming iPhone X. Don't substitute UIFeedbackGenerator use with the older vibration APIs —they aren't a good substitute. Just don't send any feedback at all.

UIKit's Built-In Support

Many of UIKit's built-controls already use UIFeedbackGenerator. If you're using Apple's controls, don't try and replicate their functionality, you'll just end up sending too much feedback. Build your app without feedback first and play around with it. Add feedback only where you think its missing, per Apple's HIG guidelines. 

Conclusion

Incorporating haptic Feedback is a great way to make your apps feel more "alive". Users may not actually realize that they're noticing, but they'll certainly be happier. As more and more people get their hands on the latest iPhones, make sure you support the UIFeedbackGenerator APIs. This blog is all about adding that extra level of fit and finish to your code and shipped products, and this is a great example of a feature that people want, even if they don't yet realize it.