LoginSignup
4
8

More than 3 years have passed since last update.

APIKitとCombineを使う

Posted at

 初投稿です。APIKit の Session に Combine の Publisher を生やそうと思います(SessionPublisher が生えていると、楽しいので)。

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 になる。

4
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
8