Last active
August 29, 2015 14:12
-
-
Save jqsilver/51171d29785ac49f343a to your computer and use it in GitHub Desktop.
Building models from json - inspired by CommandShift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
// Turning the attempt() function into an object with state, primarly to get rid of the inout param | |
// http://commandshift.co.uk/blog/2014/12/28/nice-web-services-swift-edition/ | |
protocol BlankInitable { | |
init() | |
} | |
extension Int : BlankInitable {} | |
extension String : BlankInitable {} | |
extension NSDate : BlankInitable {} | |
class JSONExtractor { | |
let dict: [String: Any] | |
var failed: Bool = false // if you attempt to get a value and it doesn't work | |
var errors = [String: String]() | |
init(_ dict: [String: Any]) { | |
self.dict = dict | |
} | |
// SwiftyJSON has typed accessors for each primitive type | |
// So you could get rid of these methods if you're using that | |
func getString(key: String) -> String { | |
return getVal(key) | |
} | |
func getInt(key: String) -> Int { | |
return getVal(key) | |
} | |
func getVal<Type : BlankInitable>(key: String) -> Type { | |
if let val = dict[key] { | |
if let actual = val as? Type { | |
return actual | |
} else { | |
return fail(key, reason: "expected \(Type.self)") | |
} | |
} else { | |
return fail(key, reason: "not present") | |
} | |
} | |
func fail<Type : BlankInitable>(key: String, reason: String) -> Type { | |
failed = true | |
errors[key] = reason | |
return Type() | |
} | |
} | |
// Experimental usage examples | |
class Thing { | |
let myStr: String | |
init(myStr: String) { | |
self.myStr = myStr | |
} | |
init?(dict: [String: Any]) { | |
let extractor = JSONExtractor(dict) | |
self.myStr = extractor.getString("my_str") | |
// self.myInt = extractor.getInt("my_int") | |
// etc | |
if extractor.failed == true { | |
return nil | |
} | |
} | |
class func fromJSON(dict: [String: Any]) -> (ThingObject?, NSError?) { | |
let extractor = JSONExtractor(dict) | |
let myStr = extractor.getString("my_str") | |
// let myInt = extractor.getInt("my_int") | |
// etc | |
if extractor.failed == false { | |
return (nil, NSError()) | |
} else { | |
return (ThingObject(myStr: myStr), nil) | |
} | |
} | |
} | |
// Trying with a map and KVC | |
class ThingObject: NSObject { | |
let myStr: String | |
init(myStr: String) { | |
self.myStr = myStr | |
} | |
init?(json: [String: Any]) { | |
self.myStr = "" // ugh | |
super.init() | |
let extractor = JSONExtractor(json) | |
let map: [(String, String, String -> Any)] = [ | |
("myStr", "my_str", extractor.getString), | |
("myInt", "my_int", extractor.getInt), | |
] | |
// These ended up not working because setValue only works with AnyObject | |
for (objectKey, jsonKey, extractorFunc) in map { | |
// setValue(extractorFunc(jsonKey), forKey: objectKey) | |
} | |
setValue(extractor.getString("my_str"), forKey: "myStr") | |
if extractor.failed == false { | |
return nil | |
} | |
} | |
} |
Sorry, I didn't notice your comment until now, but you are correct. I eventually discovered that when I started writing tests.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Shouldn't
Be
And
be
You never set failed to
false
otherwise?