帮助我了解如何正确解析此类 JSON。我想不出正确的方法。这是 API 的链接。我需要在模型视图中指定什么?如果它是一个数组,那么他们的例子就很清楚了
@Published var viewModel: \[User\] = \[\]
。还是必须要提出一些条件?
网址https://run.mocky.io/v3/35e0d18e-2521-4f1b-a575-f0fe366f66e3
但如果这是这样的旋转
{
"id": 1,
"name": "Best five-star hotel in Hurghada, Egypt",
"adress": "Madinat Makadi, Safaga Road, Makadi Bay, Egypt",
"minimal_price": 134268,
"price_for_it": "For the tour with flight",
"rating": 5,
"rating_name": "Excellent",
"image_urls": ["https://www.atorus.ru/sites/default/files/upload/image/News/56149/Club_Priv%C3%A9_by_Belek_Club_House.jpg", "https://deluxe.voyage/useruploads/articles/The_Makadi_Spa_Hotel_02.jpg", "https://deluxe.voyage/useruploads/articles/article_1eb0a64d00.jpg"],
"about_the_hotel": {
"description": "VIP-class hotel with its own golf courses. High level of service. Recommended for a respectable vacation. The hotel accepts guests from 18 years old!",
"peculiarities": ["Free Wifi throughout the hotel", "1 km to the beach", "Free fitness club", "20 km to the airport"]
}
}
我不明白如何得到正确答案。
这是模型
struct Hotel: Codable, Identifiable {
let id: Int
let name, adress: String
let minimalPrice: Int
let priceForIt: String
let rating: Int
let ratingName: String
let imageUrls: [String]
let aboutTheHotel: AboutTheHotel
enum CodingKeys: String, CodingKey {
case id, name, adress
case minimalPrice = "minimal_price"
case priceForIt = "price_for_it"
case rating
case ratingName = "rating_name"
case imageUrls = "image_urls"
case aboutTheHotel = "about_the_hotel"
}
}
// MARK: - AboutTheHotel
struct AboutTheHotel: Codable {
let description: String
let peculiarities: [String]
}
这是视图模型
import Foundation
import SwiftUI
import Combine
class HotelViewModel: ObservableObject {
@Published var hotelViewModel: [String: String] = [:]
private var bag = Set<AnyCancellable>()
func fetchHotel() {
let urlString = "https://run.mocky.io/v3/35e0d18e-2521-4f1b-a575-f0fe366f66e3"
guard let url = URL(string: urlString) else {
print("🤬ERRRROOOOOOORRRRRR")
return
}
URLSession
.shared
.dataTaskPublisher(for: url)
.receive(on: DispatchQueue.main)
.tryMap { res in
guard let response = res.response as? HTTPURLResponse, response.statusCode >= 200 && response.statusCode < 300 else {
throw URLError(.badServerResponse)
}
let decoder = JSONDecoder()
guard let hotel = try? decoder.decode(Hotel.self, from: res.data) else {
throw URLError(.badURL)
}
return hotel
}
.sink { completion in
switch completion {
case .finished:
print("AllGood")
case .failure(let error):
print(error.localizedDescription)
}
} receiveValue: { [weak self] returnedHotel in
self?.hotelViewModel = returnedHotel
}
.store(in: &bag)
}
}
我不知道如何获得它。我找不到任何好的信息。
您的网址仅返回一家酒店。您必须将其转换为酒店数组并将其分配给@Published var。例如:
class HotelViewModel: ObservableObject {
@Published var hotels: [Hotel] = []
@Published var modelError = false
@Published var modelErrorMessage: String = ""
func fetchHotel() {
let urlString = "https://run.mocky.io/v3/35e0d18e-2521-4f1b-a575-f0fe366f66e3"
guard let url = URL(string: urlString) else {
print("🤬ERRRROOOOOOORRRRRR")
return
}
URLSession
.shared
.dataTaskPublisher(for: url)
.map(\.data) // Publish the received data
.decode(type: Hotel.self, decoder: JSONDecoder()) // decode data
.map({ hotel in
[hotel]
}) // transform hotel into an array
.catch() { error -> Just<[Hotel]> in
print("Error:\(error)")
DispatchQueue.main.sync {
self.modelError = true
self.modelErrorMessage = "\(error)"
}
return Just(self.hotels)
} // in case of error, return current hotel array
.receive(on: DispatchQueue.main) // on main queue to refresh display
.assign(to: &$hotels) // save into items
}
}
使用示例:
struct HotelImageView: View {
let imageUrl: String
var body: some View {
AsyncImage(url: URL(string: imageUrl)) { phase in
switch phase {
case .failure:
Color.red
.frame(width: 256, height: 256) .clipShape(RoundedRectangle(cornerRadius: 25))
case .success(let image):
image
.resizable()
.frame(width: 256, height: 256) .clipShape(RoundedRectangle(cornerRadius: 25))
default:
ProgressView()
.frame(width: 256, height: 256) .clipShape(RoundedRectangle(cornerRadius: 25))
}
}
}
}
struct HotelView: View {
var hotel: Hotel
var body: some View {
VStack {
Text("Hotel : " + hotel.name)
VStack(alignment: .leading) {
Text("Address : " + hotel.adress)
Text("rating : \(hotel.rating)")
Text("Price : \(hotel.minimalPrice)")
LazyVGrid(columns: [GridItem(), GridItem()]){
ForEach(hotel.imageUrls, id:\.self) { imageUrl in
HotelImageView(imageUrl: imageUrl)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}
.padding([.leading], 20)
}
}
}
struct ContentView: View {
@StateObject var hotelModel = HotelViewModel()
var body: some View {
VStack {
List {
Section {
ForEach(hotelModel.hotels) { hotel in
HotelView(hotel: hotel)
}
} header: {
Text("Hotels")
} footer: {
if self.hotelModel.modelError {
Text("Error \(self.hotelModel.modelErrorMessage)")
} else {
Text("Found \(self.hotelModel.hotels.count)")
}
}
}
}
.onAppear() {
self.hotelModel.fetchHotel()
}
}
}