英文:
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:
{
"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
}
]
}
}
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 = "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
}
}
答案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 = "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"
}
}
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? // <--- 这里
英文:
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: "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 eventType = type == .upcoming ? "1" : "0" // <--- here
// --- here
guard let url = URL(string: baseUrl) else {
completion(.failure(ApiError.invalidUrl))
return
}
// --- here
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.setValue("application/json", forHTTPHeaderField: "Accept")
request.httpBody = "event_type=\(eventType)".data(using: .utf8)! // <--- 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("\n----> data: \(String(data: data, encoding: .utf8) as AnyObject) \n")
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 {
print("\n----> decoding error: \(error) \n")
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? // <--- here
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"
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论