Active Filters: Libraries

Topics

#111: Reachability.swift 📡

Topics

Mobile devices can lose or change connectivity state suddenly and unexpectedly. It's important for us to make our apps respond well and degrade gracefully when this occurs.

Reachability describes how "reachable" the Internet is at a given time. Apple has long offered sample code for dealing with Reachability, but the process has always been a bit of a nuisance.

Today we'll look at a great little library from Ashley Mills called Reachability.swift that aims to make this less painful. Let's begin.

We'll start by trying to create a new Reachability object:

let reachability: Reachability

do {
  reachability = try Reachability.reachabilityForInternetConnection()
} catch {
  print("ERROR: Unable to create Reachability")
  assumeConnectivity()
}

Then we'll set some closures on it that will be called when the Reachability state changes. These will be called on a background queue, so we'll hop to the main queue before updating any UI.

reachability.whenReachable = { reachability in
  dispatch_async(dispatch_get_main_queue()) { /* TODO */ }
}

reachability.whenUnreachable = { reachability in
  dispatch_async(dispatch_get_main_queue()) { /* TODO */ }
}

Now, we can tell the object to start listening for connectivity changes:

do { try reachability.startNotifier() } catch {
  print("ERROR: Unable to start Reachability notifier")
  assumeConnectivity()
}

Then stop it later with:

reachability.stopNotifier()

It's important to consider if our app even needs this functionality.

If possible, we should try to avoid degrading the experience at all when connectivity goes away. If this isn't possible, for example if our app is streaming live video, we should respond to the change in Reachability, and tell the user why playback was interrupted.

Note: We looked at the simple closure syntax here, but Reachability.swift has great support for NSNotificationCenter notifications as well as Wifi vs. Cellular detection.

More info about Reachability.swift can be found at git.io/reachability

Topics

#106: EasyAnimation 🏃

Topics

Animation is one of the greatest parts about building (and using) iOS apps. The APIs however, can feel a bit scattered. UIView's animation functions are wonderful, but some animations require using Core Animation directly.

When they do, things get progressively more complex depending on if we need to run multiple animations in succession, or just run code after an animation completes.

Today we'll look at a great library from Marin Todorov called EasyAnimation that improves on all of this. Let's dive in:

EasyAnimation makes animating CALayers that normally would require CABasicAnimation (or one of its siblings) work with the standard UIView.animateWithDuration functions:

UIView.animateWithDuration(0.3, animations: {
  self.view.layer.position.y = 64.0
})

Under the hood, EasyAnimation does all the heavy lifting of translating our animations back into CAAnimation code and handling all of the implementation details for us. Neat!

Normally, if we wanted to run code after one the animations on a CALayer finished, we'd need to wire up an animation delegate, implement the callback functions, make sure to clean up after ourselves, etc.

With EasyAnimation though, we're able to just use the normal completion closure.

UIView.animateWithDuration(
  0.3,
  delay: 0.1,
  options: [.BeginFromCurrentState],
  animations: {
  self.view.layer.borderWidth = 2.0
  self.view.layer.cornerRadius = 12.0
}, completion: { finished in
  self.tableView.reloadData()
})

Last but certainly not least, EasyAnimation makes "chaining" multiple animations together (running one after another) extremely convenient. It also supports cancelling the chain, repeating, delays and more:

let chain = UIView.animateAndChainWithDuration(0.3, animations: {
  self.avatarView.center = headerView.center
}).animateWithDuration(0.2, animations: {
  self.headerView.alpha = 1.0
})

More info about EasyAnimation can be found at git.io/easyanimation

Continuing where we left off in Bite #93 with our Alamofire Router, today we'll take a look at creating a Custom Response Serializer.

These are the mechanisms through which the Alamofire HTTP library translates the HTTP responses it receives into types that are more friendly to work with.

Alamofire ships with a few built-in serializers for common things like raw data, text, JSON and property lists. Today we'll be taking a look at adding our own. Let's get started.

