Oscar Swanros

I write about developing for iOS.

› Rewrite from Scratch - Autopsy of a Failed Software Project

This post brought a ton of memories back to mind. I've wanted to start a project from scratch far too many times more than I'd care to admit.

What I've found in my experience as a software developer is that trying to call the right shots from the beginning is a core part of the development process. Often this is the hard path, but I've found myself in situations where I need to change something, and thanked my past self for doing what he did.

Also remember that software is never finished.

Elixir's syntax is awesome: Railway development in Elixir using the with statement

I've been loving Elixir so far. What I like the most is its syntax. So much that lately I've been finding myself trying to use Elixir's syntax while coding Swift.

This post is definitely a good read. It shows a cool trick to handle conditional step execution of code. (It actually resembles kind of the reasoning behind the guard statement in Swift.)

Let's Encrypt leaves beta

Finally.

Encryption has never been easier. Such important sponsors put my mind at ease now, I'm going to be using Let's Encrypt for my projects from now on.

Another favourite from Xcode 7.3

In previous versions of Xcode, if you were working with Swift and tried to override a var from a super-class, you had no auto-completion suggestions. Most if the time, I had to go into the super declaration to copy the signature and then just add override in front of it.

Xcode 7.3 changes that, for good.

Yay! 🎉

#selector

One of my favourite changes in Swift 2.2 is that Objective-C selectors now are first class citizens in the Swift ecosystem. So long to error-prone selector names as Strings.

Swift 2.2 lets you use the #selector expression to reference a selector from your Swift code.

class MyView: UIView {  
    private lazy var button: UIButton = {
        let b = UIButton()

        // Swift 2.1 and earlier
        b.addTarget(
            self,
            action: "buttonTapped:",
            forControlEvents: .TouchUpInside
        )

        // Swift 2.2
        b.addTarget(
            self,
            action: #selector(MyView.buttonTapped(_:)),
            forControlEvents: .TouchUpInside
        )
        return b
    }()

    func buttonTapped(sender: UIButton) {
        // ...
    }
}

My only complain about this is that the actions still need to be declared either internal or public, declaring the action as private returns the error Argument of '#selector' refers to a method that is not exposed to Objective-C, which makes sense if yout think about it.

EDIT: I should have read the error message more carefully.

Turns out that if you want your selector to be private, you only need to add the @objc attribute to the function declaration, and you're set:

extension Selector {  
    static let cancelButtonTapped = #selector(MyView.cancelButtonTapped)
}

class MyView: UIView {  
    @objc private func cancelButtonTapped() {
        // ...    
    }
}

On Leveling Up as a Developer

I just read this post from David Bryant Copeland at naildrivin5.com.

This resonated with me:

[...] This may seem like a lot, but these meetings are crucial. Having dedicated time with your manager every week is important. So is having a weekly time to talk with the people for whom you are building software about priorities. And if you aren’t part of these discussions, they are still going to happen, but they will lack your input. That could go bad for you.

I've been on both ends of the spectrum before. Neither of which is good, you have to strive to find a balance there.

How to use NSUserDefaults correctly

NSUserDefaults is a simple, yet powerful API available for us developers. It can assist us to create beautiful user experiences.

David Smith posted a comprehensive guide on how to correctly use it and what caveats to keep an eye open for.

How to get hired at a startup when you don't know anyone

Solid advice here.

I've interviewed with companies that made me do the whiteboard stuff as most companies do, and I've had interviews where they ask me to do something — prove that I can do what my resume is claiming. (These are the companies I've had the pleasure to work with.)

But Shane takes that to another level.

Sure, it helps if you're passionate about what the company you want to become part of does.

Major key: make them notice you.

How I deal with JSON in Swift

It seems that the new hot thing these days is open sourcing your Swift JSON parsing library.

Lyft, Big Nerd Ranch, Thoughtbot, to name a few, have their own drop-in solutions for handling JSON with Swift available for you to integrate in your projects. Other libraries like SwiftyJSON and json-swift, ObjectMapper are there too. All of them are great. All of them get the job done.

This post is not a tutorial on how to use those libraries, but just a description of how I deal with JSON in my Swift apps.

Disclaimer

I'm pretty biased. Personally, I think that handling JSON in Swift, while challenging at first (specially if you're new to the language), can be a really simple task.

Also, I don't use any of the libraries that I mentioned earlier. My method might seem a lot more verbose (and it is), but bear with me.

If you just want to parse some JSON now, stop reading and use any of the libraries linked earlier (I'd go with SwiftyJSON or Freddy, from BNR). If you want to learn how to handle JSON in Swift, read along.

Groundwork

How can you decode JSON in Swift with Cocoa natively?

do {  
    let json = try NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers)
} catch { }

At this point, the constant json's type is AnyObject. Typically, you'd cast json to a dictionary of some type (maybe an Array, depending on the API you're trying to consume):

do {  
    let json = try NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers) as? [String:AnyObject]
} catch { }

Its our job now to extract data from that dictionary, and make sure that we can build our Swift models in a type-safe manner.

