How to fetch JSON API using swiftUI if the response received are inside two different arrays?

huangapple go评论60阅读模式
英文:

How to fetch JSON API using swiftUI if the response received are inside two different arrays?

问题

目标:使用SwiftUI将API数据显示在标签栏中

尝试过的方法:我尝试使用模型实现API,并将其实现到标签视图中。

问题所在:我已经尝试多次更改我的模型,仍然无法正确获取数据。

错误信息:数据无法读取,因为格式不正确。

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

可选参数(可用):
键:event_type
值:1(1-即将举行的活动,2-过去的活动)

主体:x-www-form-unlencoded

理想响应:

{
    "status": true,
    "message": "Event Listing",
    "data": {
        "upcoming": [
            {
                "event_id": "1",
                "event_title": "event tile",
                "event_start_date": "2024-02-02 16:55:08",
                "event_end_date": "2024-06-20 16:55:08",
                "event_img": "https://dev.myhss.org.uk/assets/events/event-1.png",
                "event_chargable_or_not": "0"
            }
        ],
        "past": [
            {
                "event_id": "2",
                "event_title": "event title 2",
                "event_start_date": "2023-04-15 16:55:08",
                "event_end_date": "2023-05-31 16:55:08",
                "event_img": "https://dev.myhss.org.uk/assets/events/event-2.png",
                "event_chargable_or_not": null
            }
        ]
    }
}

从答案中更新的建议代码,但问题仍然存在,错误:数据无法读取,因为格式不正确。

SwiftUI代码:

import SwiftUI

struct EventListing: Codable {
    var status: Bool
    var message: String
    var data: Event2
}

struct Event2: Codable {
    var upcoming: [Event]
    var past: [Event]
}

struct Event: Codable, Identifiable {
    var id: String
    var title: String
    var startDate: Date
    var endDate: Date
    var imageURL: URL?
    var isChargeable: Bool

    enum CodingKeys: String, CodingKey {
        case id = "event_id"
        case title = "event_title"
        case startDate = "event_start_date"
        case endDate = "event_end_date"
        case imageURL = "event_img"
        case isChargeable = "event_chargable_or_not"
    }
}

import SwiftUI

struct OkView: View {
    var body: some View {
        TabView {
            UpcomingEventsView()
                .tabItem {
                    Image(systemName: "calendar.circle")
                    Text("Upcoming")
                }
            PastEventsView()
                .tabItem {
                    Image(systemName: "calendar.circle.fill")
                    Text("Past")
                }
        }
    }
}

struct UpcomingEventsView: View {
    @State private var events: [Event] = []

    var body: some View {
        List(events) { event in
            Text(event.title)
        }
        .onAppear {
            ApiService.fetchEvents(type: .upcoming) { result in
                switch result {
                case .success(let eventListing):
                    self.events = eventListing.data.upcoming
                case .failure(let error):
                    print(error.localizedDescription)
                }
            }
        }
    }
}

struct PastEventsView: View {
    @State private var events: [Event] = []

    var body: some View {
        List(events) { event in
            Text(event.title)
        }
        .onAppear {
            ApiService.fetchEvents(type: .past) { result in
                switch result {
                case .success(let eventListing):
                    self.events = eventListing.data.past
                case .failure(let error):
                    print(error.localizedDescription)
                }
            }
        }
    }
}

enum EventType {
    case upcoming
    case past
}

class ApiService {
    static let baseUrl = "https://dev.myhss.org.uk/api/v1/events/eventlist"
    static let dateFormatter = DateFormatter()

    static func fetchEvents(type: EventType, completion: @escaping (Result<EventListing, Error>) -> Void) {
        let endpoint = type == .upcoming ? "events/upcoming" : "events/past"
        guard let url = URL(string: "\(baseUrl)/\(endpoint)") else {
            completion(.failure(ApiError.invalidUrl))
            return
        }
        URLSession.shared.dataTask(with: url) { data, response, error in
            if let error = error {
                completion(.failure(error))
                return
            }
            guard let data = data else {
                completion(.failure(ApiError.noData))
                return
            }
            do {
                dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
                let decoder = JSONDecoder()
                decoder.dateDecodingStrategy = .formatted(dateFormatter)
                let eventListing = try decoder.decode(EventListing.self, from: data)
                completion(.success(eventListing))
            } catch let error {
                completion(.failure(error))
            }
        }.resume()
    }

    enum ApiError: Error {
        case invalidUrl
        case noData
    }
}
英文:

Aim: To show the API data into Tab bar using SwiftUI

What i tried : I tried to implement the API using model and implemented it into tab view.

What is the issue: i have tried to change my model many time, still the data is not correctly fetched.

Getting error: The data couldn’t be read because it isn’t in the correct format.

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

Optional parameter(can be used):
key : event_type
value: 1(1- upcoming events, 2- past events)

body: x-www-form-unlencoded

Ideal response:

{
&quot;status&quot;: true,
&quot;message&quot;: &quot;Event Listing&quot;,
&quot;data&quot;: {
&quot;upcoming&quot;: [
{
&quot;event_id&quot;: &quot;1&quot;,
&quot;event_title&quot;: &quot;event tile&quot;,
&quot;event_start_date&quot;: &quot;2024-02-02 16:55:08&quot;,
&quot;event_end_date&quot;: &quot;2024-06-20 16:55:08&quot;,
&quot;event_img&quot;: &quot;https://dev.myhss.org.uk/assets/events/event-1.png&quot;,
&quot;event_chargable_or_not&quot;: &quot;0&quot;
}
],
&quot;past&quot;: [
{
&quot;event_id&quot;: &quot;2&quot;,
&quot;event_title&quot;: &quot;event title 2&quot;,
&quot;event_start_date&quot;: &quot;2023-04-15 16:55:08&quot;,
&quot;event_end_date&quot;: &quot;2023-05-31 16:55:08&quot;,
&quot;event_img&quot;: &quot;https://dev.myhss.org.uk/assets/events/event-2.png&quot;,
&quot;event_chargable_or_not&quot;: null
}
]
}
}

Updated the suggested code from the answer , but still, the issue persist,
Error: The data couldn’t be read because it isn’t in the correct format.

SwiftUI code:

import SwiftUI
struct EventListing: Codable {
var status: Bool
var message: String
var data: Event2
}
struct Event2: Codable {
var upcoming: [Event]
var past: [Event]
}
struct Event: Codable, Identifiable {
var id: String // Add this property to conform to Identifiable
var title: String
var startDate: Date
var endDate: Date
var imageURL: URL?
var isChargeable: Bool
enum CodingKeys: String, CodingKey {
case id = &quot;event_id&quot;
case title = &quot;event_title&quot;
case startDate = &quot;event_start_date&quot;
case endDate = &quot;event_end_date&quot;
case imageURL = &quot;event_img&quot;
case isChargeable = &quot;event_chargable_or_not&quot;
}
}
import SwiftUI
struct OkView: View {
var body: some View {
TabView {
UpcomingEventsView()
.tabItem {
Image(systemName: &quot;calendar.circle&quot;)
Text(&quot;Upcoming&quot;)
}
PastEventsView()
.tabItem {
Image(systemName: &quot;calendar.circle.fill&quot;)
Text(&quot;Past&quot;)
}
}
}
}
struct UpcomingEventsView: View {
@State private var events: [Event] = []
var body: some View {
List(events) { event in
Text(event.title)
}
.onAppear {
ApiService.fetchEvents(type: .upcoming) { result in
switch result {
case .success(let eventListing):
self.events = eventListing.data.upcoming
case .failure(let error):
print(error.localizedDescription)
}
}
}
}
}
struct PastEventsView: View {
@State private var events: [Event] = []
var body: some View {
List(events) { event in
Text(event.title)
}
.onAppear {
ApiService.fetchEvents(type: .past) { result in
switch result {
case .success(let eventListing):
self.events = eventListing.data.past
case .failure(let error):
print(error.localizedDescription)
}
}
}
}
}
enum EventType {
case upcoming
case past
}
class ApiService {
static let baseUrl = &quot;https://dev.myhss.org.uk/api/v1/events/eventlist&quot;
static let dateFormatter = DateFormatter()
static func fetchEvents(type: EventType, completion: @escaping (Result&lt;EventListing, Error&gt;) -&gt; Void) {
let endpoint = type == .upcoming ? &quot;events/upcoming&quot; : &quot;events/past&quot;
guard let url = URL(string: &quot;\(baseUrl)/\(endpoint)&quot;) else {
completion(.failure(ApiError.invalidUrl))
return
}
URLSession.shared.dataTask(with: url) { data, response, error in
if let error = error {
completion(.failure(error))
return
}
guard let data = data else {
completion(.failure(ApiError.noData))
return
}
do {
dateFormatter.dateFormat = &quot;yyyy-MM-dd HH:mm:ss&quot;
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .formatted(dateFormatter)
let eventListing = try decoder.decode(EventListing.self, from: data)
completion(.success(eventListing))
} catch let error {
completion(.failure(error))
}
}.resume()
}
enum ApiError: Error {
case invalidUrl
case noData
}
}

答案1

得分: 0

你应该将你的 DTO 更改为

     struct EventListing: Codable {
        var status: Bool
        var message: String
        var data: Event2
        
    }
    struct Event2: Codable {
        var upcoming: [Event]
        var past: [Event]
        
    }
    
    struct Event: Codable, Identifiable {
        var id: String // 添加此属性以符合 Identifiable
        var title: String
        var startDate: Date
        var endDate: Date
        var imageURL: URL?
        var isChargeable: Bool
        
        enum CodingKeys: String, CodingKey {
            case id = "event_id"
            case title = "event_title"
            case startDate = "event_start_date"
            case endDate = "event_end_date"
            case imageURL = "event_img"
            case isChargeable = "event_chargable_or_not"
        }
    }
    
在你的代码中,我看不到数据部分,但应该像这样。更改代码后,请检查 URL 是否正确且数据是否正确传递。
英文:

