ios开发:封装alamofire

一,代码:

1,networkmanager

//
//  NetworkingManager.swift
//  helloworld2
//
//  Created by liuhongdi on 2026/4/6.
//

import Alamofire
import Foundation

// 2. 封装网络请求类
class NetworkManager {
    static let shared = NetworkManager()
    
    // 基础配置:超时时间、拦截器等
    private let session: Session = {
        let configuration = URLSessionConfiguration.af.default
        configuration.timeoutIntervalForRequest = 15
        return Session(configuration: configuration)
    }()
    
    private let baseURL = "http://www.test.net"

    /// 通用请求方法
    /// - Parameters:
    ///   - path: 接口路径
    ///   - method: HTTP方法
    ///   - parameters: 参数
    ///   - completion: 成功与失败的回调
    func request<T: Decodable>(
        _ path: String,
        method: HTTPMethod = .get,
        parameters: Parameters? = nil,
        completion: @escaping (Result<T, AFError>) -> Void
    ) {
        let url = baseURL + path
        
        // 可以在这里统一添加 Header (如 Token)
        let headers: HTTPHeaders = [
            "Authorization": "Bearer your_token_here",
            "Accept": "application/json"
        ]

        session.request(url, method: method, parameters: parameters, encoding: URLEncoding.default, headers: headers)
            .validate() // 验证状态码 200..<300
            .responseDecodable(of: ApiResponse<T>.self) { response in
                switch response.result {
                case .success(let apiResponse):
                    print("manager中获取到数据:")
                    print(apiResponse)
                    
                    // 这里可以根据业务 code 处理逻辑错误
                    if apiResponse.code == 200 {
                        let data = apiResponse.data
                        completion(.success(data))
                    } else {
                        // 封装自定义错误或直接返回
                        print("业务错误: \(apiResponse.msg)")
                    }
                    
                case .failure(let error):
                    print("manager中返回错误:")
                    completion(.failure(error))
                }
            }
    }
}

2, 响应体model


import Foundation

// 使用泛型 T,并约束 T 必须符合 Decodable 协议
struct ApiResponse<T: Decodable>: Decodable {
    let status: String
    let code: Int
    let time: String
    let msg: String
    let data: T  // 这里可以是任何类型:List 结构体、单个对象、甚至是一个数组
}

3, viewmodel

import Foundation
import Alamofire

@MainActor // 确保 UI 更新在主线程
class PoemViewModel: ObservableObject {
    
    @Published var poems: [Poem] = []
    @Published var isLoading = false
    @Published var currentPage = 0
    @Published var isFull = false       // 是否全部加载完毕
    
    func fetchData(isRefresh: Bool=false) async {
        
        // 如果正在加载或已加载完毕且不是刷新,则直接返回
        if self.isLoading || (self.isFull && !isRefresh) { return }
        
        if isRefresh {
            self.isFull = false
            self.currentPage = 0
        }
        
        self.isLoading = true
        let destPage = self.currentPage+1

        let path = "/list.php?p="+String(destPage)
        print("当前请求接口:"+path)
        NetworkManager.shared.request(path, method: .get) { (result: Result<PoemListData, AFError>) in
                switch result {
                case .success(let res):

                    if (isRefresh == true) {
                        self.poems = res.list
                    } else {
                        self.poems.append(contentsOf: res.list)
                    }
                    if res.pageinfo.current_page == res.pageinfo.pages {
                        print("isFull = true")
                        self.isFull = true
                    } else {
                        print("isFull = false")
                    }
                    
                    self.currentPage = destPage
                    
                case .failure(let error):
                    print("网络请求失败: \(error.localizedDescription)")
                }
            }
        
        self.isLoading = false
    }
}

4,view

//
//  ContentView.swift
//  helloworld2
//
//  Created by liuhongdi on 2026/3/28.
//

import SwiftUI
import Alamofire


struct ContentView: View {
    @State private var message = "加载中..."

        //@State private var poems: [Poem] = [] // 存储解析后的数组
        // 初始化 ViewModel
        @StateObject private var viewModel = PoemViewModel()
    
         var body: some View {

             NavigationStack {
                 VStack {
                     Text(message)

                     List {
                         if viewModel.isLoading && viewModel.poems.isEmpty {
                             ProgressView("加载中..")
                         }
                         ForEach(viewModel.poems){ poem in
                             NavigationLink(value: poem) {
                                  PoemRow(poem: poem) // 进一步拆分出子视图
                                     .onAppear() {
                                         
                                         // 核心逻辑:如果当前行是数组最后一行,触发加载更多
                                         if poem == viewModel.poems.last {
                                             Task {
                                                 //let nextPage = viewModel.currentPage+1
                                                 await viewModel.fetchData(isRefresh: false)
                                             }
                                         }

                                     }
                             }
                         }
                     }
                     .navigationTitle("唐诗鉴赏1")
                     .navigationDestination(for: Poem.self) { poem in
                         Mp3View()
                     }
                     // 调用逻辑
                     .task{
                         print("列表出现时的task")
                         await viewModel.fetchData(isRefresh: false)
                     }
                     .refreshable{
                         await viewModel.fetchData(isRefresh: true)
                     }
                     
                 }
             }
         }

    
    
}


// 一行元素的view
struct PoemRow: View {
    let poem: Poem
    var body: some View {
        HStack(spacing: 15) {
            AsyncImage(url: URL(string: poem.image)) { image in
                image.resizable().aspectRatio(contentMode: .fit)
            } placeholder: {
                Color.purple.opacity(0.1)
            }
            .frame(width: 60, height: 60)
            .background(Color.purple)
            .cornerRadius(8)
            
            VStack(alignment: .leading){
                Text(poem.title).font(.headline)
                Text(poem.author).font(.subheadline).foregroundColor(.secondary)
            }
        }.frame(height:120)
    }
}

#Preview {
    ContentView()
}

二,测试效果:

image

posted @ 2026-04-06 14:21  刘宏缔的架构森林  阅读(2)  评论(0)    收藏  举报