.st0{fill:#FFFFFF;}

Look, Mom! My First Spike Solution in Swift 

 July 12, 2016

by Jon Reid

20 COMMENTS

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. There is one 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. (These days, I use an enum instead because then no one can instantiate it.)

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 rarely need the timestamp logging that NSLog provides. print takes me back to coding in 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 out something that works. (It can be made cleaner. But in a spike solution, clean isn’t the goal. Quick learning is the goal, so the code can be dirty.)

// 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 doing

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. 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]

Quality Coding repeats the network call spike solution, this time in Swift

Click to Tweet

Jon Reid

About the author

Programming was fun when I was a kid. But working in Silicon Valley, I saw poor code lead to fear, with real human costs. Looking for ways to make my life better, I learned about Extreme Programming, including unit testing, test-driven development (TDD), and refactoring. Programming became fun again! I've now been doing TDD in Apple environments for 20 years. I'm committed to software crafting as a discipline, hoping we can all reach greater effectiveness and joy. Now a coach with Industrial Logic!

  • 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()

  • 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 ;)

  • 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.

    • 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.

  • 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.

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

  • 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…

  • 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));

  • 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)})

  • 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)"

    • 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.

  • 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)"

  • {"email":"Email address invalid","url":"Website address invalid","required":"Required field missing"}
    >