API 结构更改为仅对象而不是数组。使用“current_page”和“timeline”参数获取数据的正确方法?

问题描述 投票:0回答:1

目标: 使用 SwiftUI 将 API 数据显示到选项卡栏中

我尝试过的:我尝试使用模型实现 API 并将其实现到选项卡视图中。但不幸的是 API 结构发生了巨大的变化,一切都变得混乱。

用于获取API数据的代码(更新了详细信息*第二次):

import SwiftUI
struct Event: Codable {
    let eventId: String
    let eventTitle: String
    let eventStartDate: String
    let eventEndDate: String
    let eventImg: String
}

enum Timeline: Int {
    case upcoming = 1
    case past = 2
}

struct MainView: View {
    @State private var events: [Event] = []
    
    var body: some View {
        TabView {
            NavigationView {
                if #available(iOS 14.0, *) {
                    List(events, id: \.eventId) { event in
                        VStack(alignment: .leading) {
                            Text(event.eventTitle)
                                .font(.headline)
                            Text(event.eventStartDate)
                                .font(.subheadline)
                            Text(event.eventEndDate)
                                .font(.subheadline)
                            if let url = URL(string: event.eventImg), let data = try? Data(contentsOf: url) {
                                Image(uiImage: UIImage(data: data)!)
                                    .resizable()
                                    .scaledToFit()
                            }
                        }
                    }
                    .navigationTitle("Upcoming Events")
                } else {
                    // Fallback on earlier versions
                }
            }
            .tabItem {
                Image(systemName: "calendar.badge.plus")
                Text("Upcoming")
            }
            .onAppear {
                fetchEvents(timeline: .upcoming, page: 1)
            }
            
            NavigationView {
                if #available(iOS 14.0, *) {
                    List(events, id: \.eventId) { event in
                        VStack(alignment: .leading) {
                            Text(event.eventTitle)
                                .font(.headline)
                            Text(event.eventStartDate)
                                .font(.subheadline)
                            Text(event.eventEndDate)
                                .font(.subheadline)
                            if let url = URL(string: event.eventImg), let data = try? Data(contentsOf: url) {
                                Image(uiImage: UIImage(data: data)!)
                                    .resizable()
                                    .scaledToFit()
                            }
                        }
                    }
                    .navigationTitle("Past Events")
                } else {
                    // Fallback on earlier versions
                }
            }
            .tabItem {
                Image(systemName: "calendar.badge.minus")
                Text("Past")
            }
            .onAppear {
                fetchEvents(timeline: .upcoming, page: 1)
            }
        }
    }
    
    private func fetchEvents(timeline: Timeline, page: Int) {
        let url = URL(string: "https://dev.myhss.org.uk/api/v1/events/eventlist")!
        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
        let body = "timeline=\(timeline.rawValue)&current_page=\(page)"
        request.httpBody = body.data(using: .utf8)
        URLSession.shared.dataTask(with: request) { data, response, error in
            guard let data = data, let events = try? JSONDecoder().decode([String: [Event]].self, from: data) else {
                print("Error fetching events: \(error?.localizedDescription ?? "Unknown error")")
                return
            }
            DispatchQueue.main.async {
                self.events = events[timeline == .upcoming ? "upcoming" : "past"] ?? []
            }
        }
        .resume()
    }
}

已实现(现有代码):这是已实现但突然发生巨大变化的内容:

如果收到的响应位于两个不同的数组中,如何使用 swiftUI 获取 JSON API?

问题是什么:我多次尝试更改模型,但仍然无法正确获取数据。 日志中出现错误:

“获取事件时出错:未知错误”

API 网址:https://dev.myhss.org.uk/api/v1/events/eventlist

参数:(键:值)

时间线:1(1-即将发生的事件,2-过去的事件)

当前页:1(1为第一页,2为第二页,依此类推)

body:x-www-form-unlencoded 或表单数据

Response 是仅获取即将发生的事件的数据的示例。当时间线值为2时,将显示过去的事件

