Handling routes from an API in a type-safe way with Swift

I’m working on an application that has to handle some kind of routing directions coming from an API in the following form:

{
    "title": "Tap to go to your profile!",
    "destination": "profile"
}

These items would show in a form of timeline of events, that when tapped, would take you to specific parts of the application.

I was thinking about how I could leverage Swift’s type-safety to make sure the application validates the destinations for each of these items and make a more robust solution for this.

Immediately, enums came to mind. I can easily define an enumeration with raw values:

enum Destination: String {
    case profile
    case info
    case feed
}

But then, I looked closely at the API’s documentation, and noticed that some destinations could come with attached metadata:

{
    "title": "Tap to go to Oscar's profile!",
    "destination": "profile:859928508374784884"
}

As you can probably guess, tapping on the item above should take you to the profile screen for the user with id 859928508374784884. I thought of enums with associated values, but that wouldn’t cut it. Associated values in enumerations prevent me from inflating instances from raw values.

I decided to come up with my alternative for enumerations to handle this specific case so I could build destination instances in a type-safe manner directly from the JSON objects coming from the API.


First, I started with a structure. Since I’m interested in being able to convert a raw string to an instance of my type and viceversa, RawRepresentable is the protocol to adopt.

struct Destination: RawRepresentable {
    private let _identifier: String
    
    var rawValue: String {
        return _identifier
    }
    
    init?(rawValue: String) {
        _identifier = rawValue
    }
}

Now, I need to check whether the raw value passed to the initializer is a valid one. For that, I defined a string set that contained all the possible identifiers for a Destination, and check for that on the failable initializer from RawRepresentable.

struct Destination: RawRepresentable {
    // ...
    
    private static let options: Set<String> = [
        "info",
        "profile",
        "feed",
        "event"
    ]
    
    init?(rawValue: String) {
        guard Destination.options.contains(rawValue) else {
            return nil
        }
        
        _identifier = rawValue
    }
}

The only thing missing right now would be handling the attached metadata for the available destinations.

For that, I added a value property that would hold the destination’s metadata (if any), and a couple more checks to the the initializer:

struct Destination: RawRepresentable {
    // ...
    var value: String?
    
    public var rawValue: String {
        return _identifier + (value != nil ? ":(value!)" : "")
    }
    
    init?(rawValue: String) {
        // Split the raw value
        let ids = rawValue.split(separator: ":")
            
        // Check that the identifier exists
        guard let identifier = ids.first else {
            return nil
        }
            
        // Convert from String.SubSequence
        let id = String(identifier)
        
        // Make sure this identifier is valid
        guard Destination.options.contains(id) else {
            return nil
        }
            
        _identifier = id
        
        // If metadata exists, add that metadata to value
        if ids.count == 2 {
            value = String(ids[1])
        }
    }
}

Now, conform with Codable so that Destination can be parsed from and to raw data:

struct Destination: RawRepresentable {
    // ...
    enum DestinationError: Error {
        case unsupportedDestination
        case wrongFormat
    }
    
    enum CodingKeys: String, CodingKey {
        case _identifier = "destination"
    }
    
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        
        try container.encode(_identifier, forKey: ._identifier)
    }
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let rawValue = try container.decode(String.self, forKey: ._identifier)
        let ids = rawValue.split(separator: ":")
        
        guard let identifier = ids.first else {
            throw DestinationError.wrongFormat
        }
        
        let id = String(identifier)
        
        guard Destination.options.contains(id) else {
            throw DestinationError.unsupportedDestination
        }
        
        _identifier = id
        
        if ids.count == 2 {
            value = String(ids[1])
        }
    }
}

Unfortunately, I coulnd’t find an elegant way to extract the initialization checks to another method (because scope initialization, etc), so the initializer from RawRepresentable has to be basically duplicated on the one from Decodable.

One last step would be making Destination conform with Hashable:

struct Destination: RawRepresentable, Codable, Hashable {
    // ...
    var hashValue: Int {
        return _identifier.hashValue
    }
}

func ==(lhs: Destination, rhs: Destination) -> Bool {
    return lhs.hashValue == rhs.hashValue
}

Note that hashValue takes only into account the identifier for the destination, not it’s attached value. That way, profile:859928508374784884 and a profile are considered equal, even though the first one has attached metadata.

This is super specific in this case, because I only care about the route and the metadata is treated as optional. However, in your own implementation, taking value into consideration for Hashable might make sense.

And that’s it. A quick hack would be to add static properties to Destination with prebuilt values so that they can be treated as enumeration cases:

struct Destination: RawRepresentable {
    static let info = Destination(rawValue: "info")!
    static let profile = Destination(rawValue: "profile")!
    static let feed = Destination(rawValue: "feed")!
    static let event = Destination(rawValue: "event")!
    
