You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI适配API结构变更,正确获取分页事件数据的方法

解决方案:适配嵌套API结构的SwiftUI TabBar事件展示

1. 定义匹配API结构的Codable模型

根据你提供的API响应嵌套结构,创建对应的Codable模型,自动处理下划线字段与驼峰命名的转换:

import Foundation

// 顶层API响应模型
struct EventResponse: Codable {
    let status: Int
    let message: String
    let data: EventData
}

// data字段的嵌套结构
struct EventData: Codable {
    let upcoming: EventSection?
    let past: EventSection?
}

// 包含事件数组与分页信息的结构
struct EventSection: Codable {
    let events: [Event]
    let pagination: PaginationInfo
}

// 事件对象模型(根据实际API返回字段补充)
struct Event: Codable {
    let id: Int
    let title: String
    let eventDate: String
    let location: String?
    // 其他API返回的事件字段
}

// 分页信息模型(根据实际API返回字段补充)
struct PaginationInfo: Codable {
    let currentPage: Int
    let totalPages: Int
    let totalItems: Int
}

2. 修正网络请求与数据解析逻辑

调整请求参数编码与解码逻辑,适配新的嵌套结构,同时优化错误捕获:

import Foundation

func fetchEvents(timeline: Int, page: Int) async throws -> [Event] {
    guard let url = URL(string: "https://dev.myhss.org.uk/api/v1/events/eventlist") else {
        throw NetworkError.invalidURL
    }
    
    var request = URLRequest(url: url)
    request.httpMethod = "POST"
    request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
    
    // 编码请求参数为x-www-form-urlencoded格式
    let parameters = [
        "timeline": String(timeline),
        "current_page": String(page)
    ]
    request.httpBody = parameters.percentEncoded()
    
    let (data, response) = try await URLSession.shared.data(for: request)
    
    // 验证响应状态码
    guard let httpResponse = response as? HTTPURLResponse, (200...299).contains(httpResponse.statusCode) else {
        throw NetworkError.invalidResponse
    }
    
    // 解码JSON,自动转换下划线为驼峰
    let decoder = JSONDecoder()
    decoder.keyDecodingStrategy = .convertFromSnakeCase
    let eventResponse = try decoder.decode(EventResponse.self, from: data)
    
    // 根据timeline返回对应事件数组
    switch timeline {
    case 1:
        return eventResponse.data.upcoming?.events ?? []
    case 2:
        return eventResponse.data.past?.events ?? []
    default:
        return []
    }
}

// 辅助工具:字典转x-www-form-urlencoded数据
extension Dictionary where Key == String, Value == String {
    func percentEncoded() -> Data? {
        map { key, value in
            let escapedKey = key.addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed)!
            let escapedValue = value.addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed)!
            return "\(escapedKey)=\(escapedValue)"
        }
        .joined(separator: "&")
        .data(using: .utf8)
    }
}

extension CharacterSet {
    static let urlQueryValueAllowed: CharacterSet = {
        let generalDelimitersToEncode = ":#[]@"
        let subDelimitersToEncode = "!$&'()*+,;="
        
        var allowed = CharacterSet.urlQueryAllowed
        allowed.remove(charactersIn: "\(generalDelimitersToEncode)\(subDelimitersToEncode)")
        return allowed
    }()
}

// 自定义网络错误枚举
enum NetworkError: Error, LocalizedError {
    case invalidURL
    case invalidResponse
    
    var errorDescription: String? {
        switch self {
        case .invalidURL: return "无效的请求地址"
        case .invalidResponse: return "服务器响应异常"
        }
    }
}

3. SwiftUI TabBar视图实现

用TabView分别展示即将到来和过往事件,结合异步加载逻辑:

import SwiftUI

struct EventTabView: View {
    @State private var upcomingEvents: [Event] = []
    @State private var pastEvents: [Event] = []
    @State private var isLoading = false
    @State private var errorMsg: String?
    
    var body: some View {
        TabView {
            EventList(events: upcomingEvents, isLoading: isLoading, errorMsg: errorMsg)
                .tabItem {
                    Label("即将到来", systemImage: "calendar.badge.clock")
                }
                .onAppear {
                    Task { await loadEvents(timeline: 1) }
                }
            
            EventList(events: pastEvents, isLoading: isLoading, errorMsg: errorMsg)
                .tabItem {
                    Label("过往事件", systemImage: "calendar")
                }
                .onAppear {
                    Task { await loadEvents(timeline: 2) }
                }
        }
    }
    
    private func loadEvents(timeline: Int) async {
        isLoading = true
        errorMsg = nil
        
        do {
            let events = try await fetchEvents(timeline: timeline, page: 1)
            timeline == 1 ? (upcomingEvents = events) : (pastEvents = events)
        } catch {
            errorMsg = "加载失败:\(error.localizedDescription)"
        }
        
        isLoading = false
    }
}

struct EventList: View {
    let events: [Event]
    let isLoading: Bool
    let errorMsg: String?
    
    var body: some View {
        List {
            if isLoading {
                ProgressView()
                    .frame(maxWidth: .infinity)
            } else if let errorMsg = errorMsg {
                Text(errorMsg)
                    .foregroundColor(.red)
                    .frame(maxWidth: .infinity)
            } else if events.isEmpty {
                Text("暂无相关事件")
                    .frame(maxWidth: .infinity)
            } else {
                ForEach(events, id: \.id) { event in
                    VStack(alignment: .leading, spacing: 4) {
                        Text(event.title)
                            .font(.headline)
                        Text(event.eventDate)
                            .font(.subheadline)
                            .foregroundColor(.secondary)
                        if let location = event.location {
                            Text(location)
                                .font(.caption)
                        }
                    }
                    .padding(.vertical, 8)
                }
            }
        }
        .navigationTitle("事件列表")
    }
}

关键注意事项

  • 确保Event和PaginationInfo的字段完全匹配API返回的字段名,若API有新增字段,需同步更新模型。
  • 若API返回的upcoming/past可能为null,模型中需声明为可选类型(EventSection?),避免解码崩溃。
  • 错误处理部分替换了原有的模糊报错,可直接展示具体错误信息,方便排查API请求或解码问题。

内容的提问来源于stack exchange,提问作者user20925367

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 01:30:06