swift part 6 三方库综合

一、 SnapKit

swift 复制代码
// 1 view.safeAreaLayoutGuide = 安全区域
// 它是自动帮你避开导航栏、TabBar、刘海、底部小黑条 的布局工具

backView.snp.makeConstraints { make in // lightGray

    make.edges.equalTo(view.safeAreaLayoutGuide).inset(10) //  内嵌  距离父视图四周 10pt
}
swift 复制代码
// 2 offset 偏移 向外偏移 往正方向移动(边向外扩)
//   inset  内嵌 向内收缩 往反方向缩进(边向内收)

backView1.snp.makeConstraints { make in

    make.left.right.equalToSuperview().inset(20) //内嵌
    make.top.equalToSuperview()
    make.height.equalTo(40)
}

backView2.snp.makeConstraints { make in

    make.top.equalTo(backView1.snp.bottom).offset(10)
    make.left.width.height.equalTo(backView1)
}
swift 复制代码
// 3 设置size

backView3.snp.makeConstraints { make in

    make.bottom.equalToSuperview().inset(10)
    make.left.equalToSuperview().offset(10)
    make.size.equalTo(CGSize(width: 100, height: 40))
}
swift 复制代码
// 4 设置宽高比 设置 center centerX

backView4.snp.makeConstraints { make in

    make.centerY.equalToSuperview().offset(100)
    make.left.equalToSuperview().offset(10)
    make.width.equalTo(100)
    make.height.equalTo(backView4.snp.width).multipliedBy(0.5)  // 高是宽的 0.5 倍

}
swift 复制代码
// 5 高度自适应

backView5.text = getRandomString(length: 60) // 随机60个汉字
backView5.snp.makeConstraints { make in // blue

    make.left.equalTo(backView4.snp.right).offset(10)
    make.top.equalTo(backView4)
    make.right.equalToSuperview().inset(10)
}
swift 复制代码
// 6 lessThanOrEqualTo 不超过

backView6.text = getRandomString(length: 200) 
backView6.snp.makeConstraints { make in // purple

    make.top.equalTo(backView5.snp.bottom).offset(5)
    make.left.equalTo(backView).offset(30)
    make.right.equalTo(backView).inset(10)
    make.height.lessThanOrEqualTo(100)
}
swift 复制代码
// 7 优先级

//  什么是 约束优先级(priority)? iOS 自动布局里,如果两个约束冲突了
//  比如:一边要求宽度 = 100,一边又要求宽度 = 200)系统就会看优先级:谁数字大,听谁的!

// .low = 250
// .medium = 500
// .high = 750
// .required = 1000(默认)

backView7.text = getRandomString(length: 200) 
backView7.snp.makeConstraints { make in // yellow

    make.top.equalTo(backView2.snp.bottom).offset(10)

    //  希望尽量左右撑满,但优先级低 (750)
    make.left.greaterThanOrEqualToSuperview().offset(20).priority(750)
    make.right.lessThanOrEqualToSuperview().offset(-20).priority(750)

    // 核心:宽度最大为 300,优先级高 (1000 - 必须满足)
    make.width.lessThanOrEqualTo(300).priority(.required)  // 默认就是 1000
}

二、 Alamofire

1.1 GET / POST

swift 复制代码
// 配置全局session

 var session: Session = {
    // 1. 创建配置
    let configuration = URLSessionConfiguration.default

    // 2. 配置 Session 参数
    configuration.timeoutIntervalForRequest = 30
    configuration.timeoutIntervalForResource = 60
    configuration.allowsCellularAccess = true

    // 3. 配置全局 Header
    configuration.httpAdditionalHeaders = [
        "Accept": "application/json",
        "User-Agent": "MyApp/1.0",
        "Content-Type": "application/x-www-form-urlencoded"
    ]

    // 4. 创建 Session
    let sess = Session(configuration: configuration)

    return sess
}()
swift 复制代码
// 发送请求 get/post

func loadData() {
    let url = "https://v.juhe.cn/toutiao/index"

    var head = HTTPHeaders()
    head.add(HTTPHeader(name: "Content-Type", value: "application/x-www-form-urlencoded"))

    let parames = ["key":"ly8bc00637b81dead3162dcd12f06e5a1e"]

    session.request(url, method: .post, parameters: parames, headers: head)
        .responseDecodable(of: AFListModel.self) { (response: AFDataResponse<AFListModel>)  in
            print("---respose",response)
            switch response.result {
            case .success(let value):
                self.model = value
                self.tableView.reloadData()
                print(self.model ?? "")

            case .failure(let error):
                print("---error---",error)

            }
        }
}

