Bulk-downloading the WWDC videos

I've been using a shell script by Olivier Hoachuck to download the WWDC videos and PDFs. The script comes in two variations, identical except that one uses curl and the other uses wget. It puts the downloads in a folder on your Desktop.

The nice thing is that it's a plain shell script. There are no Ruby or Python dependency issues, as I've heard there are with other similar scripts.

The script downloads the SD versions of the videos, which add up to about 52GB in size. To download the HD versions, globally replace "SD" in the script with "HD". I haven't finished downloading the HD videos, but they are about 6 times the size of their SD counterparts, so I'm assuming they will total roughly 312GB.

Compared to the videos, the total size of the PDFs is almost negligible at just under 400MB.

The SD quality seems fine for seeing everything you need to see. I'm downloading the HD versions just in case, and because my eyes need all the help they can get, and because hey, free videos.

What two minutes is and isn't

We shouldn't read too much into the fact that WWDC sold out in under two minutes this year. In fact, we should get away from caring at all how fast it sells out.

At first glance, Apple seems to have set an impressive record compared to previous years:

  • 2008 – 2 months
  • 2009 – 1 month
  • 2010 – 8 days
  • 2011 – 10 hours
  • 2012 – 2 hours
  • 2013 – 2 minutes

What's different this year, however, is that availability was announced 24 hours in advance. Everybody knew exactly when to be at their computers with their fingers poised over their keyboards. The moment tickets went on sale, many thousands of people around the world hit Apple's web site at the same time — many more than the number of available tickets.

This means the two-minute sellout time tells us nothing about customer interest or demand. It only tells us how fast Apple's servers were at taking orders. And even that's not quite true; it's not clear how many, but Apple had to call some people on the phone to complete orders that the servers had messed up. You could argue that WWDC didn't really sell out until every one of those people got their ticket — and again, that revised time interval wouldn't reflect on the level of demand, only on the efficiency of the order-taking mechanism.

How fast would WWDC have sold out if the servers had worked flawlessly? Not only don't we know; we shouldn't care. We're beyond bragging rights about who can put up the "sold out" sign faster, or whether we were faster this year than last. Everybody knows that WWDC, like Google I/O, has way more demand than supply, and it doesn't seem that'll change soon.

There have been suggestions to prioritize customers by some measure of merit, like old-timers first or newcomers first, but I don't agree. Or rather, I mostly don't. John Siracusa writes about a time when rushing to buy a ticket actually made a difference:

I wanted to preserve at least some aspect of the process that rewarded the most enthusiastic Apple fans: the people who are willing to be roused from bed at 2 a.m. and rush to their computers to buy tickets; the crazy ones; the people who just want it more.

I too would love to see this aspect preserved; I am, after all, one of the crazy ones. But Siracusa goes on to say that we're beyond that now, and I agree with that.

Things being as they are, I think every interested customer should get an equal chance at a ticket. I agree with Siracusa that a lottery, which would slow down the ticket-buying process, is the best way to accomplish this. What Apple should focus on is not speed, but selling its limited number of tickets in an orderly, reliable way with a minimum of stress for its customers.

Idea for a pronounced improvement

A friend wrote on Facebook:

As a basic sign of respect for Lu Lingzi, the BU student killed in last week's bombing, can the NPR folks at least take five seconds to learn how to pronounce her name correctly?

I've wished before for a class that would teach how to pronounce various languages. The idea is similar to those crash courses for tourists visiting foreign countries, but the focus would be on pronunciation, with vocabulary only as a happy side effect.

It could be done a lot of ways. It could be taught in grade school; it could be an adult education thing; it could be an app or family of apps; it could be a blog or tweet stream or a daily column in the newspaper or a YouTube channel.

It might help to have multiple teachers who specialize in different languages. Teaching could focus on just Romanized forms or, for the ambitious, it could include how to read non-Roman alphabets like Cyrillic and Korean.

I would benefit from such a class. At Thai and Vietnamese restaurants I'm sometimes uncomfortable ordering things from the menu that I'm not sure how to pronounce. One of these days I should put in the effort to learn.

Mandarin romanizes in a pretty straightforward way that I'm sure most people could learn, especially if we set aside the tones. It would help if reporters would pronounce things right, which they could do on a case-by-case basis with just a minute of coaching. For starters, they could pronounce "Beijing" with the "j" in "jingle bells" rather than the "zh" in "Doctor Zhivago".

Come to think of it, I'm not positive I pronounce "Zhivago" correctly.

Faking "switch" with an object value

Jeff Kelley started a thread on the objc-language mailing list with this idea:

