【问题标题】:SwiftUI + Decoding Yelp API ResponseSwiftUI + 解码 Yelp API 响应
【发布时间】:2020-10-22 08:19:47
【问题描述】:

我是使用 Swift 和 JSON 的新手,我会尽量描述我想要完成的工作。

我正在尝试访问 Yelp API 服务并返回和解码 JSON 结果并在列表中显示结果。

我已经成功地点击 API 并将结果记录到控制台,但我无法将结果映射到 UI 元素以在视图中显示。

下面是结果的结构和我试图在其中显示结果的视图。我在视图加载后从我的加载函数返回错误。

数据.swift

import SwiftUI

struct BusinessesResponse: Codable {
    let restaurants: [RestaurantResponse]
}

struct RestaurantResponse: Codable, Identifiable {
    let id: String
    var name: String
    var coordinates: [longlat]
    var is_closed: Bool
    var category: String
    var imageURL: URL
    var url: URL
    var review_count: Int
   var rating: Double
   var display_phone: String
   var distance: Double
}

内容视图

import SwiftUI
import YelpAPI
import Combine
import CoreLocation

struct ContentView: View {

    @ObservedObject private var locationManager = LocationManager()
    @ObservedObject var fetcher = RestaurantFetcher()
    
    var body: some View {
        VStack {
            List(fetcher.businesses) { restaurant in
                VStack (alignment: .leading) {
                    Text(restaurant.name)
                }
              }
           }
        }
    }

    struct ContentView_Previews: PreviewProvider {
        static var previews: some View {
            ContentView()
    }
}
public class RestaurantFetcher: ObservableObject {
    @Published var businesses = [RestaurantResponse]()
    
    init() {
        load(latitude: 28.4293403, longitude: -81.6241764)
    }
    
    func load(latitude: Double, longitude: Double) {
         let apikey = "API-KEY-HERE"
        let url = URL(string: "https://api.yelp.com/v3/businesses/search?latitude=\(latitude)&longitude=\(longitude)")!
        var request = URLRequest(url: url)
        request.setValue("Bearer \(apikey)", forHTTPHeaderField: "Authorization")
        request.httpMethod = "GET"
        
        URLSession.shared.dataTask(with: request) { (data, response, error) in
            do {
                if let d = data {
                    let decodedLists = try JSONDecoder().decode([RestaurantResponse].self, from: d)
                    DispatchQueue.main.async {
                        self.businesses = decodedLists
                    }
                } else {
                    print("No Data")
                }
            } catch {
                print ("Caught")
            }
        }.resume()
    }
}

来自 Yelp 的 API 的 JSON 响应

{
    "businesses": [
        {
            "id": "ZTgp2l3XbADwmOMM5rpWZg",
            "alias": "disneys-oak-trail-golf-course-lake-buena-vista",
            "name": "Disney's Oak Trail Golf Course",
            "image_url": "https://s3-media1.fl.yelpcdn.com/bphoto/G3oE_KJJ53H1iweD-j83yQ/o.jpg",
            "is_closed": false,
            "url": "https://www.yelp.com/biz/disneys-oak-trail-golf-course-lake-buena-vista?adjust_creative=s-hyKAjsx6P4UW-uqMn7aQ&utm_campaign=yelp_api_v3&utm_medium=api_v3_business_search&utm_source=s-hyKAjsx6P4UW-uqMn7aQ",
            "review_count": 12,
            "categories": [
                {
                    "alias": "golf",
                    "title": "Golf"
                }
            ],
            "rating": 3.5,
            "coordinates": {
                "latitude": 28.4055855,
                "longitude": -81.5956011
            },
            "transactions": [],
            "location": {
                "address1": "1950 W Magnolia Palm Dr",
                "address2": "",
                "address3": "",
                "city": "Lake Buena Vista",
                "zip_code": "32836",
                "country": "US",
                "state": "FL",
                "display_address": [
                    "1950 W Magnolia Palm Dr",
                    "Lake Buena Vista, FL 32836"
                ]
            },
            "phone": "+14079394653",
            "display_phone": "(407) 939-4653",
            "distance": 3845.3340908128034
        },
        {
            "id": "VVF9h1jhhOVXIvxe-MDK8g",
            "alias": "panther-lake-golf-course-winter-garden",
            "name": "Panther Lake Golf Course",
            "image_url": "https://s3-media1.fl.yelpcdn.com/bphoto/ff47f9jXs56s3Cf7obIapA/o.jpg",
            "is_closed": false,
            "url": "https://www.yelp.com/biz/panther-lake-golf-course-winter-garden?adjust_creative=s-hyKAjsx6P4UW-uqMn7aQ&utm_campaign=yelp_api_v3&utm_medium=api_v3_business_search&utm_source=s-hyKAjsx6P4UW-uqMn7aQ",
            "review_count": 1,
            "categories": [
                {
                    "alias": "hotels",
                    "title": "Hotels"
                },
                {
                    "alias": "golf",
                    "title": "Golf"
                }
            ],
            "rating": 4.0,
            "coordinates": {
                "latitude": 28.4419223,
                "longitude": -81.6303836
            },
            "transactions": [],
            "location": {
                "address1": "16301 Phil Ritson Way",
                "address2": "",
                "address3": "",
                "city": "Winter Garden",
                "zip_code": "34787",
                "country": "US",
                "state": "FL",
                "display_address": [
                    "16301 Phil Ritson Way",
                    "Winter Garden, FL 34787"
                ]
            },
            "phone": "+14076562626",
            "display_phone": "(407) 656-2626",
            "distance": 1620.1533458028462
        },
        {
            "id": "UdqKnhBDg4b04e38qFcjEA",
            "alias": "orange-83-pub-and-grill-winter-garden",
            "name": "Orange 83 Pub And Grill",
            "image_url": "https://s3-media2.fl.yelpcdn.com/bphoto/KjWvn26iBv13GnIUCW7z9Q/o.jpg",
            "is_closed": false,
            "url": "https://www.yelp.com/biz/orange-83-pub-and-grill-winter-garden?adjust_creative=s-hyKAjsx6P4UW-uqMn7aQ&utm_campaign=yelp_api_v3&utm_medium=api_v3_business_search&utm_source=s-hyKAjsx6P4UW-uqMn7aQ",
            "review_count": 1,
            "categories": [
                {
                    "alias": "pubs",
                    "title": "Pubs"
                }
            ],
            "rating": 4.0,
            "coordinates": {
                "latitude": 28.4419223,
                "longitude": -81.6303836
            },
            "transactions": [],
            "location": {
                "address1": "16301 Phil Ritson Way",
                "address2": null,
                "address3": "Orange County National Golf Center & Lodge",
                "city": "Winter Garden",
                "zip_code": "34787",
                "country": "US",
                "state": "FL",
                "display_address": [
                    "16301 Phil Ritson Way",
                    "Orange County National Golf Center & Lodge",
                    "Winter Garden, FL 34787"
                ]
            },
            "phone": "+14076562626",
            "display_phone": "(407) 656-2626",
            "distance": 1620.1533458028462
        }
    ],
    "total": 3,
    "region": {
        "center": {
            "longitude": -81.6241764,
            "latitude": 28.4293403
        }
    }
}

