Select Page

Using Taptic Engine on iOS

by | September 28, 2015 | Development

We’ve all been waiting for the arrival of iPhone 6s and it’s 3D touch capabilities. We have already covered detecting 3D touch with UIGestureRecognizer objects, but Apple’s implementation of Peek and Pop gestures elegantly use the Taptic Engine to make user experience even better.

To implement those gestures in an Apple-compliant way, you have to use UIKit’s Peek and Pop and implement UIViewControllerPreviewing protocols. That is all nice and good, but what if you wanted a more direct access to the Taptic Engine? For example: to play a music tone with it. Why? Because you can.

We’ve known about AVFoundation vibrate API’s for a while and they are still valid, but they do not interact with the Taptic Engine directly and only allow you to “play” vibrations. As Apple has demonstrated on iPhone 6s presentation, the Taptic Engine can create a much more subtle and elegant vibration, so we are mostly looking to reproduce that behaviour instead of using the normal vibrations.

Sadly, as it turns out that there is no public API to do this in iOS 9 and iOS 9.1 (but never say never).

But that does not mean it is impossible. There are of course, private API’s.

Before you continue reading this post, there is a small disclaimer:

Using private API’s may cause your application to be rejected on the App Store and should not be used in production, as it might change in the future. This post is a small research, because I am interested in how things work, not to get around Apple’s rules. By using the information in this post, you agree that you take full responsibility for any damages done using the information, including hardware malfunction, App Store rejection and any other legal actions by Apple or anyone else.

Investigating iOS 9 Runtime Headers I had found that there is a new private class available in UIKit called _UITapticEngine. It can be easily retrieved from a property on UIDevice, which keeps a reference to one, if available. To get to it you may use Objective-C runtime methods: NSSelectorFromString and performSelector:withObject:. The instance method you should call is called: _tapticEngine.

This cannot be done in Swift easily due to it’s security features, but we will use a different approach. Keep reading.

We have a reference to _UITapticEngine object now and there are three methods on it that interest us (again search runtime headers for them):

  • – (void)actuateFeedback:(int)arg1;
  • – (void)endUsingFeedback:(int)arg1;
  • – (void)prepareUsingFeedback:(int)arg1;

The most important one is called actuateFeedback: and will actually activate the Taptic Engine. It takes an integer as an object, so we can quickly call it with performSelector:

Once you run this code, you will notice that the device will vibrate normally. However, we want to feel the small vibrations from Taptic Engine, not default one. That is when the fun begins, since we do not know the exact argument we need to send into this method, as Apple probably uses an internal constant. There are only Int32 possibilities and brute forcing probably is a bad idea.

Update (thanks to Peter Baral): The code above of course will not call the method with specified argument number, because the method takes int as an argument, not an object (NSNumber). NSInvocation should be used instead.

To get a better idea for correct value, we will try another way, using Method Swizzling. To make it easier, we will use a fantastic library called Aspects. Aspects library adds the ability to add code either before or after a method is called. We can easily add a console log after actuateFeedback: method is called.

Great, all calls to this method should now output the arguments to console! Now all we need is to implement the UIKit Peek and Pop and for that we’ve used Apple’s ViewControllerPreviews: Using the UIViewController previewing APIs sample code.

After running this code, the console simply tells you that Apple uses code 1001 for Peek and 1002 for Pop. Sending these numbers into the actuateFeedback method will correctly activate the Taptic Engine.

So we are done! But wait, what about Swift?

This cannot be done only using Swift, but you can use Objective-C bridging header, to get around this issue. We can simply add additional Objective-C header for UIDevice class and we created a fake UITapticEngine header. These only serve as placeholders, so we can call the methods directly without compiler errors. The actual methods are already implemented by Apple, so we do not need implementation files. The header file for UIDevice looks something like this:

The name of _tapticEngine method is important and should not be changed. The header file for UITapticEngine should look something like this:

Add both headers to Objective-C bridging header of your Swift project and then you can use the taptic engine with this code:

For convenience you can also define 1001 and 1002 as constants or enum, to make it easy to call the function.

This code of course triggers warnings, but you can either ignore them or add:

Check out complete source code and example project is available on GitHub.

dal_face  DAL RUPNIK
  Founder of Unified Sense, tweeting at @thelegoless.