Archive for the 'Programming' Category

Writing, n.

Thursday, March 24th, 2011

Over on Tumblr (where I have also started writing), Andy Matuschak and Christopher Bowns have challenged each other:

Long story short: Andy and I threw down our respective gloves after throwing back the third glass of whiskey, and came up with this: every Tuesday, we’ll spend 20 minutes of our respective shuttle rides to work writing something. It doesn’t matter what, but when those 20 minutes are up, you publish it.

After Christopher published that, Colin Barrett entered with this piece. Discussion ensued on Twitter, as it so often does, and it included this tweet from Christopher:

@cbarrett Perfect. I think “overthinking it” can cripple your writing, but it’s hard to let go of, because it’s so useful in engineering.

I responded there, but I’d now like to expand in a somewhat different direction here.

Writing programs is writing.

Consider this: If you write a program that no human can read, you have written it badly, and may have even written an outright bad program. Switch out “program” for “manuscript” and I hope you see my point.

Don’t confuse this with literate programming. That has you write a more-or-less human-only text, and then run a program such as WEB or CWEB to translate it to source code.

The problem with literate programming is the problem with all translation from one language to another, especially automatic translation: Error.

Just as translating a human-only text from one human language to another human language can produce a garbled mash, so can translating a program from one language to another produce a nonsense, or at least broken, program.

Hence the justified suspicion of programs that “upgrade” a program even just from one version of the same language to another: It isn’t the same language; not really. Similar, but not the same. Going from Python 2 to Python 3, for example, is not much different from going from 18th-century English to 21st-century English. Do these words mean the same thing? Maybe not. This is why such translators invariably come with warnings to check the hell out of the result.

No, I’m not talking about literate programming. I’m talking about regular programming, where you write the true text of the program, the text that the interpreter interprets or the compiler converts into machine code.*

Programming is a fusion of a couple of skills. It’s often called “software engineering”, but that’s only half of it. Writing is the other half. I define “writing” here as writing text into an editor, whatever the language, and “engineering” as designing a thing to be built. Things like OOP, unit testing, and design patterns are aspects of software engineering. Things like DRY, commenting practices, naming practices, and style rules are aspects of writing.

If you write a program well, you write it not only for the interpreter or compiler, but for any humans who will read the program after you, including yourself. It is a text like any other.

Writing a program is writing. Every rule, observation, and prescription you apply to writing for humans only, apply also to programming, and vice versa. If the rule does not work, chances are it was not a good rule to begin with.


Suggested reading


* This is, itself, a form of translation; the C standard even calls it such. That’s part of why compiler bugs are possible, and why compiler authors are extremely careful about their work, which is why compiler bugs are so rare.

The application delegate and the new singletons

Friday, March 18th, 2011

Here is a global variable:

Wizard *gWizard;

I’ll call this a zeroth-order global, on the premise that I need to talk to exactly zero objects (including classes) to gain access to this object.

Next, let’s look at a singleton:

[Wizard sharedWizard]; //hope he's not busy

I’ll call this a first-order global, as we need to ask the class for it (1 step) to gain access to it.

Now, here’s a second-order global:

MyAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];

(I use UIApplication here because I see this most frequently in Cocoa Touch code, but the pattern applies equally to Cocoa.)

And here’s a third-order global:

Wizard *wizard = [appDelegate wizard];

I need to (1) ask the UIApplication class for the application object, (2) ask that for its delegate, and (3) ask that for the wizard. (Assume here that wizard is a property, not a factory method that creates Wizards on the fly.)

None of these is any less global. If I can get to it from anywhere in the program without knowing about it directly, it is global.

Therefore, all the problems of globals apply:

  • What if two threads want to use the same Wizard?
  • What if the Wizard has a delegate of its own, and I have two objects that want to be its delegate?
  • What if the Wizard keeps internal state that may be corrupted by multiple objects trying to use it? (Nothing should have to worry about this outside of the Wizard itself.)

Your application’s objects form a graph. It should not be a complex one like this:

At the top, the application object. From it, its delegate. From it, your controller objects and a wizard. From each controller object, a path (colored in red) back to the delegate and then to the wizard.

Whenever you have paths bouncing around off of other objects like that, that’s a problem. The red arrows in the problem graph show where you violate the Law of Demeter.

Your object graph should, instead, be straightforward:

At the top, the application object. From it, its delegate. From it, your controller objects. From each one, a wizard.

