breaking on failed NSAsserts

June 16, 2008

My code was crashing, with output on the console:

*** Assertion failure in -[NSTextFieldCell _objectValue:forString:], AppKit.subproj/NSCell.m:1131
An uncaught exception was raised
Invalid parameter not satisfying: aString != nil

So I set a breakpoint at - [NSException raise].  I hit the breakpoint, but the next call on the stack was runModalForWindow — there was no indication of what code of mine was doing the mischief.  I tried -[NSCell setStringValue:], but hit lots of cases where nothing was wrong.  Finally, after some googling, I tried breaking on:

-[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:]

Which was perfect.  The call stack showed exactly where I was setting the text field string value to nil, which in turn uncovered a bug in my network error checking.


Desktop iPhone, please

October 8, 2007

In the wake of the iPhone 1.1.1 software update, there’s been lots of controversy over Apple’s disabling of hacked iPhones. As an iPhone owner and a software developer I’d like to see Apple support third-party iPhone software development — I think it would make my iPhone more useful, make the iPhone a bigger success, and make the world a better place. But at the same time, I’d like Apple to think about going the other direction with its desktop computers.

I spent 45 minutes yesterday helping an elderly friend set up Verizon DSL service on her brand new HP/Compaq computer. It was my first hands-on experience with Microsoft Vista, and I was appalled. On a Mac, hooking up DSL would have meant plugging all the cables in, turning the DSL modem on, connecting to a Verizon website, and picking a username and password. But on Vista nothing worked the first time, I had to install extra software, and through it all I had to answer questions. I counted at least 20 different alerts with questions about security, certificate revocation, firewall exceptions, etc. None of them was the sort of question that my friend, or any typical computer user, could answer in any reasoned fashion. It was as if they were designed not to actually gather useful information, but to make the user feel responsible for anything that might go wrong down the line.

So why was the experience so bad? I think the core problem was like the one alluded to in the old joke about how porcupines mate (the answer: very carefully). Vista is designed to run all sorts of software, good, bad (e.g. spyware and viruses), and ugly (e.g. the Verizon software). So it tries to protect itself with restrictions on application behavior, and an avalanche of inscrutable “is this okay?” alerts. Actually having the entire system work together in an attractive, seamless way, is a ridiculous aspiration. It would be like expecting to look and feel good hugging a porcupine.

So why did my friend purchase this prickly, defensive, ugly machine? All she wants is a way to email her children and grandchildren without tying up the phone. The only app she will ever use is Internet Explorer, in order to connect to Yahoo! Mail. She used to have an iOpener, an Internet email appliance, but after years of service that machine broke, and the company is out of business. So her kids took her to Best Buy and got her a low-end HP machine, and I’m guessing they paid substantially less than they would have paid to get the lowest-end iMac.

But what she got is very poorly suited to her needs, and while an iMac would be much better, it too would have been overkill. What she really needs is a desktop iPhone: a locked-down Internet appliance, that does a few things and does them perfectly.

There’s no question that Apple could make such a thing, and sell it profitably for under $500. It would work well, and it would be beautiful. And I suspect that the market is large, especially as web services (e.g. Google Docs) make having local storage and local third party software less vital. It could be that the biggest obstacle to such a product is the kind of backlash Apple is facing today over the iPhone. An intentionally limited computer could be tarred as a step backwards, a crippled “Mac Jr.”, defined by the things it is missing rather than the benefits it delivers.

Apple does not need to sell to the people who buy $400 HPs, they’re making plenty of money selling to the higher end of the market, and they could decide that they don’t need the public relations aggravation that would accompany a desktop iPhone. But I hope they are at least considering it.


Ancient Mac History

September 5, 2007

Damon Darlin and John Markoff of the New York Times write:

Another breakthrough Apple product, the original Macintosh, initially sold briskly in 1984 and then stalled abruptly. The Macintosh market did not regain its luster until 1986 with the introduction of the Macintosh II.

My recollection is (and Wikipedia confirms) that the Macintosh II was released in 1987. The Macintosh Plus was released in January, 1986, and is often credited with saving the Mac by enabling the birth of desktop publishing.


Selling the experience

September 5, 2007

John Gruber writes:

In short, don’t get trapped over-thinking Apple’s fundamental strategy. It’s simple: Make the best products they can and sell them.

Or, to put it a bit more broadly, make the best experiences they can and sell them. The “sell” is significant, and sets Apple apart from partners/rivals like NBC and Google. Apple sells experiences, full stop. It does not clutter them with ads, Intel stickers, or anything else that would diminish them. And it includes the purchasing experience as part of the overall experience, so consistent pricing is not just a fetish — it’s part of what Apple is selling.

So it is no wonder that NBC is at odds with Apple — their goals are completely different. NBC wants to find a balance that will maximize their profits, and to that end they will use multiple competing outlets, variable pricing, bundling, loss leaders, availability restrictions, etc. Apple just wants to optimize the customer experience, and collect money from those customers.


A step forward for iPhone car adapters

August 9, 2007

