By Casey Liss
Retina Monitors

As of a few months ago, I’m living an all-Retina life. I replaced my crummy Lenovo monitors I was using at work with a 4K screen. That gave me enough resolution to run pixel-doubled, which means my monitor at work is Retina.

Having a Retina-capable external monitor is magnificent. As an iOS developer, being able to run iOS Simulator on my external monitor, at full resolution, is completely freeing. For MacBook and MacBook Pro users, I can’t recommend an external Retina-caliber monitor enough.


As I write this, there are three general options, that will work with most modern Macs:

  • Budget: LG 24UD58-B 24" 4K Monitor — ~$300
    This is what I use at work. The stand is completely static; you cannot adjust it in any way other than tilt. The case is a shiny black, and there is a single LG logo on the front. The actual LCD is great.

  • Intermediate: Dell P2415Q 24" 4K Monitor — ~$370
    The Dell is a slightly upmarket choice. It has better color reproduction and the stand is far more adjustable. The base is visually much louder, but otherwise it’s fairly nondescript.

  • I Hate Money: Dell UP2715K 27" 5K Monitor — ~$1500
    If you want something larger than 24", you’ll need to jump to 5K. Which, in turn, means you’re basically buying a 5K iMac but throwing away the computer part. This is super spendy, but it’s surely a hell of a monitor.

Once you buy one of the above, you must plug in via DisplayPort. If you use HDMI, you’ll run at 30 fps, which is awful. You can get a cable for Thunderbolt → DisplayPort from Amazon, or a USB-C → DisplayPort from Amazon.

Additionally, there are two options for those with a fancypants MacBook with USB-C:

  • I Dislike Money: LG UltraFine 4K Display — ~$700
    This is the semi-official 4K offering from Apple. It connects via USB-C and also functions as a bit of a docking station. I’ve heard very mixed reviews of the LG monitors, but in principle, they sounds great. However, if your budget can allow for it, I don’t know why you’d get the 4K and not the 5K.

  • I Really Dislike Money: LG UltraFine 5K Display — ~$1300
    This is the semi-official 5K offering from Apple. All the benefits and drawbacks of the 4K display is applicable here as well.

You can see a bit more about all of your options—for any generation of MacBook or MacBook Pro—on the official Apple Support article.


So how did I land on these three? A combination of a little trial-and-error, in concert with some information from someone I respect.

The trial-and-error happened at work. We had a couple of screens that were larger than 24" and only 4K. It was quickly obvious that the ostensible point of Retina—not being able to see pixels—did not work at those sizes. Once you cross over 24", you absolutely must go to 5K.

In summary, if you want external Retina, follow these rules:

  • No more than 24" at 4K
  • For anything more than 24", go 5K
  • Use DisplayPort or USB-C

Peer Review

On a similar vision quest, Marc Edwards starting doing math, computing the pixel density of several popular displays. He eventually landed on this super-useful image which I’ve stolen appropriated from Marc’s awesome post:

Display chart

If you look at Marc’s chart, you can see what the issue is. Displays over 24" that are only 4K land in “the bad zone”—more resolution than non-Retina, yet not enough to be full Retina.

Go Spend Money

If you’re using an external monitor hooked to an Apple laptop, I can’t encourage you enough to get yourself a Retina-capable one. You can spend as little as $300 or as much as $1500; choose the price that best fits your budget. Once you stop seeing individual pixels, you start to see how beautiful macOS really is.

As with all posts on my site, I've used affiliate links where possible. As with all posts on my site, these thoughts are 100% mine and are not paid for.

National Infertility Awareness Week

In the United States, this week is National Infertility Awareness Week. Infertility is an important issue to me, as my family has struggled with it. According to national statistics, one out of every eight couples struggles with infertility. Anecdotally, most never speak about it.

It took Erin and me four years to conceive Declan, and we were only able to do so thanks to the help of science and medicine. Declan simply would not be here otherwise.

During this awareness week, a few things have come to my attention that are worth linking.

