Wallet-based business cards

A couple of months ago I played a little bit with PassKit and Wallet after finding this little tutorial on adding a business card to Passbook (now named Wallet).

A Wallet pass is pretty easy to create—just fill some metadata into a JSON file, create a Pass Type ID certificate in your Apple Developer account, and then run a signing utility. The whole process is pretty well described, step-by-step, here, so I won’t re-iterate, but here are a couple of little perils and pitfalls to watch out for:

  • To run signpass, you’ll need to download the Xcode project in the Wallet Developer Guide. Unzip the download and open signpass.xcodeproj in the signpass folder. Build it, right-click on the executable in the Products folder in Xcode’s file navigator, and select “Show in Finder”; drag and drop it into your Documents folder.

  • After you run signpass from the Terminal, run open PassName.pkpass and it’ll open in a Preview QuickLook window. From here, you can click on the “Add to Wallet” button and it’ll go up to iCloud and download to your iOS devices, without having to upload it to a server.

Preview QuickLook

  • If the pass doesn’t show up in your iOS device, there’s probably a typo in your JSON. Look for extra/missing commas, parentheses, square brackets, &cet.

  • You can double-check by launching Xcode’s Simulator, launching the Wallet app, and dragging and dropping your .pkpass file from the Finder into your Simulator Wallet. If everything checks out, it’ll ask if you want to add it.

Simulator add pass

  • If you’re uploading it to S3, make sure you set the Content-Type to application/vnd.apple.pkpass. Normally, this is a dropdown, but you can also just type whatever you want in there and S3 will accept it.

S3 Content-Type

And now you have a fancy electronic business card that other iPhone users can scan and download. Have fun!


Using XCTAssertThrowsError in your Swift tests

There’s a new kid on the XCTest block, and its name is XCTAssertThrowsError.

I haven’t been able to find much on its usage aside from its original discussion on the swift-evolution mailing list and a Stack Overflow question, so here’s a little bit of a discussion on how I’m using it in a new project of mine.

Swift introduced some pretty neat error handling in 2.0, and Natasha the Robot provided a nice guide on how to throw an error in your code.

So, as a contrived example, let’s say you have a class called AccountManager that manages a set of Account objects:

enum ListError: ErrorType {
    case AccountAlreadyExistsInList
    case AccountDoesNotExistInList
}

class AccountManager {
    var accountList = Set<Account>()    // Account conforms to Hashable, Equatable. I promise.

    func add(_ account: Account) throws {
        if (accountList.contains(account)) {
            throw ListError.AccountAlreadyExistsInList
        }
        else {
            accountList.insert(account)
        }
    }

    func remove(_ account: Account) throws {
        if (accountList.contains(account)) {
            accountList.remove(account)
        }
        else {
            throw ListError.AccountDoesNotExistInList
        }
    }
}

(Note that in Swift 3, ErrorType has been renamed to ErrorProtocol.)

A couple of things to know about the Set type:

  • No duplicates can be added, a Set is silent when you try to add an element that it already contains.
  • Unless you’re checking its return type, Set is also silent when you try to remove an element that it doesn’t contain1.

While this protects the integrity of the Set, it could be a bit frustrating for consumers of the AccountManager class, because there’s no way to surface what’s going on when we try to add a duplicate or remove a non-existent element. So, we throw!

Specifically, what we’re doing in the AccountManager class is checking to see if the Account argument we’re passing to the add(account:) and remove(account:) functions already exists in the accountList Set, and handling the result appropriately:

  1. If we’re trying to remove an Account from the accountList and it exists, go ahead and do so. If it doesn’t, throw ListError.AccountDoesNotExistInList.
  2. If we’re trying to add an Account to the accountList and it exists, throw ListError.AccountAlreadyExistsInList. If it doesn’t, go ahead and add it.

And of course, in our unit tests, we want to check that these errors are thrown properly. Enter XCTAssertThrowsError!

// Test that adding a duplicate account to an accountList throws an error.
func testAdd_AddingDuplicateAccount_ThrowsAccountAlreadyExistsInList() {
    let accounts = AccountManager()
    let firstAccount = Account(descriptiveName: "Account 1", accountNumber: "12345AZ")
    let secondAccount = Account(descriptiveName: "Account 1", accountNumber: "12345AZ")

    accounts.add(firstAccount)

    XCTAssertThrowsError(try accounts.add(secondAccount))
}

// Test that removing an account from an empty accountList throws an error.
func testRemove_RemovingAccountFromEmptyList_ThrowsAccountDoesNotExistInList() {
    let accounts = AccountManager()
    let firstAccount = Account(descriptiveName: "Account 1", accountNumber: "12345AZ")

    XCTAssertThrowsError(try accounts.remove(firstAccount))
}

Run the tests and they’ll pass, because the tested expression throws an error. Cool!

This is a good start, but we’re only testing that an error is thrown. That’s not good enough, of course, because any error thrown will make this test pass, but we’re looking for a specific error. Let’s take a closer look at the declaration for XCTAssertThrowsError:

