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

Result Driven Development

Result Driven Development

Functional Swift Conferencer 2015

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