Here is an example of a complete solution compatible with Swift 4 and Swift 5 .
Endpoint to create URL
struct Endpoint { let path: String let queryItems: [URLQueryItem]? } extension Endpoint { var url: URL? { var components = URLComponents() components.scheme = "https" components.host = "YOUR_HOST" components.path = path components.queryItems = queryItems return components.url } }
User object model for the request body
struct User: Encodable { let name: String let surname: String let age: Int
UserResponse model for HTTP response comes from API
struct UserResponse: Decodable { let message: String let userId: String? enum CodingKeys: String, CodingKey { case message, userId = "user_id"
APIClient makes http requests to our APIs
protocol APIClientProtocol: Any { func sendUser(_ user: User, completionBlock: @escaping (_ userResponse: UserResponse?, _ error: APIClient.Error?) -> Void) } class APIClient: APIClientProtocol { fileprivate let defaultSession: URLSession = { let configuration = URLSessionConfiguration.default configuration.timeoutIntervalForRequest = 10.0 configuration.timeoutIntervalForResource = 10.0 return URLSession(configuration: configuration, delegate: nil, delegateQueue: nil) }() public init() { } public func uploadUser(_ user: User, completionBlock: @escaping (UserResponse?, APIClient.Error?) -> Void) { guard let url = Endpoint(path: "/user/upload", queryItems: nil).url else { completionBlock(nil, .brokenURL) return } var urlRequest = URLRequest(url: url) urlRequest.httpMethod = "POST" urlRequest.addValue("application/json", forHTTPHeaderField: "Content-Type") urlRequest.addValue("application/json", forHTTPHeaderField: "Accept") do { let jsonData = try JSONEncoder().encode(user) urlRequest.httpBody = jsonData } catch { completionBlock(nil, .serialization(error.localizedDescription)) return } let task = defaultSession.dataTask(with: urlRequest) { data, urlResponse, error in if let error = error { completionBlock(nil, .http(error.localizedDescription)) return } guard let httpResponse = urlResponse as? HTTPURLResponse else { return } if httpResponse.statusCode == 200 { guard let data = data else { return } do { let userResponse = try JSONDecoder().decode(UserResponse.self, from: data) completionBlock(userResponse, nil) } catch let error { completionBlock(nil, .serialization(error.localizedDescription)) } } else { completionBlock(nil, .http("Status failed!")) } } task.resume() } } extension APIClient { enum Error: Swift.Error, Equatable { case brokenURL case serialization(String) case http(String) } }
source share