Unread — Release Candidate 1.0

I started Unread in the summer of 2013. I’m happy to announce that — 1,659 git commits, 276 images, 273 classes, several dozen protocols, 19.9 megabytes of IPA, and one open-source sharing library later — I’ve just sent out the first release candidate of Unread to the beta testers. If all goes well, I will submit Unread for App Store review in the next several days.

|  22 Jan 2014




Weak Referencing Dictionary

Kolin Krewinkel, the talented developer behind the App.net client Stream, shared an interesting article on NSMapTable tonight. In the article, Charles Parnot subjects NSMapTable to a battery of tests to see if it performs as promised. The results were surprisingly disappointing.

For those not familiar, NSMapTable is best described as a mutable dictionary with weak referencing capabilities. Typically, you use a map table when you need a collection of objects in which each object persists as long at least one other object in your app has a strong reference to the it.1 For example, if you were writing an App.net client, you might want to store a local cache of user objects in a map table. As long as at least one post or profile view controller keeps a strong reference to a given user, the map table will keep a reference to the user, too. In short, it’s a way to keep things around as long as you need them, without having to manually keep track of when you no longer need them.

The problems that Parnot uncovered are related to what happens after all other objects have released an object that is a member of a map table. The expected behavior is that both keys and values for these objects will be released. In practice, the results are unpredictable. Often, only half of such objects actually get removed from the collection. The rest are just hanging around and may never get purged in a non-garbage-collected environment like iOS.

On a whim last year, I tried writing an alternative to NSMapTable that would perform the same function, but that would be able to run on iOS 5 (NSMapTable is only available on iOS 6 or later). I put the result up on Github. It’s a funny little thing. It was written before my morning coffee, so I don’t recommend using it in a production app unless you absolutely need to. Nonetheless it worked as expected in my limited tests. Here’s how it was put together:

JTSWeakReferencingDictionary

  1. The technique begins by adding an object via a category method on NSMutableDictionary, jts_setWeakReferencedObject:forKey: You add the object you want to be weakly referenced with this method instead of using the standard method.2.

  2. This method does not add the object to the dictionary directly, but instead wraps the object in an NSValue using valueWithNonretainedObject. Later on, this plays a crucial role.

  3. Just before adding the NSValue from step 2 to itself, the dictionary sets a custom property on the object passed into step 1 (the object we want to weakly reference). This is accomplished via associated objects, a form of voodoo too supernatural to cover here. The custom property is a strong reference to an instance of JTSDeallocNotifier, a subclass of NSObject that notifies a delegate whenever it is about to be deallocated. The mutable dictionary sets itself as the delegate of the dealloc notifier.

  4. So at this point, each object passed into step 1 is given a JTSDeallocNotifier and wrapped in a weak-referencing NSValue. The NSValue is then added to the mutable dictionary.

  5. As long as at least one other object in your app retains the weak-referenced object, it will persist via the NSValue as a member of the dictionary.

  6. When all strong references to the object are broken, the NSValue’s weak reference will be zeroed, and ARC will begin the process of deallocating the object and cleaning up the object’s own references. During this process, the object’s JTSDeallocNotifier is also deallocated since no other object retains it.

  7. Inside of JTSDeallocNotifier’s dealloc implementation, it notifies its delegate that it is about to be deallocated.

  8. The mutable dictionary receives the message from the dealloc notifier. The dealloc notifier has a key that is a copy of the key passed in step 1 when the original caller added the weak-referenced object to the dictionary. Using this key, the dictionary knows which NSValue it should remove from itself.

  9. The dictionary removes the NSValue for the key from step 8.

  10. At this point, ARC finishes its deallocation steps, and the weak-referenced object, its dealloc notifier, and the NSValue are all released.

I ran brief tests of this technique with collections of 10 to 20 objects, using GCD and blocks to experiment with timing. Everything worked as expected.


  1. There are other configurations, but this is the one with the most typical usage. 

  2. I didn’t get around to adding a remove method because coffee. 

