How Not to Crash #4: Threading

Here’s a simple rule: do everything on the main thread. Machines and devices are so fast these days that you can do more on the main thread than you think you can.

It’s a kind of paradise when you don’t have to think about concurrency because there isn’t any.


I’m a performance junkie. Or, more to the point, I’m a user experience junkie, and the only thing worse than something being slow is being slow and noticeably blocking the main thread. Don’t do that.

I’ll get to that. But let’s start with the main thread.

The Main Thread Rule

All the code I write expects to run on the main thread and on the main thread only, with exceptions. (We’ll get to the exceptions in a minute.)

This solves a bunch of problems. For instance, I wrote in an earlier post about unregistering for notifications in dealloc. A few people pointed out that you can’t guarantee on which thread dealloc will be called — but you can, actually, for any given object that runs on the main thread and is referenced only on the main thread.

It also means that any KVO changes are posted on the main thread, and any observing objects are also running on the main thread and expecting notifications on the main thread.

The benefits of not having to deal with concurrency are tremendous. I strongly suggest writing your entire app this way, and then testing to see if there’s anything that blocks the main thread. If not, then it’s golden, and you should ship. (Test with suitably large data, of course.)

Objects That Live in Their Own Little World

If — and only if — you find that the main thread is noticeably blocked should you look for ways to un-block it.

The first candidates are transformations that can be completely isolated from the rest of your app. I’ll use the example of processing JSON.

When I get JSON from a server, I like to turn it into intermediate objects that later get merged into model objects. Reasons:

  1. I don’t want the model objects to know about JSON.
  2. I want to deal with NSNull values, date conversions, and all other transformations before any other object sees the data.

So I use an NSOperationQueue or GCD queue (usually the latter, these days) to turn NSData returned by a server into intermediate objects.

(Always use a queue. Don’t ever use detachThreadSelector or performSelectorInBackground.)

These intermediate objects will be accessed by one thread at a time. They’re created on a background thread and then passed to the main thread, where they’re used to update the model and then discarded.

Because they are referenced on different threads during their lifetimes, I make sure that these objects never know about anything except themselves and what’s passed into their init methods. Once created on the queue, they’re immutable. They don’t observe anything and nobody should observe them (they don’t change, after all).

(This makes those objects fully thread-safe in the sense that objects that can’t change are thread-safe. However, it’s not necessary to stress the thread safety, because what’s important is that they’re safe to use on a single thread at a time, rather than on multiple threads at a time.)

Objects With Friends

Sometimes a number of objects work together. Instead of JSON, think of an RSS parser. In this example, there are three main objects involved: a SAX parser wrapper, its delegate, and the intermediate objects the delegate creates. (Conceptually exactly like the objects from the example above.)

The SAX parser wrapper and its delegate live for the length of the operation. They don’t need to be thread-safe, even though the code is run on a separate thread — because they are accessed only on that thread. While they’re working, they know nothing about the outside world, and the outside world knows nothing about them.

  1. The SAX parser wrapper knows about the NSData it was initialized with, and it knows it has a delegate.
  2. The SAX parser delegate knows about the intermediate objects it’s creating.
  3. The intermediate objects don’t know about anything.

So these objects work together, but, importantly, they never use KVO or notifications in any way. Instead they use the delegate pattern (whether via blocks or methods isn’t conceptually important).

The objects work together, but as loosely as possible while still keeping the group isolated to its task.

In the end, only the intermediate objects survive — they’re passed to the main thread, where they’re used to update the model. And then they’re discarded.

Worst-Case Scenario

I’ve used the phrase “update the model” several times and mentioned doing it on the main thread. A few years ago I would never have dreamed of that — but computers and devices have gotten so much faster that it’s worth going main-thread-only at first, and considering alternatives only after dealing with everything else that can and should be safely moved to a queue.

You really don’t want to update model objects on background threads. It’s a crash-making machine. But testing and profiling may tell you that you need to.

Try to break down the problem. If updating the model is okay except for this one thing — something that involves turning NSData into a UIImage or NSImage, for instance — then move just that slow part to a background task. (Creating an image from data or a file is a perfectly good thing to move off the main thread. It’s easily isolatable.)

