Or try one of the following: 詹姆斯.com, adult swim, Afterdawn, Ajaxian, Andy Budd, Ask a Ninja, AtomEnabled.org, BBC News, BBC Arabic, BBC China, BBC Russia, Brent Simmons, Channel Frederator, CNN, Digg, Diggnation, Flickr, Google News, Google Video, Harvard Law, Hebrew Language, InfoWorld, iTunes, Japanese Language, Korean Language, mir.aculo.us, Movie Trailers, Newspond, Nick Bradbury, OK/Cancel, OS News, Phil Ringnalda, Photoshop Videocast, reddit, Romanian Language, Russian Language, Ryan Parman, Traditional Chinese Language, Technorati, Tim Bray, TUAW, TVgasm, UNEASYsilence, Web 2.0 Show, Windows Vista Blog, XKCD, Yahoo! News, You Tube, Zeldman
1942 Letter to My Grandfather from His Father 4 Jan 2022, 5:02 pm
My Dad ran across a remarkable letter and shared it with family. I volunteered to share it more widely, and Dad and his siblings agreed.
* * *
This 1942 letter was written by John Simmons to his son Donald as Donald was about to be shipped to Europe (England first). He had enlisted after the Pearl Harbor attack.
Donald Simmons was my grandfather, and I had the fortune of knowing him.
My father and his siblings knew John Simmons, their grandfather, and with this letter they are able to know him a bit better.
John was 63 when he wrote this letter to his youngest son.
* * *
You can read all three pages together as a PDF — or you can click a thumbnail for each page for the full size version.
* * *
Below is the text. The original is written in cursive on Wright Aeronautical Corporation letterhead.
Aug. 20, 1942
My thoughts are with you tonight so strongly that I shall drop you a note. Of course it is hard for you to go but not much harder than for us to see you go. You see we love you and are now so helpless to aid you in any way. But then you are a man now and will have to make your own way from here on. And we’re sure that you have the stuff to do it. It won’t be an easy job but then, Don, no job worth the doing is. There are bound to be dark days and darker nights but you must always remember that nothing lasts forever and in the morning it will be a new day. And you are better trained than most for the work.
You are too intelligent to be told and believe that war is anything but a tragic mess, however, we are in this not with our consent but because of a treacherous attack that we did not invite. Regardless of the causes the effect is that we simply must win. And we shall win in spite of our petty bickerings among ourselves for in the final analysis we are the greatest nation on earth. We know that this country has reached the highest degree of living for its people ever attained by any nation in the history of the world and I believe enough in God to feel that with His help all the good, the right, and the fine things must survive. Of course we feel that possibly you may not come back — there is always that possibility and, too, we may be gone when you do come back but in the very grim business of war bombs and bullets go where they are sent and we must for our own peace of mind look that fact in face. Naturally the law of averages is greatly on the side of your returning to us and please God that will be our happiest day in a life time. I am confident you will receive very good training in whatever field you are placed and that you will be adequately prepared to protect yourself.
You are entering what will probably be the greatest adventure in your life. You are going to see miserable, filthy, low, mean and degrading sights for men are like that but you will also see fine, good, self sacrificing and even heroic things for men are like that also. That you will fall into the later class we who love you and have every confidence in you have not the slightest doubt. You have the background and the spirit and will to do so. Just keep yourself so you can look yourself in the face and not be ashamed of what you see. You will come through all right.
And now, old Son, I’ll close by wishing you again the best of everything there is in any old world and all the luck that there is. I truly wish I were going with you — it is hard too to stand and wait.
Sgt. Donald Simmons did, of course, make it back.
In Case I Don’t Write Here Again 5 Oct 2021, 5:03 am
This blog is almost 22 years old, and in all that time I’ve been solid about posting regularly — until this recent dry spell.
I skipped the summer. Last post was in June. There was just one that month, and just one in May.
I have an explanation: while my health and physical circumstances are unchanged and, happily, fine, I have not felt the drive to write here that I always felt.
I never, in all these years, had to push myself. I’d get an idea and I would be compelled to write it up and publish it. It was always that simple.
But I haven’t felt that way in many months, and I’m not sure I will again.
Maybe this is temporary, and there will be hundreds more posts to come.
But I kind of think not, because there’s a bigger issue: I expect and hope that eventually I will no longer be a public person — no blog, no Twitter, no public online presence at all.
I have no plan. I’m feeling my way to that destination, which is years off, surely, and I just hope to manage it gracefully. (I don’t know of any role models with this.)
Anyway. In case I don’t write here again — in case these are the last words of this blog — thank you. I loved writing here, and you are why.
NetNewsWire 6 for iOS Waiting for Review 21 Jun 2021, 3:25 am
NetNewsWire 6 for iOS — which includes new features iCloud syncing, home screen widgets, and a bunch more — was submitted to App Store review today. The team is super-psyched to get this release shipping!
What It Was Like to Sell Apps Online in 2003 7 May 2021, 11:16 pm
Apple likes to claim that the App Store replaced the system of selling software in physical boxes in stores and over the mail.
But it’s not true.
My experience selling apps before the App Store was not unique or new — it’s only interesting now because people may have forgotten this history, and younger people may never have heard it.
Here’s my story:
We shipped NetNewsWire 1.0, an RSS reader for Mac OS X, in 2003 and sold it over the web for $39.95. There were no boxes and no printed manuals — there was nothing physical at all. This situation was, at that time, completely unremarkable: it was expected.
We used a service called Kagi for the storefront and credit card processing. Kagi had been around since the ’90s, and it was well-known and trusted by the Mac community. Setting up our account and store was pretty simple — simple enough that I forget most of the details.
Part of how it worked was that we had to upload a text file to Kagi’s system, a file with license keys their system would give out one by one. I wrote a script to generate that file, and I wrote code in the app to validate a license key. I also had to create a small window where a user would enter their license code.
This meant there was some work, yes, but it was nothing compared to what you have to do to sell on the App Store.
(It could have been more convenient for purchasers, yes, and by 2005 we had plans to create an in-app purchasing system that still used Kagi, but we ended up benching that plan, for reasons.)
And — importantly — Kagi’s fee was something like 5%. (Update a little later: evidence suggests I may be misremembering: it could have been more like 10%.)
Even more importantly: Kagi didn’t review our app. I suppose, if we had been selling something egregious in some way, they might have learned of it and cut us off. But that’s a standard business relationship. Kagi was not a gatekeeper.
Kagi didn’t promote our app, either. It wasn’t their job — it was ours. They provided a storefront and trusted credit card handling, and that was all we wanted, and it was great.
By 2005 this Mac app was making well into five figures per month. Still at Kagi.
Here’s the thing: none of this was new. We were following well-worn paths. When I worked at UserLand Software, before writing NetNewsWire, we sold Frontier on the web. Lots of other companies — and not just indies — had been selling apps on the web since the ’90s, and paying more like 5% than 30%, and not having to go through any gatekeepers.
And it was pretty easy. Easier than dealing with the App Store!
Next time you hear someone from Apple forget this history, please remember that they’re forgetting on purpose.
PS Yes, you may argue the App Store’s superiority in certain respects and be correct. Don’t bother. My point is that the App Store was not replacing the system of apps-in-boxes-in-stores. It was replacing the system I talk about above.
Running with Colors 28 Apr 2021, 1:23 am
The new CDC guidance says that unvaccinated people can “Walk, run or bike outdoors with members of your household” without a mask — which I take to mean that I, a currently half-vaxed person, can go for a jog by myself and leave the mask at home.
I actually have been jogging without a mask this whole pandemic — but only after dark, when the streets are nearly empty, when I have plenty of room to maneuver around the dog minders.
And so my next run — scheduled for tomorrow — will be a daylight run. I’m so looking forward to it! Getting back to normal one delight at a time. 🎸
PS Lots of rabbits in our neighborhood these days. (Technically: smolbrownbois.) Rabbits are really dumb in one specific way: when they see me running, they run away from me — by running in the exact same direction I’m running! They could go 90° — and they eventually do — but they take a while to figure it out, and they don’t seem to remember between runs.
The lack of a price tag seems almost criminal 8 Apr 2021, 3:07 am
It was really cool to see NetNewsWire in British GQ’s list of top iOS apps — and awesome to be in the company of Fantastical (cousin Michael’s app), Widgetsmith, Linea Sketch, Halide, Overcast, and other great apps.
One part stood out to me, though: the blurb ends with “The lack of a price tag seems almost criminal” — and this isn’t the first time price has been brought up. Often the sentiment is something like “I’m surprised how good this app is, given that it’s free.”
I should explain: the app is better — much better — than it would be if it were a for-pay app. If it were a for-pay app, it would be just me working on it instead of this great team of volunteers. There probably wouldn’t be an iOS version at all: it would be Mac-only. The kind of features I don’t enjoy doing, such as the Twitter and Reddit integration (and others), wouldn’t even exist.
And it would be slow going. NetNewsWire 5 would have shipped much later than it did, and NetNewsWire 6 would not have shipped until next year, probably.
Instead, because it’s open source, we have this amazing team of people willing to work on it in their spare time. During a pandemic and everything. They’re bringing you something great out of love, with the goal of writing an app of the highest quality.
We don’t have to rush and Ship Right Now in order to make our revenue numbers. We don’t have to pick feature X over feature Y because we think it will bring in more conversions. We can care about performance and efficiency; we can say no to things that might have made money but that are outside our vision.
There’s nothing wrong with commercial software — NetNewsWire was commercial software for many, many years — but it’s also a great freedom to us that it’s not. And it allows us to make something much greater than I would have made all on my own.
(Why all on my own? Because, these days, a Mac and iOS RSS reader is not going to bring in enough revenue to pay for a team greater than one. And even paying one full salary plus health insurance would have been a hell of a longshot.)
PS Here’s how to support NetNewsWire. (Don’t send money!)
I’m Still Living with the Longterm Effects of a Disease that Now Has a Vaccine 7 Apr 2021, 2:36 am
I was in third grade when I got a severe case of chicken pox. This was in the days before there was a vaccine for it. When I returned to school, I found I couldn’t read the blackboard anymore, and I had to get glasses.
My eyesight kept getting worse in the years since, and it’s the worst that I know of among friends and family. With my contacts out, I can’t see my feet. I could trip over anything on the floor because I can’t actually see that far away.
I wear contacts instead of glasses because of the lack of peripheral vision. (These days I wear contacts and reading glasses when I’m at my computer or reading.)
I wish there had been a vaccine! I would have gladly had the better life with only normally impaired vision instead of extremely impaired.
Let me tell you how it was almost worse, though
We lived at the bottom of a quarter-mile-long hill. When it snowed, everyone parked their cars at the top of the hill, next to the highway.
I don’t remember the blizzard of ’78 at all because that’s when I had chicken pox. I had it so bad that it wasn’t just on my outside: it was inside. Eventually I couldn’t keep anything down — even the smallest sip of water would make me throw up.
But could I go to the hospital? My parents would have had to bundle me up, put me on a sled, and pull me up that hill, in the cold wind, with the road covered in deep snow, to get me to the car, to get me to the hospital. Would I have survived the trip? Unknown. I was weak and severely dehydrated. (I’ve never been as sick since. Not nearly.)
Long story short — we didn’t have to attempt the trip once I was able to hold down some cherry Jello. 🐣
But was this all better than getting a vaccine would have been? I could have died, and I’m still living with the effects. In a heartbeat I’d swap that experience for having had the vaccine.
When it’s your turn — it’ll be mine in a couple weeks — get the coronavirus vaccine! Don’t let this thing kill you or fuck you up forever.
The Perfect To-Do System Is Not Just Around the Corner 7 Apr 2021, 12:17 am
Sometimes it’s as if you can almost sense its nearness — that perfect to-do system, built on OmniFocus, Things, Asana, Todoist, or one of the many others. It’ll take just a little more thinking and tweaking. Rationalize your tagging system, change how you think about due dates, maybe write some scripts or create more templates, and you’ll have it.
And it will be so glorious! And you’ll never have to deal with this again. (You swear you’re not one of those people who futz with their to-do thing just because it’s fun.)
Here’s what you need to know: it’s a mirage.
I know, I know. How can that be, when it feels so damn near? We’re just talking about lists and a few ways — time and tags, for instance — of slicing them up. I know perfection is right here. Give me another day! I’ve so got this!
But it’s not around the corner. It’s really not. There’s no perfect system for anybody. All of these apps are pretty good, and you may find one fits you better than another, but you’re not ever going to make it the perfect system for you. Even if you started from scratch and wrote your own, you’re not getting the perfect system.
There’s no getting out of this fact: these apps are all going to take more constant input from you than you’d wish for. They don’t take away the need for some amount of self-discipline to use them effectively.
I’m so very, very sorry.
NetNewsWire 6.0 for Mac 27 Mar 2021, 11:01 pm
NetNewsWire 6.0 for Mac has shipped! See the blog post for a download link and list of changes.
The team of volunteers has done wonderful work — during multiple crises, including a pandemic — and produced one of the great NetNewsWire releases. Their reward is hearing that you love the app. 🐣🐥🎸
Next up for the team: we’re working on NetNewsWire 6 for iOS. The good news is that most of the code is shared between Mac and iOS — for instance, the iCloud syncing code is the same, and we’ve got this ship-worthy. But we still need to test and fix bugs. We’ll make an announcement on the NetNewsWire blog once we’re doing TestFlight builds.
NetNewsWire 6.0b4 Is a Final Candidate Release 24 Mar 2021, 3:46 am
We believe we’re very close to shipping NetNewsWire 6.0 for Mac. Get the latest beta here!
After we ship the Mac version, we’ll get to work on the iOS version. No ETAs on TestFlight builds, but that will be our next step.
Benefits of NetNewsWire’s Threading Model 22 Mar 2021, 1:39 am
In my previous post I describe how NetNewsWire handles threading, and I touch on some of the benefits — but I want to be more explicit about them.
It Reduces Bugs and Makes the App Stable
I would not be surprised, were it possible to know, that the largest trigger for bugs and crashing bugs (among iOS and Mac apps) is threading issues of various kinds.
Our threading model eliminates this category.
We have zero known threading bugs or crashes in production, and they’re extremely rare in development. When they appear in development they’re due to a mistake in following the model. (Something like forgetting to dispatch to the main queue when calling a callback, for instance.) They’re quickly and easily fixed.
It Makes the App Faster and More Efficient
The app does not suffer from thread explosion, which is bad for performance. Ever stop your app in the debugger and notice that it has dozens of threads? Or maybe notice that in a crash log? Not an issue for NetNewsWire.
But, more importantly, we don’t hide performance and efficiency issues by moving work to a background queue. Instead, we use the profiler in Instruments to figure out what’s slow, and then we fix it. Only after doing that would we consider moving work to a background queue.
It Saves Developer Time
Things NetNewsWire developers don’t have to do:
- Diagnose and fix threading bugs and crashes — which are often excruciating-to-impossible to reproduce and difficult to fix
- Think about how to do background work (there’s a simple model for how to do it)
- Figure out how to protect shared mutable state
This gives us more time — which is super-valuable in an open-source, all-volunteer project — to do work that improves the lives of the people who use the app.
It also improves the experience of our developers, who can concentrate on the feature they’re working on instead of on how the feature can live safely in a multithreaded universe.
Best of all: nobody is spending time tracking down a maddening threading bug that never happens on their machine, and then implementing a speculative fix — only to find later that it’s not the fix but now, actually, there’s a new crashing bug, which might have been triggered by that “fix”… and so on, forever.
Developer morale is important!
How NetNewsWire Handles Threading 20 Mar 2021, 10:08 pm
NetNewsWire is mostly not multi-threaded. Here’s what we do:
Run Most Code on the Main Thread
The default place for all of our code — UI code and otherwise — is the main thread. Synchronous code running on the main thread is the easiest code to reason about and is the least likely to trigger weird, intermittent bugs.
In other words: we try, as much as possible, to not use queues or threading at all. I can’t emphasize this enough: the best way to handle concurrency is just to not do it.
Communicate Between Components on the Main Thread
Every notification and every callback happens on the main thread.
Though a given object (or small system) may use a serial queue internally, it never, ever lets that fact leak out beyond its own boundaries.
Use Serial Queues for Pure Functions That Take Time
Parsing an RSS feed is a pure function. Since it does take some time (though less than you think), it can be done on a serial queue, in the background. The result is returned via callback on the main thread (see above).
These kinds of things are great for background processing because they’re perfectly isolatable — parsing a feed affects no state. It doesn’t do any notifications. All it does is callback, once done, with an object.
There are other examples (decoding JSON or image data) that are great for this, and which we handle the same way.
Use Serial Queues for Database Access
Our model objects are plain ol’ classes and structs. The APIs for reading and writing are main-thread-only, and they call back asynchronously but always on the main thread with a result.
Inside the database code, though, is a serial queue. It’s completely internal to that code, though, and utterly invisible to the rest of the app.
When the database code sends any notifications, it does so on the main thread. It doesn’t otherwise affect any app state.
Use Asserts and Preconditions
If you do a search on
Thread.isMainThread, you’ll find a bunch of these…
…and one example of
XCTAssertTrue(Thread.isMainThread). We don’t otherwise test if we’re in the main thread.
(I would not mind having a whole lot more of
precondition(Thread.isMainThread) than we do now.)
You may be skeptical about this model, but I’ll remind you that NetNewsWire is responsive and freakishly fast. It’s also — by far — the most stable and bug-free app I’ve ever worked on in my long career. And a big part of that is our threading model.
One of the nice things about this model is that a developer knows, just by looking at where they are, what thread the code is running on. It’s almost always main. But if you’re working on the RSS parser or similar, you know that it literally doesn’t matter, and if you’re working on the database or similar, it’s obvious that you’re using a serial queue.
As we adopt Combine, SwiftUI, and future Swift language changes to support concurrency, we will continue to use this model. The details of our implementation may change, but the model will remain the same: use the main thread everywhere except in a few cases, and make sure that those cases cannot leak knowledge or behavior of their queues outside themselves.
Some developers I’ve known seem to think that being good at concurrency makes them badass. Others seem to think that senior developers must be great at concurrency, and so they should be too.
But what senior developers are good at is eliminating concurrency as much as possible by developing a simple, easy, consistent model to follow for the app and its components.
And this is because concurrency is too difficult for humans to understand and maintain. Maybe you can create a system that makes extensive use of it, and have it be correct for one day. But think of your team! Even if you’re a solo developer, you and you-plus-six-months makes you a team.
I know you’re worried about blocking the main thread. But consider this: it’s way easier to fix a main-thread-blocker than it is to fix a weird, intermittent bug or crash due to threading.
With a main-thread-blocker, use the Time Profiler in Instruments and figure out what’s going on. It may be that something does need to move to a background serial queue — or, more likely, there’s a data structure or algorithm that could be improved, or you find your app is doing unnecessary work (or both).
How To Get There From Where You Are
I suspect most apps don’t have a clear model for concurrency. If yours does, then great! Skip the rest of this post. :)
If you want to move to the model I’ve described above, I’d start with a few things.
- Identify code that should be main-thread-only and use
assert(Thread.isMainThread)in that code
- Turn on Xcode’s Main Thread Checker
- Make sure that every Notification that your app posts is posted on the main thread. In notification handlers, add an
If you don’t use Notifications, apply that advice to KVO or whatever else you might be using.
The reason I pick Notifications and KVO is because they’re a kind of spooky action at a distance — when you’re working on the Notification handling side, you don’t know what thread the Notification was posted on, and developers tend to assume it’s the main thread. This is a common place for threadedness to leak, which causes bugs and crashes. (Accidental multithreading is the scourge of our platform.)
The next step is probably to look at the places where you’re using queues and find out which of those could be moved to the main thread. And, when you can’t move to main, build better walls: make APIs that call back on the main thread, and don’t let the fact of those queues leak out from behind the API. Don’t let threadedness leak.
You can get there incrementally.
In the meantime you may find that you’re having to dispatch to the main queue rather often as a defensive measure.
Ideally, you’re only dispatching to the main queue when it’s part of this threading model (when posting a notification from a background queue or calling a callback) and never as a defensive measure.
However, it may be a while before your app gets there, and that’s okay. (Tip: do consider the case where sometimes that call to
DispatchQueue.main.async is happening in code already running on the main thread. The async part is real, and you may not always want that: sometimes you may want code to run immediately when already on the main thread. And sometimes not.)
In the end: change all your
precondition(Thread.isMainThread) — make your app crash in production when the threading model is violated. Being able to do that — knowing that it won’t ever trip — is a great place to be. 🐣🐥🎉
New in NetNewsWire 6: iCloud Syncing 18 Mar 2021, 1:22 am
We’ve added — in addition to support for a bunch of online RSS systems — iCloud syncing in NetNewsWire 6. (Latest beta 6.0b3 is recommended at this writing.)
This is great for people who use only NetNewsWire for reading feeds — it means you don’t need an additional service or login aside from iCloud itself, which you’re almost certainly already using.
Here are some things to know…
It Reads Feeds Directly
An iCloud sync account is like an On My Mac account in that it reads feeds directly from the sources instead of going through a separate RSS system such as Feedbin or Feedly.
Many people prefer this, for privacy reasons — it means their feeds list isn’t stored on some RSS syncing system.
But some people prefer — also for privacy reasons — not to read feeds directly: they like having a system that goes directly to the sources. This way the sources don’t see their requests.
Consider your own preference when choosing to use iCloud sync or not.
Another issue with this model: you could miss articles in fast-moving feeds. (This is true for both iCloud and On My Mac accounts.)
Imagine a feed that updates a hundred times a day. Say you take a Monday off and don’t launch NetNewsWire on any device. By Tuesday, when you launch NetNewsWire, some of the articles from Monday have already fallen off the feed. You’ll never see those articles.
If you used an online system you would not miss those, because online systems never take a day off.
It Works with NetNewsWire Only
Other RSS readers include iCloud syncing. But you can’t sync those apps with NetNewsWire via iCloud — Apple’s CloudKit doesn’t allow for that. Each app gets its own storage, and other apps can’t see that storage. (Which makes sense.)
If you want to use NetNewsWire on one machine and another app on another, you’ll need to choose an RSS syncing system that both apps support. (Which shouldn’t be difficult.)
NetNewsWire Supports Multiple Accounts
This is not new in NetNewsWire 6, but it’s worth pointing out: you can have an iCloud sync account and (for instance) a Feedbin account. NetNewsWire is designed for this, just as Mail is designed for multiple email accounts.
Being able to choose which feeds are synced where is powerful.
But do note that you can have only one iCloud account in NetNewsWire. (Because of how iCloud works.)
It May Be Slow at First
NetNewsWire itself is very fast. But syncing happens at the speed of iCloud — and iCloud sometimes throttles the app: it tells NetNewsWire to back off and try again later.
This can be especially noticeable when you’re just starting off with iCloud in NetNewsWire, or whenever you add a big number of feeds, because there will be a ton of data to upload.
So: be patient right at first and whenever you’re adding a bunch of feeds to your iCloud account.
We’re Shipping the Mac App Before the iOS App
This feature will come to NetNewsWire for iOS too, of course. Our plan is to ship NetNewsWire 6 for Mac first, and then start TestFlight builds for the iOS app, and then ship NetNewsWire 6 for iOS.
The good news: the iCloud sync code is shared between the two apps, which means it’s already getting a thorough test.
You might ask why we’re not shipping Mac and iOS at the same time. Our wonderful, remarkable team of volunteers — working during their spare time during a pandemic and multiple other crises — could have handled shipping both at the same time. But splitting it up this way is my call — it’s easier for me personally to manage, and I ask for your understanding. Thanks!
The Hottest of All Xcode Tips 16 Mar 2021, 7:41 pm
It’s come to my attention that not everyone knows this. But everyone needs to know this.
In Xcode’s Organizer, in the Crashes section, you can right-click or ctrl-click on any row and choose Show in Finder. This will reveal a .crashpoint file — do a Show Package Contents and then dig in further. You will find .crash files with the full crash logs, which provide a lot more info than what you see in Organizer.
(Bonus tip: I just drag the .crashpoint package onto BBEdit, which shows the contents in its sidebar, where I can click on different .crash files. I assume other text editors have a similar feature.)
New in NetNewsWire 6: Syncing Via BazQux, Inoreader, NewsBlur, The Old Reader, and FreshRSS 14 Mar 2021, 10:11 pm
NetNewsWire 6 — currently in beta (Mac for now) — adds support for a bunch of RSS sync systems: BazQux, Inoreader, NewsBlur, The Old Reader, and FreshRSS. (NetNewsWire already supported Feedly and Feedbin: this makes the list a lot longer.)
If you’ve held off on checking out NetNewsWire because you use one of the above, well, you don’t have to wait any more. Unless you want to wait for it to get out of beta, which is completely sensible. 🐈
Not new in NetNewsWire 6, but worth remembering: NetNewsWire supports multiple accounts. You can have Feedly and Inoreader and FreshRSS, for instance. You can even have multiple of each. (You’d need multiple logins, of course.)
(We’re getting closer to our goal of supporting as many as possible of the existing systems. While I can’t guarantee we’ll get to every single one, we’re certainly trying.)
How to add an account
Open Preferences. Click the Accounts icon. Click the + button near the bottom left of the window. You’ll see a list of accounts as in this screenshot. Pick the one you want to add, and then follow the prompts for authentication and setup.
If you find an OPML file somewhere and just want to check it out without adding it to your current feeds, you can use NetNewsWire’s support for multiple accounts. (This is not new in NetNewsWire 6: this works in NetNewsWire 5 too.)
Create a new On My Mac account (you can have more than one) just for this OPML file, and then choose that account when importing the OPML file. That way you can see what’s in it, and you can copy any feeds you like into another account. When you’re done, you can just delete the temporary account (or make it inactive).
Example: if you’re an iOS developer, you might want to import some of the OPML files at Dave Verwer’s iOS Dev Directory.
New in NetNewsWire 6: Twitter Search Feeds 14 Mar 2021, 6:23 am
New in NetNewsWire 6 — currently in beta (Mac version, for now) — is the ability to add Twitter searches and read them as if they were any other kind of feed.
Open Preferences > Extensions and add your Twitter account. Close the Preferences window.
Then, from the menubar, choose File > New Twitter Feed. In the window that appears, make the type Search, provide a search string, choose an account and folder, then click Add.
(Note: these work with iCloud and On My Mac accounts only. Some of the other syncing services support special Twitter feeds too, but you have to set them up via their website.)
What’s cool about this: you can watch for mentions of whatever you want, and those come to you in the same app where your other feeds live.
I have a search feed set up for mentions of NetNewsWire, as you’d imagine. Here’s a screenshot of what it looks like in my copy of NetNewsWire. 🐣
NetNewsWire 6.0a6 for Mac 7 Mar 2021, 12:36 am
We’ve been doing a bunch of work on the crash log catcher — we even set up a Linode instance for just this one thing. (It’s the first server I’ve set up in 20 years!)
The thing is, we’re pretty much not getting crash logs for 6.0 test builds. (Less than half-a-dozen for 6.0a4, none at all for 6.0a5.) Which makes me suspicious of our system for catching and reporting crashes.
But every test I can come up with shows that the system is working just fine. So either nobody’s running these, or the app is just that stable.
Anyway — here’s the latest.
Aunt Jen’s and Uncle Pete’s Live Online Show 6 Mar 2021, 9:21 pm
My aunt Jen and uncle Pete have adapted their show “Live From New York! He’s A Prom Date!” for live-streaming (yes, a live performance over the web). Tickets are just $10 or $15 for a couple.
Sheila and I saw a rehearsal and loved it.
The show is a true story: 16-year-old Jen didn’t have a prom date, and her mom got her on an episode of the Sally Jessy Raphael show. And… well, you should see it. You’ll laugh. You might cry a little. And you just might love it as much as we did. 🎉
Mysterious SwiftUI Crash 24 Feb 2021, 8:21 pm
A friend just released a new version of their app — and it has a new crash not seen during development.
This new version of the app has a bunch of new SwiftUI code, and this crash is definitely in SwiftUI — but it’s apparently in SwiftUI itself.
Anyone else seen this? Is there something my friend can do to work around this?
Hardware Model: iPhone13,4 Code Type: ARM-64 OS Version: iPhone OS 14.4 (18D52) Exception Type: SIGTRAP Exception Codes: TRAP_BRKPT at 0x19d447a18 Crashed Thread: 0 Thread 0 Crashed: 0 libswiftCore.dylib 0x000000019d447a18 _assertionFailure(_:_:file:line:flags:) + 492 1 SwiftUI 0x00000001a011c52c ViewCache.commitPlacedChildren(from:to:) + 3032 2 SwiftUI 0x000000019ffc8eb4 specialized IncrementalChildPlacements.updateValue() + 1384 3 SwiftUI 0x00000001a00e6fbc partial apply for specialized + 24 4 AttributeGraph 0x00000001c2cbba50 AG::Graph::UpdateStack::update() + 492 5 AttributeGraph 0x00000001c2cbbe84 AG::Graph::update_attribute(AG::data::ptr<AG::Node>, bool) + 332 6 AttributeGraph 0x00000001c2cc5088 AG::Subgraph::update(unsigned int) + 884 7 SwiftUI 0x00000001a07d1cdc GraphHost.runTransaction() + 172 8 SwiftUI 0x00000001a07d4e1c GraphHost.runTransaction(_:) + 92 9 SwiftUI 0x00000001a07d37a8 GraphHost.flushTransactions() + 176 10 SwiftUI 0x00000001a07d4db4 closure #1 in closure #1 in GraphHost.asyncTransaction<A>(_:mutation:style:) + 24 11 SwiftUI 0x00000001a02a3168 partial apply for closure #1 in ViewGraphDelegate.updateGraph<A>(body:) + 28 12 SwiftUI 0x00000001a0726e9c closure #1 in ViewRendererHost.updateViewGraph<A>(body:) + 108 13 SwiftUI 0x00000001a071de7c ViewRendererHost.updateViewGraph<A>(body:) + 92 14 SwiftUI 0x00000001a029f6d0 ViewGraphDelegate.updateGraph<A>(body:) + 80 15 SwiftUI 0x00000001a07d4d84 closure #1 in GraphHost.init(data:) + 124 16 SwiftUI 0x00000001a07d66f0 closure #1 in GraphHost.asyncTransaction<A>(_:mutation:style:)partial apply + 40 17 SwiftUI 0x00000001a02c2b98 thunk for @escaping @callee_guaranteed () -> () + 28 18 SwiftUI 0x000000019ff91a10 static NSRunLoop.flushObservers() + 148 19 SwiftUI 0x000000019ff91974 closure #1 in closure #1 in static NSRunLoop.addObserver(_:) + 16 20 SwiftUI 0x000000019ff8c4b4 specialized thunk for @callee_guaranteed () -> (@error @owned Error) + 24 21 libswiftObjectiveC.dylib 0x00000001bfcc3f30 autoreleasepool<A>(invoking:) + 64 22 SwiftUI 0x000000019ff91954 closure #1 in static NSRunLoop.addObserver(_:) + 64 23 SwiftUI 0x000000019ff91aac @objc closure #1 in static NSRunLoop.addObserver(_:) + 56 24 CoreFoundation 0x0000000199798358 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 36 25 CoreFoundation 0x00000001997925c4 __CFRunLoopDoObservers + 576 26 CoreFoundation 0x0000000199792b74 __CFRunLoopRun + 1056 27 CoreFoundation 0x000000019979221c CFRunLoopRunSpecific + 600 28 GraphicsServices 0x00000001b135e784 GSEventRunModal + 164 29 UIKitCore 0x000000019c1d2ee8 -[UIApplication _run] + 1072 30 UIKitCore 0x000000019c1d875c UIApplicationMain + 168 31 AppName 0x0000000100ed8444 main (main.m:19) 32 libdyld.dylib 0x00000001994526b0 start + 4
Slow House 23 Feb 2021, 2:27 am
“Slow House,” song #3, is more exercise than song. Where most songs have a structure like ABAB or ABACAB or whatever, this one is just A. Not even AAA — just A.
I wanted to get some practice recording an acoustic guitar — it’s the first GarageBand thing I’ve done where one of the parts was from a mic (a Yeti Blue). (All the previous parts are via MIDI and Apogee Jam.)
I was thinking about “Starman” by David Bowie, and how the verse starts with iim and I chords, which is a little unusual. In “Starman” it’s Gm and F — but I have a great deal of antipathy toward Gm (it can go straight to hell, if you ask me), so I used Am and G as the chords here.
And that’s all I did. No chorus, no other changes. Still, though, I like how it weaves two melodies together, and I like how it all falls apart at the end.
I also like how some of the string-like GarageBand instruments bring kind of a vocal sound — I can almost hear words in there. (But there are none.)
It’s called “Slow House” because it’s slow (as an antidote to the very fast Vampire’s Run) and because the piano is doing kind of a House thing.
Here’s the GarageBand file: SlowHouse.band.zip
It’s not until I make song #1,000 that I’ll make something people actually want to listen to more than once! I get that. I’m sharing anyway, because I think maybe it’s interesting to watch someone learn. 🐣
Vampire’s Run 17 Feb 2021, 6:45 am
My second song, “Vampire’s Run,” just finished last night, is thorny — chromatic, key-modulating, choppy-guitared — where Tie & Suit is aggressively smooth and diatonic.
I have many notes…
I started off wanting to do something early-REM-like. The idea — kind of a bullshit idea, but I liked it anyway — was this fast arpeggio on the top three strings of the guitar: B-E-D-B E-D-B-E C-E-Eflat-C E-D-B-E A-E-C#-A E-C#-A-E A-E-B-A E-B-A-E.
REM wouldn’t have done the C-E-Eflat-C part — but the rest would have fit in. Especially that move at the end from an A chord to Asus2.
To fit some chords to this, I came up with E7, a weird C major and minor, Bmsus4, A, Asus2. The idea is that you’d play the chords as normal but with the high E string always open. (A Buck-like move.)
It’s not actually pleasing-sounding — but, again, I liked it and figured I could make something out of it.
The problem: I made the song 170 beats per minute, and I can’t actually play that lick on guitar that fast. (My personal speed limit is pretty slow, actually.) I could play it on keyboard, though, so I did. It’s the very first thing you hear in the song. I cranked up the reverb on the Steinway Grand Piano instrument, and I thought it sounded great. Like a demented Charlie Brown song.
Next up was to add percussion and a bass line. The percussion is pretty much the same thing as in Tie & Suit. The bass followed the chord roots — E C B A — except that, at the end, I threw in this little blues-scale-ish lick: A-Bflat-B-Bflat-A-G-E-G-E. Which was also total bullshit, but it totally worked, so I kept it. The interaction of that with the piano arpeggio thing just sounded really cool to me.
At this point, though, this didn’t sound like REM in the least tiny bit. It sounded like an ’80s goth instrumental. So I leaned into it — added guitar instruments Eighties Goth and Starlit Cavern. Added a bunch of echo-y things like Stutter Stack and Swirling Flutters.
Last summer I started writing a blog post called “The Vampire’s Run in the Anarchist Jurisdiction” which was about going for a run at night (instead of the day, due to the pandemic) in my neighborhood in Seattle. I wasn’t able to figure out how to write it without sounding like some very fortunate guy who had to adapt in some small way and was pretty fine about it. Wouldn’t add anything to the world, so I didn’t finish it.
But, since that title was just lying around being all goth-y, I snagged the first part of it for this song. And, well, it kinda fits — the song is very fast, and during the chorus it almost sounds like flying.
The song structure here is simple: verse chorus verse chorus verse chorus. But there’s a key change: the verse is in E and the chorus is in C.
This is one of my favorite modulations. The chorus gets a kind of floaty and slightly sinister sound — hence the flying.
The first time I remember encountering this was in Beltane Walk by T. Rex. I remember asking my uncle, a musician, to explain the sorcery behind that song — and when, some years later, I heard that same sorcery in Hungry Like the Wolf I recognized it right away.
(I am, by the way, a glam rock guy at my core. Give me T. Rex and Bowie and all their descendants.)
Anyway — if you hear a little Duran Duran in the C-G-F chorus, you’re not wrong.
The chords aren’t actually C-G-F, though — they’re C-G6-Fmaj7 in order to make the E note prominent all throughout the song. The melody of the chorus is just arpeggios on the piano: E-C-G, E-B-G, E-C-A, with the E repeated. While the piano in the verse is tightly horizontal, the chorus is a bit more vertical.
(There’s a little fun part right as we lead into the chorus — it sounds like that split-second when a vampire leaves ground and is a little shakily airborne. You settle in in a moment. In the first chorus we have B-F#; in the second it’s D-A-B-F#, which makes it sound like Pachelbel’s Canon; and in the third it’s A-E-B-F# — a little “Hey Joe” action.)
String and other instruments are doing a C-B-A thing during the chorus — which is cool because, as you’ll remember, that also appears in the verse. Those exact same notes, which sound somewhat discordant in the verse, sound sweet in the chorus.
The song comes alive in the last third while it’s repeating the chorus before ending. I was recording the piano part for that last chorus, and I was feeling a little down that this entire song was just all these arpeggios. I started improvising — and I worked up some bits to add some more melody and passion to the song. Went all pentatonic on this shit — plus the minor third for some bluesiness. Banging on the E flat a bit.
This part comes as a surprise in the song, I think, and it was just as much a surprise to me, but it’s my favorite part. Part of me thinks you shouldn’t add so much new stuff at the end, but then I remember how “Every Little Thing She Does Is Magic” has this alternate melody at the end. And I think of how The Adversary by Crime and The City Solution — a favorite of mine from a favorite soundtrack — invents a whole new song, with a different time signature even, around the 3-minute mark.
“Vampire’s Run” ends on a simple repeated phrase which I thought sounded romantic: E D-C D C-A. Vampires love that romantic stuff.
And it ends ends on a lone E note from the piano — which sounds like a question.
If you want, you can download the GarageBand file: VampiresRun.band.zip
In case they’re interesting or fun to mess with — here’s a zip with GarageBand files for various “Tie & Suit” mixes.
Computer Music 8 Feb 2021, 12:26 am
When I was a teenager I spent hundreds of hours making music on my computer. This isn’t a surprising thing to hear someone say — except that this was almost 40 years ago, and the computer wasn’t a Mac but an Apple ][ Plus.
I had an ALF II music synthesizer card with some car speakers hooked up to it. The ALF software and hardware supported nine voices, and you entered music into a staff using the computer paddles. (This page has a screenshot showing what that was like.)
Though it was a very slow way to work, and limited to beeps and bloops with varying properties, it was a miracle to me as a teenager. I took so much pleasure in layering the instruments to get something way beyond what I could perform on guitar or piano.
I stopped before turning 17. I don’t remember exactly why. Probably just spending more time writing words than music.
Tie & Suit
No recordings of the dozen or so songs I made exist. I remember fragments of some of them, and it’s fine that they don’t exist.
The one exception is the very last song I made, called “Tie & Suit.” I remember all of it: the chords, melody, arrangement, everything.
I had not, until “Tie & Suit,” written something with a traditional verse-chorus structure. Everything before that was more adventurous — to put it kindly — with odd chord progressions, lots of key changes, and weird structures. (I was convinced of my musical genius.)
I was frustrated that none of the songs I’d written were particularly memorable or even likable. I couldn’t play them on piano for someone else to enjoy. So I decided to write something simple and traditional.
I had a hobbling case of I-hate-top-forty when I was a teenager, and so I had to be sarcastic about this song: I called it “Tie & Suit” because I thought I was selling out creatively. (OMG the narcissism of me as a 16-year-old!)
Here’s the thing, though: writing a very simple melody with just five chords (I, IIm, IV, V, VIm) and a traditional structure worked! I don’t remember any of the earlier songs, but I remember this one perfectly well.
There’s a lesson in there, though I was too young to learn it.
I’ve been promising myself for years that I would allow myself to start making music again — only this time on a Mac with GarageBand. And so I did, just a few weeks ago. (This is my first time using GarageBand other than recording a couple tracks for James Dempsey last summer.)
I started where I left off, with “Tie & Suit.” Here’s a version where I tried to recreate what it sounded like on my Apple ][ Plus 37 years ago. It still ends up sounding more sophisticated than the original — it’s hard to make GarageBand sound like an early ’80s ALF II. I did my best (short of getting obsessed with it).
Also: it sucks. I know the song sucks. It doesn’t matter! I was 16 when I wrote it, and now I’m learning GarageBand and having fun.
The next thing I did was to try and make a modern version that’s still true to the original. “Tie & Suit 2021” also sucks — but it was so much fun to make!
I am now completely addicted to GarageBand. The pleasure of layering instruments together, picking their parts, and letting it play is every bit as intense as it was when I was 16 on that primitive gear. It’s hard to say that about most things! But this is just fucking wonderful.
URLSession’s Delegate Queue Should Be the Main Queue 27 Jan 2021, 9:01 pm
Consider this line of code from RSWeb, a framework NetNewsWire uses:
urlSession = URLSession(configuration: sessionConfiguration, delegate: self, delegateQueue: OperationQueue.main)
delegateQueue parameter specifies the queue for all delegate callbacks from URLSession — and here I’ve just gone and made it the main queue.
Some of you are screaming. I can hear you: “Brent, Brent, Brent — you’re going to block the main thread! Don’t do it!”
Let’s Back Up and Review the Documentation
The documentation says of the
delegateQueue parameter that it’s…
An operation queue for scheduling the delegate calls and completion handlers. The queue should be a serial queue, in order to ensure the correct ordering of callbacks.
(Pause right here and go check your own code to make sure you’re actually using serial queues. There’s a good chance you’re not.)
Given that the delegate callbacks should be on a serial queue, those callbacks should return as quickly as possible so that they don’t block subsequent calls.
You may have some expensive things to do — NetNewsWire, for instance, does RSS parsing and database updating. Those things should be done on separate serial queues — they should not be done on the delegate callback thread, because you don’t want to block it.
In other words, this all means that your delegate methods should be so fast that they wouldn’t block the main thread.
So Just Use the Main Thread
Your code that uses URLSession almost certainly has some data structures that do things like map a task identifier to some model object of some kind. You could go to lengths to make sure this is all thread-safe — and hope and pray nobody messes it up six months from now — or you could take the easy, safe route and go main-thread-only with that code.
That’s what we do with NetNewsWire. (And did with Vesper and other apps.)
It’s important to know I’m a performance junkie and a stability junkie — and with this practice I satisfy both.
iOS Jobs at Audible 26 Jan 2021, 11:42 pm
We could be co-workers! I love working at Audible, and I think you would too. Plus — no small thing — you’d be working on an app that means so much to millions of people.
Scroll down on this page to find a list of jobs (plus a link to a longer list). On the iOS team we have several engineering positions, a QA engineering position, and a software developer manager position.
New Domain for NetNewsWire 18 Jan 2021, 4:55 am
I was not foresighted enough, back in the early 2000s, to register the netnewswire.com domain — and so, of course, somebody else bought it with the hopes of selling it to me.
(These days the first thing you do is register the domain name. But that wasn’t as much a thing back then.)
I’ve been long resigned to never own that domain name. But then, just a few days before Christmas, I got an email from Ben Ubois, founder and developer of Feedbin, saying that he’d bought it for me. We transferred the domain pretty quickly after.
And then, this weekend, I got the new site set up — and now you can find NetNewsWire where it always should have been: netnewswire.com.
Thanks to Ben!
Early Test Build: NetNewsWire 6.0d4 for Mac 17 Jan 2021, 10:10 pm
The app is made of cat’s claws and fury. I try my damnedest to warn you off:
You should probably skip it. It’s not a beta. It’s not even an alpha. It’s a d release — where d, which used to stand for development, now stands for dangerous.
…let us satisfy your curiosity in advance: it’s just an app. It’s got some more features. You know the story, and you don’t need to run this if you’re just curious.
With that out of the way: the team has been doing great work! This is the first build with a Big Sur UI and app icon. It’s universal (Intel and Apple Silicon). It adds syncing via iCloud, Inoreader, NewsBlur, and others. It has special support for Twitter and Reddit feeds. Etc.
But I really do mean it when I say it’s early and dangerous. I’d like your help testing, but I need you to know it could be quite rocky and we may not have time to help you. You could lose data. You’d be doing a favor for the team which we have no way to repay — other than by eventually releasing the best app we can.
I’ve been haunted since hearing, in the early days of the pandemic, that if we all wore masks for six weeks this thing would be over.
I was there. I’ve done that for six weeks, and another six weeks, and another. And now it’s worse than ever. It’s a challenge not to be angry.
There are healthy, uninfected people right now, today, who are excited for the vaccine and who will die before they get it.
Bogus Code Signing Crash 5 Jan 2021, 8:02 am
For a few people, NetNewsWire for Mac crashes on startup — and the crash log erroneously blames an invalid code signature.
If it were truly invalid, the app wouldn’t launch for anybody. But it launches fine for almost everybody.
This started with macOS 10.15.4 and continues in macOS 11. We’ve posted more details on our issue tracker at GitHub.
Does anybody have any ideas for how to work around this?
When Are We Back To Normal? 31 Dec 2020, 9:01 pm
I’m trying to figure out when we’ll be back to something like normal. I’m thinking of a few things:
- Biden’s goal is 100 million doses in his first 100 days
- The vaccines require two doses
- There are 330 million people in the United States
- Dr. Fauci has said that we’ll achieve herd immunity once we have 70-85% of the population vaccinated
Let’s say we achieve herd immunity, and some kind or normalcy, at around 250 million people vaccinated, which is just over 75%.
Since each person requires two doses, that’s 500 million doses.
The pace Biden is aiming for is 1 million doses per day. If we’re able to achieve and sustain that pace starting late in January — which is not at all a sure thing — we’ll have administered 500 million doses around mid-year 2022.
I keep hearing people talk optimistically about April or May of 2021. Me, I’m hoping to be able to see my family for Thanksgiving and Christmas 2021, but I’m not counting on it as a sure thing.
We’re going to need to go much faster with the vaccines. To reach 75% of the population by, say, Labor Day, in time for kids to go back to school, we’re going to need to administer 500 million doses by mid-August, about seven months after Biden’s inauguration. (Remember that immunity is ramped up about two weeks after the second dose.) To make this happen we’ll need to administer 2-2.5 million doses per day.
I don’t see how we get there by counting on local drug stores to administer most of the doses. I suspect we’re going to need to use parks and high school football fields — big, open spaces where large crowds can line up safely. And we’re going to need to do it day after day, with no days off.
(Is any of my math wrong? There’s no point in being overly-precise here — but please tell me if I’ve made some error that changes things significantly.)
I remember Rudolph the Red-Nosed Reindeer as my favorite of the Christmas specials — as the truly great of the genre, as the Superman of Christmas specials.
And then we watched it last night, for probably the first time this century. It was brutal.
The story was pretty rough to start with: mutant child laborer is shunned until the CEO finds a way to exploit his difference, and only then do his peers accept him. And we’re supposed to take that as a happy ending.
But there’s something far, far worse that I had utterly forgotten.
Here’s the scene: the Abominable Snow Monster is threatening Rudolph’s family. Yukon Cornelius causes a boulder to fall on the monster’s head and he passes out. And then — here’s where Sheila and I both gasped — Hermey, the elf who, charmingly, wants to be a dentist, pulls out all of the snow monster’s teeth. The teeth are shown, pulled out by the roots, littered on the snow.
Hermey is a fucking war criminal. We’re shook.