func XCTAssertThrowsError<T>(_ expression: @autoclosure () throws -> T,
                                _ message: @autoclosure () -> String = default,
                                     file: StaticString = #file,
                                     line: UInt = #line,
                             errorHandler: (error: ErrorProtocol) -> Void = default)

I’ve split the signature up so that there’s just one argument per line. The description for each argument is available in the documentation, so I won’t repeat them here, but the important thing to note is that XCTAssertThrowsError is actually a generic on T. This means that in the expression argument, we can add a closure that checks to see if, in fact, an error of type T is being thrown.

So let’s add those checks to our two tests:

// Test that adding a duplicate account to an accountList throws an AccountAlreadyExistsInList error.
    func testAdd_AddingDuplicateAccount_ThrowsAccountAlreadyExistsInList() {
    let accounts = AccountManager()
    let firstAccount = Account(descriptiveName: "Account 1", accountNumber: "12345AZ")
    let secondAccount = Account(descriptiveName: "Account 1", accountNumber: "12345AZ")

    accounts.add(firstAccount)

    XCTAssertThrowsError(try accounts.add(secondAccount)) { (error) -> Void in
        XCTAssertEqual(error as? ListError, ListError.AccountAlreadyExistsInList)
    }
}

// Test that removing an account from an empty accountList throws an AccountDoesNotExistInList error.
func testRemove_RemovingAccountFromEmptyList_ThrowsAccountDoesNotExistInList() {
    let accounts = AccountManager()
    let firstAccount = Account(descriptiveName: "Account 1", accountNumber: "12345AZ")

    XCTAssertThrowsError(try accounts.remove(firstAccount)) { (error) -> Void in
        XCTAssertEqual(error as? ListError, ListError.AccountDoesNotExistInList)
    }
}

Now we’re sure that we’re testing that the right error is being thrown in our tests: in the closure, we call another assertion, XCTAssertEqual, to check that the error being thrown is the type of ListError that we expect.

What does this mean? We no longer have to create weirdo functions that return a tuple <U, V>, where U is the result and V is an error that you can check for.

You can add other arguments to your assertion, like a message or the specific file and line number if the test fails, but for now this should be enough to get you started checking your error throwing.

  1. Of course, you should be checking the return type, and you’d see that you got back nil, but like I said: this is a contrived example. ↩


Posting to Hugo on GitLab Pages from iOS

In moving this blog to a static site generator, one concern was whether I’d still be able to work on and/or publish a post from my phone. I tend to do a fair bit of mobile drafting while I’m in the subway or waiting in line, and will sometimes publish content when I’m away from my computer. YOLO.

Requirements

It’s a given—since that’s how the site has been setup—that you need a GitLab.com account, and Hugo set up with GitLab CI to build the site and deploy it to GitLab Pages.

On my iPhone, I have Working Copy, which I use for git operations on the repository. It’s an excellent app, and when you pair it with Editorial’s powerful workflow and text editing capabilities, you’ve got a great toolkit for repository management as well as writing.