Note that each of your controllers should own—or, if you prefer, hire—a Wizard all to itself. This eliminates contention between objects and reduces the likelihood of contention between threads (assuming each of the owning objects is supposed to only work on a single thread and not juggle multiple threads).

If contention is not a problem and you have a good reason why there should be only one Wizard, such as memory pressure or union regulations, then use a singleton. But use a real singleton, and only when necessary, and beware of singletons in disguise.

The new and improved Cocoa links card

Tuesday, March 15th, 2011

I’ve previously mentioned that I made a business card full of useful Cocoa and Cocoa Touch links to give to new Cocoa and Cocoa Touch programmers at events such as CocoaHeads.

Today, I have updated it and given it a web page. 1-up and 10-up (US Letter) PDFs are available there, as well as the full list of unshortened links.

I encourage you to print out the 10-up onto perforated business card paper, or have it professionally printed (keeping in mind that you probably won’t need 1000 of them), and make the cards available to novice Cocoa and Cocoa Touch programmers at the CocoaHeads or NSCoderNight events you attend. Just please be sure to print both sides, since my credit link is on the back.

Mentoring

Tuesday, March 8th, 2011

I originally wrote this for the Adium development list, in a discussion of the upcoming Google Summer of Code, for the benefit of those of our developers who have never mentored students in GSoC before and may be considering doing so. Read this in that context; for example, “our” means “the Adium project’s”.


  • Work with students from the very beginning on the quality of their [application] submissions. 90% of the submissions will be crap. Look past this. See what lies within. If it can be improved, try to get them to improve it. Those who do will be good students.

  • Spur your student to work on code. Make sure they’re committing at a regular and satisfactory rate.

  • Ensure your student actually writes code and doesn’t just crib together tutorials and/or Stack Overflow answers and/or ask you to provide code* for various tasks they need to accomplish. Wildly varying coding styles, inconsistent variable and method naming, and poor indentation are warning signs.

  • Communicate constantly. This includes the aforementioned spurring your student to work as well as being available to answer any questions they have about where things are in our code base, what they need to do to achieve certain goals or specific sub-tasks, etc. These questions are virtuous; you should encourage them, just short of demanding them. The student is not that only in name; they are here both to write code and to learn.

  • Find a medium that works for both of you. For me, email (or Twitter, nowadays) would be best. Maybe you prefer IM, or even voice chat/phone (if it isn’t too expensive). Don’t try to force your habits on the student; if you love the phone but they hate it (or vice versa), a happy medium will work better.

  • Be sure they understand and use Mercurial and good VCS practices generally. Frequent, discrete commits; neither waiting “until it’s done” to commit (it should compile) nor committing half-done work periodically (e.g., daily) nor committing amalgams of unrelated work (they should commit specific sections that comprise a single change).

  • Nowadays, I recommend that you have them fork our Bitbucket repo and push to their fork.

  • Review their code constantly. Subscribe to the commits list (or their fork’s commits feed) and review everything they write.

  • Read their commit messages, not just their code. Lists are a warning sign (unrelated changes lumped together). Inadequately describing the change is also a problem. Work the student out of these habits as soon as possible.

  • Don’t wait until mid-term exams to sit your student down for a serious talk about their work or lack thereof. If they’re committing garbage, set them straight as soon as possible—do not wait. If they don’t commit, get them writing and committing as soon as possible.

  • Always be ready to fail your student. Be compassionate, understanding of life’s realities; they’re not a slave. But they are here to work, and if they don’t do the job or if they do a bad job, be ready to fail them.

  • Make sure they know where they stand. If there’s 1–2 weeks before exams and they’re still in danger of failing, make sure they know what’ll happen if they don’t shape up.

I can’t claim to have been perfect in all of these points in my own mentoring (many of these I learned by not doing them), but it’s what I’ve found works.

There might also be something on the GSoC sites about this. Some viewpoints vary, particularly along the leniency-to-hardassness spectrum.

If you are not willing to do all of this, or don’t think you’ll have the time, you should not mentor a student.

* Six words that should worry every mentor or other help-offerer: “Can you show me a sample?”

Apple documentation search that works

Sunday, March 6th, 2011

You’ve probably tried searching Apple’s developer documentation like this:

The filter field on the ADC documentation library navigation page.

Edit: That’s the filter field, which is not what this post is about. The filter sucks. This isn’t just an easy way to use the filter field; it’s an entirely different solution. Read on.

