|
@@ -1,412 +0,0 @@
|
|
|
-//
|
|
|
-// NetworkManager.swift
|
|
|
-// AIEmoji
|
|
|
-//
|
|
|
-// Created by 100Years on 2025/1/16.
|
|
|
-//
|
|
|
-
|
|
|
-import Alamofire
|
|
|
-import ObjectMapper
|
|
|
-
|
|
|
-
|
|
|
-let TSNetworkShared = TSNetworkManager.shared
|
|
|
-
|
|
|
-/// 网络工具类
|
|
|
-class TSNetworkManager {
|
|
|
-
|
|
|
- static let shared = TSNetworkManager()
|
|
|
- private init() {}
|
|
|
-
|
|
|
- /// 通用 Headers
|
|
|
- private var defaultHeaders: HTTPHeaders {
|
|
|
- return ["Content-Type": "application/json",
|
|
|
- "accept": "application/json",
|
|
|
-// "Authorization": "Bearer YOUR_ACCESS_TOKEN" // 按需修改
|
|
|
- ]
|
|
|
- }
|
|
|
-
|
|
|
- lazy var afSession: Session = {
|
|
|
-// let configuration = URLSessionConfiguration.af.default
|
|
|
-// configuration.timeoutIntervalForRequest = 15 // 请求超时时间(秒)
|
|
|
-// configuration.timeoutIntervalForResource = 30 // 资源超时时间(秒)
|
|
|
-// let session = Session(configuration: configuration)
|
|
|
-// return session
|
|
|
-
|
|
|
- return AF
|
|
|
- }()
|
|
|
- lazy var encoder: JSONEncoding = {
|
|
|
- return JSONEncoding(options: .withoutEscapingSlashes)// 关键:禁用斜杠转义
|
|
|
- }()
|
|
|
-
|
|
|
- func postStreamRequest(
|
|
|
- urlString: String,
|
|
|
- parameters: [String: Any]? = nil,
|
|
|
- streamHandler:@escaping (String) -> Void,
|
|
|
- completion: @escaping (Result<Any, Error>) -> Void
|
|
|
- )-> Request? {
|
|
|
-
|
|
|
- guard let url = URL(string: urlString) else {
|
|
|
- completion(.failure(NSError(domain: "url nil", code: 0)))
|
|
|
- return nil
|
|
|
- }
|
|
|
-
|
|
|
- // 1. 创建 URLRequest
|
|
|
- var urlRequest = URLRequest(url:URL(string: urlString)!)
|
|
|
- urlRequest.httpMethod = "POST"
|
|
|
- urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
|
-
|
|
|
- // 2. 将字典参数转换为 JSON 数据并设置为请求体
|
|
|
- do {
|
|
|
- let jsonData = try JSONSerialization.data(withJSONObject: parameters, options: [.withoutEscapingSlashes])//去掉转义
|
|
|
- urlRequest.httpBody = jsonData
|
|
|
- } catch {
|
|
|
- dePrint("Failed to encode parameters: \(error)")
|
|
|
- completion(.failure(error))
|
|
|
- return nil
|
|
|
- }
|
|
|
-
|
|
|
- // 3. 使用 streamRequest 发起流式请求
|
|
|
- let request = afSession.streamRequest(urlRequest)
|
|
|
- request.responseStreamString{ stream in
|
|
|
- switch stream.event {
|
|
|
- case .stream(let result):
|
|
|
- switch result {
|
|
|
- case .success(let string):
|
|
|
- dePrint("🚰🚰🚰Stream Received string string: \(string)")
|
|
|
- streamHandler(string)
|
|
|
- case .failure(let error):
|
|
|
- dePrint("Stream error: \(error)")
|
|
|
- completion(.failure(error))
|
|
|
- }
|
|
|
- case .complete(let cpl):
|
|
|
- if let error = cpl.error {
|
|
|
- dePrint("Stream Request failed with error: \(error)")
|
|
|
- completion(.failure(error))
|
|
|
- } else {
|
|
|
- dePrint("Stream success")
|
|
|
- completion(.success("Stream success"))
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- return request
|
|
|
- }
|
|
|
-
|
|
|
- func request(
|
|
|
- method:HTTPMethod,
|
|
|
- urlString: String,
|
|
|
- parameters: [String: any Any & Sendable]? = nil,
|
|
|
- completion: @escaping (Result<Any, Error>) -> Void
|
|
|
- ) -> Request{
|
|
|
- dePrint("✈️✈️✈️网络请求:\(urlString)")
|
|
|
- dePrint("✈️✈️✈️参数:\(String(describing: parameters))")
|
|
|
-
|
|
|
- var encoding: ParameterEncoding = URLEncoding.default
|
|
|
- if method == .post {
|
|
|
- encoding = encoder
|
|
|
- }
|
|
|
- let request = afSession.request(urlString, method: method, parameters: parameters, encoding: encoding, headers: defaultHeaders, interceptor: nil)
|
|
|
- request.responseString { response in
|
|
|
- self.handleResponse(response, completion: completion)
|
|
|
- }
|
|
|
- return request
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-}
|
|
|
-
|
|
|
-extension TSNetworkManager {
|
|
|
-
|
|
|
-
|
|
|
- /*
|
|
|
- // Data 数组
|
|
|
- let dataArray: [[String: Any]] = [
|
|
|
- [
|
|
|
- "data": imageData,
|
|
|
- "fieldName": "file1", // 字段名
|
|
|
- "fileName": "App-Icon.png", // 文件名
|
|
|
- "mimeType": "image/png" // MIME 类型
|
|
|
- ],
|
|
|
- [
|
|
|
- "data": textData,
|
|
|
- "fieldName": "file2", // 字段名
|
|
|
- "fileName": "hello.txt", // 文件名
|
|
|
- "mimeType": "text/plain" // MIME 类型
|
|
|
- ]
|
|
|
- ]
|
|
|
- */
|
|
|
-
|
|
|
- /// 上传多个 Data 数据
|
|
|
- /// - Parameters:
|
|
|
- /// - url: 上传地址
|
|
|
- /// - dataArray: Data 数组,每个元素是一个字典,包含 Data 和字段名
|
|
|
- /// - parameters: 其他参数(可选)
|
|
|
- /// - headers: 自定义请求头(可选)
|
|
|
- /// - completion: 完成回调,返回结果或错误
|
|
|
- func uploadData(
|
|
|
- urlString: String,
|
|
|
- dataArray: [[String: Any]], // Data 数组,每个元素包含 Data 和字段名
|
|
|
- parameters: [String: Any]? = nil, // 其他参数
|
|
|
- headers: HTTPHeaders? = nil, // 自定义请求头
|
|
|
- progressHandler: @escaping (Progress) -> Void, // 上传进度回调
|
|
|
- completion: @escaping (Result<Any, Error>) -> Void
|
|
|
- )-> Request? {
|
|
|
-
|
|
|
- guard let url = URL(string: urlString) else {
|
|
|
- completion(.failure(NSError(domain: "url nil", code: 0)))
|
|
|
- return nil
|
|
|
- }
|
|
|
-
|
|
|
- // 1. 设置默认请求头
|
|
|
- var defaultHeaders: HTTPHeaders = [
|
|
|
- "accept": "application/json",
|
|
|
- "Content-Type": "multipart/form-data"
|
|
|
- ]
|
|
|
- // 合并自定义请求头
|
|
|
- if let customHeaders = headers {
|
|
|
- customHeaders.forEach { defaultHeaders[$0.name] = $0.value }
|
|
|
- }
|
|
|
-
|
|
|
- dePrint("✈️✈️✈️网络请求:\(urlString)")
|
|
|
- dePrint("✈️✈️✈️dataArray:\(String(describing: dataArray))")
|
|
|
- dePrint("✈️✈️✈️参数:\(String(describing: parameters))")
|
|
|
- // 2. 使用 Alamofire 上传 Data
|
|
|
- let request = afSession.upload(
|
|
|
- multipartFormData: { multipartFormData in
|
|
|
- // 添加 Data
|
|
|
- for dataItem in dataArray {
|
|
|
- if let data = dataItem["data"] as? Data,
|
|
|
- let fieldName = dataItem["fieldName"] as? String,
|
|
|
- let fileName = dataItem["fileName"] as? String,
|
|
|
- let mimeType = dataItem["mimeType"] as? String {
|
|
|
- multipartFormData.append(
|
|
|
- data,
|
|
|
- withName: fieldName,
|
|
|
- fileName: fileName,
|
|
|
- mimeType: mimeType
|
|
|
- )
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 添加其他参数
|
|
|
- if let parameters = parameters {
|
|
|
- for (key, value) in parameters {
|
|
|
- if let data = "\(value)".data(using: .utf8) {
|
|
|
- multipartFormData.append(data, withName: key)
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- },
|
|
|
- to: url, // 上传地址
|
|
|
- headers: defaultHeaders // 请求头
|
|
|
- )
|
|
|
-
|
|
|
- request
|
|
|
- .uploadProgress{ progress in
|
|
|
- // 3. 上传进度回调
|
|
|
- // 上传进度回调
|
|
|
- dePrint("✈️✈️✈️进度: \(progress.fractionCompleted * 100)%")
|
|
|
- progressHandler(progress)
|
|
|
- }
|
|
|
- .responseString{ response in
|
|
|
- // 4. 处理响应
|
|
|
- self.handleResponse(response, completion: completion)
|
|
|
- }
|
|
|
-
|
|
|
- return request
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- /// 下载文件
|
|
|
- /// - Parameters:
|
|
|
- /// - url: 下载URL
|
|
|
- /// - destination: 目标保存路径 (可选,不传则使用临时目录)
|
|
|
- /// - progressHandler: 进度回调 (0.0~1.0)
|
|
|
- /// - completion: 完成回调 (返回文件URL或错误)
|
|
|
- func downloadFile(
|
|
|
- urlString: String,
|
|
|
- to destination: URL? = nil,
|
|
|
- progressHandler: ((Double) -> Void)? = nil,
|
|
|
- completion: @escaping (Result<URL, Error>) -> Void
|
|
|
- ) -> DownloadRequest? {
|
|
|
-
|
|
|
-
|
|
|
- guard let url = URL(string: urlString) else {
|
|
|
- completion(.failure(NSError(domain: "url nil", code: 0)))
|
|
|
- return nil
|
|
|
- }
|
|
|
-
|
|
|
- // 设置下载目标路径
|
|
|
- let destination: DownloadRequest.Destination = { temporaryURL, response in
|
|
|
- // 如果用户指定了目标路径
|
|
|
- if let destination = destination {
|
|
|
- // 确保目录存在
|
|
|
- let directory = destination.deletingLastPathComponent()
|
|
|
- try? FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true, attributes: nil)
|
|
|
- return (destination, [.removePreviousFile, .createIntermediateDirectories])
|
|
|
- }
|
|
|
-
|
|
|
- // 否则使用临时目录
|
|
|
- let documentsURL = FileManager.default.temporaryDirectory
|
|
|
- let suggestedFilename = response.suggestedFilename ?? url.lastPathComponent
|
|
|
- let fileURL = documentsURL.appendingPathComponent(suggestedFilename)
|
|
|
-
|
|
|
- return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
|
|
|
- }
|
|
|
-
|
|
|
- // 开始下载
|
|
|
- let request = afSession.download(url, to: destination)
|
|
|
- .downloadProgress { progress in
|
|
|
- // 主线程回调进度
|
|
|
- DispatchQueue.main.async {
|
|
|
- progressHandler?(progress.fractionCompleted)
|
|
|
- }
|
|
|
- }
|
|
|
- .response { response in
|
|
|
- // 主线程回调结果
|
|
|
- DispatchQueue.main.async {
|
|
|
- switch response.result {
|
|
|
- case .success(let fileURL):
|
|
|
- if let fileURL = fileURL {
|
|
|
- completion(.success(fileURL))
|
|
|
- } else {
|
|
|
- completion(.failure(NSError(domain: "DownloadError", code: -1, userInfo: [NSLocalizedDescriptionKey: "文件路径无效"])))
|
|
|
- }
|
|
|
- case .failure(let error):
|
|
|
- completion(.failure(error))
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return request
|
|
|
- }
|
|
|
-
|
|
|
-}
|
|
|
-
|
|
|
-extension TSNetworkManager {
|
|
|
-
|
|
|
- private func handleResponse(
|
|
|
- _ response: AFDataResponse<String>,
|
|
|
- completion: @escaping (Result<Any, Error>) -> Void
|
|
|
- ) {
|
|
|
-
|
|
|
- switch response.result {
|
|
|
- case .success(let value):
|
|
|
-
|
|
|
- if let resultDict = dataToJSONObject(data:response.data) as? [String:Any] {
|
|
|
-
|
|
|
-// let code = resultDict.safeInt(forKey: "code")
|
|
|
-// if responseType != nil {
|
|
|
-// if let model = T(JSONString: value) {
|
|
|
-// handleSuccess(data: model, response: response, completion: completion)
|
|
|
-// } else {
|
|
|
-// let models = Mapper<T>().mapArray(JSONString: value)
|
|
|
-// handleSuccess(data: models ?? [], response: response, completion: completion)
|
|
|
-// }
|
|
|
-// } else {
|
|
|
- handleSuccess(data: resultDict, response: response, completion: completion)
|
|
|
-// }
|
|
|
-
|
|
|
- }else{
|
|
|
- handleFail(error: NSError(domain: "Unable to parse data", code: 0), response: response, completion: completion)
|
|
|
- }
|
|
|
-
|
|
|
- case .failure(let error):
|
|
|
- handleFail(error: error, response: response, completion: completion)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- func handleSuccess(data:Any,
|
|
|
- response: AFDataResponse<String>,
|
|
|
- completion: @escaping (Result<Any, Error>) -> Void){
|
|
|
- dePrint("🚗🚗🚗网络请求成功:\(String(describing: response.request?.url?.absoluteString))")
|
|
|
- dePrint("🚗🚗🚗网络请求成功:\(response)")
|
|
|
- completion(.success(data))
|
|
|
- }
|
|
|
-
|
|
|
- func handleFail(error:Error,
|
|
|
- response: AFDataResponse<String>,
|
|
|
- completion: @escaping (Result<Any, Error>) -> Void){
|
|
|
- dePrint("🚗🚗🚗网络请求失败:\(String(describing: response.request?.url?.absoluteString))")
|
|
|
- dePrint("🚗🚗🚗网络请求失败:\(response)")
|
|
|
- completion(.failure(error))
|
|
|
- }
|
|
|
-
|
|
|
- func dataToJSONObject(data: Data?) -> Any? {
|
|
|
- guard let data = data else { return nil }
|
|
|
- do {
|
|
|
- let jsonObject = try JSONSerialization.jsonObject(with: data, options: [])
|
|
|
- return jsonObject
|
|
|
- } catch {
|
|
|
- print("Failed to convert Data to JSON object: \(error.localizedDescription)")
|
|
|
- return nil
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-func kNetWorkCodeSuccess(data:Any?) -> [String:Any]? {
|
|
|
- guard let data = data else { return nil }
|
|
|
- if let dataDict = data as? [String:Any]{
|
|
|
- let code = dataDict.safeInt(forKey: "code")
|
|
|
-
|
|
|
- switch code {
|
|
|
- case 200://成功
|
|
|
- return dataDict
|
|
|
-// case 400://机器人提示
|
|
|
-// TSAbnormalPopUpAlertVC.showRobotWarning()
|
|
|
-// return nil
|
|
|
- default:
|
|
|
- return nil
|
|
|
- }
|
|
|
- }
|
|
|
- return nil
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-func kNetWorkResultSuccess(data:Any?) -> [String:Any]? {
|
|
|
- guard let dataDict = kNetWorkCodeSuccess(data: data) else { return nil }
|
|
|
- if let result = dataDict["result"] as? [String:Any]{
|
|
|
- return result
|
|
|
- }
|
|
|
- return nil
|
|
|
-}
|
|
|
-
|
|
|
-func kNetWorkMessage(data:Any?) -> String? {
|
|
|
- guard let data = data else { return nil }
|
|
|
- if let dataDict = data as? [String:Any] ,
|
|
|
- let message = dataDict["message"] as? String{
|
|
|
- return message
|
|
|
- }
|
|
|
- return nil
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-class JsonStringTransform<T:Mappable>: TransformType {
|
|
|
- typealias Object = T
|
|
|
- typealias JSON = [String: Any]
|
|
|
-
|
|
|
- func transformFromJSON(_ value: Any?) -> T? {
|
|
|
-
|
|
|
- if let jsonString = value as? String {
|
|
|
- let obj = T(JSONString: jsonString)
|
|
|
- return obj
|
|
|
- }
|
|
|
-
|
|
|
- if let dict = value as? [String: Any] {
|
|
|
- let obj = T(JSON: dict)
|
|
|
- return obj
|
|
|
- }
|
|
|
-
|
|
|
- return nil
|
|
|
- }
|
|
|
-
|
|
|
- func transformToJSON(_ value: T?) -> [String : Any]? {
|
|
|
- return value?.toJSON() as? [String: Any]
|
|
|
- }
|
|
|
-}
|