Today’s earworm…
Daring Fireball: ‘What’s the Deal With Water Bottles?’
From the New Yorker via DF, a delightful survey of comics and their water bottles. As a kid with cable, I watched a lot of standup, so I found this extremely interesting. And the presentation is molto bene – the animated magazine version of the web.
Tricorders
When I was a kid, I expected my tricorder would look like this
But it turned out like this
Which still seems weird.
Via WIL WHEATON dot TUMBLR dot COM - vizreef: Yokogawa // AQ1200 Multi Field...:
The MYSTERIOUS Error.
Callisto is a Catalyst app, mostly running iOS style code on a Mac. But there are some things that Catalyst apps can’t do – like run a python server as another process. But Mac apps can!
There are a lot of instructions around the Internet about how to use AppKit code within your Catalyst app to do those Mac things that Catalyst doesn’t cover yet. This article from Steve Troughton-Smith was our guide.
We also want to do auto-updates like a regular Mac app. We’re distributing outside the Mac App Store for Reasons, so we’re in charge of our own updates. Yep, dear reader, that means Sparkle, which should be fine, right? So we get the Sparkle framework and link it in to our AppKit bundle which is a plugin for our main app. Compile, run and 💥. This error is staring at us from the console.
code signature in (<full path redacted>/Sparkle.framework/Versions/A/Sparkle) not valid for use
in process using Library Validation: not allowing mapping of development code into production process
What does that mean? After some furious googling, we’re left empty handed and confused. The worst kind of error is that special, new error that no one else has ever seen. Bollocks.
After some futile futzing around in the dark, I give up and burn a ‘code level’ support ticket with Apple. I send a fairly detailed outline of the problem and the next morning I get back a nice response asking for more details. In particular, how are the app and the framework code signed? (You can check that with codesign -vvvd <.app or .framework>
.)
And immediately, I see it.
App Signature:
Authority=Developer ID Application: Oak City Labs, LLC (XXXXXXXXX)
Framework Signature:
Authority=Apple Development: Continuous Integration (YYYYYYYYY)
The app is signed with a production identity while the framework is signed with a development identity. That’s what that weird error meant about not allowing mapping of development code into production process
. The codesign
docs point out how frameworks and apps need to both be signed with the same Team ID (which I checked), but don’t mention this distinction between production / development signing identities.
I update Xcode so that the AppKit bundle was being signed with the same production identity and all is well! We are fully Sparkle enabled! Curiously, the AppKit bundle has always been signed with the development identity before, yet it loaded fine at runtime. That must be one of those fine distinctions between a bundle and a framework that I don’t quite understand.
But now we celebrate! 🎉🎉🎉
Throw some Results
Errors, we’ve all got ‘em. What do we do with ‘em?
Swift has a couple of different error handling patterns. There’s try
and catch
which I always think of as kind of the nuclear option. Something went wrong, the code blows up with a throw
and our hero, the intrepid programmer, has to deal with it right here, right now. And then there’s Result
, an elegant weapon for a more civilized age. Call a method and get back a Result
, then deal with it as you like – check for the value, handle the error or maybe even ignore it altogether. Depending on your situation, one of these is likely to suit better than the other.
But sometimes the situation changes. Maybe you wrote some code that should obviously throw
(or calls some library code that throws
), but it would really make a lot more sense to return a Result
. As it turns out, it’s really easy to convert between try
/catch
and a Result
type.
Imagine you have some code that can throw
. Maybe even it always throws, because it is a badIdea()
. But you need to have a Result
instance instead. The Result
type has an initializer that handles this perfectly. You hand it a closure that returns the Success
type for the Result
and any error thrown by the closure is used for the Failure
case. Basically, they’ve wrapped up all the boilerplate in this initializer, and you get a very concise way to convert try
into a Result
.
enum HorrendousError: Error {
case veryBad
}
func badIdea() throws -> String {
throw HorrendousError.veryBad
}
let result = Result<String, Error> { try badIdea() }
The full signature of that initializer is Result.init(catching: () -> Success)
. The single argument is a closure returns the Success
type – here that’s a String
. If the closure throws, then that gets wrapped up as the .failure
case.
How about doing the reverse? You’ve got a result type, but you’d rather access the value in a try
/ catch
sort of way. The Result
type itself has a accessor method for just such an occasion!
let value = try someResult.get()
If the Result
type is the .success
case, it just returns the successful value. If someResult
is a .failure
case, the get()
method will throw
. This is especially useful if you want to access the result and ignore any errors:
let optionalValue = try? someResult.get()
This is also handy in testing when you expect a Result
type to be successful. Use the get()
method to access the value and let XCTest
’s support for tests that throw
to handle the error cases. This can make tests shorter and easier to read too.
For me, discovering the interchangeable nature of try
/catch
and the Result
type was a bit of an ‘aha!’ moment. You’re not locked in to on type of error handling or another. With these convenient converters, you can fluidly move from one type to another as needed. And without a lot of boilerplate code for the conversions!