Swift Regex Deep Dive
iOS MacOur introductory guide to Swift Regex. Learn regular expressions in Swift including RegexBuilder examples and strongly-typed captures.
When I put up the draft of my Digital Crown blog post for internal review, my friends MarkD and Step both saw the little one-liner I’d thrown in about making the Taptic Engine “click” as you scroll the Digital Crown. The response was, “Hey! there are other haptics?! Call ‘em out!” But to their disappointment, I declined, saying it was a post for another day.
Today is that day! Let’s take a quick look at the Taptic Engine, how it works and how to make it work.
The Taptic Engine is an electromagnetic linear actuator. Under the hood, a varying electric current moves a magnetic mass around. Unlike typical “offset mass rotation” vibration motors, it’s possible to have a nearly instantaneous result. There’s no “spin up” time. Likewise, the mass stops pretty quickly. It’s not just quiet, it’s nearly silent. And finally, it is possible to make them directional. All of these are distinct advantages over offset mass motors.
Creative Electron published this video describing the linear actuator in the iPhone 6 Plus. It’s similar enough to the Taptic Engine to give a sense of how it operates.
You can also see iFixit’s torn-apart Taptic Engine. Fair warning: It’s not for the faint of heart; those iFixit guys can be pretty violent.
In the first version of WatchKit, the WKInterfaceDevice
class consisted of some device metrics and the image cache. In WatchKit 2, it takes on a new role by adding the -playHaptic:
method. This method takes one argument, an enum WKHapticType
.
typedef NS_ENUM(NSInteger, WKHapticType) {
WKHapticTypeNotification,
WKHapticTypeDirectionUp,
WKHapticTypeDirectionDown,
WKHapticTypeSuccess,
WKHapticTypeFailure,
WKHapticTypeRetry,
WKHapticTypeStart,
WKHapticTypeStop,
WKHapticTypeClick
} WK_AVAILABLE_WATCHOS_ONLY(2.0);
On iOS, we enjoy the ability to construct custom vibration patterns. Not in code, mind you, but through the Accessibility functions. Sadly, the watch offers no such customization.
In order to get a feel (ba-dump bump) for the various haptic types, I threw together a sample project. It contains a WKInterfacePicker
and a WKInterfaceButton
. The picker is populated with the known notification types.
First, the UI:
and the code. There’s almost nothing to it.
@interface InterfaceController()
@property (unsafe_unretained, nonatomic) IBOutlet WKInterfacePicker *picker;
@property (unsafe_unretained, nonatomic) IBOutlet WKInterfaceButton *button;
@property (nonatomic) NSInteger selectedType;
@end
@implementation InterfaceController
- (void)awakeWithContext:(id)context {
[super awakeWithContext:context];
[self.picker setItems:[self pickerItems]];
}
- (void)willActivate {
[self.picker focusForCrownInput];
[super willActivate];
}
- (IBAction)pickerDidChange:(NSInteger)value {
self.selectedType = value;
}
- (IBAction)didTapButton {
[[WKInterfaceDevice currentDevice] playHaptic:self.selectedType];
}
- (NSArray *)pickerItems {
NSArray *strings = @[
@"Notification",
@"Direction Up",
@"Direction Down",
@"Success",
@"Failure",
@"Retry",
@"Start",
@"Stop",
@"Click",
];
NSMutableArray *items = [[NSMutableArray alloc] initWithCapacity:strings.count];
for(NSString *str in strings) {
WKPickerItem *item = [[WKPickerItem alloc] init];
item.title = str;
[items addObject:item];
}
return items;
}
@end
The first thing I noted is that all these sounds except the click have an audio counterpart. I hadn’t expected that, though I suppose I should have.
Notification
is exactly what you’d expect: it feels like the regular notification tone.Direction Up
and Direction Down
, my first thought was, wow, directional haptics! Wrong. Two haptic taps corresponding with two ascending tones, or two descending tones.Success
comprises three tones forming an ascending major chord.Failure
is a three-note octave: high, high, low.Retry
is three quick taps with a single tone repeated.Start
is one strong tap.Stop
is two strong taps with about a half second interval.Click
is the quietest, most unobtrusive of them all.Sending in values higher than WKHapticTypeClick
will produce a response
exactly like WKHapticTypeNotification
.
We here at Big Nerd Ranch sometimes find ourselves wanting for demo app ideas. Unlike most development, we start wth an API call, and have to construct an app that exercises it. Much to my luck, the illustrious Erica Sadun mentioned on Twitter during the WWDC keynote that she was expecting a watch metronome app rather quickly. Thanks, Erica, for the great idea! Your watch metronome is here!
Only… it’s not such a great metronome. It keeps time reasonably well; I have no doubt that the NSTimer
is firing right when expected. But once we make our call to -playHaptic:
, we’re at the whim of the OS as to exactly when that plays. In practice, I felt a slight variation at higher tempos. If the Start
type had a shorter sound, I could use it.
Further, the taps are pretty light. My wife is a choral conductor, and she would not be able to reliably feel the taps while moving her arms around as she does.
“Prominent Haptics” could help, I see you thinkin’. Ah, but you’re wrong.
Prominent Haptics prepends each event with a long vibration, rendering it useless when performing as a metronome. (Bzzzz tap. Bzzzz tap. Bzzzz tap.)
There’s not a whole lot to cover in this API, so we’ll end this post here. In closing, I’d just like to suggest that using haptics can be overdone quite easily. It can really enhance to user experience, and Apple has
done a great job of using it tastefully within their apps. But it could be annoying.
Worse, it eats battery life. Moving that mass around requires a lot of your precious juice. You don’t want to make something that seems cool at first blush, but relegates your watch to the charger by 2 p.m. And power consumption is something we really have to watch (ha!) on the Apple Watch.
I hope this helps you build a better metronome!
Update: I am getting reports from the watchOS 2 Gold Master that the background operation no longer works. I thought this might happen. You see, originally I didn’t shut off the tap, figuring the extension would get killed right away. Imagine my surprise when the tap continued – even after I “killed” the app via the watch face. I added a little bit of code to stop the tap, and made it optional – making the “background tap” a feature!
As often happens, I built my “feature” on top of an undocumented assumption, that in fact went against the behavior the docs would suggest. And as Apple came closer to release, at some point, they tightened down that assumption, and my feature broke. Should I blame Apple? No, they clearly did the right thing.
But now what do I do if I want to schedule a tap of some sort from my app? Must I go back to the iOS app and schedule a local notification? I haven’t discovered this capability yet in watchOS, but it must exist: The watch’s timer app seems to be able to schedule notifications completely local to the watch. When you use the watch timer, your phone doesn’t know about it, but when you use the phone timer, the watch shows the alert as well. Perhaps the Timer app is using a private API that will become public in the next round.
Our introductory guide to Swift Regex. Learn regular expressions in Swift including RegexBuilder examples and strongly-typed captures.
The Combine framework in Swift is a powerful declarative API for the asynchronous processing of values over time. It takes full advantage of Swift...
SwiftUI has changed a great many things about how developers create applications for iOS, and not just in the way we lay out our...