It could be that the problem is the database: perhaps you find that it’s otherwise fast to create objects and update properties in memory, even a whole bunch of them. In that case, you might do what I do, which is de-couple the database calls from the main thread. (It’s not that hard: the database code needs to run on a serial background queue, and it should do everything in the exact some order that things happen in the main thread.)

Which is to say: there are options.

But if you still find that you have to update the model on a background thread, then you just have to do it. Remember that the rest of your app is on the main thread, so when posting notifications and so on, do so on the main thread.


Do everything on the main thread. Don’t even think about queues and background threads. Enjoy paradise!

If, after testing and profiling, you find you do have to move some things to a background queue, pick things that can be perfectly isolated, and make sure they’re perfectly isolated. Use delegates; do not use KVO or notifications.

If, in the end, you still need to do some tricky things — like updating your model on a background queue — remember that the rest of your app is either running on the main thread or is little isolated things that you don’t need to think about while writing this tricky code. Then: be careful, and don’t be optimistic. (Optimists write crashes.)

How Not to Crash #3: NSNotification

In general, I prefer NSNotification to KVO and (especially) to bindings. I do use KVO sometimes — there are times when it’s the most sensible thing. But NSNotification, like many older APIs, is easier to use without crashing.

But you still need to be careful.

The One Way to Crash

When an object registers for a notification, and then is deallocated without unregistering, then the app will crash if that notification is posted. That’s the thing you need to avoid. The rest of this article describes how to do that.

The Big Rule

I have one simple, hard-and-fast rule: NSNotifications are posted on the main thread only. No exceptions. If some code is running in another thread and it needs to post a notification, it does so on the main thread.

This avoids all problems with notifications coming in on threads you don’t expect. It avoids race conditions with unregistering for notifications.

Almost all of an app’s code should be written to run on the main thread. Code that runs in an NSOperation or GCD queue should be isolated from everything else, and should use a delegate pattern (with or without blocks) when multiple objects work together.

Ensuring that notifications are always posted on the main thread ought to be easy. (I’ll do another how-not-to-crash article on threading and queues that goes into more detail.)

Blanket Unregistering

Some people like the extra housekeeping work of unregistering for each NSNotification explicitly in dealloc. You get things like this:

[[NSNotificationCenter defaultCenter] removeObserver:self name:kSomeNotificationName object:someObject];
[[NSNotificationCenter defaultCenter] removeObserver:self name:kSomeOtherNotificationName object:someOtherObject];

You can prove when you write this that it’s correct. But it’s not enough to think of a snapshot of your code — you have to think about your code as it moves through time.

And future you or future somebody else might add another notification, and not remember to call removeObserver for that specific notification. And then there’s a crash.

The other problem is that future coder may have to go through your code and do an audit to make sure each registered observation is removed. This is a pain: it’s manual and error-prone.

Instead, always do this:

[[NSNotificationCenter defaultCenter] removeObserver:self];

It’s what Indiana Jones would do.

Beware Double Registrations

If an object registers for a notification, and then registers for it again, the notification handler will get called twice. There’s no automatic coalescing.

(This used to happen in the old days on iOS a lot with viewDidLoad. People would put registration code there — but remember that views could get unloaded and reloaded, which meant multiple registrations for the same notification.)

Your notification handlers should be written so that they can deal with getting called twice. And it should be impossible for a given object to register twice for the same notification. Both.

Register in init, unregister in dealloc

In almost every single case, I register for observations in an init method and remove observations in dealloc. If I find that an object needs to add and remove observations during the lifetime of the object, then I consider it a strong code smell.

There’s a good chance that 1) either it doesn’t really need to do that, or 2) the object should be split into smaller objects.

You know that an init method will be called just once for a given object. You know that dealloc will be called just once when there are no other references to that object. You can use this knowledge to balance out registering and unregistering without having to think about it or keep track of it. So easy.

Avoid addObserverForName

Some people like -[NSNotificationCenter addObserverForName:​object:​queue:​usingBlock:]. It feels modern because it’s block-based, and we all love blocks. (I sure do.)