You’ve probably been searching it like this:

Google.

(And yes, I know about site:developer.apple.com. That often isn’t much better than without it. Again, read on.)

There is a better way.

Better than that: A best way.

Setup

First, you must use Google Chrome or OmniWeb.

Go to your list of custom searches. In Chrome, open the Preferences and click on Manage:

Screenshot with arrow pointing to the Manage button.

In OmniWeb, open the Preferences and click on Shortcuts:

Screenshot of OmniWeb's Shortcuts pane.

Then add one or both of these searches:

For the Mac

Chrome OmniWeb
Name ADC Mac OS X Library
Keyword adcmac adcmac@
URL http://developer.apple.com/library/mac/search/?q=%s http://developer.apple.com/library/mac/search/?q=%@

For iOS

Chrome OmniWeb
Name ADC iOS Library
Keyword adcios adcios@
URL http://developer.apple.com/library/ios/search/?q=%s http://developer.apple.com/library/ios/search/?q=%@

Result

Notice how the results page gives you both guides and references at once, even giving specific-chapter links when relevant. You even get relevant technotes and Q&As. No wild goose chases, no PDF mines, no third-party old backup copies, no having to scroll past six hits of mailing-list threads and Stack Overflow questions. You get the docs, the right docs, and nothing but the docs.

For this specific purpose, you now have something better than Google.

Another Voices That Matter conference coupon

Friday, January 28th, 2011

The next Voices That Matter iPhone developers’ conference is April 9–10 in Seattle. Early-bird pricing lasts until February 25, and the coupon code this time is SEABLOG. The total for the “core conference” with both discounts is $395 USD.

As usual, I don’t expect to be there. Have fun without me!

PSA: Removing objects from arrays

Saturday, January 1st, 2011
  1. You create an NSMutableArray.
  2. You add the same object to it twice.
  3. You send the array one removeObjectIdenticalTo: message, passing the object you added.

What is the count of the array?

If you said 1, you’re wrong.

Worse, you might think the opposite of addObject: is removeObject: (especially if you’d never heard of the …IdenticalTo: version), but that’s even more wrong: removeObject: finds the object(s) to remove by testing equality (sending them isEqual: messages), not simply searching for the object you passed in.* That means it may remove objects that aren’t the object you passed in, but are equal to it. So, unless you really do want to remove any objects equal to the one you have, you should prefer removeObjectIdenticalTo:.

But that still removes the object entirely from the array, regardless of how many times it’s there. Unless you really want that, you more probably want this:

NSUInteger idx = [myArray indexOfObjectIdenticalTo:obj];
[myArray removeObjectAtIndex:idx];

And even then, that will remove the object from the first place you added it in at, not the last, so if you specifically need to remove it from the last place you added it (LIFO instead of FIFO), then you need to enumerate the array backwards, counting an index down as you go, remove the object at the index upon finding the object, and finally break out of the loop.

To make that easy and avert the otherwise high likelihood of off-by-one errors in many independent implementations, here’s a category you can add to your projects. Use anywhere you need the opposite of -[NSMutableArray addObject:].

* This doesn’t matter for sets, since it dupe-checks every object coming in based on equality anyway. With a set, removing the equal object and removing the same object are the same thing, which is why NSMutableSet doesn’t have removeObjectIdenticalTo:. Not so for arrays, which is why NSMutableArray does.

End of the Graveyard

Saturday, December 4th, 2010

The iPhone Application Graveyard is now closed.

I’ve been meaning to do this for months; I’m just now getting around to doing it.

I have a few reasons:

The Graveyard has served its purpose.

The iPhone App Store today is more open and more free than it originally was. The rules are now available to App Store developers, and several apps that Apple previously either rejected or “pocket rejected”, most prominently Google Voice, are now available in the Store.

I don’t know how much of this is attributable to the Graveyard and how much is just Apple having figured these things out, but to whatever extent the Graveyard is responsible, it has done all it can.

The Graveyard can do no more.

Apple’s made very clear that they intend to “curate” the App Store. It will never be a completely free, do-as-thou-wilt market like the Mac market still is, and I have no hope that Apple will ever make the iPhone App Store optional like the Mac App Store will be.

I see no way that the App Stores can ever be more free without losing that curation factor. And it is a factor—I can’t ignore that Apple checking every application probably, hopefully filters out some effluent from the influent stream.

