inessential by Brent Simmons

On Twitter, Pádraig asks a great question, and probably every app developer should read the replies:

What are some of the reasons you would delete an app within a day of installing it?

We don’t take him seriously because he’s crazy.

But crazy people do take him seriously.

The Nordic Museum in my neighborhood is now officially the National Nordic Museum. Very cool!

We also have a statue of Leif Erickson. And we hold a parade for Sytennde Mai every year.

Big story on RSS readers in the iOS App Store today:

Really simple syndication — better known as RSS — is an enduringly useful technology that collects online articles from various sources in one convenient place.

The bummer about these articles is that the full thing can only be read in the iOS App Store. It would be nice if they actually appeared on the web, too.

The articles are often very well done and beautifully illustrated — and it would be to the benefit of Apple, and app developers, if these articles were findable and readable by people sitting in front of a computer.

Some More RSS-y Things

I didn’t know that NewsBlur was open source — including the iOS app — and that David Sinclair works on it. Very cool.

* * *

The iDownloadBlog ends an article on Reeder 4 beta with this:

Is there anyone out there still using RSS?

Don’t tell me I’m the only one…

I get it. And I realize the person was kind of joking around.

But here’s the thing: tons of people use RSS readers. There’s no shame in it; you’re not the last person; there’s not going to be a last person.

This category of software is not going away, and no one needs to be meek about it. There are probably more RSS reader apps these days than ever before.

Other services for getting news and links exist too. But there are lots and lots of people who use RSS readers. Not still using them, as if they’re stuck in the mud. No: they are people who like them and see no reason to stop liking them! (Many of those people use other services too. Of course!)

* * *

Nick Heer writes about developments on the RSS front:

I think the hardest question for RSS readers is how it could gain broader interest outside of the more technically sophisticated user group.

I agree that that’s a hard question, but I’m also not sure if it’s the most important challenge. I’m not writing an RSS reader because I expect a mass market to use it. I think lots of people will, but nothing like the number of Mac users who use Twitter, Facebook, or even Apple News.

That said… we should be trying to bring the benefits of RSS readers to more people based on ethical grounds. Deliberately — or through inaction — reserving technology for a sophisticated group is Not a Good Thing.

The answer is probably a collection of answers, and the way to get there is step by step.

Reeder 4 beta for Mac is up!

I had been on the verge of writing about this. I’d noticed some people saying things to the effect of, “Oh, he must have stopped working on it. He’s been so quiet.”

This is one of those things that drives me nuts. When a developer says they’re working on something, and then they’re quiet, it’s because they’re busy working on it.

Back in the old days, when I would hear that someone was writing an RSS reader, I’d worry that it was more competition.

These days when I hear that same news, I get excited. Sure, I want lots of people to use NetNewsWire — but, way more than that, I want lots of people to use RSS readers.

The more the better, since no one app is going to be the right thing for everybody.

And here’s Rob Fahrni working on a river-of-news type RSS reader for Mac and iOS. Cool!

Writing an open source app, and not having to think about making money with it, is utterly thrilling. I love this.

Among other things, it means I get to be a cheerleader for other people’s work, which is totally fun for me. Makes me happy.

Feed Discovery with Feed Compass

Maurice Parker, who’s contributed a lot to NetNewsWire, has started working on a directory app for feeds: Feed Compass.

It’s still at the proof-of-concept phase — very early days. The idea is that it can read OPML files from the web, show you a list of feeds, and show a preview of what’s in a feed when you select one. Most importantly, there’s a big Subscribe button so you can add the feed to your RSS reader.

The subscribe button should work with any Mac RSS reader that supports the feed protocol (which is probably all of them). It’s not just NetNewsWire-specific, in other words — it should work with Reeder, ReadKit, and others. (I haven’t tested this, though.)

* * *

This isn’t all of what needs to be done to make feed discovery easier and better. It’s a step, though, and it can be combined with other steps.

Some things I like about it:

  • It’s decentralized. It doesn’t rely on any one server, since servers may come and go.
  • It’s open source, which means you can contribute and/or fork. The code can’t go away.
  • It’s free, which means there’s no barrier to use.
  • It works (as I mentioned) with any Mac RSS reader, not any one in particular.
  • It could be ported to iOS.
  • It means we don’t need to include a feed directory built-in to NetNewsWire. (This helps keep NetNewsWire focused.)

