Building a silly WatchKit App

WatchKit apps are easy to build and draining to debug. I don’t know what it is about extensions but they’re never fun to work with. The other day, daughter asked for an app to play some favorite sounds. I promised I’d write up a post about this, so here it is.

The entirety of the code took maybe a couple of minutes. This is one of the many places where using SwiftUI is a sheer delight. I did have to play with renderingMode to override the default B&W scheme for three of my buttons:

I decided to go with bundle-stored audio rather than deal with the complexities of adding audio assets to an xcassets item. Quick and easy. I limited the daughter to just 4 out of her long list of requested sounds to simplify my life. She returned with her list grudgingly prioritized.

Trimming and converting the audio to wav took seconds, thanks to command-line ffmpeg. Creating the art, too, was simple. I just used Preview to create all the required App Icons. Call it another 5 minutes to get that done. It was a great time to use Preview’s magic selection wand and its support for layers and transparency.

Where all the trouble lay was getting the watch app to consistent deploy to hardware for testing. Although this was a watch-only app, the phone still plays a big role. And I finally discovered that keeping my phone tethered to the computer when deploying to the watch made for a much smoother and more consistent experience. But the frustrations of failed deploys led to over an hour invested.

Here’s a quick summary of where my development time was spent.  Area corresponds to the level of effort:

The actual coding was nothing. Asset prep is always tedious but it’s O(n). It’s pretty clear from the start how much effort is needed, which varies by the number of assets used. You don’t add more complexity when you’re working with the right formats and tools.

The vast majority of headache is getting the Xcode tooling and the hardware to sync, install, and test. This really should be the easiest part, but it was endlessly frustrating. I don’t know if my tether-breakthrough is a universal solution or if my next project will be just as frustrating.

In the end, what should have been a half hour project stretched to several hours. I hope the next one will be way shorter.

The surprising joys of Preview

For years, I’ve been saying how surprising macOS’s Preview app is. Just about everyone knows it is _there_ and lots of people use it to look at pictures and crop and occasionally to annotate. But there’s so much more that Preview can do.

Did you know that you can use Preview to scan and fill out forms using nearby Phones so you can get paper work done and submitted? That’s just one underused feature. It syncs with the phone’s document scanner, to find the outlines of the paper, then performs geometry correction so you can fill out paperwork, whether it’s for your next group hike or your kid’s camp outing. Fill, sign, send, and you’re done.

Preview can also be your new lightweight drawing program, whether you want to work with shapes and text or with freeform lines:

Preview can also adjust photo levels for basic color corrections. The right-hand side is the original. The left shows the picture after I applied auto levels, upped the contrast, and warmed the picture slightly. (Yes, that’s me on the left, and my son on the right in the Powerade reflection.)

I’m not sure who gave the Preview team the go-ahead to add lots of silly and delightful features or whether this is just a dogfood target that somehow got shared with the public, but there’s just so much you can do with it. The other evening, my daughter had begged for a watchkit app and I used Preview to populate my XCAssets for the watch app icon.

If I can get enough people to sign up, I’ll be giving a workshop this week on Preview through Try Swift World, although it’s a bit of a hard sell given how weird a topic it is for an audience of developers.

What’s your favorite hidden app feature that few people know about?

“mint” (a simple SwiftPM installer) has improved my command line life

SwiftPM’s lack of an easy install feature has long been an issue of mine and for other people too. As the linked forums thread suggests, to accomplish this for a general audience requires some careful thinking: adding to /usr/local/bin is not always the best solution for every user.

Still, it’s a feature whose absence is notable. To fill the gap, I’ve discovered Yonas Kolb’s mint. Thank you to Leo Dion, who introduced this to me. It is ridiculously simple to use. Could it get easier than mint install erica/now? Admittedly my github id is short and so is my project name, but I think my point stands…

Yesterday, I hastily added a SwiftPM project to remind so it too could be installed via mint. Suddenly, adding the project specification is no longer an afterthought but a driving feature. It’s made SwiftPM support far more valuable to me for executables.