I don’t update it.

I’ve got a dozen different things to do that are more important than updating the Graveyard.

I want to work at Apple.

Cold, hard reality is that I want to work for Apple, and they will not hire a person that has a page on their website decrying their policies. (Don’t get me wrong: I wouldn’t expect them to.) This isn’t the only reason why I’m killing the Graveyard—everything I wrote above is true—but it is one of them.

So, this position is now open.

If you want to keep the Graveyard alive, you can do that by taking it over.

The Graveyard is implemented as a couple of plain-text hand-edited databases and a Python script that converts them to the web page (as a static HTML file) and Atom feed (as a static XML file). This is how the Graveyard stayed up in the face of being Fireballed, Macworlded, etc.

You can keep it that way, or you might turn it into a wiki. I leave the choice to you.

If you want to take over the Graveyard, email me. I’m sure you know my email address by now. I’ll send whoever I think can best run it a zip archive of the data files and Python script.

You may also be interested in the Application Submission Feedback site. I don’t know who runs it, but it’s a great guide to what you can’t do in the App Store.

Four rules of debugging

Saturday, November 13th, 2010
  1. It is not the compiler’s fault.
  2. It is almost certainly not the kernel’s fault.
  3. It is probably not the library’s/framework’s fault.
  4. It is most probably your fault.

Helping people

Saturday, November 6th, 2010

There’s a recurring theme I keep seeing in questions on Stack Overflow.

I’ve said those words a few times now. I’ve said it twice at my recognizing-Cocoa-bugs presentation, which I’ve given at one CocoaHeads and at the MacTech Conference (just concluded yesterday). I’ve also said it a few other times at CocoaHeads, usually in preface to explaining something that’s both germane to the current conversation and the common source of confusion in the questions I’m then referring to. Plus, I’ve said it a couple of times in posts here.

In the concluding session of the conference, Edward Marczak, MacTech’s executive editor, cited a number of tweets related to various cool things that happened in the past few days. One of them was from me, inviting attendees to flag me down for one of my useful-Cocoa-links cards, which I’d previously offered up to audience members in my session. He told the audience how cool he thought it was that I’d thought ahead and made those cards.

The reason I’d made those cards was because I’d identified a pattern. In my answers on Stack Overflow, I often cite one or more of a few key documents:

or tell the questioner to listen to or turn on some warning or other and link to my warnings blog post as part of that.

My introductory document for Cocoa and Cocoa Touch, in its own introduction, identifies a similar genesis: Everything on that page is something I’d had to explain to multiple people on Stack Overflow, so I decided to put it all in one place for easy linking (by me) and easy reading (by new Cocoa or Cocoa Touch programmers). That document is also among the links on the card.

Each of the things I got praised for in that concluding session, and thanked for (besides Growl, which is mostly a lot of other people’s work) outside of sessions, I made because I’d identified a need by spotting a pattern.

There’s no reason you can’t do this, too. What have you done multiple times lately? Especially, what have you done or made or found for other people multiple times?

Somewhere on Eric Raymond’s site, there’s an explanation of the difference between newbies and wizards. It’s not that the wizards know all that much more than the newbies (though they often do have a stronger/deeper conceptual understanding); asked for the name of a function, say, the wizard will probably not know it exactly (unless it is very simple, such as CFRetain, or one they use very frequently, such as CFRetain).

The difference between them is that the wizard knows where to look stuff up. I sometimes refer to this as “swapping things in”, the analogy being virtual memory: I remember very little at any given moment, precisely because I know where I can get it from when I need to get it back into my mental working set.

The wizard knows how to help themself.

How can you enable people to help themselves?


Here’s that card, if you want one.

Manpage Monday: memset_pattern(3)

Monday, September 6th, 2010

From the manpage:

     void
     memset_pattern4(void *b, const void *pattern4, size_t len);

     void
     memset_pattern8(void *b, const void *pattern8, size_t len);

     void
     memset_pattern16(void *b, const void *pattern16, size_t len);

These are analogous to memset(), except that they fill memory with a replicated pattern either 4, 8, or 16 bytes long.

Handy if you want to scribble 0xdeadbeef (or any other value of your choice) over something.

As noted in the manpage, these functions require Mac OS X 10.5 or later. I don’t know about iOS.

Ship-It Saturday: PRHEmptySingleton repository

Saturday, September 4th, 2010