    static func profile(value: String) -> Destination? {
        return Destination.rawValue("profile:(value)")
    }
}

Here’s how all that comes together:

let json = ["destination": "profile:859928508374784884"]
let jsonData = try! JSONSerialization.data(withJSONObject: json, options: .prettyPrinted)

let decoder = JSONDecoder()
let parsedDestination = try? decoder.decode(Destination.self, from: jsonData)

let simpleDestination = Destination.profile
let profileDestination = Destination.profile(value: "859928508374784884")!

parsedDestination == profileDestination // #=> true

And of course, you can switch on Destination values as well:

switch parsedDestination! {
case .profile: 
    router.navigateToProfile(parsedDestination)
    
default:
    // Handle other cases.
}

I thought this could make for a nice example on how to leverage Swift’s flexibility to help when the default data structures are not compatible with our use cases.

If you have any comments or idea, or I missed something, feel free to ping me on Twitter @Swanros.

Thanks for reading!

Becoming a Better iOS Developer Through Tooling: My AltConf 2017 talk

The fine folks at Realm have now uploaded the remaining videos from AltConf 2017 — including the presentation that I gave there.

You can watch it a Realm Academy, here.

Presenting in the US for the first time was a really exciting experience for me — and something that I’m going to push myself to do more of. I really loved the rush of being there presenting in a foreign country on a foreign language.

I’m gonna call this one a victory — although I know there’s some stuff in the presentation that could’ve used a little more polishing, I’m quite happy with how everything turned out.

What’s nice about this, is that I can now watch myself with a calm head and take notes of things that I can improve for future conference talks in the US or around the world.

I’d really appreciate any constructive feedback you share with me about this presentation. Hit me up at oscar@swanros.com, or @Swanros on Twitter.

Apple starts rejecting apps with “hot code push” features

This thread from Apple’s Developer Forums is making some waves on the internets today. Apparently, they’re now rejecting apps that include code that can modify your app after it’s released to the store.

Rollout.io seems to be the SDK that’s setting the alarm off, so far.

If you use Rollout.io, or another “push code after you release to the store” service, it’s time to start thinking about changing your strategy. (clears throat I told you).

From Rollout.io‘s FAQ:

Does Rollout comply to Apple’s Guidelines?

Yes. As per Apple’s official guidelines, Rollout.io does NOT alter binaries. Rollout uses JavascriptCore to add logic to your patches. For more details, check out how Rollout is compliant with App Store guidelines.

I wouldn’t build my business on such a fine line.

And also, please don’t act surprised when Apple start rejecting React Native apps down the road.

The moral of the story: stay away from JavaScript.

Bringing piping syntax from Elixir to Swift

I’ve been writing Elixir for my main backend needs for the past few months and I’ve really liked it — specially its syntax. In fact, I’ve gotten so into its syntax that I’ve often found myself trying to write Swift as if it were Elixir.

Oops!

So I decided to experiment a little bit with bringing my favourite syntax from Elixir over to Swift natively.

Disclaimer: This is just an experiment. To get this to work on an actual project, a lot more work needs be done.


Elixir is a functional language that’s gained a lot of traction in the past few months thanks to its simplicity and core features, such as the supervision tree.

Every type in Elixir is a value, which means that it gets copied when passed to a function — the function gets a new copy of the value, which it can then modify without affecting the original instance.

> aMap = %{a: 1}
  %{a: 1}

> bMap = Map.update(aMap, :a, 0, &(&1 * 3))
  %{a: 3}

> aMap 
  %{a: 1}

> bMap
  %{a: 3}

Also, Elixir has this great “pipe” operator that lets you write cleaner code and streamline data manipulation in a really easy way:

> cMap = aMap |> Map.update(:a, 0, &(&1 / 3))

The “pipe” (|> ) operator takes the value on its left and passes it to the function on the right as its first parameter. This is included as part of the language and works with any function.

Let’s bring that to Swift!

In this example, I’m going to use two simple structs (value types in Swift):

struct Company {
    let name: String
}

struct Person {
    let name: String
    let work: Company
}

let apple = Company(name: "")
let joe = Person(name: "Joe", work: apple)

A characteristic of functional programming languages is that they strive for immutability. Vaues should not mutate during program execution, but copying values and modifying them is good.

Tipically, if we wanted to update joe‘s name, we’d do something like

joe.name = "Joe Wood"

If we did this, the Swift compiler would raise two errors:

  1. the name property on Person is declared as a constant (let), which means it can’t mutate during execution — a change to var is suggested.
  2. the joe instance was declared as a constant (let), which means it can’t mutate during execution — a change to var is suggested.

What we don’t want, though, is enable the ground to shift under our feet, so let’s keep everything as a constant.

Another approach would be to create a function that did so:

extension Person {
  mutating func update(name: String) {
    self.name = newName
  }
}

