初投稿です。APIKit の Session
に Combine の Publisher
を生やそうと思います(Session
に Publisher
が生えていると、楽しいので)。
extension Session {
struct Send<R>: Publisher where R: Request {
typealias Output = R.Response
typealias Failure = SessionTaskError
let request: R
func receive<S>(subscriber: S) where S: Subscriber, Failure == S.Failure, Output == S.Input {
let task = Session.send(request) { result in
switch result {
case let .success(response):
_ = subscriber.receive(response)
subscriber.receive(completion: .finished)
case let .failure(error):
subscriber.receive(completion: .failure(error))
}
}
let subscription = Cancel(task: task)
subscriber.receive(subscription: subscription)
}
}
struct Cancel: Subscription {
let combineIdentifier = CombineIdentifier()
let task: SessionTask?
func request(_: Subscribers.Demand) {}
func cancel() {
task?.cancel()
}
}
}
リクエストをつくります。
class GitHubAPI {
struct SearchRepositories: Request {
struct Response: Codable {
struct Item: Codable {
let fullName: String
}
let items: [Item]
}
let baseURL = URL(string: "https://api.github.com")!
let method = HTTPMethod.get
let path = "/search/repositories"
var queryParameters: [String: Any]? {
["q": query]
}
let query: String
func response(from object: Any, urlResponse _: HTTPURLResponse) throws -> Response {
guard let data = object as? Data else {
throw ResponseError.unexpectedObject(object)
}
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
return try decoder.decode(Response.self, from: data)
}
}
}
// Codable のために APIKit のパースを止める
class PassthroughParser: DataParser {
let contentType: String? = nil
func parse(data: Data) throws -> Any {
return data
}
}
extension Request {
var dataParser: DataParser {
PassthroughParser()
}
}
リクエストを送ります。
Session.Send(request: GitHubAPI.SearchRepositories(query: "SwiftUI"))
.map { $0.items }
.flatMap { Publishers.Sequence(sequence: $0) }
.sink { print($0.fullName) }
すべてが Publisher
になる。