October 24, 2014

Swift, Reflection, Downcasting, Protocols, and Structs: A solution

Posted in iPhone development, mac development tagged , , , at 10:01 pm by tetontech

Currently, Swift does not have the ability to do full reflection. It can, through the mirror struct, examine property names and values. Swift does not allow you to set the values directly. You can, however, instantiate a struct based on the structs type but the compiler will crash if you attempt to do this in any anonymous way. In addition to no real reflection abilities, Swift objects and structs do not support key-value coding. It turns out that a solution to some of the reflection issues in Swift is the addition of key-value coding to structs. An additional restriction to doing reflection in Swift is the inability to downcast an instance who’s type Any or AnyObject to a known protocol. It may be that Apple is resolving these issues but that doesn’t help those of us who need to do reflection now. Let’s look at what is available in Swift. This will show us its current limitations.

The reflect function and MirrorType

The reflect function and MirrorType structure were designed by Apple to display information for the Xcode IDE. Xcode uses these to show debugging information. This is why the results are read-only. The example below shows a struct being examined. The struct is not complex. It represents a person and has name, age, optional height, and calculated description properties.

struct Person:Printable{
    var name:String
    var age:Int
    var height:Double?
    var description:String {
        return "name:\(self.name) age:\(self.age) height:\(self.height)"
    }
}

Using the standard MirrorType and the reflection function we can get almost all of the properties and their values. Unfortunately there are problems accessing both the description property and working with the height. The description doesn’t show up in the children and the value returned for the height is an optional.

let aPerson = Person(name:"Sally", age:35, height:5.9)
let structMirror = reflect(aPerson)
let numChildren = structMirror.count
println("child count:\(numChildren)")
for index in 0..<numChildren{
   let (propertyName, propertyMirror) = structMirror[index]
   println("name: \(propertyName) value: \(propertyMirror.value)")
}

Run Results:

child count:3
name: name value: Sally
name: age value: 35
name: height value: Optional(5.9)

 

The height being an optional would not be a problem except there is no way to ask if something is an optional. If there was, then we could eventually get the underlying value. Since there isn’t a way to detect an optional I could not use this approach in my project. I needed the actual value. I needed it to corrctly building a piece of Sqlite insertion SQL. You will probably run into the same limitation in your projects.

Instantiation

In Swift an instance can be created from the struct or object’s description. Doing this to create a new Person is shown below.

let child = Person.self(name: "bob", age: 3, height: 2.5)

This works great as long as you know ahead of time what type of instance needs to be created. It is possible to store the Person.self value, it’s type is Metatype, in a constant, variable, or collection and create an instance using the variable.

let personType = Person.self
let personFromType = personType(name: "jessie", age: 14, height: 5.2)

Creating collections of different types of Metatypes, a feature needed in most reflection based coding, is also possible. It does require that each struct implement a common protocol. For this example I’ve created the Thing protocol. It declares that any Thing must have a name of type string.

protocol Thing{
    var name:String { get set }
}

I’ve modified the Person struct to implement Thing and created a Dog struct that is also a Thing.

struct Person:Printable,Thing{
    var name:String
    var age:Int
    var height:Double?
    var description:String {
        return "name:\(self.name) age:\(self.age) height:\(self.height)"
    }
}
struct Dog:Thing{
    var name:String
    var breed:String
}
protocol Thing{
    var name:String { get set }
}

Now an Array or a Dictionary can be created that holds both Person and Dog Metatypes.

let initializerList:[Thing.Type] = [Person.self, Dog.self]
let initializerDict:[String:Thing.Type] = ["Person":Person.self, "Dog":Dog.self]

It seems strait forward to initialize a Person. The following code seems like it should work but fails to compile.

let aMetaType = initializerDict["Person"]!
let anotherPerson = aMetatype()

The compiler failure says that a Thing doesn’t have an init function. That is true. The Thing protocol has no init function with no parameters defined. We can add one and modify the structs to also have init functions with no parameters. Now the compiler error goes away, but to do this well all the properties of the structs have to either be Optionals or have meaningful default values. Changing the properties to Optionals causes the problem mentioned earlier regarding getting actual values out of Optionals without know what type they are.

Adding an init to the Thing protocol causes another problem. Initializing a struct from the stored Metatype causes the compiler to segmentation fault (crash). The compiler should have either failed to compile the code since Thing doesn’t have an actual init function or a virtual lookup should have been done to call the Person’s init function.

So we have hit three dead ends using Swift’s standard behavior. We can’t get actual values from optional properties, we can’t create instances from commonly stored Metatypes, and the compiler crashes when we try to do anything complex.

Another reflection issue in Swift is the inability to downcast from Any to a custom protocol type, another common need when using reflection in code. You can downcast to a standard type like String (anAny as String) and even a custom type like Person (anAny as Person). But there is a compilation failure if you try to downcast to Thing (anAny as Thing). The error states that Any and Thing are unrelated. This indicates that custom protocols are not an Any.

What useful reflection can be done under these limitations?

A Solution

As mentioned in a previous post, I’m writing a library to work with Swift structs in the same way that CoreData works with objects. To create this library I  must do reflection. I need to convert structs into SQL statements and Sqlite result sets into Arrays of structs who’s types are unknown. To get around Swift’s current reflection limitations I applied key-value coding. The custom protocol KeyValueCodable makes this possible.

protocol KeyValueCodable{
    var KVTypeName:String {get}
    init()
    subscript(index:String)->Any? { get set }
    func instantiate()->KeyValueCodable
    func downCastFromAny(anAny:Any)->KeyValueCodable?
}