|  11 Jan 2014




The Indie Ocean

My finger-to-the-wind says that apps and services aiming for mainstream consumer appeal, which were already impossibly hard, are only going to get harder in the coming years. There’s too much noise, and attention is fleeting. Any company that hasn’t started by now has already lost.

But on the other hand, iOS is a rapidly-maturing operating system. It’s officially seven years old today. The latest round of iPads and iPhones have practically removed all hardware barriers limiting what software can do.

In other words, while the tides of attention-based, multi-million-dollar startups churn red with the blood of all the contenders, I think there’s a vast blue ocean waiting for independent developers to make prosumer and pro software. I look at an app like Editorial by Ole Zorn and think, “My god, that’s where the rest of us should be going.”

Mass market, VC-funded companies can’t ship those kinds of apps. Apple’s apps will never be all things to all customers, either. But many people are still going to need advanced tools to get through their day-to-day lives. Not just writers, but first grade teachers, speech-language pathologists, youth pastors, session guitarists, ICU nurses — everyone.

I think that success will come to folks who can figure out how to ship “maximum viable products” on day one, without venture funding and without going bankrupt in the process, apps that are carefully designed and built to solve real problems for real people.

|  9 Jan 2014




Unread App Status Report

The Polar Vortex of 2014 has been a boon for Unread. Icy midwestern weather trapped me indoors and blurred the week into one, uninterrupted, very productive Work Day. I cleared the last must-have feature from the Version 1.0 todo list a few days ago. Only a few stepping stones remain between here and App Store submission. If you have any questions not answered below, feel free to ask me.

Fixes and Polishing

The private beta has been a relief. The testers have sent fantastic notes and bug reports. Most of their concerns are fixed and ready to ship. Fresh eyes are indispensable after you’ve spent six straight months staring at the same screens. The app is on feature lockdown from now on: no new features until after App Store submission. I’m obsessively polishing the smaller details. The best news I can give you is that the testers are really enjoying the app. I hope you will, too.

Feedly Integration

Technically, I’ve integrated Feedly into Unread already, but I only have access to the development sandbox. I’ve requested access to the production environment so that any Feedly user can sign in with Unread. If for some reason Unread is not approved for production access, then I will submit Version 1.0 without Feedly, trying again later. I requested access very recently, however, so I’m confident that Feedly integration will be in the 1.0. This process may end up pushing back the 1.0 several weeks. Unread also works with FeedWrangler (which I use personally) and Feedbin.

App Submission Collateral

I need to assemble the items required by the App Store, the usual bric-a-brac: screenshots, an app description, et cetera. Easy stuff.

Pricing Announcement

I’ve finally chosen a price: Unread will be available at an introductory price of $2.99, for a limited time. I know the tides have turned in favor of freemium pricing models, but those models work best for:

I considered using the free-with-pro-upgrade model we use in Riposte, but it doesn’t make sense for an RSS reader. Riposte’s biggest challenge is drawing customers to App.net, which has a much smaller network of users.1 RSS, while not as popular as it once was, is an established medium with plenty of existing users. Hopefully there’s a good number of them who aren’t afraid to spend a few bucks on a new app — especially one that tries very hard to be a fresh take on an old idea.


  1. App.net also has the Developer Incentive Program, which is a nice bonus. 

|  9 Jan 2014




Pull-To-Do-Action

Loren Brichter, inventor of the now ubiquitous pull-to-refresh control, interviewed in an article by Fast Company called “Why the Pull-to-Refresh Gesture Must Die”:

Brichter, however, feels that it’s high time his gesture evolves. “The fact that people still call it ‘pull-to-refresh’ bothers me—using it just for refreshing is limiting and makes it obsolete,” he says. “I like the idea of ‘pull-to-do-action.’”

If you like the sound of that, you’re gonna love Unread.

|  20 Dec 2013