A while back I got rid of Google Analytics for this website and Taphouse's too. I also went and downloaded fonts that I could embed in the site to get rid of TypeKit. I thought that was it, but it turns out my monospace font was downloaded too. Thanks to IBM Plex that's no longer the case.

Goodbye anything that can track you!

Automation Revelation

When I relaunched my website last summer using my homegrown blog engine (Maverick) I adopted the textbundle format for writing posts and I was using Ulysses for my writing. But after launching the site and using Ulysses for long enough I was running into some friction with their Markdown syntax. That led me to the decision to not renew my subscription when the time came.

I'd since gotten a BBEdit license after hearing tons of people I highly respect speak glowingly about it and decided to use it for writing my posts. It doesn't support textbundle natively like Ulysses does, but it does well enough for my needs. textbundles are in fact just a folder with a Markdown file inside with an optional assets directory. So I've been using BBEdit for writing on my Mac ever since.

But I've been running into a different kind of friction: making new posts was kind of painful. I've been manually making the directory structure, copying the info.json file, and creating the text file myself. I'd written an Alfred snippet to create the front matter that each post needs, but that's become problematic too because dates are hard (more on that in a minute).

Well, we've gotten deluged with snow in my neck of the woods so I figured I'd dive in to a couple of new tools and get this thing automated. I watched through the NSScreencast episodes with John Sundell explaining his Marathon scripting tool written in Swift, for Swift. I was able to take that knowledge and make a little script which outputs the directory structure I need, fills the front matter on the post, and launches me into BBEdit where I can write away.

Part of the spark for this was that I'd approached dates all wrong when I built Maverick. I used dates in my current time, not against UTC. I have posts automatically show up on my microblog using micro.blog's cross posting functionality and when my posts came over they were off by 8 hours or so (I'm in the Pacific time zone). So I had to go through all my posts and update times, then use dates that are UTC based (formatting them using an ISO8601DateFormatter and .withInternetDateTime).

I should be all set now, and my posts showing up in the correct order on my microblog. Happy Saturday!


Here's the script that I wrote, in case you're interested 🙂

CALayer Mask Inversion

I've got a view controller with a camera view, overlaid with a partially transparent dimming view. I need to punch a hole out of the dimming view to let the camera shine through in its full glory. My first crack was the code below, and it works:

override func viewDidLayoutSubviews() {

    let inset: CGFloat = 16.0
    let rectWidth = self.view.frame.width - (inset * 2)
    let rectHeight = rectWidth * 0.63
    let rectSize = CGSize(width: rectWidth, height: rectHeight)

    let rectOriginY = (self.view.frame.height / 2) - (rectHeight / 2)
    let rectOriginX = inset
    let rectOrigin = CGPoint(x: rectOriginX, y: rectOriginY)

    let maskRect = CGRect(origin: rectOrigin, size: rectSize)

    let maskLayer = CAShapeLayer()
    let path = CGMutablePath()
    maskLayer.path = path
    maskLayer.fillRule = .evenOdd

    self.dimmingView.layer.mask = maskLayer

Which produces what I want:

But it was redrawing too often (during transitions especially). I decided to have the mask only draw once, and I needed the view hierarchy set up. So I put it in viewWillAppear. Exact same body as above just different lifecycle method. Here's that result:

How in the world can the same code produce such different results?


Thanks to Tom Bunch for asking me about the state of our view controller's superLayer. At viewWillAppear, it has no super layer but at viewDidLayoutSubviews it has one. The class of the layer is a UIWindowLayer (which is a private class). Turns out that the super layer must apply some transform that causes our inversion to happen.

To get around the problem of the too many redraws I have instead opted for a simple hasAppliedCutout boolean state check during viewDidLayoutSubviews. It's not the cleanest solution but it will work for what I need without too much extra fuss.

On the Sharp Edges of UIView.mask

I'm working on implementing a new user-facing feature at work, and the designs I've been given call for having a dark gray semi-transparent view overlaying a view, and this semi-transparent view has a rectangle punched out of it. Not having done this kind of thing before I went searching the docs on CALayer to see what options are available. I knew that layers could mask other layers so this seemed like the right place to go.

While using a layer-based mask seemed promising initially, I wanted flexibility to move this mask around pretty easily using Auto Layout constraints. I stumbled upon a really helpful article from Paul Hudson outlining the UIView property called mask. From the docs, this property is:

An optional view whose alpha channel is used to mask a view’s content.

That's the entire description we're given. I ran the code from Paul's blog post in a playground and sure enough I was able to mask the view (more on this later). So I adopted it in our app, and I ran it. I looked and there was no mask. In fact, the entire overlay was not visible. Huh? Why would this work in a playground but not the app?

Turns out there are a couple of undocumented gotchas that go along with using a mask, and if you don't know them you could spend a lot of time learning the hard way. I'm hoping this will spare you some lost hours. Here we go.

  1. The mask view cannot be part of a view hierarchy. This means no Auto Layout, and no constraint manipulation. It's all frames with this view.
  2. The mask cannot be held on to by anything except for the view it's masking. Don't try to store it as a property or you're in for a bag of hurt (as in it won't work at all, and the view it's masking will no longer be visible).

I suppose that if #2 were to be solved it could make #1 possible, but the reality is that if this property is going to have as many usability issues as it does then the documentation needs to be updated to reflect that. If you've been in the Apple developer sphere for very long you've heard the phrase "Radar or GTFO". With that in mind, I submitted rdar://47809462 (OpenRadar link) as I do not want to GTFO 🙂.

🔗 No More Oppatoo

No More Oppatoo

But kids inspire love, such deep unconditional love. You love and treasure how they are, down to the smallest quirk. Then, suddenly, right in front of you, they change. While one might grow used to the slow, sad change of growing apart from an adult you love, this feels very different.

This post from Allen Pike has been sitting with me for a while. I've got 2 boys, 5 and 2.5 years old, and my wife and I get to see their personalities develop. Atticus (our 5 year old) used to be attached to his pacifier, calling for it before going to bed with adorable cries of "Paci!" and Finnian (the 2.5 year old) went through an adorable phase of saying "yep" to everything.

Right now Finnian is saying "That's awesome", endlessly singing songs like "Baby Shark", and "Jesus Loves Me", and going on and on about choosing the purple bath bomb. Atticus loves watching toy train videos on YouTube and hugging every stuffed animal he comes across and saying "nice !".

I know these phases will end someday, perhaps soon. For now they will be replaced with other super cute things. The day ~may~ will come when the new things are simply grammatically correct and no longer endearing in the way toddlers are. It seems parenting is a bittersweet endeavor.

Take pictures, shoot video, make audio recordings. The memories will only last in our minds for so long, but we can preserve them thanks to the pocket computers we all carry. I think that's one of the best parts about living in the time we do. We can always remember our kids as they were, while celebrating the people they become.