Quality Coding
Shares

Look, Mom! My First Spike Solution in Swift

Swift, here I come!

It’s time to start another version of the MarvelBrowser project. As I did with the Objective-C version, I begin the Swift version with a spike solution. But the first time was to see if I could satisfy Marvel’s authentication requirements. Basically, I needed to get the incantation correct. This time, I know the steps to take, but I will repeat them in Swift.

I have two goals:

  1. Make it work
  2. Make it Swifty

Could you give me feedback on the Swiftiness of my code?

[This post is part of the series TDD Sample App: The Complete Collection …So Far]

Hide those API keys

In Objective-C, I put the definitions of my public and private API keys into NSStrings:

static NSString *const MarvelPublicKey = @"my-public-key";
static NSString *const MarvelPrivateKey = @"my-private-key";

A big drawback of Objective-C is its lack of namespaces. Actually, there is a namespace: the single, global namespace. The real problem is that we can’t create more. To avoid clashes, we use long, verbose names.

So here’s how I decided to do it in Swift:

struct MarvelKeys {
    static let publicKey = "my-public-key"
    static let privateKey = "my-private-key"
}

This struct is never instantiated. Its sole purpose is to add semantic organization.

First steps

In the viewDidLoad method of ViewController, I begin by concatenating a timestamp, the private key, and the public key:

    override func viewDidLoad() {
        super.viewDidLoad()

        // Concatenate keys per https://developer.marvel.com/documentation/authorization
        let timeStamp = "1" // Hard-coded for spike
        let keys = timeStamp + MarvelKeys.privateKey + MarvelKeys.publicKey

        // Confirm manually:
        print(keys)
    }

Here are some things that strike me about Swift:

  • The need to declare a method as an override method provides important feedback. It asks the question, “Don’t you need to call super?”
  • Type inference is cool. I know, it’s so 2 years ago. But not having to declare the types… yeah. That’s nice.
  • The super-easy string concatenation takes me back to my BASIC days as a kid. (I do hear the voice of Alex Stepanov in my head. He’s complaining that it violates the commutative property of +. Ah well, purity sometimes gives way to pragmatism.)
  • I almost never need the timestamp logging that NSLog does. print again takes me back to BASIC.

Create MD5 hash

This is where Swift first got hard for me. How do I call the plain-C function CC_MD5? Because Objective-C is a strict superset of C, everything in C is available. This is a strength of Objective-C that kept it going for 30 years. It’s also a disadvantage to have a language that is a two-headed beast, bringing along all of C’s lack of safety.

To access CC_MD5 from Swift, I had to create a bridging header MarvelBrowser-Swift-Bridging-Header.h:

#import <CommonCrypto/CommonCrypto.h>

How do I convert the concatenated keys to a UTF8 string, pass it in, and get data back? This was frustrating, but eventually I figured things out. Swift 3 apparently makes this much simpler. (Either that, or the Swift 2 examples I found weren’t very good. Or maybe both.)

        // Create MD5 hash:
        var digest = [UInt8](repeating: 0, count: Int(CC_MD5_DIGEST_LENGTH))
        CC_MD5(keys, CC_LONG(keys.utf8.count), &digest)
        var hash = ""
        for (_, byte) in digest.enumerated() {
            hash += String(format: "%02x", byte)
        }

        // Manually confirm that it's 32 hex digits:
        print(hash)

Question: Is the first argument to CC_MD5 automatically converted to UTF8? Or am I just getting lucky, because my string happens to have nothing but ASCII-expressible characters?

To convert each byte to hex, the original Objective-C code used a classic for-loop. I thought about

        for i in 0 ..< CC_MD5_DIGEST_LENGTH

But the only purpose of the index is to access elements of the hash array. Using for-in over an enumeration seems like a more Swifty approach.

Create URL string

I see that stringWithFormat gets a lot less use in Swift.

        // Manually confirm URL string:
        let urlString = "https://gateway.marvel.com/v1/public/characters?nameStartsWith=Spider&ts=\(timeStamp)&apikey=\(MarvelKeys.publicKey)&hash=\(hash)"
        print(urlString)

Swift’s string interpolation makes it so you no longer have to worry about variable order. Again, this is so 2 years ago. Still, I pause to appreciate it.

I did find one thing to complain about: Isn’t there a way in Swift to wrap a long string literal across multiple lines?

Create and fire the data task

Creating the data task, and logging the results, was pretty straightforward. In Swift 3, the hardest part was continuing to type “NS” by habit, and finding the right way to express UTF8 encoding.

        // Create data task:
        let session = URLSession.shared
        let url = URL(string: urlString)!
        let dataTask = session.dataTask(with: url) { (data, response, error) in
            print("error: ", error);
            print("response: ", response);
            let str = String(data: data!, encoding: String.Encoding.utf8)
            print("data: ", str);
        }
        dataTask.resume()

I’m aware of the two exclamation marks in the code above. Something tells me force-unwrapping is generally something to avoid. But in this case, it’s just a spike solution. The code will be kept off to the side in the spike-networking branch. I’ll exercise more care in the master branch.

Ahh, that closure. There are two things I like about it. First is the name “closure” over the more generic “block”. It better expresses what happens already happens to variables in Objective-C blocks, so I like the more precise name.

Finally, the ability to express a closure argument as a trailing closure. Again, let me pause to appreciate this language.

How’d I do so far?

I have to say, so far Swift is pretty cool. My appreciation may diminish when I get into stubbing and mocking. But for today, I will sit back and smile. And I was glad when I saw JSON results in the console.

How did I do as far as making my code Swifty? Got any tips for unwrapping nullables? Leave a comment below to add your observations!

[This post is part of the series TDD Sample App: The Complete Collection …So Far]

About the Author Jon Reid

