Share
Embed
Transcript
35 slides
-
@_riteshhh Combine It’s a new reactive framework by Apple which
provides a declarative Swift API for processing values over time. -
@_riteshhh Pillars of Combine Publisher Just Fail Future DataTaskPublisher Map
FlatMap Filter Catch Sink Assign AnySubscriber [Operators] Subscriber -
@_riteshhh Networking • URLSession • URLSessionDataTask • func dataTask(URL) ->
URLSessionDataTask (without Combine) -
@_riteshhh Networking (with Combine) • URLSession • URLSession.DataTaskPublisher • func
dataTaskPublisher(URL) -> DataTaskPublisher -
@_riteshhh DataTaskPublisher typealias DataTaskResult = (Data, URLResponse) public struct DataTaskPublisher
: Publisher { /// The kind of values published by this publisher. public typealias Output = DataTaskResult /// The kind of errors this publisher might publish. public typealias Failure = URLError } -
@_riteshhh DataTaskPublisher let url = URL(string: "http://api.openweathermap.org/data/2.5")! let session =
URLSession.shared session .dataTaskPublisher(for: url) .sink( receiveCompletion: { print("Completion/Failure: \($0)") }, receiveValue: { print("Value: \($0)") } ) -
@_riteshhh DataTaskPublisher let url = URL(string: "http://api.openweathermap.org/data/2.5")! let session =
URLSession.shared session .dataTaskPublisher(for: url) .sink( receiveCompletion: { print("Completion/Failure: \($0)") }, receiveValue: { print("Value: \($0)") } ) -
@_riteshhh DataTaskPublisher let url = URL(string: "http://api.openweathermap.org/data/2.5")! let session =
URLSession.shared session .dataTaskPublisher(for: url) .sink( receiveCompletion: { print("Completion/Failure: \($0)") }, receiveValue: { print("Value: \($0)") } ) •Gateway timeout error •No internet error -
@_riteshhh DataTaskPublisher let url = URL(string: "http://api.openweathermap.org/data/2.5")! let session =
URLSession.shared session .dataTaskPublisher(for: url) .sink( receiveCompletion: { print("Completion/Failure: \($0)") }, receiveValue: { print("Value: \($0)") } ) •401: Invalid API Key •404: Not found -
@_riteshhh Validation • move response into correct stream (i.e. 4xx
response into failure stream and 2xx into output/value stream) -
@_riteshhh Validation func validateResponse() -> ValidatedPublisher enum ValidationError: Error {
case error(Error) case jsonError(Data) } typealias ValidatedPublisher = AnyPublisher<DataTaskResult, ValidationError> typealias DataTaskResult = (Data, URLResponse) -
@_riteshhh Validation func validateResponse(isValid: IsValidCallback) -> ValidatedPublisher typealias IsValidCallback =
(DataTaskResult) -> Bool typealias ValidatedPublisher = AnyPublisher<DataTaskResult, ValidationError> typealias DataTaskResult = (Data, URLResponse) -
@_riteshhh Validation extension Publisher where Output == DataTaskResult { func
validateResponse(isValid: IsValidCallBack) -> ValidatedPublisher } -
@_riteshhh Validation func validateResponse(isValid: IsValidCallback) -> ValidatedPublisher { return self
.mapError { ValidationError.error($0) } } -
@_riteshhh Validation func validateResponse(isValid: IsValidCallback) -> ValidatedPublisher { return self
.mapError { ValidationError.error($0) } .flatMap { (result) -> ValidatedPublisher in } } -
@_riteshhh func validateResponse(isValid: IsValidCallback) -> ValidatedPublisher { return self .mapError
{ ValidationError.error($0) } .flatMap { (result) -> ValidatedPublisher in if isValid(result) { // return the value in the Output/Success stream } else { // return the value in the Failure stream } } } -
@_riteshhh func validateResponse(isValid: IsValidCallback) -> ValidatedPublisher { return self .mapError
{ ValidationError.error($0) } .flatMap { (result) -> ValidatedPublisher in if isValid(result) { // return the value in the Output/Success stream } else { // return the value in the Failure stream } } } -
@_riteshhh func validateResponse(isValid: IsValidCallback) -> ValidatedPublisher { return self .mapError
{ ValidationError.error($0) } .flatMap { (result) -> ValidatedPublisher in if isValid(result) { // return the value in the Output stream return Just(result) .setFailureType(to: ValidationError.self) .eraseToAnyPublisher() } else { // return the value in the Failure stream } } } -
@_riteshhh func validateResponse(isValid: IsValidCallback) -> ValidatedPublisher { return self .mapError
{ ValidationError.error($0) } .flatMap { (result) -> ValidatedPublisher in if isValid(result) { // return the value in the Output stream return Just(result) .setFailureType(to: ValidationError.self) .eraseToAnyPublisher() } else { // return the value in the Failure stream } } } -
@_riteshhh func validateResponse(isValid: IsValidCallback) -> ValidatedPublisher { return self .mapError
{ ValidationError.error($0) } .flatMap { (result) -> ValidatedPublisher in if isValid(result) { // return the value in the Output stream return Just(result) .setFailureType(to: ValidationError.self) .eraseToAnyPublisher() } else { // return the value in the Failure stream return Fail( outputType: DataTaskResult.self, failure: ValidationError.jsonError(result.data) ).eraseToAnyPublisher() } } } -
@_riteshhh func validateResponse(isValid: IsValidCallback) -> ValidatedPublisher { return self .mapError
{ ValidationError.error($0) } .flatMap { (result) -> ValidatedPublisher in if isValid(result) { // return the value in the Output stream return Just(result) .setFailureType(to: ValidationError.self) .eraseToAnyPublisher() } else { // return the value in the Failure stream return Fail( outputType: DataTaskResult.self, failure: ValidationError.jsonError(result.data) ).eraseToAnyPublisher() } } } -
@_riteshhh func validateResponse(isValid: IsValidCallback) -> ValidatedPublisher { return self .mapError
{ ValidationError.error($0) } .flatMap { (result) -> ValidatedPublisher in if isValid(result) { // return the value in the Output stream return Just(result) .setFailureType(to: ValidationError.self) .eraseToAnyPublisher() } else { // return the value in the Failure stream return Fail( outputType: DataTaskResult.self, failure: ValidationError.jsonError(result.data) ).eraseToAnyPublisher() } } } -
@_riteshhh func validateResponse(isValid: IsValidCallback) -> ValidatedPublisher { return self .mapError
{ ValidationError.error($0) } .flatMap { (result) -> ValidatedPublisher in if isValid(result) { // return the value in the Output stream return Just(result) .setFailureType(to: ValidationError.self) .eraseToAnyPublisher() } else { // return the value in the Failure stream return Fail( outputType: Output.self, failure: ValidationError.jsonError(result.data) ).eraseToAnyPublisher() } }.eraseToAnyPublisher() } -
@_riteshhh func validateStatusCode(_ isValid: (Int) -> Bool) -> ValidatedPublisher {
return validateResponse { (data, response) in let statusCode = (response as? HTTPURLResponse)?.statusCode ?? -1 return isValid(statusCode) } } func validateResponse(isValid: (DataTaskResult) -> Bool) -> ValidatedPublisher { ... } Validation -
@_riteshhh let url = URL(string: "http://api.openweathermap.org/data/2.5")! let session = URLSession.shared
session .dataTaskPublisher(for: url) .sink( receiveCompletion: { print("Completion/Failure: \($0)") }, receiveValue: { print("Value: \($0)") } ) Declarative Networking -
@_riteshhh let url = URL(string: "http://api.openweathermap.org/data/2.5")! let session = URLSession.shared
session .dataTaskPublisher(for: url) .validateStatusCode { (200..<300).contains($0) } .sink( receiveCompletion: { print("Completion/Failure: \($0)") }, receiveValue: { print("Value: \($0)") } ) Declarative Networking -
@_riteshhh let url = URL(string: "http://api.openweathermap.org/data/2.5")! let session = URLSession.shared
session .dataTaskPublisher(for: url) .validateStatusCode { (200..<300).contains($0) } .validateResponse { !$0.data.isEmpty } .sink( receiveCompletion: { print("Completion/Failure: \($0)") }, receiveValue: { print("Value: \($0)") } ) Declarative Networking -
@_riteshhh let url = URL(string: "http://api.openweathermap.org/data/2.5")! let session = URLSession.shared
session .dataTaskPublisher(for: url) .validateStatusCode { (200..<300).contains($0) } .validateResponse { !$0.data.isEmpty } .mapErrorJson(to: ApiErrorResponse.self, decoder: decoder) .sink( receiveCompletion: { print("Completion/Failure: \($0)") }, receiveValue: { print("Value: \($0)") } ) Declarative Networking -
@_riteshhh let url = URL(string: "http://api.openweathermap.org/data/2.5")! let session = URLSession.shared
session .dataTaskPublisher(for: url) .validateStatusCode { (200..<300).contains($0) } .validateResponse { !$0.data.isEmpty } .mapErrorJson(to: ApiErrorResponse.self, decoder: decoder) .mapValueJson(to: ApiValueResponse.self, decoder: decoder) .sink( receiveCompletion: { print("Completion/Failure: \($0)") }, receiveValue: { print("Value: \($0)") } ) Declarative Networking -
@_riteshhh Further reading • Declarative Networking with Combine –– riteshhh.com
• Awesome Combine –– Github