Storing and using references to types, including init

We can put a static function declaration in a protocol.

  protocol MyProtocol {
    static func doSomething()->String
  }

as easily as any other type. But you may be wondering, how would we ever use it? Static methods aren't called from instances, and without an instance, how do we know which type we have?

  var someType:MyProtocol.Type?

To store a reference to a type, or to a type that conforms to a protocol, use the type or protocol name, followed by .Type. Here, we've also made it optional.

  struct MyStruct : MyProtocol {
    static func doSomething()->String {
      return "MyStruct did something"
    }
  }
  someType = MyStruct.self

We provide a value for this type by using a type name, and then .self.

  let text:String? = someType?.doSomething()  //"MyStruct did something"

Calling the static method then proceeds like any other method call on an optional type:

  struct MyIncompatibleType {
    static func doSomething()->String {
      return "MyIncompatibleType did something"
    }
  }
  someType = MyIncompatibleType.self  //compile error, type mismatch

We cannot provide a reference to a type that does not conform to the protocol.

I've found cases where a reference to a type of class was useful, but what's more useful is to be able to initialize objects of different types depending on some kind of other value.

  protocol ExpressibleByJSON {
    init(json:[String:Any])throws
  }

To do that, we'll need to declare an init method in a protocol.

  struct KeySignature:ExpressibleByJSON {
    let baseNote:Int
    init(json:[String:Any]) throws {
      //...
    }
  }

For a struct, we just implement the init method. That's all.

But for a class, we need to think about subclasses.

  class KeySignature:ExpressibleByJSON {
    let baseNote:Int
    required init(json:[String:Any]) throws {
      //...
    }
  }
``
  class MajorKeySignature : KeySignature {
    //...
  }