理想的反应:

    {
        "status": true,
        "message": "Event Listing",
        "data": {
            "upcoming": {
                "0": {
                    "event_id": "5",
                    "event_title": "event title 5",
                    "event_short_description": "short desc 5",
                    "event_detailed_description": "details description 2",
                    "event_start_date": "2025-05-15 16:58:56",
                    "event_end_date": "0000-00-00 00:00:00",
                    "event_mode": "0",
                    "event_level": "0",
                    "event_level_details": null,
                    "event_for_karyakartas_only": "1",
                    "event_for_which_karyakartas": null,
                    "event_attendee_karyakarta_roles": null,
                    "event_attendee_gender": "M",
                    "event_age_category": null,
                    "event_reg_part_time_allowed": null,
                    "event_reg_guest_allowed": null,
                    "event_chargable_or_not": null,
                    "event_capacity": null,
                    "event_waiting_list_allowed": null,
                    "event_wait_list_capacity": null,
                    "event_contact": "<span>Chintu Parekh</span>\r\n<br> <span>[email protected]</span>\r\n<p>01255 9875585</p>",
                    "event_notes": "Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 55 BC, making it over 2000 years old. Richard McClintock, a Latin professor",
                    "event_additional_info_required": null,
                    "event_img": "https://dev.myhss.org.uk/assets/events/event-2.png",
                    "event_img_detail": "https://dev.myhss.org.uk/assets/events/event-2-detail.png",
                    "event_created_date": "2025-05-10 16:55:08",
                    "event_created_by": null,
                    "event_online_details": null,
                    "offline_address_id": null,
                    "building_name": "",
                    "address_line_1": "",
                    "address_line_2": "",
                    "city": null,
                    "county": null,
                    "postal_code": "",
                    "country": null,
                    "latitude": null,
                    "longitude": null
                },
                "1": {
                    "event_id": "10",
                    "event_title": "event title 10",
                    "event_short_description": "short desc 10",
                    "event_detailed_description": "details description 2",
                    "event_start_date": "2025-05-15 16:58:56",
                    "event_end_date": "0000-00-00 00:00:00",
                    "event_mode": "0",
                    "event_level": "0",
                    "event_level_details": null,
                    "event_for_karyakartas_only": "1",
                    "event_for_which_karyakartas": null,
                    "event_attendee_karyakarta_roles": null,
                    "event_attendee_gender": "M",
                    "event_age_category": null,
                    "event_reg_part_time_allowed": null,
                    "event_reg_guest_allowed": null,
                    "event_chargable_or_not": null,
                    "event_capacity": null,
                    "event_waiting_list_allowed": null,
                    "event_wait_list_capacity": null,
                    "event_contact": "<span>Chintu Parekh</span>\r\n<br> <span>[email protected]</span>\r\n<p>01255 9875585</p>",
                    "event_notes": "Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 55 BC, making it over 2000 years old. Richard McClintock, a Latin professor",
                    "event_additional_info_required": null,
                    "event_img": "https://dev.myhss.org.uk/assets/events/event-2.png",
                    "event_img_detail": "https://dev.myhss.org.uk/assets/events/event-2-detail.png",
                    "event_created_date": "2025-05-10 16:55:08",
                    "event_created_by": null,
                    "event_online_details": null,
                    "offline_address_id": null,
                    "building_name": "",
                    "address_line_1": "",
                    "address_line_2": "",
                    "city": null,
                    "county": null,
                    "postal_code": "",
                    "country": null,
                    "latitude": null,
                    "longitude": null
                },
                "2": {
                    "event_id": "2",
                    "event_title": "event title 2",
                    "event_short_description": "short desc 2",
                    "event_detailed_description": "details description 2",
                    "event_start_date": "2024-04-15 16:58:56",
                    "event_end_date": "2023-05-31 16:55:08",
                    "event_mode": "0",
                    "event_level": "0",
                    "event_level_details": null,
                    "event_for_karyakartas_only": "1",
                    "event_for_which_karyakartas": null,
                    "event_attendee_karyakarta_roles": null,
                    "event_attendee_gender": "M",
                    "event_age_category": null,
                    "event_reg_part_time_allowed": null,
                    "event_reg_guest_allowed": null,
                    "event_chargable_or_not": null,
                    "event_capacity": null,
                    "event_waiting_list_allowed": null,
                    "event_wait_list_capacity": null,
                    "event_contact": "<span>Chintu Parekh</span>\r\n<br> <span>[email protected]</span>\r\n<p>01253 9875485</p>",
                    "event_notes": "Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor",
                    "event_additional_info_required": null,
                    "event_img": "https://dev.myhss.org.uk/assets/events/event-2.png",
                    "event_img_detail": "https://dev.myhss.org.uk/assets/events/event-2-detail.png",
                    "event_created_date": "2023-04-10 16:55:08",
                    "event_created_by": null,
                    "event_online_details": "http://netflix-events.com",
                    "offline_address_id": "7",
                    "building_name": "",
                    "address_line_1": "Talbot Primary School",
                    "address_line_2": "East Moor Road",
                    "city": "Leeds",
                    "county": "Leeds",
                    "postal_code": "LS8 1AF",
                    "country": "United Kingdom",
                    "latitude": "53.842083",
                    "longitude": "-1.51691735"
                },
                "3": {
                    "event_id": "7",
                    "event_title": "event title 7",
                    "event_short_description": "short desc 7",
                    "event_detailed_description": "details description 2",
                    "event_start_date": "2024-04-15 16:58:56",
                    "event_end_date": "2023-05-31 16:55:08",
                    "event_mode": "0",
                    "event_level": "0",
                    "event_level_details": null,
                    "event_for_karyakartas_only": "1",
                    "event_for_which_karyakartas": null,
                    "event_attendee_karyakarta_roles": null,
                    "event_attendee_gender": "M",
                    "event_age_category": null,
                    "event_reg_part_time_allowed": null,
                    "event_reg_guest_allowed": null,
                    "event_chargable_or_not": null,
                    "event_capacity": null,
                    "event_waiting_list_allowed": null,
                    "event_wait_list_capacity": null,
                    "event_contact": "<span>Chintu Parekh</span>\r\n<br> <span>[email protected]</span>\r\n<p>01253 9875485</p>",
                    "event_notes": "Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor",
                    "event_additional_info_required": null,
                    "event_img": "https://dev.myhss.org.uk/assets/events/event-2.png",
                    "event_img_detail": "https://dev.myhss.org.uk/assets/events/event-2-detail.png",
                    "event_created_date": "2023-04-10 16:55:08",
                    "event_created_by": null,
                    "event_online_details": null,
                    "offline_address_id": null,
                    "building_name": "",
                    "address_line_1": "",
                    "address_line_2": "",
                    "city": null,
                    "county": null,
                    "postal_code": "",
                    "country": null,
                    "latitude": null,
                    "longitude": null
                },
                "4": {
                    "event_id": "8",
                    "event_title": "event title 8",
                    "event_short_description": "short desc 8",
                    "event_detailed_description": "details description 2",
                    "event_start_date": "2024-04-15 16:58:56",
                    "event_end_date": "2023-05-31 16:55:08",
                    "event_mode": "0",
                    "event_level": "0",
                    "event_level_details": null,
                    "event_for_karyakartas_only": "1",
                    "event_for_which_karyakartas": null,
                    "event_attendee_karyakarta_roles": null,
                    "event_attendee_gender": "M",
                    "event_age_category": null,
                    "event_reg_part_time_allowed": null,
                    "event_reg_guest_allowed": null,
                    "event_chargable_or_not": null,
                    "event_capacity": null,
                    "event_waiting_list_allowed": null,
                    "event_wait_list_capacity": null,
                    "event_contact": "<span>Chintu Parekh</span>\r\n<br> <span>[email protected]</span>\r\n<p>01253 9875485</p>",
                    "event_notes": "Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor",
                    "event_additional_info_required": null,
                    "event_img": "https://dev.myhss.org.uk/assets/events/event-2.png",
                    "event_img_detail": "https://dev.myhss.org.uk/assets/events/event-2-detail.png",
                    "event_created_date": "2023-04-10 16:55:08",
                    "event_created_by": null,
                    "event_online_details": null,
                    "offline_address_id": null,
                    "building_name": "",
                    "address_line_1": "",
                    "address_line_2": "",
                    "city": null,
                    "county": null,
                    "postal_code": "",
                    "country": null,
                    "latitude": null,
                    "longitude": null
                },
                "total_pages": 2,
                "current_page": 1,
                "per_page": 5,
                "pagination_links": "",
                "next": 2,
                "previous": 0,
                "total_records": 8
            },
            "past": {}
        }
    }
