Active Filters: Swift

Topics

#184: More Swift Tricks 🎩

Topics

Today we'll continue our look at Swift tips and tricks. Let's dive in.

Nested Types

We'll begin with an organizational tip. We can nest our type declarations inside one another to group related definitions:

enum CostumeDepartment {
  case Day, Night

  struct Color { /* ... */ }
  struct Image { /* ... */ }
  struct Text { /* ... */ }

  func appLogoImage() -> Image { /* ... */ }
}

Swift's compiler is smart enough to take note of this nesting whenever ambiguity arises. This means we can use types nested inside multiple "container" types without any issues.

Even-lazier Lazy Definitions

We covered the lazy keyword in Bite #179. It turns out, we can be even lazier, and omit the extra closure altogether. (Still works the same).

lazy var backgroundView = SpecialBackgroundView()

Variadic Parameters

Objective-C supported these as well, but they were a bit of pain to work with there. In Swift, we simply add an ellipsis (...) to our function's definition, and we receive the arguments as a strongly-typed Swift** array**.

func sum(numbers: Int...) -> Int {  
  var total = 0
  for number in numbers { total += number }
  return total
}

...or if we wanted to write that function in an even Swiftier way:

func sum(numbers: Int...) -> Int { return numbers.reduce(0, combine: +) }

Topics

#180: More Swift Tricks 🎩

Topics

Today we'll continue our look at interesting Swift features. Let's begin.

Type Matching with ‘is’

Try as we might, sometimes still we end up with an AnyObject or Any reference in our Swift code. We can ask if it is a given type or subtype using Swift's powerful pattern matching:

protocol Vehicle { }
struct Spaceship : Vehicle { }

let thing: Any = Spaceship()

if thing is Vehicle { print("let's move") }
else { print("can't move") }

Parsimonious Declarations

Hat tip to Erica Sadun for passing along this one. We can use a sort of Tuple-ish syntax to create a bunch of related references inline:

var (top, left, width, height) = (0.0, 0.0, 100.0, 50.0)
spaceship.width = width

Inline Closures

This technique has a few different applications, but one good example is capturing a value from a switch statement inline:

enum Theme {
  case Day, Night, Dusk, Dawn

  func apply() {
    // ...

    let backgroundColor: UIColor = {
      switch self {
        case Day: return UIColor.whiteColor()
        case Night: return UIColor.darkGrayColor()
      }
    }()

    // ... set backgroundColor on all UI elements
  }
}

Topics

#179: Swift Tricks: Properties 🎩

Topics

Today we'll look at a couple more tips for working in Swift. This time, we'll focus on Swift properties. Let's dive in.

Property Observers

In Objective-C, we'd likely use Key-Value Observing to "know" when the value of a property on one of our objects changes.

KVO still works great in Swift. In many cases, we might be able to get away with Swift's (much cleaner) property observers:

struct Spaceship {
  let name: String

  var currentSpeed: Int = 0 {
    willSet { print("About to change speed to \(newValue)") }
    didSet {
      if currentSpeed > oldValue {
        print("Increased speed to \(currentSpeed)")
      } else if currentSpeed < oldValue {
        print("Decreased speed to \(currentSpeed)")
      }
    }
  }
}

Lazy Properties

"Lazy" initialization is a great way to put off expensive work until some later time in our code. Back in Objective-C, if we wanted to "lazily" initialize a property, we might go override it's getter, and check if a private instance variable is nil or not, then populate it if it's not, then return it. Whew!

In Swift, we can forget all of this and accomplish the exact same with a simple new keyword:

lazy var couchPotatoes = [String]()

We can also use this technique to lazily init our UI elements, neat!

lazy var someLabel: UILabel = {
  let label = UILabel()
  label.translatesAutoresizingMaskIntoConstraints = false
  label.font = UIFont(name: "AvenirNext", size: 20)
  return label
}()

Topics

#175: More Swift Tricks 🎩

Topics

Today we'll continue our look at interesting Swift features. Let's begin.

Multiple/Chained Optional Unwrapping

This is great when we've got multiple optionals to unwrap. We can even use unwrapped references from earlier in the statement in later parts of the statement. Neat!

if let URL = NSURL(string: someURL),
   let data = NSData(contentsOfURL: URL),
   let ship = Spaceship(data: data) {
    // use 'ship'
}

Multiple Initialization

We can initialize multiple references in one statement (and one line). This works for when using both let and var:

let ship1 = "Tantive IV", ship2 = "Ghost", ship3 = "Outrider"

Slicker Array Inits

We can simplify Array (and Dictionary) init statements like this:

let spaceships: [Spaceship] = []

To this:

let spaceships = [Spaceship]()

Semicolons: Gone but not forgotten!

Removing the need for trailing semicolons was one of Swift's most welcome features. However, they actually are present in Swift, and can serve a useful purpose. They'll let us write multiple statements on one line. This works great (for example) in guard statements:

guard let error == nil else { fail(error!); return }

Optionals in Swift are a great way for us to represent either some value, or no value at all. Like many parts of Swift, they're actually built (mostly) in Swift itself. Today we'll pop the hood, and take a look at how they work. Let's get started.

We'll begin by heading over to the definition of the Optional enum in the Swift headers. We can get here by pressing ⌘⇧O, then typing "Optional":

public enum Optional<Wrapped> : _Reflectable, NilLiteralConvertible {
  case None
  case Some(Wrapped)
  // ...

Right away we'll see that Optional types are merely an enum with an associated value. That associated value has a type, but it's a generic placeholder for whatever type we'll make Optional references to. The trailing-? syntax is merely syntactic sugar, for example:

String?

is simply a shorthand way of writing:

Optional<String>

Swift enums really show their power here, we're able to represent a fairly high level concept, with this relatively simple construct. Next, let's look at initializing Optionals.

/// ...

public init()

public init(_ some: Wrapped)

The first init statement handles Optionals with no value, the second handles the presence of some value. Additionally, Optional adopts NilLiteralConvertable, so all three of these statements mean the same thing:

let foo: String? = nil
let foo = Optional<String>()
let foo: String? = .None

Similarly, these are also the same:

let foo: String? = "Hello"
let foo = Optional<String>("Hello")
let foo: String? = .Some("Hello")

Topics

#170: Swift Tricks Revisited 🎩

Topics

Today we'll look at a few more interesting Swift tips and tricks. Let's get started.

Multiple Switch Cases

We'll start with a simple one. We can simplify messy switch statements by combining the similar cases. This is similar to how we can use multiple case statements in other languages, but a bit less wordy:

extension FirstOrderAPI {
  var method: HTTPMethod {
    switch self {
    case .Spaceships,
         .Crew,
         .Stormtroopers,
         .Planets: return .GET

    case .AddSpaceship,
         .AddCrewMember,
         .AddStormtrooper,
         .AddPlanet: return .POST

    case .RemoveSpaceship,
         .RemoveCrewMember,
         .RemoveStormtrooper,
         .DestroyPlanet: return .DELETE
    }
  }
}

Operator Parameters

We can actually pass literal operators in as a parameter, for example:

[1, 2, 3, 4].reduce(0, combine: +)

This works because the + operator is actually a function under the hood:

public func +(lhs: Int, rhs: Int) -> Int

Simpler Reuse Identifiers

In Objective-C, It's quite common to use NSStringFromClass to use a cell's class name as it's reuseIdentifier. In Swift though:

let reuseIdentifier = String(SpaceshipTableViewCell)

Shout out to Natasha for pointing this one out!

Topics

#164: Swift Tricks 🐇🎩

Topics

In Bite #159, we started looking at some syntactic shortcuts in Swift. Today we'll continue by looking at a few (perhaps) lesser-known Swift tricks, and their effects. Let's get started.

@autoclosure

This attribute can help save space when writing simple closures:

func cache(key: String, @autoclosure cacheIf: () -> Bool)

Now, the compiler will infer the curly braces {} around our statement:

cache("spaceships", cacheIf: ships.count > 0)

private(set)

struct Spaceship { private(set) var name = "Untitled" }

With the private(set) declaration, we're telling the compiler this property has the default access-level of internal for reads, but writes can only happen in the source file where it's declared.

For frameworks, we can configure both the getter/setter explicitly:

public struct Droid {
  public private(set) var number: String
}

final

When optimizing performance-critical code, dynamic dispatch can be our enemy.

If our class's properties and functions can be overridden by subclasses, we're going to pay the performance cost of an indirect call or access every time we use one of those functions or properties.

We can easily prevent this by declaring a property or function final.

This way, any attempts to override it will result in a compile-time error. We can also declare a class final, which applies the final keyword to all the class's functions and properties.

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.

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

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