I like using issues as jumping off points for new posts (including this one, so I also use Git Trident to manage this in GitLab from my phone. Matt’s working hard on making this the best app for handling GitHub and GitLab issues on your iOS device, and I’m especially looking forward to offline access.

Workflow

This post was a test for how to add content to the site from my phone, and here’s how I’m going to do it from now on:

  1. Usually (but not always), an issue tagged post idea is the basis for a new post. When I’m ready to write, I assign the issue to myself and set a due date for it.
  2. I then create a new branch as n_post_post-subject_yyyy-mm-dd in Working Copy, where n is the issue number and yyyy-mm-dd is the post date. If no issue exists, I drop the n_ prefix in the branch name. I try to avoid setting a title, as this can change over the course of the post. “Post subject” is already pretty well defined, since I know what I want to write about.
  3. Next, I create a new text file in Working Copy under /content/post, and open it in Editorial via the share sheet to start writing. I have a TextExpander snippet to add the TOML front matter; it’s also set as a draft, to ensure it doesn’t get published accidentally.
  4. Then comes the hard part: write the post. Commit as necessary with Working Copy’s workflow for Editorial. I commit and push for a “cloud save”, or if I think I might want to continue elsewhere (i.e., on my desktop, or in a web editor1.
  5. When it’s all said and done, I set 'draft = false in the front matter, commit and push with message Closes #n, where n is the issue number.
  6. When I’m ready to publish, I launch Safari and go to GitLab.com to open a new merge request. Merging the draft branch into master will trigger GitLab CI; once the build passes, the post will be published. I also delete the original branch.
  7. That’s it! The post is published. Enjoy a tasty, refreshing beverage!

I wrote the majority of this post on my iPhone, although I’ll admit that it does feel a bit silly to tap away on a 4.7” screen when I’ve got a 24” monitor and full-size keyboard to work with. Still, for drafting and editing, it’s great, and in a pinch (or, say, on an iPad with an external keyboard), you can keep your blog running from your iOS device without issue.

  1. Web editor, you say? Why yes! You can edit a file in GitLab, then commit the changes. Neat, huh? ↩


A six-month update

As I mentioned last week, today is July 1st, which marks a convenient half-way point for the year. On New Year’s Day, I posted a short list of goals that I was hoping to make progress towards. Here’s how it all breaks down.

1. Post something here every Friday.

So far, so good. I haven’t missed a single week, which I’m pretty pleased with. I’ll grant that it hasn’t always (ever?) been a particularly interesting read, but I’ve been trying hard to keep showing up. Discipline isn’t easy, but it helps if you try to avoid breaking the streak.

2. Post to a journal at least once every day.

I was doing pretty well with this through to the end of March, and gave up by April 22nd. It started well, but frankly, I wasn’t comfortable in writing my deepest thoughts into a service that can’t guarantee its privacy—not that I think the fine folks at Bloom would ever consider it, but if key employees have access to the decrypted data, that means necessarily that there’s some risk of a data breach.

And I could feel it when I was writing, too—I was self-censoring just in case. And if you can’t just write whatever you want in your journal, of all places, then it’s not really a journal.

So, I’ve deleted whatever I’d posted there, and I’m re-thinking this goal. There’s value to writing regularly, sure, and there’s value to getting your thoughts down, but between this blog, the Break Before Make tumblr blog, and Twitter, I think I’ve got more than enough opportunity to write.

3. Make real progress towards my Mac app.

Myeah.

Well, I settled on a name and registered a domain. I keep starting new projects and I’ve got even more in my Someday/Maybe list. I really need to focus more on this.

4. Contribute to open-source projects.

I feel like I haven’t done too much with this yet. I’ve written for open-source projects, but beyond that, this also feels like something that’s maybe taking focus away from the Mac app.

5. Get in better shape.

After taking care of some knee issues with my physiotherapist, she gave me the green light to start getting active again. So, as of the end of March, I’ve started by jogging. My goal is to get in three 15-minute runs per week—I’m not trying to run a marathon, just improve my cardiovascular health.

It’s been, well, shaky.

I’ve lapsed into no-run periods a few times, either because of an injury, or because I’ve been too lazy to get out and do it. But generally, there’s a very strong downward trend in my pace (from a 7:41 to a 6:17), so I’m happy. Moreover, it’s just been getting easier to run for extended periods of time.

It’s high time I add some kind of strength training into the mix, though!

Other salient projects

As linked above, other projects have come up that I started work on.

For one, you may notice the new look of this site. As of today, it’s being built by Hugo and is running on GitLab Pages. That’s been a fun little project, with the side benefit of saving me a few bucks a month, too. I’m considering cross-posting over to Medium, but we’ll see. There’s some theme work to be done, but otherwise, hello Hugo!

I’ve also open-sourced my first iOS app, in the hopes that having it in public will embarrass me into updating it. But again, I really don’t want this to distract me from the Mac app I want to write.

What’s next

Honestly?

I don’t know.

But the intention is, for the next six months, to:

  • keep up with writing here, on (at least) a weekly basis;
  • keep up with improving my general fitness and health; and
  • really get cracking on this Mac app I keep hinting at.

Will I get sidetracked? Undoubtedly. Will I venture into new little projects that may or may not be abandoned? Yeah, it’s possible. We live in flux, after all, and it’s way more fun to explore the forest than it is to walk the path.

Definitely more tk.


Flux

Reminder: as of next week, the RSS feed for this site is moving! If you subscribe, please be sure to point your favourite reader to:

http://makebeforebreak.com/index.xml

Next week is July the first. It’s Canada Day here, which means it’s a national holiday, but in Montreal, it’s also an unofficial moving day.

So it’s a propos that this site will be moving over to its new digs on that same day.

But July first also marks the halfway point of the year, a year which I started with some set of goals. So it’s also a good time to take stock of progress made against those goals, and make any necessary course-corrections.

I don’t intend on revisiting that here today, but I can tell you that I know that I haven’t made the kind of progress I was hoping for. In fact, the only thing I’ve “succeeded” at, strictly speaking, is posting something here every Friday. It hasn’t always been easy, but so far the streak has remained unbroken.

So, technically, I’m looking at an 80% failure rate. That doesn’t sound encouraging at all.

I could feel bad about this.

But I don’t.

Ash Furrow gave a great talk called Loosely Held Strong Convictions last year. It reminded me that, despite our belief to the contrary, we exist in a state of flux.

It’s strange how we unconsciously fight this. I mean, we know that things—including ourselves—are constantly changing. And yet, we often try to plot rigid courses through life, despite the fact that today’s road may well be tomorrow’s wall.

It’s fine to change your mind. In fact, it’s encouraged. It means you’re adaptable. You’re able to go beyond living your life as a to-do list.

The hard part in all of this, what I fight with the most, is whether or not I’m changing my mind because a path has become irrelevant, or just unpleasant. I may not be doing super well at getting to the gym (like, at all), but I am trying to run for 15 minutes or so, three times a week.

Except if it’s raining.

Or if I really need to get into the office soon.

Or maybe if I woke up with a bit of a gurgle in my tummy.

Discipline is hard. Which means it’s way more valuable than motivation. It’s easy to do the pleasant thing when you’re motivated; it’s useful to do the unpleasant thing because you’re disciplined.

So, yeah. By all means, make changes to your opinions and your plans. But do it because it’s a good idea, not because it makes your life easier.