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:
letreachability:Reachabilitydo{reachability=tryReachability.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={reachabilityindispatch_async(dispatch_get_main_queue()){/* TODO */}}reachability.whenUnreachable={reachabilityindispatch_async(dispatch_get_main_queue()){/* TODO */}}
Now, we can tell the object to start listening for connectivity changes:
do{tryreachability.startNotifier()}catch{print("ERROR: Unable to start Reachability notifier")assumeConnectivity()}
Then stop it later with:
reachability.stopNotifier()
It's important to consider if our appeven 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 NSNotificationCenternotifications as well as Wifi vs. Cellular detection.
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.animateWithDurationfunctions:
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.
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:
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 Alamofireresponses into structs that conform to the Decodableprotocol from the Decodable JSON parsing library. (Bite #86).
First we'll extend Request and add a new genericresponseCollectionfunction 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 Routerand our new custom response serializer out together:
The responseCollectionfunction handles arrays of objects. Making a responseObjectserializer 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.
We'll start by declaring a new enum type that conforms to the URLRequestConvertableprotocol (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 Alamofirerequest code like this:
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 Decodableprotocol on our model types, like so:
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 rankproperty manually:
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.
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 Mappableprotocol on our types to support converting to and from JSON:
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:
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:
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.
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.
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:
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.
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.
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