以下是我的APIManager
代码,我在所有应用程序中都使用它。但有时,guard 语句在功能上失败connectToServer
,这意味着除了我的记录之外,这里的事情甚至在插入statusCode
DBHTTPURLResponse
之后200...299
也是如此statusCode
200...299
。我不知道会发生什么。
我认为这种行为的原因来自ServerURL
,因为我使用的开发服务器的 IP 地址http://00.000.0.000/
没有安全性。一旦我将它移至域,因为https://XXX.XXXXXXXXXX.XXXXX/
它工作正常。你能帮我弄清楚这个吗?
它还支持异步调用吗?
import UIKit
struct APIResponse : Decodable {
let status : Bool
let message : String
let extra: String?
}
internal let BASE_URL = "http://00.000.0.00/app/v0_1/api/" // Example server URL
enum APIPath: String {
case registration = "registration"
case login = "login"
case getProfile = "get_profile"
func directURL() -> URL? {
let urlPath = BASE_URL + self.rawValue
return URL(string: urlPath)
}
func extendedURL(using parameters: [String: Any]) -> URL? {
let extendedPath = parameters.map { $0.key + "=" + "\($0.value)" }.joined(separator: "&")
let urlPath = BASE_URL + self.rawValue + "?" + extendedPath
return URL(string: urlPath)
}
}
enum APIMethod: String {
case get = "GET"
case put = "PUT"
case post = "POST"
case patch = "PATCH"
case delete = "DELETE"
}
enum APIHeaders {
case user
case app
var authorization: [String:String] {
let acceptLanguage = UserDefaults.standard.value(forKey: UDKeys.appleLanguage) as? String ?? ""
if self == .user {
let token = UserDefaults.standard.value(forKey: UDKeys.userToken) as? String ?? ""
return ["Content-Type": "application/json", "Accept": "application/json", "Accept-Language": acceptLanguage, "Token" : token]
}
return ["Content-Type": "application/json", "Accept": "application/json", "Accept-Language": acceptLanguage]
}
}
struct APIRequest {
var url: URL?
var method: String
var parameters: Data?
var headers: [String:String]
init(path: APIPath, method: APIMethod, headers: APIHeaders) {
self.url = path.directURL()
self.method = method.rawValue
self.headers = headers.authorization
}
init(path: APIPath, parameters: [String: Any], method: APIMethod, headers: APIHeaders) {
self.url = path.extendedURL(using: parameters)
self.method = method.rawValue
self.headers = headers.authorization
}
init(path: APIPath, method: APIMethod, body: [String:Any], headers: APIHeaders) {
self.url = path.directURL()
self.method = method.rawValue
self.parameters = try? JSONSerialization.data(withJSONObject: body, options: .sortedKeys)
self.headers = headers.authorization
}
init<Encode: Encodable>(path: APIPath, method: APIMethod, body: Encode, headers: APIHeaders) {
self.url = path.directURL()
self.method = method.rawValue
self.parameters = try? JSONEncoder().encode(body)
self.headers = headers.authorization
}
}
struct APIError: Error {
let reason: String
let code: String?
init(reason: String, code: String? = nil) {
self.reason = reason
self.code = code
}
}
struct APIDispatcher {
static let instance = APIDispatcher()
private init() {}
func dispatch<Decode: Decodable>(request: APIRequest, response: Decode.Type, result: @escaping (Result<Decode, APIError>) -> ()) {
DispatchQueue(label: "queue", attributes: .concurrent).async {
self.connectToServer(with: request) { (resultant) in
switch resultant {
case .success(let data):
do {
let decoded = try JSONDecoder().decode(response, from: data)
DispatchQueue.main.async {
result(.success(decoded))
}
} catch let decodedError {
print("[Decoded Error]: ", decodedError)
do {
let apiResponse = try JSONDecoder().decode(APIResponse.self, from: data)
let apiError = APIError(reason: apiResponse.message, code: apiResponse.extra)
DispatchQueue.main.async {
result(.failure(apiError))
}
} catch {
let apiError = APIError(reason: decodedError.localizedDescription)
DispatchQueue.main.async {
result(.failure(apiError))
}
}
}
case .failure(let error):
DispatchQueue.main.async {
result(.failure(error))
}
}
}
}
}
func dispatch(request: APIRequest, result: @escaping (Result<Dictionary<String,Any>, APIError>) -> ()) {
DispatchQueue(label: "queue", attributes: .concurrent).async {
self.connectToServer(with: request) { (resultant) in
switch resultant {
case .success(let data):
do {
let serialized = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as! Dictionary<String,Any>
DispatchQueue.main.async {
result(.success(serialized))
}
} catch {
let error = APIError(reason: error.localizedDescription)
DispatchQueue.main.async {
result(.failure(error))
}
}
case .failure(let error):
DispatchQueue.main.async {
result(.failure(error))
}
}
}
}
}
private func connectToServer(with request: APIRequest, result: @escaping (Result<Data, APIError>) -> ()) {
guard let url = request.url else {
let error = APIError(reason: "Invalid URL")
result(.failure(error))
return
}
var urlRequest = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalAndRemoteCacheData, timeoutInterval: 30)
urlRequest.httpMethod = request.method
urlRequest.httpBody = request.parameters
urlRequest.allHTTPHeaderFields = request.headers
print(urlRequest)
let urlSessionConfiguration = URLSessionConfiguration.default
urlSessionConfiguration.waitsForConnectivity = false
urlSessionConfiguration.timeoutIntervalForRequest = 30
urlSessionConfiguration.timeoutIntervalForResource = 60
let urlSession = URLSession(configuration: urlSessionConfiguration)
urlSession.dataTask(with: urlRequest) { (data, response, error) in
if let error = error {
let error = APIError(reason: error.localizedDescription)
result(.failure(error))
return
}
guard let httpResponse = response as? HTTPURLResponse,
(200...299).contains(httpResponse.statusCode) else {
let error = APIError(reason: "Server Error")
result(.failure(error))
return
}
if let data = data {
result(.success(data))
}
}.resume()
}
}
注意:BASE_URL 和 APIResponse 可能因项目而异。
我用它作为
func login() {
self.startLoading()
let body = ["mobile_number": phoneNumberTF.text!, "password" : passwordTF.text!, "uuid" : UIDevice.current.identifierForVendor!.uuidString]
let apiRequest = APIRequest(path: .login, method: .post, body: body, headers: .app)
APIDispatcher.instance.dispatch(request: apiRequest) { result in
self.stopLoading()
switch result {
case .success(let response):
break
case .failure(let error):
break
}
}
}
编辑:我的错我statsCode
现在完全颠倒了我修改它。