Rich Ellis
Rich Ellis

Reputation: 99

extending Foundation classes to conform to Swift protocol

I am trying to define a protocol for which I would like to add conformance to several Foundation classes as well as some custom types of my own. I first tried doing this with a convenience initializer in the protocol, but that does not seem possible. I read in the linked thread on the Apple dev forums where they talked about using a class method that returns type Self, but I am unable to figure out how to go about this.

typealias JSONObject = AnyObject
protocol JSONParseable {
    static func fromJSONObject(jsonObject: JSONObject) throws -> Self
}

extension NSURL: JSONParseable {
    class func fromJSONObject(jsonObject: JSONObject) throws -> Self {
        guard let jsonString = jsonObject as? String else {
            throw JSONParseError.ConversionError(message: "blah")
        }
        guard let result = NSURL(string: jsonString) else {
            throw JSONParseError.ConversionError(message: "blah")
        }
        return result // Error: cannot convert return expression of type 'NSURL' to return type 'Self'
    }
}

I found a similar question but the answer there was to mark the class as final -- I obviously can't do that on a Foundation class.

Could someone explain how to fix my approach above? Or suggest a different approach for adding protocol conformance to Foundation classes?

Upvotes: 2

Views: 384

Answers (1)

R Menke
R Menke

Reputation: 8401

Use self.init(....) instead of NSURL(....) since this also needs to work for NSURL subclasses.

protocol JSONParseable {
    static func fromJSONObject(jsonObject: JSONObject) throws -> Self
}

extension NSURL : JSONParseable {

    class func fromJSONObject(jsonObject: JSONObject) throws -> Self {

        guard let jsonString = jsonObject as? String else {
            throw JSONParseError.ConversionError(message: "blah")
        }
        guard let result = self.init(string: jsonString) else {
            throw JSONParseError.ConversionError(message: "blah")
        }
        return result
    }
}

Hack : define a typealias T, this will return an NSURL even for subclasses.

protocol JSONParseable {
    typealias T = Self
    static func fromJSONObject(jsonObject: JSONObject) throws -> T
}

extension NSURL : JSONParseable {

    typealias T = NSURL
    class func fromJSONObject(jsonObject: JSONObject) throws -> T {

        guard let jsonString = jsonObject as? String else {
            throw JSONParseError.ConversionError(message: "blah")
        }
        guard let result = NSURL(string: jsonString) else {
            throw JSONParseError.ConversionError(message: "blah")
        }
        return result
    }
}

Upvotes: 2

Related Questions