A friend of a friend has launched Fruitful, a fertility mentorship program. Infertility is nigh impossible to truly understand without having lived through it. Even those that have struggled with infertility may have had wildly different experiences: natural conception after simply tracking the woman’s cycle, conception only via intrauterine insemination, conception only with in-vitro fertilization, or perhaps conception only with the aid of donor sperm, a donor egg, or a surrogate.

Fruitful is—using their own words—“committed to making infertility suck less”. I would have adored having a group like this available to me when we were struggling. Though new, I have high hopes for Fruitful; it’s a real boon for those struggling.

A few personal stories have also been shared with me this week. All of them are truly and uniquely beautiful, and captured in their own way.

E. Christopher Clark’s story of going through IVF way back in 2005 is a great read:

Our first wedding anniversary came and went, then our second and our third. There was always an excuse for why it hadn’t happened yet. Your mother was working a job she didn’t like, or I was working a job I didn’t like. Or else it was the both of us at the same time. There were two years when she went back to school for her Master’s degree, and two years where I went back for mine, and those years overlapped to boot. And when we went back to school we continued to work as well. So there was the issue of time. But in the back of our minds there lingered the horrible question: What was wrong?

Brian Moritz’s tale of conceiving is also a wonderful read about his IVF baby:

Funny thing, that pronoun I just used. When my wife and I talk about our experiences, we always use the plural. “We” went through IVF. Because that’s what families do. They go through things together. That’s what being a family means, what being a team means.

But let’s be very clear — “we” didn’t do anything.

My day-to-day life, my body, underwent at worst a minor inconvenience. I had to reschedule a few things at work and school, get up earlier than normal, make a couple of awkward trips to the doctor.

My wife did everything.


My wife was warned of all the potential problems. She was given an explanation.

Nevertheless, she persisted.

Finally, if video is more your thing, this ~3 minute pregnancy announcement by Reid Wolcott made me cry in the middle of my work day:

Infertility is an extraordinarily difficult struggle, that can make you feel uniquely alone. Let this week remind all of you—those who aren’t parents, those who are, and those who wish so desperately to be—of one thing:

You are not alone.

My Favorite Prankster

I got news this morning that my friend Jason Seifer had passed away suddenly.

I’m shocked, and really sad.

Jason was like nobody I’ve ever met. Which is an odd thing to say, since we had never actually met in person. Nevertheless, I immediately started to cry upon hearing the news; an odd thing to do for someone who, on paper, was just a voice in my head.

Jason Seifer was one of two co-hosts of one of my favorite podcasts of all time, IRL Talk. He, along with Faith Korpi, were a duo that had both immeasurable and also indescribable chemistry. They were in so many ways the same, and yet in so many ways so very different, I enjoyed every moment of IRLTalk, and its predecessor, Geek Friday.

Let me take a paws to describe a small part of why Jason is so important to me.

Long before I was a podcast host, I was a podcast listener. I got into podcast consumption by wanting to hear what an old childhood friend of mine was doing. Once Build and Analyze started, that led me to Hypercritical, The Talk Show, and most things that Dan Benjamin touched. Including Geek Friday.

Suddenly, there was a podcast with a woman co-hosting. I immediately asked Erin to listen, as I figured she’d be more likely to enjoy a show that was only obliquely about the nerdy stuff I like, but instead about nerding out over anything.

We listened to every episode together, and when Dan left, we weren’t sure what to make of his replacement. This “Jason Seifer” fellow was… a tornado.

It didn’t take us long to fall in love with this rebooted Geek Friday, and things only got better when it eventually became IRLTalk.

I longed for longer car rides with Erin, so we could finally have some time together to listen to IRLTalk. We couldn’t wait to hear what ridiculous pranks Jason pulled, nor what ridiculous puns he would commit to wholeheartedly. I’m still amazed by his long troll of singing George Michael’s Faith to Faith without her even knowing.

Erin and I bonded over IRLTalk, laughing along with Faith and Jason. We were in hysterics the whole time.

IRLTalk exposed Erin to podcasting in a way I was never able to before. It was incredibly convenient not too much later when I started recording my own show with friends.