Make sure you name your primary file main.swift, your product to .executable, and if you’re developing in Xcode, override your path to point to the Xcode-project-style folder for the source files, usually the same name as the project itself. I mention this because Xcode by default (File > New > Swift Package) builds library projects, not executable ones. At the command-line use swift package init --type executable.

// swift-tools-version:5.2
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
    name: "remind",
    platforms: [
        .macOS(.v10_12)
    ],
    products: [
        .executable(
            name: "remind",
            targets: ["remind"]),
    ],
    dependencies: [
        .package(url: "https://github.com/apple/swift-argument-parser", from: "0.0.6"),
    ],
    targets: [
        .target(
            name: "remind",
            dependencies: [.product(name: "ArgumentParser", package: "swift-argument-parser")],
            path: "remind/"
        ),
    ],
    swiftLanguageVersions: [
        .v5
    ]
)

As for mint itself, you can build it or install it via HomeBrew: brew install mint.

If you have any suggestions on how I can improve my SwiftPM work, better integrate tagging, or any other tips, please let me know. Thanks!

Catalina GIFfing: Quick workflow from screen to animated GIFs

All the leaves are green.

And the sky is blue.

I’ve been at my desk.

With screenshot play…

(To be fully truthful, it’s currently raining cats, dogs, kittens, and puppies. But it’s lovely here in the high desert.)

With my newly updated workflow creating the following GIF took about a minute maybe from start to post. The secret? QuickTime Screen Recording (bless you ⌘-Shift-5) and “gifify” courtesy of Homebrew. Set record, demo, stop, convert, drop into WordPress:

I love how easy it is to invoke screen recording these days with macOS’s updated capture interface. It’s especially nice how the optional delay time allows me to get into the zone before recording actually starts.

Back to installation, the blocker was getting homebrew to get itself into position to fully support Catalina. I had to apply homebrew update and homebrew upgrade and homebrew doctor a number of times. Not only did I get gifify installed, but ffmpeg is finally back to working and I once again have emacs for all my git needs.

I’ll spare you how bad the emacs transition was other than to say if you have to disable system integrity and mount read-write by hand, you’re probably doing it the wrong way.

With ffmpeg, it was the dependent libraries including the ones already installed into macOS (like openssl ). Homebrew refused to link:

Warning: Refusing to link macOS provided/shadowed software: openssl@1.1

I wish I had known early about the update/upgrade/doctor approach applied multiple times by the way, not just once, until everything stops complaining and the doctor says “Your system is ready to brew”. Because at that point, installs are a breeze. Installing before then, when the configuration seemed irreparably broken was probably a bad choice.

I spent a bit of time after removing my current bandaid symbolic links. It seems to have helped that I ended up granting separate privileges to ruby in Security & Privacy a while ago. I don’t remember why I did but it’s in there and I vaguely remember going through the process while cursing Cat.

June’s almost here and I wonder if Catalina.successor() will be better or more of the same. It hasn’t been a good Cat year for me.

Musings on `Result` and building a command line utility with completion handlers

Collaborating with Paul Hudson is a pleasure but the time difference can be, well, confusing. So when I started putting together an outline about the new command line argument parser for Pragmatic, one of the first things I wrote was a command line utility to tell me what time it was in Bath, UK:

% now bath uk
Bath 4:16:42 PM (GMT+1 United Kingdom Time)

I use CLGeocoder to use whatever terms I enter after the command as the hints to look up places of interests. I grab the first match and pull the time zone from that match (or throw an error if there’s no possible match).

Command-line utilities are not particularly well known for their asynchronous feature support. Because geocodeAddressString does run asynchronously, I use the quick and dirty trick of starting a runloop that executes until the completion handler finishes. I’m basically adapting an async method to sync.

This gave me an opportunity to finally get around to using the new Result type. Restrictions on other projects prevented me from kicking its wheels (or I was already using my own version from way back).

I struggled a little with how to incorporate Result. Here’s an earlier go at this. I use an optional resultto store the result, which is then set in completion scope:

var result: Result<[CLPlacemark], Error>?

