73

Starting in iOS 5, users are able to create custom vibration patterns for alerts and rings. The following screenshot shows the UI for creating one (Contacts app in iOS 6):

Screenshot of UI for creating custom vibrations in iOS 6

I've been searching around, including the documentation, and I cannot find any public APIs that expose the creation or playback of custom vibrations. The closest thing is to use the AudioToolbox framework to play a short, constant vibration:

AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);

Does anyone know if there are APIs for custom vibrations? It doesn't necessarily have to be public APIs. I'm curious to know what the Contacts app uses. Does anyone know?

P.S. Others have suggested _CTServerConnectionCreate in CoreTelephony (example). I tried it, but couldn't get any vibration going for some reason.

October 2015 Update:

There are new private APIs in iOS 9 to interact with the Taptic Engine in compatible devices. See Taptic in iOS 9 for more.

Community
  • 1
  • 1
Andrew
  • 2,770
  • 1
  • 22
  • 29
  • 1
    [Core Haptics](https://developer.apple.com/documentation/corehaptics) is coming in iOS 13! It's currently in beta. See "Creating Custom Haptic Patterns" [here](https://developer.apple.com/design/human-interface-guidelines/ios/user-interaction/haptics/) for more info. – peacetype Jul 23 '19 at 00:41

2 Answers2

104

After serval hours' digging in the Contact App, I have figured out how it works.

ABNewPersonViewControlle invoke some class in ToneLibrary framework to do this.

The call stack looks like this:

0   CoreFoundation                  0x3359a1d4 CFGetTypeID + 0
1   CoreFoundation                  0x33596396 __CFPropertyListIsValidAux + 46
2   CoreFoundation                  0x33517090 CFPropertyListCreateData + 124
3   AudioToolbox                    0x38ac255a AudioServicesPlaySystemSoundWithVibration + 158
5   ToneLibrary                     0x35a7811a -[TLVibrationRecorderView vibrationComponentDidStartForVibrationRecorderTouchSurface:] + 38
6   ToneLibrary                     0x35a772b2 -[TLVibrationRecorderTouchSurface touchesBegan:withEvent:] + 342
7   UIKit                           0x3610f526 -[UIWindow _sendTouchesForEvent:] + 314
8   UIKit                           0x360fc804 -[UIApplication sendEvent:] + 376

After search "AudioServicesPlaySystemSoundWithVibration" on the web , I found nothing.

So I decide to look into it myself. It's a private function in AudioToolbox framework.

the declaration of the function is like

void AudioServicesPlaySystemSoundWithVibration(SystemSoundID inSystemSoundID,id arg,NSDictionary* vibratePattern)

"inSystemSoundID" is SystemSoundID .just like "AudioServicesPlaySystemSound", pass "kSystemSoundID_Vibrate".

"arg" is not important, pass nil to it , everything will still work fine.

"vibratePattern" is a pointer of "NSDictionary", the Contact App pass into { Intensity = 1; OffDuration = 1; OnDuration = 10; } for recording user input.

But only call this function will make a vibration never stop. So I have to found some function to stop it.

The answer is "AudioServicesStopSystemSound". It's also a private function in AudioToolbox framework.

the declaration of the function is like

void AudioServicesStopSystemSound(SystemSoundID inSystemSoundID)

I guess the Contact App use AudioServicesPlaySystemSoundWithVibration in touchesBegan method, and AudioServicesStopSystemSound in touchEnd method to reach this effect.

TLVibrationController will manager a vibrate pattern object to record the process you input.

At last it generate a dictionary to pass into AudioServicesPlaySystemSoundWithVibration to replay the whole process like below:

NSMutableDictionary* dict = [NSMutableDictionary dictionary];
NSMutableArray* arr = [NSMutableArray array ];

[arr addObject:[NSNumber numberWithBool:YES]]; //vibrate for 2000ms
[arr addObject:[NSNumber numberWithInt:2000]];

[arr addObject:[NSNumber numberWithBool:NO]];  //stop for 1000ms
[arr addObject:[NSNumber numberWithInt:1000]];

[arr addObject:[NSNumber numberWithBool:YES]];  //vibrate for 1000ms
[arr addObject:[NSNumber numberWithInt:1000]];

[arr addObject:[NSNumber numberWithBool:NO]];    //stop for 500ms
[arr addObject:[NSNumber numberWithInt:500]];

[dict setObject:arr forKey:@"VibePattern"];
[dict setObject:[NSNumber numberWithInt:1] forKey:@"Intensity"];


AudioServicesPlaySystemSoundWithVibration(4095,nil,dict);

So if you want a custom vibrations in iOS. Use AudioServicesPlaySystemSoundWithVibration and AudioServicesStopSystemSound.

Kevin Cao
  • 1,332
  • 1
  • 8
  • 13
  • 5
    Notice: AudioServicesPlaySystemSoundWithVibration is only available on iOS6. – Kevin Cao Oct 24 '12 at 10:57
  • I'm struggling to make arbitrary vibrations to work. The approach you described allows to start and stop a vibration with a simple pattern (onduration/offduration) but if I want Contacts.app-like functionality, this isn't enough. Making several calls in a row or using NSTimer for staggering calls doesn't work. I guess there are other undocumented params that I'm missing. – Evgeny Shadchnev Dec 27 '12 at 09:50
  • 1
    More about "VibePattern". And I edit the answer to post some code. – Kevin Cao Jan 05 '13 at 05:50
  • Thanks a lot, that's exactly what I've been missing. I made it work using NSTimer but it's hacky and may not be reliable. Thanks again! – Evgeny Shadchnev Jan 05 '13 at 21:36
  • **Undefined symbols for architecture armv7: "AudioServicesPlaySystemSoundWithVibration(unsigned long, objc_object*, NSDictionary*)", referenced from:** – Jonny Mar 13 '13 at 10:04
  • 2
    @Jonny FOUNDATION_EXTERN "AudioServicesPlaySystemSoundWithVibration(unsigned long, objc_object*, NSDictionary*) will do the trick. Don't forget to also link against AudioToolbox.framework – JonasG Jun 09 '13 at 11:09
  • 9
    Now Apple reject AudioServicesPlaySystemSoundWithVibration on submit ion. – Max Tymchii Jun 22 '13 at 14:41
  • 3
    Is there any way to implement a custom vibration without getting the app rejected by Apple? – bobsacameno Aug 10 '14 at 11:29
  • @KevinCao is there any way to check vibrate in ON/OFF programmatically? – Shohrab Aug 03 '16 at 15:39
  • 1
    Can we somehow run this function dynamically to bypass the review? – kelin Dec 26 '16 at 11:15
  • 1
    You could maybe ask your server if the `app_has_been_approved == true` and if it has, use your custom functionality and otherwise fallback to the regular vibration. After your app gets approved then send back `app_has_been_approved` as true from your server. If they found out (**which they will**) I'm sure you'd get booted from the developer program and your apps with you. – Will Brickner May 12 '17 at 08:27
  • 2
    this will only make you receive a nice mail like this: ** We have discovered one or more issues with your recent delivery for "Appname". To process your delivery, the following issues must be corrected: Non-public API usage: The app references non-public symbols in Appname: _AudioServicesPlaySystemSoundWithVibration ** – Edoardo Aug 22 '17 at 14:27
4

There's a project called HapticKeyboard that does this. See: http://code.google.com/p/zataangstuff/source/browse/trunk/HapticKeyboard/Classes/HapticKeyboard.m?r=93

Specifically, look at the last set of functions

_CTServerConnectionSetVibratorState(&x, connection, 0, 0, 0, 0, 0);

There are two problems:

  1. I doubt you'd get your app allowed into the app store. Apple will see this as a battery drain for users and they are pretty strict about the user experience
  2. This may require jailbreaking. There have been several new iOS versions since I've last played with this, so it's hard to tell
Jonathan
  • 1,498
  • 2
  • 20
  • 41
  • Thanks for your answer. I have indeed already looked at HapticKeyboard (which is 4 years old) before I asked my question. I tried to trigger vibration as described in the code, but it did not work for me. I am looking for APIs Apple used to implement custom vibrations in Contacts, as seen in the screenshot. – Andrew Oct 23 '12 at 16:59
  • 1
    Apple's HapticEngine and all the vibration responses on other devices are actually a battery drain. Btw, the code link is broken. – kelin Dec 26 '16 at 11:13