Skip to main content

Mike Kreuzer

Conditional conformance in Swift 4.2

June 7, 2018

Exciting new stuff coming out of WWDC this week, including info on what's new in Swift 4.2.

Swift 4.2 comes with Xcode 10.0 beta, and among other goodies includes some changes to how protocols apply to arrays. I've just updated an earlier post on this (Is serializable really a word?), but it's worth repeating here: this is exciting stuff. Now, with the single line:

extension Array: Serializable where Element: Serializable {}

…the Serializable protocol I wrote applies to all arrays of Serializable elements. That is so clean it is to my eyes simply beautiful. Testament to a lot of hard work.

This info come via the talk "What's new in Swift?", worth watching as as ever are all the other WWDC videos.

Tags:

Game machine

March 4, 2018

I had the yen to play some computer games. I used to play a lot, but haven't played any these last few years for various I-had-kids related reasons. Having kids is great, but the gaming has suffered. Now I have a bit more time, so I looked at what there was to play on a Mac. I looked hard. At pretty much every game that had been made for the platform. Ever. Ten minutes later I decided to buy or build another Windows box.

Then I had another idea. The games I wanted to play were mostly older, so I didn't need anything too fancy, and I had my old Mac Mini sitting in a drawer, unloved. Would Windows 10 boot? The machine was no longer able to get upgrades, it was the now nine year old 2009 model… Long story short, yes, Windows worked fine. It worked better than fine, it now worked better than whatever version of MacOS had been on there.

Windows still sucked. But Windows 10 sucked a lot less than Windows 8 did, and if there's one thing Microsoft does right it's backwards compatibility. The suckage & the backwards compatibility are not unrelated things, that's the compromise they, and now I have made. I was playing games from 1997, these games are twenty years old, and here I am, feeling nostalgic and happy.

So far the sound doesn't work, but I'm not too fussed about that, and stuff with 16 bit installers (some of these games are old) is off limits… as is the one game I had written with the XNA game framework (which I vaguely remember as a C# game framework from 2007 or so). But mostly (as someone else used to say) it just works.

For work and work related play my MBP hits it out of the park, but some of my evenings are now spent otherwise, in gorgeous and glorious turn based 2D.

Tags:

Is serializable really a word?

September 30, 2017

Swift 4 brought with it two things I've been waiting for since Swift's early days: multi-line strings and JSON serialisation. I've seen a few half implementations of a serialising protocol extensions recently, so for completeness here's a fuller version, with failable initialisation, reading & writing to files, and some date sanity.

import Foundation

protocol Serializable: Codable {
    init?(from source: Data?)
    init?(contentsOf fileURL: URL?) throws
    func save() -> Data?
    func save(to fileURL: URL) throws
}

extension Serializable {
    init?(from source: Data?) {
        guard let source = source else {
            return nil
        }
        let decoder = JSONDecoder()
        decoder.dateDecodingStrategy = .iso8601
        if let val = try? decoder.decode(Self.self, from: source) {
            self = val
        } else {
            return nil
        }
    }
    init?(contentsOf fileURL: URL?) throws {
        guard let fileURL = fileURL else {
            return nil
        }
        self.init(from: try Data(contentsOf: fileURL))
    }
    func save() -> Data? {
        let encoder = JSONEncoder()
        encoder.dateEncodingStrategy = .iso8601
        return try? encoder.encode(self)
    }
    func save(to fileURL: URL) throws {
        guard let data = self.save() else {
            return
        }
        try data.write(to: fileURL, options: .atomic)
    }
}

(And I forgot to put decoder.dateDecodingStrategy in there when I first published this. Damn.)

I went with init and save rather than serialize and deserialize partly because init felt right, but also because serialize and deserialize inevitably make me scratch my head about which is which. I'm still in two minds about whether to swallow those error throws, or to demand an unwrapped URL… maybe, I mostly tend to use throws for disk operations so this mix feels right for now.

From then on serialisation is a simple function call anywhere you invoke the protocol:

struct SerializableStruct: Serializable {
    let name: String
}

let myStruct = SerializableStruct(name: "ok")
let data = myStruct.save()
let aStructAgain = SerializableStruct(from: data)

UPDATE - June 7, 2018 – The following is solved now, see below

The only issue I have so far – and this is minor compared to the joy I feel at the thought of dozens of Swift to JSON libraries finally being killed by Codable – is that collections don't automagically know about this. Protocol "inheritance" only works down the chain, so for example an array of structs which are declared to be serializable has to be encoded and decoded "manually", or else wrapper inside another struct or class that is itself serializable, so:

// eg for
let plainArray = [myStruct]

//either
struct ArrayWrapper: Serializable {
    let wrapper: [SerializableStruct]
}
let wrappedArray = ArrayWrapper(wrapper: plainArray)
let dataYetAgain = wrappedArray.save()

// or
let dataYetAgainAgain = try? JSONEncoder().encode(plainArray)

// etc

Unless I'm doing something wrong of course!

UPDATED FOR Swift 4.2 - June 7, 2018

Nope, not doing anything wrong until earlier this week with Swift 4.2. Applying the extension to Array when its elements are Serializable "just works" now, which is very, very nice.

Continuing the original example:

// new in 4.2
extension Array: Serializable where Element: Serializable {}

let myStruct2 = SerializableStruct(name: "woo hoo!")
let plainArray = [myStruct, myStruct2]
let dataAgain = plainArray.save()
let arrayAgain = Array<SerializableStruct>(from: dataAgain)
Tags:

Previously

Earlier posts...