CLGeocoder().geocodeAddressString(hint) { placemarks, error in
    switch (placemarks, error) {
    case (_, let error?):
        result = .failure(error)
    case (let placemarks?, _):
        result = .success(placemarks)
    default:
        fatalError("Geocoder error, no results.")
    }
    CFRunLoopStop(CFRunLoopGetCurrent())
}
CFRunLoopRun()

This code bothered me, and not just because I had to test and unwrap result when control returned to the main part of my method. It seems so obvious that Result should have a simple initializer based on the common elements found in legacy completion handlers. This would collapse those arguments to, for example, completion(Result(error, data)). Unless I’m missing something big here, I thought to build my own convenience initializer:

extension Result {
    init(_ success: Success?, _ failure: Failure?) {
        precondition(!(success == nil && failure == nil))
        switch (success, failure) {
        case (let success?, _):
            self = .success(success)
        case (_, .let failure?):
            self = .failure(failure)
        default:
            fatalError("Cannot initialize `Result` without success or failure")
        }
    }
}

That extension allowed me to collapse the code down to this. Notice how I can use the initializer here to eliminate the optional result, simplifying my extraction with get, towards the end. Admittedly, it’s not always easy to come up with an initial value that can be overwritten this way, but here it worked. The handler became two lines long, and processing the result to get my placemark (including all error handling), another two lines:

static func fetchPlaceMark(from hint: String) throws -> CLPlacemark {
    var result: Result<[CLPlacemark], Error> = Result([], nil)

    CLGeocoder().geocodeAddressString(hint) { placemarks, error in
        result = Result(placemarks, error)
        CFRunLoopStop(CFRunLoopGetCurrent())
    }
    CFRunLoopRun()

    let placemarks = try result.get()
    return placemarks[0]
}

I quite like my initializer and wonder why something like that doesn’t already exist unlike init(catching:() -> Success). I’m curious as surely I’ve missed something important. Even in normal completion handlers, I’d imagine using the legacy Error? and Data? optionals would be a common use-case for Result

Let me know.

Broken App Store downloads on Mojave: We could not complete your purchase

This has been happening to a lot of people recently. You open App Store and try to update apps or download new ones. Instead:

And if you have 48 apps to update, you have to click OK 48 times. Argh.

I spent nearly two hours with Apple yesterday trying to resolve.

Instead, I should have just tweeted because when I did Bas Broek had the answer almost immediately:

I had already rebooted, reset NVRAM/PRAM, cleaned out my Application Support for the App Store, and, get this, at the advice of Apple itself, reinstalled freaking Mojave to try to resolve it.

What a waste of time.

I hope this may come up in someone’s Google search to save them time.

  1. Quit App Store
  2. At the terminal: open $TMPDIR/../C/com.apple.appstore/
  3. In Finder: trash everything in that folder including any pending updates / stuck items.
  4. Relaunch App Store
  5. Done

Update: Gwynne Raskind adds: “$TMPDIR/../C is confstr(_CS_DARWIN_USER_CACHE_DIR)”.

Catalina permissions: Chrome, Zoom, etc

Ran into trouble this weekend where I was unable to add permissions for a number of apps to allow access to my microphone and camera.  (And yes, I’m aware of the security horrors of Zoom but I had work to do.)

They wouldn’t give the normal permissions request:

Instead, I got a message directing me to System Preferences:

Once there, the prefs did not list the app for normal check-to-enable:

I couldn’t unlock and drag on an app.

With some help from Bas Broek and this article, which specifically addressed the inability to grant access in Catalina, I discovered that rebooting with a NVRAM/PRAM reset might help. It sounded like sacrificing chicken entrails but it worked. While a regular reboot didn’t help, the Cmd+Option+PR reboot did.

Apple Support Article: How to reset NVRAM or PRAM on your Mac.

I hope this helps someone else to avoid the time I wasted.

So you’re going to teach remotely…

Remote meetings and teaching are an unfortunate necessity of the times. For those of us already using these platforms in positive ways, perhaps we can share some lessons on how to enhance your toolset.I thought I’d put together a quick post to address some of the ways things the classroom experience changes and suggest some technology support  for online classes.