A subclass conforms to all the protocols its super class conforms to, which means it needs to respond to the init(json:... method. Since it isn't usual to inherit initializers in this way, we have to use the required keyword.

  class MajorKeySignature : KeySignature {
    required init(json:[String:Any]) throws {
      //...
    }
  }

This means all subclasses are "required" to implement this initializer.

That completes the implementation. Here's how we use it.

Let's say we want to allocate instances of the best "key signature" given some JSON.

  class MinorKeySignature : KeySignature {
    required init(json:[String:Any]) throws {
      //...
    }
  }

  extension KeySignature {
    static var signatureTypes:[String:KeySignature.Type] = [
      "major":MajorKeySignature.self
      ,"minor":MinorKeySignature.self
    ]
    static func new(json:[String:Any])throws->KeySignature {
      if let commonType = json["type"] as? String
        ,let initingType = signatureTypes[commonType]
      {
        return try initingType.init(json:json)
      }
      return try KeySignature(json:json)
    }
  }

So, let's look at this dynamic initialization. We've made a static function on our KeySignature type. It extracts a single value from our JSON, and uses that as a key into a Dictionary of types. That Dictionary is also staticl it's a property. It then calls the init method on the type to create an instance of the particular KeySignature concrete type.

  class KeySignature
    //...
    var json:[String:Any] {
      return ["base":baseNote]
    }

  class MajorKeySignature ...
    override var json:[String:Any] {
      var superJSON:[String:Any] = super.json
      superJSON["type"] = "major"
      return superJSON
    }

One principle of writing great code is "Don't Repeat Yourself", or "DRY". We've repeated critical keys into JSON objects as string literals. Here's some serialization code that turns the KeySignatures back into JSON, we need the same keys there. These keys also appear as "magic values" in the code. I know Apple is big on magic, but "magic values" are bad, because they don't convey intention, and they're easy to break and not realize it. So let's make these more semantic.

  extension KeySignature {
    struct JSONKeys {
      static var type:String = "type"
      static var majorType:String = "major"
      static var minorType:String = "minor"
      static var baseNote:String = "base"
    }
    static var signatureTypes:[String:KeySignature.Type] = [
      JSONKeys.majorType : MajorKeySignature.self
      ,JSONKeys.minorType : MinorKeySignature.self
    ]
    static func new(json:[String:Any])throws->KeySignature {
      if let commonType = json[JSONKeys.type] as? String
        ,let initingType = signatureTypes[commonType]
      {
        return try initingType.init(json:json)
      }
      return try KeySignature(json:json)
    }
  }

  class KeySignature
  //...
    var json:[String:Any] {
      return [JSONKeys.baseNote:baseNote]
    }

  class MajorKeySignature ...
    override var json:[String:Any] {
      var superJSON:[String:Any] = super.json
      superJSON["type"] = "major"
      return superJSON
    }

We've added a nested type, JSONKeys, with static vars that provide the key values for the JSON we'll be transforming into KeySignatures. In our serialization code somewhere else, we'd use these same references, removing the repetition of the magic values.

Unlike most of the convenient features we've added in Swift, moving our JSON key constants into static properties of a nested type added more text instead of making our code shorter. But, we've removed the magic values by making their use more semantic, and avoided repeating ourselves if we use these values other places in code. We've also name-spaced the JSON keys. If I have another type, a TimeSignature, for example, it can contain its own JSONKeys nested struct, with its own static properties.

Option Sets

Declaring OptionSets

Ok, one more piece of magic with statics. In C-based languages, it's common to pass a number of boolean conditions by packing them as bits in an integer type. Apple's Cocoa API's often call these "options". Using them usually meant taking a break from Obj-C coding to review the bitwise operators semantics, and then conjure some magic formulas to combine, remove or mask specific values or ranges.

In Swift, Sets are built in to the language, so it would be very nice if we had some kind of optimized way to pack Sets of Bools into an Int, and that's exactly what an OptionSet is.

  struct GZFlags : OptionSet {
  }

First of all, we create a new struct, and have it inherit from the OptionSet protocol. This will be part of a GZip file reader, which has an 8-bit options field.

If we know how many options we'll need, we know how big our Int needs to be. For our use, we'll pick UInt8, which as you'll remember, is a 1-byte unsigned integer.

  struct GZFlags : OptionSet {
    let rawValue:UInt8
  }

We add a rawValue field with the type that you want backing the options. This uses a feature called "associated types" for protocols, which we'll cover in a later week.

  struct GZFlags : OptionSet {
    var rawValue: UInt8
    static let isText:GZFlags = GZFlags(rawValue:1)
    static let headerCRC:GZFlags = GZFlags(rawValue:2)
    static let hasExtraFields:GZFlags = GZFlags(rawValue:4)
    static let hasFileName:GZFlags = GZFlags(rawValue:8)
    static let hasComment:GZFlags = GZFlags(rawValue:16)
  }

As the last step, we add as many literal values as we like. Each is a static property, and initialized with an integer value that turns on a single bit. The magic of protocols and extension defaults and associated types is what enables this default init(rawValue: method to exist.

Using OptionSets

Suppose we have an function with a GZFlags as an argument:

  func writeBlock(options:GZFlags) {
    //...
  }

We'll take advantage of two features of Swift to provide values.

  writeBlock(options:[])

1) We'll use an array to initialize the Set. Swift doesn't provide a dedicated Set-initialization syntax, so we'll create an array of values.

2) We'll take advantage of Swift's type inference to reference merely the static property names.

  writeBlock(options:[.isText, .hasComment])

Notice that we're not writing lots of <<, & or | bit-wise operators to combine these values with "masks", we're providing a list of semantic constants.

  func writeBlock(options:GZFlags) {
    if options.contains(.isText) {
      ...

When reading values from the OptionSet, we're also not using bit-wise operations. We use standard SetAlgebra functions, like .contains, or .intersect.

As you read through the Swift standard module, and the included Foundation, and Dispatch modules, you'll see OptionSets are frequently nested types.

Summary

Today, we learned how to use statics from protocols with type references. We learned to DRY our code and eliminate magic values by creating static properties on nested types. Last, we learned how to replace bit-flag integers with OptionSet types.

Treating bit-flag-packed integers as Sets, now that's Swift!