metatext-app-ios-iphone-ipad/MastodonAPI/Sources/MastodonAPI/MastodonAPIClient.swift

87 lines
3.4 KiB
Swift
Raw Permalink Normal View History

2020-08-31 01:33:11 +02:00
// Copyright © 2020 Metabolist. All rights reserved.
import Combine
2020-09-05 04:31:43 +02:00
import Foundation
2020-08-31 03:40:58 +02:00
import HTTP
import Mastodon
2020-08-31 01:33:11 +02:00
2020-09-07 06:56:18 +02:00
public final class MastodonAPIClient: HTTPClient {
2020-09-09 03:02:55 +02:00
public var instanceURL: URL
2020-08-31 01:33:11 +02:00
public var accessToken: String?
2020-09-23 09:04:37 +02:00
public required init(session: URLSession, instanceURL: URL) {
2020-09-09 03:02:55 +02:00
self.instanceURL = instanceURL
super.init(session: session, decoder: MastodonDecoder())
2020-08-31 01:33:11 +02:00
}
2020-09-24 03:33:13 +02:00
public override func dataTaskPublisher<T: DecodableTarget>(
2020-12-17 07:48:06 +01:00
_ target: T, progress: Progress? = nil) -> AnyPublisher<(data: Data, response: HTTPURLResponse), Error> {
super.dataTaskPublisher(target, progress: progress)
2020-09-24 03:33:13 +02:00
.mapError { [weak self] error -> Error in
if case let HTTPError.invalidStatusCode(data, _) = error,
let apiError = try? self?.decoder.decode(APIError.self, from: data) {
return apiError
}
return error
}
.eraseToAnyPublisher()
2020-08-31 01:33:11 +02:00
}
}
extension MastodonAPIClient {
2020-12-17 07:48:06 +01:00
public func request<E: Endpoint>(_ endpoint: E, progress: Progress? = nil) -> AnyPublisher<E.ResultType, Error> {
dataTaskPublisher(target(endpoint: endpoint), progress: progress)
2020-09-24 03:33:13 +02:00
.map(\.data)
.decode(type: E.ResultType.self, decoder: decoder)
.eraseToAnyPublisher()
}
public func pagedRequest<E: Endpoint>(
_ endpoint: E,
2020-10-06 00:50:05 +02:00
maxId: String? = nil,
minId: String? = nil,
sinceId: String? = nil,
2020-12-17 07:48:06 +01:00
limit: Int? = nil,
progress: Progress? = nil) -> AnyPublisher<PagedResult<E.ResultType>, Error> {
2020-10-06 00:50:05 +02:00
let pagedTarget = target(endpoint: Paged(endpoint, maxId: maxId, minId: minId, sinceId: sinceId, limit: limit))
2020-12-17 07:48:06 +01:00
let dataTask = dataTaskPublisher(pagedTarget, progress: progress).share()
2020-09-24 03:33:13 +02:00
let decoded = dataTask.map(\.data).decode(type: E.ResultType.self, decoder: decoder)
let info = dataTask.map { _, response -> PagedResult<E.ResultType>.Info in
2020-10-06 00:50:05 +02:00
var maxId: String?
var minId: String?
var sinceId: String?
2020-09-24 03:33:13 +02:00
if let links = response.value(forHTTPHeaderField: "Link") {
let queryItems = Self.linkDataDetector.matches(
in: links,
range: .init(links.startIndex..<links.endIndex, in: links))
.compactMap { match -> [URLQueryItem]? in
guard let url = match.url else { return nil }
return URLComponents(url: url, resolvingAgainstBaseURL: true)?.queryItems
}
.reduce([], +)
2020-10-06 00:50:05 +02:00
maxId = queryItems.first { $0.name == "max_id" }?.value
minId = queryItems.first { $0.name == "min_id" }?.value
sinceId = queryItems.first { $0.name == "since_id" }?.value
2020-09-24 03:33:13 +02:00
}
2020-10-06 00:50:05 +02:00
return PagedResult.Info(maxId: maxId, minId: minId, sinceId: sinceId)
2020-09-24 03:33:13 +02:00
}
return decoded.zip(info).map(PagedResult.init(result:info:)).eraseToAnyPublisher()
}
}
private extension MastodonAPIClient {
// swiftlint:disable force_try
static let linkDataDetector = try! NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue)
// swiftlint:enable force_try
func target<E: Endpoint>(endpoint: E) -> MastodonAPITarget<E> {
MastodonAPITarget(baseURL: instanceURL, endpoint: endpoint, accessToken: accessToken)
2020-08-31 01:33:11 +02:00
}
}