Active Filters: Libraries

Today we'll look at a fun and interesting library that can help improve how users navigate around in our apps.

It comes to us from Taiki Suzuki and it's called SAHistoryNavigationViewController.

It works by augmenting UINavigationController's traditional interactions to add a gesture to view each of the view controller's that have been pushed on to it. We can use it just like a regular navigation controller:

let vc = SpaceshipsViewController()
let nc = SAHistoryNavigationViewController(rootViewController: vc)

presentViewController(nc, animated: true, completion: nil)

The library will handle showing the history scroll view when the user long presses the back button. If the user's device supports 3D Touch, the history scroll view activate with that gesture instead.

We can also trigger the history view to be shown at anytime like this:

navigationController?.showHistory()

We can customize the background color shown behind all the “zoomed out” view controller easily:

navigationController?.setHistoryBackgroundColor(.blackColor())

We can also customize the background view by subclassing, and overriding the contentView function.

More info about SAHistoryNavigationViewController can be found at git.io/historync

Today we'll check out a library called Shoyu that aims to make setting up UITableViews a breeze. Let's dive in:

Shoyu works as a sort of DSL. It adds a source property on to UITableView, then gives as a few functions like createSection and createRow to fill the table view with content. Let's see what it takes to add a section and a row:

tableView.source = Source() { source in
  source.createSection { section in
    section.createRow { (row: Row<SpaceshipTableViewCell>) in
      row.reuseIdentifier = "SpaceshipCell"
      row.height = 52
      row.configureCell = { cell, _ in cell.nameLabel.text = "Tantive IV" }
    }
  }
}

We can use a similar technique to create and attach headers and footers to sections:

section.createHeader {
  $0.reuseIdentifier = "SpaceshipHeader"
  $0.height = 120
  $0.configureView = { view, _ in
    view.backgroundColor = .blackColor()
  }
}

There's also a few other functions to help us do things like dynamically set a row's height, or run some code when a row is selected:

section.createRow { row in      
  row.didSelect = { _ in /**/ }
  row.heightFor = { indexPath -> CGFloat? in
    return calculateHeight(indexPath)
  }
}

Last but not least, we can pass in an array of our model objects, then easily configure our cells with them:

section.createRows(spaceships) { (spaceship, row: Row<SpaceshipTableViewCell>) in
  row.height = 52
  row.configureCell = { cell, _ in cell.configure(spaceship) }
}

More info about Shoyu can be found at git.io/shoyu

In Bite #162, we took our first look at RxSwift, and how it can transform how we write code. We learned how Observables can simplify our code, and mentioned creating our own Observables briefly, but today we'll dive a bit deeper. Let's begin:

Creating our own Observables is how we can bring code that wasn't built with reactive principals in mind, into the RxSwift world. It's helpful to think of Observables as merely a slightly different way to organize code we were already writing.

Let's look at a simple asynchronous function call:

let task = loadSpaceships { spaceships, error in
  guard error == nil else { handleError(error); return }
  self.resultsTableView.reloadData()
}

We can actually use this code as a starting point to write a new function that wraps this code and converts it into an Observable.

We'll be responsible for sending values along, reporting any errors, and letting observers know if/when the work is completed.

func spaceships() -> Observable<[Spaceship]> {
  return Observable.create { o in
    let task = loadSpaceships { spaceships, error in
      guard error == nil else { o.onError(error!); return }

      o.onNext(spaceships)
      o.onCompleted()
    }

    return AnonymousDisposable { task.cancel() }
  }
}

We use Observable's create function to get a reference to an observer in a closure. We can use this observer to send along relevant status updates.

Lastly, we return an AnonymousDisposable closure that cancels the NSURLSessionDataTask when we're disposed.

Now this code can live in the RxSwift world as an Observable, be subscribed to, and be used alongside any other Observables:

Observable.zip(crew(), spaceships()) { return ($0, $1) }
  .subscribeNext { print($0) }

One last note: We've been talking about all of this in terms of RxSwift, but other reactive or functional libraries might have different names for their streams of values. Even if we're using something other than RxSwift, the underlying concepts will usually remain largely the same. We can still use the techniques here to lift the code out of the conventional world and into the reactive one, regardless of which library we're using.

Topics

#162: Reacting with RxSwift ⚗

Topics

We first learned about reactive-style programming in Bite #127, with ReactiveCocoa. (Give that Bite a quick look first if you're new to the topic).

Today we'll check out an alternative solution called RxSwift. It just hit 2.0, so now is a great time to take a look. Let's dive in.

Before we begin it's important to note that there are tons of great libraries available for writing this style of code. We can enjoy the benefits of functional and reactive programming with any of them.

