Xeaza Xeaza - 1 month ago 19
Swift Question

How to deserialize NSDate with SWXMLHash

I'm using SWXMLHash and have written an

extension
on
NSDate
for
XMLElementDeserializable.


I've followed how the basic types are extended at the end of this file.

What I have looks like this:

import Foundation
import SWXMLHash

struct BlogPost: XMLIndexerDeserializable {
let date: NSDate

static func deserialize(blogPost: XMLIndexer) throws -> BlogPost {
return try BlogPost(
date: blogPost["date"].value()
)
}
}

extension NSDate: XMLElementDeserializable {
/**
Attempts to deserialize XML element content to an NSDate

- element: the XMLElement to be deserialized
- throws: an XMLDeserializationError.TypeConversionFailed if the element cannot be deserialized
- returns: the deserialized NSDate value formatted as "EEE, dd MMM yyyy HH:mm:ss zzz"
*/
public static func deserialize(element: XMLElement) throws -> NSDate {
guard let dateAsString = element.text else {
throw XMLDeserializationError.NodeHasNoValue
}

let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "EEE, dd MMM yyyy HH:mm:ss zzz"
let date = dateFormatter.dateFromString(dateAsString)

guard let validDate = date else {
throw XMLDeserializationError.TypeConversionFailed(type: "Date", element: element)
}
return validDate
}
}


However, I'm getting an error that says:




Method 'deserialize' in non-final class 'NSDate' must return 'Self' to
conform to protocol 'XMLElementDeserializable'


I've looked around S.O. for other answers to the same error and I haven't gleaned much information from them.

Any suggestions would be much appreciated. Thanks!

Answer

Okay, this is pretty odd, but the problem exists because NSDate is a class instead of a struct. It apparently takes some work to get a class to conform to a protocol that returns self - it is far easier to get this to work with a struct!

I'll have to add some additional documentation to show how this can work.

Check out the following code (modified from your example) to see if it works for you:

import Foundation
import SWXMLHash

extension NSDate: XMLElementDeserializable  {
    // NOTE: for SWXMLHash 3.0 with Swift 3.0, this will be (_ element: XMLElement)
    public static func deserialize(element: XMLElement) throws -> Self {
        guard let dateAsString = element.text else {
            throw XMLDeserializationError.NodeHasNoValue
        }

        let dateFormatter = NSDateFormatter()
        dateFormatter.dateFormat = "EEE, dd MMM yyyy HH:mm:ss zzz"
        let date = dateFormatter.dateFromString(dateAsString)

        guard let validDate = date else {
            throw XMLDeserializationError.TypeConversionFailed(type: "Date", element: element)
        }

        // NOTE THIS
        return value(validDate)
    }

    // AND THIS
    private static func value<T>(date: NSDate) -> T {
        return date as! T
    }
}

let xml = "<root><elem>Monday, 23 January 2016 12:01:12 111</elem></root>"

let parser = SWXMLHash.parse(xml)

let dt: NSDate = try! parser["root"]["elem"].value()

See also Return instancetype in Swift and Using 'self' in class extension functions in Swift.

NOTE: Added a comment noting that this will look slightly different for SWXMLHash 3.0.