I apologize for the stream of thought and incompleteness of this post. I wanted to put something out there right away. I’ll try to add, edit, and revise when time allows.

All Cameras On

I found the biggest struggle was my inability to wander through the classroom, peek over shoulders, and talk quietly to individual participants. I’m a walker and the online classroom is very much one of staying still.

My first rule is all cameras on with a further all cameras on faces. People are often uncomfortable with this, either switching their cameras off or pointing them up to the ceiling, essentially isolating themselves as students in a way they cannot do in a normal classroom, even by sitting at the very back with a phone in their lap. Push back and be insistent.

This rule goes a long way towards bringing the classroom together and making up for the fact that so much of the teaching experience is limited to a single viewpoint. The teleconferencing Zoom site offers a camera grid feature, which displays all participants at once (although not necessarily on the same screen, you will have to page through for large gatherings). This feature is especially important when you give in-class exercises, allowing you to keep track of the emotional temperature and find students who may be struggling.

Unfortunately, Zoom does provide an equivalent “screen peek”, so you cannot look over shoulders. Each screen must be shared and viewed individually.

As you teach, make sure you encourage participation, even more than you would in a normal classroom as there is always a danger of getting lost in your monotone without the immediate ability to “read the room” as you teach. Be extra conversational with give and take, much more than you would in a traditional class or seminar. This helps offset the technology and creates a warmth and inclusivity that otherwise would be missing.

Take Breaks

If you’re teaching extended classes, workshops, or labs, make sure to offer regular breaks to stretch and hit the bathroom. Put a timer onscreen. Most search engines allow you to enter “5 minute timer” and produce that timer for you automatically. Teaching, even remotely, is much more physically active a task than learning so remember your student’s endurance will be less than yours.

Prepare

Create a preflight list to ensure you’ve hit all the setup points before you go live teaching, including testing your camera and microphone and cleaning up your teaching platform. Don’t overshare. Depersonalize your desktop and your workspace.

Where possible, use a second unshared screen for teaching notes, outlines, and coordination. It’s far easier to lose track of time and go on tangents in a remote classroom because of the immediacy of the face-to-face conversation. Make sure you know what time it is, and what you have to cover in your lesson plan.

Have on hand a list of teaching objectives, scripts for live coding demos, working and starting code configurations. Don’t forget wrap-ups, next steps, and so forth. If you can, rehearse before you teach.

  • GitHub Place shareable starting source on GitHub or another publicly available site so students can follow along with minimal setup time. I use GitHub’s gist a lot.
  • Snippetty: Minimize typing with Snippetty. This menu-bar-sourced utility collects, orders, and offers the code or text snippets you’ll need during your session. Snippetty also lets you add presenter notes to those snippets. The less you type directly, the fewer typos you’ll encounter directly, and you limit long pauses during your lesson.
  • Snippety is end-of-life’d, sadly. I love this tool.

Display Tools

When displaying slides, whether from a second screen or a tablet, use the presenter notes feature to remind yourself of important topics.

A highlighting tool such as ScreenBrush (ScreenBrush for Mac and ScreenBrush for iOS) is great for circling, underlining, and drawing arrows to onscreen information.

I’m particularly fond of LargeType from Gold Mountain Software, which enables you to emphasize text or code at a readable huge size. This is a great way to call out key information. I’ve customized System Preferences > Keyboard > Shortcuts > Services > Text > LargeType to add a custom keyboard combination to perform selection enlargement without using menus. I currently have it set to Command-Control-Option-L, but you can pick whatever keyboard shortcut best works for you.

Don’t forget that your screen will be hard to read. macOS’s accessibility features enable you to use the mouse, trackpad, and keyboard to zoom parts of the screen. This allows you to zoom in on code and other important details that could be missed by showing an entire large screen. The zoom centers around your cursor by default, so it tracks your interest point and allows that field of vision to be shared with others. Visit System Preferences > Accessibility > Zoom to set your preferences

Demoing