RxSwift is another take on reactive/functional programming. Similar to ReactiveCocoa, RxSwift works with streams of values called Observables. We can subscribe to them, transform them, bind user interface elements to them, and create them.

Additionally, RxSwift ships with RxCocoa, a framework that provides extensions for UIKit that bring it into RxSwift's reactive world.

Let's check out some examples.

Binding a UITextField to a UILabel:

shipNameField.rx_text
  .map { "Launching \($0)..." }
  .bindTo(statusLabel.rx_text)

Creating our own Observable:

Observable<String>.create {
  $0.onNext("Howdy! 🐴")
  $0.onCompleted()

  return NopDisposable.instance
}

Transforming each value change of a search field into an API request, then updating our UI when it completes:

searchBar.rx_text
  .flatMapLatest {
    API.getSearchResults($0)
  }
  .subscribeNext { _ in
    self.tableView.reloadData()
  }

RxSwift can transform how we write code in all the layers of our apps. With tight UIKit integration from RxCocoa, we can now forget about delegates, notifications, KVO, etc and instead use one simple interface to think about how data flows through our app.

Learn more about RxSwift at git.io/rxs

It's quite common to need to display short status messages or notifications inside our apps. Today we'll look at a library called Whisper that is absolutely packed with features that make displaying these types of messages both simple and beautiful. Let's dive in.

Whisper provides 3 different types of display styles. All allow for extensive customization including colors, fonts, etc.

First up is "Whispering" a Message:

let message = Message(
  title: "🚨 Imperial Troops Have Entered the Base!",
  color: .redColor()
)

Whisper(message, to: navigationController, action: .Present)
Silent(navigationController, after: 3)

We call the Silent function right after we present the Whisper, this will dismiss the message after 3 seconds.

Next, let's try "Shouting" an Announcement.

This one's great for longer text, and looks great with an image. We can also pass in an closure to run when the user taps the banner.

let announcement = Announcement(
  title: "Han Solo Says:",
  subtitle: "Who's scruffy lookin'?!",
  image: UIImage(named: "avatar")
)

Shout(announcement, to: self)

Lastly, we can "Whistle" a Murmur to display a subtle, classy message in the status bar:

let murmur = Murmur(title: "The last transport... is away! 🚀", backgroundColor: murmurColor)
Whistle(murmur)

More info about Whisper can be found at git.io/whisper

