翻訳者の序文
タスクは私のために設定されました: Flickr.comからJSON形式でデータをアップロードし、現時点で写真がモデルの配列に撮影された上位100の場所について:
//------ Places struct Places { var places : [Place] } //----- Place struct Place { let placeURL: NSURL let timeZone: String let photoCount : Int let content : String }
純粋に実用的なタスクに加えて、 Swiftで「型推論」がどのように機能するか、関数型プログラミングにおけるSwiftの機能を確認したかったので、Tony DiPasquale の記事「Efficient JSON in Swift with Functional Concepts」およびジェネリック ' '、ジェネリック
Result : UITableViewController .
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
型
Result : UITableViewController .
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
拡張し
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h ,
FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON.
NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
,
Optional
JSON ( error ), . JSON Objective-C -
NSDictionary
,
. Swift , , , . JSON
Dictionary<String, AnyObject>
.
AnyObject
- , JSON
String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , .
User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, -
User
. , , . , , : . , , API, .
, JSON
typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
,
Either<A, B>
. , , . Swift
Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
,
callback
, , ""
User
, (
error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
,
getUser
switch
Either
, -
User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, ,
Left
NSError
. ,
Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h ,
FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON.
NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
,
Optional
JSON ( error ), . JSON Objective-C -
NSDictionary
,
. Swift , , , . JSON
Dictionary<String, AnyObject>
.
AnyObject
- , JSON
String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , .
User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, -
User
. , , . , , : . , , API, .
, JSON
typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
,
Either<A, B>
. , , . Swift
Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
,
callback
, , ""
User
, (
error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
,
getUser
switch
Either
, -
User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, ,
Left
NSError
. ,
Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h ,
FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON.
NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
,
Optional
JSON ( error ), . JSON Objective-C -
NSDictionary
,
. Swift , , , . JSON
Dictionary<String, AnyObject>
.
AnyObject
- , JSON
String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , .
User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, -
User
. , , . , , : . , , API, .
, JSON
typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
,
Either<A, B>
. , , . Swift
Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
,
callback
, , ""
User
, (
error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
,
getUser
switch
Either
, -
User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, ,
Left
NSError
. ,
Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h ,FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON.NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
,Optional
JSON ( error ), . JSON Objective-C -NSDictionary
,
. Swift , , , . JSONDictionary<String, AnyObject>
.AnyObject
- , JSONString, Double, Bool, Array, Dictionary
null
. JSON , , JSON , .User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, -User
. , , . , , : . , , API, .
, JSONtypealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
,Either<A, B>
. , , . SwiftEither<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
,callback
, , ""User
, (error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
,getUser
switch
Either
, -User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, ,Left
NSError
. ,Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1),Result . Swift , . (constant class
) genericA
.
( . Swiftenum
generic , , , generic, ""class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON .String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-,Maybe
,Optional
Swift .bind
(""), ,Optionals
, ""Optional
c ,-Optional
Optional
.Optional
, , -.None
,.None
,bind
""Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift ,>>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, .apply
, . , "" -Optional
. ,Optional
,-Optional
..Some
, ,Optional
. -.None
,.None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , (init
)User
, Swift (auto-currying). , , , .User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
-.None
, user.None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
,callback
.return
,NSError
. bug , 3 : , JSON JSONUser
.
.bind
Result
.
parseResponse
Result
data
. API iOSNSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
,data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
-data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
,>>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, ,JSONDecodable
,Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
,Optional User
Result. , resultFromOptional
decode
,decode
.
,performRequest
. :performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"
Result : UITableViewController
.
, Swift " " Objective-C , Flickr.com Flickr API, "Stanford CS 193P iOS 7" , Objective-C .
:
extension Place: JSONDecodable { static func create(placeURL: String)(timeZone: String)(photoCount: String)(content: String) -> Place { return Place(placeURL: toURL(placeURL), timeZone: timeZone, photoCount: photoCount.toInt() ?? 0, content: content) } static func decode(json: JSON) -> Place? { return _JSONParse(json) >>> { d in Place.create <^> d <| "place_url" <*> d <| "timezone" <*> d <| "photo_count" <*> d <| "_content" } } } extension Places: JSONDecodable { static func create(places: [Place]) -> Places { return Places(places: places) } static func decode(json: JSON) -> Places? { return _JSONParse(json) >>> { d in Places.create <^> d <| "places" <| "place" } } }
... :
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //--------------- URL places Flickr.com ------------------------------------------ let urlPlaces = NSURLRequest( URL: FlickrFetcher.URLforTopPlaces()) performRequest(urlPlaces ) { (places: Result<Places>) in println("\(stringResult(places))") } } }
"" Swift Objective-C - EfficientJSONBrief-Bridging-Header.h , FlickrFetcher.h
API Flickr
:
Github .
Apple , Swift , iOS OS X. , Xcode 6 Beta1, Swift , , JSON - - Objective-C . Swift , , , . , Swift , , , runtime . , , , . API JSON, ( Generics ) .
User
, - , , JSON. NSJSONSerialization.JSONObjectWithData(NSData, Int, &NSError)
, Optional
JSON ( error ), . JSON Objective-C - NSDictionary
,
. Swift , , , . JSON Dictionary<String, AnyObject>
. AnyObject
- , JSON String, Double, Bool, Array, Dictionary
null
. JSON , , JSON , . User
:
struct User { let id: Int let name: String let email: String }
, :
func getUser(request: NSURLRequest, callback: (User) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in var jsonErrorOptional: NSError? let jsonOptional: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) if let json = jsonOptional as? Dictionary<String, AnyObject> { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(user) } } } } } task.resume() }
if-let
, - User
. , , . , , : . , , API, .
, JSON typealias
.
typealias JSON = AnyObject typealias JSONDictionary = Dictionary<String, JSON> typealias JSONArray = Array<JSON>
:
-, .
, Either<A, B>
. , , . Swift Either<A, B>
:
enum Either<A, B> { case Left(A) case Right(B) }
Either<NSError, User>
, callback
, , "" User
, ( error
).
func getUser(request: NSURLRequest, callback: (Either<NSError, User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Left(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Left(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Right(user)) return } } } } // "" - , callback callback(.Left(NSError())) } task.resume() }
, getUser
switch
Either
, - User
.
getUser(request) { either in switch either { case let .Left(error): // case let .Right(user): // - user } }
, , Left
NSError
. , Result , , , . :
enum Result<A> { case Error(NSError) case Value(A) }
Swift (1.1), Result . Swift , . (constant class
) generic A
.
( . Swift enum
generic , , , generic, "" class box
):
final class Box<A> { let value: A init(_ value: A) { self.value = value } } enum Result<A> { case Error(NSError) case Value(Box<A>) }
Either
Result
, :
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional as? JSONDictionary { if let id = json["id"] as AnyObject? as? Int { if let name = json["name"] as AnyObject? as? String { if let email = json["email"] as AnyObject? as? String { let user = User(id: id, name: name, email: email) callback(.Value(Box(user))) return } } } } // "" - , callback callback(.Error(NSError())) } task.resume() } getUser(request) { result in switch result { case let .Error(error): // case let .Value(boxedUser): let user = boxedUser.value // - user } }
. .
:
JSON JSON . String, Int
Dictionary
, 3 .
func JSONString(object: JSON?) -> String? { return object as? String } func JSONInt(object: JSON?) -> Int? { return object as? Int } func JSONObject(object: JSON?) -> JSONDictionary? { return object as? JSONDictionary }
JSON :
if let json = JSONObject(jsonOptional) { if let id = JSONInt(json["id"]) { if let name = JSONString(json["name"]) { if let email = JSONString(json["email"]) { let user = User(id: id, name: name, email: email) } } } }
if-let
. , , "" .
-, Maybe
, Optional
Swift . bind
(""), , Optionals
, "" Optional
c , -Optional
Optional
. Optional
, , - .None
, .None
, bind
"" Optional
.
infix operator >>> { associativity left precedence 150 } func >>><A, B>(a: A?, f: A -> B?) -> B? { if let x = a { return f(x) } else { return .None } }
>>=
bind
(""); Swift , >>>
.
JSON , :
if let json = jsonOptional >>> JSONObject { if let id = json["id"] >>> JSONInt { if let name = json["name"] >>> JSONString { if let email = json["email"] >>> JSONString { let user = User(id: id, name: name, email: email) } } } }
Optional
:
func JSONString(object: JSON) -> String? { return object as? String } func JSONInt(object: JSON) -> Int? { return object as? Int } func JSONObject(object: JSON) -> JSONDictionary? { return object as? JSONDictionary }
fmap
, . apply
, . , "" - Optional
. , Optional
, -Optional
. .Some
, , Optional
. - .None
, .None
. Swift :
infix operator <^> { associativity left } // Functor's fmap (usually <$>) infix operator <*> { associativity left } // Applicative's apply func <^><A, B>(f: A -> B, a: A?) -> B? { if let x = a { return f(x) } else { return .None } } func <*><A, B>(f: (A -> B)?, a: A?) -> B? { if let x = a { if let fx = f { return fx(x) } } return .None }
, , ( init
) User
, Swift (auto-currying). , , , . User
:
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } }
, JSON :
if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString }
- .None
, user .None
. , .
getUser
:
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in // , callback if let err = error { callback(.Error(err)) return } var jsonErrorOptional: NSError? let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) // JSON, callback if let err = jsonErrorOptional { callback(.Error(err)) return } if let json = jsonOptional >>> JSONObject { let user = User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString if let u = user { callback(.Value(Box(u))) return } } // "" - , callback callback(.Error(NSError())) } task.resume() }
: returns "bind" ()
, callback
. return
, NSError
. bug , 3 : , JSON JSON User
.
. bind
Result
.
parseResponse
Result
data
. API iOS NSURLResponse
data
, :
struct Response { let data: NSData let statusCode: Int = 500 init(data: NSData, urlResponse: NSURLResponse) { self.data = data if let httpResponse = urlResponse as? NSHTTPURLResponse { statusCode = httpResponse.statusCode } } }
parseResponse
Response
, data
.
func parseResponse(response: Response) -> Result<NSData> { let successRange = 200..<300 if !contains(successRange, response.statusCode) { return .Error(NSError()) // } return .Value(Box(response.data)) }
Optional
Result
, .
func resultFromOptional<A>(optional: A?, error: NSError) -> Result<A> { if let a = optional { return .Value(Box(a)) } else { return .Error(error) } }
- data
JSON:
func decodeJSON(data: NSData) -> Result<JSON> { let jsonOptional: JSON! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &jsonErrorOptional) return resultFromOptional(jsonOptional, NSError()) // NSJSONSerialization }
JSON :
struct User { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> Result<User> { let user = JSONObject(json) >>> { dict in User.create <^> dict["id"] >>> JSONInt <*> dict["name"] >>> JSONString <*> dict["email"] >>> JSONString } return resultFromOptional(user, NSError()) // } }
, >>>
Result
:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> { switch a { case let .Value(x): return f(x.value) case let .Error(error): return .Error(error) } }
Result
:
enum Result<A> { case Error(NSError) case Value(Box<A>) init(_ error: NSError?, _ value: A) { if let err = error { self = .Error(err) } else { self = .Value(Box(value)) } } }
>>>
.
func getUser(request: NSURLRequest, callback: (Result<User>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) let result = responseResult >>> parseResponse >>> decodeJSON >>> User.decode callback(result) } task.resume() }
, . : “ . !”, - !
: "" generics
, , JSON. generics, .
JSONDecodable
, :
protocol JSONDecodable { class func decode(json: JSON) -> Self? }
, , JSONDecodable
, Result
:
func decodeObject<A: JSONDecodable>(json: JSON) -> Result<A> { return resultFromOptional(A.decode(json), NSError()) // custom error }
User
:
struct User: JSONDecodable { let id: Int let name: String let email: String static func create(id: Int)(name: String)(email: String) -> User { return User(id: id, name: name, email: email) } static func decode(json: JSON) -> User? { return JSONObject(json) >>> { d in User.create <^> json["id"] >>> JSONInt <*> json["name"] >>> JSONString <*> json["email"] >>> JSONString } }
decode
User
, Optional User
Result. , resultFromOptional
decode
, decode
.
, performRequest
. : performRequest
parseResult
:
func performRequest<A: JSONDecodable>(request: NSURLRequest, callback: (Result<A>) -> ()) { let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, urlResponse, error in callback(parseResult(data, urlResponse, error)) } task.resume() } func parseResult<A: JSONDecodable>(data: NSData!, urlResponse: NSURLResponse!, error: NSError!) -> Result<A> { let responseResult = Result(error, Response(data: data, urlResponse: urlResponse)) return responseResult >>> parseResponse >>> decodeJSON >>> decodeObject }
GitHub .
- , , Haskell Learn You a Haskell. Pat Brisbin options .
GitHub . , , , , , .
Tony DiPasquale , :
"Real World JSON Parsing with Swift" - " JSON Swift"
"Parsing Embedded JSON and Arrays in Swift" - " JSON (Arrays) Swift."
- ( ) :
"Swift e Tony DiPasquale “ JSON Swift”"
"Swift Tony DiPasquale “ JSON Swift”"
"Swift “ JSON (Arrays) Swift.”"