Reputation: 1399
I'm new to working with Swift and JSON, I'll try to describe what I'm trying to accomplish as best as I can.
I'm trying to access the Yelp API service and return and decode the JSON results and display the results in a list.
I've successfully been able to hit the API and log the results to the console, but I haven't been able to map the results to UI elements to display within a view.
Below is the struct for the results and the view I'm attempting to display the results in. I'm returning the error from my load function after the view loads.
Data.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
}
ContentView
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()
}
}
JSON response from Yelp's API
{
"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
}
}
}
Upvotes: 1
Views: 542
Reputation: 54611
First of all, note that at the top level of your JSON response there is an object { }
and not an array [ ]
.
Which means you need to decode BusinessesResponse
instead of [RestaurantResponse]
:
let response = try JSONDecoder().decode(BusinessesResponse.self, from: d)
self.businesses = response.restaurants
Also note you're trying to decode restaurants
and in the JSON response you have businesses
. You can either rename your field inside BusinessesResponse
to restaurants
or, which might be better, use CodingKeys
:
struct BusinessesResponse: Codable {
enum CodingKeys: String, CodingKey {
case restaurants = "businesses"
}
let restaurants: [RestaurantResponse]
}
Also note you have category
and imageUrl
fields which don't exist in the JSON response. And the categories
field in the JSON response is an array of objects.
Instead you can do:
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
}
If you decide to use CodingKeys
then you can also change your other variables: is_closed
, review_count
to camelCase.
Alternatively if all your variables in the model will be the camelCase equivalents of the snake_case keys in the JSON response you can use:
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
Upvotes: 1