Archive for the 'Programming' Category

macOS installer cards

Thursday, September 29th, 2016

Being both a creature of habit and a digital packrat, I never install macOS from the “Install macOS” app.

Instead, what I do is have a cache of 8 GB class-10 microSDHC cards (usually bought from Micro Center, but they make great Amazon filler items, too), which I permanently assign as macOS installer media.

(I would buy SDHC cards, but Micro Center sells the microSDHC cards for cheaper—50¢ cheaper as I write this.)

Populating the card

Once you have a card, you’ll need to put the bits on it. DiskMaker X is the easiest way to do this, though there is also a manual process.

Labeling it

Anonymous cards all of the same capacity and speed and possibly even description does not scale well as an inventory system. Ideally, your card comes in a clamshell case, like the Micro Center ones do, or you can reassign such a case that came with another card. You can probably even buy empty microSD cases, like you can CD jewel-cases.

I have a Brother labeler that I use to print a small label with the release’s version and marketing name on it: “OS X 10.7 Lion”, “OS X 10.8 Mountain Lion”, “OS X 10.9 Mavericks”, “OS X 10.10 Yosemite”, “OS X 10.11 El Capitan”, and now “macOS 10.12 Sierra”. That label goes on the front of the case.

Photo of my flash memory cards on which I have the past several years' macOS installers. Lion was on an SD rather than microSD card; Micro Center have changed their case form factor a few times; I started printing rather than hand-writing labels with 10.9, and I haven't yet labeled the card that will go to Sierra.
I wrote this while the Sierra installer application was downloading.

And then done

The very pretty installation volume that DiskMaker X created for Sierra, complete with window background matching the installer icon.

Now you have labeled, permanent installer media that you can use forever (or however long the cards last). If you ever need to roll back to an old version, reinstall the dot-zero from the card and then combo-update up to whatever version you want.

So you want to run an open-source project

Sunday, September 11th, 2016

How much of this you’ll want to do will depend on just how much of a Thing you want your project to be, on a scale from “I’m just gonna drop some code on GitHub” to “I want to wear the title of ‘maintainer’”.

GitHub

You don’t have to use GitHub as such; Gitlab and Bitbucket are alternatives, and they’re all more or less equivalent. But GitHub has network effects in its favor: Most potential contributors are there.

“Contributors” doesn’t just mean code. Anybody can clone your repository, check something in, and email you the patch. But writing issue tickets and editing the wiki (if you choose to have one) are valuable, too, and those typically require an account.

Your project on GitHub (or wherever) will include one or more of the following:

  • Code/contents (the Git repository from which GitHub and Gitlab take their names)
  • An issue tracker (more on that later)
  • Forks (other people’s copies of your repository, to which they have their own access to push changes)
  • Merge requests (changes made in one fork, then submitted “upstream” toward the original repository for consideration and possible integration into the “main” code base)
  • A wiki (documentation)
  • A website

Travis

Like with GitHub, there are alternatives. Travis is the one I’ve used.

Travis is a continuous-integration system. Whenever someone pushes changes to your repository, or submits a merge request, Travis will build your project and run your tests, according to instructions you provide. It’ll then provide an indication—on GitHub, right on the merge request page—of how well or poorly that went.

There’s a related service called Coveralls that tracks the code coverage in your project—how much of your code is being run by your tests. Code paths that the tests aren’t going through are potential bug sites: When that code path does get traveled, what happens might not be what you expect.

Coveralls tells you how much of your code was exercised, as a percentage, and also gives you a line-by-line map of what code was run and what code wasn’t.

Project infrastructure documents: README, LICENSE, CONTRIBUTING/CODE_OF_CONDUCT

(If you write these in Markdown, they’ll have the .md extension: README.md, etc.)

README

Your README is your Getting Started guide for both users and contributors. Awesome Labs has a template you can follow (optimized for iOS libraries); the basic points you should cover include:

  • What is the product? What does it do? Why do I need it?
  • What does it look like? (Screenshots/example terminal sessions/example code)
  • What does it require?
  • How do I install it?
  • How do I use it, at least in the most common case?
  • How do I uninstall it (if applicable)?

LICENSE

This is where you put your statement of copyright, and the license agreement for your program, outlining what sorts of copying you explicitly allow and which ones you explicitly don’t. Common ones include variants of the BSD and MIT licenses, GPLv2, and GPLv3.

GitHub suggests a few popular licenses when you create a new repository, and provides an advanced section with many more licenses where you can compare their finer points.

CONTRIBUTING

