Skip to content

Commit

Permalink
IMPROVEMENT: Networking (#171)
Browse files Browse the repository at this point in the history
  • Loading branch information
adamayoung authored May 1, 2024
1 parent 17d8ace commit c32b6fd
Show file tree
Hide file tree
Showing 376 changed files with 8,307 additions and 3,729 deletions.
28 changes: 21 additions & 7 deletions Sources/TMDb/Adapters/URLSessionHTTPClientAdapter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,22 +31,36 @@ final class URLSessionHTTPClientAdapter: HTTPClient {
}

func perform(request: HTTPRequest) async throws -> HTTPResponse {
var urlRequest = URLRequest(url: request.url)
urlRequest.httpMethod = request.method.rawValue
urlRequest.httpBody = request.body
for header in request.headers {
urlRequest.addValue(header.value, forHTTPHeaderField: header.key)
}
let urlRequest = Self.urlRequest(from: request)

let data: Data
let response: URLResponse

do {
(data, response) = try await perform(urlRequest)
} catch let error {
throw error
}

let httpResponse = Self.httpResponse(from: data, response: response)
return httpResponse
}

}

extension URLSessionHTTPClientAdapter {

private static func urlRequest(from httpRequest: HTTPRequest) -> URLRequest {
var urlRequest = URLRequest(url: httpRequest.url)
urlRequest.httpMethod = httpRequest.method.rawValue
urlRequest.httpBody = httpRequest.body
for header in httpRequest.headers {
urlRequest.addValue(header.value, forHTTPHeaderField: header.key)
}

return urlRequest
}

private static func httpResponse(from data: Data, response: URLResponse) -> HTTPResponse {
guard let httpURLResponse = response as? HTTPURLResponse else {
return HTTPResponse(statusCode: -1, data: nil)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// CreateSessionRequestBody.swift
// APIClient.swift
// TMDb
//
// Copyright © 2024 Adam Young.
Expand All @@ -19,8 +19,8 @@

import Foundation

struct CreateSessionRequestBody: Encodable, Equatable {
protocol APIClient {

let requestToken: String
func perform<Request: APIRequest>(_ request: Request) async throws -> Request.Response

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// CompanyEndpoint.swift
// APIRequest.swift
// TMDb
//
// Copyright © 2024 Adam Young.
Expand All @@ -19,22 +19,16 @@

import Foundation

enum CompanyEndpoint {
protocol APIRequest: Identifiable, Equatable {

case details(companyID: Company.ID)
associatedtype Body: Encodable & Equatable
associatedtype Response: Decodable

}

extension CompanyEndpoint: Endpoint {

private static let basePath = URL(string: "/company")!

var path: URL {
switch self {
case let .details(companyID):
Self.basePath
.appendingPathComponent(companyID)
}
}
var id: UUID { get }
var path: String { get }
var queryItems: [String: String] { get }
var method: APIRequestMethod { get }
var headers: [String: String] { get }
var body: Body? { get }

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// Endpoint.swift
// APIRequestMethod.swift
// TMDb
//
// Copyright © 2024 Adam Young.
Expand All @@ -19,8 +19,10 @@

import Foundation

protocol Endpoint {
enum APIRequestMethod: Sendable {

var path: URL { get }
case get
case post
case delete

}
67 changes: 67 additions & 0 deletions Sources/TMDb/Domain/APIClient/APIRequestQueryItem.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
//
// APIRequestQueryItem.swift
// TMDb
//
// Copyright © 2024 Adam Young.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an AS IS BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

import Foundation

typealias APIRequestQueryItems = [APIRequestQueryItem.Name: CustomStringConvertible]

struct APIRequestQueryItem {

private init() {}

}

extension APIRequestQueryItem {

struct Name: ExpressibleByStringLiteral, CustomStringConvertible, Equatable, Hashable {

private let name: String

init(_ name: String) {
self.name = name
}

init(stringLiteral: String) {
self.init(stringLiteral)
}

var description: String {
name
}

}

}

extension APIRequestQueryItem.Name {

static let page = APIRequestQueryItem.Name("page")
static let sortBy = APIRequestQueryItem.Name("sort_by")
static let withPeople = APIRequestQueryItem.Name("with_people")
static let watchRegion = APIRequestQueryItem.Name("watch_region")
static let includeImageLanguage = APIRequestQueryItem.Name("include_image_language")
static let includeVideoLanguage = APIRequestQueryItem.Name("include_video_language")
static let query = APIRequestQueryItem.Name("query")
static let year = APIRequestQueryItem.Name("year")
static let firstAirDateYear = APIRequestQueryItem.Name("first_air_date_year")
static let sessionID = APIRequestQueryItem.Name("session_id")
static let language = APIRequestQueryItem.Name("language")
static let apiKey = APIRequestQueryItem.Name("api_key")

}
75 changes: 75 additions & 0 deletions Sources/TMDb/Domain/APIClient/CodableAPIRequest.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
//
// CodableAPIRequest.swift
// TMDb
//
// Copyright © 2024 Adam Young.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an AS IS BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

import Foundation

class CodableAPIRequest<Body: Encodable & Equatable, Response: Decodable>: APIRequest, CustomStringConvertible {

let id: UUID
let path: String
let queryItems: [String: String]
let method: APIRequestMethod
let headers: [String: String]
let body: Body?

var description: String {
var description = """
APIRequest:
\tid: \(id.uuidString)
\tpath: \(path)
\tqueryItems: \(queryItems.map { "\($0)=\($1)" }.joined(separator: "&"))
\tmethod: \(method)
\theaders: \(headers.map { "\($0): \(1)" }.joined(separator: " "))
"""

if let body {
description += "\nBody:\n\(body)"
}

return description
}

init(
id: UUID = UUID(),
path: String,
queryItems: APIRequestQueryItems = [:],
method: APIRequestMethod = .post,
body: Body? = nil,
headers: [String: String] = [:]
) {
self.id = id
self.path = path
let queryItems = queryItems.map { (key: APIRequestQueryItem.Name, value: any CustomStringConvertible) in
(key.description, value.description)
}
self.queryItems = Dictionary(uniqueKeysWithValues: queryItems)
self.method = method
self.body = body
self.headers = headers
}

static func == (lhs: CodableAPIRequest<Body, Response>, rhs: CodableAPIRequest<Body, Response>) -> Bool {
lhs.path == rhs.path
&& lhs.queryItems == rhs.queryItems
&& lhs.method == rhs.method
&& lhs.headers == rhs.headers
&& lhs.body == rhs.body
}

}
43 changes: 43 additions & 0 deletions Sources/TMDb/Domain/APIClient/DecodableAPIRequest.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//
// DecodableAPIRequest.swift
// TMDb
//
// Copyright © 2024 Adam Young.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an AS IS BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

import Foundation

class DecodableAPIRequest<Response: Decodable>: CodableAPIRequest<EmptyBody, Response> {

init(
id: UUID = UUID(),
path: String,
queryItems: APIRequestQueryItems = [:],
method: APIRequestMethod = .get,
headers: [String: String] = [:]
) {
super.init(
id: id,
path: path,
queryItems: queryItems,
method: method,
body: nil,
headers: headers
)
}

}

struct EmptyBody: Encodable, Equatable {}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loading

0 comments on commit c32b6fd

Please sign in to comment.