But it’s a bad idea. You may have saved yourself writing a notification handler method, but you’ve made your housekeeping worse because now you have an extra object to keep around and do a removeObserver: on later. That means no blanket unregistering; it means you’re back to doing audits; it means you have another thing to get right.

You might like that the block-based version means you can keep the registration and the notification handler together — but the cost is too high in housekeeping and potential crashes.

How Not to Crash #2: Mutation Exceptions

You get a collection from somewhere and enumerate it — and then you get an error about the collection being mutated as it was being enumerated. The app crashes.

You can avoid this unhappy fate with one simple trick: don’t enumerate mutable collections.

Disagree with me

You might hold the reasonable position that the real answer is not to mutate a mutable collection while enumerating it. You should have enough understanding of your app to be able to write code that safely enumerates a mutable collection.

Yes, you should. You absolutely should.

However: writing crash-free code is about removing doubt. It’s about minimizing the chances for errors, and minimizing the chance that future changes (by you or somebody else) introduce a crash.

Mutable collections should not be part of public API

It should be extremely rare — or, better, never — that an object has a public property that is a mutable collection. Mutable collections should be internal to the object.

(Furthermore, as much as possible, public collections should be read-only. This isn’t always possible, of course.)

Now, it’s entirely likely that an object has a public collection that is internally a mutable collection. Think of an object that tracks operations. It might make the following public:

@property (nonatomic, readonly) NSArray *operations;

And internally there’s this:

@property (nonatomic) NSMutableArray *mutableOperations;