I would love to see a switch statement we could use for objects, testing equality with each case: statement. @switch would work nicely as the name, and I envision it working like this:
@switch(myString) {
    case @"hello world":
        // Do something
        break;
    case @"another one":
        break;
}
This would be equivalent to writing the code using if statements and sending -isEqual: messages, but with a much more readable control flow.

The blocks approach

One suggested approach was to use a dictionary to map each "case" value to a block that should be executed when the "case" value matches the "switch" value. Jeff Biggus posted this solution mainly as an academic exercise, but I think it's about as tidy as the blocks-based approach gets:

typedef void (^voidBlock)(void);
 
#define swoosh( test_var__, action_dictionary__... ) \
        ((voidBlock)action_dictionary__[test_var__])();
 
[...]
 
NSString *test = @"that";
swoosh( test, @{
        @"this" : ^{ NSLog( @"found this" ); },
        @"that" : ^{ NSLog( @"found that" ); },
});

Nicolas Bouilleaud posted an alternative solution using +resolveInstanceMethod: that allows you to write:

[[@"foo" switch]
 case:@"bar" :^{ success = NO; }
 case:@"baz" :^{ success = NO; }
 case:@"foo" :^{ success = YES; }
 ];

I'm not crazy about using blocks for a few reasons:

  • You have to think about whether you need to use __block variables.
  • You can't put break, continue, or return statements in the blocks and have them work as they would in an analogous switch statement; often their very presence would be a syntax error.
  • If you use a dictionary of blocks, the "case" objects have to conform to NSCopying so that they can be dictionary keys. Also, as Nicolas points out, if you use a dictionary, you can't specify the order in which the cases are tested. So, no dictionaries; but you can imagine a similar approach using an array.
  • Xcode's auto-indenting of blocks looks really, really horrible. In theory, the typographical quirks of an IDE have nothing to do with the soundness of a technical approach, but Xcode is so bad about this that I can't ignore it. For example, if I put the first NSLog above on its own line, Xcode does this:
swoosh( test, @{
       @"this" : ^{
    NSLog( @"found this" );
},
       @"that" : ^{ NSLog( @"found that" ); },
       });

objswitch, objcase, endswitch

I don't know how original this is, but by using a few macros and a small class used behind the scenes, I came up with a different approach that is essentially syntactic sugar around nested else-if statements. Here's an example of how it looks, as formatted by Xcode:

objswitch(someObject)
objcase(@"one")
{
    // Nesting works.
    objswitch(@"b")
    objcase(@"a") printf("one/a");
    objcase(@"b") printf("one/b");
    endswitch
 
    // Any code can go here, including break/continue/return.
    // Xcode will indent it nicely.
}
objcase(@"two") printf("It's TWO.");  // Can omit braces.
objcase(@"three",  // Can have multiple values in one case.
        @"tres",
        @"trois") { printf("It's a THREE."); }
defaultcase printf("None of the above.");  // Default case is optional.
endswitch

I would argue this is even a tiny bit nicer than switch/case syntax, because you don't need unsightly break statements to keep the cases from bleeding into each other.

If someObject is @"one", the output is

oneb

If someObject is @"tres", the output is

It's a THREE.

In this example all the "case" values are strings, but they can be any object.

The "keywords" objswitch, objcase, objkind, and endswitch are actually macros. There is a simple class called ObjectMatcher that is instantiated by objswitch. You can see the code on GitHub.

objkind

Some people mentioned wanting similar syntax for testing the class of the object rather than its value. Testing an object's class is often a sign of suboptimal design (see here for a brief discussion, here for my thoughts on the matter), but for those occasions when you decide it's the right approach, you can use my objkind macro:

objswitch(someObject)
objkind(NSNumber) { printf("It's a NUMBER."); }
objkind(NSString) { printf("It's a STRING."); }
objkind([NSArray class],
        [NSDictionary class],
        [NSSet class]) printf("It's a collection.");
endswitch

Note that if you're only passing one class name to objkind, you can just give the class name:

objkind(NSNumber)

But if you pass multiple classes, you have to say [MyClass class] or [MyClass self] (or, if you prefer, MyClass.class or MyClass.self) for every item after the first one. This is a limitation of __VA_ARGS__ macros. I prefer all the items to look the same — hence:

objkind([NSArray class],
        [NSDictionary class],
        [NSSet class])

Like objcase, objkind is just a wrapper around a nested else-if statement, so you can freely mix objcase and objkind within the same objswitch.

selswitch, selcase

One place I might like a similar construct is for doing a switch statement on a selector. I have validateUserInterfaceItem: methods all over the place with lots of nested if-statements like this:

SEL itemAction = [anItem action];
 
if (itemAction == @selector(selectSuperclass:))
{
    // ...
}
else if (itemAction == @selector(selectAncestorClass:))
{
    // ...
}
else if ((itemAction == @selector(selectFormalProtocolsTopic:))
         || (itemAction == @selector(selectInformalProtocolsTopic:))
         || (itemAction == @selector(selectFunctionsTopic:)))
{
    // ...
}

I created selswitch and selcase macros that let you do this:

selswitch([anItem action])
selcase(@selector(selectSuperclass:))
{
    // ...
}
selcase(@selector(selectAncestorClass:))
{
    // ...
}
selcase(@selector(selectFormalProtocolsTopic:),
        @selector(selectInformalProtocolsTopic:),
        @selector(selectFunctionsTopic:))
{
    // ...
}
endswitch

I haven't decided whether the improvement is big enough that I'd use this rather than plain old nested ifs, especially since validateUserInterfaceItem: is called very frequently and should be as fast as possible.

[UPDATE: I've made several edits since I first published this post, ranging from fixing typos to inserting a sentence or two. I was originally flagging each change, but to reduce clutter I decided just to add this mention at the bottom.]

Writing a .service bundle

You can provide system services (items that appear in the Services menu) in two ways: through an application (YourApp.app) or through a standalone service (YourService.service). The first way is well documented, but I've found it hard to find instructions for the second way.

The first way: a regular .app

Your Cocoa application can provide services that appear in the Services menu. Apple's "Services Implementation Guide" explains how to do this. Basically:

  • Create a class (or use an existing class) with one method for each service. Each service receives input and optionally returns output by way of NSPasteboard.
  • Add an "NSServices" key to Info.plist with info about each service.
  • At some point during application startup, call NSRegisterServicesProvider().

Put the application in /Applications, or a subfolder thereof, to get the system to detect the new service(s).

When an application service is invoked, the application becomes active. There seems to be no way to avoid this. At best you can immediately hide the application, but there will still be a flicker as windows appear and disappear.

UPDATE: Mark Munz offered this tip on the cocoa-dev mailing list:

[T]he solution I used to prevent Services from bringing the app forward is to have a background (LSUIElement) helper app that acts as the NSServices provider. Depending on what you need to do, you could either support the service directly in the helper app or use it to talk to the parent app to perform the necessary work (without requiring it to activate). I use the second approach.

The second way: a .service bundle

If you want a service that is purely a background operation, you can create a standalone service that has no UI. It's very simple once you know how, but I haven't found a lot of help online, although some books discuss it and you can find examples on GitHub by searching for NSRegisterServicesProvider.

I expected a "System Service" template in Xcode, but there is none. Instead, you use a "Cocoa Application" project and code it just like a regular application with services except:

  • Change the target's wrapper extension from "app" to "service": Project > Target > Build Settings > Wrapper Extension.
  • Make it a background application by adding to Info.plist: LSUIElement = YES. (This is the "Application is background only" item in the popup menu that appears in Xcode's plist editor.)
  • Remove resources you won't need, like MainMenu.xib, AppDelegate, and Credits.rtf.
  • Edit main.m to instantiate the service-provider object and enter a run loop. Here's an example.

Build the project and copy YourService.service to ~/Library/Services. You may have to relaunch apps to get them to see the service.

Tips

  • Instead of editing Info.plist to add the NSServices entries, you might prefer to use Xcode's UI. I don't know when this was added to Xcode, but you can go to Project > Target > Info and there's a "Services" area at the bottom. It's easy to miss if you don't know it's there.

  • To uninstall the service, remove YourService.service from ~/Library/Services. You won't be able to delete it if the service has a running process, so you might have to kill the process first.

  • If the service doesn't appear in the Services menu, you may have to force the menu to be reconstructed. Unfortunately there's no command you can call from the command line. It's trival to write one though — all it has to do is call NSUpdateDynamicServices().

See also

As I mentioned, Apple's "Services Implementation Guide" has plenty of detail on writing application services, but as far as I can tell the only help it gives on standalone services is this one line:

> To build a standalone service, use the extension .service and store it in Library/Services.

The breakthrough that got me going was when I found example code on the Timac blog. I already had implemented a service in an application. By looking at Timac's code and doing monkey-see-monkey-do, I got my first standalone service working.

I then realized I could find plenty more examples by searching GitHub for "NSRegisterServicesProvider".

Here's the service I wrote. It copies method names to the clipboard so you can paste them in emails, code comments, docs, and so forth.