We want to make it super easy to translate Alamofire responses into structs that conform to the Decodable protocol from the Decodable JSON parsing library. (Bite #86).

extension Alamofire.Request {
  public func responseCollection<T: Decodable>(completionHandler: Response<[T], NSError> -> Void) -> Self {
    let responseSerializer = ResponseSerializer<[T], NSError> { request, response, data, error in

      guard error == nil else { return .Failure(error!) }

      let result = Alamofire
        .Request
        .JSONResponseSerializer(options: .AllowFragments)
        .serializeResponse(request, response, data, error)

      switch result {
      case .Success(let value):
        do {
          return .Success(try [T].decode(value))
        } catch {
          return .Failure(Error.errorWithCode(.JSONSerializationFailed,
            failureReason: "JSON parsing error, JSON: \(value)"))
        }
      case .Failure(let error): return.Failure(error)
      }
    }

    return response(responseSerializer: responseSerializer, completionHandler: completionHandler)
  }
}

First we'll extend Request and add a new generic responseCollection function that returns a Response struct.

Notice that in the generic part of the function definition we allow any type T that confirms to the Decodable protocol.

Now the fun part. We'll first serialize the response using the built-in JSON response serializer, then switch on its result, returning the failure case if it exists.

If the JSON was parsed successfully, we'll try to decode using Decodable.

Now we can try both our Router and our new custom response serializer out together:

Alamofire.request(LBOC.Router.Bites)
  .responseCollection { (response: Response<[LBOC.Bite], NSError>) in
    guard response.result.error == nil else { return }

    let bites = response.result.value

    print(bites)
}

The responseCollection function handles arrays of objects. Making a responseObject serializer function to handle single object responses is almost identical. Simply replace all occurrences of [T] with T.

Alamofire 3.0 will be released soon so now is a good time to start looking at how best to build an API client for our app. We'll be covering different areas of this topic periodically over the next few weeks.

Today we'll start by looking at one part of the equation: routing.

We've talked about routing before in the context of iOS URL schemes and libraries like JLRoutes (Bite #62). This is a bit different. We'll be creating a Router type that will help us unify and simplify our code when we make requests by generating URL requests from enum cases.

enum Router: URLRequestConvertible {
  static let baseURL = NSURL(string: "https://littlebitesofcocoa.com")!

  case Bites
  case Bite(Int)
  case BitesTagged(Int)

  var URL: NSURL { return Router.baseURL.URLByAppendingPathComponent(route.path) }

  var route: (path: String, parameters: [String : AnyObject]?) {
    switch self {
      case .Bites: return ("/", nil)
      case .Bite (let biteID): return ("/\(biteID)", nil)
      case .BitesTagged(let tagID): return ("/", ["t": tagID])
    }
  }

  var URLRequest: NSMutableURLRequest {
    return Alamofire
      .ParameterEncoding
      .URL
      .encode(NSURLRequest(URL: URL), parameters: (route.parameters ?? [ : ])).0
  }
}

We'll start by declaring a new enum type that conforms to the URLRequestConvertable protocol (which comes from Alamofire). Then we'll define a few different cases that each correspond to an endpoint on our HTTP API. We'll add a computed route property that returns a tuple to translate from our enum cases into URL path and parameter values.

Finally, we'll finish conforming to URLRequestCovertable by adding a computed property that encodes our URL and parameters into an NSMutableURLRequest. Neat.

Now we can use our new Router to write some super clean Alamofire request code like this:

Alamofire.request(Router.Bites)
  .responseObjects { (response: Response<[Bite], NSError>) in
    let bites = response.result.value
    print(bites)
  }

The responseObjects custom response serializer converts JSON to our own custom struct types. We'll look at how to create it in the near future.

We can also access raw URL requests like this:

let request = Router.Bite(93).URLRequest

Topics

#86: Decodable 🔩

Topics

We continue our look at frameworks that map JSON into model types today with Decodable by Johannes Lund. Decodable is another fantastic solution for this task. It takes advantage of Swift 2's new error handling functionality, and unlike ObjectMapper (covered in Bite #84), the properties on your models don't need to be optionals. Let's take a closer look:

To get things wired up we just need to implement the Decodable protocol on our model types, like so:

struct Spaceship {
  var captain: User
  var topSpeed: Double
}

extension Spaceship: Decodable {
  static func decode(j: AnyObject) throws -> Spaceship {
    return try Spaceship(
      captain: j => "captain",
      topSpeed: j => "topSpeed"
    )
  }
}

Then we can convert from JSON to one of these structs like this:

do {
  let ship = try Spaceship.decode(json)
} catch let error { print(error) }

We can decode JSON arrays like this:

do {
  let ships = try [Spaceship].decode(array)
} catch let error { print(error) }

Decodable also handles nested types and is quite flexible. For example, we aren't even forced to use Decodable on every type. Here we bypass Decodable a bit and instantiate a value for our rank property manually:

extension User: Decodable {
  static func decode(j: AnyObject) throws -> User {
    return try User(name: j => "name", rank: Rank(name: j => "rank"))
  }
}

Some other noteworth Decodable features are its wonderful printable errors, and how easy it is to add custom decode functions for things like parsing custom date formats, etc.

More info about Decodable can be found at git.io/decodable

Topics

#84: ObjectMapper 📲

Topics

There are plenty (no really, plenty) of options when it comes to parsing JSON into model objects on iOS. We'll be taking a look at some of them from time to time over the coming weeks. First up is ObjectMapper by Hearst. Let's take a look how it works with a fictional set of types:

With ObjectMapper, we implement the Mappable protocol on our types to support converting to and from JSON:

struct Spaceship: Mappable {
  var captain: User?
  var topSpeed: Double?

  init?(_ map: Map) { }

  mutating func mapping(map: Map) {
    captain  <- map["captain"]
    topSpeed <- map["topSpeed"]
  }
}

struct User: Mappable {
  var name: String?
  var rank: Int?

  init?(_ map: Map) { }

  mutating func mapping(map: Map) {
    name <- map["name"]
    rank  <- map["rank"]
  }
}

Then we can convert from JSON to one of these structs like this:

let ship = Mapper<Spaceship>().map(JSONString)

And go the other way (from a struct back to JSON) like this:

let JSONString = Mapper().toJSONString(ship, prettyPrint: true)

ObjectMapper can easily handle nested objects, here on our Spaceship model, we've got an optional User property for the captain of the ship.

It also supports subclasses and custom transforms when serializing/deserializing properties. One of the best things about ObjectMapper are the extensions available for other great iOS libraries like Alamofire and Realm (covered in Bite #49). Here's AlamofireObjectMapper in action:

Alamofire.request(.GET, "https://api.jeditemple.com/ships/31", parameters: nil)
  .responseObject { (response: Spaceship?, error: ErrorType?) in
    print(response?.topSpeed)
  }

ObjectMapper stands out in the simplicity of it's API and the ease at which we can jump in and start using it.

More info about ObjectMapper can be found at git.io/objectmapper

Topics

#81: RateLimit 🏁

Topics

RateLimit is great library from Sam Soffes. Its purpose is extremely simple, but powerful.

Let's dive in.

It's quite common to want to limit certain tasks by frequency. For instance, consider a feature that loads search suggestions as a user types characters into a text field. We'd probably want to throttle that to make sure we're not calling some HTTP API too often. Let's take a look at how to do this with RateLimit:

func updateSearchResultsForSearchController(searchController: UISearchController) {
  RateLimit.execute(name: "search-suggestions", limit: 0.5) {
    API.loadSearchSuggestions(searchController.searchBar.text)
  }
}

We give our rate-limited task a name and a limit, then pass in a closure to be limited. Neat.

Sam offers another great example: Loading new data for a view controller inside viewDidAppear. Imagine if that view controller lived inside a tab bar controller and the user decided to switch back and forth between tabs rapidly.

There'd be no reason to check for new data each time the view was shown, but we would like to check if enough time had passed since the last check.

override func viewDidAppear(animated: Bool) {
  super.viewDidAppear(animated)

  RateLimit.execute(name: "load-posts", limit: 60.0) {
    API.loadNewPosts()
  }
}

One last example: Performance. Imagine we were performing some complicated task in a loop, and wanted to update a UIProgressBar to reflect its progress.

We might not want to update the UIProgressBar every single iteration. To make our UI nice and responsive, we might limit the updates to only once every 500 milliseconds or so.

Note: By default RateLimit doesn't persist our limits across app launches. If we do need this behavior, we can just replace RateLimit with PersistentRateLimit.

More info about RateLimit can be found at git.io/ratelimit

Topics

#75: Locksmith 🔐

Topics

Locksmith is a library from Matthew Palmer that really improves how we can interact with the Keychain. Let's look at the basics:

Reading Data

let dictionary = Locksmith.loadDataForUserAccount("kyloren")

Writing Data

do {
  try Locksmith.saveData(
    ["saberColor": "#FF0000"],
    forUserAccount: "kyloren"
  )
} catch { }

Updating Data

do {
  try Locksmith.updateData(
    ["saberColor": "#30FF00"],
    forUserAccount: "kyloren"
  )
} catch { }

Deleting Data

do {
  try Locksmith.deleteDataForUserAccount("kyloren")
} catch { }

That's just the beginning though. Locksmith supports almost every feature Keychain has to offer. It also embraces protocols, and protocol extensions. Let's look at an example of how to improve our own types, making them easier to store in the Keychain.

Let's say we have a simple struct in our app to represent user accounts. We'll implement a couple of protocols from Locksmith on it:

struct JediTempleAccount: GenericPasswordSecureStorable,
  CreateableSecureStorable, ReadableSecureStorable,
  DeleteableSecureStorable {

  let username: String, password: String

  let service = "jedi-temple"
  var account: String { return username }
  var data: [String: AnyObject] { return ["password": password] }
}

Now we can work with accounts and the Keychain like this:

let account = JediTempleAccount(username: "kyloren", password: "yaysith")

do { try account.createInSecureStore() } catch { } // save
let result = account.readFromSecureStore() // retrieve
do { try account.deleteFromSecureStore() } catch { } // delete

More info about Locksmith can be found at git.io/locksmith

Topics

#74: Static 📺

Topics

Static is a library from Venmo that makes it very simple to create simple static-content table views. Let's take a look at how to use it:

The basic idea of Static is that we'll configure what is displayed in our table view by supplying Section and Row objects to a DataSource object:

class SettingsViewController: TableViewController {
    override func viewDidLoad() {
    super.viewDidLoad()

    dataSource.sections = [
      Section(rows: [
        Row(text: "Jake Marsh", detailText: "@jakemarsh",
          cellClass: SubtitleCell.self,
          selection: { [unowned self] in
            // edit profile
          }
        ),
        Row(
          text: "Cellular Downloads",
          accessory: .View(cellularDownloadsSwitch)
        )],
        footer: .Title("Version: \(appVersion)")
      )
    ]
  }
}

Custom accessories are simple to implement with Static:

Row(text: "Enable Hyperdrive", accessory: .DetailButton({
  // configure hyperdrive settings
})),

Note how we not only give the cell a detail button, but we pass in a closure to be run when the detail button is tapped. We can even use a custom UIView as an accessory like we did with the switch in our first example.

We used a simple string as our footer here, but we can easily use a view instead:

Section(rows: [], footer: .View(footerView)).

Static does a great job separating concerns. Rows don't know anything about the actual UITableViewCells that eventually get shown to represent them. Static uses the .Value1 cell type by default and comes with a few other standard cell types. We can easily add our own by implementing the CellType protocol on any of our own UITableViewCell subclasses.

More info about Static can be found at git.io/statictv

Topics

#62: JLRoutes 🔗

Topics

JLRoutes is a library from Joel Levin that makes it very easy to manage "routes" for URL schemes in an app. It's common to need to "deep link" in to an app. Other apps can use these to open your app to a specific spot, or we can use them within our own app such as when the user opens a push notification. Let's take a look.

First we'll add a new URL Type for our app. In Xcode, we'll go to our project, then target settings, then to the Info tab, and finally to the URL Types section.

Neat, now URLs in the form of spaceships://something will open our app! Now let's add our first route.

These can added anywhere, but it's best to set them up early, when the app has first launched.

JLRoutes.addRoute("/spaceships/:id") { (params: [NSObject : AnyObject]!) -> Bool in
  guard let spaceshipID = params["id"] as? String else { return false }

  let vc = SpaceshipViewController()      
  vc.spaceshipID = spaceshipID

  self.currentNavigationController().pushViewController(vc, animated: true)

  return true
}

A few things to note here. First notice the :id portion of the route path. This lets us easily extract out portions of the route from the params dictionary.

Also note how we use guard and return false if we can't handle the URL for some reason.

In addition, JLRoutes has support for wildcard matching as well as route namespaces.

More info about JLRoutes can be found at git.io/routes

Page 8 of 10