But again, that mutating keyword there is a code-smell in the functional world. The way to do this the functional way is:

extension Person {
  func update(name: String) -> Person {
    return Person(name: newName, work: self.work)
  }
}

We created a function that changes only one property on the instance — it receives the new value for the property, and returns a whole new instance of the type with that value replacing the original one.

joe.update(name: "Joe Wood").name // Joe Wood
joe.name                          // Joe

Now, the problem with this is that the function that we created is operating on the type-instance.

Note: In Elixir, a map literal can be written as %{a: 1}, but to transform and operate on that instance, we have to use the Map module. That’s kind of what we want to achieve here.

We want that the Person struct (or module, if you’re thinking in Elixir) to have a series of methods that receive Person instances to operate with.

In order to achieve that, we need to do a bit of groundwork.

Lenses

There is a concept in functional programming called “data lenses”. Explaining in detail what a data lens is, is way out of scope of this post, but you can read more about it here and here, and generally, just searching “lenses functional programming” in any search engine.

The idea is to have a data type (a Lens) that is generic and lets us get and set (a.k.a. zoom to) information on the type that the Lens is generic to.

Example:

struct Lens<Whole, Part> {
  let get: (Whole) -> Part
  let set: (Whole, Part) -> Whole
}

let personNameLens = Lens<Person, String> (
  get: { $0.name },
  set: { Person(name: $1, work: $0.work) }
)

personNameLens.get(joe)                  // Joe
personNameLens.set(joe, "Joe Wood").name // Joe Wood

That’s a Lens. However, we’ll only need a subset here, so let’s define a new data type that gives us only the set method:

struct SetLens<Whole, Part> {
  let set: (Whole, Part) -> Whole
}

To update a Person instance, we can do this now:

let setPersonNameLens = SetLens<Person, String> (
  set: { Person(name: $1, work: $0.work) }
}

setPersonNameLens.set(joe, "Joe Wood")

Now lets do a bit of work to move the interface up to type-level by making our SetLens a static property of Person, for brevity:

extension Person {
  static let SetName = SetLens<Person, String> (
    set: { Person(name: $1, work: $0.work) }
  ) 
}

Person.SetName.set(joe, "Joe Wood")

We still have the problem that we’d have to interact with the SetLens directly. To mimic the Elixir behaviour, we need to wrap the SetName property on a static function that returns a function that when called, calls the set method on the lens.

extension Person {
  static func Update(name: String) -> Person -> Person {
    return { p in SetName.set(p, newName) }
  }
}

We define a static function that takes a new name as a parameter, and returns a function that takes a Person and returns a Person. Calling this function just passes the values to the SetLens.

Now, let’s introduce the pipe operator:

precedencegroup Applicable {
    associativity: left
}
infix operator |>
func |> <A, B>(a: A, rhs: (A) -> B) -> B {
    return rhs(a)
}

This new custom operator takes a value to the left and a function on the right, and just calls the function on the right passing the value on the left. How convenient!

We can now do this:

joe |> Person.UpdateName("Joe Wood")

Ta’da!

Doing the appropriate amount of work, we could be writing this really soon:

joe
  |> Person.Update(name: "Joe Wood")
  |> Person.Update(work: Company(name: "Apple"))

And voilá, we’ve migrated one of Elixir’s core features to Swift, while maintaining type-safety!

Final comments

Admittedly, it took a long of work just to migrate this one feature over to Swift. This does not mean, though, that it can’t be done for big code-bases and “real apps.” One of the best features of Swift is that the compiler is real smart and can tell us on compile-time if we messed something up. ❤️

This was just a small exercise, but I do appreciate the similarities that Swift and Elixir have, and the fact that both languages are flexbile enough that we can do these sort of things. Pretty cool.


Hey! I’m writing a book on best practices for iOS development with Swift, and it’s called (drum roll) Best Practices for Developing iOS Apps with Swift.

Subscribe below to get a 50% off coupon code when the book is released — also get the first chapter for free when it is ready!

#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 fileprivate func cancelButtonTapped() {
        // ...    
    }
}

EDIT: In Swift 3.0, the function needs to be set to fileprivate in order to be available from outside its scope.

How I deal with JSON in Swift

Edit: I’ve updated this post to Swift 3’s syntax. The original post with Swift 2.X syntax can be found here.


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 us to integrate in our projects. Other libraries like SwiftyJSON and json-swift, ObjectMapper are there for us, 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.

Before we start

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 that can be overworked.

Also, I don’t use any of the libraries that I mentioned earlier to parse JSON in any of my apps. My method, which I’m about to show you, might seem a lot more verbose (hint hint it is) — but bear with me.

That said, if you just want to parse some JSON right 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.


