Archive for October, 2015

Sets vs Dictionaries smackdown in #swiftlang

Traditional Cocoa has a bad dictionary habit. From user info to font options to AV settings, NSDictionary has long acted as the Cocoa workhorse for passing data. Dictionaries are flexible, easy to use, and a minefield of potential disasters.

In this post, I’m going to discuss an alternative approach, one that’s far more Swift-y. It’s not yet a completely turn-key solution but I think its one that showcases a much better mindset for how APIs should be working in a post-Swift world.

Working with Dictionary-based Settings

Here is some code I pulled out of a random project of mine. (If you’re familiar with my other writings, it’s from my Movie Maker class.) This Objective-C code builds an option dictionary, which is used to construct a AV pixel buffer:

 NSDictionary *options = @{
    (id) kCVPixelBufferCGImageCompatibilityKey : 
        @YES,
    (id) kCVPixelBufferCGBitmapContextCompatibilityKey : 
        @YES,
 };

This example casts the option keys to (id) and uses Objective-C literal notation to transform Booleans to NSNumber instances. The Swift version of this is much simpler. The compiler is smart enough to associate  values with the declared dictionary type.

let myOptions: [NSString: NSObject] = [
    kCVPixelBufferCGImageCompatibilityKey: true,
    kCVPixelBufferCGBitmapContextCompatibilityKey: true
]

Even in Swift, this is far from an ideal way to pass values to APIs.

Characteristics of Setting Dictionaries

Settings dictionaries like the examples you just saw exhibit common characteristics, which are worth examining carefully.

  • They have a fixed set of legal keys. There are about a dozen legal pixel buffer attribute keys in AVFoundation. This collection rarely  changes and those keys are tied to a well-established task.
  • The requested values associated with each key instance are of known types. These types are more nuanced than just NSObject, for example “The number of pixels padding the right of the image (type CFNumber).”
  • Type safety does matter for the passed values, even though this cannot be enforced through an NSDictionary. Both compatibility keys in the preceding example should be Boolean, not Int or String or Array or even NSNumber.
  • Valid entries appears just once in the dictionary, as keys are hashed and new entries overwrite older ones.

In Swift, the characteristics in the above bullets list are far more typical of sets and enumerations than dictionaries. Here are some reasons why.

  • An enumeration expresses a full range of possible options for a given type. Most Cocoa APIs similar to this example have fixed, unchanging keys.
  • Enumerations enable you to associate typed values with individual cases. Cocoa APIs document the types they expect to be passed for each key.
  • Like dictionaries, sets restrict membership to avoid multiple instances.

For these reasons, I think these settings collections are better expressed in Swift as sets of enumerations instead of an [NSString: NSObject] dictionary.

Transitioning from Keys

Stepping back a second, consider the current state of the art. AVFoundation defines a series of keys like the following. (This is not a complete set of the pixel buffer keys, by the way)

const CFStringRef kCVPixelBufferPixelFormatTypeKey;
const CFStringRef kCVPixelBufferExtendedPixelsTopKey;
const CFStringRef kCVPixelBufferExtendedPixelsRightKey;
const CFStringRef kCVPixelBufferCGBitmapContextCompatibilityKey;
const CFStringRef kCVPixelBufferCGImageCompatibilityKey;
const CFStringRef

Each of these items is a  constant string and each string is used to index dictionaries. Callers build dictionaries using these keys, passing arbitrary objects as values.

In Swift, you can re-architect this key-based system into a simple enumeration, specifying the valid type associated with each key case. Here’s what this looks like for the five cases listed above. The  associated types are pulled from the existing pixel buffer attribute key docs.

enum CVPixelBufferOptions {
 case CGImageCompatibility(Bool)
 case CGBitmapContextCompatibility(Bool)
 case ExtendedPixelsRight(Int)
 case ExtendedPixelsBottom(Int)
 case PixelFormatTypes([PixelFormatType])
 // ... etc ...
}

Re-designing options this way produces an extensible enumeration with strict value typing for each possible case. This approach offers you a major type safety victory compared to weak dictionary typing.

