Reputation: 294
Let me start by saying what I have works...
let a:String
let b:Int
let c:Double
init?(doc:XMLDocument){
guard let tempString = (try? doc.objects(forXQuery: "path/path/A").first as? XMLNode)?.stringValue else {return nil}
self.a = tempString
guard let tempString2 = (try? doc.objects(forXQuery: "path/B").first as? XMLNode)?.stringValue else {return nil}
guard let tempInt = Int(tempString2) else {return nil}
self.b = tempInt
guard let tempString3 = (try? doc.objects(forXQuery: "path/path/C").first as? XMLNode)?.stringValue else {return nil}
guard let tempDouble = Double(tempString3) else {return nil}
self.c = tempDouble
}
}
I am not happy with it. I can simplify by doing null check and reusing a temporary variable
init?(doc:XMLDocument){
var tempString:String? = (try? doc.objects(forXQuery: "path/path/A").first as? XMLNode)?.stringValue
if tempString != nil {self.a = tempString!} else {return nil}
tempString = (try? doc.objects(forXQuery: "path/B").first as? XMLNode)?.stringValue
if tempString != nil && Int(tempString!) != nil {self.b = Int(tempString!)!} else {return nil}
tempString = (try? doc.objects(forXQuery: "path/path/C").first as? XMLNode)?.stringValue
if tempString != nil && Double(tempString!) != nil {self.c = Double(tempString!)!} else {return nil}
}
Ideally I want to be able to assign the properties as I check them without going through some sort of temporary variable.
something like:
struct Foo {
let a:String
let b:Int
let c:Double
init?(doc:XMLDocument){
do {
if case a = try (doc.objects(forXQuery: "path/path/A").first as? XMLNode)?.stringValue { } else {return nil}
if case b = Int(try (doc.objects(forXQuery: "path/path/B").first as? XMLNode)?.stringValue ?? "ZZZ") { } else {return nil}
if case c = Double(try (doc.objects(forXQuery: "path/path/C").first as? XMLNode)?.stringValue ?? "ZZZ") { } else {return nil}
} catch {
return nil
}
}
}
This errors with use of self without initializing all stored properties.
Is there a better way to do it?
Upvotes: 0
Views: 48
Reputation: 294
building off of Kamran's answer I got this.
extension XMLDocument {
func firstNode(forXQuery: String) throws -> XMLNode{
do {
guard let temp = try self.objects(forXQuery: forXQuery).first as? XMLNode else {throw InitError() }
return temp
} catch {
throw error
}
}
}
extension XMLNode {
func intValue() throws -> Int {
guard let tempString = self.stringValue else {throw InitError()}
guard let tempInt = Int(tempString) else {throw InitError()}
return tempInt
}
}
extension XMLNode {
func stringValue() throws -> String {
guard let tempString = self.stringValue else {throw InitError()}
return tempString
}
}
extension XMLNode {
func doubleValue() throws -> Double {
guard let tempString = self.stringValue else {throw InitError()}
guard let tempDouble = Double(tempString) else {throw InitError()}
return tempDouble
}
}
extension XMLNode {
func floatValue() throws -> Float {
guard let tempString = self.stringValue else {throw InitError()}
guard let tempFloat = Float(tempString) else {throw InitError()}
return tempFloat
}
}
which pretties my initializer to
struct Foo {
let a:String
let b:Int
let c:Double
init?(doc:XMLDocument){
do {
a = try doc.firstNode(forXQuery: "path/path/a").stringValue()
b = try doc.firstNode(forXQuery: "path/path/b").intValue()
c = try doc.firstNode(forXQuery: "path/path/c").doubleValue()
} catch {
return nil
}
}
}
Much better, but it still feels loose.
Upvotes: 0
Reputation: 15248
I would suggest adding some helpful extensions to XMLDocument
and XMLNode
as below,
extension XMLDocument {
public func xmlNode(forXQuery query: String) throws -> XMLNode? {
return try self.objects(forXQuery: query).first as? XMLNode
}
}
extension XMLNode {
public var intValue: Int? {
if let value = self.stringValue, let intValue = Int(value) {
return intValue
}
return nil
}
public var doubleValue: Double? {
if let value = self.stringValue, let doubleValue = Double(value) {
return doubleValue
}
return nil
}
}
So, now your struct will look like this,
struct Foo {
let a: String
let b: Int
let c: Double
init?(doc: XMLDocument) {
guard let a = try? doc.xmlNode(forXQuery: "path/path/A")?.stringValue,
let b = try? doc.xmlNode(forXQuery: "path/B")?.intValue,
let c = try? doc.xmlNode(forXQuery: "path/path/C")?.doubleValue else { return nil }
self.a = a
self.b = b
self.c = c
}
}
Upvotes: 1