As a side note: **[I’m writing a book](http://iosbestpractices.com)** — subscribe now to get a **50% off discount code** when the book launches late October.


Groundwork

Is there a way to decode JSON using native APIs on Cocoa/CocoaTouch? Yep:

do {
    let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers)
} catch { }

At this point, the constant json‘s type is Any. 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:Any]
} catch { }

Its our job now to extract data from that dictionary, and make sure 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 a chapter from Chris Eidhof’s approach, let’s define the following helper functions (updated for Swift 3, with the new operator declaration syntax and @discardableResult):

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

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

To explain briefly:

  • The flatten(x:) 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.”

Then, by defining these next methods on a Dictionary extension, we can extract data from JSON structures in a type-safe manner. These are the building blocks of what I’m trying to do here.

extension Dictionary where Key: ExpressibleByStringLiteral, Value: Any {
    public func p3_number(key: Key) -> NSNumber? {
        return self[key] >>>= { $0 as? NSNumber }
    }
    
    public func p3_int(key: Key) -> Int? {
        return self.p3_number(key: key).map { $0.intValue }
    }
    
    public func p3_float(key: Key) -> Float? {
        return self.p3_number(key: key).map { $0.floatValue }
    }
    
    public func p3_double(key: Key) -> Double? {
        return self.p3_number(key: key).map { $0.doubleValue }
    }
    
    public func p3_string(key: Key) -> String? {
        return self[key] >>>= { $0 as? String }
    }
    
    public func p3_bool(key: Key) -> Bool? {
        return self.p3_number(key: key).map { $0.boolValue }
    }
}

If we had the following JSON structure:

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

To get the age property, we can now do:

guard let age = json.p3_int(key: "age") else {
	return
}

A lot cleaner than having to cast each member every time.

The fun part

The next step is to define a single protocol named JSONParselable:

public protocol JSONParselable {
    static func with(json: [String:Any]) -> Self?
}

JSONParselable basically indicates that a given type can be inflated using JSON data. It requires a static function on the type that returns an instance of itself.

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

Say we have 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 with(json: [String:Any]) -> Review? {
        guard
            let comment = json.p3_string(key: "comment"),
            reviewer    = json.p3_string(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 with(json: [String:Any]) -> Author? {
        guard
            let name   = json.p3_string(key: "name"),
            websiteURL = json.p3_string(key: "website_url")
            else {
                return nil
            }

        // Since the twitterURL property is optional,
        // I can just call json.p3_string(key:) and pass that value to the
        // initializer. All good.
        return Author(
            name: name,
            websiteURL: websiteURL,
            twitterURL: json.p3_string(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. (Apple recommends another approach, which I’m not in favor of. Read my comments here.
  2. Up until this point, I haven’t touched nested objects at all.

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

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

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

        func sanitizedAuthors(dicts: [[String:Any]]?) -> [Author] {
            guard let dicts = dicts else {
                return [Author]()
            }
            
            return dicts.flatMap { Author.with($0) }
        }
        
        func sanitizedReviews(dicts: [[String:Any]]?) -> [Review] {
            guard let dicts = dicts else {
                return [Review]()
            }
            
            return dicts.flatMap { Review.with($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 dataTaskFinished(data: Data) {
    do {
        guard
            let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String:Any],
            let book = Book.with(json: 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 you call me crazy:

  • The end result 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:

  • There are 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 URL objects!

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

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

extension Author: JSONParselable {
    static func with(json: [String:Any]) -> Author? {
        guard
            let name	          = json.p3_string(key: "name"),
            let websiteURLString	= json.p3_string(key: "website_url"),
            let websiteURL      	= URL(string: websiteURLString)
            else {
                return nil
            }

        let twitterURLString = json.p3_string(key: "twitter_url")

        return Author(
            name: name,
            websiteURL: websiteURL,
            twitterURL: URL(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? Let me know. You can reach me as @Swanros on Twitter.

Swift blog: Keeping up with the Swift mailing lists

It can be a pain. Specially if you’re subscribed to all of them.

Thankfully, there’s an app for that. 🙄

Hirundo is a dedicated RSS reader for the Swift mailing lists. Read comfortably, keep up with discussions, bookmark conversations.

Just download it now.

Swift blog: Watching the language evolve

Now that Swift is open source, we have a lot of visibility inside the project. That includes new features and discussions that can help us be prepared for new features or important changes on the language itself.

If you go over to https://github.com/apple/swift-evolution/tree/master/proposals you can get a pretty good idea where the language is headed to, and, as Daniel Jalkut mentioned, potentially help you overcome bad habits and save you a ton of time writing code that you’ll have to change later.

I’d keep an eye on that page and read each proposal thoroughly. These people are very smart and they know what they’re talking about — I’m sure I can learn a lot from them and their reasoning behind their proposals.

My favourite so far: Removing currying func declaration syntax