Active Filters: Swift

Topics

#318: Codable Enums 📠

Topics

In Bites #316 and #317, we began looking at Swift's new Codable (Encodable & Decodable) protocols. Today we'll continue by learning more about how Swift Enums work can work with these protocols. Let's get started.

First off, lets try a basic example. Here's an enum:

enum SpaceshipKind {
  case transport
  case freighter
  case fighter
}

If we simply do this:

enum SpaceshipKind : Codable {

We'll get an error: Type 'SpaceshipKind' does not conform to protocol 'Decodable'.

We can get around this by making our enum a "raw" value type like a String:

enum SpaceshipKind : String, Codable {

Nice. Since Strings are Codable, this works great. Same goes for Int and other basic Codable types.

This is a fine solution for simple use cases, but things get a little (alright, a lot) more involved when one or more of our enum cases has associated values.

Here's an example:

public enum ContentKind : Codable {
  case app(String)
  case movie(Int)
}

If we compile now, we get that same does not conform... error.

Let's fix that by extending our enum. First, since we'll be taking on more of the encoding and decoding logic ourselves, we'll need our own Swift Error type to throw when things go wrong.

extension ContentKind {
  enum CodingError: Error { case decoding(String) }

Then, we'll need to tell Swift the keys we'll be using to encode or decode our data. We'll use another enum for this, this one adopting the built-in CodingKey protocol:

extension ContentKind {
  enum CodingError: Error { case decoding(String) }
  enum CodableKeys: String, CodingKey { case app, movie }

At this point we simply have to implement two more functions:

init(from decoder: Decoder) throws
func encode(to encoder: Encoder) throws

First we want to add the ability to decode a new ContentKind enum from some encoded data, then we want to
enable encoding a ContentKind into some data.

Let's look at the implementation of decoding all at once for clarity:

init(from decoder: Decoder) throws {
  let values = try decoder.container(keyedBy: CodableKeys.self)

  if let bundleID = try? values.decode(String.self, forKey: .app) {
    self = .app(bundleID)
    return
  }

  if let storeID = try? values.decode(Int.self, forKey: .movie) {
    self = .movie(storeID)
    return
  }

  throw CodingError.decoding("Decoding Failed. \(dump(values))")
}

First we pull all the values out of the decoder's container.

Then we check for each of our two possible cases, configuring ourselves and returning if successful. Finally, if we didn't return successfully and reach the end, we throw one of those CodingErrors we defined earlier.

Neat.

Last but not least, lets implement encoding:

func encode(to encoder: Encoder) throws {
  var container = encoder.container(keyedBy: CodableKeys.self)

  switch self {
  case let .app(bundleID):
    try container.encode(bundleID, forKey: .app)
  case let .movie(storeID):
    try container.encode(storeID, forKey: .movie)
  }
}

This time we grab the encoder's container up front, then simply switch on ourselves to see which value to encode.

Now our associated value enum can be encoded and decoded all we want. Nice!

Pro Tip: Writing all of this by hand can get extremely tedious for anything more than a few simple cases. Try something like Sourcery (Bites #292, #294, and #295) to generate this more boilerplate-ish Codable implementation.

That's all for now, have a question or idea for a Bite? Send it to hello@littlebitesofcocoa.com.

Topics

#316: Codable Dates 📠

Topics

In Bite 315 we started looking at the new Codable protocol in Swift 4. Today we'll learn how to work with Date types when encoding and decoding. Let's dive in.

We'll start with another simple struct:

struct Spaceship : Codable {
  var name: String
  var createdAt: Date
}

Then, we'll create one and encode it into JSON to see what it looks like:

let ship = Spaceship(
  name: "Skyhopper",
  createdAt: Date()
)

let encoder = JSONEncoder()

if let data = try? encoder.encode(ship) {
  print(String(data: data, encoding: .utf8)!)
}

This prints:

{
  "name":"Skyhopper",
  "createdAt":524735194.61138701
}

There's a lot more than meets the eye here though. That JSONEncoder we created has some incredibly helpful properties on it.

The one we want is .dateEncodingStrategy. Yep, it's a Swift enum with all sorts of goodies inside. Let's check them out.

We've already seen the default option, which is called .deferredToDate.

Another great one is .iso8601:

{
  "name":"Skyhopper",
  "createdAt":"2017-08-18T07:50:53Z"
}

Super neat to see that built in to the system in such a clean way.

Decoders have a similar property as well.

We've all had to decode some crazy date format from an HTTP API before. Now it's (hopefully) a little less painful.

Let's try decoding a funky format.

All we need to do is use the .formatted(DateFormatter) value for our JSONDecoder's .dateDecodingStrategy property.

We'll give it this JSON to decode:

{
  "name":"Skyhopper",
  "createdAt":"Friday, Aug 18, 2017"
}

Then, we'll set up our decoder and formatter:

let decoder = JSONDecoder()

let formatter = DateFormatter()
formatter.dateFormat = "EEEE, MMM d, yyyy"

decoder.dateDecodingStrategy = .formatted(formatter)
let data = jsonString.data(using: .utf8)!

try? decoder.decode(Spaceship.self, from: data)

Which decodes to our struct as expected:

Spaceship(
  name: "Skyhopper",
  createdAt: 2017-08-18 05:00:00 +0000
)

Neat!

That's all for now. Happy coding!

Topics

#315: Codable Basics 📠

Topics

Swift 4 has brought with it a ton of great improvements. Today we'll cover one of them, it's the new Codable protocol! Codable ships with Swift 4 as part of the Swift standard library and allows us to conform our own types to it to get encoding and decoding functionality "for free".

Encoding and decoding types has never been easier.

Let's dive in!

Before we begin lets back up a second and look at what we're trying to accomplish when it comes to encoding and decoding.

Essentially we want to take an instance of an object or struct and convert (or "encode") it into some other, (usually "machine-readable") format. Then, later, we want to be able to convert (or "decode") the information in that format back into our object or struct.

These days the most common format for encoding/decoding is JSON. It's supported just about everywhere and has the advantage of being both human and machine readable.

For this reason, we'll be discussing JSON in this Bite. It's important to note though, that Codable isn't specific to just JSON. It's built to support encoding and decoding into just about any format we can think of.

In the past, we might have used something like the JSONSerialization class in Foundation to do encode our types into JSON.

This works great, but the encoding input and decoding output was either a Dictionary<String: Any>, or an Array of them. Then we'd need to manually convert to/from our "real" types.

With Codable, we can do much better!

Let's start with a simple struct conforming to Codable:

struct Spaceship : Codable {
  var name: String
}

let ship = Spaceship(name: "Skyhopper")

Now, all we need to do to convert our ship to JSON is:

let data = try? JSONEncoder().encode(ship)

This returns a Data that we can send to a server, store to disk, or do anything else we need with.

Let's convert it to a string real quick so we can take a look at it:

if let string = String(data: data, encoding: .utf8) {
  print(string)
}

This prints:

{"name":"Skyhopper"}

Nice! Notice how we didn't need to specify the names of the "keys" in the JSON, they are inferred from the property names on our struct.

For decoding, we can easily go the other way:

let ship = try? JSONDecoder().decode(Spaceship.self, from: data)

Note how both the .encode and .decode functions can throw and thus we're using the try? keyword to return a nil Optional if encoding or decoding fails.

Before we go let's cover one more nice thing about Codable.

Let's add another struct to the mix:

struct Person : Codable {
  var firstName: String
  var lastName: String
}

struct Spaceship : Codable {
  var name: String
  var pilot: Person
}

Nice. Now when we encode a Spaceship, the system notices that the pilot property's value is also Codable and does the right thing for us automatically:

{"name":"Skyhopper", "pilot":{"firstName":"Luke", "lastName":"Skywalker"}}

That's all for today, next time we'll look at using Codable with more complex types of data likes Dates. Happy coding!

Topics

#302: Lazy Properties 🐤💤

Topics

Today we're going to begin looking at the lazy keyword in Swift. It describes some functionality in the language for what Apple calls " just-in-time calculation" of work.

This is great for times when we have expensive work to perform, or maybe the work is just some ugly complex code we'd like to tuck away. Perhaps we simply want to better compartmentalize the bits of our code that initialize UI elements, the use cases are plenty.

Today we'll start with Lazy Variables. Let's dive in!

Say we have a View Controller with a titleLabel. Pretty old school standard stuff.

We need to create the titleLabel, save it to a property, configure some stuff about it, then add it into the view heirarchy.

We want this work to be done after the view is loaded, and not when the view controller is first created, so we are forced to put it in viewDidLoad.

Additionally, we're forced to use a UILabel! force-unwrapped type on our titleLabel property to make the compiler happy.

It ends up looking something like this:

class ViewController: UIViewController {
  var titleLabel : UILabel!

  override func viewDidLoad() {
    super.viewDidLoad()

    titleLabel = UILabel()

    titleLabel.font = UIFont(name: "Source Sans Pro", size: 18.0)
    titleLabel.textColor = UIColor.brown

    view.addSubview(titleLabel)
  }

Let's make our titleLabel lazy and clean this junk up:

class ViewController: UIViewController {
  lazy var titleLabel : UILabel = {
    let label = UILabel()

    label.font = UIFont(name: "Source Sans Pro", size: 18.0)
    label.textColor = UIColor.brown

    return label
  }()

  override func viewDidLoad() {
    super.viewDidLoad()

    view.addSubview(titleLabel)
  }
}

Neat!

We've now dramatically improved our viewDidLoad implementation. Cool.

We've also neatly compartmentalized the bit of code that sets up that label. Cool cool.

Perhaps most importantly though, we've now better expressed our intentions using language keywords (and their resulting behaviors), Cool cool cool.

Note: We have to use var instead of let when defining lazy properties like this. Our property might not (likely won't) have an initial value until after the instance of our view controller is created and its view is loaded. This completely breaks the rules of lets (constants).

That's all for now, in future Bites we'll look at other lazy behaviors in Swift!

Topics

#257: Property Observers on Local Variables 🐤

Topics

We first looked at Swift's property observers back in Bite #179. They're a great way to know when a property's value is about to change, or just finished changing.

Traditionally, they look like this:

class Spaceship : NSObject {
  var name: String {
    didSet { print(name) }
  }
}

Nothing too fancy here, just printing the latest value for name, anytime a ship's name changes.

This works great for properties on types, but it turns out we can actually use these same observers on local variables as well:

var name = "Tim Cook" {
  didSet { print(name) }
}

name = "Eddy Cue"
name = "Craig Federighi"

This prints:

Eddy Cue
Craig Federighi

Neat!

Kelan Champagne mentions an interesting technique for super-simple value-change tracking:

var previousStatuses = [String]()
var awayMessage: String? = nil {
  willSet {
    guard let awayMessage = awayMessage else { return }
    previousStatuses.append(awayMessage)
  }
}

awayMessage = "out to lunch, brb"
awayMessage = "eating dinner"
awayMessage = "emo song lyrics"
awayMessage = nil

print("Previous: ", previousStatuses)

This prints:

Previous: ["out to lunch, brb", "eating dinner", "emo song lyrics"]

This technique might not make sense in many situations, but is a nice feature to know about in case we can ever benefit from it.

A huge thanks to both Kelan Champagne and Chris Eidhof for pointing this out!

Swift Protocols are great. They allow us to express the idea of expecting a set of functionality, without needing to expect a specific concrete type. Today we'll look at creating a Protocol to make working with colors in our apps a bit more flexible. Let's dive in. 🏊

We'll start by creating a new Protocol, giving it a conventional name, and require just one read-only property from our conforming types. This property will always return a UIColor.

public protocol ColorConvertible {
  var colorValue: UIColor { get }
}

Now, let's try this new Protocol out by creating an enum that represents the different colors we use in our app:

public enum SpaceshipsColors : String {
  case white = "FFFFFF"
  case red = "E2574C"
  case gold = "EFC75E"
  case darkTeal = "314D5B"
  case lightTeal = "3CB39E"
  case spaceGray = "233640"
  case aluminum = "6A838D"
  case black = "000000"
}

Looking pretty good, now let's extend our enum to conform to ColorConvertible.

We're using Hue here (Bite #195) to convert our hex color code Strings into UIColors.

extension SpaceshipsColors : ColorConvertible {
  public var colorValue: UIColor {
    return UIColor.hex(rawValue)
  }
}

Now we can reference colors in our code by name, and get full autocompletion when typing them. Using our new Protocol is super simple, let's make a quick UIView subclass to show it off:

class SpaceshipNameHeaderView : UIView {
  var nameLabel: UILabel
  var nameTextColor: ColorConvertible? {
    didSet {
      nameLabel.textColor = nameTextColor?.colorValue
    }
  }
}

It'd be nice if we could still supply "one-off" UIColors as well. No problem, let's simply conform UIColor itself to be ColorConvertible. We can just return self:

extension UIColor : ColorConvertible {
  public var colorValue: UIColor {
    return self
  }
}

Finally, we can use it like this:

let hv = SpaceshipNameHeaderView()

// works!
hv.nameTextColor = UIColor.black

// works just as well!
hv.nameTextColor = SpaceshipsColors.aluminum

Swift Protocols can help us write code that is both expressive and quite flexible. They can take a while to get a handle on, but understanding them is a key step towards unlocking the full power of Swift.

Today we'll take our first step towards contributing to the Swift language. We'll learn how to download the codebase, compile it, and run tests. Let's get started!

First, we'll prepare our environment. We'll be doing all of this on a Mac today, but all of this is also possible on Linux (and a bunch of other platforms).

We'll begin by using homebrew to install a couple of build tools:

brew install cmake ninja

Once that's done, we'll make a new directory somewhere sensible where we can put everything:

mkdir -p ~/projects/swift

Next, we'll do the first clone:

git clone git@github.com:apple/swift.git

Once this is done, we'll pull down all the dependencies and other projects involved with building Swift:

./swift/utils/update-checkout --clone-with-ssh

We have now cloned and checked out a lot of code, it's time to get building. We'll head into the swift directory and kick off a standard build, then run basic tests like this:

cd swift
./utils/build-script -r -t

That's it! At this point we can go get a cup of coffee (no really, this is going to take a while, even on a fast machine).

With these skills in hand though, we can now edit code, and see what effects our changes have on tests.

We can run basic tests at anytime with:

./utils/build-script --test

Or run more intensive validation tests like this:

./utils/build-script --validation-test

This is just the beginning, in the future we'll look at fixing a Swift bug, and contributing our work back to the project.

Topics

#250: Improvements to C APIs in Swift 3 🐤

Topics

We’ve covered a little about working with C APIs in Swift back in Bite #189. Swift 3 brings us a ton of new goodies and improvements around C APIs, let's dive right in and take a look.

Swift 3 improves upon a number of areas of C API usage in Swift, but the biggest one is importing functions as members. Essentially, taking free floating C functions, renaming them, and shoving them onto a type as functions.

One place where this behavior really shines is when using CoreGraphics to draw into a view.

Here's some basic drawing code in Swift 2:

guard let context: CGContext = UIGraphicsGetCurrentContext() else { return }

CGContextSetStrokeColorWithColor(context, UIColor.redColor().CGColor)
CGContextSetLineWidth(context, 2)
CGContextDrawPath(context, .Stroke)

In Swift 3, the code to do the same would be:

guard let context: CGContext = UIGraphicsGetCurrentContext() else { return }

context.strokeColor = UIColor.red().cgColor
context.lineWidth = 2
context.drawPath(mode: .Stroke)

The mechanisms powering this conversion under the hood are pretty neat. The full (now implemented) proposal can be found here, but here's a quick overview:

The first is a bit of automatic inference. Many APIs (like CoreGraphics and CoreFoundation) use consistent (albeit often verbose) naming schemes for their functions. The Swift compiler can now exploit this to (for example) detect functions returning a specific type and convert them into init functions in Swift.

So this C function would come in to Swift 2 like this:

func CGColorCreate(space: CGColorSpace?, _ components: UnsafePointer<CGFloat>) -> CGColor?

But in Swift 3 it's an init function:

extension CGColor {
  init?(space: CGColorSpace?, components: UnsafePointer<CGFloat>)
}

There's tons more of these automatic inferences for things like detecting getter/setter pairs of functions, converting Boolean functions into Bool properties, etc.

Finally, authors of C libraries can customize exactly how their functions are imported into Swift using the new swift_name macro.

Here's an example from the proposal that demonstrates how we can define which type our function gets imported on to.

struct Point3D rotatePoint3D(Point3D point, float radians)
__attribute__((swift_name("Point3D.rotate(self:radians:)")));

The string parameter we pass to swift_name has a bunch of little extra syntax to import things as inits, regular functions, getters/setters, etc.

As always when talking about Swift improvements, it's a good idea to check out the full proposal for more info.

Topics

#249: Enums in Swift 3 🐤

Topics

We've been looking at some the changes coming in Swift 3 recently in Bites #243 and #244. We'll continue our tour today by checking out what is new the world of Swift Enums. Let's dive right in.

We'll start with the obvious, in Swift 2 (and earlier) enums have been PascalCased. In Swift 3, they are camelCase.

Before:

enum PersonalityTrait {
  case Extraversion(multiplier: Float)
  case Agreeableness(multiplier: Float)
}

After:

enum PersonalityTrait {
  case extraversion(multiplier: Float)
  case agreeableness(multiplier: Float)
}

The Swift 3 migrator in Xcode 8 will prompt us to convert all of our enums to this new convention.

Additionally, all of Apple's frameworks have this new convention applied:

label.textAlignment = .right
label.baselineAdjustment = .alignCenters
label.lineBreakMode = .byWordWrapping

In Swift 2, there was some inconsistency about the requirements around when the "dot" in the "dot notation" was required.

In Swift 2, all of this was valid:

enum AdvertisementKind {
  case Small, Medium, Large, Giant

  func debugDescription() -> String {
    switch self {
      case Small: return "Small Ad"
      case .Medium: return "Medium Ad" // leading dot
    }

    if self == Large {
      return "Large Ad"
    }

    if self == .Giant {
      return "Giant Ad"
    }
  }
}

Note how enum cases are used interchangeable both with and without dots.

In Swift 3, this has been cleaned up and dots are now required when using this shorthand style to access enum cases. There's one exception and that is static functions on enums, which still infer self:

enum AdvertisementKind {
  case small, medium, large, giant

  static func randomKind() -> AdvertisementKind {
    return medium
  }

Watching Swift evolve in these ways is a great way to understand not just the syntactic structural changes being made, but also the reasoning and goals behind them. Those interested should definitely read over the proposal that prompted this change here.

The Swift language is always evolving. Bugs are being fixed, new proposals are implemented, etc. It'd be great if we could try out these features out as they're being implemented, without having to wait for official releases.

Today we'll take a look at how to download, install, and try out the latest Swift Toolchain in Xcode. Let's get started!

We'll begin by heading to https://swift.org/download/ and looking for the "Snapshots" section. Here we can also find many other preview releases, etc.

Click "Xcode" to download an installer:

We'll run open the installer, and complete its steps.

We can now open Xcode and select our new Toolchain.

We can also manage Toolchains in Xcode's preferences:

With our new Toolchain selected, all builds will use it, neat!

Getting involved with Swift's development is a great way to stay informed on where things are headed. We'll look at getting even more involved in future Bites!

Page 1 of 4