Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Result Driven Development

Result Driven Development

Functional Swift Conferencer 2015

Avatar for Brian Partridge

Brian Partridge

December 13, 2015
Tweet

More Decks by Brian Partridge

Other Decks in Technology

Transcript

  1. Agenda • What is Result? • Result with HTTP Requests

    • Result Handling Pipelines • Parsing Using Generics
  2. protocol ResultType { typealias Value init(value: Value) init(error: ErrorType) var

    value: Value? { get } var error: ErrorType? { get } }
  3. enum Optional<T> { … func map<U>(f: (Wrapped) -> U) ->

    U? func flatMap<U>(f: (Wrapped) -> U?) -> U? }
  4. protocol ResultType { … func map<U>(f: (Value) -> U) ->

    Result<U> func flatMap<U>(f: (Value) -> Result<U>) -> Result<U> } enum Optional<T> { … func map<U>(f: (Wrapped) -> U) -> U? func flatMap<U>(f: (Wrapped) -> U?) -> U? }
  5. func executeRequest(request: …, session: …, completion: (NSData?, NSError?)->()) { session.dataTaskWithRequest(request,

    completionHandler: { (data, response, error) in if let error = error { completion(nil, error) return } }).resume() }
  6. func executeRequest(request: …, session: …, completion: (NSData?, NSError?)->()) { session.dataTaskWithRequest(request,

    completionHandler: { (data, response, error) in if let error = error { completion(nil, error) return } guard let httpResponse = response as? NSHTTPURLResponse else { fatalError(…) } if httpResponse.statusCode != 200 { completion(nil, HTTPError()) return } }).resume() }
  7. func executeRequest(request: …, session: …, completion: (NSData?, NSError?)->()) { session.dataTaskWithRequest(request,

    completionHandler: { (data, response, error) in if let error = error { completion(nil, error) return } guard let httpResponse = response as? NSHTTPURLResponse else { fatalError(…) } if httpResponse.statusCode != 200 { completion(nil, HTTPError()) return } guard let data = data else { completion(nil, BadDataError()) return } }).resume() }
  8. func executeRequest(request: …, session: …, completion: (NSData?, NSError?)->()) { session.dataTaskWithRequest(request,

    completionHandler: { (data, response, error) in if let error = error { completion(nil, error) return } guard let httpResponse = response as? NSHTTPURLResponse else { fatalError(…) } if httpResponse.statusCode != 200 { completion(nil, HTTPError()) return } guard let data = data else { completion(nil, BadDataError()) return } completion(data, nil) }).resume() }
  9. func executeRequest(request: …, session: …, completion: (NSData?, NSError?)->()) { session.dataTaskWithRequest(request,

    completionHandler: { (data, response, error) in if let error = error { completion(nil, error) return } guard let httpResponse = response as? NSHTTPURLResponse else { fatalError(…) } if httpResponse.statusCode != 200 { completion(nil, HTTPError()) return } guard let data = data else { completion(nil, BadDataError()) return } completion(data, nil) }).resume() }
  10. func executeRequest(request: …, session: …, completion: (NSData?, NSError?)->()) { session.dataTaskWithRequest(request,

    completionHandler: { (data, response, error) in if let error = error { completion(nil, error) return } guard let httpResponse = response as? NSHTTPURLResponse else { fatalError(…) } if httpResponse.statusCode != 200 { completion(nil, HTTPError()) return } guard let data = data else { completion(nil, BadDataError()) return } completion(data, nil) }).resume() }
  11. func executeRequest(request: …, session: …, completion: (Result<NSData>)->()) { session.dataTaskWithRequest(request, completionHandler:

    { (data, response, error) in if let error = error { completion(.Failure(error: error)) return } guard let httpResponse = response as? NSHTTPURLResponse else { fatalError(…) } if httpResponse.statusCode != 200 { completion(.Failure(error: HTTPError())) return } guard let data = data else { completion(.Failure(error:BadDataError())) return } completion(.Success(value: data)) }).resume() }
  12. func fetchLatestWidget(session: …, completion: (Result<Widget>)->()) { let request = ...

    executeRequest(request, session: session) { result in } }
  13. func fetchLatestWidget(session: …, completion: (Result<Widget>)->()) { let request = ...

    executeRequest(request, session: session) { result in switch result { case let .Failure(error): completion(.Failure(error)) case let .Success(data): let widget = Widget(data: result.value) completion(.Success(widget)) } } }
  14. func fetchLatestWidget(session: …, completion: (Result<Widget>)->()) { let request = ...

    executeRequest(request, session: session) { httpResult in let widgetResult = httpResult.map { Widget(data: $0) } completion(widgetResult) } }
  15. func fetchLatestWidget(session: …, completion: (Result<Widget>)->()) { let request = ...

    executeRequest(request, session: session) { httpResult in switch result { case let .Failure(error): completion(.Failure(error)) case let .Success(data): } } }
  16. func fetchLatestWidget(session: …, completion: (Result<Widget>)->()) { let request = ...

    executeRequest(request, session: session) { httpResult in switch result { case let .Failure(error): completion(.Failure(error)) case let .Success(data): let json = // Parse JSON } } }
  17. func fetchLatestWidget(session: …, completion: (Result<Widget>)->()) { let request = ...

    executeRequest(request, session: session) { httpResult in switch result { case let .Failure(error): completion(.Failure(error)) case let .Success(data): let json = // Parse JSON let widget = // Instantiate Widget } } }
  18. func fetchLatestWidget(session: …, completion: (Result<Widget>)->()) { let request = ...

    executeRequest(request, session: session) { httpResult in switch result { case let .Failure(error): completion(.Failure(error)) case let .Success(data): let json = // Parse JSON let widget = // Instantiate Widget completion(.Success(widget)) } } }
  19. func fetchLatestWidget(session: …, completion: (Result<Widget>)->()) { let request = ...

    executeRequest(request, session: session) { httpResult in switch result { case let .Failure(error): completion(.Failure(error)) case let .Success(data): let json = // Parse JSON // Handle failures let widget = // Instantiate Widget // Handle failures completion(.Success(widget)) } } }
  20. func fetchLatestWidget(session: …, completion: (Result<Widget>)->()) { let request = ...

    executeRequest(request, session: session) { httpResult in let jsonResult = httpResult.flatMap(parseJSONData) let widgetResult = jsonResult.flatMap(parseWidgetJSON) completion(widgetResult) } }
  21. func fetchLatestWidget(session: …, completion: (Result<Widget>)->()) { let request = ...

    executeRequest(request, session: session) { httpResult in let result = httpResult .flatMap(parseJSONData) .flatMap(parseWidgetJSON) completion(result) } }
  22. func fetchLatestWidget(session: …, completion: (Result<Widget>)->()) { let request = ...

    executeRequest(request, session: session) { httpResult in let result = httpResult // HTTP 500 .flatMap(parseJSONData) .flatMap(parseWidgetJSON) completion(result) // HTTP 500 } }
  23. func fetchLatestWidget(session: …, completion: (Result<Widget>)->()) { let request = ...

    executeRequest(request, session: session) { httpResult in let result = httpResult .flatMap(parseJSONData) // JSON Error .flatMap(parseWidgetJSON) completion(result) // JSON Error } }
  24. protocol PBMessageType { init(data: NSData) } class CompleteBillRequest: PBMessageType {

    let billID: String let completionDate: NSDate } class CompleteBillResponse: PBMessageType { let status: Status let bill: Bill } class Status: PBMessageType { let success: Bool let localizedDescription: String? }
  25. protocol PBMessageType { init(data: NSData) } class CompleteBillRequest: PBMessageType {

    let billID: String let completionDate: NSDate } class CompleteBillResponse: PBMessageType { let status: Status let bill: Bill } class Status: PBMessageType { let success: Bool let localizedDescription: String? }
  26. protocol PBMessageType { init(data: NSData) } class CompleteBillRequest: PBMessageType {

    let billID: String let completionDate: NSDate } class CompleteBillResponse: PBMessageType { let status: Status let bill: Bill } class Status: PBMessageType { let success: Bool let localizedDescription: String? }
  27. func parseProtoResponseData<T>(data: NSData) -> Result<T> { let object = T(data:

    data) if object.status.success { return .Success(value: object) } else { return .Failure(StatusError()) } }
  28. func parseProtoResponseData<T>(data: NSData) -> Result<T> { let object = T(data:

    data) // 'T' cannot be constructed if object.status.success { return .Success(value: object) } else { return .Failure(StatusError()) } }
  29. func parseProtoResponseData<T:PBMessageType>(data: NSData) -> Result<T> { let object = T(data:

    data) if object.status.success { return .Success(value: object) } else { return .Failure(StatusError()) } }
  30. func parseProtoResponseData<T:PBMessageType>(data: NSData) -> Result<T> { let object = T(data:

    data) if object.status.success { // Value of type 'T' has no member 'status' return .Success(value: object) } else { return .Failure(StatusError()) } }
  31. protocol PBResponseMessageType { let status: Status } extension CompleteBillResponse: PBResponseMessageType

    {} func parseProtoResponseData<T: PBResponseMessageType>(data: NSData) -> Result<T> { … }
  32. protocol PBResponseMessageType { let status: Status } extension CompleteBillResponse: PBResponseMessageType

    {} func parseProtoResponseData<T: PBResponseMessageType>(data: NSData) -> Result<T> { … }
  33. protocol PBResponseMessageType { let status: Status } extension CompleteBillResponse: PBResponseMessageType

    {} func parseProtoResponseData<T: PBResponseMessageType>(data: NSData) -> Result<T> { … }
  34. func completeBill(session: …, completion: (Result<Bill>)->()) { let request = ...

    executeRequest(request, session: session) { httpResult in } }
  35. func completeBill(session: …, completion: (Result<Bill>)->()) { let request = ...

    executeRequest(request, session: session) { httpResult in let completeBillResult: Result<CompleteBillResponse> } }
  36. func completeBill(session: …, completion: (Result<Bill>)->()) { let request = ...

    executeRequest(request, session: session) { httpResult in let completeBillResult: Result<CompleteBillResponse> completeBillResult = httpResult.flatMap(parseProtoResponseData) } }
  37. func completeBill(session: …, completion: (Result<Bill>)->()) { let request = ...

    executeRequest(request, session: session) { httpResult in let completeBillResult: Result<CompleteBillResponse> completeBillResult = httpResult.flatMap(parseProtoResponseData) let billResult = completeBillResult.map { $0.bill } } }
  38. func completeBill(session: …, completion: (Result<Bill>)->()) { let request = ...

    executeRequest(request, session: session) { httpResult in let completeBillResult: Result<CompleteBillResponse> completeBillResult = httpResult.flatMap(parseProtoResponseData) let billResult = completeBillResult.map { $0.bill } completion(billResult) } }
  39. References Networking With Monads By John Gallagher @ Functional Swift

    Conf 2014 http://2014.funswiftconf.com/speakers/john.html antitypical/Result https://github.com/antitypical/Result
  40. Q&A