Though Erin and I were lucky enough to meet Faith in person in 2014, we were never lucky enough to meet Jason. Nevertheless, Jason and I stayed friends after IRLTalk ended; including him sending me an utterly preposterous Christmas card portrait this past year. And this gem, received only last month, for my birthday on St. Patrick’s Day:

Jason's goofy birthday image

I’m going to miss you, Jason. You brought a unique joy to my life that can never be replaced.



One of my favorite series of blog posts was Swift Diary, written by Brent Simmons while he was learning Swift. He laid it all out there, risked sounding ignorant, but learned a lot. So did all his readers. In many ways, he was an inspiration for this post.

So, too, were my dear friends Myke and Stephen. They’ve been branching out from audio into video for a long time. At first I shrugged it off as just doing what all the Cool Kids™ are doing. Then I saw their videos get better and better, and I couldn’t help but feel intrigued.

Maybe that was feeling left out and left behind—they were moving forward while I was standing still. I wanted to try to do a video or two of my own, but I knew I didn’t want to do a vlog and I didn’t have a museum of old Apple hardware laying around to film. What then?

Last night’s post was the perfect excuse. I wanted to try to make a screencast. I would basically take the time to show, rather than tell, what I was talking about in that post. Twenty minutes later, I had my first screencast. A couple hours later, it was on YouTube.

This video has many problems that I need to fix for next time:

  • I’m backlit
  • The font in Xcode, Visual Studio Code, and Terminal is way too small
  • I’m way too monotone
  • I made too many mistakes, because I was talking extemporaneously rather than from some sort of script
  • Only after I uploaded the video I realized the audio was only in the left channel. The linked video is actually the second copy, now with fixed audio.
    • Goodbye, ~50 views and ~3 thumbs up. 😭

I share the above partially to avoid getting that feedback, but also to acknowledge—like Brent did—that I am fumbling along, learning as I go. Hopefully the next video will be better, and the next even better still. For as long as I keep up with it.

I intend to convert my series on RxSwift into one or more videos sometime over the next few weeks. I think that they’re the perfect content for a screencast. After that, who knows. All I know is, this one was fun to make.

I’ve embedded the video below, but it’s best watched fullscreen on as large a screen as you have handy. Please do share any feedback you may have; the only way for me to get better is to learn from my mistakes.

The Magic of Sourcery

Today we had a “hack day” at my company. We get to spend the day working on something that’s at least tangentially related to what we do day-to-day, but doesn’t have to be directly applicable. Today, I finally got around to trying Sourcery.

Sourcery is a code generation tool for Swift. It is particularly well suited for auto-generating code that is very repetitious; code most developers refer to as “boilerplate”. Some examples that come to mind:

  • Equatable implementations
  • Hashable implementations
  • struct initializers
  • Lens implementations

The documentation isn’t super obvious at a glance, so I’m writing this to serve as a quick primer to get you going.

Let’s start with a basic Mac command line app, just to keep things simple. This app has a basic type:

struct Person {
    var firstName: String
    var lastName: String
    var birthDate: Date
    var age: Int {
        return Calendar.current.dateComponents([.year],
                                               from: birthDate,
                                               to: Date()).year ?? -1

This type has a trio of normal properties, and one computed/read-only property. In Swift, in order to compare the equality of two Person instances, we need to implement a protocol called Equatable. To do so is trivial—at least in this case:

extension Person: Equatable {
    static func ==(lhs: Person, rhs: Person) -> Bool {
        guard lhs.firstName == rhs.firstName else { return false }
        guard lhs.lastName == rhs.lastName else { return false }
        guard lhs.birthDate == rhs.birthDate else { return false }
        return true

However, imagine doing this across ten types. Across fifty. How many structs and classes are in your project? Implementing these constantly is rote work, and not really what I’m being paid for. For repetitive tasks like these, is there any way to automate them?

There is, and it’s Sourcery.

Sourcery allows you to auto-generate code based on templates. Those templates can be written in several templating languages; for the purposes of this post, I’ll be concentrating on Stencil.

The combination of Sourcery and a Stencil template allows us to auto-generate an Equatable implementation for every one of our types.

Our template looks like this:

{% for type in types.implementing.AutoEquatable %}
// MARK: {{ }} Equatable
extension {{}}: Equatable {
    static func ==(lhs: {{}}, rhs: {{}}) -> Bool {
        {% for variable in type.storedVariables %}guard lhs.{{}} == rhs.{{}} else { return false }
        {% endfor %}
        return true
{% endfor %}

Let’s explore the important lines:

{% for type in types.implementing.AutoEquatable %}

Here, we’re enumerating on every type in our project that implements AutoEquatable. AutoEquatable is simply a marker protocol:

protocol AutoEquatable {}

We use it, in concert with this precondition, to allow us to opt in to auto-generation of an Equatable implementation. If we want to write our Equatable by hand, we do so. If we want to have it auto-generated, we simply add the marker protocol to our type:

extension Person: AutoEquatable {}

Coming back to our template, we are now in our enumeration of all the types that implement AutoEquatable. For each of those types, we create the extension for the type in question, and the required function:

extension {{}}: Equatable {
    static func ==(lhs: {{}}, rhs: {{}}) -> Bool {

In this function, we need to do a comparison for each of the stored properties in that type. Properties like Person's age aren’t stored, and thus, don’t need to be compared. Sourcery makes that happen for us too:

{% for variable in type.storedVariables %}guard lhs.{{}} == rhs.{{}} else { return false }

The above basically says “for each of the stored variables in this type, do a comparison”. The rest of the template is pretty self-explanatory.

Do note, however, that I’m leaving a lot of stuff out of this template, for brevity’s sake. I strongly encourage you to check out the sample template that Sourcery provides.

Performing Code Generation

Now, we need to use Sourcery to perform the generation. Starting from our sample app, we can download the Sourcery binaries, and include them in our project. In this case, I’ve put it in ./Resources/sourcery. You can see it here.

Now, we can run Sourcery on our files. Assuming we’re in our project’s home folder—the folder that has our SourceryDemo.xcodeproj in it—we run it as such:

   --sources ./SourceryDemo 
   --templates ./SourceryDemo/Templates/ 
   --output ./SourceryDemo/Autogenerated

You can see the result of Sourcery’s magic here:

// Generated using Sourcery 0.5.9 —

// MARK: Person Equatable
extension Person: Equatable {
    static func ==(lhs: Person, rhs: Person) -> Bool {
        guard lhs.firstName == rhs.firstName else { return false }
        guard lhs.lastName == rhs.lastName else { return false }
        guard lhs.birthDate == rhs.birthDate else { return false }
        return true

Sourcery has generated exactly what we wanted: a func ==() that will compare each of the stored properties on Person. Neat-o!

Automatic Regeneration

What happens if we want to make changes to our template, and then very quickly see the results? Flipping back and forth between our template and is tedious and frustrating. Luckily, Sourcery has a solution for this. If you add the convenient --watch option to your command line, it will stay open, constantly watching for changes in your templates and your source files.

Take this a step further, and you can watch your changes happen in real time. My old friend, Visual Studio Code is a great helper here:

Sourcery live demo

As you can see, the moment I hit ⌘-S to save the template, the Swift code is automatically regenerated. Neat stuff.

It’s worth noting that Visual Studio Code doesn’t ship with Stencil highlighting by default. To install it, press ⌘-T and then enter the command ext install stencil.

Pre-Build Step

Once you have your templates squared away, presumably by writing them in concert with Visual Studio Code, you still don’t want to generate new ones by hand. What happens if you add a new type? Sourcery will pick this up automatically, but only if you re-run it.

We can easily ensure that Sourcery is run every time we build. We can add a pre-build step in Xcode. To do so is easy. Select your target in Xcode, and then select the Build Phases tab. You can add a new one:

Run Script Build Step

Now, every time we build, the first step will be to refresh the Sourcery output.

Way Forward

Though what I did today was simply a proof of concept, my intention for Monday is to spend some time integrating Sourcery into our real projects.

Should you wish to see my setup, I have put a sample project on Github. If you look at the commit history, you can walk through each step, much like I did with my RxSwift primer.

Temperature scales, compared

I was a guest on this week’s episode of Pragmatic, a podcast about nerdy things by nerdy people.

On this episode, John and I discussed the merits and drawbacks of the metric and imperial units of measure. This is a topic that I’ve discussed numerous times on Twitter. As an Australian, John had some, let’s call them, differing viewpoints.

I had a lot of fun making an impassioned plea for… some things. As it turns out, my position on metric versus imperial is more nuanced than perhaps even I expected. You’ll have to listen to see what parts of imperial I defended, and what I was embarrassed by.


Today I joined Dan Moren, Aleen Simms, and Jason Snell on Clockwise. On today’s episode, we discussed video games we’re either playing or looking forward to, the rumored 10" iPad Pro, video streaming services, and platform envy.

Clockwise is such a blast to record; if you haven’t listened to it, you should really give it a shot.


Making my triumphant return to The Incomparable, I appeared on today’s episode, covering The Rocketeer (Amazon, iTunes). This is one of my favorite movies, and like Hunt, one that I’ve loved since I was a boy.

I was joined by David Loehr, Dan Moren, Joe Rosensteel, and of course, Jason Snell. The episode was a ton of fun, and I’m glad the panel was all die-hard Rocketeer fans like myself.

Appearing on The Incomparable is truly an honor, and I’m so glad I got asked to come back.


When I wrote the Node portion of my push notification toolchain, I was doing so because I wanted to be able to simply cURL a URL, without having to worry about HTTP verbs, headers, or anything else. The Node endpoint proxied my requests for me, so that I didn’t have to worry about anything but a title and a message.

At the time I hadn’t written any sort of local script, so being able to do

curl http://localhost/performAPush?title=Hi&message=Done

was helpful. It wasn’t until I wrote the done script that it became apparent that my Node proxy wasn’t really providing any value anymore.

As Jon noted via Twitter, this isn’t strictly speaking necessary. cURL can do all of this for me, if I’m willing to do so. I could script this out such that a shell script of some sort does the heavy lifting, rather than an endpoint on my web server, or having to remember all the requisite cURL options.

Paul DeLeeuw came to a similar conclusion, and put together a nice walkthrough of a PHP script he wrote to get the job done. By taking this approach, Paul didn’t need a web server; he’s tickling the Pushover URL directly.

Shell → Watch Notifications

At work we recently switched from Cocoapods to punic. The reasons why are irrelevant for the purpose of this post. However, one of the traits of using punic is very long build times when you’re building all your dependencies. On the plus side, builds of our project tend to be pretty quick.

On the occasions that I do need to run a punic build, I often want to start working on something else while I wait. However, I also want to know the moment that the build is done, so I can continue working on our app. Thanks to a combination of a shell script, a web server, and Pushover, I can.

Pushover is a free service that will convert emails or API calls to push notifications to their native app. They also have an API you can use to perform a push. I have a URL that I can hit that will transform a HTTP GET with a couple parameters to a call to Pushover’s API. Here’s my code written for Node, as an example.

function (response, query, request) {
  new Promise(function (resolve, reject) {
    var options = {
      'url': '',
      form: {
        token: "{pushover token}",
        title: query.title,
        message: query.message,
        user: "{pushover user}"
    };, function(err,httpResponse,body) {
      if (err || typeof(body["errors"]) !== "undefined") { 
        reject(body["errors"] || err); 
      } else { 
  }).then(function() {
  }, function (error) {

I can call this with a URL such as:


This URL is extremely easy to tickle using cURL. I can make it even easier to call by automatically URL encoding the input using php. This is written for fish but wouldn’t be hard to do in any other shell:


set escaped (php -r "echo urlencode('$argv');")
curl -s "http://localhost/performAPush?title=Done&message=$escaped" > /dev/null

So I can call that script, which I’ve called done, as follows:

> ~/done punic build is complete!

Which results in this hitting my watch:

Push notification

Putting it all together, I can do something like this (again, fish shell syntax):

> punic build; ~/done punic build is complete!

Now I can walk away and take care of other things, but still know the moment my build is ready.