//  public let AF = Session.default

1.2 表单上传

swift 复制代码
func uploadToPicGo() {
    // 1. 准备图片
    guard let image = UIImage(named: "nonedata"),
          let imageData = image.jpegData(compressionQuality: 0.8) else {
        print("❌ 图片加载失败")
        return
    }

    // 2. 配置请求
    let url = "https://www.picgo.net/api/1/upload"

    var headers = HTTPHeaders()
    headers.add(name: "Content-Type", value: "multipart/form-data")

    // 3. 发起上传
    AF.upload(multipartFormData: { formData in
        formData.append(imageData,                 // 文件的value
                       withName: "image",          // 文件的key 
                       fileName: "test_photo.jpg", // 服务器保存时用的名字
                       mimeType: "image/jpeg")     // 文件类型

    }, to: url, method: .post, headers: headers)
    .responseString { response in
        switch response.result {
        case .success(let value):
            print("上传成功!")
            print("服务器返回: \(value)")

            //  URL 在 data.url 字段
            if let json = value as? [String: Any],
               let data = json["data"] as? [String: Any],
               let imageUrl = data["url"] as? String {
                print("📸 图片地址: \(imageUrl)")
            }

        case .failure(let error):
            print("上传失败: \(error)")
        }
    }
}


// 传文件路径
// public func append(_ fileURL: URL, withName name: String, fileName: String, mimeType: String) { ...... }

// responseString 为临时打印数据, 开发推荐用这个模型去接收返回值 
// .responseDecodable(of: AFListModelself { (response: AFDataResponse<AFListModel>)  in ...... }

1.3 下载

swift 复制代码
 func downloadVideo() {
        guard let url = URL(string: testURL) else {
            statusLabel.text = "无效的 URL"
            return
        }

        // 1. 定义下载文件的保存位置
        let destination: DownloadRequest.Destination = { _, _ in
            let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
            let fileURL = documentsURL.appendingPathComponent("downloaded_test_video.mp4")
            // 如果之前有相同文件,先删除,确保测试每次都重新下载
            if FileManager.default.fileExists(atPath: fileURL.path) {
                try? FileManager.default.removeItem(at: fileURL)
            }
            return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
        }

        statusLabel.text = "开始下载..."

        // 2. 发起下载请求并监听进度
        AF.download(url, to: destination)
            .downloadProgress { progress in
                DispatchQueue.main.async { // 更新
                    self.progressView.progress = Float(progress.fractionCompleted)
                    let percent = Int(progress.fractionCompleted * 100)
                    self.statusLabel.text = "下载进度: \(percent)%"
                    print("进度: \(percent)%")
                }
            }
            .response { response in
                DispatchQueue.main.async { // 更新
                    switch response.result {
                    case .success(let fileURL):
                        self.statusLabel.text = "下载完成!"
                        print("文件保存至: \(fileURL?.path ?? "未知路径")")
                    case .failure(let error):
                        self.statusLabel.text = "下载失败: \(error.localizedDescription)"
                        print("错误: \(error)")
                    }
                }
            }
    }

三、 moya

Moya 核心

  • 你需要创建一个遵循 TargetType 协议的枚举(Enum)
  • 用它来列出应用中的所有 API 端点。

3.1 配置moya

swift 复制代码
/// 1- 定义 loding样式, session, 超时,  MoyaProvider

import HandyJSON
import Moya
import Alamofire
import Result
import MBProgressHUD
import SwiftyJSON


/// loding样式
let LoadingPlugin = NetworkActivityPlugin { (type, target) in
    DispatchQueue.main.async {
        guard let vc = topVC else { return }
        switch type {
        case .began:
            MBProgressHUD.hide(for: vc.view, animated: false)
            MBProgressHUD.showAdded(to: vc.view, animated: true)
       
        case .ended:
            MBProgressHUD.hide(for: vc.view, animated: true)
        }
    }
}

// 配置session
let session: Session = {
//    let evaluators: [String: ServerTrustEvaluating] = [
//      "abcd.citibank.com": DisabledTrustEvaluator(),
//        "eat.beef.com.cn": DisabledTrustEvaluator()
//   ]
//
//    let serverTrustManager = ServerTrustManager(evaluators: evaluators)
//    let configuration = URLSessionConfiguration.default
//    return Session(configuration: configuration, serverTrustManager: serverTrustManager)
    
    return Session(configuration: URLSessionConfiguration.default)
}()

