Skip to content

Instantly share code, notes, and snippets.

View malcommac's full-sized avatar
👋
Nice to meet u

Daniele Margutti malcommac

👋
Nice to meet u
View GitHub Profile
func isValidJSON(_ jsonString: String) -> Bool {
if let data = jsonString.data(using: .utf8) {
do {
_ = try JSONSerialization.jsonObject(with: data, options: [])
return true
} catch {
return false
}
}
return false
// The movies search in action
let movies = try await client.fetch(Movies.Search("Godfather", year: 1972))
// The ranking in action
let page = try await client.fetch(Rankings.List(.topRated, region: .US))
var client: HTTPClient = {
client = HTTPClient(baseURL: "https://api.themoviedb.org/3")
client.queryParams = [
.init(name: "api_key", value: "<API KEY>"),
.init(name: "language", value: "IT-it")
]
return client
}()
public extension HTTPClient {
func fetch<T: APIResourceConvertible>(_ convertible: T) async throws -> T.Result {
let result = try await fetch(convertible.request())
return try result.decode(T.Result.self)
}
}
extension Rankings {
struct List: APIResourceConvertible {
public typealias Result = MoviesPage
public enum Category: String, CaseIterable {
case upcoming, popular
case topRated = "top_rated"
}
public extension Movies {
struct Search: APIResourceConvertible {
public typealias Result = MoviesPage
var query: String
var includeAdult: Bool = false
var year: Int?
public init(_ query: String, year: Int? = nil) {
public protocol APIResourceConvertible {
associatedtype Result: Decodable // the output object of the service
func request() -> HTTPRequest // function which generate a request
}
public enum Rankings { }
public enum Movies { }
public func fetch() async throws {
// prepare the request and execute it
let task = try await request.urlSessionTask(inClient: client)
let response = try await fetch(request, task: sessionTask: task)
// ask to validator the action to perform on this request
let action = client.validate(response: response, forRequest: request)
switch action {
case .failChain(let error):
return HTTPResponse(error: error) // fail with error
func validate(response: HTTPResponse, forRequest request: HTTPRequest) -> HTTPResponseValidatorResult {
if !(200..<300).contains(response.statusCode) {
// invalid response, we want to fail the request with error
throw HTTPError.invalidResponse
}
return .nextValidator // everything is okay, move to next validator
}