json swift
1个回答
0
投票

这是我的工作代码

... to correctly fetch API data smoothly when the API structure is completely changed?
注意所有细节。它使用动态键(AnyKey)来获取即将到来的和过去的数据 成一系列事件。您剩下要做的就是单独下载图像,而无需 使用
try? Data(contentsOf: url)
,请使用常规
URLSession
AsyncImage
,如图所示。

注意,您仍然需要查阅 API 文档,以确定哪些属性是可选的,然后将

?
添加到这些属性中。

代码中的重要部分是使用

do/try/catch
模式,其中
print(error)
中有一个
catch
。如果没有这个,你将一直努力寻找错误的原因。

import Foundation
import SwiftUI

struct ContentView: View {
    var body: some View {
        MainView()
    }
}

enum Timeline: Int {
    case upcoming = 1
    case past = 2
}

struct MainView: View {
    @State private var events: [Event] = []
    
    var body: some View {
        TabView {
            NavigationView {
                if #available(iOS 14.0, *) {
                    List(events) { event in
                        VStack(alignment: .leading) {
                            Text(event.eventTitle)
                                .font(.headline)
                            Text(event.eventStartDate)
                                .font(.subheadline)
                            Text(event.eventEndDate)
                                .font(.subheadline)
                            AsyncImage(url: URL(string: event.eventImg)) { image in
                              image
                                .resizable()
                                .scaledToFit()
                            } placeholder: { ProgressView() }
                        }
                    }
                    .navigationTitle("Upcoming Events")
                } else {
                    // Fallback on earlier versions
                }
            }
            .tabItem {
                Image(systemName: "calendar.badge.plus")
                Text("Upcoming")
            }
            .onAppear {
                fetchEvents(timeline: .upcoming, page: 1)
            }
            
            NavigationView {
                if #available(iOS 14.0, *) {
                    List(events) { event in
                        VStack(alignment: .leading) {
                            Text(event.eventTitle)
                                .font(.headline)
                            Text(event.eventStartDate)
                                .font(.subheadline)
                            Text(event.eventEndDate)
                                .font(.subheadline)
                            AsyncImage(url: URL(string: event.eventImg)) { image in
                              image
                                .resizable()
                                .scaledToFit()
                            } placeholder: { ProgressView() }
                        }
                    }
                    .navigationTitle("Past Events")
                } else {
                    // Fallback on earlier versions
                }
            }
            .tabItem {
                Image(systemName: "calendar.badge.minus")
                Text("Past")
            }
            .onAppear {
                fetchEvents(timeline: .past, page: 1)
            }
        }
    }
    
    private func fetchEvents(timeline: Timeline, page: Int) {
        let url = URL(string: "https://dev.myhss.org.uk/api/v1/events/eventlist")!
        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
        let body = "timeline=\(timeline.rawValue)&current_page=\(page)"
        request.httpBody = body.data(using: .utf8)
        
        URLSession.shared.dataTask(with: request) { data, response, error in
            guard let data = data else {
                print("Error fetching events: \(error)") // <-- here
                return
            }
            //  print("\n----> data: \(String(data: data, encoding: .utf8) as AnyObject) \n")
            do {
                let results = try JSONDecoder().decode(EventListing.self, from: data)
                //  print("\n----> results: \(results) \n")
                self.events = timeline == .upcoming ? results.data.upcoming.events : results.data.past.events
            } catch let error {
                print("\n----> decoding error: \(error) \n") // <-- here important
            }
        }
        .resume()
    }
}

