NSCoding-like Persistence


(Jon Hull) #1

What is the current plan for persisting things like enums and structs in Swift?

After the POP talk at WWDC, I excitedly rewrote many of my frameworks to use protocols and structs/enums instead of NSObject subclasses. For the most part, this has been fantastic… most things are much more powerful/flexible using protocols and value types. The one problem I keep running into, however, is how to persist things in the same way you can with NSCoding. NSCoding requires NSObject as a base class.

I spent the last couple of weekends playing with ideas for a persistence framework and I was able to get about 90% of the way there. I was able to encode types like [(Int,Float)] by supplying closures to the encoding function. For example: func encodeArray<T>(array:[T], forKey:String, mapping:(T)->Encoding). There is a simpler version without the closure for arrays where T adheres to my “Encodable” protocol: func encodeArray<T:Encodable>(array:[T], forKey:String). I also have legacy support for objects which adhere to NSCoding.

Instead of going straight to data like NSCoding, I encode to an intermediate format (A struct with an internal enum). Then you have another protocol which can turn that format into data, json, plist, etc…

90% working, but I ran into a few problems. First, I have to use this horrible thing to get the type system to cooperate in some cases:

func horribleAutocastHack<T>(x:Any, fail:(()throws-> T)? = nil)throws -> T{
    if let cast = x as? T{
        return cast
    }
    if let fail = fail{
        return try fail()
    }
    fatalError("Autocast failed")
}

Second, I had to use static methods instead of init in my protocol, since init fails on classes where I don’t have access to the code (e.g. NSData). It wants me to mark it both required and final, which I can’t do.

Third, I have no idea how to persist closures. It may just be flat out impossible. The big effect it has had is that anywhere where I had to build a thunk to store a collection of heterogeneous objects (adhering to a protocol with self requirements) it can no longer be persisted. This is actually the biggest issue, and I haven’t even been able to hack around it.

Finally, I am pushing the generics system pretty hard. My first version was randomly crashing the compiler so I had to pull back on that front (e.g. having encodeArray instead of just encode which infers the arrayness). All of the various discussions around factory methods are also relevant.

I was able to get the rest working though.

Is there an official plan which I am duplicating effort on? I would be happy to share my code (after a couple more iterations) if that would be helpful/inspiring. I would also be happy to just work on other things and wait for the official solution if there is one coming… I may have to wait for more powerful generics in any case.

I feel like this is one of those things that is impacting a lot of real world codebases, and delaying adoption of POP, value types, etc...

Thanks,
Jon