Active Filters: Techniques

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

Today we'll tackle a common question from those beginning to learn iOS development:

"UICollectionView and UITableView seem to overlap in terms of functionality in many ways. When should I use one or the other?"

The practical answer is actually a common idiom when building software: What's the simplest possible solution?

Let's look at the basics of table and collection views, then we'll circle back and learn how we apply that answer to our question.

UITableViews are all about vertically scrolling lists of content. They expect us to use them to display rows of content inside sections. We get a ton of conventional "iOS-native" feeling functionality for free such as group-style table views that look like iOS's settings app or system-standard accessory views.

In short: UITableViews give us a lot for free, but they expect us to think in their terms.

UICollectionViews are infinitely more customizable than Table Views. We can completely customize a collection view's layout, even animate between layouts.

UICollectionViews don't provide anywhere near as much functionality out of the box, but they offer an incredible amount of power and capability.

In many ways, Collection Views are the more-capable big sister to Table Views. They aren't constrained by "rows" or "accessory views". They are great for simple grids, all the way up to incredibly complex layouts.

Back to our original answer: What's the simplest possible solution?

We can now answer our question by rephrasing it: Given what I'd like to build, will a UITableView or a UICollectionView help me get there faster, with less (or at least simpler) code?

Building a Settings screen or vertical list of social media posts? A UITableView is probably the way to go.

Building a photo gallery, or perhaps a screen that needs to scroll content horizontally? We'll need a UICollectionView for those.

In either case, if we ever start to feel like we're fighting against UIKit, that's a good time to step back and re-evaluate.

Topics

#159: Swift Shortcuts 💇

Topics

Today we'll begin checking out some syntactic niceties available in Swift. These are tricks that can help make our code more readable, and can often make it easier to reason about. Let's get started:

Omitting Types

The Swift type system is pretty great. As long as the compiler can infer the type, we can simply leave it out when calling static functions:

struct Theme {
  let titleColor: UIColor
  static let currentTheme = Theme(titleColor: .blackColor())
}

let nightMode = Theme(titleColor: .darkGrayColor())

This trick also works just as well for static properties:

let cell = ThemeTableViewCell(theme: .currentTheme)

Shorthand Argument Names

We can use a $x syntax to reference closure arguments:

spaceships.sort { $0.name > $1.name }

Trailing Closures

Many functions accept a closure as their last parameter:

func changeTheme(theme: Theme, completion: () -> ())

In these cases, we can call them with this shorter syntax:

Theme.changeTheme(dayMode) { /* */ }

Nil coalescing

The ?? operator offers us a way to express a sort of "fallback" relationship between two statements. When the first statement is nil, it "falls back" to the second.

let cellTitle = trooper.nickname ?? trooper.troopID

At a technical level this returns either an unwrapped optional, or the value on the right, which can't be an optional.

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

Today we'll take our first look at ReactiveCocoa. It's a huge topic, so we'll start with why we might want to use it. Let's dive in. 🏊

Traditionally, we'd wire up the different components in our app declaratively. We'd use common Cocoa techniques like Delegates, KVO, NSNotificationCenter notifications, target/actions, etc.

At first our app might have just a text field and delegate. But then we add a button, and maybe a segmented control. Things can quickly get messy. We inevitably end up with code like this sprinkled throughout our view controllers:

if crewNameIsValid && crewRankIsValid && !ship.full {
  createButton.enabled = true
} else {
  createButton.enabled = false
}

We're also using a ton of different mechanisms to accomplish the same conceptual task: Updating our UI to reflect state changes. Managing all of this in a small app might not seem like a big issue, but in even moderately complex codebases, this can be a big source of bugs. 🐞

Enter ReactiveCocoa (also commonly called "RAC"). It's a library for iOS and OS X that helps us write code that works with streams of values over time. It also allows us to write code that's closer to how we think about our app while building it:

"The Create Crew Member button should only be enabled: if the crew name is valid, and a rank has been selected, and the ship is not already full."