Both my Belkin and Neo iPod car adapters started working (for some definition of “working”) with the iPhone 1.0.1 update. Thanks to commenter Allen Huffman for pointing out that change. Now the audio from the iPhone’s iPod mode is routed to the car stereo, as expected. But there are still problems:

  • Everytime I plug in the iPhone (or start the car, if the iPhone was plugged in while the car was off) I get the warning about the accessory not being designed for the iPhone, and the suggestion to use Airplane mode. I have not had any problems saying no to Airplane mode, but having to answer the question each time is a drag.
  • Sometimes, with the Belkin, I have to unplug it and plug it in again (and answer the Airplane mode question) before the audio is routed correctly.
  • The iPhone is a great iPod if you have two hands and your eyes free, but it’s a step backwards if you’re trying to pause, play, or skip to the next track while keeping a hand on the wheel and eyes on the road.
  • I’m used to leaving my iPod in the car, but I want to keep my iPhone with me. Running errands to a few stores involves a lot of shuffling the iPhone between my pocket and the adapter.

I’m grateful for this additional compatibility — I’ll take advantage of it on long drives, when there’s a passenger to control the iPhone. But otherwise I’ll stick to iPods for car use. At least until Apple comes out with an iPhone that that sends audio wirelessly from my pocket to the car stereo, and can be controlled (also wirelessly) with the steering wheel and dashboard audio controls….


PyObjC learnings

July 26, 2007

Lesson #1: Don’t forget to return self when you override init. Otherwise nothing works, but it isn’t obvious why.

Lesson #2: Initialize instance variables in init. I started out trying to avoid overriding init whenever possible. Perhaps it was because I was gunshy after learning Lesson #1 the hard way. Also, I don’t like the two lines of boilerplate that begin every overridden init:

class MyObject(NSObject):
    def init(self):
        self = super(MyObject, self).init()
        if self is None: return None

One of the great things about Python is its terseness, and having to type that stuff every time is annoying. I think that if I knew enough Python I could write a function decorator to handle the obligatory call to superclass, but then I’d have to add the decorator module to the project and import it — not a big savings in typing.

So when I needed to initialize an instance variable I tried to avoid overriding init, and instead would perform the initialization in the class definition:

class MyObject(NSObject):
    _array = []
    _lastIndex = 0
   ...etc...

That way instance methods could refer to self.instance-variable-name and get the initialized class attribute if no per-instance value had yet been set. Having that series of assignments at the top of the class definition also served to gather together all the instance variable names I planned to use, rather than having them scattered through the code.

But experienced Python programmers will immediately spot the problem. Objects assigned to class attributes are shared between all the instances of the class, and so changes made to mutable objects by one instance are seen by all the others. If instance A added an item to self._array, instance B’s self._array would have it as well.

That was not at all what I wanted. So I’ve gone back to overriding init whenever I need to initialize instance variables.


iPhone and the Neo Ion

July 26, 2007

I finally got around to trying the iPhone with the iPod adapter in our other car, a Neo Ion from MP3YourCar. The Ion is wired directly into the stereo’s CD changer port, so there’s just one cable and the audio quality is great. But, like the Belkin, the Ion charges the iPhone but does not route audio to the car stereo. And (unlike the Belkin), the cable to the iPhone is too short to allow use of the phone (except as a speakerphone) while it’s plugged in. The short cable can be a hassle even with an iPod (e.g. when someone in the back seat wants to choose the music), and so far I haven’t found any iPod dock extension cables.

Apple was farsighted to standardize on the 30-pin dock connector for multiple generations of iPods, and smart to use it in the iPhone, so it’s a shame that the compatibility with existing adapters is only partial.


objc.selector and objc.signature

July 12, 2007

I’ve been trying to learn PyObjC by going through Aaron Hillegass’s excellent Cocoa Programming for Mac OS X, doing the exercises in Python instead of Objective C. For the most part the translation between Python and Objective C provided by PyObjC is remarkably seamless, but of course I immediately ran into one of the seams.

I was trying to bind an NSTableView to an array stored in a model object, by way of an NSArrayController. The array was named “employees”, and so I needed to implement collection accessors for inserting and removing items, following the patterns insertObject:in<Key>AtIndex: and removeObjectFrom<Key>AtIndex:, where <Key> is the capitalized name of the collection. So in Objective C these would look like:


- (void) insertObject:(id) obj inEmployeesAtIndex: (unsigned int) index;
- (void) removeObjectFromEmployeesAtIndex: (unsigned int) index;

The translation to Python is simple, just replace colons with underscores and add the self parameter:


def insertObject_inEmployeesAtIndex_(self, obj, index):
def removeObjectFromEmployeesAtIndex_(self, index):

I ran the app, and got an error as soon as I tried to remove an item from my table, causing the removeObjectFromEmployeesAtIndex_ method to be called. It seemed that the index parameter was being set to None (Python’s nil). How was that happening?

