// // ApiRequest.swift // Nextcloud Cookbook iOS Client // // Created by Vincent Meilinger on 16.11.23. // import Foundation import OSLog struct ApiRequest { /// The server address, e.g. https://example.com let serverAddress: String let path: String let method: RequestMethod let authString: String? let headerFields: [HeaderField] let body: Data? /// The path to the Cookbook application on the nextcloud server. let cookbookPath = "/index.php/apps/cookbook" init( serverAdress: String, path: String, method: RequestMethod, authString: String? = nil, headerFields: [HeaderField] = [], body: Data? = nil ) { self.serverAddress = serverAdress self.method = method self.path = path self.headerFields = headerFields self.authString = authString self.body = body } func send() async -> (Data?, NetworkError?) { Logger.network.debug("\(method.rawValue) \(path) sending ...") // Prepare URL let urlString = serverAddress + cookbookPath + path Logger.network.debug("Full path: \(urlString)") guard let urlStringSanitized = urlString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else { return (nil, .unknownError) } guard let url = URL(string: urlStringSanitized) else { return (nil, .unknownError) } // Create URL request var request = URLRequest(url: url) request.httpMethod = method.rawValue // Set authentication string, if needed if let authString = authString { request.setValue( "Basic \(authString)", forHTTPHeaderField: "Authorization" ) } // Set other header fields for headerField in headerFields { request.setValue( headerField.getValue(), forHTTPHeaderField: headerField.getField() ) } // Set http body if let body = body { request.httpBody = body } // Wait for and return data and (decoded) response var data: Data? = nil var response: URLResponse? = nil do { (data, response) = try await URLSession.shared.data(for: request) Logger.network.debug("\(method.rawValue) \(path) SUCCESS!") return (data, nil) } catch { let error = decodeURLResponse(response: response as? HTTPURLResponse) Logger.network.debug("\(method.rawValue) \(path) FAILURE: \(error.debugDescription)") return (nil, error) } } private func decodeURLResponse(response: HTTPURLResponse?) -> NetworkError? { guard let response = response else { return NetworkError.unknownError } switch response.statusCode { case 200...299: return (nil) case 300...399: return (NetworkError.redirectionError) case 400...499: return (NetworkError.clientError) case 500...599: return (NetworkError.serverError) case 600: return (NetworkError.invalidRequest) default: return (NetworkError.unknownError) } } }