Introducing Swish 1.0: Protocol Based Networking for iOS

Sid Raval

Swish 1.0, our Swift networking library, is out! Swish came to life through working on many client projects and writing more or less the same networking stack over and over. Our goals with Swish are:

  • Be able to test our networking layer.
  • Keep the networking layer self contained.
  • By default, decode JSON into domain objects via Argo.
  • If we want to do something different than decoding JSON, it should be as painless as possible.

And so Swish was born!

Reasonable Default Behavior

Our 90% use case is that we get JSON from an endpoint, and we want to decode it into a struct via Argo. Swish makes that easy. Let’s say you have an endpoint, GET https://www.example.com/comments/1, that returns the following JSON:

{
  "id": 1,
  "commentText": "Pretty good. Pret-ty pre-ty pre-ty good.",
  "username": "LarryDavid"
}

We’ll model this with the following struct, and implement Argo’s Decodable protocol to tell it how to deal with JSON:

import Argo
import Curry

struct Comment: Decodable {
  let id: Int
  let text: String
  let username: String

  static func decode(json: JSON) -> Decoded<Comment> {
    return curry(Comment.init)
      <^> j <| "id"
      <*> j <| "commentText"
      <*> j <| "username"
  }
}

(n.b. if you’re not familiar with the Curry library, check this blog post out)

We can model the request by defining a struct that implements Swish’s Request protocol:

struct CommentRequest: Request {
  typealias ResponseObject = Comment
  let id: Int

  func build() -> NSURLRequest {
    let url = NSURL(string: "https://www.example.com/comments/\(id)")!
    return NSURLRequest(URL: url)
  }
}

We can then use Swish’s default APIClient to make the request:

let request = CommentRequest(id: 1)

let dataTask = APIClient().performRequest(request) { (response: Result<Comment, SwishError>) in
  switch response {
    case let .Success(value):
      print("Here's the comment: \(value)")
    case let .Failure(error):
      print("Oh no, an error: \(error)")
  }
}

And that’s it!

Customize via Protocols

Swish’s defaults make decoding via Argo seamless, but if you want to customize, provide a different implementation for any of the following protocols:

  • RequestPerformer allows you to define how the NSURLRequest should be carried out
  • Deserializer allows you to process the NSData from the response however you wish
  • Parser allows to you convert the result of your deserialization into a Representation (e.g. JSON, by default)
  • Client allows you to define how the objects implementing the above protocols are linked together.

Checkout the Swish repository for more!