struct EventListing: Codable {
    let status: Bool
    let message: String
    let data: DataClass
}

struct Event: Codable, Identifiable {
    let id = UUID()
    
    let eventID, eventTitle, eventShortDescription, eventDetailedDescription: String
    let eventStartDate, eventEndDate, eventMode, eventLevel: String
    let eventLevelDetails: String?
    let eventForKaryakartasOnly: String
    let eventForWhichKaryakartas, eventAttendeeKaryakartaRoles: String?
    let eventAttendeeGender: String
    let eventAgeCategory, eventRegPartTimeAllowed, eventRegGuestAllowed, eventChargableOrNot: String?
    let eventCapacity, eventWaitingListAllowed, eventWaitListCapacity: String?
    let eventContact, eventNotes: String
    let eventAdditionalInfoRequired: String?
    let eventImg, eventImgDetail: String
    let eventCreatedDate: String
    let eventCreatedBy: String?
    let status: String
    let eventOnlineDetails, offlineAddressID: String?
    let buildingName, addressLine1, addressLine2: String
    let city, county: String?
    let postalCode: String
    let country, latitude, longitude: String?
    
    enum CodingKeys: String, CodingKey {
        case country, latitude, longitude, status, city, county
        case eventID = "event_id"
        case eventTitle = "event_title"
        case eventShortDescription = "event_short_description"
        case eventDetailedDescription = "event_detailed_description"
        case eventStartDate = "event_start_date"
        case eventEndDate = "event_end_date"
        case eventMode = "event_mode"
        case eventLevel = "event_level"
        case eventLevelDetails = "event_level_details"
        case eventForKaryakartasOnly = "event_for_karyakartas_only"
        case eventForWhichKaryakartas = "event_for_which_karyakartas"
        case eventAttendeeKaryakartaRoles = "event_attendee_karyakarta_roles"
        case eventAttendeeGender = "event_attendee_gender"
        case eventAgeCategory = "event_age_category"
        case eventRegPartTimeAllowed = "event_reg_part_time_allowed"
        case eventRegGuestAllowed = "event_reg_guest_allowed"
        case eventChargableOrNot = "event_chargable_or_not"
        case eventCapacity = "event_capacity"
        case eventWaitingListAllowed = "event_waiting_list_allowed"
        case eventWaitListCapacity = "event_wait_list_capacity"
        case eventContact = "event_contact"
        case eventNotes = "event_notes"
        case eventAdditionalInfoRequired = "event_additional_info_required"
        case eventImg = "event_img"
        case eventImgDetail = "event_img_detail"
        case eventCreatedDate = "event_created_date"
        case eventCreatedBy = "event_created_by"
        case eventOnlineDetails = "event_online_details"
        case offlineAddressID = "offline_address_id"
        case buildingName = "building_name"
        case addressLine1 = "address_line_1"
        case addressLine2 = "address_line_2"
        case postalCode = "postal_code"
    }
}