* * *

Again, this is just the start of something. It’s not the one and only true answer to the problem of feed discovery — but it could be part of a collection of solutions that work together.

For more about discovery… When Maurice was doing research on the topic, he found this thread on Dave Winer’s GitHub enlightening, and I recommend reading it.

* * *

I had posted, to the NetNewsWire Slack group (email me for an invitation), a list of RSS app ideas. These are apps that could use NetNewsWire code to get a head start (though you wouldn’t have to).

The below is what I posted…

Here are some ideas for apps I’d love to write, that could use some NetNewsWire code, but that I’m too busy to write — but that would be cool if someone else wrote!

Big Internet Backup — discussed earlier. Give it a bunch of feeds, and it keeps everything forver. Searchable. Smart feeds. Etc.

Timeline/river-of-news reader - RSS presented as in a Twitter client. Reverse-chronological list. Super simple. Probably doesn’t even track read/unread (just position in timeline).

Microblog client as productivity app — multiple accounts, standard sidebar. Features like drafts. Scriptable. Searchable. Keeps items for a long time. Possibly even a detail pane that shows linked-to web pages. Connection to MarsEdit.

Feed creator — makes it easy to create feeds, including podcast and appcast feeds. (For instance: I do the appcast feed for NetNewsWire by hand, which is crazy. There should be an app for this.)

Feed debugger - given a feed, this app tells you about the issues. Is it a valid feed? Does it support conditional GET? Are attributes missing? Misspelled? Etc. This is especially important as it appears that feedvalidator.org may be down for the count.

Feed directory - reads in OPML from various sources on the web (such as Dave Verwer’s and Dave Winer’s collections) and creates a directory. When you select a feed, it downloads it and shows a preview of what you’d get. One-click subscribe to the default reader (whether it’s NetNewsWire or another app).

Though ideally these would be open source apps, they don’t have to be. (You can use NetNewsWire code in commercial apps, as long as you credit NetNewsWire in the about box or somewhere appropriate.)

I don’t claim that any of these are money-makers. Maybe some of them? Depends. I do think they’d all be very useful, and that you have a leg up since you can start with NetNewsWire code.

Questions about Marzipan Apps on the Mac

I’m thinking in advance about the things I’d like to know about Marzipan.

My questions, in no particular order:

  • Will we be able to ship Marzipan apps that run only on the Mac?
  • Will we be able to ship Marzipan Mac apps outside of the App Store?
  • Will there be a universal app format that includes iOS and Mac versions?
  • Will Marzipan Mac apps appear in the Mac App Store?
  • Will AppKit be deprecated?
  • If AppKit is not deprecated formally, will it get much in the way of new features?
  • Will apps have access to AppKit? Can they mix-and-match UIKit and AppKit?
  • Will apps be able to support AppleScript?
  • Will apps be able to send Apple events to other apps?
  • Will we be able to edit the main menu in Interface Builder?
  • Will apps support standard Mac customizable toolbars? If so, can these be edited in Interface Builder?
  • Will apps have resizable splitviews (without having to write this ourselves)?
  • Will apps support fullscreen with collapsible/slide-out-y sidebars?
  • Will apps support three-paned splitviews (a la Mail) without having to write the whole thing yourself?
  • Will document-based apps be able to open a separate window per document?
  • Will apps support additional windows? (In Apple News, choose File > Discover Channels & Topics… — what you really want is for that to come up in a separate window, I would think.)
  • Will apps support floating windows (such as for an inspector or a social media compose-message window)?
  • Will the Macintosh Human Interface Guidelines be updated to account for the differences in Marzipan apps?
  • Will table views support drag-and-drop to reorganize without doing the iOS modal thing with the grabbers in the cells?
  • Will there be some kind of way to use NSOutlineView (or equivalent)?
  • Will table views support standard Mac highlighting? And multiple selection?
  • Will sandboxing be required for Mac Marzipan apps?
  • Will the tab key work to move focus from view to view?
  • Will apps be able to use Mac-only frameworks such as SearchKit?
  • Is Apple working on a cross-platform, pure Swift app framework that would make Marzipan, UIKit, and AppKit obsolete?