For example, in RAC, we can get a reference to a signal producer, which will send along our field's text, every time it changes:

let crewNameValid = crewNameTextField
  .rac_textSignal()
  .toSignalProducer()
  .map { $0 as! String }

crewNameValid.startWithNext { text in /* do something with text */ }

We'll do this for our rank control too. Next, we can merge both of these signal producers into one, then use the values that signal producer sends along to set our createButton's enabled property.

let formValidSignal = zip(crewNameValid, crewRankValid).map { $0 && $1 }

ReactiveCocoa is a giant departure from how we've traditionally built our apps. Don't worry if it doesn't make sense right away. In the future, we'll dive deeper into RAC, and look at all the different ways we can use it.

More info about ReactiveCocoa can be found at git.io/RAC

In our ongoing quest to build better apps, it's important for us to keep up as new techniques and best practices emerge. One such technique that's recently been growing in popularity is the use of View Models. Today we'll look at how they fit into an app, and why we'd use them.

Traditionally, we'd use the Model View Controller technique to build our apps. Sounds great, but on iOS it usually ends up looking something like this:

This approach can often lead to gigantic UIViewController subclasses, with long lists of responsibilities: networking/data loading code, table/collection view delegate methods, UI interaction, UI layout, and so on.

That's where the Model View View Model approach comes in. Don't be fooled by the name, we still need view controllers. When applied on iOS, MVVM ends up looking something like this:

We'll add a new view model type in between our model and view controller. Our view model will take over loading data, as well transforming/processing that data. Then it will expose that transformed data as read-only properties.

struct SpaceshipViewModel {
  private var ship: Spaceship

  let displayName: String { "\(ship.squadron): \(ship.name)" }
  func loadShips(start: Int = 0) { ... }
}

With our new View Model, we now have a clear place to put things like networking, data persistence, and all our business logic. Our view controllers can now relax a bit and handle things like user input and size class changes. Next time, we'll look at how we can make our view controller automatically react to changes in the view model.

Author's Note: Interested in learning more about this? This introduction from Bob Spryn is fantastic. It covers everything from start to finish and does a great job explaining both the "why" and "how" along the way. Highly recommended reading.

Interface Builder is awesome. Assembling our interfaces in it can be a great way to work. Sometimes though, we want to create custom views that we draw and manage ourselves in code. It'd be nice if these custom views could work just as well inside Interface Builder as they do in our code. Today we'll look at two awesome features that debuted with Xcode 6 called @IBInspectable and @IBDesignable. Let's dive in:

@IBInspectable var borderWidth: CGFloat = 0 {
  didSet { layer.borderWidth = borderWidth }
}

When crafting a custom view, we can make a property “inspectable” in Interface Builder by prefixing its definition with @IBInspectable. Now, when we drag an instance of our custom view onto the canvas in Interface Builder we'll be able to change our properties with native controls!

This technique even works with extensions! Tired of not being able to configure things like a UIButton's borders in IB? Let's add a tiny extension to UIButton to make it more inspectable:

extension UIButton {
  @IBInspectable var borderWidth: CGFloat {
    get { return layer.borderWidth }
    set { layer.borderWidth = borderWidth }
  }
}

This is great so far, but what we really want is to be able to see the effects of our property edits live in the IB canvas. We can prefix any view's definition with @IBDesignable to tell IB to render the view, live, right on the canvas.

@IBDesignable
class SpeedometerView : UIView {
  var pieChartView: PieChartView

  @IBInspectable var meterColor: UIColor = UIColor.yellowColor() {
    didSet { pieChartView.segmentColor = meterColor }
  }

