Last week, Paul Hudson wrote a far more actionable and, well, kind version of my prior post on the… undesirable state of Apple’s documentation. Paul’s post — Reimagining Apple’s Documentation — was largely framed around a WWDC wish list.
I’m taking this chance to make my own WWDC wish list. There are many others like it, but this one is mine. And really, it’s only two items.
1. Improved & Prioritized Documentation
Given my constant kvetching about this, it should be of no surprise that the #1 thing I want from Apple is improved documentation. I can’t stress enough: reading Paul’s post is a great way to see what kinds of actual changes Apple can make to improve finding and using their documentation.
But, as Paul said in his post, it’s more than just that. Apple doesn’t seem to prioritize documentation in the way I think they should. I could quote the entirety of Paul’s post, but this in particular stuck out to me:
No APIs mentioned in the WWDC Platforms State of the Union talk should be allowed to ship with “No Overview Available” as their documentation – either can the feature or prioritize documentation.
For my money, I think this should be true of all new APIs, but that’s not a particularly realistic expectation. Nevertheless, anything that makes it into the Platforms State of the Union — the keynote that’s aimed squarely at developers — should absolutely have fully cooked documentation to go with it.
I also loved this idea of Paul’s:
The top 100 most popular APIs should get either a screenshot or a video showing exactly how it works and what the result is.
Hear hear.
2. More Breadth to Combine
Easily my favorite new API to come out of Apple from the last couple years is Combine. It is a first-party take on ReactiveX, in many ways aping RxSwift in the best possible way.
Combine was introduced at WWDC 2019, and I immediately fell in love. Though it makes many choices I’m not sure I would, I immediately dove in, writing a series of blog posts comparing and constrasting it with RxSwift.
Combine also powers a lot of both Peek‑a‑View and a new thing I’m working on.
Politics Killed the Framework Star?
Last year, at WWDC 2020, I was super amped to see the improvements made to
Combine. I was hoping more than anything else to see a Combine knock-off of
RxCocoa. This hypothetical set of extensions would allow developers
to use Publisher
s that are exposed by UIKit controls such as UIButton
,
UITextField
, etc.
Unfortunately, that never landed. In fact, there were almost no updates to Combine in 2020. I was, and remain, really saddened by this.
I can only speculate why Combine didn’t get any more love in 2020, but if I were a betting man, I’d guess it’s politics. SwiftUI is the new hotness. Though some of SwiftUI is powered by Combine, a lot of what makes SwiftUI great could be similarly accomplished using Combine and “CombineCocoa”.
If I were the captain of the good ship SwiftUI, I would not be keen to see “CombineCocoa” off the port bow. If there’s an alternative to SwiftUI that leverages all of UIKit, but with some new affordances for faster and easier development, that’s a threat. I’d fire all my cannons, as quickly as possible.
To do so would be immature, and it would be against Apple’s best wishes. The best thing Apple can do is provide as many options for developers as they can. Why should I have to throw away my years-deep knowledge of UIKit just to use SwiftUI?
One of the things that made Swift great — from day one — was its ability to coexist [mostly] gracefully with Objective-C. The same is true of SwiftUI: UIKit and SwiftUI can [mostly] coexist without too many compromises. As a developer, this affords me the ability to use the right/best tool for the job: SwiftUI for things that are less interactive; UIKit for when I have intense/complex interactions, or need to have deeper control.
My money is on Combine being neutered — if not straight-up scuttled — by an over-zealous SwiftUI champion, politicking within Apple. I surely hope that isn’t the case, because a rising tide raises all boats. Giving developers like me the option to use whichever tool is the best fit for the job makes for better apps, and a better user experience on Apple devices. Dare I say, Tim, that it also increases customer sat?
What Does Combine Need?
To my eyes, there’s a couple of ways that Combine could be improved. In summary, it could mostly be summarized as “more breadth”.
Combine could most directly be improved by increasing its coverage of
legacy APIs. When Combine originally shipped, it included some very
simple but very useful bindings for URLSession
—
dataTaskPublisher(for:)
— and for NotificationCenter
— publisher(for:object:)
. I’d love to see many other Apple
APIs get this kind of basic Publisher
coverage:
That’s just a few that jump to mind off-hand. There are so many others that could stand to get the same treatment.
Another thing that I really wish Combine had — and which existed in some
early betas! — is a way to programmatically create a Publisher
with a
closure. Effectively, I’d love to have the equivalent of Future.init(:)
that worked for Publisher
s that signal more than once.
I know that this is one of the many neat things that is added in CombineExt,
but it’d be great if I could create Publisher
s via closure without having to
bring in an entire open-source project.
(See also some other neat CombineExt tricks like materialize
.)
Why Bother with Combine?
A keen-eyed observer may note that there are several technologies that are very close to landing in Swift that may obviate Combine entirely. Some obvious examples:
Both of these tools do a great job covering some of the Combine surface area. However, they leave out most of what makes Combine so great.
The Components of Combine
To my eyes, Combine is a combination of a few different things:
- A consistent way to do multi-threaded/asynchronous programming
- …including a consistent and understandable way of hopping between threads
- A consistent way to deal with asynchronous messaging:
- Callbacks
NotificationCenter
- Key-value observation
- A consistent way to manipulate streams of data
To most, the first two items — asynchronicity and messaging — may
seem like the stars of the show. Au contraire, mon ami. For my money, manipulation
is the real winner. Take a gander at the sidebar on RxMarbles.
There are so many classes of things one can do with an Observable
/Publisher
:
- Conditionals
- Combinations
- Filtering
- Mathematics
- Transformation
- Timing
Note that each of those groups above has many operations that can be performed.
It’s quite a bit more than just .map()
, .compactMap()
, and .flatMap()
.
It’s this incredibly broad and deep collection of operations that allows the canonical example of how powerful Combine/functional reactive programming can be:
let searchTerms: AnyPublisher<String, Never> = /* ... */
let searchRequestPublisher = searchTerms
// Don't send values until they've been
// static for at least 0.3 seconds
.debounce(for: .seconds(0.3), scheduler: RunLoop.main)
// Don't send values until they're
// more than 3 characters
.filter { query in query.characters.count > 3 }
With this combination of a timing operator (.debounce(for:scheduler:)
) and
a filtering operator (.filter(_:)
) makes it possible, with two lines of code,
to ensure that searchRequestPublisher
only gets new values when the text the
user input has been still for 0.3 seconds, and is more than 3 characters.
Actors and async
/await
Aren’t Enough
All of these things are absolutely possible with Actors, and with async
/await
,
but there is so much more code required.
async
/await
— or, at least, my understanding of it today — makes
asynchronous programming easier to both write and reason about. Asynchronous
code written using async
/await
looks, at a glance, almost identical to
the synchronous code we are all used to.
Actors build on the vast improvements of async
/await
and, via convention
as well as compiler rules, prevent race conditions, and generally
ensures improved safety, particularly around threading and data access.
All of these protections and affordances are important. In some cases, even
as a devout Combine fan, I can absolutely see myself turning to async
/await
or Actors in order to accomplish a task. Here again, that’s what makes all
these technologies so great: they permit me to use the best tool for the
task at hand.
So, Apple, my wish is for you to re-discover Combine, refine it, and build it out. However, reading the tea leaves, right now I’ll happily settle for you not sending Combine out to pasture. 🥺
One More Thing
The iPad hardware is ridiculously powerful. Please, please, can we have some software improvements to match? Some things that I prefer to do on my Macs that I can’t do nearly as effectively on my iPad:
- Managing more than two concurrent applications
- Any sort of proper software development
- Culling and ingestion of photos, particularly from my big camera
- Podcasting
Granted, not all of these things I necessarily want to do on my iPad, but it really chaps my behind that I can’t. Or if I can, without having so many gotchas and caveats that it makes it a waste of time.
You’ve given yourselves such powerful hardware. Let’s combine forces and make use of it, together.
Last week I also had the distinct honor of joining my pals Stephen Hackett and David Sparks on their phenomenal Mac Power Users podcast.
On this episode, we discussed my current gear landscape, what I’m expecting to upgrade soon, and how my iPad and iPhone fit into my working life. We also chatted about my Synology, my absolutely 🍌 photo management workflow, iOS development, Raspberry Pis, and David’s new project.
Mac Power Users is such a mainstay in our community; it amazes me I’ve been asked to be on it once, much less four times. (!) Hopefully I can convince David and Stephen I have at least a few more in me. 😏
Last week, I joined Max Roberts on his semi-new Max Frequency podcast. Max and I actually met through the Relay Members’ Discord, where he immediately endeared himself to me by sharing a couple of photos of Disney World. 🤩
On this episode, we discuss my recent foray into aerial photography, Formula 1 and Drive to Survive, video games (!), and three years (!!) of me being independent. Then, the tables are turned, and I pepper Max with some questions about what it’s like to work in The Happiest Place on Earth.
I really enjoyed talking with Max, and it was neat to discuss some stuff that I don’t typically have the occasion to chat about on my other shows.
This week I was happy to join Brianna Wu, Mikah Sargent, and Dan Moren on Clockwise. On this episode, we discussed Apple’s new iPad Pro, AirTags, Apple TV, and how we customize our hardware.
Clockwise is such a fun — if stressful! — show to do. Being used to being able to let my thoughts air out, being concise doesn’t exactly come naturally. 🙃 If you’ve ignored all my previous pleas to give the show a shot, now is a great time to dive in.
Last month was Black History Month. My friend Ish Shabazz took it upon himself to celebrate and document it by taking to Twitter and tweeting a link or short thread every single day.
It seemed wrong to me to have these tweet threads all disappear into the ether. They were all valuable, and I’m ashamed to admit, I learned a lot by reading them. Ish put together a Twitter Moment if you’d like to read them sequentially.
To keep these locked within a Twitter Moment seemed wrong, though. Further,
although Ish has weaved a bit of a through-line between them, his daily threads
can all stand on their own. Jumping around is both easy and fun. You can see
all of them linked chronologically below, with titles added by me. However,
if you’d like to jump around randomly, just smash that bell press the
button below.
I encourage you to take a look at any and all of them — you’ll surely learn something. I sure did.
Furthermore, since I have your attention, I absolutely must point you to Ish’s recent talk, Programming with Purpose. It’s a 25-minute talk that ostensibly is about writing code, but really is a series of tips about living life. It has big Last Lecture energy, which is about as big a compliment as I can pay a lecture.
Ish took time out of his day — every day, for a month — to help educate people like me, and maybe you, about what it’s like to be Black, particularly in America. The least we can do to pay him back is to listen.
In 2017, shortly after the unthinkable happened, a different unthinkable happened. As a former resident of Charlottesville, it confounded me the place of love and inclusion that I love could be turned into a symbol for hatred and racism. It seems one can import evil into anywhere.
Shortly after those horrific events, and the completely senseless death of Heather Heyer, a concert was staged to raise money for local charities, and to try to fill Charlottesville with love again. This concert was called A Concert for Charlottesville.
It featured a slew of artists, all of which put on phenomenal performances. For my money, I think The Roots and Justin Timberlake were the highlights, but I enjoyed almost every artist that participated.
The concert was simulcast/livestreamed to several websites, and thanks to
the magic of youtube-dl
, I was able to capture it in its entirety.
Though I doubt that I am in posession of the only copy of this concert, I have yet to stumble across another copy of the entire concert in the wild.
I brought this up on a recent ATP as one of my most prized media posesssions.
I noted that I had been considering putting it on archive.org
, but I feared
the legal repercussions. I don’t want to put it on YouTube, because I’m not
looking to make money off of my recording; I simply want to share what I think was
a very special concert that was borne of a very shitty time.
In thinking about this some more, I’ve decided to upload my file to archive.org
.
I haven’t a clue if it’ll last more than a week, especially after I call attention
to it with this blog post. But this concert is too good to be sitting only on
my Plex.
Last I tried it, streaming it off archive.org
didn’t work too well, so I’d strongly
recommend completing the ~13GB (!) download, and playing it locally. Assuming they
weren’t stripped, I also added chapter marks for each artist’s set, and in some cases,
each song in each artist’s set.
Regardless, if you want to give it a shot, you can find
A Concert for Charlottesville on archive.org
.
If you’re interested in donating as a thanks to these artists, the official website is still up, and accepting donations.
Apple’s new Fitness+ service is a very interesting offering.
I’m someone who has never been particularly athletic, and often fairly un-fit, I wasn’t sure what to make of Fitness+. I am probably stronger than I’ve ever been, thanks to a couple of years of far more consistent exercise, but I wouldn’t say I’m terribly fit. I like food too darn much. 😋
Regardless, it’s been interesting comparing and contrasting Fitness+ to the exercise programs I typically follow on Beachbody on Demand[1]. I joined Chaim Cohen on a bonus episode of inThirty to discuss exactly that.
As the name implies, the episodes are always about 30 minutes, though given my normal loquaciousness, I pushed Chaim to just a hair over. 😇 Regardless, if you’d like to hear what I do and don’t like about Fitness+, this is the ticket.
Beachbody is a MLM, and as with all MLMs, rather gross. However, their exercise videos I find to be very good. Beachbody on Demand is their Netflix-like offering, a great way to consume Beachbody exercise videos without getting roped into the pyramid scheme. ↩
My love for Plex is not a secret. I’ve written about Plex many times before
on this site. The same goes for my love of ffmpeg
. As
wonderful a tool as ffmpeg
is, occasionally it lets me down.
I recently ripped a two-part concert BluRay for easier playback on
Plex. I did so using MakeMKV, as always. This had the advantage of
preserving the chapter markers in each disc; I could easily go in using Subler
and rename all the canned names, say Chapter 20
, to useful names, such as
While My Guitar Gently Weeps
.
My problem came in when I wanted to merge the two files. Using my normal ffmpeg
incantation did not automatically carry the chapters across to the merged file.
# filelist.txt:
# file clapton1.mp4
# file clapton2.mp4
ffmpeg -f concat -i filelist.txt -c copy clapton-joined.mp4
What I wanted was for both sets of chapters to be preserved, with the chapters in the second file automatically offsetting themselves by the duration of the first file. Thus, if the first file is two hours, chapter one of the second file would start at two hours, not at 0 minutes.
Perhaps there’s a ffmpeg
incantation I can use to accomplish what I wanted,
but if so, I couldn’t find it.
So, I turned to something I’ve been using more and more lately: Python.
I should state up front I’m a terrible Python developer, and am probably breaking every known coding convention and/or best practice. However, on the off chance someone is looking for a script to do exactly the above, I’ve written one.
This script is run like this, for example:
python3 ./mergechapters.py clapton1.mp4 clapton2.mp4 Clapton-Full.mp4
Assuming you have two files with the chapters marked and named as you wish, the script does the following:
- Uses
ffprobe
to get the duration of the first file (clapton1.mp4
in our example) - Gets all the metadata — including chapter information — from the first file
- Reads the second file’s chapter list using
ffprobe
(clapton2.mp4
in our example) - Offsets each of the second file’s chapter’s timestamps by the duration found in step #1
- Appends this new chapter information to the metadata found in step #2
- Writes this combined metadata, and a file list, to disk
- Uses
ffmpeg
to merge the two files, and install chapters using the combined metadata found in step #5 - Cleans up after itself
Again, there are surely easier ways to do this, but this seems to have worked with my example files. I’ll be trying it again on other ripped concert films shortly.
You can find the script as a gist on Github.
Though I’m not actively requesting feedback/pointers/tips on how to write better Python, if you’re bored, please feel free to fork that gist and improve it as you see fit. (I’d prefer a fork rather than comments on that gist, if you don’t mind, please.)
However, if you happen to know of an incantation I can use directly with ffmpeg
to make this all happen in one step, I’m all ears.
The first thing you see in the first episode of Letterkenny is this:

Needless to say, it’s an odd way to start a show. Particularly a comedy.
I like a lot of shows that start off rough. The first season of Parks and Rec is awful. If memory serves, the first season of 30 Rock wasn’t exactly stellar. Letterkenny, however, had me rolling from the very first scene I saw.
Letterkenny is an extremely smart, extremely well-written, completely silly comedy that I can’t say enough good things about. However, I joined Lisa Schmeiser, Philip Michaels, Jason Snell, and Don Schaffner on The Incomparable in trying to sing the praises of this wonderful, goofy show.
If you haven’t seen the show, I give it my strongest recommendation. It’s weird, so it may not be your cup of tea, but it’s delightful. If you want a sampler, you may enjoy this [not aurally work safe] examination of “dad noises”.
I will never turn down an excuse to discuss one of my favorite places on Earth, Walt Disney World. It’s also especially delightful to guest on a show where you have an unusually great rapport with the hosts. Starport75 ticks both of these boxes.
In honor of last week’s exciting events, I joined Chris and Glenn on this week’s episode to discuss all things Hall of Presidents. As interesting as that discussion was, be sure to stick around for a fun, if impromptu, rapid-fire round at the end. We discuss two of my favorite counter-service eateries in the Magic Kingdom.