We have hints at some of the answers — see Steven Troughton-Smith’s blog. (Subscribe to his feed.) He’s doing excellent work.

But it’s worth remembering that we don’t really know much yet, because even what we’ve learned so far may not reflect the actual shipping reality.

* * *

I care about the answers as a developer. Though I’m not going to rewrite NetNewsWire as a Marzipan app, I do have a bunch of other app ideas I’d like to do, and many of those should be iOS apps as well as Mac apps.

If Marzipan means I can get those apps made more easily and in less time — and that the Mac versions will be as good as an AppKit app, without compromises — then I’ll be happy to adopt it.

But that part is critical. It has to be as good a Mac app as the AppKit version that I would have written. Otherwise it’s not worth my time. Other people may make other calculations, and I respect that.

(Note to those who think of me as a Mac-only programmer: I’ve written several iOS apps, including Vesper, Glassboard, and early versions of NetNewsWire, AllThingsD, and Variety.)

Text-Editing Key Bindings and the Mac

The system I wrote about yesterday for specifying key bindings in NetNewsWire is just a small version of the system built into the Cocoa Text System.

Most Mac users probably don’t know about this. I forget about it for years at a time. But you can actually customize text editing keyboard shortcuts globally by editing a plist.

(Link via Daniel Jalkut, sent privately.)

Implementing Single-Key Shortcuts in NetNewsWire

NetNewsWire supports using single-key shortcuts for some commands: the k key marks all as read, for instance. The space bar scrolls the current article, or goes to the next unread if there’s nothing more to scroll.

There are a bunch of these, and they’re documented: see NetNewsWire Help menu > Keyboard Shortcuts.

These kinds of shortcuts are fairly rare in Mac apps, and rightly so — they’re best for apps where power users might need to process a bunch of stuff and move around quickly. That doesn’t describe every app (thank goodness!).

Since this isn’t really a standard AppKit thing, you might wonder how I implemented this in NetNewsWire.

Getting keyboard events

You have to override keyDown(with event: NSEvent) — so NetNewsWire has subclasses such as SidebarOutlineView in order to get keyboard events.

I could, if I wanted, just get the character from the event and do a switch statement with code like this:

case 'k':
	markAllAsRead()

This gets ugly pretty quickly, and, importantly, I plan to make keyboard shortcuts customizable (at some point), so I chose a different path.

Property List Files

If they’re customizable, then the specifiers need to be stored in some data format. Property lists (plists) are a good choice, since they’re easy to read and edit in Xcode.

Now, I’m not making them customizable yet, but I figured it would be smart to use the same format and implementation for the default sets of shortcuts.

Inside the plists

There are a few plist files: one for global shortcuts, one for the sidebar, one for the timeline, etc. (I could have made just one file, but I didn’t. Doesn’t matter.)

Each file contains an array of shortcuts; each shortcut has a key and an action. The key is the actual key, such as k or ' — or a placeholder such as [rightarrow] where the actual key couldn’t be listed in the plist. (The app translates placeholders into their actual values.)

A shortcut may also have one or more modifiers, and those are represented as booleans: shiftModifier, for instance. This way we can differentiate between the space key and shift-space.

The action part is a string specifying which method to call for the given shortcut.

Here’s an example of one of those files.

The keyDown implementation

SidebarOutlineView overrides keydown:

override func keyDown(with event: NSEvent) {
	if keyboardDelegate.keydown(event, in: self) {
		return
	}
	super.keyDown(with: event)
}

The keyboardDelegate gets the first shot at handling the key. If it doesn’t handle it, then normal processing continues.

SidebarKeyboardDelegate

There are several keyboard delegates, but let’s look at SidebarKeyboardDelegate, since these all work about the same.

In init it reads its keyboard shortcut definitions from a plist in the app bundle and creates a Set<KeyboardShortcut>. A KeyboardShortcut is a KeyboardKey (defined in same file) plus an action string.

A KeyboardKey describes the key, including its integer value and any combination of modifiers (shift, option, command, control).

