Active Filters: Libraries

Attributed strings are a fantastic way to work with styled text on iOS and OS X. NSAttributedString is an incredibly full-featured API, but because of this, simple tasks sometimes require a fair amount of boilerplate code. Today we'll look at BonMot, a library from Raizlabs that simplifies the process of composing attributed strings.

BonMot takes the form of a set of chainable functions that we can use to compose attributed strings:

let fancyQuote = "Traveling through hyperspace ain't" +
                 "like dusting crops, farm boy.\n" +
                 " β€” Han Solo"

quoteLabel.attributedText = BONChain()
  .fontNameAndSize("AmericanTypewriter", 17.0)
  .lineHeightMultiple(1.8)
  .string(fancyQuote)
  .attributedString

BonMot will do all the heavy lifting of applying attributes and return a fully-formed NSAttributedString ready for use wherever we need.

We can use BonMot to concatenate multiple composed attributed strings:

let chain = BONChain()

chain.appendLink(BONChain().string("one fish"))
chain.appendLink(BONChain().string("two fish"), separator:commaSpace)
chain.appendLink(BONChain().string("red fish").textColor(UIColor.redColor()), separator:commaSpace)
chain.appendLink(BONChain().string("blue fish").textColor(UIColor.blueColor()), separator:commaSpace)

BonMot even supports the NSTextAttachment parts of the NSAttributedString API. We can generate an β€œimage next to a label” like this:

chain.appendLink(BONChain().image(ackbar))
chain.appendLink(
  BONChain()
    .fontNameAndSize("Futura-MediumItalic", 17.0)
    .string("It's a trap!"), separator:" ")

We've only scratched the surface here, BonMot has a ton of features to offer. More info about BonMot can be found at git.io/bonmot

Today we're looking at SwiftyBeaver, a library from Sebastian Kreutzberger that aims to improve Xcode's logging capabilities. Let's dive in.

After adding the library to our project we'll need to import the library in our AppDelegate. We'll also need to create a global log reference we can use throughout our app:

import SwiftyBeaver
let log = SwiftyBeaver.self

Then we'll need to tell SwiftyBeaver to log somewhere, let's set it up to log to Xcode's console and a log file:

let console = ConsoleDestination()
let file = FileDestination()

log.addDestination(console)
log.addDestination(file)

SwiftyBeaver is built around the concept of "destinations". Here we're adding one for the Xcode console, then another to log to a file in our app's documents directory.

Now, we can use our global log reference to log messages. There's functions for each of the different levels. Each will log its level in its own color:

log.verbose("Nothing really happened") // gray
log.debug("A thing happened") // blue
log.info("A good thing happened") // green
log.warning("A probably bad thing happened") // yellow
log.error("A definitely bad thing happened") // red

Both built-in destinations have a ton of configuration options allowing us to customize how we'd like our logs to behave. We can customize the format of log statements or change a file destination's location, we can even define our own destinations if needed!

More info about SwiftyBeaver can be found at git.io/swiftybeaver

We're continuing our look at Xcode Playgrounds today with CocoaPods. Let's see what it takes to import a CocoaPod into a Playground.

We'll begin by creating a new Xcode Project. Next, we'll head into our project's directory and run pod init to generate our Podfile. We'll open it up and configure it just as we usually would:

platform :ios, '9.0'
use_frameworks!  
link_with 'Spaceships'

pod 'Alamofire'

Then we'll run pod install to generate the Workspace file for our project and install our pods. We're almost done, next we need create a new Playground and add it to our project.

Finally, we need to add our Playground to our Podfile's link_with directive (only its name, not the .playground extension):

link_with 'Spaceships', 'scratch-pad'

We'll run pod install one more time and we're done. We can now import the pod we installed into our Playground and try it out. Neat!

Formatting strings is one of the most common tasks in building software. It's usually not that complex, but it can be tempting to cut corners.

When working with things like currency or street addresses, having a robust formatting system in place can be crucial in ensuring our app works well all over the world.

Today we'll look at Format, a library from Roy Marmelstein that can help us achieve all this with ease.

Format starts by extending Swift's number types to add a format function. We can call this on any number (even literals) and it will return a String with our desired format:

134.format(Decimals.Two) // => "134.00"

Format makes localizing a breeze. It uses the device's current locale by default, or we can render a specific one:

let gb = NSLocale(localeIdentifier: "GB")
87.format(Currency.GBP, locale: gb) // => "Β£ 87.00"

Ordinal numbers can really help class up the joint:

134.format(General.Ordinal) // => "134th"

Format can format in all sorts of interesting ways, for example:

10.11.format(General.SpellOut) // => "ten point one one"

Numbers are cool, but what about addresses? Almost every nation has a slightly different convention for how they format street addresses.

