Browse through the showcased feeds, or enter a feed URL below.
Brent Simmons’s weblog.
A feed by Brent Simmons
Permalink - Posted on 2020-06-06 22:00
Not for the first time — but hopefully with more depth and breadth this time, and greater understanding — I’m reading and listening to Black authors and voices.
Anti-racism book recommendations are just a search away. Here’s one I found on the Chicago Public Library’s site.
Black Lives Matter.
Permalink - Posted on 2020-05-19 00:14
NetNewsWire is fast because performance is one of our core values. Being fast is part of the very definition of the app.
I suspect that it’s hard to do this any other way. If you take a month or two to speed things up, from time to time, your app will always be — at best — just kind of heading toward satisfactory, but never to arrive.
The best general advice I can give is just this: make sure performance is part of the foundation of your app. Make sure it‘s part of every decision every day.
Make sure, in other words, that performance isn’t just a topping — it’s the pizza.
Below are some of the specific reasons NetNewsWire is fast. Because NetNewsWire is — like many apps these days — basically a fancy database browser where data comes from the web, some of these will apply to other apps.
The below items are in no particular order.
The most painful way to parse XML is with a SAX parser — but it’s also how you’ll get the best performance and use the least memory. So we use SAX in our RSParser framework.
On my 2012 iMac, parsing a local copy of some past instance of the Daring Fireball Atom feed — relatively large at 112K in size — happens in 0.009 seconds.
That’s fast, but we do another thing as well: run the parser in the background on a serial queue. Since parsing is a self-contained operation — we input some data and get back objects — there are no threading issues.
The parsers are fast — but we also do our best to skip parsing entirely when we can. There are two ways we do that.
We use conditional GET, which gives the server the chance to respond with a 304 Not Modified, and no content, when a feed hasn’t changed since the last time we asked for it. We skip parsing in this case, obviously.
We also create a hash of the raw feed content whenever we download a feed. If the hash matches the hash from the last time, then we know the content hasn’t been modified, and we skip parsing.
The parser isn’t the only code we run on a serial queue. When an operation can be made self-contained — when it can just do a thing and then call back to the main thread, without threading issues — we use a serial queue if there’s any chance it could noticeably block the main thread.
The key is, of course, making sure your operations are in fact self-contained. They shouldn’t trigger KVO or other kinds of notifications as they do their work.
(A simple example of a background thing, besides feed parsing, is creating thumbnails of feed icons.)
Here’s an example of a trap that’s easy to fall into. Say a user is marking an article as read. Calling
article.read = true triggers, via KVO or notifications or something, things like database updates, user interface updates, unread count updating, undo stack maintenance, etc.
Now say you’re marking all articles in the current timeline as read. You could call
article.read = true for each article — and, for each article, trigger a whole bunch of work. This can be very, very slow.
We have specific APIs for actions like this, and those APIs expect a collection of objects. The same API that marks a single article as read is used to mark 10,000 articles as read. This way the database is updated once, the unread counts are updated once, and we push just one action on the undo stack.
We also try to coalesce other kinds of work. For instance, during a refresh, the app could recalculate the unread count on every single change — but this could mean a ton of work.
So, instead, we coalesce these — we make it so that recalculating unread counts happens not more often than once every 0.25 seconds (for instance). This can make a huge difference.
For an app that is, again, just a fancy database browser, this is where the whole thing can be won or lost.
While Core Data is great, we use SQLite more directly, via FMDB, because this gives us the ability to treat our database as a database. We can optimize our schema, indexes, and queries in ways that are outside the scope of Core Data. (Remember that Core Data manages a graph of objects: it’s not a database.)
We use various tools — such as EXPLAIN QUERY PLAN — to make sure we’ve made fetching, counting, and updating fast and efficient.
We do our own caching. We run the database on a serial queue so we don’t block the main thread. We use structs instead of classes, as much as possible, for model objects. (Not sure that matters to performance: we just happen to like structs.)
To make searching fast, we use SQLite’s Full Text Search extension.
I could, and probably should, write more articles going into details here. The database work, more than anything else, is why NetNewsWire is fast.
We often need to look up things — a feed, given its feedID, for instance — and so we use dictionaries frequently. This is quite common in Mac and iOS programming.
What I suspect is less common is use of sets. The set is our default collection type — we never want to check to see if an array contains something, and we never want to deal with duplicate objects. These can be performance-killers.
We use arrays when some API requires an array or when we need an ordered collection (usually for the UI).
Instead of guessing at what’s slow, we use the profiler in Instruments to find out exactly what’s slow.
The profiler is often surprising! Here’s one thing we found that we didn’t expect: hashing some of our objects was, at one point, pretty slow.
Because we use sets quite a lot, there’s a whole lot of hashing going on. We were using synthesized equality and hashability on some objects with lots of string properties — and, it turns out, hashing strings is pretty darn slow.
So, instead, we wrote our own hash function for these objects. In many cases we could hash just one string property — an article ID, for instance — instead of five or ten or more.
My experience with stack views tells me that they’re excruciatingly slow. They’re just not allowed.
When people praise a timeline-based app like NetNewsWire, they often say something like “It scrolls like butter!” (I imagine butter as not actually scrolling well at all, but, yes, I get that butter is smooth.)
While we use Auto Layout plenty — it’s cool, and we like it — we don’t allow it inside table cell views. Instead, we write our own layout code.
This is not actually difficult. Maybe a little tedious, but laying out a table cell view is pretty easy, really.
I figure that optimized manual layout code is always going to be faster than a constraint solver, and that gives us an edge in smooth scrolling — and this is one of the places where an otherwise good app can fall on its face.
And: because that layout code doesn’t need a view (just an article object and a width), we can run it at any time. We use that same code to determine the height of rows without having to run an Auto Layout pass.
Text measurement is slow — slow enough to make even manual layout too slow. In NetNewsWire we do some smart things with caching text measurement.
For example: if we know that a given string is 20pts tall when the available width is 100 and when the available width is 200, we can tell, without measuring, that it will be 20pts tall when the available width is 150.
There’s no silver bullet. Making an app fast means doing a bunch of different things — and it means paying attention to performance continuously. 🍕
Permalink - Posted on 2020-05-18 22:15
I just heard that the default feeds in NetNewsWire are okay as-is, and I don’t need to collect permissions for Apple.
Great! I’m so pleased.
Permalink - Posted on 2020-05-17 23:00
Tomorrow’s the first day at my new job. Exciting!
Starting a new job has led me to look at my entire list of responsibilities — which is too long — and figure out what I need to drop so that I can pay enough attention to the projects that need it most.
My most important projects (outside of my job) are NetNewsWire and this blog. This blog, because, well, blogging is part of how I breathe. And NetNewsWire because I love the app — and it’s a real thing in the world now, with users, a team of developers, and great features coming up.
I wanted to do another half-dozen or so apps alongside NetNewsWire, starting with Rainier, but I’m dropping development on those so I can concentrate entirely on NetNewsWire. This is personally disappointing, but it’s honest: I just don’t have time for Rainier and these other apps. Work on these would take away from NetNewsWire, and that would be wrong.
Another move I’m making: Manton Reece has agreed to take over the repo and website for JSON Feed. I’ve been the bottleneck here with a 1.1 version, and I shouldn’t be. Manton will take care of this way better than I’ve been able to. (I hope to get everything transferred over to Manton in the next few weeks.)
Permalink - Posted on 2020-05-12 17:49
As of this morning the ink is all dry, and I can happily report that my new job is at Audible. I’ll be an architect on the mobile team.
I’m very excited for this job! It’s perfect for me in so many ways — not least that it’s about books.
My plan is for this to be my last job — I plan to work at Audible until I retire. I start Monday. 🐣🐥🕶
Permalink - Posted on 2020-05-12 17:37
While I’ve been job-hunting, the mighty NetNewsWire team has kept rolling — and today we published the first update to the iOS app.
This update fixes bugs, makes the app faster, and adds polish. Read the (rather lengthy) change notes for the full scoop.
We did add one new feature: on the settings screen you can choose which color palette to use: go with the current system setting or specify light or dark.
If you’re already running NetNewsWire, it should update in the normal way. If you haven’t tried it yet, go get it — for free — on the App Store.
Permalink - Posted on 2020-05-12 16:53
The question of publishing NetNewsWire on the Mac App Store won’t be decided until the minute that it’s actually published there.
If it ever is, that is. I go back and forth on it.
Here’s the thing to remember: our goal is to get as many people using RSS readers as possible. Period. Keep this goal in mind.
Publishing on the Mac App Store would mean that some people would see the app who might never have seen it otherwise.
There are also people who, due to personal or workplace policy, download apps only from the Mac App Store.
Publishing on the Mac App Store seems like a no-brainer, then. We’d get more people using RSS readers — we’d further our goal.
But it’s not so simple.
As with everything else, there are trade-offs. There are costs and benefits.
The benefit is reaching more people. There are several costs.
Some are right up front: we’d have to sandbox the app and test it. We’d have to do a set of screenshots for the Mac App Store; we’d have to write the description text for the page.
But I don’t mind one-time costs that much when there’s a solid benefit.
There are ongoing costs, though: we’d have two configurations of the Mac app, one for the Mac App Store and one for direct download, and we’d have continue to maintain and test both. This is kind of a pain, but not terrible.
There’s a cost that’s worse than the technical and testing costs: I would have to deal personally with the stress and uncertainty of a second App Store. The NetNewsWire team is amazing and does a ton of great work — but the team can’t do this part. It’s on me.
The issue with the default feeds reminds me that, at any time, even for a small bug-fix update, App Store review may decide that an app can’t be published as-is for some reason.
You‘d be right to think that, with an issue like this, it would come up the same on both App Stores — solve it in one place and you’ve solved it in both. It’s not like I’d have double the issues.
But sometimes the issue actually is platform-specific. For example: NetNewsWire Lite 4.0 for Mac was held up by Mac App Store review for three weeks due to a bug in WebKit. (Yes, this was nine years ago.)
This is supposed to be fun. It’s work that I love doing for a great cause. And I just keep thinking that dealing with the iOS App Store is enough to ask of me, and there’s no requirement that I go through this with the Mac App Store too. The personal cost is just too high.
We can achieve our goal in other ways: ship Feedly syncing on the Mac, ship iCloud syncing on both apps, continue making the app more appealing to more people. Do more marketing.
In other words, publishing on the Mac App Store is not the only lever we have, and I’m leaning toward just not doing it. At least not this year.
We’ve got other, better things to do — and I’ll enjoy those things a hell of a lot more, and I think you will too.
Permalink - Posted on 2020-05-12 02:19
One of the teams I talked to during my job hunt — and that didn’t put me through a scary tech interview :) — was the folks at Duet.
They make an app where you can use an iPad as a second screen for your Mac. They also support Android and Windows. (You should check out their apps.)
I thoroughly enjoyed talking with the team, and I believe I would have been very happy working there.
They’re looking for a Mac developer. Maybe you? Get in touch via their contact page.
Permalink - Posted on 2020-05-12 01:08
Here’s the latest on the story from yesterday.
NetNewsWire 5.0.1 for iOS was approved for the App Store this morning, and I assumed that was the end of it. I figured this whole thing was just an error.
But later today I heard from Apple that, while this latest version has been approved, the app is now under further review for this issue.
This isn’t quite over yet — but at least we could ship 5.0.1, so that’s cool, and I’m glad.
The issue really is about the default feeds. They’re added by default on the first run of the app.
Apple suggested some options — things I could do if, after further review, they decide that I need to bring the app into legal compliance:
For now I’m not doing any of those things, since Apple’s review is ongoing. I’ll wait for the review to complete.
If the review completes and I do need to do something, I’ll take the first option: I’ll get the necessary documentation.
(Yes, I could change the UX instead. But I don’t want to — the app works the way I think is best. You could debate whether I’m right or wrong on that point, but there’s no debating that this is the UX I want.)
As I wrote on the NetNewsWire FAQ about the default feeds:
We change the feeds from time to time. We don’t have any arrangements with the feed owners, though we usually ask permission — unless it’s something like Daring Fireball or Six Colors where it would obviously be no problem.
The authors of Daring Fireball, Six Colors, and a few other sites are friends, and I don’t need to bug them to ask permission. There are other default feeds where I know the people less well (or not at all), and I have asked permission from people — not because I was worried legally but because it seemed like basic courtesy. I don’t think anyone’s ever said no, but I did want to give them the chance.
But can I find all these conversations, and can I turn those conversations over to Apple without asking the other parties?
I don’t think so. So this would mean going through and getting explicit permission from a dozen-ish different people and turning copies over to Apple.
Which is fine. I can do that if Apple decides they need that documentation. It’s not onerous.
I’m trying to figure out what bothers me. I think there are two things.
One is just that the App Store has always seemed rather arbitrary. The guidelines don’t even have to change for unseen policies to change, and it’s impossible to know in advance if a thing you’re doing will be okay and stay okay. (Recall that NetNewsWire has been doing the same thing with default feeds for 18 years.)
This gets really tiring, because every time we submit an app — even just a bug-fix release, like 5.0.1 is — I have to deal with the anxiety as I wonder what’s going to happen this time.
The other issue is a little harder to explain, but it goes like this:
If a site provides a public feed, it’s reasonable to assume that RSS readers might include that feed in some kind of discovery mechanism — they might even include it as a default. This is the public, open web, after all.
Now, if NetNewsWire were presenting itself as the official app version of Daring Fireball, for instance, then that would be dishonest. But it’s not, and that’s quite clear.
To nevertheless require documentation here is for Apple to use overly-fussy legal concerns in order to infantilize an app developer who can, and does, and rather would, take care of these things himself.
In other words: lay off, I want to say. I’m an adult with good judgment and I’ve already dealt with this issue, and it’s mine to deal with.
Permalink - Posted on 2020-05-11 00:45
Update the next morning (May 11): NetNewsWire 5.0.1 for iOS has been approved for the App Store — which happened before I had the chance to provide documentation. I will assume that the policy enforcement change described below was just an error.
NetNewsWire 5.0.1 for iOS is delayed due to an apparently new, or newly-enforced, issue: if an RSS reader includes default feeds, Apple will ask for documentation that says you have permission to include those default feeds.
The first RSS app that got tagged with this, that I know of, was NewsWave. We submitted NetNewsWire 5.0.1 for iOS for review a couple days ago and had the same issue.
Specifically, it’s in violation of legal guideline 5.2.2:
I need this for the default feeds. A couple of those are mine, and so I need about a dozen documents to cover all the default feeds.
I could complain about this — the default feeds have never been an issue in NetNewsWire’s 18 years of life — but I’m done and can’t even right now. I’ll do what I have to do.
I do have a follow-up question, though: could an RSS reader contain some kind of directory of feeds, in order to help people with feed discovery?
NetNewsWire of Old had this, and some kind of modern version would be a good idea. We’ve been talking about it. But if we have to get documented permission for every single feed in the directory, we probably wouldn’t do it.
PS If you write an RSS reader for Mac or iOS, and want to ask me any questions about this, feel free to email me.
Permalink - Posted on 2020-05-09 18:06
Consider the person who finds your app on the iOS App Store. They decide they want your app: they tap the get or purchase button.
The app downloads — hopefully quickly; hopefully you‘ve made it small, because you’ve pictured this moment and you care about even this aspect of the user experience — and then the user taps the Open button.
They’ve already waited to see the app. They’re excited to see what it’s like and get started using it!
And, now that it’s here, you can put a bunch of obstacles in their way — which will cause you to lose some of these people — or you can satisfy their interest and curiosity right away by getting them into the app.
* * *
Here’s me: when I download an app with a first-run tutorial, I try to find a way to short-circuit it and get to the actual app. If I can’t, I just race through it, knowing I wouldn’t have remembered any of it anyway.
Either I can figure out the app later or I can’t.
Similarly, if an app has first-run setup to do, I try to avoid it. If it has first-run setup and a tutorial, I’ll just give up unless I know for absolute sure that I want this app.
* * *
Isn’t there some quote, maybe even from Steve Jobs, about apps early in the day of the App Store, that went something like this? “iPhone apps should be so easy to use that they don’t need Help.”
I’ve always thought to myself, since then, that if I see a first-run tutorial, they blew it. Apps should be designed so that you can figure out the basics quickly, and then find, through progressive disclosure, more advanced features.
It seems to me that the best first-run experience is to get people into the app as quickly as possible, because that’s where they want to be.
They’ve already waited long enough — finding the app, downloading it — and now you want to delay the joy even longer, and thereby tarnish or even risk it? Don’t do it!
Remember that people are busy, often distracted, and there are zillions of other apps. Your app is not the world. The person is the world.
* * *
Remember that every single thing in your app sends a message. A first-run tutorial sends the message that your app has a steep learning curve. Definitely a turn-off.
It provokes anxiety in the user immediately, in two related ways: 1) “Will I ever be able to learn this apparently hard-to-use app?” and 2) “Will I remember any of this tutorial at all? Do I need to get out pen and paper and take notes?”
And that’s the first impression. Your app makes the user anxious.
Permalink - Posted on 2020-05-07 22:40
I’m not going to name names here. Don’t worry. :)
I’m in the market for a Mac app for _____. It’s a not-uncommon need, and so I figured I’d have some good choices.
I’d also like an iOS app that syncs with that Mac app — but the Mac app is the more important of the two, because I sit in front of a Mac all day. (To me. Some people are iOS-first, which is totally cool.)
I downloaded and tried a couple apps. One required an account just to try the app, which pissed me off, but I did it anyway.
The first thing I tried, with each app, was to resize the window. This is a good test because I get frustrated with sluggish apps: window resizing is a decent way to get some idea of how the app performs.
I know I’m not playing fair — I’m on a 2013 MacBook Air — but the app I write is fast on this machine, and other apps should be too.
Both apps were sluggish with window resizing. They were bad enough that I could have just stopped right there.
But it was actually worse than that.
With one of the apps, the upper position of the window could actually change during window resizing. It could even go offscreen. I don’t even know how that bug is possible.
The other app was almost as bad: the upper position of the window would sometimes jump down around 20 pixels then back up, real fast. It made the window seem to flicker. Nasty.
The basics of window resizing behavior should be impossible to mess up — AppKit should be handling this. If it’s messed up, then something in the app is fighting the frameworks. That’s a bad sign for the quality of the rest of the app.
I picked an action that would be 1) super-common and 2) something that every user should expect to be undo-able.
In one app, I did the thing and then chose Undo. It didn’t do anything that I could see — the Undo command was available, but had no visible effect. I did Undo again. No visible change. God knows what was happening.
In the other app, Undo just wasn’t available. This is actually better than a faulty Undo — but, still, it’s not good.
I poked around a little more, enough to find some additional bugs, and then I trashed both apps. I deleted the account I had had to create for the one app.
By not paying attention to the basics of a good Mac app, each of these apps lost a potential customer who’s 1) happy to financially support app development, and 2) who has a blog that a bunch of people in our community read, where he likes to praise things that are good.
Maybe that’s not worth it? But doing a not-good Mac app is somehow worth it? I don’t understand.
Permalink - Posted on 2020-05-07 01:15
As part of my job hunt — about which I have good news, but I don’t want to say until it’s really officially official — I’ve done a few interview “loops” with large companies.
I’m not going to give away anything about who the companies are or the people or teams involved, but I can talk a little about what it’s like to do these in this first dreadful half of 2020. Or at least what it’s like for me.
The first thing is: the loop is over video. These used to be onsites, of course, and now they’re virtual. Get used to seeing people’s couches and bookshelves. The various video systems seemed pretty similar.
Instead of a whiteboard, there’s a shared text editor on a web page. Different companies use different products, but these also seem to be pretty similar. When I was typing in the editor, I didn’t really pay attention to the video, which was fine.
The video apps also had the ability to do screen sharing — so, on a few occasions, I turned on screen sharing and actually built a small app in Xcode.
The questions were otherwise about code, design, and behavior, which is no different from any other year.
The toughest ones for me are about the code — there might be a clever way to go from my simple O(n2) solution to O(n), say, but these are the kind of problems where I have to turn off all sound in the room and go distraction-free to solve. With another person or two there I freeze.
The coding questions where we built something in Xcode were much more fun. Seems like a good way to make people comfortable and let them show how they naturally work.
Each interview lasted around 45 minutes to an hour.
Some of the loops I did in a single day of five to six hours of interviews and a short lunch break. I’ve also done loops split over two days. I think I prefer the two-days approach, since by the last hours on an all-in-one-day loop I was tired.
My hope is that I never go through another one of these again. I prepared as much as I could. I did questions on LeetCode. I bought and read all of Data Structures & Algorithms in Swift — which is a good book, and I recommend it to everybody. I did a course on interview design questions. I did a bunch of other research. It all helped — and it helped especially with confidence.
One of the things to remember, if you’re doing one of these, is that people want you to succeed. They’re hoping they can hire you. Don’t forget that.
Permalink - Posted on 2020-05-03 00:27
I knew about the 1918 pandemic — but not really. It was just kind of a fact with no details filled in and nothing important attached to it.
I’m a big fan of American art and literature of the period, and, while the war is frequently referenced, the pandemic is barely mentioned. If ever. I don’t remember it coming up in The Great Gatsby or anything else.
But wasn’t this a massive event? Wikipedia tells us it lasted at least a year, infected about a third of the world, and killed from 17 million to 100 million people.
That’s enormous — but it was basically just jotted-down and then erased from the popular memory, it seems.
* * *
I’ve already forgotten March — all I remember is that it felt like it lasted an eon. Much of April is a blur.
My theory: there will be no demand for novels or movies or TV shows that remind us of all this.
We’re living through a time we’re going to try to forget, as if we’re under some kind of anesthesia that makes us forget the operation and how painful it is.
Permalink - Posted on 2020-05-02 23:45
Some people are starting to act as if this whole thing is pretty much over. It has become more and more the norm to bend the rules, and there will be increased social pressure to go along.
I think this is important to note: you will feel bad, sometimes really bad, if you don’t go along.
But please remember that this is a sacrifice. Part of that sacrifice might be that you have to feel bad about not doing what your group is doing. But we have to do this to save lives.
Permalink - Posted on 2020-04-30 17:46
We’ve been enjoying the HBO series “The Plot Against America.” I’m a long-time fan of Philip Roth books, though I haven’t read this one yet.
The action, at least in the first few episodes, takes place in the Weequahic neighborhood in Newark, NJ.
I recalled that my father’s mother is from that same neighborhood.
So I was doing a little research on my family connection last night, and I ran across the wedding announcement of my great-grandfather and great-grandmother, and I found it fascinating.
Here’s the headline:
Here’s the PDF — it’s from The Coast Advertiser on September 16, 1921.
The story has a lot of detail:
…she wore a gown of white satin trimmed with rose point lace. A court train of satin trimmed with tulle and orange blossoms fell from the shoulders and her veil of rose point lace was arranged in cap effect and caught on each side with orange blossoms. She carried a shower bouquet of white roses and lilies of the valley.
(It goes on.)
The story also omits a bunch of details — where did they go on their honeymoon?
To tie this back to Philip Roth: I used Maps to find out how far their house was from Philip Roth’s childhood house, presumably the location for “The Plot Against America” — just under a mile.
* * *
I knew my great-grandmother, who we called Ohma. I didn’t know my great-grandfather.
Ohma succumbed to Alzheimer’s disease eventually. But, before then, when I was quite young, she used to tell me the story of Peter Cottontail — who was me, she said, since my first name is Peter. (It is.)
For many years I’ve had a picture of her father, Ernest Keer — my great-great-grandfather — on the wall in my office. In the picture he’s doing what I do — sitting in his office at a desk, doing some reading. He was a lawyer in Newark. The picture is from April 1912.
Permalink - Posted on 2020-04-30 02:37
I don’t claim that this is a beautiful diagram, and it might scale weirdly, but it does show how NetNewsWire is layered.
When writing an iOS or Mac app — or an app that’s both, as in this case — I like to 1) break up the app into separate components, and 2) make those components depend on each other as little as possible, and 3) when there are dependencies, make them clear and sensible.
Starting at the bottom level are the submodules: RSCore, RSDatabase, RSParser, RSTree, and RSWeb. These are built as frameworks: RSCore.framework and so on.
Each of these is in a standalone repo and is useful on its own. They don’t even depend on each other — they don’t depend on RSCore, for instance, though you might have expected that they do.
The lack of dependencies promotes reuse — not just by me, among my projects, but by other people too.
It also makes these easier to work on. I don’t have to worry that a change in one affects another one. I don’t have to pull the latest RSCore before working on RSDatabase, for instance.
In NetNewsWire we treat these as Git submodules. It would be great to switch to Swift Package Manager, but I’m not sure if that has all the features we need yet. (Maybe it does. If so, then great, but there’s no rush.)
We continue our bias, inside the app itself, toward using actual frameworks.
The bottom layer is Articles.framework, which is the data model for articles, article status, authors, and so on. Articles depends on nothing else in the app.
ArticlesDatabase.framework and SyncDatabase.framework depend on Articles. ArticlesDatabase stores actual articles data; SyncDatabase stores data used to implement syncing.
The last in-app framework is Account.framework, and it depends on everything below it. An Account is what you think it is: it’s an On My Mac (or iPhone or iPad) account or it’s an account that connects to a syncing system (such as Feedbin or Feedly). It’s at the top of the data storage — to fetch articles for the timeline, for instance, the code asks an Account.
All of these in-app frameworks — like everything in the actual app — may depend on the submodules.
Here live a number of controllers that do various things like OPML import and export, downloading feed icons and favicons, rendering articles, handling user commands and undo (such as mark-all-read).
Some could be broken out into yet more in-app frameworks. (We would be more vigilant about that if we felt, at this point, that we’re losing the battle against app complexity. I’m glad to say we’re not in that position.)
NetNewsWire is a Mac and iOS app. It’s built on AppKit on Macs and UIKit on iOS (as opposed to using Catalyst, which would have let us use UIKit for both).
As the diagram shows, there’s a lot of code shared between the platforms, but that stops at the UI code. The UI level is the top level, and it depends on everything below it.
The benefits of components and being careful with dependencies are clear — but why use actual frameworks? After all, a conceptual module doesn’t have to translate to an actual separate library target.
I’ve found that it’s easier, when using a framework, to ensure for a fact that you don’t let an unwanted dependency to slip in. It’s kind of like treat-warnings-as-errors — it makes sure you’re not getting sloppy with dependencies.
Other reasons: when I’m working on a framework, I find it easier to just concentrate on exactly what I’m doing there and let the rest of the app slip from my mind temporarily. And, finally, we’re more likely to write tests for frameworks.
It may just be psychology, but it’s important anyway: smaller, self-contained (or mostly so) things are just easier to treat well.
Permalink - Posted on 2020-04-29 17:32
I went outside yesterday to bring my emptied garbage and yard waste cans back from the street.
It was a pretty nice day — not really sunny but warm enough. Lots of people were outside. The woman on the corner was talking to somebody on the sidewalk. My neighbors were out. There was a bicyclist on the street, some folks walking on my side of the street, some folks walking on the other side of the street. The guy who lives behind me was outside and playing music.
Way too many people. It felt oppressive. I beelined it back inside after taking off my gloves.
Permalink - Posted on 2020-04-27 18:17
Some number of people, on Twitter and elsewhere, have told me that it’s not about getting the right answer — it’s about showing the interviewer how you go about solving problems.
I’ve read a bunch of the advice on this, and the advice says things like: “Start talking. Restate the problem. Talk out an approach. Consider how much space/time it will use. And then start writing code.”
Which is of course not at all how I solve problems. I usually start with some hazy intuitive approach and start writing code. I code and think at the same time. I revise what I wrote, or even delete it. Then I go for lunch.
I come back to it, and if I’m still stuck I look in the documentation. Or Apple’s dev forums or Stack Overflow or Wikipedia. I might ask someone on my team or I might ask some friends on a Slack group. Or maybe I figure out an approach on my own after all, and then just do web searches to validate the approach.
And — this is critical — as I’m doing all of this I’m using the IDE I always use, with autocorrect, profiler, debugger, etc. All my tools. Where I’m used to the text editor and its syntax coloring and how it balances braces. Where hitting cmd-S — as I habitually do — doesn’t result in my browser prompting me to save the current page.
And — even more critical — I don’t have a 45-minute time constraint. Nobody is watching me type and judging. I’m writing code to solve a problem, rather than writing code to get a job.
There’s a huge difference between “solve this performance problem with a binary search” and “pass this test so you can feed your family.”
* * *
There’s a whole small industry to help people prepare for these tests — so it’s not like you’re getting the authentic programmer showing up. You’re getting the person who’s prepared for one of these.
Because of that, an interviewer is even less likely to learn how a candidate approaches solving a problem. Instead, they’ll learn how well the candidate prepared to make a good impression — which tells you nothing about how they’d actually solve a problem.
I think these end up favoring people with more time to prepare. It probably helps if college isn’t a decades-old memory — the closer you are to taking tests in school, the more comfortable you’ll be, and the less you’ll feel like this is an absurd exercise with no meaning.
Permalink - Posted on 2020-04-26 23:58
I don’t have a CS degree, but I have decades of experience — I know what a linked list is, for instance, and could write one by hand easily if called to. In a few different languages, even. I could talk about the trade-offs between a linked list and a contiguous array. Etc. I’ve got all that.
But these tests are kicking my butt a little bit. I think I’ve figured out why.
Consider a question like this:
You need to add two numbers and get a sum. But, importantly, the digits of those numbers are stored in arrays, and they’re backwards.
The return value also needs to be in a backwards array.
If inputs are
[3,8,0,8], the function should return
[7,0,7,8] — because
624 + 8083 == 8707.
My style of coding is to break problems into steps and make it super-obvious to other people — and future-me — what the code is doing. I like to write code so clear that comments aren’t needed.
I’d start with a top-level function something like this:
let num1 = number(from: array1) let num2 = number(from: array2) let sum = num1 + num2 return array(from: sum)
That’s clear, right? There are two functions referenced in the above code that are clearly transformers — one goes from an array to a number, and the other goes from a number to an array.
So the next steps are to fill those in, along with any additional helper functions.
If I were on the other side of the table, and this is what the candidate did, I would be quite happy — because they’ve achieved not just correctness but clarity. They’ve solved the problem using a coding style that I’d want to see in production code.
But that’s not what these questions want to see at all.
What they want — at least in the experience I’ve had so far — is for you to have some kind of insight into the problem that allows you to solve it in a more efficient way.
You may have already figured it out for this particular question: but, just in case not, here’s the tip — the answer should mirror the way we actually do sums on paper.
Remember that we go right-to-left, and we build up the answer digit-by-digit.
624 + 8083 ------ 8707
The arrays are already backward, even! So just write a loop that does exactly what you do when doing this by hand (including the carry-the-one part). You create the answer — the
[7,0,7,8] — as you go along.
In production code, if a problem like this came up, I’d ask “How the hell did we get here?” and try to backtrack and figure out what insanity caused this, because it’s just not right.
But, if this code were truly needed, I’d write code the way I normally would, with clarity in mind first.
And then, if my tools told me it was too inefficient with time or space, I’d figure out a more efficient version.
These questions, then, are able to test what you might come up with when you’re in that position.
The thing is, what I would most want to know is how people write code for the 99% of time when they’re not in that position. That’s not being tested here.
Permalink - Posted on 2020-04-24 23:03
Proposal: instead of calling these video things we do “happy hour” or “virtual happy hour,” let’s just call them “video hour.”
Here’s why: it might be the morning for some people on the video; not everybody drinks alcohol; it’s a new thing, not a replacement for an old thing.
Permalink - Posted on 2020-04-24 22:55
Maurice Parker, NetNewsWire iOS lead developer, talks about accessibility in the latest episode of the AppleVis podcast.
I love this for a couple reasons: it’s about accessibility, which is a core value, and a member of the NetNewsWire team who isn’t me did the interview. It was Maurice’s first podcast interview, and he did a fantastic job. 🎸
Permalink - Posted on 2020-04-09 02:29
It was only few months ago I wrote up a NetNewsWire 2020 roadmap — and that roadmap is already wrong!
Not a surprise. That’s apps for ya.
But also: remember that NetNewsWire is made by a group of volunteers, and we all live in the same world everybody else lives in. I even lost my job, and I’m spending much of my time looking for a new job. (Had I not been laid-off, NetNewsWire 5.0.1 for iOS would probably have shipped already.)
Here’s what — we think — is coming up next. Probably in this order, even!
This release will include a bunch of bug fixes and improvements. We’re down to the last few bugs right now. I expect this to be on the App Store within a few weeks.
This release is also a bug-fixes-and-improvements release. No new features.
This release will add Feedly syncing, a Reader view, and other features that the iOS version already has. It might be sandboxed and might also be on the MAS. (Not totally sure yet.) Should include support for at least one other sync system.
Support for additional sync systems. Other things. (Hard to say at this distance.)
* * *
When will anything actually ship? There are No ETAs. And everything in the above schedule could change!
Permalink - Posted on 2020-04-08 02:47
We haven’t been going anywhere. We’re getting our groceries and things delivered.
I’m going for jogs — but only after 8 pm, once it’s dark, once the sidewalks are clear enough. My neighborhood has a surprising amount of walkers. Normally I love that, but right now I just wish it would rain like hell and keep them all home.
Instead it’s sunny, so I wait till nighttime when everyone’s inside on their various devices.
Today was the first time I went anywhere. It’s been more than a month since I was in a car or outside of my neighborhood — but I went to South Lake Union, to the Omni office, to go get my personal things.
I wore a mask that Sheila made me. Wore disposable gloves. Used a pen to touch the buttons in the elevator.
It was certainly weird to be in the office. Almost nobody there. The couple people who were there were nice, of course.
I packed everything I wanted to take home in a small box. A quilted wall hanging Sheila had made, a couple framed photos (Babe Ruth; Avalon, NJ), a laptop that I own, some mints.
I left behind the stuff of mine I didn’t want to bring home — bean bag chair, coat rack, small table, black fleece blanket.
A guitar, too. I left behind a guitar. Just a cheap acoustic guitar, but weirdly awesome for how little I paid for it. It’s free to the first person in the office to grab it.
I can’t remember when it was, now — last year, maybe — there was a meetup for The Automators podcast held in the Omni cafeteria. At some point that evening, James Dempsey sang a few songs while I accompanied him on that guitar.
Which means the guitar has some amount of public life lived, which is good for its soul. I did my part for it, and now it’s someone else’s turn.
Anyway… We took the box home. It’s in a closet, where it will sit quarantined for a week before I’ll touch it. Nothing urgent in there. Radioactive but uninteresting.
I changed all my clothes and washed my hands like crazy. Multiple times. Used the really hot water from that one faucet, which probably doesn’t matter.
I’m weirdly busy — the job hunt is more than a full-time job. I’m treating it that way, anyway. I’ll be anxious about this until I get a job, and I don’t need more anxiety right now.
I’m keeping an open mind and talking to as many potential employers as I can. I’m leaning strongly toward a full-time job (rather than contracting). I just had one; I’d like another one.
Permalink - Posted on 2020-04-01 19:01
I have never in my life had as many people getting in touch with me than over the past 24 hours. (I’m way behind on answering email and Twitter DMs.)
Many are messages of support, which I cherish and thank you for.
There are also people asking if I might be interested in working with them — which is awesome. I have an open mind, and all kinds of different work sound interesting to me.
I do have three requirements:
I could work in an office again (once that kind of thing is possible again). Or not. I could do coding. Or writing. Or marketing or managing or designing. I could work on iOS or Mac apps. I could do something cloud-related. Good work is interesting, and I’m not stuck on any one particular type of work.
But the things that make me me won’t change. I’m a Seattle guy who blogs and writes an open source RSS reader.
* * *
Have I said how lucky I am? I am amazingly lucky and fortunate. There are plenty of people to worry about during the current crisis. I’m not one of those people.
Permalink - Posted on 2020-03-31 16:59
Yesterday, along with about ten people (I’m not sure exactly), I was laid off from my job at the Omni Group, and now I’m looking for new work.
But first things first. You’re curious about Omni.
* * *
Omni’s been around for almost 30 years, and I hope it’s around for another 30. It’s one of the great Mac and iOS shops — they will sing songs about Omni, at maximum volume, in the great halls.
But businesses go up and down, and Omni’s had a bit of a down period. Normally that would be fine, but the current economic circumstances turn “a bit of a down period” into something more serious — and, in order to get things going the right way again, the company had to lay off some people. Including me.
This is, notably, the first time Omni has ever had to lay off people. And I bet that the company wouldn’t have had to this time, either — but, well, (gestures at everything) there’s all this.
It’s been my pleasure and my honor to be a part, for over five years, of the journey of this wonderful Mac and iOS app-making business.
While the company has some challenges ahead of it, I take heart in knowing it has been around for decades, and that it has calm, cool leadership and, especially, a great group of inspired people working hard to get back to the point where the company is thriving.
Omni deserves your love. I was just there, and I was just laid off — which is a hard thing — but I’m telling you it deserves your love.
* * *
Enough about Omni. Here’s me! I’m looking for work. I’m not sure if I prefer a full-time job or contracting positions — I think the actual work and the people matter more to me than how it’s structured.
If you read my blog, you know me: I ship great apps. I also design apps, write, host a podcast, do marketing, do community relations, project-manage things. Lots of skills right here. 🐣
If you might be interested and want to talk, email me at firstname.lastname@example.org.
* * *
The hard part of all this: normally everyone would get together for a happy hour for a few drops of encouragement and friendship. These rituals are nice, and they matter to me.
But — (gesturing again at this fallen world) — nope.
Permalink - Posted on 2020-03-27 19:45
That headline is a bit provocative, I realize, and it doesn’t capture all the truth. So I’ll elaborate.
Download numbers for NetNewsWire 5.0 for iOS just passed the total for NetNewsWire 5.0.3 for Mac: 37,618 for iOS, 36,774 for Mac.
Yes, the iOS app has more downloads, and it got there in only a few weeks. But if the iOS market were so much larger than the Mac market, you’d have expected this to happen within a day or two of launch day.
This particular app makes for an interesting experiment for a few reasons:
Based on the above, and knowing that way more people use iOS than macOS, you’d expect the iOS app to be way more popular. But it’s not. It’s a little more popular.
I find this super-fascinating, because it’s some data — admittedly just one app — that confirms what I’ve thought for a long time, which is that, for some types of apps, a Mac app would do as well as an iOS app.
And, given that Mac apps are less complex to write than iOS apps these days, a Mac app could be more profitable than an iOS app.
Permalink - Posted on 2020-03-23 03:39
On the NetNewsWire blog, I wrote about how it’s easier to build NetNewsWire now — and you don’t even need a paid developer account (just a free one).
Also: we welcome contributors! Of all skills levels and experience. Especially people new to app development. (NetNewsWire is my vehicle for giving back to the community, and one of the best to do that is to help new people get settled into the app development world.)
Permalink - Posted on 2020-03-20 19:27
I’ve seen a few people on Twitter asking if there’s a Slack group they could join for the purpose of socializing.
So I’ve set up a #socializing channel on the NetNewsWire Slack group. You’re welcome to join — you don’t have to be a NetNewsWire user.
Everybody on the Slack group is super-nice, and there is a Code of Conduct.
I’m not going to try to convince you to use NetNewsWire. 🐯 I’m just using the tool I have to help in this very small way.
Permalink - Posted on 2020-03-19 22:28
Our lead iOS developer Maurice Parker found an app called Proxyman that looks pretty cool.
It’s like Wireshark or Charles, except that it‘s a Mac-assed Mac app (which I love).
Update a little later: a few people have asked me, “What’s a Mac-assed Mac app?”
Answer: it’s a phrase I stole from my friend Collin Donnell to describe Mac apps that are unapologetically Mac apps. They’re platform-specific and they’re not trying to wow us with all their custom not-Mac-like UI (which often isn’t very accessible).
I consider NetNewsWire to be a Mac-assed Mac app, and it’s a point of pride.
Slack, on the other hand, is most definitely not — though it’s not only Electron apps that miss the mark of Mac-assed-Mac-app-ness.
Permalink - Posted on 2020-03-16 06:02
Monday is the 14th day my wife and I will spend in self-isolation. We’re getting groceries once a week, but anything optional is just not done, and everything else is optional.
We skipped the small dinner party we were invited to on the weekend. We haven’t been to a bar or a restaurant or even to a coffee shop. I’m not getting my hair cut tomorrow.
Even with this, we’re still washing our hands frequently and not touching our faces — because the mail still comes and a couple packages have come. Because groceries are things from outside our bubble.
We’re lucky — I know we’re lucky. Incredibly lucky. But, if you’re similarly lucky, I beg you to do this too.
Yes, the economic cost is terrible. But that doesn’t compare to the loss in lives that’s coming.
Maybe you really, really want to go do this one thing, and everyone’s careful, and you’re young and healthy and don’t know any old people. Don’t do it. Don’t.
This may go on for months. Fine. Staying home to save lives is really not hard.
Permalink - Posted on 2020-03-15 01:32
Common wisdom is, I think, that the iOS market is so much larger than the Mac market that an equivalent iOS app would be downloaded five or ten times more than a Mac app.
I kind of accept that wisdom, but I also kind of think the iOS App Store has so many more apps that it may erase that difference. An equivalent iOS app might get downloaded about the same number of times, or even fewer times.
Well, anyone with a Mac and an iOS app has some idea. Like me.
Here’s our situation. NetNewsWire for Mac is direct-download-only, and NetNewsWire for iOS is on the App Store. Both are completely free and have no IAP. NetNewsWire for iOS is, worldwide, a 4.9-star app. Both apps have been reviewed well by places like MacStories.
Downloads for NetNewsWire 5.0.3 for Mac, released Oct. 22, 2019: 32,223
Downloads for NetNewsWire 5.0 for iOS, released March 9, 2020: 26,089
My guess is that the iOS app will surpass the Mac app in downloads in about a week.
But there’s an important thing to remember: the majority of downloads happen in the first couple of days after release.
So I don’t expect the iOS app to be ten or even five times more popular than the Mac app. Maybe twice as popular? It’s early enough that it’s hard to say.
One thing that could really change these numbers is, of course, being featured by Apple. Another thing that could possibly change things, but in the other direction, is if we release the Mac app on the Mac App Store.
Every app is unique, and the situation will be different for each app. But just thinking that the iOS market is so much larger, and your app will do better by that proportion, is probably naive. It’s not nearly as straightforward as that.