Lets define what basic data types we can retrieve from a JSON structure:

  1. Ints
  2. Floats
  3. Strings
  4. Bools

Note: Nested objects and arrays are just arrangements of the basic data types.

Taking chapter from Chris Eidhof's approach, I defined the following helper functions:

func flatten<A>(x: A??) -> A? {  
    if let y = x { return y }
    return nil
}

infix operator >>>= {}  
func >>>= <A, B> (optional: A?, f: A -> B?) -> B? {  
    return flatten(optional.map(f))
}

Briefly:

  • The flatten(_:) function takes a double optional and removes one level of optional-ness.
  • The custom operator >>>= takes an optional of type A to the left, and a function that takes an A as a parameter and returns an optional B to the right. Basically, it says "apply."

These next functions retrieve data from JSON structures in a type-safe manner, and they're the building blocks for what I'm trying to do here.

func number(input: [NSObject:AnyObject], key: String) -> NSNumber? {  
    return input[key] >>>= { $0 as? NSNumber }
}

func int(input: [NSObject:AnyObject], key: String) -> Int? {  
    return number(input, key: key).map { $0.integerValue }
}

func float(input: [NSObject:AnyObject], key: String) -> Float? {  
    return number(input, key: key).map { $0.floatValue }
}

func double(input: [NSObject:AnyObject], key: String) -> Double? {  
    return number(input, key: key).map { $0.doubleValue }
}

func string(input: [String:AnyObject], key: String) -> String? {  
    return input[key] >>>= { $0 as? String }
}

func bool(input: [String:AnyObject], key: String) -> Bool? {  
    return number(input, key: key).map { $0.boolValue }
}

If we had the following JSON structure:

{
   name: "Oscar Swanros",
   age: 22
}

To get the age property, then:

guard let age = int(json, key: "age") else {  
    return
}

The fun part

I defined a single protocol JSONParselable:

protocol JSONParselable {  
    static func withJSON(json: [String:AnyObject]) -> Self?
}

My types that can be inflated from a JSON structure need to conform to this protocol. The implementation is rather simple.

Say I'm trying to consume an API with the following JSON response:

{
  id: 289928,
  title: "Freakonomics: A Rogue Economist Explores the Hidden Side of Everything",
  number_of_pages: 320,
  authors: [
      {
        name: "Steven D. Levitt",
        website_url: "http://pricetheory.uchicago.edu/levitt/home.html",
        twitter_url: "http://twitter.com/freakonomics"
      },
      {
        name: "Stephen J. Dubner",
        website_url: "http://stephenjdubner.com",
      }
  ],
  reviews: [
    {
      comment: "If Indiana Jones were an economist, he’d be Steven Levitt… Criticizing Freakonomics would be like criticizing a hot fudge sundae.",
      reviewer: "Wall Street Journal"
    },
    {
      comment: "The guy is interesting!",
      reviewer: "Washington Post Book World"
    },
    {
      comment: "Principles of economics are used to examine daily life in this fun read.",
      reviewer: "Great Reads"
    }
  ]
}

Working from the inside-out, these are the models that I'd define:

struct Review {  
    let comment: String
    let reviewer: String

    init(
        comment: String,
        reviewer: String
        ) {
            self.comment  = comment
            self.reviewer = reviewer
    }
}

struct Author {  
    let name: String
    let websiteURL: String
    let twitterURL: String?
    // I defined twitterURL as optional because, judging from the sample
    // JSON, it is a field that may or may not be present in
    // the response. Ideally, this would be well defined
    // on the documentation for the API I'm trying to consume.

    init(
        name: String,
        websiteURL: String,
        twitterURL: String? = nil
        ) {
            self.name       = name
            self.websiteURL = websiteURL
            self.twitterURL = twitterURL
    }
}

struct Book {  
    let id: Int
    let title: String
    let numberOfPages: Int
    let authors: [Author]
    let reviews: [Review]

    init(
        id: Int,
        title: String,
        numberOfPages: Int,
        authors: [Author],
        reviews: [Review]
        ) {
            self.id            = id
            self.title         = title
            self.numberOfPages = numberOfPages
            self.authors       = authors
            self.reviews       = reviews
    }
}

Now I have models that map directly to my API definition, including proper handling of values that may not be in the response at all. Now, on to implementing the JSONParselable protocol.

I'll do one of the inner models first:

// Review.swift
extension Review: JSONParselable {  
    static func withJSON(json: [String:AnyObject]) -> Review? {
        guard
            let comment = string(json, key: "comment"),
            reviewer    = string(json, key: "reviewer")
            else {
                return nil
                // A valid Review always has a comment and a
                // reviewer.
            }

        return Review(
            comment: comment,
            reviewer: reviewer
        )
    }
}

Then...

// Author.swift
extension Author: JSONParselable {  
    static func withJSON(json: [String:AnyObject]) -> Author? {
        guard
            let name   = string(json, key: "name"),
            websiteURL = string(json, key: "website_url")
            else {
                return nil
            }

        // Since the twitterURL property is optional,
        // I can just call string(_:key:) and pass that value to the
        // initializer. All good.
        return Author(
            name: name,
            websiteURL: websiteURL,
            twitterURL: string(json, key: "twitter_url")
        )
    }
}

