Sources/SPTDataLoaderSwift/Request+Combine.swift (99 lines of code) (raw):
// Copyright 2015-2023 Spotify AB
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#if canImport(Combine)
import Combine
import Foundation
@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
public extension Request {
func publisher() -> ResponsePublisher<Void> {
return ResponsePublisher(request: self)
}
func dataPublisher() -> ResponsePublisher<Data> {
return ResponsePublisher(request: self)
}
func decodablePublisher<Value: Decodable>(
type: Value.Type = Value.self,
decoder: ResponseDecoder = JSONDecoder()
) -> ResponsePublisher<Value> {
return ResponsePublisher(request: self, decodableType: type, decoder: decoder)
}
func jsonPublisher(options: JSONSerialization.ReadingOptions = []) -> ResponsePublisher<Any> {
return ResponsePublisher(request: self, options: options)
}
func serializablePublisher<Serializer: ResponseSerializer>(
serializer: Serializer
) -> ResponsePublisher<Serializer.Output> {
return ResponsePublisher(request: self, serializer: serializer)
}
}
// MARK: -
private typealias ResponseProvider<Output> = (@escaping (Output) -> Void) -> Void
@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
public struct ResponsePublisher<Value>: Publisher {
public typealias Output = Response<Value, Error>
public typealias Failure = Never
private let request: Request
private let responseProvider: ResponseProvider<Output>
fileprivate init(request: Request, responseProvider: @escaping ResponseProvider<Output>) {
self.request = request
self.responseProvider = responseProvider
}
public func receive<S: Subscriber>(subscriber: S) where S.Failure == Failure, S.Input == Output {
let subscription = ResponseSubscription(
request: request,
responseProvider: responseProvider,
subscriber: subscriber
)
subscriber.receive(subscription: subscription)
}
public func valuePublisher() -> AnyPublisher<Value, Error> {
return setFailureType(to: Error.self).flatMap(\.result.publisher).eraseToAnyPublisher()
}
}
@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
private extension ResponsePublisher {
init(request: Request) where Value == Void {
self.init(request: request) { completion in
request.response(completionHandler: completion)
}
}
init(request: Request) where Value == Data {
self.init(request: request) { completion in
request.responseData(completionHandler: completion)
}
}
init(request: Request, decodableType: Value.Type, decoder: ResponseDecoder) where Value: Decodable {
self.init(request: request) { completion in
request.responseDecodable(type: decodableType, decoder: decoder, completionHandler: completion)
}
}
init(request: Request, options: JSONSerialization.ReadingOptions) where Value == Any {
self.init(request: request) { completion in
request.responseJSON(options: options, completionHandler: completion)
}
}
init<Serializer: ResponseSerializer>(request: Request, serializer: Serializer) where Value == Serializer.Output {
self.init(request: request) { completion in
request.responseSerializable(serializer: serializer, completionHandler: completion)
}
}
}
@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
private final class ResponseSubscription<Output, DownstreamSubscriber: Subscriber>: Subscription where DownstreamSubscriber.Input == Output {
private let request: Request
private let responseProvider: ResponseProvider<Output>
private let subscriber: DownstreamSubscriber
init(request: Request, responseProvider: @escaping ResponseProvider<Output>, subscriber: DownstreamSubscriber) {
self.request = request
self.responseProvider = responseProvider
self.subscriber = subscriber
}
func request(_ demand: Subscribers.Demand) {
guard !request.isCancelled else { return }
responseProvider { [subscriber] response in
_ = subscriber.receive(response)
subscriber.receive(completion: .finished)
}
}
func cancel() {
request.cancel()
}
}
#endif