You should change your DTO as

 struct EventListing: Codable {
var status: Bool
var message: String
var data: Event2
}
struct Event2: Codable {
var upcoming: [Event]
var past: [Event]
}
struct Event: Codable, Identifiable {
var id: String // Add this property to conform to Identifiable
var title: String
var startDate: Date
var endDate: Date
var imageURL: URL?
var isChargeable: Bool
enum CodingKeys: String, CodingKey {
case id = &quot;event_id&quot;
case title = &quot;event_title&quot;
case startDate = &quot;event_start_date&quot;
case endDate = &quot;event_end_date&quot;
case imageURL = &quot;event_img&quot;
case isChargeable = &quot;event_chargable_or_not&quot;
}
} 

in your codes i can not see the data part but that should be like these. After change code please check the url is correct and data is coming correctly.

答案2

得分: 0

尝试以下代码,使用具有POST标头的URLRequest,而不是您的URL,并修改Event模型,特别注意var isChargeable: String?而不是Bool,对我有效。

此外,您需要查阅服务器文档,确定哪些属性是可选的,并相应地调整代码(添加?)。

var isChargeable: String?  // &lt;--- 这里
英文:

Try the following code, using a URLRequest with a POST header, instead of your url, and
modified Event model, note especially, the var isChargeable: String? not Bool, works for me.

In addition, you need to consult the server docs, to determine which properties are optional, and adjust the code (add ?) accordingly.

struct ContentView: View {
var body: some View {
OkView()
}
}
struct OkView: View {
var body: some View {
TabView {
UpcomingEventsView()
.tabItem {
Image(systemName: &quot;calendar.circle&quot;)
Text(&quot;Upcoming&quot;)
}
PastEventsView()
.tabItem {
Image(systemName: &quot;calendar.circle.fill&quot;)
Text(&quot;Past&quot;)
}
}
}
}
struct UpcomingEventsView: View {
@State private var events: [Event] = []
var body: some View {
List(events) { event in
Text(event.title)
}
.onAppear {
ApiService.fetchEvents(type: .upcoming) { result in
switch result {
case .success(let eventListing):
self.events = eventListing.data.upcoming
case .failure(let error):
print(error.localizedDescription)
}
}
}
}
}
struct PastEventsView: View {
@State private var events: [Event] = []
var body: some View {
List(events) { event in
Text(event.title)
}
.onAppear {
ApiService.fetchEvents(type: .past) { result in
switch result {
case .success(let eventListing):
self.events = eventListing.data.past
case .failure(let error):
print(error.localizedDescription)
}
}
}
}
}
enum EventType {
case upcoming
case past
}
class ApiService {
static let baseUrl = &quot;https://dev.myhss.org.uk/api/v1/events/eventlist&quot;
static let dateFormatter = DateFormatter()
static func fetchEvents(type: EventType, completion: @escaping (Result&lt;EventListing, Error&gt;) -&gt; Void) {
let eventType = type == .upcoming ? &quot;1&quot; : &quot;0&quot; // &lt;--- here
// --- here
guard let url = URL(string: baseUrl) else {
completion(.failure(ApiError.invalidUrl))
return
}
// --- here
var request = URLRequest(url: url)
request.httpMethod = &quot;POST&quot;
request.addValue(&quot;application/x-www-form-urlencoded&quot;, forHTTPHeaderField: &quot;Content-Type&quot;)
request.setValue(&quot;application/json&quot;, forHTTPHeaderField: &quot;Accept&quot;)
request.httpBody = &quot;event_type=\(eventType)&quot;.data(using: .utf8)!  // &lt;--- here
// --- here (with: request)
URLSession.shared.dataTask(with: request) { data, response, error in
if let error = error {
completion(.failure(error))
return
}
guard let data = data else {
completion(.failure(ApiError.noData))
return
}
//        print(&quot;\n----&gt; data: \(String(data: data, encoding: .utf8) as AnyObject) \n&quot;)
do {
dateFormatter.dateFormat = &quot;yyyy-MM-dd HH:mm:ss&quot;
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .formatted(dateFormatter)
let eventListing = try decoder.decode(EventListing.self, from: data)
completion(.success(eventListing))
} catch let error {
print(&quot;\n----&gt; decoding error: \(error) \n&quot;)
completion(.failure(error))
}
}.resume()
}
enum ApiError: Error {
case invalidUrl
case noData
}
}
struct EventListing: Codable {
let status: Bool
let message: String
let data: DataClass
}
struct DataClass: Codable {
let upcoming: [Event]
let past: [Event]
}
struct Event: Codable, Identifiable {
var id: String // Add this property to conform to Identifiable
var title: String
var startDate: Date
var endDate: Date
var imageURL: URL?
var isChargeable: String?  // &lt;--- here
enum CodingKeys: String, CodingKey {
case id = &quot;event_id&quot;
case title = &quot;event_title&quot;
case startDate = &quot;event_start_date&quot;
case endDate = &quot;event_end_date&quot;
case imageURL = &quot;event_img&quot;
case isChargeable = &quot;event_chargable_or_not&quot;
}
}

huangapple
  • 本文由 发表于 2023年4月19日 19:27:22
  • 转载请务必保留本文链接:https://go.coder-hub.com/76053943.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定