The singleton-done-right example from my article on the subject is now in a Mercurial repository on Bitbucket. The repository includes not only the class (which I’ve put in the public domain), but also a test suite for some of the test cases listed in the post.

There are some adventurous testing techniques at work here.

First, since we don’t want multiple test runs to use the same singleton instance, the test cases actually run in subprocesses. Each test method is prefaced with this code:

if (!isInSubprocess) {
    [self runTestInSubprocess:_cmd];
    return;
}

That method calls fork.

In the child process, the test case sets isInSubprocess to YES, then tells itself to perform that selector; this gets execution back to the test method, which checks the variable again, finds that it’s true this time, and continues on with the test.

The parent process calls waitpid and checks the result; if the child failed, the parent propagates the failure. If the child crashed (got a signal), the parent raises the same signal; if the child simply exited abnormally, then the parent exits with the same status.

Second, there’s test case #6:

  • If [super init] returns a different object, alloc/init won’t break.

That one is hard to test, because PRHEmptySingleton’s superclass is NSObject, and -[NSObject init] doesn’t return a different object. (Not in any version of Foundation I’ve ever encountered, anyway.)

So, the first step is to create an intermediate class that does return a different object. But that doesn’t help as long as PRHEmptySingleton is still a direct subclass of NSObject.

The simple solution would be to just change PRHEmptySingleton’s superclass, but that reduces the purity of the testing: A test should be able to work without modifying the code under test, and any changes to the code under test should be permanent changes that aren’t only to enable the test; you should be able to explain the changes as if the test case did not exist.

So what I did was to import everything in my prefix header, even more than I usually do, and then import my intermediate class’s header, and then use the preprocessor to ensure that any direct subclasses of NSObject declared elsewhere are declared as subclasses of the intermediate class. Thus, the prefix header causes PRHEmptySingleton to be a subclass of the intermediate class with no changes to PRHEmptySingleton’s header. It’s a bit of a hack, and doing this sort of thing could potentially cause problems in a larger program or test suite, but in this context, it works.