- (NSArray *)operations {
  return self.mutableOperations;

That’s perfectly legal code: because mutableOperations is an NSMutableArray, it’s also an NSArray. (I did it this way for years. I thought to myself, “Hey, I’m a grownup. I can handle it.” But what I didn’t realize was that grownup developers write code to make errors less likely.)

Properties specified as immutable should be immutable in fact

In the above example, you’re advertising operations as an array that can be enumerated safely at any time. Another person — or you yourself, looking at this in six months — won’t necessarily realize that really you’re getting back a mutable array that can’t necessarily be safely enumerated.

Here’s the truth-in-advertising solution:

- (NSArray *)operations {
  return [self.mutableOperations copy];

(It wouldn’t hurt to modify the property declaration also to make it clear that it’s a copy, but I admit that I don’t always do that. It would have the advantage of making it completely clear to the user of the API what’s going on.)

You might push back, citing performance or memory use issues or both — but I’ll admit something: I’m a performance junkie, and I spend an inappropriate amount of time in Instruments making sure things are fast and use a non-weird amount of memory. And I’ve never, ever found this to be a problem. If your app has performance or memory use issues, the problem is something else, not these copies. (Though you might consider using @autoreleasepool so that these copies don’t last very long.)

Make the copy.

Bonus points: don’t believe their lies

I recently fixed a mutation error when enumerating NSTextStorage layoutManagers:

@property (readonly, copy) NSArray *layoutManagers;

Obviously it’s safe to enumerate. It’s an NSArray, it says, and it’s a copy. Cool. Enumerate away.

But it’s a lie. In the debugger I found that it’s an NSMutableArray (__NSArrayM) — and that it’s not a copy at all. It’s NSTextStorage’s _layoutManagers instance variable, which is declared as an NSMutableArray.

And some code in my enumeration block did a thing that triggered a mutation in layoutManagers, and the app crashed.

The answer: enumerate a copy of layoutManagers. Problem solved.

There’s a general point: if you’re getting a collection from code that isn’t yours, it doesn’t hurt to be defensive and enumerate a copy.

How Not to Crash #1: KVO and Manual Bindings

I’ve been fixing crashing bugs recently — but rather than write about fixing crashing bugs, I thought it would be more interesting to write about not creating crashing bugs in the first place.

In this first installment I’ll talk about KVO, manual bindings, retain cycles, and invalidate methods.

Bindings means never having to say goodbye

iOS developers don’t have this, but Mac folks do: we can bind a property to another property. We can make it so that if updates, then updates too.

NSKeyValueBinding.h is in AppKit. Take a look at bind:​toObject:​withKeyPath:​options:

Let’s imagine a button with a title property. That title property should update whenever some controller’s title updates. Let’s say the controller owns that button. You might write code like this:

static NSString *kTitleKey = @"title";
[self.button bind:kTitleKey toObject:self withKeyPath:kTitleKey options:nil];

Very convenient, and it works wonderfully.

And you’re well on the road to crashing.

Here’s the problem: the binding retains the toObject object. Which means that the button effectively retains the controller. If the controller retains its button (it should), then there’s a retain cycle. Neither will become zombies, but they could become abandoned.

One way to crash — and this is a true story — is if the abandoned controller listens for a notification (call it BSNotification), and it Does Something when receiving a BSNotification, and when it Does Something it crashes, because it’s no longer conceptually valid and it doesn’t know how to deal with the current state of things.

KVO means having to do everything perfectly every time

Let’s add a third object, a model object. What we really want is this flow:

modelObject.title changes, which updates controller.title, which updates button.title.

We’ll use KVO this time.

In the controller:

- (NSString *)title {
  return self.modelObject.​title;

+ (NSSet *)keyPaths​ForValues​AffectingTitle {
  return [NSSet setWithObject:​@"modelObject.title"];

Okay — now we have the entire flow. When modelObject.title changes, that affects controller.title, and button.title updates with the correct value.

Very convenient, and it works wonderfully.

It will crash, of course, when modelObject is deallocated (because an instance of modelObject was deallocated while it still has an observer).

If, instead, controller is retaining modelObject (as it probably should be), then you have a third object that will be abandoned and never deallocated, and it will sit around stewing and growing eviler by the minute.

One way to solve the problem that isn’t that great

The controller could have a method with a name like invalidate that breaks the retain cycles. Once broken, then dealloc will eventually be called for the controller, its button, and its model object.

You might write code like this, which you call when you know for a fact you’re finished with the controller:

- (void)invalidate {
  [self.button unbind:kTitleKey];
  self.modelObject = nil;

Here’s why this solution isn’t great:

Reference counting is a nice solution — it guarantees that when dealloc is called, you know that no object has a strong reference to the object being deallocated. This makes dealloc a great place to remove observations and similar that need removing.

But if you use something like an invalidate method, you’re trying to do the work of reference counting yourself. You have to call invalidate, and you have to call it at the right time. Can you make that guarantee forever, for every object that has an invalidate method? What if something changes so that more than one object retains the controller? Who calls invalidate, and when?

That’s a lot of extra work and thinking, and part of the goal of programming is to make errors less likely. Relying on invalidate makes errors more likely.

A better way to solve the problem

Let’s go back to the problem we’re trying to solve:

modelObject.title changes, which updates controller.title, which updates button.title.

Let’s also be clear: controller knows about modelObject and button, but neither of those two know about each other, and neither of those two know about the controller. Here’s how we might handle it without the need for an invalidate method.

In the controller, nuke the custom getter. Nuke keyPaths​ForValues​AffectingTitle. Nuke the use of bind:​toObject:​withKeyPath:​options:.

Instead, create a custom setter — because, after all, setters are called when something changes, and the entire problem to solve is propagating changes to a title property.

- (void)setTitle:(NSString *)title {
  _title = title;
  self.button.title = title;

That solves half the problem: when controller.title changes, button.title changes.

We can’t do something similar with modelObject, since it doesn’t know about the controller. Instead, have the controller observe modelObject.title.

[self.modelObject addObserver:self forKeyPath:​kTitleKey options:0 context:​kTitleContext];

Then in the KVO observation method, watch for kTitleContext, then do self.title = self.modelObject.title. This will call the controller’s setTitle: — which then updates button.title.

With this solution there are no retain cycles. There is one line of tear-down work to do, but you can do it in the controller’s dealloc method:

[_modelObject removeObserver:​self forKeyPath:kTitleKey context:​kTitleContext];

Review and recommendations

The solution we came up with fixes the retain cycle without your having to remember to call an invalidate method and call it at the exact right time. It’s safer code.

There’s a good chance it’s less code, too, and it’s more explicit code.

Some recommendations:

Don’t use bind:​toObject:​withKeyPath:​options: ever, in any circumstance. (iOS people: consider yourselves lucky that it’s not an option. Also consider that there’s probably a reason it never made it to iOS.)

Use a custom setter rather than a custom getter when you’re propagating changes. (It’s in the setter where a thing changes, after all.)

Avoid invalidate methods in favor of letting reference counting do its thing — because if you are the one trying to track references, you’re going to make mistakes. (I realize avoiding invalidate methods isn’t always possible, but it’s probably more possible than you think it is.)

Interlocking observations of any kind make it difficult to think about what happens in your app: it’s better to be explicit whenever it’s reasonable. Once you get enough of these tendrils of observations you’ve built an impenetrable jungle, and making changes becomes scary.

In theory, bindings and KVO are there to promote loose coupling, but in practice the coupling is often just as tight — if not tighter, in a sense — and harder to debug and get right. It’s generally best to do explicit observations (as opposed to keyPaths​ForValues​AffectingXyz) and keep your keyPaths free of . characters.

No Forgiveness

I was a fan of Alex Rodriguez when he was a Mariner. And I would have remained a fan when he left for the Rangers — because that’s baseball. Good and beloved players sometimes change teams. I remained a fan of Ken Griffey, Jr., Randy Johnson, and Ichiro Suzuki after they switched teams.

But there was this in 2001:

A-Rod believes Boeing should follow him to Texas. That comment from the former Mariners shortstop came up during a segment of CNBC’s show “Squawk Box” featuring Boeing Chairman and CEO Phil Condit.

A-Rod was quoted on the show as making this pitch to Boeing, “I moved to Dallas-Ft. Worth to improve my future… so should you.”

It’s one thing to move to another team and say goodbye to your fans — those fans who were the first to cheer for you — many of whom were Boeing employees. It’s quite another thing to take a shot at the livelihood of those fans. What an insult.

Fuck him. I’m still disgusted. And I was not surprised to find out he was a cheater.

Not a man.


Whenever you use enumerateObjectsUsingBlock when you could have just used a for-thing-in-collection loop, God kills a kitten. And decides to delay rain in California by another week. And explodes stars — the homes of ancient and wise civilizations, who would have been our friends some day — in faraway galaxies.

Conference Scholarships for Women in Design

The Man Who Deleted All His Tweets

I’ve deleted all my tweets (and re-tweets and favorites) — or, rather, I’ve deleted all the easy-to-find tweets. Apparently 3,764 remain that I can’t find. (I’ve requested an archive, which should do the trick.)

I have a number of good reasons not to like Twitter: how poorly it’s treated third-party developers (some of whom are my friends); how it’s become the bright and shining home of bullies, outrage, and the mob mentality; how it’s fallen in love with TV and celebrities; how it’s turning into yet another way to show me ads.

But those aren’t my reasons for deleting my tweets. Instead, it’s because Twitter is a blogging (or micro-blogging, really) service that doesn’t meet my requirements, which are:

  1. I should be able to host my content using my own domain, and

  2. I should be able to move to another service (or to my own server) without anybody noticing the difference. (Links shouldn’t break, etc.)

Were Twitter just the world’s global chat room — with tweets as ephemeral as anything I type into an irc, HipChat, or Slack window — I wouldn’t care.

But Twitter is half chat room and half micro-blogging service, and it is absurd to allow Twitter an exception to my rules about owning my own blogs.

Compromise Position

It’s not that I’m anti-social. I do use HipChat, Slack, and Messages (irc not so much anymore) to talk with my friends and co-workers. And there’s always email. (Email we shall always have with us.)

Messaging systems from Glassboard on have replaced a lot of what I used to use Twitter for, and this seems to be a general trend. I like this trend. Not everything has to be broadcast to the world.

But what gives me pause is Twitter’s egalitarian nature. Twitter’s strength and weakness are the same thing: anybody can talk to anybody.

So I haven’t deleted my account or made it private. I will respond to some messages. It’s just that I’ll delete my response after a day or a week or whatever so that Twitter is a chat-only service for me. (I should automate this.)

What I don’t have yet, though, is a replacement micro-blogging system. I’m going to let that just be an unmet need for now. Perhaps it’s the grain of sand that irritates me into generating a pearl. And perhaps not.


I’m not advocating for anything. You can disagree with me on Twitter’s being half chat room and half micro-blogging service. I don’t expect anybody to follow my lead, and I don’t expect anybody to feel bad for not following my lead.

OmniOutliner for Mac 4.2.1

It’s on the Omni website and will be on the Mac App Store once approved. (In a week, give or take, I suppose.)

I like this particular release a ton because we concentrated on fixing crashing bugs to the exclusion of almost everything else.

Ideally we’d have zero known crashing bugs. OmniOutliner isn’t quite there, but it can see that promised land from where it stands.

Argument about crashes

You could argue that fixing crashes isn’t that important these days. The risk of data loss isn’t what it was, now that so many apps do auto-saving, syncing, and state restoration. And re-launching an app is much quicker than it used to be. (Remember the old days of counting the number of bounces in the Dock?)

So a crash is really just a slight annoyance, you could argue — and you could argue that users take the occasional crash in stride.

I understand the argument, and I disagree. Each crash means somebody got a little surprised and angry, even if only for a moment — and that’s hard to wave away. If you care about your craft, you care deeply that what you make never unintentionally makes somebody mad.

And it’s also notable that once a user triggers a particular crash, they’re fairly likely to hit it again. Maybe it’s something about their document, or their machine, or the steps they’re taking to accomplish a certain task.

That person won’t be slightly annoyed at the second and third crashes. That person is — quite rightly — going to email support, particularly if the crash stops them from completing their work. So now the crash, however rare, is costing the developer time and money. (If you don’t buy the craft argument, you should buy this pragmatic one.)

Software doesn’t have to crash. You may think that it’s an idealistic goal, but it’s not — it’s do-able (I’ve done it; other people have done it), and it matters.

Video Interview with Mark Alldritt

Mark Alldritt has been one of my indie heroes for 20 years. In this interview he talks about, among other things, his work with AppleScript and Illustrator.


My friend and co-worker at Omni Dan Segars released SpotFunds, his first iPhone app, not long ago.

Keep track of just how much you’re spending on coffee (and similar). It’s an absurdly high amount. But you won’t know that till you get SpotFunds — the $3.99 app that saves you money.

(Note to Dan: there’s your marketing text, right there.)

Dave Winer’s idea was to build a system where his friends could recommend specific episodes of a podcast and they’d be displayed on a single page. And then you can listen to things — good things, hopefully — that you wouldn’t have heard otherwise.

If you’re in a podcast bubble, this is a way out.

OmniOutliner 4.2

OmniOutliner 4.2 is available direct from Omni. (It’s been submitted to Mac App Store but isn’t up yet.)

I don’t think I’ll make anyone too terribly sad if I say that it’s my favorite Omni app. I use it for a bunch of different things — outlining app architecture and features, presentations, articles, and so on. Things that later end up in Xcode or Keynote or BBEdit start out in OmniOutliner. It’s where I think and organize.

Highlights: the new release (here are the release notes) updates the look for Yosemite and fixes a bunch of crashing bugs.

I’m proudest of the crash bug fixes — crashing sucks, and fixing crashing bugs is fun. (Sincerely. I enjoy the detective work.)

My theory about crashing bugs is that they cost more than most developers think they do. They cost you in goodwill (users hate crashes; users don’t think of crashes as acceptable inconveniences) and they cost in support and QA time.

In my ideal universe, all developers everywhere would prioritize crashing bugs over everything else, all the time.

Dave on Silo-Free Software

Scripting News:

I guess you could say I believe there are other reasons to make software, other than making money. Some people like to drive racecars when they get rich. What I want is to drive my own Internet, and for you to drive yours too.

Two Apps

My friends Chris Parrish and Guy English just released Napkin 1.5, a huge update to their already-awesome visual markup app.

And cousin Michael and team just released Fantastical 2.0. You already know about it and you’ve bought it. I’m just reminding you that you have good taste. :)

OmniOutliner 4.2 Public Test

OmniOutliner, on Twitter:

OmniOutliner for Mac 4.2 public test builds are now live! Contains Yosemite UI updates and other bug fixes.

I’ve been helping work on OmniOutliner, mostly on updating it for Yosemite. There’s plenty more to do, but some of the obvious things — vibrant sidebar, new-style toolbar buttons — have been done.

OmniOutliner is the first Omni app I started using (many years ago), and it’s still my favorite.

If you’re not faint-hearted, and you’re an Outliner fan, then please grab the test version and help us make sure this release will be awesome.

New Blogging App: MyWord Editor

MyWord Editor, from Dave Winer, is a “a simple silo-free blogging tool that creates beautiful essay pages.” It’s open source.

The announcement page has an aside about RSS that I enjoyed:

Of course every MyWord user has a great full-featured RSS 2.0 feed. We love RSS and it feeds us and we feed it, it's doing great, and anyone who disses it is a mean rotten silo-lover.

NSSegmentedControl with Menus

Mac developers: you probably recall that you can set a menu for each segment in an NSSegmentedControl, and the menu appears when you click and hold.

The problem with that is discovery — there’s nothing that shows that there’s a menu there.

So here’s what I want to do: put a downward-pointing arrow or chevron graphic to show that there’s a menu. And make it so that if you click on the arrow directly, you don’t have to click-and-hold.

But I don’t want to an entirely custom thing: I still want to use NSSegmentedControl.

What’s the best way to do this? Just place arrow buttons on top of the NSSegmentedControl?

I figure I can draw the arrows easily enough by subclassing NSSegmentedCell — have drawSegment:​inFrame:​withView: call super and then draw the arrows. But drawing isn’t enough — I want to have an actual button so you don’t have to click-and-hold.

Yet it seems weird to just place buttons on top of a control. How would you do this?

Three Down, One to Go

I was an Omni fan for many years before joining the company. It’s not just because the apps are so good — it’s also because it works like this: imagine what the right thing to do is, then watch Omni do it.

Right now the right thing, for our users and for the good of the apps, is to make them all universal — and as free upgrades for the iPad versions.

We just released OmniOutliner universal. It joins OmniGraffle and OmniPlan — with OmniFocus next. (Which people all around me are working on.)

We’re on a roll.

(Me, I’m not helping at all with this universal stuff. I take no personal credit. I’m in Mac-land — my home! — doing Yosemite updates and fixing bugs.)

NetNewsWire Status: Everything I Know

People ask me — on Twitter, in person, in chat, via email — how NetNewsWire is coming along. (I just got another email this morning.)

Answer: I don’t know. Yes, I do see Black Pixel employees in person once or twice a month, but they don’t tell me. (The employees I see don’t necessarily know. But, if they did, it wouldn’t be right to talk about internal stuff like that, so they don’t.)

All I know is what I read on Twitter:

We are still working on NetNewsWire, but don’t have any timeline information to share about future updates.

NetNewsWire was sold to Black Pixel in June 2011. It has been updated: at least one 3.x update to the Mac version, and a Mac 4.0 beta program.

I did, at one point (last Summer, I think), contact them about buying it back, and Black Pixel declined right away (we didn’t get as far as discussing terms). (It would have been a great story, right? NetNewsWire and I go from Ranchero Software to NewsGator; NetNewsWire gets placed safely in Black Pixel’s hands as NewsGator turns into an enterprise company; NetNewsWire returns to Ranchero Software.)

And that’s all I know.

I’d still be interested in buying it back, but I strongly suspect this is off the table, or so expensive that it wouldn’t make sense. (The expensive part isn’t the code, it’s the name.)

What I would have done with it: Mac version only. Syncing would be via Feedly, Feedbin, etc.

The idea is that it would be easy to get into — since it would sync with something you’re probably already using — and it would be easy to mix-and-match. You could run NetNewsWire on your Mac and Unread on your iPhone. (I use Unread: I’m a fan. There are other good readers, too.)

I’d make it a for-pay app, probably $24.99 or thereabouts — and not really worry about it making money. (Because I wouldn’t need it to, and because I’d be working on it with a more powerful motivation: love.)

Black Pixel could take the above as unsolicited advice: go Mac only, and sync with an existing service. One developer and an occasional designer could do this.

But I suspect they’ve made other choices, and development is probably pretty far along — so, instead, I’ll just relax and let them surprise me. There are great developers at Black Pixel, and I stand by to be delighted.


Ads via The Deck