  // ...

When we combine the two techniques we unlock a much more intuitive (and fast) way to build custom views. We can even implement a special function called prepareForInterfaceBuilder() on our view where we can configure it with example data that will be drawn only when it's rendered in IB.

If anything goes wrong, we can debug our view by setting a breakpoint like normal, then choosing Editor > Debug Selected Views from Xcode's menu.

With additions like UIStackView, Auto Layout has matured into quite a powerful system for managing how our views are arranged on screen. When things go wrong though, it can sometimes be difficult to diagnose the specific cause of the issue. Today we'll look at a few techniques for making sense of the madness caused by... Auto Layout Bugs! 🐞🔪🔪🔪

Most issues arise from "Unsatisfiable Layouts". That's fancy-talk for "two or more constraints you gave Auto Layout conflict with each other." The solution will of course be different in every case, but here's some sensible things to ask ourselves when an error first occurs:

  • Is translatesAutoresizingMaskIntoConstraints set to false on the views we're adding constraints to?

  • Are the priorities of each of constraint, as well as content hugging and compression resistance priorities (Bite #69) what we expect them to be?

  • "Can this required constraint work at a 999 priority?" Remember, Auto Layout will try to get as close to our desired result as possible, while still satisfying all other constraints.

Identifiers

constraint.identifier = "image-fixed-width"

Identifiers help us more easily spot the important bits in those giant log outputs Auto Layout loves to show us. They can be added in code or in Interface Builder.

Log Specific Constraints

profileHeaderView.constraintsAffectingLayoutForAxis(.Vertical)

When debugging complex layouts, it can sometimes be helpful to look at only the constraints involving a specific problem view or area. We can use this function to grab an array of the constraints affecting a particular axis. Neat.

Brain Surgery

When all else fails, don't be afraid to go in and start temporarily commenting-out or disabling constraints then observe the results. This can often lead to unexpected insights into how constraints are behaving.

Practice!

A great way to fight Auto Layout issues is to try to catch them before they happen. That means becoming more familiar with Auto Layout, which means practice. For example: When a question or issue comes up, create a new 'dummy' Xcode project. Throw some views and constraints in there and try it out. Tweak some priorities, observe their effects in the isolated environment. When in doubt, try it out!

Topics

#108: Error Handling 📛

Topics

Today we're going to talk about Error Handling. First though, a bit of a pitch: Great apps handle errors gracefully. Think of error handling as the dental flossing of creating apps. Sure, not the most exciting part of the job, but very important. Alright, let's dive in:

enum BlueprintFileError: ErrorType {
  case Interrupted
  case CorruptData
  case ShipBoardedByVader
}

func decryptDeathStarPlans() throws -> BlueprintFile {
  // ...

  throw BlueprintFileError.Interrupted
}

To allow one of our functions to throw an error, we add a throws keyword to its definition. Then we can create our own enum that inherits from the system's ErrorType.

Then anywhere inside our function, when something goes wrong, we can throw the appropriate error.

Elsewhere in our code, we can use a do/catch block to handle these errors. We'll try our dangerous code in the do block, then catch any errors below.

do {
  try decryptDeathStarPlans()
} catch BlueprintFileError.Interrupted {
  alert("Decryption Interrupted!", options: [ "Try Again", "Cancel" ])
} catch BlueprintFileError.CorruptData {
  alert("Sorry, the file could not be read from disk.", options: [ "OK" ])
} catch BlueprintFileError.ShipBoardedByVader {
  transferFilesToDroid("R2-D2")
  alert("Ship is being boarded, " +
    "decryption will continue on R2-D2", options: [ "OK" ])
} catch let error {
  alert("Decription Failed", options: [ "Try Again", "Cancel" ])
}

Finally, let's look at how best to actually handle these errors. Every app is unique, and will need special consideration around how best to handle its errors. That being said, here's some guidelines that should apply in most cases, and are illustrated above:

  • Fail as gracefully as possible, and preserve as much of the user's work as possible.
  • If necessary, tell the user what happened in clear simple terms. (No jargon).
  • When possible, give the user a way to try the task again.
  • Handle all cases, even if unlikely, if it can go wrong, it will, for someone.
Page 3 of 4