KeyValueCodable includes an initializer without any parameters, but it is only used by KeyValueCodable‘s instantiate function so we avoid the compiler crashing problem. It overcomes the issue of retrieving actual values from optionals by having a subscript that can return and set the values of any type of property. It overcomes the down casting problem by having each struct do its own downcast. It also overcomes a previously unmentioned reflection problem. You can’t get a string representation of a struct’s name from Swift’s standard reflection behavior.

Using KeyValueCodable puts more responsibility on the programmer than is usually required in other languages, but it makes both reflection and key-value coding possible. Here is the Person struct modified to implement the KeyValueCodable protocol. There is much more code in Person than there was before, but most of it is boiler-plate code. It can be copied, pasted, and modified.

struct Person:Printable,KeyValueCodable{
    let KVTypeName = "Person"
    var name:String?
    var age:Int?
    var height:Double?
    var description:String {
        return "name:\(self.name) age:\(self.age) height:\(self.height)"
    }
    init(){}
    func instantiate() -> KeyValueCodable {
        return Person()
    }
    func downCastFromAny(anAny: Any) -> KeyValueCodable? {
        if isKVCodable(anAny){
            let mirror = reflect(anAny)
            let numChildren = mirror.count
            var aPerson = Person()
            for index in 0..<numChildren{
                 let (propertyName, propertyMirror) = mirror[index]
                 switch propertyName{
                 case "id":
                     aPerson["id"] = propertyMirror.value as? String
                 case "name":
                     aPerson["name"] = propertyMirror.value as? String
                 case "height":
                     aPerson["height"] = propertyMirror.value as? Double
                 case "age":
                     aPerson["age"] = propertyMirror.value as? Int
                 default:
                     0//do nothing
                 }
             }
             return aPerson
         }
         return nil
     }
     subscript(index:String) -> Any?{
        get{
            switch index{
            case "name":
                return name
            case "age":
                return age
            case "height":
                return height
            case "KVTypeName":
                return KVTypeName
            default:
                return nil
            }
        }
        set(aValue){
            switch index{
            case "name":
                name = aValue as? String
            case "age":
                age = aValue as? Int
            case "height":
                height = aValue as? Double
            default:
                0//do nothing
            }
        }
    }
}

Person now has a subscript which can be used to get and set the value of any parameter. This is also true when the Person has been upcast to a KeyValueCodable. Person can now be ‘downcast’ from an Any. This is done by creating a copy of the data in the Any and applying it to a new Person. This isn’t such a big deal since Swift structs are copied every time they are passed to a function anyway.

There is a function, isKVCodable, called in the downCastFromAny function. It uses reflect and MirrorType to look for the KVTypeName property required to be part of each KeyValueCodable.

func isKVCodable(possibleCodable:Any?)->Bool{
    if let anActualAny = possibleCodable?{
        let mirror = reflect(anActualAny)
        let numChildren = mirror.count
        var aCodable:KeyValueCodable?
        //discover if is a codable
        for index in 0..<numChildren{
            let (fieldName, fieldMirror) = mirror[index]
            if fieldName == "KVTypeName"{
                return true
            }
        }
    }
    return false
}

Using KeyValueCodable I’ve been able to overcome Swift’s limitation for my reflection heavy project. I look forward to making it even more generic and useful for everyone. Suggestions are appreciated.

In another posting I will describe how to use reflection to find and access a struct’s methods.

 

2 Comments »

  1. Sam Isaacson said,

    Have tried to follow your logic but the following won’t compile due the last line:

    protocol Thing{
    var name:String { get set }
    init()
    }

    struct f1:Thing{
    var name = “AddOne”
    init(){}
    func AddOne(x:Int)->Int{return x+1}
    }

    struct f2:Thing{
    var name = “Div2”
    init(){}
    func DivTwo(x:Double)->Double{return x/1}
    }

    let arr:[Thing.Type] = [f1.self, f2.self]

    let aMeta = arr[0]
    var test = aMeta() \\PROBLEM!!!

    My requirement is actually to store a bunch of functions with different signatures in a collection of type Any and then be able to dynamically downcast them as appropriate – hence my interest in your article.

    • tetontech said,

      The problem you are running into is you are treating a struct as a function. When you retrieved arr[0] it was a f1 struct instance. Adding () to an f1 instance doesn’t make sense to the compiler since it is not a function.

      If you want to store functions in a dictionary you can. To do so, all of your functions must have the same parameter and return type signatures. I’ve modified your functions to have the same signatures and added one of my own as an example.

      func doStuff(parameters:Dictionary)->Any{
      println(parameters[“message”] as String)
      return “done”
      }

      func addOne(parameters:Dictionary)->Any{return 1 + (parameters[“x”] as? Int)!}

      func divTwo(parameters:Dictionary)->Any{return (parameters[“x”] as? Double)!/2}

      Here is the code, broken up line by line to make it more understandable, that creates a dictionary with the functions and then calls them ‘anonymously.’

      let funcArray = [“stuff”:doStuff, “add”:addOne, “div”:divTwo]

      let someFunction = funcArray[“stuff”]!
      var result = someFunction([“message”:”blah”])
      println(result)

      let otherFunction = funcArray[“div”]!
      result = otherFunction([“x”:37.5])
      println(result)

      With the code looking like this, we are really close to the Application Controller Pattern. I’ll do a posting on that.

      Hope this helps.


Leave a comment