In keyDown, SidebarKeyboardDelegate first gives MainWindowKeyboardHandler a chance to handle the key. MainWindowKeyboardHandler handles keys that are global across views.

If MainWindowKeyboardHandler doesn’t handle it, then it looks for a KeyboardShortcut that matches the pressed key.

let key = KeyboardKey(with: event)
guard let matchingShortcut = KeyboardShortcut.findMatchingShortcut(in: shortcuts, key: key) else {
	return false
}

If it finds a matching shortcut, then it makes the shortcut do the thing it’s supposed to do:

matchingShortcut.perform(with: view)

Performing the shortcut

Remember that a KeyboardShortcut has a KeyboardKey and an actionString. That string is pulled from the plist: it’s something like nextUnread: or openInBrowser:.

We turn this into a selector using NSSelectorFromString and store it in a local variable action.

(The Objective-C runtime has a particularly beautiful thing: messages are real things, and they exist separately from objects.)

The perform(with:) method looks like this:

public func perform(with view: NSView) {
	let action = NSSelectorFromString(actionString)
	NSApplication.shared.sendAction(action, to: nil, from: view)
}

Here’s Apple’s documentation on NSApplication.shared.sendAction.

The short version of what it does: if to (the target) is nil, it starts with the first responder, then checks its nextResponder, then its nextResponder, and so on until it finds an object that responds to the specified selector (the action) — and then it asks that object to perform that method (or: it sends that message to the receiver). (It also checks the window’s delegate and some other things before giving up, when it gets that far.)

This means, for instance, that I don’t have to implement openInBrowser: in SidebarOutlineView. It could be implemented in its view controller, in MainWindowController, etc., as long as there is an implementation in the responder chain.

This way the implementation can be placed where it makes sense. I can even move openInBrowser: without needing to change the plist configuration.

And — this is important — it means that there might be (and there are, in NetNewsWire) multiple implementations of openInBrowser:. The sidebar has one which opens the home page of the selected feed. The timeline has a different one which opens the URL for the selected article.

The one that gets called is based on which one of these is the first responder, because that’s where the responder chain starts. (Which is another way of saying: the implementation that gets called is based on what thing has user focus.)

Both of these openInBrowser: commands are represented by the same KeyboardShortcut from the global keyboard shortcuts plist.

Not Magic

It may seem like NSApplication.shared.sendAction is doing something reserved for Apple that you couldn’t do, or couldn’t do easily.

But you could actually write this yourself. This simple version (which just checks nextResponder) has all the critical bits.

func sendAction(_ action: Selector, to target: Any?, from sender: Any?) -> Bool {
	var responder = NSApplication.shared.keyWindow?.firstResponder
	while responder != nil {
		if responder?.responds(to: action) ?? false {
			responder?.perform(action, with: sender)
			return true
		}
		responder = responder?.nextResponder
	}
	return false
}

Obviously we don’t have the source to NSApplication.shared.sendAction — but, at least in concept, that’s all it’s doing. (Minus the part where it checks some things after exhausting nextResponder.)

Summary

In a Mac app, how does the Edit menu‘s Copy command know what to do? It walks the responder chain: the first to claim that it responds to the copy: message then performs the copy: method.

You don’t have just one copy: method somewhere that has to figure out the context (figure out what has focus) and then do the right thing. Instead, you may have multiple copy: methods.

(This is much less of a thing on iOS. But when you go to use Marzipan with your iOS apps, there’s a good chance you’re going to need to know about this.)

The Copy command (and others) use the same pattern I’m using here, in other words.

It’s also worth thinking about how nibs and storyboards actually get loaded by your app. All the wired-up actions are, at some level, just strings — so the frameworks (UIKit and AppKit) use NSSelectorFromString to turn these strings into real messages.

Having an idea of how all this works under-the-hood is useful knowledge: it means you understand your app better. It also means that when you’re faced with something like implementing a keyboard shortcuts system, you’ll have the right tools for the job.

(Of course this isn’t the right tool for every job.)

PS Try this!

In Xcode, set a symbolic breakpoint for NSSelectorFromString. Make the Action a sound. Check the box next to “Automatically continue after evaluating actions” — this way it won’t actually stop.

Now launch your app. Even if you don’t ever use this, your app sure does!

