// // RESTManager.swift // IOS_study // // Created by CC-star on 2025/7/3. // import Foundation final class RESTManager { static let shared = RESTManager() private init() {}//让RESTManager只在这个class在内部被实例化,不在外部实例化,只能在这个作用域里面被调用 //通过REST API获取一条数据 func findOne(form urlStr: String) async throws -> T { guard let url = URL(string: urlStr) else { throw URLError(.badURL) } var request = URLRequest(url: url) request.httpMethod = "GET" request.setValue("application/json", forHTTPHeaderField: "Content-Type") request.setValue(kAppID, forHTTPHeaderField: "X-LC-Id") request.setValue(kAppKey, forHTTPHeaderField: "X-LC-Key") let (data, response) = try await URLSession.shared.data(for: request) //print(String(data: data, encoding: .utf8) ?? "Invalid data") guard let httpRespsonse = response as? HTTPURLResponse, 200..<300 ~= httpRespsonse.statusCode else { throw URLError(.badServerResponse) } let decoder = JSONDecoder() //decoder.keyDecodingStrategy = .convertFromSnakeCase//数据库字段是下划线写法的时候 //把数据库里面的时间戳字符串转换成Date类型(了解) decoder.dateDecodingStrategy = .custom { decoder -> Date in let container = try decoder.singleValueContainer() let dateString = try container.decode(String.self) let isoFormatter = ISO8601DateFormatter() isoFormatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds] // 支持毫秒 if let date = isoFormatter.date(from: dateString) { return date } throw DecodingError.dataCorruptedError(in: container, debugDescription: "Invalid date format: \(dateString)") } return try decoder.decode( T.self, from: data) } //通过REST API获取数组数据 func find(form urlStr: String) async throws ->[T] { guard let url = URL(string: urlStr) else { throw URLError(.badURL) } var request = URLRequest(url: url) request.httpMethod = "GET" request.setValue("application/json", forHTTPHeaderField: "Content-Type") request.setValue(kAppID, forHTTPHeaderField: "X-LC-Id") request.setValue(kAppKey, forHTTPHeaderField: "X-LC-Key") let (data,_) = try await URLSession.shared.data(for: request) //print(String(data: data, encoding: .utf8) ?? "Invalid data") guard let json = try JSONSerialization.jsonObject(with: data) as? [String: Any], let results = json["results"] as? [[String: Any]] else { throw URLError(.cannotParseResponse) } let decoder = JSONDecoder() //decoder.keyDecodingStrategy = .convertFromSnakeCase//数据库字段是下划线写法的时候 //把数据库里面的时间戳字符串转换成Date类型(了解) decoder.dateDecodingStrategy = .custom { decoder -> Date in let container = try decoder.singleValueContainer() let dateString = try container.decode(String.self) let isoFormatter = ISO8601DateFormatter() isoFormatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds] // 支持毫秒 if let date = isoFormatter.date(from: dateString) { return date } throw DecodingError.dataCorruptedError(in: container, debugDescription: "Invalid date format: \(dateString)") } let tData = try JSONSerialization.data(withJSONObject: results) return try decoder.decode([T].self, from: tData) } //通过REST API上传数据 func save(to urlStr: String, object: T) async throws {//T:代表通用类型,泛型,Encodable编码 guard let url = URL(string: urlStr) else { throw NetworkError.invalidURL//抛出问题 -> 无效网址 }//有值则赋值给url,没有则进入else var request = URLRequest(url: url) request.httpMethod = "POST" request.setValue("application/json", forHTTPHeaderField: "Content-Type") request.setValue(kAppID, forHTTPHeaderField: "X-LC-Id") request.setValue(kAppKey, forHTTPHeaderField: "X-LC-Key") let encoder = JSONEncoder() // encoder.keyEncodingStrategy = .convertToSnakeCase//数据字段是下划线的写法的时候 request.httpBody = try encoder.encode(object)//注意:使用链接的方式请求LeanCloud REST需要去掉Date字段(或其他处理) let (data, response) = try await URLSession.shared.data(for: request)//向服务器请求数据,并返回一个元组类型【一个数据,一个响应】 guard let httpRespsonse = response as? HTTPURLResponse else { throw NetworkError.notHTTPResponse } if !(200..<300).contains(httpRespsonse.statusCode) {//如果不是 2开头的响应码 -> [200,300) let errRespsonse = try JSONSerialization.jsonObject(with: data) as? [String: Any]//Any:字典类型 let errMsg = errRespsonse?["error"] as? String ?? "响应数据类型转换失败" //如果errRespsonse没有值,则返回error //如果上面的errRespsonse?["error"] as? String都不满足则返回:响应数据类型转换失败,【??】空壳运算符 throw NetworkError.requestFailed("错误码:\(httpRespsonse.statusCode),错误信息:\(errMsg)") } } }