Format wraps CNPostalAddressFormatter from the Contacts framework (Bite #24) to make formatting addresses quite simple:

AddressFormatter().format(
  "1 Infinite Loop", 
  city: "Cupertino", state: "CA", postalCode: "95014", 
  country: "USA", ISOCountryCode: "US")

// => "1 Infinite Loop\nCupertino CA 95014\nUSA"

Format also extends CLPlacemark, adding a function to format that class's addressDictionary property. Users expect things to look familiar. Never underestimate the power of good localization.

More info about Format can be found at git.io/format

Building a good authentication system is a lot of work. Instead of starting from scratch, it'd be great if we could build on top of some existing popular service, and allow our users to log in with their existing account there.

Today we'll check out SimpleAuth from Caleb Davenport. It provides an extremely easy-to-use way to implement social sign-in inside our apps. Let's take a look:

SimpleAuth is built around the concept of Providers. In this context, a provider contains all the code needed to talk to individual services like Twitter, Facebook, etc.

Let's add support for signing in with Twitter to an app. We'll start by adding the pod to our Podfile, then run pod install.

pod 'SimpleAuth/Twitter'

Then, we'll need a consumer key and secret from Twitter. We can get these by creating a new app on Twitter's developer portal.

Back in our Application Delegate, we'll configure SimpleAuth's Twitter provider with our info:

SimpleAuth.configuration()["twitter"] = [
  "consumer_key": "12345REDACTED",
  "consumer_secret": "ABCDETOPSECRET"
]

Finally, we can sign a user in like this:

SimpleAuth.authorize("twitter") { userDictionary, error in
  User(username: userDictionary["nickname"]).persist()
}

Then we can grab their Twitter username out of a user dictionary. (Which contains keys like uid, image, etc.) SimpleAuth uses these names to abstract away the different attribute names each service uses to represent these values. For example, the field uid always holds a unique user identifier and is present on almost all providers.

SimpleAuth ships with a ton of built-in providers (Twitter, Facebook, Instagram, Tumblr, Dropbox, Foursquare, etc.). It also makes it very straightforward to create our own providers, just in case we ever need to. Neat!

More info about SimpleAuth can be found at git.io/simpleauth.

We've covered Auto Layout quite bit, but so far we've only been using the classes and APIs that Apple ships. Today we'll start checking out some third-party libraries that can improve our experience when working with Auto Layout.

First up is Cartography from Robert BΓΆhnke. Let's take a look:

Cartography takes the form of a sort of DSL for Auto Layout. It allows us to take conventional UIKit code like this:

view.addConstraint(
  NSLayoutConstraint(
    item: editButton,
    attribute: .Right,
    relatedBy: .Equal,
    toItem: launchButton,
    attribute: .Left,
    multiplier: 1.0,
    constant: -8.0
  )
)

And express it using a new constrain function:

constrain(editButton, launchButton) { editButton, launchButton in
  editButton.right == launchButton.left - 8
}

We can pass in up to 5 UIView or NSView instances at once, then a closure. In that closure, we'll use ==, >=, or <= operators to define constraints upon the attributes of the view instances. This can really help improve readability:

constrain(viewA) { a in
  // fixed sizes:
  a.height == 44

  // centering inside a parent view:
  a.centerX == a.superview!.centerX

  // inequalities:
  a.top >= a.superview!.top + 10
}

But wait, there's more! We can capture the created constraints like this:

let group = constrain(launchButton) { button in
  button.top  == button.superview!.top
  button.left == button.superview!.left
}

Then move that button by replacing the captured constraints like this:

constrain(launchButton, replace: group) { button in
  button.bottom == button.superview!.bottom
  button.right  == button.superview!.right
}

Additionally, it provides helper functions for aligning and distributing:

constrain(viewA, viewB, viewC) { viewA, viewB, viewC in
  align(top: viewA, viewB, viewC)
  distribute(by: 8, horizontally: viewA, viewB, viewC)
}

Cartography has shortcuts for edges and sizes, operators for setting priorities, and much more! More info can be found at git.io/cartography.

It's another fastlane friday here on LBOC. Today we'll be looking at another awesome tool in the fastlane suite called scan. It provides an easy way to run the tests of our iOS or OS X app. Let's dive in.

Before we begin, let's look at why a tool like scan can be helpful.

Xcode ships with a great command line tool called xcodebuild that allows to do all sorts of interesting things to our projects from the command line. It can be a bit verbose to configure though, and its output isn't very readable at a glance.

There's other tools like xcpretty that can help improve this output, but they take a fair amount of configuration as well.

That's where scan comes in. It takes care of all of this (plus a lot more) in one simple command: scan.

We'll start by installing scan:

gem install scan

Then we run our tests at anytime like this:

scan

This is all we need for basics usage. scan will auto-detect things like our workspace, but we can always configure things as well:

scan --scheme "app-store"

Like other fastlane tools, we can run scan init to generate a new Scanfile, where we can store all our configuration options:

scheme "Spaceships"
clean true
output_types "html"

Other Features

πŸ“ƒ Displays nice output, stores original xcodebuild log in ~/Library/Logs/scan

πŸ“„ Can generate HTML, JSON or JUnit reports

πŸ“£ Can send well-formatted test results to Slack. Check out the slack_only_on_failure configuration option to only report failed tests.

scan also helps with resolving common Xcode oddities like duplicated simulators or simulators that stop responding. Finally, scan works great with tools continuous integration tools like Jenkins and services like Travis. Happy testing!

More info about scan can be found at git.io/scan

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

Regular Expressions are a fantastic way to search through text. Apple provides support for them via the NSRegularExpression class. It has great support for matching, extracting, etc. Its API can be a bit verbose for simple matches though. Today we'll look at Regex, a tiny little library from Adam Sharp that makes writing regular expressions in Swift much more friendly and expressive. Let's check it out:

Regex has a bunch of great features, but at its core it allows us to take regular expression code like this:

let stringToMatch = "star wars"

let regex = try! NSRegularExpression(
  pattern: "star (wars|trek)",
  options: NSRegularExpressionOptions(rawValue: 0)
)

let isWarsOrTrek = regex.firstMatchInString(
  stringToMatch,
  options: NSMatchingOptions(rawValue: 0),
  range: NSMakeRange(0, stringToMatch.characters.count)
) != nil

...and turn it into something just a tad more readable:

Regex("star (wars|trek)").matches(stringToMatch)

Regex is backed by NSRegularExpression under the hood, and it does a great job of "swift-ifying" its API. In addition to simple Boolean checks, we can also use Regex in a few other rather Swifty ways. For example, pattern matching:

switch starThing {
case Regex("gate$"): print("dial the gate!")
case Regex("wars$"): print("the force is strong")
case Regex("trek$"): print("set phasers to stun")
default: break
}

Last but not least, we can easily grab any captured strings:

let starRegex = Regex("star( trek| wars|gate)")
if let captured = starRegex.match(inputString)?.captures.first {
  print("You chose: \(captured).")
}

More info about Regex can be found at git.io/regex

Topics

#116: Instructions πŸŽ“

Topics

Teaching users how to use our apps is incredibly important. We should strive to do this through familiar UI patterns, intuitive flows, good error handling, etc. Sometimes though, we just need to explain what's going on.

Today we'll look at a fantastic new project from FrΓ©dΓ©ric Maquin called Instructions that can help us do just that. It's a library for easily adding "coach marks" to our apps. Let's dive in.

We'll start by adding a new CoachMarksController property to our view controller, and setting it as the data source for it:

let coachMarksController = CoachMarksController()

override func viewDidLoad() {
  super.viewDidLoad()
  coachMarksController.datasource = self
}

Next, we'll extend our view controller so it conforms to the CoachMarksControllerDataSource protocol. Instructions is highly customizable. It has support for custom body and arrow views, positions, highlights, and more. Let's keep things simple here and add a regular coach mark for a button in our app.

func numberOfCoachMarksForCoachMarksController(coachMarksController: CoachMarksController) -> Int {
  return 1
}

func coachMarksController(coachMarksController: CoachMarksController, coachMarksForIndex index: Int) -> CoachMark {
  return coachMarksController.coachMarkForView(launchButton)
}

func coachMarksController(coachMarksController: CoachMarksController, coachMarkViewsForIndex index: Int, coachMark: CoachMark) -> (bodyView: CoachMarkBodyView, arrowView: CoachMarkArrowView?) {
  let coachViews = coachMarksController.defaultCoachViewsWithArrow(true, arrowOrientation: coachMark.arrowOrientation)

  coachViews.bodyView.hintLabel.text = "This button launches the spaceship, proceed with caution!"
  coachViews.bodyView.nextLabel.text = "Got it"

  return (bodyView: coachViews.bodyView, arrowView: coachViews.arrowView)
}

It's good practice to let users who don't need our coach marks skip them. Instructions has us covered. We can setup a "skip view" on our controller. We'll use the default one, which shows in the nav bar:

let skipView = CoachMarkSkipDefaultView()
skipView.setTitle("Skip", forState: .Normal)
coachMarksController.skipView = skipView

Finally we'll start the whole flow in viewDidAppear:

override func viewDidAppear(animated: Bool) {
  super.viewDidAppear(animated)
  coachMarksController.startOn(self)
}

Success! When we launch our app, the coach marks are now shown:

More info about Instructions can be found at git.io/instructions

Page 7 of 10