Files
JellyKit/Sources/JellyKit/Client.swift
T
2026-10-19 13:47:55 -07:00

73 lines
2.8 KiB
Swift

//
// Client.swift
// JellyKit
//
// Created by Alex Loren on 10/10/26.
//
import Foundation
public struct Client {
// MARK: - Properties
private let server: String
private let session: URLSession
private let defaultHeaders: [String:String]
private let decoder: JSONDecoder = .init()
private let encoder: JSONEncoder = .init()
// MARK: - Functions
public init(server: String, session: URLSession = .shared, headers: [String:String] = [:]) {
self.server = server
self.session = session
self.defaultHeaders = headers
}
/// When all a client needs is the HTTP status code of an endpoint, this function should be used.
/// - Parameter request: Any Emby or Jellyfin `Request` conforming to the `Request` protocol.
/// - Throws:`RequestError.invalidResponse` if the response could not be coerced into a `HTTPURLResponse`.
/// - Returns: The HTTP status code.
func statusRequest(_ request: any Request) async throws -> Int {
let urlRequest = try createRequestObject(for: request)
let (_, response) = try await session.data(for: urlRequest)
guard let response = response as? HTTPURLResponse else {
throw RequestError.invalidResponse
}
return response.statusCode
}
/// Sends a request to an Emby or Jellyfin server, and builds a model object from that response.
/// - Parameter request: Any Emby or Jellyfin `Request` conforming to the `Request` protocol.
/// - Throws:`RequestError.invalidResponse` if the response could not be coerced into a `HTTPURLResponse`.
/// - Returns: A `Codable` model object whose type is infered from the receiving variable.
func dataRequest<T>(_ request: any Request) async throws -> T? where T: Codable {
let urlRequest = try createRequestObject(for: request)
let (data, response) = try await session.data(for: urlRequest)
guard let response = response as? HTTPURLResponse else {
throw RequestError.invalidResponse
}
return try decoder.decode(T.self, from: data)
}
/// Verifies and creates a `URLRequest` from information provided by the `Request`.
/// Merges the `Request` headers, if any, with the default headers.
/// - Parameter request: Any Emby or Jellyfin `Request` conforming to the `Request` protocol.
/// - Throws: `RequestError.badURL` if unable to create a `URL` from the string.
/// - Returns: A fully formed `URLRequest` ready to send.
private func createRequestObject(for request: Request) throws -> URLRequest {
guard let url = URL(string: "\(server)\(request.endpoint)") else {
throw RequestError.badURL
}
let headers = defaultHeaders.merging(request.headers ?? [:]) { (current, new) in
return new
}
var urlRequest = URLRequest(url: url)
urlRequest.httpMethod = request.method
urlRequest.allHTTPHeaderFields = headers
return urlRequest
}
}