【问题讨论】:

    标签: json swift swiftui yelp-fusion-api


    【解决方案1】:

    首先,请注意在 JSON 响应的顶层有一个对象 { } 而不是数组 [ ]

    这意味着您需要解码BusinessesResponse 而不是[RestaurantResponse]

    let response = try JSONDecoder().decode(BusinessesResponse.self, from: d)
    self.businesses = response.restaurants
    

    还请注意,您正在尝试解码 restaurants,并且在 JSON 响应中您有 businesses。您可以将BusinessesResponse 中的字段重命名为restaurants,或者使用CodingKeys,这可能更好

    struct BusinessesResponse: Codable {
        enum CodingKeys: String, CodingKey {
            case restaurants = "businesses"
        }
    
        let restaurants: [RestaurantResponse]
    }
    

    另请注意,您的 JSON 响应中不存在 categoryimageUrl 字段。 JSON 响应中的 categories 字段是 objectsarray

    您可以这样做:

    struct RestaurantResponse: Codable, Identifiable {
        enum CodingKeys: String, CodingKey {
            case id, name, is_closed, categories, url, review_count, rating, display_phone, distance
            case imageURL = "image_url"
        }
    
        ...
        var categories: [RestaurantCategory]
        var imageURL: URL
        ...
    }
    
    struct RestaurantCategory: Codable {
        var alias: String
        var title: String
    }
    

    如果您决定使用CodingKeys,那么您还可以将其他变量:is_closedreview_count 更改为 camelCase

    或者,如果模型中的所有变量都是 JSON 响应中 snake_case 键的 camelCase 等效项,则可以使用:

    let decoder = JSONDecoder()
    decoder.keyDecodingStrategy = .convertFromSnakeCase
    

    【讨论】:

    • 使用RestaurantCategory 的上述结构来访问JSON 响应中的嵌套信息数组,我将如何在文本视图中显示它? Text(restaurant.title) 不起作用,但显然我使用它是错误的。
    • var categories: [RestaurantCategory] 是元素的集合,这意味着一个餐厅可以有多个类别。您要显示哪个?如果是第一个,你可以做restaurant.categories[0].title(假设categories不为空)。
    • 感谢您回答了我的问题。理想情况下,我计划显示所有类别,但我可以在其上放置一个 if 语句来控制它是否显示。谢谢你的帮助,我很感激。
    • 您可以在一个Text 视图中显示所有类别或在ForEach 中具有多个视图。但我建议您创建一个新问题,您可以在其中准确指定您希望如何显示您的类别。我很乐意为您提供帮助。
    猜你喜欢
    • 1970-01-01
    • 2018-02-12
    • 2016-06-07
    • 1970-01-01
    • 1970-01-01
    • 2019-09-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多