Swift Regex Deep Dive
iOS MacOur introductory guide to Swift Regex. Learn regular expressions in Swift including RegexBuilder examples and strongly-typed captures.
Ever be coding along, giddily hooking objects together and doing that voodoo that you do so well, and all the sudden you hit a wall. Things stop working. You’ve hit, what could be, A Bug. “Can this really be broken? What’s going wrong?” And then you’re stuck in a gumption trap , shaving yaks until you can get back to your important work.
I was working on a posting about notifications, and as an exercise I wrote a little notification spy on the three major notification centers: the default notification center, the distributed notification center, and NSWorkspace
’s notification center.
It was simple enough code, like:
NSNotificationCenter *center;
center = [NSNotificationCenter defaultCenter];
[center addObserverForName: nil
object: nil
queue: nil
usingBlock: ^(NSNotification *notification) {
QuietLog (@"NOTIFICATION %@ -> %@", notification.name, notification.userInfo);
}];
That worked fine – I could see all sorts of notifications bouncing through Cocoa. The distributed notification center worked great too. Then I added the code for NSWorkspace
’s:
center = [[NSWorkspace sharedWorkspace] notificationCenter];
[center addObserverForName: nil
object: nil
queue: nil
usingBlock: ^(NSNotification *notification) {
QuietLog (@"WORKSPACE %@ -> %@", notification.name, notification.userInfo);
}];
And I got nothing from this. Zilch. I should have gotten notifications when switching between applications, when apps launch and exit, machine going to sleep, and so on.
So, I may have a bug on my hands. When I see something strange, my first inclination is to blame myself. I don’t often blame the toolkit, and I rarely blame the compiler. Pretty much every time someone says something like “hey, NSArray
is broken!” to me, I reply “It’s very unlikely that something as fundamental as NSArray
is broken like that. I bet you’re doing something wrong somewhere.” That thought applies to myself too. I assume that I’m either misusing the API, or I’ve missed some important detail.
The original program was in a single source file command-line utility. Maybe NSWorkspace really wants to exist in a full-fledged CocoaApp. So I made a new Cocoa application in Xcode and pasted my notification code into main(). Still no joy. Maybe it wanted to be registered after NSApplication
got up and running, so I put it into applicationDidFinishLaunching:
. Still no notifications delivered. Maybe it’s something ARC related, so built my tests with and without ARC. Luckily the same misbehavior on both.
I checked the docs, the headers, and the 10.7 release notes to see if NSWorkspace’s notification center was documented as changing behavior, along the lines of “In Mac OS X 10.7 we finally removed the deprecated workspace notification center we wanted to axe since 10.0 and we laugh in your face if you still want to use it”. Luckily there was nothing of the sort. I also asked Uncle Google and checked out Stack Overflow and Apple’s devforums to see if anyone else had problems with with this.
It’s not looking hopeful – no similar complaints. It would have been nice if it had been a widespread problem and someone smarter than me had already figured it out. Oops, forget I said that. I have a reputation to maintain.
So the next thing to check is are workspace notifications broken on my machine or on my version of OS X.
I snagged Kevin Ballard’s Notification Watcher utility and ran it. Sure enough, it saw all the workspace notifications.
That’s good news – it’s not something fundamentally broken on my system. Took another look at my code. Seems to be ok. The fact that it works for two out of three notification centers makes me think it’s not my code.
I had written something similar in my past using the old-school notification API. On a whim, tried that out:
center = [[NSWorkspace sharedWorkspace] notificationCenter];
[center addObserver: self
selector: @selector(observeWorkspaceStuff:)
name: nil
object: nil];
It worked. Tons of workspace notifications. What The Frack? This makes me think it’s a toolkit bug. It shouldn’t matter how I register for a notification. It should just work.
Whether you love or hate Radar, Apple’s bug reporting and tracking system, you have to use it to report bugs to Apple. Usually I’m on the cynical side of the pendulum, since most of the bugs I file either stay open forever, or are duplicated to ones filed three years ago (and for duped bugs you have to beg DTS or a friend inside of Apple to poll the status of it). By the time they do get fixed in the next OS version I’ve already coded a workaround and have long since forgotten about it.
I get mixed signals from Apple engineers about providing test cases. Sometimes it can take hours to boil the problem down to something small. I was griping on IRC a couple of years ago about spending four hours on a test case and got the bug duped within hours. “Don’t worry about that! If we want a test case, we’ll ask you for one if we need it.” On the other hand, it can be many months until they ask for a test case in which case I don’t have the time or inclination to make one. If I’m filing a bug, I err on the side of burning a chunk of my life making an unnecessary test case. Sometimes the act of making a small sample can let you know it’s an error in your code after all.
Luckily I was pretty close to a small reproducible test case due to the nature of the code I write for these postings, so I went ahead and polished it up. Here’s the code if you want to look at it. http://…/Spy.zip . The interesting part boils down to this:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
NSNotificationCenter *center;
#if 0
center = [[NSWorkspace sharedWorkspace] notificationCenter];
[center addObserver: self
selector: @selector(observeWorkspaceStuff:)
name: nil
object: nil];
#endif
center = [[NSWorkspace sharedWorkspace] notificationCenter];
[center addObserverForName: nil
object: nil
queue: nil
usingBlock: ^(NSNotification *notification) {
NSLog (@"+++++++ WORKSPACE??? %@ -> %@", notification.name, notification.userInfo);
}];
}
- (void) observeWorkspaceStuff: (NSNotification *) notification {
NSLog (@"------ WORKSPACE?!");
} // observeWorkspaceStuff
If I run it like this (using just the block-based notification), I get nothing. If I change the #if 0
to an #if 1
, I get both notification handlers called. It looks like someone has to register a non-block notification before this notification center will acknowledge a blocky one. I can’t think of a reason for that to be necessary (skimmed the docs and the headers again), so I conclude it’s a toolkit bug.
Time to go visit bugreport.apple.com, which occasionally fills me with dread because I’ve had spotty success with it in the past . My main advice is to create your bug report in another editor (TextEdit, BBEdit, etc) and then paste it into the web form. That way when it fails you won’t have to re-create the report from scratch. If you’re uploading something large (bigger than a couple dozen K), upload it separately in case the upload induces radar horkage.
Apple likes their reports in a specific format:
Summary – a quick description of the problem
Steps to Reproduce – Hopefully a simple list of steps to take. If things are complicated, I include some code in an attached project that they can compile and run.
Expected Results – What did you hope happened doing the steps? What do you think is correct behavior?
Actual Results – How did the universe disappoint you?
Regression – Have you seen things working in the past? It’s useful information if you know if something worked fine in 10.6.5 but now is broken in 10.6.8. That helps narrow down the behavior change. In this case, newer API seems broken but older API still works.
Notes – any additional information.

After I file a radar, I put the bug report into Tim Burks’ totally awesome Open Radar too, so that anyone can see the bug report. Of course, you shouldn’t make radars public on pre-release / NDA software.
I like adding videos to my bug reports. If nothing else, it makes my mind happier that there’s actual evidence of misbehavior and I’m not just smoking my socks. It’s harder to brush off a non-reproducible problem report if it’s clearly happening to someone else. If the problem is UI-related, it can be instructive seeing things like how fast (or slow) the user is mousing around. ScreenFlow http://www.telestream.net/screen-flow/ is my favorite screen recording software. It’s pretty easy to edit down the recording when you’re done. If something seems to be mouse or keyboard related, I can have it highlight clicks and releases and display keys being pressed. You can find the movie for this bug.
Now that the bug report is out of the way, I can go back to work on what I was working on originally. Hopefully you’ll see the fruits of those labors on thursday.
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...