Reflector 3 wirelessly mirrors an iOS screen, enabling you to load, launch, and demo apps from your deployment device. I also use Reflector 3 as a wireless screen-mirroring app from my iOS device.

If you don’t mind being tethered, QuickTime allows you to present your device screen for a connected iOS device.

Whiteboarding

On my iPad, I use Linea Sketchto draw. It has a present mode that hides all the normal iOS app details and just shows a plain whiteboard drawing screen. I recently upgraded to a lastest-gen iPad mini with 1st gen pencil and that has been fantastic. I also got good results before my upgrade with the rubber tipped Heiyo stylus. Use a firm stylus for writing. Most stylii are designed for drawing and to simulate brushes.  The Heiyo doesn’t scratch the screen like the metal-tipped ones, is easy to charge (microUSB), and works with older iPads.

Zoom

Zoom offers a number of great built-in features for questions, messaging, raising hands, and so forth. I particularly like using it to know when students are done with a particular task: clear the participant list and have them use a check mark or thumbs up when they have finished. This way you can continue the class without having to wait a full number of minutes.

The same tool is great for “Who thinks that X is Y” style questions. Always encourage the student to answer before looking at what others have voted.

If you can, supplement Zoom with Slack. It’s much better for creating a record of q&a (start a thread specifically for that), and for when you need to distribute text/code from teacher to students. In terms of q&a, a great approach is to allow 3-5 minutes where everyone can add their questions and then you review the questions and choose which ones you wish to answer. I got this technique from Andrew Madsen and it’s terrific. He also recommends using sli.do for audience participation.

How to contribute to the iOS Developer Survey

Dave Verwer, bless his heart, put together an amazing collection of developer statistics in this year’s iOS developer survey. From careers to conferences, dev tools to frameworks, he profiled the community to discover what is happening on our platforms and in our jobs. If you haven’t already been over to the survey site, go and click the link. Don’t worry, this post will still be here when you get back.

The raw data only goes so far. What the survey needs now is you. Dave is looking for community members to help analyze the results so we can better understand what all this data means. For example, we’ve come to love and cherish our dev tools in a way that was unthinkable just a few years ago. Are the high marks for Xcode universal or are  coders with, say, five or more years of development experience particularly grateful for this advancement?

If you have an interest in deeper analysis, Dave has tools to help you slice, dice, and Julian fry the results. Right now there are just four articles available that evaluate the core information (“Is WWDC already a virtual conference?”, “How popular is Swift?”), and they’ve been written by Dave himself.

This data is crying out for deeper investigation, more voices, and diverse insights. If you have time and curiosity, I urge you to reach out to Dave on Twitter and get involved.

This is a fantastic opportunity to give a little back to the community that nurtures our professional lives.

How to fix: Ooops, I lost track of those beta upgrades

What happens when you put off beta upgrades and put them off and off and off and suddenly the release version has outpaced the beta? This happens:

And this happens:

So there you are, stuck in beta and complaining to your friends about what is, ultimate, my your fault. If you don’t update on a timely basis, you can waste a few hours (as I just did) getting your system back on track so you can mess with (for example, just to pull something out of thin air) Swift Playgrounds for iOS for Mac (which requires the 10.15.4 beta10.15.3).

Just so you don’t waste time, don’t try installing the 10.15.3 combo updater. (Image above) You need to:

  • Unenroll from the beta program,
  • Install the latest macos from the App Store,
  • Re-enroll into the beta program, and
  • Upgrade to the latest beta.

Prepare to waste your entire morning on this, hopefully less the time I spent figuring it out courtesy of friends. Once you get the App Store blessed macos install going, remember that once it reboots, there’s another hour of wait time just on the standard install:

Those 13 minutes are a lie.

Expect at least another hour once you get to the beta access installer:

Hopefully this post will help anyone doing a websearch to get out of exactly this issue at some point in the future.

A final note: Once you’re up to date, swear to yourself that your rule of “never be the first to install a new beta” doesn’t mean “never be the last to install the new beta“. Hopefully I’ve learned my lesson.