// 配置超时
let timeoutClosure = {(endpoint: Endpoint, closure: MoyaProvider<HttpRueqest>.RequestResultClosure) -> Void in
    if var urlRequest = try? endpoint.urlRequest() {
        //设置请求时长
        urlRequest.timeoutInterval = 15
        closure(.success(urlRequest))
    } else {
        closure(.failure(MoyaError.requestMapping(endpoint.url)))
    }
}

// 无加载动画
let ApiProvider = MoyaProvider<HttpRueqest>(requestClosure: timeoutClosure, session: session)
// 有加载动画
let ApiLoadingProvider = MoyaProvider<HttpRueqest>(requestClosure: timeoutClosure,  session: session, plugins: [LoadingPlugin])


/// 2-  定义 枚举 
// 每一个case 代表一个接口

enum HttpRueqest {
    // 获取验证码
    case getCode
    //任务详情
    case taskDetail(tkid:String, excutionStatus: String, idList: [String], testType: String ,orderStatus:String)
    // 测试
    case test
    // 笑话列表
    case joke
}


/// 3- 枚举遵守 TargetType 协议
// 配置每个接口的 url 参数  请求方式 header
extension HttpRueqest: TargetType { // TargetType协议需要 实现如下变量
    // 3.1 配置基础base url
    
    var baseURL: URL {
        switch self {
        case .test  :
            //生产
            //  return  URL(string: "https://ubpapi.beetest.com.cn/")!
            //测试
            return  URL(string: "https://v.juhe.cn/")!
        default:
            return URL(string: "https://v.juhe.cn/" )!
        }
    }
    //  3.2 配置具体路径
    
    var path: String {
        switch self {
        case .getCode:
            return "ucenter/sms/" + (UserDefaults.standard.string(forKey: AccountInfo().phoneNum) ?? "" ) + "/13" + "/" + (UserDefaults.standard.string(forKey: AccountInfo().pictureNum) ?? "aaaaaa" )
        case .taskDetail:
            return "ucenter/login/cbsp/"
            
        case .test:
            return "test"
        case .joke:
            return "joke/content/list"
        }
    }
    // 3.3 http方法
    var method: Moya.Method {
        switch self {
        case .getCode,.joke:
            return .get
        case .taskDetail, .test:
            return .post
        }
    }
    
    // 3.4 请求参数
    var task: Moya.Task {
        var param: [String:Any] = [:]
        switch self {
        case .getCode:
            param =  [:]
        case .taskDetail:
            param =  [:]
        case .test:
            param =  [:]
            
        case .joke:
            let timeStamp10  = Int(Date().timeIntervalSince1970)
            param = ["sort":"desc",
                     "time":timeStamp10,
                     "key":"ly7f7b6e816760ec15f878b0e0ec7aa5e9"
            ]
            return .requestParameters(parameters: param, encoding: URLEncoding.default)
        }
        
        return .requestParameters(parameters: param, encoding: JSONEncoding.default)
    }
    
    // 3.5 请求头可选
    var headers: [String : String]? {
        switch self {
        case  .test, .taskDetail:
            return ["Content-Type":"application/json" , "Accept":"application/json",
                    "authorization": String(UserDefaults.standard.string(forKey: AccountInfo().token) ?? "")]
        case .joke:
            return ["Content-Type":"application/x-www-form-urlencoded"]
        default:
            return ["Content-Type":"application/json" , "Accept":"application/json"]
        }
        
    }
}


/// 4 封装网络请求
extension MoyaProvider {
    @discardableResult
    // 返回 returnData
    public func request<T: HandyJSON>(_ target: Target,
                                    model: T.Type,
                                    completion: ((_ returnData: T?) -> Void)?) -> Cancellable? {
        // 判断网络
        CCTools.shared.netWorkReachability { (status) in
            if status == .notReachable {
                CProgressHUD.hideHud()
                CProgressHUD.showErrorMessage(message: "网络异常")
                return ;
            }
        }
        return request(target, completion: { (result) in // (_ result: Result<Moya.Response, MoyaError>)
            // 1 没有回调函数
            guard let completion = completion else {
                return
            }
            print("-------->target:",target)
            print("-------->url:",String(describing: result.value?.request))
            // 2 没有返回结果
            guard let returnData = try? result.value?.mapModel(T.self) else {
                completion(nil)
                return
            }
            // 3 返回 returnData
            completion(returnData)
        })
    }
}