With that, five of the six test cases in the original post are now covered (I’m not sure how to cover #3 without changing PRHEmptySingleton.[hm]), and the code is under version control, so you can subscribe to and track changes.

Please use singletons responsibly.

Dueling conferences

Thursday, August 5th, 2010

There are two four five six development conferences coming up this fall:

Here’s an iCalendar file of these events.

The cheapest conference is 360|MacDev, at $300 ($200 with early-bird pricing). The second-cheapest is Voices That Matter, costing $395 with the coupon and early-bird pricing. SecondConf is a little more expensive, at $449, and 360|iDev is a little more expensive than that, at $599. The iPhone/iPad DevCon is hard to compare; it’s either the most expensive at $1395 ($1065 with early-bird pricing) or the least expensive at free, depending on which kind of ticket you get. Ignoring the DevCon, MacTech’s conference is the most expensive at $899, but is also the broadest, consisting of development (both Mac and iOS) plus an IT track.

I will not be at the Voices That Matter conference, SecondConf, the DevCon, or 360|iDev (I’m still not an iPhone/iPad developer), nor 360|MacDev (too far), but I will be at the MacTech conference. In fact, I’ll be presenting.

My presentation, intended for new Cocoa and Cocoa Touch developers, will be a demonstration of what various kinds of bugs look like in Cocoa and Cocoa Touch applications, along with how to hunt down and fix those bugs.

Whichever conference you go to, have fun, and if you’re coming to the MacTech conference, I hope to meet you there.

UPDATE 2010-09-10: Added SecondConf and iPhone/iPad DevCon (thanks to Jeff in the comments for telling me about the latter).

UPDATE 2010-09-11: Added 360|iDev after its organizers followed me on Twitter. Five conferences in seven weeks—maybe some of you could get together and consolidate next year? And again a few minutes later: And 360|MacDev, making six in 11 weeks.

Audio

Sunday, June 20th, 2010

Matt Legend Gemmell tells you what to do and what not to do when making a product page. One point I felt worth expanding upon:

Either have a professional-sounding voiceover, without pauses and “um”s, with great audio quality, or don’t have a voiceover at all. Superimpose explanatory text titles instead. Be honest with yourself about how your voice sounds. If you’re the typical male engineer, your voice is probably going to be a major turn-off, and you probably can’t do talk-along without pausing, making various noises, and restarting your sentences. Get someone else to do it, or use text.

There’s nothing you can do about the innate quality of your voice, and if that sucks, then “get someone else to do it, or use text” is good advice. (Alternatively, you may be able to train yourself or be trained to speak better, depending on your exact problem. Mine used to be that I didn’t open my mouth enough, so everything sounded like I was talking through clenched teeth.)

But audio quality is something you can improve, and it matters. Care about this stuff; your potential customers do.

  • Buy a microphone. Yes, your laptop has one built-in. It sucks.

    The microphone itself may be all right, but it’s in indirect physical contact with a fan, a hard drive, and other noise-inducing parts. It’s also too far away from you. Good enough for voice chat, but not for recording. Buy a separate microphone and record with that.

  • Practice good microphone technique. Get fairly close to the microphone—about 6 inches/15 cm—and talk straight over it. Talking over it will avoid loud spikes (pops) on the recording from plosive sounds, such as the start and end of the word “pop”.

    You could buy a pop-filter and then talk straight into the microphone (through the filter), but I’m trying to keep your monetary and effort expenses low.

    Also, if you’re soft-spoken, speak up, like you’re talking to someone down the hallway. Don’t yell, but do project.

    And no matter what you do, don’t ever, ever touch the microphone, its stand, or its cord.

  • Set your gain. In simple terms, your input’s gain is how hard the microphone is listening. You want to avoid clipping—that’s where the signal maxes out (0 dB) and would go out of bounds if it could. You want the signal to be high enough for your voice to be clearly audible, while never, ever going high enough to clip.

    You’ll generally do this in the Sound pane of System Preferences, but your recording software may have its own control for this. Sound Studio does.

    The only way to find out the right amount of gain for you (and your microphone) is through experiment. Say something over and over while cranking up the gain, then go into actual recording, and when it clips, dial it back down and start over.

    Note that it is pretty hard to set the gain too low, except when it’s obviously too low, but it is very easy to set it too high. Err on the low side.

  • Edit. There are three components to this.

    • Cut out ums, ers, pauses, etc. You can record as much of that as you want—speak as you naturally do in recording. Then edit out anything between words.

      On a related note, pause for a second or so between sentences, both to breathe in and to make it easy for you to edit in re-takes.

      You may also want to write and print out an outline of the points you want to hit in your voice-over, as a map through the presentation for you to follow. Use OmniOutliner or TaskPaper, or pencil on paper (as long as you can read it easily). Read items between sentences; don’t read while you speak. Don’t write a script unless you can pull script-reading off. An outline (or script) will keep you from rambling and focus your sentences, reducing the number of ums, ers, pauses, and pointless sentences you need to cut out.

    • Remove noise. Amadeus Pro has a great tool for this. Use it.

    • Compress the edited recording. I don’t mean use a codec, I mean compress the levels—bring the quiet bits up so that they aren’t quiet anymore, while keeping the loud bits where they are (without clipping them).

      You should also use AUPeakLimiter (one of the built-in Audio Units), or an equivalent, to crank up the compressed levels to 0 dB. The difference with how you set the gain earlier is that you set the gain to keep your levels under 0 dB, whereas here, you’re amplifying to at 0 dB.

      90% of screencasts are not loud enough. I do not appreciate having to crank up my system volume to hear you. You can fix this.

All of this is not trivial, but it’s not hard, either. Practice will make it easier, and the result—clean audio on your screencast (or podcast)—will be worth it.

Setting the iPhone API documentation to iPad display mode on your Mac

Monday, April 5th, 2010

Those who’ve bought iPads have noticed that the iPhone API documentation comes in a special iPad-optimized flavor:

Basically, like an iPhone app for viewing the iPhone documentation. Here's a screenshot of the page in Safari on my Mac.

Yes, that’s desktop Safari showing it.

Contrary to my expectation, it does not use user-agent sniffing to detect an iPad. In fact, it’s detected by a JavaScript script (credit) when you go to an iPad-specific front page.

The code has a debugging feature, which they left in and you can (for now) enable to use the iPad display mode in your WebKit-based browser. Here’s how to enable it:

  1. Open any page on developer.apple.com.
  2. Open this URL:

    javascript:localStorage.setItem('debugSawtooth', 'true')
  3. Go to the iPad documentation list.

There are actually two interfaces, corresponding to the two orientations of a physical iPad. The one I showed above, with the API tree in a sidebar, is the landscape orientation; portrait moves the API tree into a pop-over, under a button labeled “Library”. The page chooses one or the other by the aspect ratio of the window.

“Sawtooth” has some drawbacks:

  • On a Mac, your scroll wheel (or two fingers) won’t work; you must drag the interface instead, which corresponds to finger-dragging on the actual device.
  • Once the interface loads, its size and orientation are fixed; it won’t adapt to a window resize until you reload. This, too, is only a problem on a Mac (you can’t resize your iPad).
  • There’s no way to copy a link to a specific document, unless you can find an internal link (the API-tree table view doesn’t count). This can be a problem if you want to link to, say, a framework reference.
  • You can’t get the Sawtooth interface unless you go through the iPad front page. If you go to a framework reference, class reference, programming guide, or other more specific page, you’ll get the regular interface. Same thing when going through the regular front page.

Even so, it’s pretty spiffy. I wish I had a version with all of the above problems fixed for viewing Mac API documentation.

* Credit: JR Ignacio found the JavaScript code and excerpted it into a GitHub paste.

Undocumented Goodness: CGSSetSystemDefinedCursor

Saturday, April 3rd, 2010

As with all undocumented private APIs, this one may change or go away any time. Use with extreme caution or, better yet, not at all.

As you’d expect from a CGS function, you’ll need a CGSConnection to use this one:

typedef void *CGSConnectionRef;
extern CGSConnectionRef _CGSDefaultConnection(void);
extern unsigned CGSSetSystemDefinedCursor(CGSConnectionRef connection, unsigned cursorID);

(I am, of course, not sure of the correct type for cursorID, although it is 32-bit.)

The valid cursor IDs range from 0 to 8; for anything else, CGSSetSystemDefinedCursor returns kCGErrorIllegalArgument. You’ll also get that error if you pass a bogus connection ref.

Here are the cursor IDs, as I’ve named them:

enum {
    kCGCursorIDArrow,
    kCGCursorIDIBeamComposite,
    kCGCursorIDIBeamInvert,
    kCGCursorIDLink,
    kCGCursorIDCopy,
    kCGCursorIDArrowWhite,
    kCGCursorIDContextualMenu,
    kCGCursorIDPinwheel,
    kCGCursorIDHidden,
};

IBeamComposite is the normal I-beam cursor. IBeamInvert is a variation that inverts what’s underneath it, instead of compositing over it. The benefit is that this shows up well on dark backgrounds (it appears white), whereas IBeamComposite blends in.

Link and Copy are the Mac OS 8.5 versions of those cursors, not the larger full-color ones introduced in Mac OS X 10.2.

ArrowWhite is exactly what it says on the box: A white-with-black-outline version of the standard Mac arrow cursor. The colors of the Windows arrow cursor with the stem length of the Mac arrow cursor.

ContextualMenu is my rough interpretation, as the stack of white rectangles next to the (black) arrow cursor looks only vaguely like a series of menu items. To my eyes, it looks more like a stack of giant hard disk platters (like the standard icon for a database that has become common recently) than a contextual menu, but I doubt that this was what they meant. This is not the contextual menu cursor from Mac OS 8 and 9.

Pinwheel is exactly what it says on the box. It’s even animated. This is the prize of the collection: If you ever want to deliberately put on the pinwheel cursor (which you should NEVER EVER DO), this is the way to do it.

Hidden is also exactly what it says on the box: A fully-transparent cursor image.

So, there you go, for your information, entertainment, and hopefully not use.

Capture Cursor

Saturday, March 13th, 2010

Most people don’t know how to get the cursor that’s currently on the screen—a useful ability, especially if you’re writing screenshot or screen-recording software. I’ve written an app that demonstrates the technique, or at least tries to.

It’s a bit flaky. The API it uses, IOFramebuffer, doesn’t tell me how many frames there are or what format they’re in, so the app assumes ARGB in native byte-order and doesn’t worry about frames. This gives wrong results more of the time than I like.

I’ve filed a request for a higher-level API, which would make the task much easier and the app much shorter.

I’ve posted a build in the repository’s downloads area, in case you’d like to see it in action. If you want to build it yourself, you’ll need to download SGHotKeysLib and put the source where the Capture Cursor Xcode project expects it to be.

UPDATE 2010-08-28: I’ve pushed a change, [9cbec7dd5169], that deletes the IOFramebuffer-based category method and uses Snow Leopard’s new, far more reliable [NSCursor currentSystemCursor] instead. I suggest you do the same.

The Green Checkmark of Acceptance

Saturday, March 13th, 2010

Here’s how answers to a question on Stack Overflow appear to the questioner:

Every answer has, below the helpful/unhelpful buttons, a hollow checkmark button.

When the questioner clicks on one of those checkmarks, it marks the answer as the accepted answer to that question, and changes the checkmark from a gray stroke to a green fill.

Everybody else reading the question will see, below the questioner’s name, an indication of how many of their questions have accepted answers. Today, for example, my questions have:

Peter Hosey
23.1k 2 14 28
67% accept rate

This indicates that I have accepted answers on four-sixths of my questions.

Sometimes, I see a comment like this semi-fictional example (written by me, based on several real examples I’ve seen) on a question whose author has a low or zero acceptance rate:

0% accept rate? You really should accept answers on your questions, or people may not answer any further questions from you.

This is a bad reason to accept answers.

The real reason to accept an answer is that you believe it’s the correct answer.

Sometimes questioners choose bad answers (deprecated APIs, hacky solution, etc.). When that happens, it’s a problem because it may lead future readers astray—they may think that this is the correct answer (because the questioner said so), without reading the other answers or the comments and finding out that this way sucks and/or there is a better one.

The same problem happens when a questioner accepts an answer because they think they have to, out of some sort of social obligation, rather than because they truly believe it is the correct answer. They may not have the correct answer yet, or there may not be a correct answer yet, but they feel like they have to accept something, so they accept the best answer they have, however good or bad it is, solely to raise that all-important number.

That sucks.

Questioners: About a day after asking a question, you should return to it, read all the answers, try them in descending order by votes, and accept the one that works and is the least hacky, for the benefit of other people who have the same question you asked. Take comments into account—something may not look hacky, but a comment may point out the hackiness.

And if there is no good answer, you don’t need to accept anything. For the same reason (the benefit of future readers), you should leave the question open.

It’s OK to have an acceptance rate that is below 100% or even low, as long as you are accepting answers that you find work and are non-hacky, on as many of your questions as you can. As long as you’re making that effort, you’re doing it right.

People who post comments like the one above: Why are you so desperate for karma? It’s not like it’s scarce or valuable. Net scores on answers are meaningful (usually), but your personal total, like mine, is next to meaningless. It’s a reward, yes, but an empty one, so I don’t see why you get all hurt when you perceive a risk that someone may not give it to you.

In summary: Don’t worry about it. Accept correct answers, write correct answers, and don’t worry about your acceptance rate or anyone else’s.

Voices That Matter iPhone Developers Conference 2010

Monday, February 22nd, 2010

I’m still Mac-only, so I still won’t be there, but here’s the information for this year:

  • The conference is in Seattle, Washington on April 24th and 25th.
  • The speakers, including Aaron Hillegass, Jonathan “Wolf” Rentzsch, Erica Sadun, and Eric Buck, will cover many different topics, including Core Data, Core Location and MapKit, in-app purchasing, UI design, and OpenGL ES.
  • Early bird pricing is $200 off until March 12th.
  • If you use the coupon code “PHBLOGS”, you’ll get another $100 off.

(I do still wonder what the “PH” stands for…)

If this sounds good to you, register for the conference.

See also Brent Simmons’s post about it. He wants to play pinball with you.

Easy main-thread performing category

Saturday, February 13th, 2010

Anyone who’s ever had to perform a message on the main thread knows how ugly the code can be:

[obj performSelectorOnMainThread:@selector(receiveAnswer:)
                      withObject:[NSNumber numberWithUnsignedInteger:42]
                   waitUntilDone:NO];

I’ve written a category that changes the above code to something much simpler:

[[obj performOnMainThread_PRH] receiveAnswer:42];

Notice that not only is the code much easier to read and to write, but you no longer need to box and unbox primitive values.

You can get the code, under a BSD license, from the higher-order main-thread perform repository.

Back when I originally mentioned my creation of the category on Twitter, Dave Dribin replied that he had already written one of his own. His is part of his DDFoundation framework, and requires DDInvocationGrabber (a fork of Jonathan Wight‘s CInvocationGrabber), also part of that framework.

My category requires nothing but Cocoa and is not part of a larger framework. That said, it’s interesting to read in his code how the functionality of the Invocation Grabber makes his implementation nothing but a thin wrapper around it. (Also, his version lets you say waitUntilDone:YES.)