The problem was the type of the index parameter — Cocoa was passing an unsigned int, which happened to be zero. But PyObjC defaults to treating all parameters as objects, so that zero was treated as the nil object. PyObjC knows about the methods of lots of Cocoa classes, and can construct special cases so they all work automatically, but it couldn’t know about this call — it couldn’t know that I’d name my array employees, and therefore need those specific collection accessors. I had to find a way to tell PyObjC that my functions take an unsigned int as a parameter.

Unfortunately, the documentation for how to do this is scattered around. Examples are mentioned in passing in the introduction and How to wrap an Objective-C class library documents, and I got the idea that the selector and signature functions in the objc module were involved. So I fired up the Python interpreter and typed print objc.selector.__doc__ and print objc.signature.__doc__. But the most helpful reference was Geoff Wilson’s Showing a NSSavePanel as a sheet, a blog post that described solving a similar problem with an NSSavePanel callback.

The solution, was to follow the definition of insertObject_inEmployeesAtIndex_ with:

insertObject_inEmployeesAtIndex_ = objc.selector(
insertObject_inEmployeesAtIndex_,
signature = 'v@:@I'
)

and to follow the definition of removeObjectFromEmployeesAtIndex_ with:

removeObjectFromEmployeesAtIndex_ = objc.selector(
removeObjectFromEmployeesAtIndex_,
signature = 'v@:I'
)

In Python 2.4 it’s a bit simpler; you can preceed the definition of insertObject_inEmployeesAtIndex_ with:

@objc.signature('v@:I')

and the definition of removeObjectFromEmployeesAtIndex_ with:

@objc.signature('v@:I')

objc.selector or objc.signature tell PyObjC what sort of arguments to expect from Objective C. Figuring out the signature is the one tricky part, but fortunately these functions are simple. Apple documents the supported type encodings. For example, 'v@:I' indicates a function that returns void ('v'), is an object method ('@:'), and takes one unsigned integer argument ('I'). Things get more complicated with pointers, output parameters, etc. I do think it would be helpful if the PyObjC documentation included an extensive collection of example method signatures, along with a more explicit explanation for when objc.selector and objc.signature are required.


The iPhone and the original Macintosh

July 12, 2007

John Gruber made a comment in the second episode of The Talk Show podcast to the effect that the iPhone merits comparison with the original Macintosh 128K. That got me thinking about the two products, and their introductions.

I was in college in 1984, and while there was some hype generated by the “1984″ Super Bowl commercial, I don’t recall knowing anyone who was looking forward to January 24 the way I was. On the big day I picked up that week’s copy of InfoWorld, with Steve Jobs on the cover, and the first issue of MacWorld, at Out-of-Town News in Harvard Square, and headed into the Harvard Coop to see the machines on display. They had a couple set up, running MacPaint and MacWrite, with ImageWriters you could use to print out your doodles. There was no hoopla, no crowd, and at $2,499 (without peripherals) certainly no worries about running out of stock. All the same, the machines were magical, utterly unlike the personal computer state of the art.

The two things that struck me most immediately about the Mac were the screen, and the mode of interaction. At a time when other computers used green (or amber)-on-black text, with big fuzzy pixels, the Mac 128K used sharp black-on-white. And while you communicated with other computers through a layer of abstract commands, typing “copy letter.txt b:” because you’d learned that command for backing up a file to a different disk, on a Mac you moved your hand to actually pick up that file, and drop it where you wanted it to go. It was the difference between typing directions and driving a car.

Of course these are also the two things that strike you first about an iPhone. The screen is so sharp that it has nearly as many pixels as the Mac 128K, squeezed into a space that fits in a pocket. And again, the interaction is direct: you flick photos, pinch maps, and tap on phone numbers, instead of pressing some button that’s arbitrarily labeled with the function you want.

A dozen years after the Mac 128K shipped virtually all personal computers worked like a Mac. I’m not sure that the iPhone will be as sweepingly successful a template: by 2019 we may have moved on to phones that we wear like glasses and interact with by voice command, or something similarly futuristic. But in the meantime I expect to see more and more iPhone-like phones, from Apple and others.

By the way, it is easy to forget how primitive the personal computers of 1984 were when the Mac debuted. For a look back, check out James Fallows’ excellent Living with a Computer. Originally published just 18 months before the Mac was released, it portrays a now almost unimaginable world. Tellingly, Apple is the only manufacturer mentioned that still makes personal computers. The rest — Tandy, Processor Technology, DEC, Heath, Atari, Commodore, Victor, Vector, Wang, Sinclair, North Star, Xerox, Osborne, and even IBM — either no longer exist or have fled the business.


Advantage: iPhone Google Maps

July 12, 2007

In planning out-of-town business meetings I’ve found myself looking up hotels on my iPhone instead of using the MacBook Pro in front of me. Typing the name of the town where I’ll be is definitely slower on the iPhone, but the other steps — calling the hotel to ask about meeting rooms, and storing the hotel address, phone number and URL so I’ll have them later — only take a couple finger taps. The iPhone does much less than a laptop, but sometimes the few things it does are the exact things you want, with the result that you can do them more directly.