Jon is a coach and consultant on iOS Clean Code (Test Driven Development, unit testing, refactoring, design). He’s been practicing TDD since 2001. You can learn more about his background, or see what services he can bring to your organization.

follow me on:
Disclosure: The book links below are affiliate links. If you buy anything, I earn a commission, at no extra cost to you.

Leave a Comment:

20 comments
Alexis says last year

I would change it

struct MarvelKeys {
    static let publicKey = "my-public-key"
    static let privateKey = "my-private-key"
}

to

enum MarvelKeys {
    static let publicKey = "my-public-key"
    static let privateKey = "my-private-key"
}

to avoid the chance of trying to instantiate the MarvelKeys struct:
let keys = MarvelKeys()

Reply
Diogo says last year

Hey Jon, great post, glad your coming to the Swift community ;)

Did you consider using an enum with no cases for your ‘struct MarvelKeys’, this way it is impossible to instantiated it ;)

Reply
    Sam says last year

    I was thinking enum at first glance too…

    Reply
BJ Miller says last year

Great job! I’m very inspired by all your posts and have learned so much from you.

Regarding your questions and request for feedback, here are two points:

1. Wrapping a long string literal across multiple lines? Yes. Use the + operator. For example:
let five = 5
let myString = “this is the beginning of a long string ” +
“that i will make across a few different lines ” +
“with some string interpolation with the number \(five)”
print(myString)

2. Unwrapping optionals: I wrote a post on this for my publisher, hopefully it will help. It was about a year ago, but it all still applies (there are some nuances in Swift 3 with Implicitly Unwrapped Optionals, but if I understand correctly, behavior is still for most intents and purposes the same): http://www.informit.com/articles/article.aspx?p=2359760

Basically, you can unwrap forcefully (not recommended in most cases) with !, chain with ? (which will provide an optional result), unwrap or use a default with ??, or bind using if-let/guard-let syntax, or any other binding combination. Since an optional is just an enumeration, you can also switch on it, but the other methods are much easier to work with and understand.

Reply
    Jon Reid says last year

    BJ, thanks for the info on optionals. There’s much more there than I realized! I’ll study your article.

    As for wrapping a long string literal, see my response to Paul Williamson below.

    Reply
Eric-Paul says last year

The closure-in parameters `(data, response, error)` only need to be parenthesized if you add their types. If you can do it with type inference, you might as well drop the parens.

Reply
Jim says last year

Regarding force unwrapping… first one is a good candidate for a `guard` and second one perhaps an `if let data = data {…}`

Reply
Oren says last year

After watching your talks on youtube, I’m really inspired to start developing in TDD.. So thank you for that.
About the code, I like using NSURLComponents to build the URL, specially the queryItems property…

Reply
Mike M says last year

Hey Jon! Great post!

Curious as to what your thoughts are on this approach in terms of achieving “swifty”. ;) To avoid possibly force unwrapping nil `data` one can use an `if let`.

if let data = data {
print(“data: “, String(data: data, encoding: String.Encoding.utf8));
}

If we wanted to do more things with `data`, we could avoid the extra `if` indentation by using `guard`.

guard let data = data else { return }
print(“data: “, String(data: data, encoding: String.Encoding.utf8));

Reply
Justin Stanley says last year

Yup immediatley wanted that struct to be an enum!

Reply
John Regner says last year

Creating the hash from the digest could be simplified by not enumerating if you don’t need the index.

for byte in digest { //do work }

This could be “simplified” even a bit more by using reduce.

let hash = digest.reduce("", combine: { $0 + String(format: "%02x", $1)})

Reply
    Jon Reid says last year

    Ahh now that’s exactly the kind of “next step” I was hoping for! Takes a little getting used to, but then I could read it okay.

    Reply
Gabriel Hauber says last year

Nice! I just found your blog and am finding some useful stuff in here :)

Not bad for making things “Swifty” on a first pass, but you can do better! In addition to John’s comment, above, the suggestion to change to an enum for the keys is a good one (you’d only go the struct route if you planned on allowing extensions to add further keys). But I’d also suggest dropping the plural on “MarvelKeys” and. It would be nice to also remove the redundant “Key” part of each case, but this results in a clash of name with reserved keyword (“public” and “private”), which you can either get around by capitalising the first letter (Public/Private) and breaking the case naming convention (start with lowercase), or just keep publicKey/privateKey names (and breaking the naming convention of not repeating redundant info). If you go with the former, you get:

enum MarvelKey {
    case Public = "my-public-key"
    case Private = "my-private-key"
}

And usage:

let keys = "\(timeStamp)\(MarvelKey.Private)\(MarvelKey.Public)"

Reply
    Jon Reid says last year

    Yes, as you can tell, I was struggling to find a way to work around the keywords. Bending the rules seems like a decent approach.
    Changing “MarvelKeys” to “MarvelKey” is subtle, but improves legibility at the point-of-call.
    I like it.

    Question: Why do you prefer string interpolation over plain concatenation? I find concatenation easier to read because it introduces blank spaces. …Ohh, because you changed the keys from statics to cases. Curiously, doing so broke the spike, so I reverted that bit.

    Reply
    Jon Reid says last year

    I recently switched from Public to `public`

    Reply
Paul Williamson says last year

I did find one thing to complain about: Isn’t there a way in Swift to wrap a long string literal across multiple lines?

Sure! Just terminate the string and add a + operator. E.g.

let urlString = "https://gateway.marvel.com/v1/public/characters" +
    "?nameStartsWith=Spider" +
    "&ts=\(timeStamp)" +
    "&apikey=\(MarvelKeys.publicKey)" +
    "&hash=\(hash)"

Reply
    Jon Reid says last year

    How can I tell whether this does what I want (create a single literal) rather than perform operations on several literals?

    Reply
Add Your Reply