In addition, the individual enumeration cases are also clearer, more succinct, and communicate use better than exhaustively long Cocoa constants. Compare these cases with, for example,  kCVPixelBufferCGBitmapContextCompatibilityKey. The Cocoa names take responsibility for mentioning their use as a constant (k), their related class (CVPixelBuffer), and their usage role (key), all of which can be dropped here.

Building Settings Collections

With this redesign, your call constructing a set should look like the following example. I say should because this example will not yet compile.

// This does not compile yet
let bufferOptions: Set<CVPixelBufferOptions> = 
    [.CGImageCompatibility(true), 
     .CGBitmapContextCompatibility(true)]

Swift cannot compile this because the CVPixelBufferOptions options are not yet Hashable. At this point, all you can build is an array, which does not guarantee unique membership:

// This compiles
let bufferOptions: [CVPixelBufferOptions] =
    [.CGImageCompatibility(true),
     .CGBitmapContextCompatibility(true)]

Arrays are all well and lovely but they lack the unique options feature that dictionaries offer and that is driving this redesign.

Differentiating Values

The Hashable protocol enables Swift to differentiate instances which are basically the same thing from instances which are different things. Both sets and dictionaries use hashing to ensure that members and keys are unique. Without hashing, they cannot provide these assurances.

When establishing settings collections, you want to build sets that won’t construct an example like the following, where multiple members with conflicting settings are present at the same time:

[.CGImageCompatibility(true),
 .CGImageCompatibility(false)] // which one?!

These are clearly two distinct enumeration instances due to the different associated values. In this example, you’ll want a set to discard all identical options that follow the first member added to the set, leaving just the “true” case. (Dictionaries follow the opposite rule. Subsequent additions replace existing members instead of being discarded.)

You achieve this by implementing hashing, which enables you to compare enumeration cases.

Implementing Hash Values

For this specific use-case, you need to create a hashing function that considers  case and only case, and not associated values. There is currently no native construct in Swift that offers this functionality, so you need to build this on your own.

Swift’s Hashable protocol conforms to Equatable, so your implementation must address both sets of requirements. For Hashable, you must return a hash value. For Equatable, you must implement ==.

public var hashValue: Int { get } // hashable
public func ==(lhs: Self, rhs: Self) -> Bool // equatable

Basic enumerations, e.g. enum MyEnum {case A, B, C} provide raw values that tell you which item you’re working with. These are numbered from zero, and are super handy to use. Unfortunately, enumerations with associated values don’t provide raw value support, making this exercise a lot harder than it might otherwise be. So you have to build hash values by hand, which kind of sucks.

Here’s an extension of CVPixelBufferOptions, which manually adds hash values for each case. Ugh.

extension CVPixelBufferOptions: 
    Hashable, Equatable {
    public var hashValue: Int {
        switch self {
          case .CGImageCompatibility: return 1
          case .CGBitmapContextCompatibility: return 2
          case .ExtendedPixelsRight: return 3
          case .ExtendedPixelsBottom: return 4
          case .PixelFormatTypes: return 5
       }
    }
}

public func ==(lhs: CVPixelBufferOptions, 
    rhs: CVPixelBufferOptions) -> Bool {
    return lhs.hashValue == rhs.hashValue
}

On the (slightly) bright side, these hash values have absolutely no meaning and are never exposed to API consumers, so if you need to stick in extra values, you can do so. That said, this approach is ugly and hacky and feels very un-Swift.

Once you add these features, however, everything starts to work. You can build sets of settings, with the assurance that each setting appears just once and their associated values are well typed.

Final Thoughts

The approach described in this post, clunky hashing support and all, is far better than the Cocoa NSDictionary approach. Type safety, enumerations, and set, all provide a better solution for what otherwise feels like an archaic API dinosaur.

What Swift really needs here, though, is something closer to option sets with associated values. At a minimum, adding raw value support to all enumeration cases (not just basic ones that lack associated or intrinsic values) would be a major step forward.

Thanks, Erik Little

Swift Developer’s Cookbook: Status Update #swiftlang

The Swift Developer’s Cookbook just finished copy edits and is now going into layout and proofing. Fingers crossed production can get this out sooner than the official date. You can preorder a copy at Amazon now.