struct DataClass: Codable {
    let upcoming: EventDatum
    let past: EventDatum
}

struct EventDatum: Codable {
    var events: [Event]
    let totalPages, currentPage, perPage: Int?
    let paginationLinks: String?
    let next, previous, totalRecords: Int?
    
    enum CodingKeys: String, CodingKey {
        case next, previous
        case totalPages = "total_pages"
        case currentPage = "current_page"
        case perPage = "per_page"
        case paginationLinks = "pagination_links"
        case totalRecords = "total_records"
    }
    
    struct AnyKey: CodingKey {
        var stringValue: String
        var intValue: Int?
        
        init?(stringValue: String) {  self.stringValue = stringValue  }
        init?(intValue: Int) { return nil }
    }
    
    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        totalPages = try values.decodeIfPresent(Int.self, forKey: .totalPages)
        currentPage = try values.decodeIfPresent(Int.self, forKey: .currentPage)
        perPage = try values.decodeIfPresent(Int.self, forKey: .perPage)
        next = try values.decodeIfPresent(Int.self, forKey: .next)
        previous = try values.decodeIfPresent(Int.self, forKey: .previous)
        totalRecords = try values.decodeIfPresent(Int.self, forKey: .totalRecords)
        paginationLinks = try values.decodeIfPresent(String.self, forKey: .paginationLinks)
        events = []
        let container = try decoder.container(keyedBy: AnyKey.self)
        container.allKeys.forEach { key in
            if let results = try? container.decode(Event.self, forKey: key) {
                self.events.append(results)
            }
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.