Attention:

  1. Notice how the protocol implementation is done in an extension for each type. This to emphasize the notion that the actual type does not care about how it is created, as long as it is valid.
  2. Up until this point, I haven't dealt with nested objects.

Next, I need to parse the main Book object. Here's how I do it:

// Book.swift
extension Book {  
    static func withJSON(json: [String:AnyObject]) -> Book? {
        // #1
        guard
            let id        = int(json, key: "id"),
            title         = string(json, key: "title"),
            numberOfPages = int(json, key: "number_of_pages")
            else {
                return nil
            }

        // #2
        let authorsDicts = json["authors"] as? [[String:AnyObject]]
        let reviewsDicts = json["reviews"] as? [[String:AnyObject]]

        func sanitizedAuthors(dicts: [[String:AnyObject]]?) -> [Author] {
            guard let dicts = dicts else {
                return [Author]()
            }

            return dicts.flatMap { Author.withJSON($0) }
        }

        func sanitizedReviews(dicts: [[String:AnyObject]]?) -> [Review] {
            guard let dicts = dicts else {
                return [Review]()
            }

            return dicts.flatMap { Review.withJSON($0) }
        }

        // #3
        return Book(
            id: id,
            title: title,
            numberOfPages: numberOfPages,
            // #4
            authors: sanitizedAuthors(authorsDicts),
            reviews: sanitizedReviews(reviewsDicts)
        )
    }
}

Dissecting the last code listing:

  • #1: Make sure that the required data is in the JSON dictionary, and extract it accordingly.
  • #2: Extract the dictionaries for authors and reviews respectively.
  • #3: Return a new instance of Book passing the required data first.
  • #4: For authors and reviews, just .flatMap the array of dictionaries, and construct an array of only valid Authors and Reviews.

The final implementation would look like this:

// Omitting networking code for brevity

func dataTaskFinishedWithData(data: NSData) {  
    do {
        guard
            let json = try NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers) as? [String:AnyObject],
            book = Book.withJSON(json)
            else {
                return
        }

        // #1
        print(book)
    } catch {
        print(error)
    }
}

At this point (#1), I have a fully-packaged, type-safe, valid instance of Book, with nested models, even, not only a type-safe JSON type that I still have to extract data from. 🎉

Why do it this way

My method is (really) verbose, I know. But think about this few points before calling me crazy:

  • The end result using is not a "type-safe-parsed JSON object," as most of the libraries out there give you, but a fully type-safe, valid model instance.
  • While this may seem a lot of work, it lets you be more granular about how you inflate your models from data from a server.
  • My method, at least to me, makes it easier to translate API documentation to code.

Other highlights:

  • No custom operators to deal with. Yes, the >>>= operator was defined, but it is not required for the actual implementation of the JSON parsing (Argo, for instance, requires you to use custom operators to perform the actual parsing)
  • Modularity. Dealing with nested objects kinda solves itself. If you make every nested model conform to JSONParselable, you can just send the original JSON blob to the outermost object, and you're done.

Extra!

Did you notice that Author has two properties that their name makes you think they're URLs but are actually Strings?

Using my method, they can actually be NSURL objects!

struct Author {  
    let name: String
    let websiteURL: NSURL
    let twitterURL: NSURL?

    init(
        name: String,
        websiteURL: NSURL,
        twitterURL: NSURL? = nil
    ) {
        self.name       = name
        self.websiteURL = websiteURL
        self.twitterURL = twitterURL
    }
}

extension Author: JSONParselable {  
    static func withJSON(json: [String:AnyObject]) -> Author? {
        guard
            let name         = string(json, key: "name"),
            websiteURLString = string(json, key: "website_url"),
            websiteURL       = NSURL(string: websiteURLString)
            else {
                return nil
            }

        let twitterURLString = string(json, key: "twitter_url")

        return Author(
            name: name,
            websiteURL: websiteURL,
            twitterURL: NSURL(string: twitterURLString)
        )
    }
}

So, there's that.

Wrapping up

As I said at the beginning of this post, the JSON parsing libraries that are out there are really popular (because they work), and they can help you get from point A to point B really quick (if that's what you want).

My issue with those libraries is that they are too general-purpose for my taste. They are designed to work out of the box with any generic JSON response.

However, my issue is that my apps are not interacting with a generic API expecting generic JSON — they interact with a very specific API that outputs very specific JSON.

If my problem is really specific, why not solve it very specifically?

Have anything to add? Leave it in the comments section. You can reach me as @Swanros on Twitter.

Striving for excellence as a software developer

Josh Smith's post is really resonated with me. It is more on the technical side of things, but still a valuable resource.

There are basic common sense things about how to write good code that seem obvious once you know them, but are not so obvious to people who have not yet learned those lessons. Some excellent advice is repeated so often that it seems like a cliché to even mention.

Read it here.