Today we're looking at another great library from Hyper called Pages. It's essentially a convenience wrapper on top of UIPageViewController (previously covered in Bite #55). It makes working with paged view controllers a bit more friendly. Let's begin:

We'll start by composing a PagesController. This will be the view controller that contains the other view controllers:

let pagesVC = PagesController([shipsVC, stormtroopersVC])

pagesVC.enableSwipe = true
pagesVC.showBottomLine = false
pagesVC.showPageControl = false

We use a few properties to configure the look and feel and behavior we want, then we can use the view controller like normal.

Pages has some awesome convenience functions for programmatically scrolling to pages:

pagesVC.goTo(1) // go to page at index 1
pagesVC.next() // go forward 1 page
pagesVC.previous() // go back 1 page

We can also easily add view controller pages on the fly:

pagesVC.add([resistanceBasesVC, jediTemplesVC])

Last but not least, Pages is quite configurable offering properties for customizing not only the look and feel, but also behaviors such as whether or not changing view controllers affects the title shown in navigation bar of the navigation controller containing the pages.

More info about Pages can be found at git.io/pages

We've covered using UIImagePickerController to allow users to capture photos or select them from their library in Bite #83.

This is great for some situations, but often we'll want to offer our users a few more features and a little extra usability polish.

Today we'll look at a library called ImagePicker from Hyper. It's absolutely packed with features and has a super slick interface. Let's check it out:

ImagePicker is incredibly simple to use, It's just a view controller:

let imagePickerController = ImagePickerController()
imagePickerController.delegate = self

presentViewController(imagePickerController, animated: true, completion: nil)

We'll be notified via delegate functions as the user selects/takes photos:

func doneButtonDidPress(images: [UIImage]) {
  // TODO: Process images
  dismissViewControllerAnimated(true, completion: nil)
}

We can customize colors, fonts, and even the text shown in labels using a handy configuration struct filled with static properties:

Configuration.doneButtonTitle = "Upload"

More info about ImagePicker can be found at git.io/imagepicker

Today we'll check out a library called AwesomeCache by Alexander Schuch that caches data in-memory and on disk. Let's do it.

AwesomeCache can be interacted with in a few different ways. Let's start by creating a cache:

let cache = try! Cache<NSString>(name: "photos")

We can store values in the cache like this:

cache.setObject(
  responseString, 
  forKey: "a-unique-cache-key",
  expires: .Seconds(300)
)

If we're not setting an expiration time, we can use this shorthand:

cache["a-unique-cache-key"] = "Hello"

We can retrieve values from the cache in a similar way:

let cachedResponse = cache["photos.all"]

One of the handiest features of AwesomeCache is how it can help cache API responses (or any async code really). Here's how it works:

cache.setObjectForKey("photos.all", cacheBlock: { success, failure in
  self.requestLatestPhotos({ response in
    success(response, .Seconds(300))
  }, failure: { error in
    failure(error)
  })
}, completion: { object, isLoadedFromCache, error in
  if let object = object {
    // object is now cached, and ready to use
  }
})

If a value already exists in the cache for the given key, the completion** closure** is called right away. Otherwise, the cacheBlock closure is called. Inside the cacheBlock we call our custom own API code, then tell AwesomeCache if we succeeded or failed.

More info about AwesomeCache can be found at git.io/awesomecache

Way back in Bite #93 we talked about creating a "router" for Alamofire that would generate URL requests from enum cases.

In most apps we'll likely end up with some version of that code to help communicate with the API for that particular app. It'd be great if there was some sort of "standard" way to write this code each time.

Today we'll look at a library called Moya that aims to provide this. Let's dive in. 🏊

Moya is a network abstraction layer that encapsulates calling Alamofire functions. It helps us avoid creating messy custom network abstractions in every project.

To use Moya in an app, we'll need an enum with a case representing each API endpoint we want to hit:

enum FirstOrder { case AllTroops; case Troop(String) }

Then we'll extend it to conform to Moya's TargetType protocol:

extension FirstOrder: TargetType {
  var baseURL: NSURL {
    return NSURL(string: "https://api.firstorder.galaxy.far.away")!
  }

  var path: String {
    switch self {
    case .AllTroops: return "/troops"
    case .Troop(let id): return "/troops/\(id)"
    }
  }

  var method: Moya.Method { return .GET }

  var parameters: [String: AnyObject]? {
    switch self {
    case .AllTroops: return ["sort": "recent"]
    default: return nil
    }
  }

  var sampleData: NSData {
    switch self {
    case .AllTroops:
      return "[{\"id\": \"FN-2187\"}]".dataUsingEncoding(NSUTF8StringEncoding)!
    case .Troop(let id):
      return "{\"id\": \"\(id)\"}".dataUsingEncoding(NSUTF8StringEncoding)!
    }
  }
}

Finally, we'll create a new Moya provider using our enum and make it available globally:

let FirstOrderAPI = MoyaProvider<FirstOrder>()

Now we can make requests like this:

FirstOrderAPI.request(.Troop("FN-2187")) { result in
  switch result {
  case .Success(let response):
    do {
      let json = try response.mapJSON()
      // TODO: Parse JSON
    } catch(let error) { handleError(error) }
  case .Failure(let error): handleError(error)
  }
}

This is just a taste, Moya has a ton to offer including some fantastic enforcement of good testing practices.

In conforming to the TargetType protocol, we're actually required to provde sampleData. We can also customize things further by providing an endpointClosure when creating our provider.

More info about Moya can be found at git.io/moya

Style is important when writing code. Following conventions and guidelines helps tell the story of our code to our team and our future-selves. Today we'll learn about SwiftLint, a tool from Realm that can help us enforce Swift conventions and best practices. Let's get started.

SwiftLint provides a downloadable install package on its Github page, but here we'll install using Homebrew. We'll run:

brew install swiftlint

Now, we can run swiftlint rules at the command line to see all the different convetions and standards that will be enforced. We can also run swiftlint lint in the root directory of our project to see what rules we're breaking right in the terminal.

Our next step is to add SwiftLint as a build phase to our project.

We'll head over to our project's settings, then to the Build Phases tab. We'll click the + button to add a** new “Run Script” phase**. We'll name it “SwiftLint” and give it the follow script content:

if which swiftlint > /dev/null; then
  swiftlint
else
  echo “Download SwiftLint: https://github.com/realm/SwiftLint"
fi

Now when we build our project, SwiftLint will let us know via regular Errors and Warnings in Xcode when there's something to fix.

We can configure how SwiftLint behaves in complete detail by creating a new file called .swiftlint.yml and putting it in the root directory of our project. We can fill out this file to customize (for example) which conventions are enforced:

disabled_rules:
  - colon
  - control_statement

We can disable rules “in-line” in our code with special comments:

// swiftlint:disable colon
let noWarning :String = "" // No warning about colon placement
// swiftlint:enable colon
let yesWarning :String = "" // Warning generated

Finally, SwiftLint can correct some violations (trailing_newline, trailing_semicolon, etc.). Just run swiftlint autocorrect.

More info about SwiftLint can be found at git.io/swiftlint

Page 6 of 10