This file describes guidelines for contributors, including:

  • Requirements of the contributions themselves. Examples of such requirements include:
    • Please add tests for any new features, and update tests for any behavior changes.
    • Please run the tests and be sure they pass before submitting your merge request.
    • Your change must pass all tests on all supported platforms. See the README for our platform requirements.
    • Please match the style of existing code. In particular, please use one tab/four spaces/eight spaces/6.34 spaces per indent, and please put opening braces ({) on the same line/their own line.
    • Please submit the complete, original series of changes—don’t squash merge.
    • Please squash-merge your changes—we don’t want the complete, original series of changes.
  • Information on how to contribute, if it differs at all from the usual “fork the project, commit and push your change, then submit a merge request”.
  • Requirements of contributors (e.g., a code of conduct such as the Contributor Covenant).
  • Info about any project mailing lists, IRC channels, Slack channels, etc., perhaps with their own code of conduct, such as the Community Covenant.

Codes of conduct are important for making explicit the standards of behavior you expect from project members and contributors. Don’t tolerate jerks—if somebody breaks your rules, even if their contributions are good on technical merit, kick them out. (You define what “kick them out” means, and may want to document it in your CONTRIBUTING file.)

Known jerks continuing to be tolerated/welcomed in your project is a visible “stay away” sign to other people who might otherwise bring equally good contributions with less hassle.

Issue tracker

Issue trackers are two things:

Your project management tool

You use issue tickets to track the work that has been done and that needs doing.

Most issue trackers provide a category/tag/label feature to identify on each ticket what sort of work it is. You can also create labels for priorities, if you want to track how essential or postponable each item is. (Some issue trackers have a separate priority field.)

Have a “help wanted” or “up for grabs” tag you can put on bugs that you don’t want to do yourself, and mention it in your CONTRIBUTING instructions. “help wanted” bugs are the ones you explicitly reserve for new contributors to do. Most should be simple or good introductions to the codebase, but you can also include harder jobs where (for reasons of your own time) you’re willing to accept the patch but not write it yourself.

You may also be able to create milestones for future releases you’re planning, into which you put each ticket to track when that work should be done (or at least landed), and, inversely, what work must be done before a release can ship.

Everyone else’s wish list

You’ll get a lot of bug reports and feature requests. Many of them will be duplicates, which you’ll close with a reference to the original/anointed ticket for the same bug/request/task.

If your project grows large/popular enough, you may choose to lock down the issue tracker so only contributors can add or edit tickets, and then take requests and bug reports instead through a support email address or form. Whoever reads that email then updates tickets as needed and sends ticket links or ticket numbers to support querents.

You will need to say no to some things, and some people will be displeased. You will also need to postpone some things because they aren’t as important or there isn’t a clear path forward (e.g., no steps to reproduce), and some people will be displeased because they want their feature/a fix now. There are no two ways about it; you are going to make some people unhappy. It’s not your fault.

Be polite; be fair; be the better person. Sometimes that still won’t be enough—some people won’t accept anything less than “yes, right away, it’ll be fixed in the next ten minutes”. And that sucks. But it’s not your fault.

Code review

Code review is where one or more contributors (and/or the maintainer) look at a proposed (or even already committed) change, looking for errors and potential improvements.

Some guidelines:

  • Review the patch, not the person. Don’t call the contributor an idiot or accuse them of wasting their time. Be gentle; be constructive. Yes, even if the patch is total trash—make it a teachable moment. Recognize language barriers and early learners. (But also don’t waste your time excessively—sometimes you will have to give up on someone who is a time sink. Even then, be nice.)
  • Prefer questions to statements. Be open-minded, not reactionary. Be willing to accept that an approach that isn’t what you would have done may be better. At the very least, be willing to accept it if it isn’t worse.
  • Praise good ideas, novel (but not overly clever) approaches, attention to performance, reliability, and ease-of-use, and hard work. Code review isn’t just a place to point out what they did wrong—it’s also a place to appreciate, explicitly, what they did right.

The simplest form is to look at the diff (the proposed change) in each merge request. You can comment on the merge request as a whole, and, on most services, on individual lines within the diff. Use these comments to ask for clarification, ask for documentation/comments, suggest changes, or suggest alternative approaches.

There are dedicated code review tools, such as Phabricator and ReviewBoard. I’ve used Phabricator; it’s good. I haven’t used ReviewBoard.

On a GitHub project, I typically just use the built-in merge request interface.

Package managers

They are many, and which ones you have to choose from will depend on what you’re making. For libraries for Apple platforms, there are CocoaPods and Carthage. For tools for macOS, there are Homebrew, MacPorts, and Fink.

Your README should include information on which package managers you support, and how to obtain your software through them. Expect people to request that you add your software on other package managers, or somebody to add it themselves and other people to subsequently complain to you when it doesn’t work. You decide what you do when these happen.

Documentation

It should start with your README, but ideally should not end there.

If you’re making a library, have header/API documentation. On Apple platforms, Xcode now has built-in support for this; it’ll display the contents of your documentation comments when you option-click a symbol in C, Objective-C, or Swift code.

Another place to put documentation (and also do broader planning than fits comfortably in an issue ticket) is your project’s wiki.

Colorized man pages: Understood and customized

Monday, August 15th, 2016
man() {
    env \
        LESS_TERMCAP_mb=$(printf "\e[1;31m") \
        LESS_TERMCAP_md=$(printf "\e[1;31m") \
        LESS_TERMCAP_me=$(printf "\e[0m") \
        LESS_TERMCAP_se=$(printf "\e[0m") \
        LESS_TERMCAP_so=$(printf "\e[1;44;33m") \
        LESS_TERMCAP_ue=$(printf "\e[0m") \
        LESS_TERMCAP_us=$(printf "\e[1;32m") \
            man "$@"
}

Screenshot of Terminal showing the zsh manpage with the above customizations.

Pretty neat, right? Let’s tear it apart, see what it’s doing, and think about how we might customize it to suit ourselves.

(more…)

Unicode date formats, YYYY?!

Saturday, October 24th, 2015

Last year, I tweeted:

This year, I noticed that the problem comes earlier, so I sent out an early reminder:

But exactly what problem am I referring to? Dan Wood wondered exactly what the YYYY issue is and what languages it affects.

So here’s a short explainer, not bound by the constraints of Twitter.

Year for week-of-year

You may have heard of “ISO 8601 format” (2015-01-01), but in fact, that’s only one of three formats that ISO 8601 defines:

  • Calendar format: 2015-01-01
  • Week format: 2015-W01-04
  • Ordinal format: 2015-001

The ordinal format is straightforward: It’s the NNNth day of the calendar year.

But the week format does not work that way. The first week of a year is not even guaranteed to contain January 1st! Rather, -W01-01 (the first day of the first week) is the Monday of the week that contains the year’s first Thursday. (Yes, that is the actual definition from the ISO 8601 standard.)

As such, ISO defines a parallel track of years that have the same year numbers as calendar years, but start and end on different dates (and always start on a Monday). ISO-week-year 2015 starts on the Monday of the week containing 2015’s first Thursday; that Monday is Monday, 2014-12-29 (2015-W01-01). 2015-01-01 is Thursday, 2015-W01-04.

Unicode date formats

YYYY and yyyy are Unicode date format patterns. These offer quite a bit more flexibility than the old str[fp]time(3) formats, particularly in choosing different representations of the same value (e.g., “September” vs “Sep” vs “09” vs “9”).

  • YYYY is defined as the “year for week-of-year”: that is, the year for ISO week dates.
  • yyyy is defined as the calendar year.

Whom this affects

The second part of Dan Wood’s question is what languages this affects.

NSDateFormatter* and CFDateFormatter both accept the Unicode date format syntax.

Contrary to what I’d previously assumed, PHP does not use Unicode date formats. As befits PHP, it uses something that looks the same but works subtly differently: “Y” is always the full calendar year, whereas “y” is a two-digit calendar year. ISO week years are “o”.

I actually don’t know of any others. Feel free to chime in in the comments if you know any other languages or frameworks that include built-in support for Unicode date formats.

* On the Mac, an NSDateFormatter configured for “10.0 behavior” accepts str[fp]time(3) formats; “10.4 behavior” is Unicode date formats. All other current Apple OSs, including iOS, have NSDateFormatters with 10.4 behavior only. CFDateFormatter has never supported str[fp]time(3) formats.

Alternatives to “guys”

Friday, June 19th, 2015

When you’re addressing a mixed-gender or unknown-gender group, you should not use the word “guys”.

(Everything in this post also applies to “dudes” and “fellows”, and the singulars of all three. For example, “the IT guy”.)

The word refers to people who are male. It doesn’t matter what you meant by it in a particular instance, or how you as an individual tend to use it: That is what it means. That is what it conveys. That is what people hear when you say it.

As Julia Evans found, different usages vary in how they’re received, and there’s almost always a difference by gender. You’re more likely to think “guys” is gender-neutral if you’re a guy.

When you use it to address people of mixed or unknown gender, you reinforce the idea of male-as-default: This masculine word can refer to anybody! Funny how that doesn’t work for feminine words.

When you use it to address people of mixed or unknown gender, you erase the non-male people in the audience: Everybody here is guys! There’s nobody else here, no non-guys at all, no, sir.

So stop it. Stop saying “guys”.

(Except, of course, when you really do mean a group entirely of guys, like a men’s sport team.)

You may think it’s perfectly normal. It’s common, and that’s different. “Normal” implies healthy, and this isn’t healthy.

If you start paying attention, it won’t sound so “normal” after all, once you start noticing every time somebody refers to people who aren’t all guys as guys.

You might think “OK, I’ll say ‘guys and girls’ or ‘ladies and gentlemen’ instead.” Don’t do that. That does not include everybody: There are more than two genders, and not everybody inhabits any of them.

We can do better than that. We can include everybody.

So, what should you say instead?

These are words and phrases that include rather than exclude. That acknowledge rather than erase.

If you have other alternatives to suggest, please do suggest them in the comments.

The to-do graph

Sunday, December 21st, 2014

Earlier this year, I started practicing GTD.

I’m not very good at it yet. But I’ve learned some things.

And I’ve proven that lists suck.

A list is a single, flat, ordered collection of items. In GTD, you’ll have one list per project, and each item is an action.

This makes sense if you consider the list to be a record of everything you did toward that project, in order, written before instead of after.

But who does that?

Who micromanages the order of things they’re going to do before they do it?

Who has such perfect foresight?

Who has that kind of time?

Moreover, the order of a list is implicitly transitive: Every item in the list must come after every item before it and before every item after it.

That isn’t true: Some things can be done at the same time, or in either order. For example, you can work on assets while you install Xcode, and then you can work on assets before code or code before assets.

A list is a serial queue. No work can proceed until the work before it is finished.

The alternative to that is to throw out the ordering altogether: Put items in in the order you think of/receive them, and then every time you want to start one, scan through the whole list until you find something that you haven’t done and can do.

It’s a choice between false information—order relationships that don’t really exist—and no information.

The latter seems worse: Some of the orders are true, and therefore valuable, so why throw them all out?

How can we avoid that?

What makes the true orders true? Why do those actions have to be done before those other actions?

Dependencies.

This action must be done before that other action because the other action depends on it.

Some actions depend on other actions. Some actions depend on multiple other actions.

This is a graph.

That’s what I’ve switched to. I still use OmniFocus (version 1), but only as an inbox; I migrate those items to my to-do graph, which I keep in OmniGraffle.

Example graph of two projects, “Build new app” and “Work on existing app”, with actions such as “Create Xcode project”, “Fix bug in frobulator”, and “Add BSP reticulation”.
You can tell this is a made-up example because some of these actions are not concrete enough to be proper GTD.

The graph enables me to express dependencies without making up false orderings. Items that can be done in parallel are in parallel.

I mainly edit the outline, rather than the boxes on the graph directly. You could use something like OmniOutliner or TaskPaper, but those can’t visualize the graph. OmniGraffle has an “auto layout” (no relation to the Cocoa feature) option that automatically creates and arranges boxes in the graph corresponding to items in the outline.

The top-level items in the outline, the roots of the graph, are goals. I typically write these as high-level imperative sentences such as “build initial version of app”.

All, or occasionally nearly all, of the other nodes are indivisible actions. Each is a single concrete step toward the goal.

The leaf nodes are “next actions”: At any time, I should be able to pick a next action as the next thing I’m going to do.

I also create nodes for other people’s actions that I’m waiting on. These nodes look like “So-and-so: Do such-and-such”. When that happens, I take it off; its parent—if it has no other dependencies—then becomes a next action.

I set OmniGraffle to lay the graph out as an upward tree, so that each project actually does look tree-like, with the “root” at the bottom.

To mark an action as done, I have two choices: I can set the node’s font to strike-through, or just delete it. I typically strike through my own completed actions, and delete obviated actions, completed actions by others, and completed goals.

You’ve gathered by now that OmniGraffle lacks some things for this.

  • It wasn’t designed to be used as a to-do list, so it lacks a concept of “done”. Instead of a checkbox, I have the Font Panel.
  • Similarly, there are no priority or due-date options. I generally don’t use these, but some projects do, in fact, have a due date, or greater or lesser priority than other projects, and it’d be helpful to track that.
  • The outline editor, which superficially looks like a mini OmniOutliner, lacks or changes about half of OmniOutliner’s keyboard commands. I really wish that if the keyboard focus is on the outline, that it would just respond exactly as OmniOutliner would to every possible keypress.
  • The option to have a node pinned to the far (top) row of the tree is a per-node option, so I can’t have it automatically lay out all next actions in the same row.
  • The outline structure, like OmniOutliner’s, means I cannot have multiple nodes depend on the same action—which they very well could in reality.

I have two points:

  • A graph is a much better way to express to-dos than a flat list.
  • There currently isn’t a Mac app ideally suited to this. OmniGraffle is a great graph editor, but I’m using it for a purpose it wasn’t designed for. I’d pay good money for an app of OmniGraffle’s quality and basic nature, but optimized for to-do keeping.

Simple starter Cocoa app ideas

Wednesday, December 11th, 2013

Inspired partly by tonight’s Hour of Code, here are some index-card-sized outlines of some simple app projects you can make as someone new to Cocoa.

Text editor/word processor

  • Document-based Mac app
  • In document window: NSTextView
  • Use NSAttributedString to read/write document data
  • Document types:
    • public.plain-text
    • public.rtf
    • com.apple.rtfd
    • com.microsoft.word.doc
  • Extra credit:
    • Add a ruler (NSRulerView)

Picture viewer

  • Document-based Mac app
  • In document window: IKImageView
  • Use CGImageSource to read image (picture) & its properties
  • Document types:
    • public.png
    • public.jpeg
  • Extra credit:
    • Floating inspector panel showing the properties in an NSTableView
    • Color-correction panel (IKImageEditPanel)
    • Support folders (public.folder): display images from folder in IKImageBrowserView

PDF viewer

  • Document-based Mac app
  • In document window: PDFView
  • Use PDFDocument to read from .pdf file
  • Document types:
    • com.adobe.pdf
  • Extra credit:
    • Toolbar with zoom in/out buttons, zoom % field, page number field

How to read what I’ve been writing

Sunday, August 11th, 2013

You might have noticed that this blog of mine has gotten mighty quiet on the sort of programming-related (especially Cocoa-related) topics I historically have written about here.

There have been, and will continue to be, occasional exceptions, but, for the most part, this will remain the case for the foreseeable future.

So, where do I write about programming nowadays?

MacTech magazine.

Cover of the August 2011 issue of MacTech magazine.
The first issue with an article of mine in it.

Here’s some of what I’ve written about:

  • C and Objective-C basics
  • Introduction to NSOperationQueue
  • Uses of GCD besides dispatch_async (this one was split over two issues)
  • How Cocoa and Cocoa Touch use blocks
  • A sampling of available developer tools, both Apple and third-party (co-written with Boisy Pitre)
  • Reviews of developer documentation viewers
  • Using Quick Look
  • Practical applications of Core Image

If you want to read my previous articles, they sell old print issues for $10 each, and they sell old issues from January 2012 onward in their iPad app for $5 each.

If you want to read future articles, it’s cheaper to subscribe: iPad subscriptions are $11 (in-app) for three months, and print subscriptions are $47 for a year (or cheaper with certain coupons).

I’ve got some good stuff coming up. The immediate next thing is a two-parter on essential tools and best practices for developers. Part 1 should be in the August issue.

How to decide which ownership keyword to give a property

Sunday, July 21st, 2013
  1. If the object may come in a mutable variant (like NSString has NSMutableString), use copy, so that you don’t end up holding a mutable object that somebody mutates while you’re holding it.

  2. If you will own the object, use strong. (Optionally, leave it out, because strong is the default for objects.)

  3. If the object will own you, use weak. For example, a table view weakly references its data source and delegate, because they are very often the VC or WC that indirectly owns the table view.

  4. If the object cannot be referenced with a true weak reference, use unsafe_unretained. A select few classes*, such as NSTextView, do not support weak references. You will get an exception at run time if you try to establish a weak reference to such an object. You will have to use unsafe_unretained instead.

    The reason weak is preferable is because, if the object dies while being weakly referenced, the weak references automatically get changed to nil. That’s also the part that certain classes are allergic to. unsafe_unretained doesn’t have this feature, which is why the classes that don’t support weak can still be referenced with unsafe_unretained—but, the caveat is that if you use unsafe_unretained, you must ensure that the object will never get deallocated while you are weakly holding it—i.e., that you let go of your unsafe unretained reference before the last owner of the object lets go of that ownership.

  5. Never use assign. For objects, unsafe_unretained is synonymous and clearer (it explicitly says that it is unsafe, which it is). For non-objects (such as NSUInteger and CGFloat, leave it out—assign is the default for such values.

* The Transitioning to ARC FAQ includes a complete list of classes that do not support weak references.

(This is an expanded version of a comment I posted on Stack Overflow.)

A language-contrast exercise

Sunday, March 31st, 2013

Python’s str type has a translate method that, given a second string representing a translation table, returns a new string in which characters in the first string are looked up at their ordinal positions in the translation table and replaced with the characters found at those positions.

The identity translation table, performing no changes, is table[i] = i. For example, table['!']* is '!', so exclamation marks are not changed. If you made a table where table['!'] were '.', exclamation marks would be changed to periods (full stops).

I’d like to see implementations of a program that does that, with the input string encoded in UTF-16 and the translation table encoded in UTF-32 (a 0x11000-element long array of UTF-32 characters), with the table initialized to its identity: table[i] = i.

And yes, you need to handle surrogate pairs correctly.

Some languages that I would particularly like to see this implemented in include:

  • C
  • Haskell
  • LISP
  • A state-machine language (I don’t know of any off-hand; this might be their time to shine)

I know how I would do this in C, and I’m sure I could bash something out in Python, but how would you do this in your favorite language?

As a test case, you could replace “ and ” (U+201C and U+201D) with « and » (U+00AB and U+00BB).

If you want to post code in the comments, <pre>…</pre> should work. Alternatively, you can use Gist.

* I’m using the C sense of '!' here. In Python, this would be table[ord('!')], since characters in Python are just strings of length 1, and you can’t index into a string with another string; ord is a function that returns the ordinal (code-point) value of the character in such a string.

Ebooks of “The Structure and Interpretation of Computer Programs”

Sunday, December 30th, 2012

There are no official ebooks of SICP, but there are a few unofficial ebook conversions from the free web version. Here are the two best versions I’ve found:

How to create a new class in Xcode 4

Tuesday, October 16th, 2012
  1. Choose which group you want to put the class into. You must do this first, before anything else, or you will have even more work to do later.
  2. Right-click on the group or on any file reference inside it. You must create the class this way, or you will have even more work to do later.
  3. Choose “New File”. You use this same command to create classes, nibs, storyboards, plists, and files of several other types. There is no “New Class” command, and the command is called “New File” even though it often creates multiple files.
  4. Choose which platform you want to create this class for. You must choose exactly one, even if your project is cross-platform, and even if this class will be cross-platform (e.g., NSManagedObject subclass). Even if your project is single-platform, the platform for which the file(s) should be created will not be inferred from the project’s platform.
  5. (Optional) Choose which group of file templates to look in.
  6. If you performed step 5, and guessed wrong, correct yourself. (For example, OS X nibs are not among the “Resource” templates, even though they go in the Resources subfolder. You want “User Interface”.)
  7. Choose which template to use.
  8. Set the class name.
  9. Set the superclass name.
  10. Turn on “With XIB for user interface” if you’re creating a window controller or view controller.
  11. Choose where to save the file(s).
  12. Nibs created along with a WC or VC are created unlocalized (outside of a .lproj folder), so if you did step 10, select the nib and click “Make localized”.

It seems to me that there is room here for optimization.

Characters in NSString

Sunday, June 3rd, 2012

Working with Unicode in any encoding but UTF-32 (which we don’t use because, for nearly all text, it wastes tons of memory) has some pitfalls:

As UTF-8’s name implies, its code units (roughly speaking, character values) are 8 bits long. ASCII characters are all one code unit long (in UTF-8, this means that 1 ASCII character == 1 byte), but any character outside of that range must be encoded as multiple code units (multiple bytes). Thus, any single character above U+007F will end up as more than one byte in UTF-8 data.

This first observation is not limited to Emoji; it’s true of most characters in Unicode. Most characters take up more bytes in UTF-8 data than “characters” in an NSString.

As we’ll see a couple of tweets later, though, even NSString’s length can be problematic.

UTF-16 data may begin with a single specific character that is used as a byte-order mark.

(I should point out, just in case it isn’t obvious, that code units in UTF-16 are two bytes, as opposed to UTF-8’s one-byte code units. This still isn’t enough to encode any Unicode character in a single code unit, though, which will become important shortly.)

The BOM’s code point is U+FEFF. If you encode this in big-endian UTF-16 (UTF-16BE), it comes out as 0xFEFF, exactly as you’d expect. If you encode it in UTF-16LE, it comes out as 0xFFFE, which is not a character.

Thus, a BOM indicates which byte-order all of the subsequent code units should be in. If the first two bytes are 0xFFFE, you can guess that it’s 0xFEFF byte-swapped, and if that’s true, then the rest of the code units (if indeed they are UTF-16) are little-endian. The BOM isn’t considered part of the text; it’s removed in decoding.

The BOM is also used simply to promise and detect that the data is UTF-16: If you see one, whichever way it is, then the rest of the data is probably UTF-16 in one form or the other.

So it’s useful to include the BOM for data that may be saved somewhere and later retrieved by something that may need to determine its encoding.

-[NSString dataUsingEncoding:] includes the BOM, so that you can just take the data and write it out (if it is the whole data—more on that in a moment). Since the data it returns has the BOM character in it, the data’s length includes the two bytes that encode that character. -[NSString lengthOfBytesUsingEncoding:], on the other hand, counts only the bytes for the characters in the string; it does not add 2 bytes for a BOM.

A corollary to this is that if you send dataUsingEncoding: to an empty string, the data it returns will not be empty. So, are you testing whether the string you’ve just encoded is empty by testing whether the data’s length is zero? If so, your test is always succeeding/always failing.

One problem with the BOM is that it should only appear at the start of the data, which means you can’t just encode a bunch of strings using dataUsingEncoding: and then, say, write them all to a file or socket one after another, because the output will end up with BOMs (or, worse, invalid characters, namely U+FFFE) sprinkled throughout.

The naïve solution to that is to staple strings together, then encode and write out the entire agglomeration. If performance (particularly memory consumption) is an issue and you’re writing the output out piecemeal anyway, a more efficient solution would be to use getCharacters:range: or getBytes::::::: to extract raw UTF-16 code units into your own buffer.

Unicode, the character set, can hold up to 0x20000 characters. Foundation’s unichar type is 16-bit, which means it can only hold values within the range of 0x0000 to 0xFFFF.

This is a problem for all of the characters above 0xFFFF, including the Emoji characters, which are in the range from U+1F300 to U+1064F.

UTF-16 addresses this problem by means of a system called surrogates. It’s similar to what UTF-8 does for the same problem, except that the values that UTF-16 uses are within two defined ranges of actual characters.

Surrogates come in pairs. The first one is called the high surrogate, and the second is called the low surrogate. The ranges of usable characters are named accordingly.

The bomb character, 💣, encodes to UTF-16 as 0xD83D 0xDCA3.

NSString and CFString use the word “character” all over the place, but what they really mean is “UTF-16 code unit”. So the aforementioned single-character string actually contains two “characters”:

2012-06-03 13:15:45.498 test[14761:707] 0: 0xD83D
2012-06-03 13:15:45.501 test[14761:707] 1: 0xDCA3

Beware of such things when enforcing length limits. Be sure of whether you’re counting ideal characters or code units in some encoding. Also make sure you’re clear on whether a destination with a length limit (e.g., Twitter) counts up to that limit in ideal characters or in code-units in some encoding.

Also, as @schwa mentions in the same tweet, this all applies to characterAtIndex: as well (indeed, everything in NS/CFString that talks about “characters”). So, for example, [bombString characterAtIndex:0UL] will really retrieve only half of the character.

As noted above, each of these Emoji characters is encoded in UTF-16 as two code units in a surrogate pair. A surrogate pair has a high surrogate and a low surrogate.

The high surrogate identifies a range of 210 characters; the low surrogate identifies a specific character within that range. Since the poop character and the bomb character are within the same range, they have the same high surrogate—i.e., the same first “character” in their NSString/UTF-16 representations.

As the example demonstrates, just because a string contains only one ideal character doesn’t mean that characterAtIndex:0 will return 1.0 character. It may return 0.5 characters.

Greg Titus answered this one for me:

No worries about surrogate pairs or lengths greater than 1 for characters that exist in ASCII (≤ U+007f).

Recap

  • “Characters” in NS/CFString are really UTF-16 code units.
  • Some characters in Unicode—including, but by no means limited to, Emoji—are outside the range of what a single UTF-16 code unit—a single NSString “character”—can hold.
  • Therefore, do not assume that a single character is a single “character”.
  • Neither should you assume that a single character will be a single byte in UTF-8. That sounds obvious, but…
  • Both of the preceding rules can trip you up when checking against length limits (or sending text to something else that will do such a check). Make sure you know whether the limit is in ideal characters (U+whatever) or code units in some encoding, and make sure you count the appropriate unit and do so correctly.
  • Those rules also have a way of tripping you up whenever you extract a single “character” at a time from a string. You should probably only do this when looking for known ASCII characters (e.g., for parsing purposes), and even then, please consider using NSScanner or NSRegularExpression instead.

On the API design of CGBitmapContextCreate

Friday, June 1st, 2012

Let’s review the prototype of the CGBitmapContextCreate function:

CGContextRef CGBitmapContextCreate (
 void *data,
 size_t width,
 size_t height,
 size_t bitsPerComponent,
 size_t bytesPerRow,
 CGColorSpaceRef colorspace,
 CGBitmapInfo bitmapInfo
);

The arguments:

  • data may be a pointer to pixels. If you pass NULL, the context will create its own buffer and free that buffer itself later. If you pass your own buffer, the context will not free it; it remains your buffer that you must free after you release the context, hopefully for the last time.
  • width and height are what their names say they are, in pixels.
  • bitsPerComponent is the size of each color component and the alpha component (if there is an alpha component), in bits. For 32-bit RGBA or ARGB, this would be 8 (32÷4).
  • bytesPerRow is as its name says. This is sometimes called the “stride”.
  • colorspace is a CGColorSpace object that specifies what color space the pixels are in. Most importantly, it dictates how many color components there are per pixel: An RGB color space has three, CMYK has four, white or black has one. This doesn’t include alpha, which is specified separately, in the next argument.
  • bitmapInfo is a bit mask that specifies, among other things, whether components should be floating-point (default is unsigned integer), whether there is alpha, and whether color components should be premultiplied by alpha.

The most immediate problem with this function is that there are so damn many arguments. This is especially bad in a C function, because it’s easy to lose track of what each value specifies, especially when so many of them are numbers. Suppose you want to make an 8-by-8-pixel grayscale context:

CGContextRef myContext = CGBitmapContextCreate(NULL, 8, 8, 8, 8, myGrayColorSpace, kCGImageAlphaNone);

Now, without looking at the prototype or the list, which argument is bitsPerComponent, which is bytesPerRow, and which are width and height?

Objective-C’s names-and-values message syntax can help with this, as we can see in the similar API (for a different purpose) in NSBitmapImageRep:

NSBitmapImageRep *bir = [[NSBitmapImageRep alloc]
    initWithBitmapDataPlanes:NULL
                  pixelsWide:8
                  pixelsHigh:8
               bitsPerSample:8
             samplesPerPixel:4
                    hasAlpha:YES
                    isPlanar:NO
              colorSpaceName:NSCalibratedRGBColorSpace
                 bytesPerRow:8
                bitsPerPixel:8*4];

But this has other problems, notably the redundant specification of bitsPerPixel and samplesPerPixel. With that and the isPlanar argument, this method takes even more arguments than CGBitmapContextCreate. More importantly, it doesn’t solve the greater problems that I’m writing this post to talk about.

EDIT: Uli Kusterer points out that bitsPerPixel is not redundant if you want to have more bits not in a component than just enough to pad out to a byte. That’s a valid (if probably unusual) use case for NSBitmapImageRep, so I withdraw calling that argument redundant.

I’m going to use the example of both of these APIs, but mainly CGBitmapContextCreate, to talk about a few principles of API design.

The first is that it should not be possible for an object to exist in an unusable state. From the moment a freshly-created object is returned to you, you should be able to use it without it blowing up in your face.

From this principle follows a corollary: Everything an object needs in order to function, it should require when you instantiate it. Otherwise, the object would exist without the needed information—and thereby be unable to function—until you provide it.

It might seem that these APIs are as long as they are in order to uphold that principle. After all, a bitmap context needs to have someplace to put its pixels, right? (In fact, CGBitmapContextCreate‘s buffer argument was required until Snow Leopard and iOS 4.) It needs to know what format the pixels should be in, right?

Now for the second principle: Any information that an object does not need in order to function should be omitted from initialization and provided afterward. In Objective-C, the most common means of this post hoc specification are readwrite properties and delegate messages. Generally, for anything that could be specified in the initializer, the post hoc way to specify it would be via a property.

We’d like to invoke the second principle and move things out of the initializer, but that would seem to conflict with the first principle: What can we move that the context does not require?

The resolution is in a third principle—one that is not specific to APIs, but applies to all interfaces, including user interfaces: An interface should have reasonable defaults for as many parameters as it can—it should only require the user to provide values for parameters for which no default can be reasonably chosen in advance.

With that in mind, let’s look at some of CGBitmapContextCreate‘s arguments and see how we might apply the reasonable-defaults principle to simplify it:

  • bitsPerComponent, bitmapInfo, and colorspace: Most commonly, the caller will want 8-bit RGBA or ARGB, often with the goal of making sure it can be used on the graphics card (either by way of a CG- or CALayer or by passing the pixels directly to OpenGL). That’s a reasonable default, so these three can be eliminated.

    We could make them properties, but there’s an alternative: We could dynamite bitmapInfo and merge some of its values with bitsPerComponent in the form of several pixel-format constants. You’ve seen this approach before in QuickTime and a few other APIs. CGBitmapContext only supports a specified few pixel formats anyway, so this simply makes it impossible to construct impossible requests—another good interface principle.

  • bytesPerRow: Redundant. The number of bytes per row follows from the pixel format and the width in pixels; indeed, CGBitmapContextCreate computes this internally anyway and throws a fit if you guessed a number it wasn’t thinking of. Better to cut it and let CGBitmapContextCreate infer it.

    Making you compute a value for bytesPerRow does provide an important safety check, which I’ll address shortly.

    EDIT: Alastair Houghton points out another case for keeping bytesPerRow. This doesn’t apply to CGBitmapContextCreate, which rejects any value that doesn’t follow from the pixel format and width in pixels, but could be valid for NSBitmapImageRep and CGImage.

  • data (the buffer): Since Snow Leopard and iOS 4, the context will create its own buffer if you don’t provide one. That makes it explicitly optional, which means it is not required.

The only arguments that are truly required are the width and height, which tell the context how many pixels it should allocate its initial buffer for in the given (or default) pixel format.

In fact, if we take the above idea of replacing three of the arguments with a single set of pixel-format constants, then we don’t actually need to make any of the properties readwrite—there isn’t any reason why the owner of the context should be changing the pixel format on the fly. You might want to change the width or height, but CGBitmapContext doesn’t support that and we’re trying to simplify, not add features.

So, what problems do the current APIs solve, what problems do they raise, and how would we address all of both problems?

  • Specifying the pixel format (bitsPerComponent, colorspace, bitmapInfo) up front saves the context having to reallocate the buffer to accommodate any pixel-size changes.

    If we simply removed the pixel format arguments from the initializer and made them readwrite properties (or a property), then the context would have to reallocate the buffer when we change the pixel format from the default (ARGB or something similar) to something else (e.g., grayscale).

    The immediate solution to that would be for the context to allocate its buffer lazily the first time you draw into it, but that would mean every attempt to draw into the context would hit that “have we created our buffer yet” check.

    A better solution would be to follow the above idea of condensing the specification of the pixel format down to a single constant; then, we could have a designated initializer that would take a pixel-format value, and a shorter initializer for the default case that calls the DI with the default pixel-format value.

  • Specifying the buffer as a plain pointer (or pointer to one or more other pointers) requires the dimensions of the buffer to be specified separately.

    It’s a mystery to me why CGBitmapContextCreate doesn’t take a CFMutableData and NSBitmapImageRep’s initializers don’t take an NSMutableData. With these, the length in bytes would be associated with the buffer, enabling the context/rep to check that the length makes sense with the desired (or default) pixel format. This would be better than the current check in two ways: First, the current check only checks bytesPerRow, ignoring the desired height; second and more importantly, the current check only checks the value you gave for bytesPerRow—it can’t check the actual length of the buffer you provided.

    (From that, you can derive a bit of guidance for using the current API: If you pass your own buffer, you should use the value you computed for bytesPerRow in computing the length of your buffer. Otherwise, you risk using one stride value in allocating the buffer and telling a different one to CGBitmapContextCreate.)

  • Requiring (or even enabling) the buffer to be provided by the caller is redundant when the API has all the information it needs to allocate it itself.

    This was especially bad when the buffer was required. Now that CGBitmapContext can create the buffer itself, even having that optional input is unnecessary. We can cut this out entirely and have the context always create (and eventually destroy) its own buffer.

  • The caller must currently choose values for parameters that are not important to the caller.

    The current API makes you precisely describe everything about the context’s pixels.

    WHY? One of the central design aspects of Quartz is that you never work with pixels! It handles file input for you! It handles rendering to the screen for you! It handles file output for you! Core Image handles filtering for you! You never touch pixels directly if you can help it!

    99% of the time, there is no reason why you should care what format the pixels are in. The exact pixel format should be left to the implementation—which knows exactly what format would be best for, say, transfer to the graphics card—except in the tiny percentage of cases where you might actually want to handle pixels yourself.

With all of this in mind, here’s my ideal API for creating a bitmap context:

typedef enum
#if __has_feature(objc_fixed_enum)
: NSUInteger
#endif
{
    //Formats that specify only a color space, leaving pixel format to the implementation.
    PRHBitmapContextPixelFormatDefaultRGBWithAlpha,
    PRHBitmapContextPixelFormatDefaultRGBNoAlpha,
    PRHBitmapContextPixelFormatDefaultWhiteWithAlpha,
    PRHBitmapContextPixelFormatDefaultWhiteNoAlpha,
    PRHBitmapContextPixelFormatDefaultCMYK,
    PRHBitmapContextPixelFormatDefaultMask,

    PRHBitmapContextPixelFormatARGB8888 = 0x100,
    PRHBitmapContextPixelFormatRGBA8888,
    PRHBitmapContextPixelFormatARGBFFFF, //128 bits per pixel, floating-point
    PRHBitmapContextPixelFormatRGBAFFFF,
    PRHBitmapContextPixelFormatWhite8, //8 bpc, gray color space, alpha-none
    PRHBitmapContextPixelFormatWhiteF, //Floating-point, gray color space, alpha-none
    PRHBitmapContextPixelFormatMask8, //8 bpc, null color space, alpha-only
    PRHBitmapContextPixelFormatCMYK8888, //8 bpc, CMYK color space, alpha-none
    PRHBitmapContextPixelFormatCMYKFFFF, //Floating-point, CMYK color space, alpha-none

    //Imagine here any other CGBitmapContext-supported pixel formats that you might need.
} PRHBitmapContextPixelFormat;

@interface PRHBitmapContext: NSObject

- (id) initWithWidth:(NSUInteger)width
    height:(NSUInteger)height;
- (id) initWithWidth:(NSUInteger)width
    height:(NSUInteger)height
    pixelFormat:(PRHBitmapContextPixelFormat)format;

//There may be an initializer more like CGBitmapContextCreate/NSBitmapImageRep's (taking individual pixel-format values such as color space and bits-per-component), but only privately, to be used by the public DI.

//Mutable so that an asynchronous loader can append to it. Probably more useful in an NSBitmapImageRep analogue than a CGBitmapContext analogue.
@property(readonly) NSMutableData *pixelData;

@property(readonly) NSColorSpace *colorSpace;
@property(readonly) bool hasAlpha;
@property(readonly, getter=isFloatingPoint) bool floatingPoint;
@property(readonly) NSUInteger bitsPerComponent;

- (CGImageRef) quartzImage;
//scaleFactor by default matches that of the main-menu (Mac)/built-in (iOS) screen; if it's not 1, the size (in points) of the image will be the pixel size of the quartzImage divided by the scaleFactor.
#if TARGET_OS_MAC
- (NSImage *) image;
- (NSImage *) imageWithScaleFactor:(CGFloat)scale;
#elif TARGET_OS_IPHONE
- (UIImage *) image;
- (UIImage *) imageWithScaleFactor:(CGFloat)scale;
#endif

@end

With the current interface, creating a context generally looks like this:

size_t bitsPerComponent = 8;
size_t bytesPerComponent = bitsPerComponent / 8;
bool hasAlpha = true;
size_t bytesPerRow = (CGColorSpaceGetNumberOfComponents(myColorSpace) + hasAlpha) * bytesPerComponent * width;
CGContextRef context = CGBitmapContextCreate(NULL, width, height, bitsPerComponent, bytesPerRow, myColorSpace, myBitmapInfo);

With an interface such as I’ve described, creating a context would look like this:

PRHBitmapContext *context = [[PRHBitmapContext alloc] initWithWidth:width height:height];

Or this:

PRHBitmapContext *grayscaleContext = [[PRHBitmapContext alloc] initWithWidth:width height:height pixelFormat:PRHBitmapContextPixelFormatWhite8];

Xcode and Friends

Friday, February 17th, 2012

Xcode’s distribution has changed greatly in 4.3.

First, it’s now simply an app, without an Installer package. If you install it through the Mac App Store, it’ll install directly–no more “Install Xcode.app” (which I think I read earlier that you have to delete, although I can’t speak to this myself). If you install it from the disk image, it’s a drag-and-drop install.

Second, the set of applications that come with it (now bundled inside) is now much smaller. The other developer applications have been split out into separate disk images that are only available on connect.apple.com.

So, I thought I’d make a catalog listing where everything is now. Every one of the below sections corresponds to a disk image on connect.apple.com, and with the exception of Xcode, every one of those disk images is only available from connect.apple.com—only Xcode is available from the Mac App Store.

Xcode (and the other core tools)

The core tools are inside Xcode regardless of where you get it from. That will be either:

The applications bundled inside Xcode are:

  • Application Loader: One way of submitting your application to the Mac App Store.
  • FileMerge: Differencing tool. As its name implies, primarily for visually merging three versions rather than comparing two.
  • Icon Composer: Create IconFamily (.icns) files from multiple individual images.
  • Instruments: Use this to make your app more efficient, or to hunt zombies.
  • OpenGL ES Performance Detective

Accessibility Tools for Xcode

  • Accessibility Inspector (a.k.a. UIElementInspector): Examine the Accessibility properties of NS/HIViews in any application.
  • Accessibility Verifier: Automatically runs through an application’s accessible object hierarchy, including windows and views, and produces an outline of things it does wrong or fails to do that could cause accessibility problems for users.

Audio Tools for Xcode

  • AU Lab: Set up chains of Audio Units to filter audio or route it from a source to a destination.
  • CoreAudio: Sample code for some of Core Audio’s older APIs.
  • HALLab: Tool for inspecting the audio object hierarchy.

Auxiliary Tools for Xcode

  • 64BitConversion: Tools for porting 32-bit Cocoa code to 64-bit.
  • Clipboard Viewer: See every type on the clipboard (general pasteboard).
  • CrashReporterPrefs: Change how your system reacts to an application crashing.
  • Dictionary Development Kit: Build your own dictionaries for Dictionary.app.
  • Help Indexer: Makes your Help Books searchable. (Only useful for Mac developers.)
  • LegacyAPISurvey: Tells you what APIs you’re using that are in danger of being deprecated.
  • Modem Scripts: Examples of CCL scripts (essentially, modem drivers).
  • PackageMaker: Build Installer packages.
  • Repeat After Me: Test the Speech Synthesis Manager, exporting to either phoneme text or an AIFF file.
  • SleepX
  • SRLanguageModeler: Something to do with Speakable Items, but I couldn’t figure out how to work it.

Dashcode for Xcode

  • Dashcode: IDE for making Dashboard widgets. It “includes a design canvas that produces the graphics assets for you, as well as a powerful code editor, and even a full JavaScript debugger”.

Graphics Tools for Xcode

  • CI Filter Browser (Dashboard widget)
  • OpenGL Profiler
  • OpenGL Shader Builder
  • Pixie: Like DigitalColor Meter, but more developer-oriented. YMMV on which is better.
  • Quartz Composer: Create compositions that can be used within applications or as screensavers. Also useful for developing Core Image filters.
  • Quartz Composer Visualizer
  • Quartz Debug: Monitor your computer’s graphics performance (global frame rate), toggle various settings in the Quartz Compositor, and enable or disable the HiDPI resolutions.

Hardware IO Tools for Xcode

  • Bluetooth Diagnostics Utility
  • Bluetooth Explorer
  • btdump
  • IORegistryExplorer: See what’s connected to your Mac.
  • Network Link Conditioner (prefpane): Makes your internet connection pretend to suck so you can see how your app performs under such conditions.
  • PacketLogger: Another Bluetooth tool.
  • USB Prober: Inspect your USB buses and the devices connected to them.

Why you should almost always write a test app for your Radar bug reports

Wednesday, November 9th, 2011
  1. You’ll understand the bug better. This means you can write a better bug report, which will help Apple fix it more quickly (meaning you may get the fix more quickly).

  2. They’ll understand the bug better. This, too, helps Apple fix it more quickly.

  3. You may find that it is not a bug in the API at all, but that you were misusing it. Perhaps you were using something on a thread that you shouldn’t have been, or expecting some argument to be used a certain way when it’s actually used differently.

    In this case, you may be able to use the API after all, saving you the time you would have spent hacking around a non-bug. This also saves them the time they would have spent triaging and eventually responding to a non-bug.

    If your misunderstanding was borne out of poor documentation (misleading, inaccurate, vague, incomplete), you can file a bug report about that instead. Then the documentation gets better and future users of the same API avoid making the same error you did.

A test app isn’t appropriate for every kind of Radar, but when it is, including it helps everyone.

Much faster

Saturday, October 15th, 2011

I’ve just pushed a couple of improvements to my ISO 8601 date formatter. Previously, it was pathetically slow compared to C standard library parsing and unparsing; now, it is faster.

Timing ISO8601DateFormatter
Time taken: 0.130194 seconds
Number of dates and strings computed: 10000 each
Time taken per date: 0.000013 seconds
Timing C standard library parsing and unparsing
Time taken: 0.192645 seconds
Number of dates and strings computed: 10000 each
Time taken per date: 0.000019 seconds

You’ll want at least revision [61d2959c6921] or later.

My thanks to Sam Soffes and Rudy Richter for alerting me to the speed problem.

Edited at 16:35. Previously, it was almost as fast as C stdlib. Now it is faster.

Conferences 2011

Monday, September 12th, 2011

It’s that time again! Just like last year, there are a bunch of different conferences going on; unlike last year, I’m not going to even attempt to list all of them.

The two that I have a reason to mention are:

  • Voices That Matter. This time it’s in Boston, November 12 and 13. Their early bird pricing is still on through September; I’ll let their site tell you more. As I do whenever I mention it here, I have a coupon code for it, which is BSTBLOG. As usual, due to time and expense constraints, I won’t be attending this one.
  • MacTech Conference. I’m presenting again—same topic as last year, how to recognize, find, and fix bugs in Cocoa applications, but this time it’s the Xcode 4 edition. The early bird period has ended, but you can get the same $500 off by signing up through this referral link. The time and place is November 2, 3, and 4 in Universal City, California.

Last year at the MacTech Conference, I brought with me some of my useful Cocoa links business card. I’ll be doing that again this year, so if you attend the MacTech Conference, feel free to ask me for one.

I hope to see you at the MacTech Conference!

How to make the Help key do something useful

Tuesday, June 14th, 2011

If you want to see the techniques explored in this blog post in a working application, download ContextHelpTest and/or its source code.

If you’ve used Mac OS X with an Apple extended keyboard of some sort, chances are you’ve seen this:

The Help cursor, a question mark.

That’s the cursor that comes up when you press the Help key. And every time, if you click while that cursor is up, you get a beep and the cursor changes back. (If you press a key instead, the cursor changes back and the keystroke goes through, which often will still get you a beep.)

So most of us probably forget the Help key exists, and curse it when we are reminded of it by pressing it by accident.

But what does it do, really? What is it meant to do?

Every responder can respond to a message called helpRequested:. The default implementation is to ask the help manager for the attributed string set as help for itself. If you’ve never set any help for it, then the help manager will return nil, and the default helpRequested: implementation will pass the message on to the next responder. If you have set help for the responder, then it will tell the help manager to show that help.

So here’s what you need to do:

  • To associate a help text with a view or other object, send the help manager a setContextHelp:forObject: message, passing the help attributed string and the view/other object. When the object is a view, this is all you need to do for Help-clicking on the view to do the right thing.
  • To programmatically show context help for an object, use showContextHelpForObject:locationHint:. Note that you pass the object to look up, not the help text, here. The location hint is where the user might have Help-clicked to bring up the context help.
  • To make your custom view able to show context help for things it draws within itself, override helpRequested:, find what the user clicked on, lazily set the context help for the clicked object (if appropriate), and look it up. If the user didn’t click on anything or you don’t have any help worth providing for it, pass the message on to super.
  • To programmatically enter Help-key mode, send the activateContextHelpMode: action message to the application object. If you want to make a control or menu item in a nib do this, connect it to that action on the First Responder.

Note that the help manager does not retain your definable objects. If an object that has help set for it is deallocated, that will cause a crash later on. Therefore, when setting context help for a view, the view itself should do so within its initWithFrame: (or other designated initializer) and initWithCoder: methods, and remove itself from context help in dealloc. (I don’t know how this goes under GC or ARC.)

You might also have noticed that I’m not simply saying “view” or “responder”. The help manager does not restrict you to setting help for responders or views; any object can have context help set for it. This includes model objects. This is very, very useful for implementing a view that does selective context help on things within it: You set context help for each model object, and the view tells the help manager to show help for the clicked model object.

When setting context help for a non-responder, the controller that owns it should do that, and should remove the object from context help before releasing the ownership.

So, here’s a test app.

Context help works the usual way on the word-count field:

Help-clicking on the word-count field shows a tooltip-looking popover that shows the context help associated with the field.

The text view is a subclass of NSTextView that implements helpRequested: by determining what word the user clicked on (by sending itself characterIndexForInsertionAtPoint: and then using a CFStringTokenizer to walk through its words) and then looking that word up in the dictionary.

Help-clicking on the word-count field shows a tooltip-looking popover that shows the dictionary definition associated with the clicked-on word.

Naturally, since not everybody has a Help key anymore, I provided an alternative.

The Edit menu contains a Define menu item, with the keyboard shortcut of ctrl-slash.

I look forward to seeing what uses you come up with in your apps for this.

How to make me lose all interest in your app and not even want to try it much less buy it

Tuesday, April 26th, 2011

Choose any—or, preferably, none:

  • Make custom buttons, but don’t hire a professional designer to design them. Draw your UI in your pirated copy of Photoshop and make the best buttons you, fellow non-graphically-superpowered programmer, can manage, which look like you downloaded them from GeoCities in 1995 or got them out of a “1001 Buttons!” book from the same year.
  • Make custom UI controls (especially buttons) simply because you can.
  • Fill your App Store page’s “screenshots” section with images that are not purely screenshots. Showing an iPhone 4 with your app on it is a minus. Showing an older iPhone is another minus. Putting in your own inane blather marketing copy with your paint program’s text tool is a minus. Putting the iPhone and/or text on any kind of background is a minus. Any image that does not show the app at all is 500 minuses.
  • Show screenshots, but only of some of the app. Leave me wondering whether your app has the feature or UI pattern I’m looking for. (If your app is free, I’ll try it and find out. If it’s not, I won’t.)
  • Custom backgrounds without (good) custom UI. Extra debit if your background is plain white.
  • Abbr btn nms.

How to keep me actually interested and maybe even get me to buy your app:

  • If you make custom UI, make it awesome. Make a truly original UI that would belong on the cover of Macworld. Make it a custom UI with a purpose that guides and justifies the customizations. (Beware the difference between “purpose” and “theme”.) Otherwise, stick to plain Cocoa Touch controls wherever possible. Functional beats ugly.
  • If you go functional, follow the HIG. Either way, keep things clean and well-organized. Don’t force too much into a single screen. If you “have to” pack multiple things on a line, that’s too much. If you “have to” abbreviate words, that’s too much. Consider cutting features; simplicity is a virtue. If you need to break things out into other views, do it.
  • The screenshots section is for screenshots only. If you need to indicate a gesture, composite in a finger and an arrow, and don’t do that in more than one screenshot. No added text. Ever.

Don’t miss the comments. I’m sure some of you have some other don’ts to suggest that I forgot.