/// 5 数据转模型
extension Response {
    func mapModel<T: HandyJSON>(_ type: T.Type) throws -> T {
        // 1 转json
        let jsonString = String(data: data, encoding: .utf8)
                
        print(jsonString ?? "")
        
        // 2 转字典
        let dict = try? JSONSerialization.jsonObject(with: (jsonString?.data(using: .utf8))!, options: .mutableContainers)
        if dict != nil {
            
            _ = dict as! NSDictionary
            
        }
        // 3 转模型
        guard let model = JSONDeserializer<T>.deserializeFrom(json: jsonString) else {
            throw MoyaError.jsonMapping(self)
        }
        // 4 返回模型
        return model
    }
}

3.2 使用moya

swift 复制代码
// 1 moya原生方法 发送请求

// -> 获取json数据

func load1 () {
    ApiLoadingProvider.request(.joke) { result in
        print(result)
        switch result {
        case .success(let resp):
            print("->状态码:\(resp.statusCode)") // 打印状态码
            let jsonStr = String(data: resp.data, encoding: .utf8) ?? "空数据"
            print("->接口返回原始JSON:\(jsonStr)")
        case .failure(let err):
            print("->请求失败:\(err.localizedDescription)")

        }
    }
}
// resp 为 Moya.Response类型, 通过获取属性data从而获取数据json 

// result:  Result<Moya.Response, MoyaError>)枚举

// 所以let resp 为 Moya.Response类型,  case success(Success)


// 注意! 直接打印 result 不能打印出错误信息,需匹配success, failure 才行 
swift 复制代码
// 2 moya原生方法 + 封装  发送请求

// -> 获取模型

func load2() {
    ApiLoadingProvider.request(.joke, model: jockModel.self) { [unowned self] result in
        if result?.error_code == 0, let data = result?.result?.data {
            self.dataSource.append(contentsOf: data)
            self.tableView.reloadData()
        } else {
            CProgressHUD.showErrorMessage(message: result?.reason)
        }
    }
}
// result为 jockModel模型
swift 复制代码
// 3 Alamofire 原生请求

// -> 获取模型

func load3() {
    let url = "https://v.juhe.cn/joke/content/list"

    var head = HTTPHeaders()
    head.add(HTTPHeader(name: "Content-Type", 
                        value: "application/x-www-form-urlencoded"))

    let parames = ["sort":"desc",
                   "time":Int(Date().timeIntervalSince1970),
                   "key":"ly7f7b6e816760ec15f878b0e0ec7aa5e9",
                   "page": "1",
                   "pagesize": 20
                  ] as [String : Any]

    AF.request(url, method: .post, parameters: parames, headers: head)
        .responseDecodable(of: jockModel.self) { (response: AFDataResponse<jockModel>)  in
            print("---respose",response)
            switch response.result {
            case .success(let value): 
                self.dataSource.append(contentsOf: value.result?.data ?? [])
                let model: jockLastModel =  self.dataSource[0]
                print("---->",model.content ?? "")

            case .failure(let error):
                print("---error---",error)

            }
        }
}
// success value 为 jockModel模型

// response为结构体类型接收泛型,  DataResponse<Success, AFError> 

// DataResponse 的result属性为 枚举类型 result: Result<Success, Failure>
相关推荐
多彩电脑1 天前
Swift里字符串的索引
开发语言·swift
大熊猫侯佩2 天前
WWDC26 前瞻:告别野蛮生长,SwiftUI 即将迎来的「工业级」进化
swiftui·swift·wwdc
大熊猫侯佩3 天前
丢掉包袱,硬刚 Rust:WWDC26 前瞻与 Swift 6.4 的底层革命
swift·编译器·wwdc
大熊猫侯佩3 天前
WWDC26前瞻:Swift 即将迎来的那些新内置类型
swift·编程语言·wwdc
人月神话-Lee4 天前
【图像处理】图像直方图——从“频率分布“到“智能决策“
图像处理·人工智能·ios·ai编程·swift
划水的code搬运工小李4 天前
下载CSDN到PDF
开发语言·pdf·swift
2501_915106325 天前
iOS开发工具有哪些?iOS 开发每个阶段的实用工具
ide·vscode·ios·objective-c·个人开发·swift·敏捷流程