I am trying to implement an NSXMLParserDelegate in Swift but I am having issues using the attributes dictionary in some of the delegate methods. For instance, in parser:didStartElement:
:
func parser(parser: NSXMLParser!, didStartElement elementName: String!, namespaceURI: String!, qualifiedName qName: String!, attributes attributeDict: [NSObject : AnyObject]!) {
trying to access attributeDict values like:
if self.feedType == FeedType.RSS {
self.insideLink = true
} else {
if (String(attributeDict["rel"]) == "alternate") && (String(attributeDict["type"]) == "text/html") {
self.link = String(attributeDict["href"])
self.insideLink = false
self.doneWithLink = true
}
}
I get an error message: "'String' is not convertible to 'FeedType'".
enum FeedType:String {
case ATOM = "feed"
case RSS = "rss"
}
class FeedParser:NSObject, NSXMLParserDelegate {
var feedType:FeedType?
Is an error. Same for a dozen other variations on the theme... any insights?
I guess the question is how to properly use the key / value pairs in attributeDict
which are of type [NSObject: AnyObject]
?
Looks like you might have a bogus error message: I'd recommend filing a bug.
I guess the question is how to properly use the key / value pairs in
attributeDict
which are of type[NSObject: AnyObject]
You're right: this looks like the root of the problem. The solution is not to construct a String
from the dictionary lookup, but to (conditionally) cast to String
. That is, rather than try to create a new String
from a reference of indeterminate (and optional) type, ask Swift if that reference is actually to something that can be interpreted as a String
:
let rel = attributeDict["rel"] as? String
That gets you an optional, both because attributeDict
might not contain a value for the key rel
, and because a conditional cast can fail (attributeDict["rel"]
might be a value of a different type). Because you need to check two conditional casts and their unwrapped values, you could end up with a lot of force-unwrap operators (!
) or deeply nested if-let
blocks... that'd get ugly real fast.
Instead, you can take advantage of the fact that Optional
is an enum. Its definition looks something like this:
enum Optional<T> {
case None // aka nil
case Some(T) // what you get when you unwrap a non-nil optional
}
With that, you can use pattern matching to do the lookup, cast, unwrap and test safely and concisely:
switch (attributeDict["rel"] as? String, attributeDict["type"] as? String) {
case let (.Some(rel), .Some(type)) where rel == "alternate" && type == "text/html":
// harvest link
default:
// no match... fall back to other behavior, log an error, etc
}
Note that on the "harvest link" step you'll still need to conditionally cast and unwrap the attributeDict["href"]
lookup. You could do that by nesting an if-let
construct in that case
, or by adding a third lookup to the switch
tuple.
By the way, all the self.
notation is unnecessary in Swift (unless you're in a closure or need to disambiguate method parameters from instance properties).