The cookbook will participate in Pearson’s brand new  Content Update Program, which (fingers crossed) will allow material to be refreshed.

  • Chapter 1 Welcome to Modern Swift
  • Chapter 2 Printing and Mirroring
  • Chapter 3 Optionals?!
  • Chapter 4 Closures and Functions
  • Chapter 5 Generics and Protocols
  • Chapter 6 Errors
  • Chapter 7 Types
  • Chapter 8 Miscellany

41XD0j6+b5L._SX388_BO1,204,203,200_

POLL: Which of these #swiftlang books would you pay for?

Some notes: The initial feedback on a tvOS book had low numbers and my Swift posts have massively outperformed all my Apple TV/tvOS posts. If I’m taking on a project, I really need a critical mass of readers to keep this business going. If you don’t see a topic in the following covers, that you think is hot, that needs documentation, and that has legs for potential sales, drop a comment, an email, or a tweet in my direction and let me know what topic it is and why it’s one I should consider writing about. Thanks all.

Announcing Swift Documentation Markup

Now available on iBooks.  Sale price for first week: $4.99.

Screen Shot 2015-10-14 at 3.08.34 PM

And, yes. This book does describe how to incorporate animated kitten gifs into Xcode Quick Help pop-ups.

Overview:

No matter how clear and well written, naked code is never quite self-documenting. There is always a role for comments, whether the in-line narration of key design points or the formal annotation of public declarations. Nearly every modern language, including Swift, offers some kind of structured comment system that documents APIs for developers that consume them.

This short book introduces Swift’s documentation markup system using simple, illustrated examples, with plenty of discussion of best practices. You’ll discover the components that make up Swift’s structured comment system and learn how to best integrate them into your own code.

I’ve built this material out of examples from Swift’s standard library, from release notes, and by reverse-engineering extensible style-sheet specifications. I’ve supplemented core details with a thoughtful discussion of best practices that should stand the test of time as Apple updates this currently undocumented system.

I hope you find this book to be a useful and worthy addition to your development library. I’ve had a great time writing it. Hopefully you’ll have a great time reading it.

Why tech writers can’t do fiction

Especially romance.

“She looked longingly at him. Starting with the upper-left corner of his face, her eyes tracked down and to the right, making sure to scan all emotional features while paying careful attention to possible outlying data points.”

“He carefully rested his hand upon hers with a minimum of pressure to avoid damage to her extremities.”

“‘Darling,’ he said. ‘It appears you are in good health, with adequate teeth and other markers of youth and vitality. Would you like to move east approximately 25 feet into the bedroom, and assume a supine position upon the provided rest area?'”

“‘Oh yes,” she replied, intending to fully comply with the technical specifications he had established. ‘Let us run unit tests without full coverage!'”

Xcode Playgrounds book: New edition submitted to iTunes #swiftlang

cover

It may be a little hard to see the words between “second” and “edition” but it now reads like this:

Screen Shot 2015-10-08 at 4.15.03 PM

Given all the updates I had to do this week, it’s more than a “smidge” and probably should have been a third edition.

Once again I want to thank everyone for your continued support of this book. If you haven’t bought a copy yet, you can grab the latest edition from the iTunes Bookstore. The update should go live tomorrow. Check in iBooks for pending updates.

If you’ve already bought, THANK YOU. Without your support, I would not have the flexibility to keep expanding and enhancing this project. So please keep spreading the word: your evangelism means everything to me. Tell your friends, your boss, your colleagues, and your computer-programming pets to pick up a copy.

So what’s new in this edition? I’ve done a back to front update to incorporate all the new XCPlayground features in Xcode 7.1 beta 3: the new liveView feature that can be used with views and view controllers (and the XCPlaygroundLiveViewable protocol that supports rendering arbitrary model objects), the new ways to continue execution and capture values, etc.

I also found at least one stray println (bad me!), a few places where updated code did not get updated screenshots, and so forth. If you find any errors, please drop me an email. I’m always happy to update where I can.

Next up, when I’m not working on reviewing copyedits for the Swift Developer’s Cookbook (now available for pre-order), I think I’ll be working on my short illustrated guide to header docs.

Thanks again, be well, and don’t be a stranger. I’m open to suggestions, feedback, book requests, and kitten gifs.