And, for bonus points, also set a similar breakpoint for NSClassFromString. Give it a different sound.

Xcoders Speaker Needed

I you can make it to the Seattle Xcoders meeting March 14, and you might be interested in speaking, please get in touch! More details on xcoders.org.

Cool Stuff from The IconFactory

Our good friends at The IconFactory are getting back to their roots by making free wallpapers and desktop icons — fun stuff, made for fun. Here’s how you can help support them with this.

NetNewsWire Feedback Incoming

I’ve been getting more NetNewsWire feedback now that I’ve called it actually usable now.

Feedback is always an education. (See the most recent issues for some of it.)

There are things I expect to see, and then do see. Things I expect to see, and then don’t see. And things I didn’t expect at all.

It’s a great reminder that everybody’s different, and people want different things. They want to use the app the way they want to use it.

The tricky part is deciding what to do, of course. When I was younger, and selling NetNewsWire, I was reluctant to add features and preferences — but I did it anyway. A lot. I wanted people to buy the app!

But it did mean that NetNewsWire became, in at least one person‘s words, a kind of “Swiss Army knife” of RSS readers. This made it difficult to move forward with new features that I thought would be cool and useful.

Now that I’m older, and I’m not trying to please everybody and make money, I’m even more reluctant. I want to keep the app as simple as possible — because I like simple apps, and because it means I have time to add other features that I’ve never done before, but that I always thought would be cool.

But, at the same time, I really do want it to be used by as many people as possible. So there’s a tension there which I find interesting. My position on it is just to go slowly — which I can’t really help anyway — and think hard about each issue.

One That I Did Not See Coming

One of the unexpected things is Add way to see how many total unread items there are within the app.

There is a way, of course. There are two ways, even: the unread count appears in the Dock icon and beside the All Unread smart feed.

You’d think that would be enough — but it’s not. Consider that you might have the Dock hidden, and consider that you might have enough feeds and folders in the sidebar so that you can’t see the All Unread smart feed — it’s scrolled off.

Then what?

It could go in the toolbar — but some people run with the toolbar hidden. And, anyway, I never like status-y stuff in the toolbar. It could be a non-default toolbar thing — people could add it. But I’ve learned that lots of people don’t know you can customize toolbars.

How about a status bar at the bottom of the sidebar that can’t scroll off? Older versions of NetNewsWire had this.

Sure — but the trend these days, which I like very much, is to have a clean bottom edge to the window. No chrome. Look at Mail, Safari, Pages, and Numbers.

Well, okay — do that, but make it a View menu option, off by default, so we keep the clean edge.

Ugh. Now we’re going down the road of endless permutations of little things you can configure. That’s the road I want to avoid as much as possible. Do we really add that just because of the probably rare case where someone hides their Dock and the All Unread smart feed is scrolled off?

Another idea: that bottom-of-the-sidebar status view could appear only when the All Unread feed is scrolled off. But I really hate non-stable UI with weird little changes like that. It always seems too clever, and it makes me think the designer thinks they can paper over a design flaw by showing off.

Or there could be a non-scrolling indicator at the top of the sidebar. That ruins the nice line going along the top, though. But maybe the timeline needs a thing at the top for sorting, so maybe that line will go away anyway? And: wouldn’t this look weirdly redundant when the All Unread feed is not scrolled off?

So… what to do? I’m not actually asking for suggestions — though I’ll get some, because people tend to read things like this as problems-to-solve. (And I don’t mind suggestions. Not at all.) But what I actually intend here is just a look behind the development process at the point where people start giving feedback on an app.

Here’s what happens at this point: your design meets conditions you didn’t account for. They’re often rare cases, but they’re legitimate. And all the options seem pretty bad.

What will probably happen, in this case, is that I’ll punt on figuring it out till after 5.0 ships. I have no idea what I’ll end up doing. Which is part of the fun. :)

Our community is mourning the loss of Tristan O’Tierney today.

I met him before the iPhone days, I’m pretty sure. While we were never close, I was always happy to see him at WWDC and similar events, and I liked him tremendously. I had somehow missed that he had been struggling. I wish so much that he had not been.

Joshua Blankenship misses 2004 and personal websites. (Via Colin Devroe.)

Archive