Not getting all data from API - ios

let APIUrl = NSURL(string:"https://api.openweathermap.org/data/2.5/weather?lat=(currentLocation.coordinate.latitude)&lon=(currentLocation.coordinate.longitude)&appid=e7b2054dc37b1f464d912c00dd309595&units=Metric%22")
let request = URLRequest(url:APIUrl! as URL)
let dataTask = URLSession.shared.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
if (error != nil) {
print(error ?? "Error is empty.")
} else {
let httpResponse = response as? HTTPURLResponse
print(httpResponse ?? "HTTP response is empty.")
}
guard let responseData = data else {
print("Error: did not receive data")
return
}
do {
let weatherData = try JSONDecoder().decode(MyWeather.self, from: responseData)
let ggtemp = weatherData.main?.temp
print(ggtemp!, "THIS IS THE TEMP")
DispatchQueue.main.async {
self.tempDisplay.text = String(format: "%.1f", ggtemp!)
}
} catch {
print("error parsing response from POST on /todos")
return
}
})
dataTask.resume()
}
I want to get the temperature of my current Location.
Here is what I get when app runs:
<NSHTTPURLResponse: 0x28322fbe0> { URL: https://api.openweathermap.org/data/2.5/weather?lat=(currentLocation.coordinate.latitude)&lon=(currentLocation.coordinate.longitude)&appid=e7b2054dc37b1f464d912c00dd309595&units=Metric%22 } { Status Code: 400, Headers {
"Access-Control-Allow-Credentials" = (
true
);
"Access-Control-Allow-Methods" = (
"GET, POST"
);
"Access-Control-Allow-Origin" = (
"*"
);
Connection = (
"keep-alive"
);
"Content-Length" = (
78
);
"Content-Type" = (
"application/json; charset=utf-8"
);
Date = (
"Sun, 21 Oct 2018 09:57:38 GMT"
);
Server = (
openresty
);
"X-Cache-Key" = (
"/data/2.5/weather?lat=&lon=&units=Metric%22"
);
} }
error parsing response from POST on /todos
Any ideas on how to get the temp from this API?
Here is my struct code:
struct Coordinate : Decodable {
let lat, lon : Double?
}
struct Weather : Decodable {
var id : Int?
var main, myDescription, icon : String?
enum CodingKeys : String, CodingKey {
case id = "id"
case main = "main"
case icon = "icon"
case myDescription = "description"
}
}
struct Sys : Decodable {
let type, id : Int?
let sunrise, sunset : Date?
let message : Double?
let country : String?
}
struct Main : Decodable {
let temp : Double?
}
struct MyWeather : Decodable {
let coord : Coordinate?
let cod, visibility, id : Int?
let name : String?
let base : String?
let weather : [Weather]?
let sys : Sys?
let main : Main?
let dt : Date?
}

I check your api in postman get this response
{
"coord": {
"lon": 90.36,
"lat": 23.79
},
"weather": [
{
"id": 721,
"main": "Haze",
"description": "haze",
"icon": "50d"
}
],
"base": "stations",
"main": {
"temp": 304.15,
"pressure": 1013,
"humidity": 62,
"temp_min": 304.15,
"temp_max": 304.15
},
"visibility": 3000,
"wind": {
"speed": 4.1,
"deg": 330
},
"clouds": {
"all": 20
},
"dt": 1540182600,
"sys": {
"type": 1,
"id": 7879,
"message": 0.0056,
"country": "BD",
"sunrise": 1540166342,
"sunset": 1540207601
},
"id": 1337178,
"name": "Dhaka District",
"cod": 200
}
this is your full API response model class used this code
struct MyWeather : Decodable {
let coord : Coordinate?
let weather : [Weather]?
let base : String?
let main : Main?
let visibility: Int?
let wind : Wind?
let clouds : Clouds?
let dt : Date?
let sys : Sys?
let id : Int?
let name : String?
let cod : Int?
}
struct Coordinate : Decodable {
let lat : Double?
let lon : Double?
}
struct Weather : Decodable {
var id : Int?
var main, myDescription, icon : String?
enum CodingKeys : String, CodingKey {
case id = "id"
case main = "main"
case icon = "icon"
case myDescription = "description"
}
}
struct Main : Decodable {
let temp : Double?
let pressure : Int?
let humidity : Int?
let temp_min : Double?
let temp_max : Double?
}
struct Wind : Decodable {
let speed : Double?
let deg : Int?
}
struct Clouds: Decodable {
let all : Int?
}
struct Sys : Decodable {
let type : Int?
let id : Int?
let message : Double?
let country : String?
let sunrise : Date?
let sunset : Date?
}

Your code is parsing the data. However, I noticed at the end of your URL units=Metric%22 which should just be units=Metric. Also, your lat and lon will be incorrect. lat=(currentLocation.coordinate.latitude) should be lat=\(currentLocation.coordinate.latitude) and same for lon.

Related

How to get value from Optional(Optional(<__NSSingleObjectArrayI >(25)))

I am trying to get the value of "price" key which is "25"
I am getting this response Json From Backend
{
"errorCode": 0,
"message": "Request successfully served.",
"data": {
"games": {
"TWELVEBYTWENTYFOUR": {
"jackpot_amount": "KES 40,000.00",
"draw_date": "2021-05-21 10:59:45",
"extra": {
"jackpotAmount": 40000,
"unitCostJson": [
{
"currency": "KES",
"price": 25
}
]
},
}
},
"currentTime": {
"date": "2021-05-20 22:28:18.738038"
}
}
}
This is my code so far :
fetchData { (dict, error) in
let playerLoginInfo = dataDict["data"] as? NSDictionary
let playerGameInfo = playerLoginInfo?.value(forKey: "games") as? NSDictionary
if let TWELVEBYTWENTYFOUR = playerGameInfo?.value(forKey: "TWELVEBYTWENTYFOUR") as? NSDictionary {
let extra = TWELVEBYTWENTYFOUR.value(forKey: "extra") as? NSDictionary
let unitCostJson = extra?.value(forKey: "unitCostJson") as? NSArray
print("price")
print(unitCostJson?.value(forKey: "price") as? Any)
}
}
I get this is console :
Optional(Optional(<__NSSingleObjectArrayI 0x600001f091d0>(
25
)
))
I have seen this question How can I access values within Optional NSSingleObjectArrayI? but I couldn't figure out a solution
Edit:
I have now used Codeable to get data:
struct Resp: Codable {
let errorCode: Int
let message: String
let data: Dat
}
struct Dat: Codable {
let games: Games
let currentTime: CurrentTime
}
struct Games: Codable {
let game_code: String
let datetime: String
let estimated_jackpot: String
let guaranteed_jackpot: String
let jackpot_title: String
let jackpot_amount: String
let draw_date: String
let extra: Extra
let next_draw_date: String
let active: String
}
struct Extra: Codable {
let currentDrawNumber: Int
let currentDrawFreezeDate: String
let currentDrawStopTime: String
let jackpotAmount: Int
let unitCostJson: [UnitCostJson]
}
struct UnitCostJson: Codable {
let currency: String
let price: Int
}
struct CurrentTime: Codable {
let date: String
let timezone_type: Int
let timezone: String
}
I'm trying to get value from price now with this code
do{
let resp:Resp = try JSONDecoder().decode(Resp.self , from:data);
let data = resp.data
let games = data.games
let extra = games.extra
let unitCostJson = extra.unitCostJson
print(unitCostJson[0].price)
}
catch{
GlobalFunctions.shared.callOnMainThread {
self.showAlert(Message: "Something went wrong. Please retry.")
}
}
It is going into catch
How should I get the data inside on the unitCostJson now??
I butchered your struct and removed any irrelevant properties (compared to the json), if you want to add them back then you need to use an CodingKey enum
struct Resp: Codable {
let errorCode: Int
let message: String
let data: Dat
}
struct Dat: Codable {
let games: [String:Games]
let currentTime: CurrentTime
}
struct Games: Codable {
let extra: Extra
}
struct Extra: Codable {
let unitCostJson: [UnitCostJson]
}
struct UnitCostJson: Codable {
let currency: String
let price: Int
}
struct CurrentTime: Codable {
let date: String
}
Now you can access the unitCost like this
let unitCost = resp.data.games["TWELVEBYTWENTYFOUR"]?.extra.unitCostJson

Parse plain json string value inside a response swift codable

I want to parse this response. Everything else is parsed - except Images. I receive it in a string but then I cannot convert it into a dictionary. This is my model.
struct PropertyList: Decodable {
let result: Property?
let success: Bool = false
struct Property: Decodable {
let name: String?
let description: String?
let propertyType: PropertyType
let latitude, longitude: String
let images: String?
let areaSize:Int?
let threeSixtyView: String?
let threeDModel: String?
enum CodingKeys: String, CodingKey {
case name
case propertyDescription = "description"
case propertyType, latitude, longitude
case threeDModel = "threeDModel"
case images = "images"
}
}
}
struct PropertyType: Codable {
let id: Int
let name, propertyTypeDescription: String
enum CodingKeys: String, CodingKey {
case id, name
case propertyTypeDescription = "description"
}
}
API Response :
"name": "Al Deyar",
"description": "Al Deyar Villa",
"propertyType": {
"id": 709415277471,
"name": "villa",
"description": "villa"
},
"latitude": "1",
"longitude": "2",
"viewOfWater": null,
"threeDModel": "https://viewer.archilogic.com/?sceneId=80d4b3bb-98de-4279-a867-633bf67c6e72&s=m2fss0p3slst",
"images": "[{\"id\": 1, \"image\":\"https://neigborhood-images.s3.amazonaws.com/property/1BEEA0B6-A2B1-4D5E-837B-9C2B00F46EE4_2048x2048.jpg\"},\n{\"id\": 2, \"image\":\"https://neigborhood-images.s3.amazonaws.com/property/984D2D29-2448-4B68-827F-EC912AB9AF14_2048x2048.jpg\"},\n{\"id\": 3, \"image\":\"https://neigborhood-images.s3.amazonaws.com/property/EBARZA-_0002_Layer_11_2048x2048.jpg\"},\n{\"id\": 4, \"image\":\"https://neigborhood-images.s3.amazonaws.com/property/EBARZA-_0002_Layer_10_ad2d92e2-3740-4d1d-8e9c-ed41cf89c3b2_2048x2048.jpg\"},\n{\"id\": 5, \"image\":\"https://neigborhood-images.s3.amazonaws.com/property/EBARZA-furniture_0001_Layer_21_2048x2048.jpg\"},\n{\"id\": 6, \"image\":\"https://neigborhood-images.s3.amazonaws.com/property/EBARZA-furniture_0044_Layer_5_2048x2048.jpg\"},\n{\"id\": 7, \"image\":\"https://neigborhood-images.s3.amazonaws.com/property/EBARZA-furniture_0042_Layer_3_2048x2048.jpg\"}]"
> Blockquote
images is a nested JSON string which has to be decoded separately.
A solution is to declare the value containing the nested JSON string as struct (ImageJSON) with a singleValueContainer and decode the string on second level.
I left out the properties which are not in the JSON
let json = """
{
"success" : true,
"result" : {
"name": "Al Deyar",
"description": "Al Deyar Villa",
"propertyType": {
"id": 709415277471,
"name": "villa",
"description": "villa"
},
"latitude": "1",
"longitude": "2",
"viewOfWater": null,
"threeDModel": "https://viewer.archilogic.com/?sceneId=80d4b3bb-98de-4279-a867-633bf67c6e72&s=m2fss0p3slst",
"images":"[{\\"id\\": 1, \\"image\\":\\"https://neigborhood-images.s3.amazonaws.com/property/1BEEA0B6-A2B1-4D5E-837B-9C2B00F46EE4_2048x2048.jpg\\"},{\\"id\\": 2,\\"image\\":\\"https://neigborhood-images.s3.amazonaws.com/property/984D2D29-2448-4B68-827F-EC912AB9AF14_2048x2048.jpg\\"},{\\"id\\": 3,\\"image\\":\\"https://neigborhood-images.s3.amazonaws.com/property/EBARZA-_0002_Layer_11_2048x2048.jpg\\"},{\\"id\\": 4,\\"image\\":\\"https://neigborhood-images.s3.amazonaws.com/property/EBARZA-_0002_Layer_10_ad2d92e2-3740-4d1d-8e9c-ed41cf89c3b2_2048x2048.jpg\\"},{\\"id\\": 5,\\"image\\":\\"https://neigborhood-images.s3.amazonaws.com/property/EBARZA-furniture_0001_Layer_21_2048x2048.jpg\\"},{\\"id\\": 6,\\"image\\":\\"https://neigborhood-images.s3.amazonaws.com/property/EBARZA-furniture_0044_Layer_5_2048x2048.jpg\\"},{\\"id\\": 7,\\"image\\":\\"https://neigborhood-images.s3.amazonaws.com/property/EBARZA-furniture_0042_Layer_3_2048x2048.jpg\\"}]"
}
}
"""
struct Response : Decodable {
let result: Property
let success: Bool
}
struct Property: Decodable {
let name: String
let description: String
let propertyType: PropertyType
let latitude, longitude: String
let images: ImageJSON
let threeDModel: URL
}
struct PropertyType: Codable {
let id: Int
let name, description: String
}
struct Image : Decodable {
let id : Int
let image : URL
}
struct ImageJSON : Decodable {
let images : [Image]
init(from decoder : Decoder) throws {
let container = try decoder.singleValueContainer()
let imageJSONString = try container.decode(String.self)
let imageJSONData = Data(imageJSONString.utf8)
images = try JSONDecoder().decode([Image].self, from: imageJSONData)
}
}
let data = Data(json.utf8)
do {
let decoder = JSONDecoder()
let response = try decoder.decode(Response.self, from: data)
let images = response.result.images.images
print(images)
} catch {
print(error)
}
Try the below code :
Update your json :
let json = """
{
"name": "Al Deyar",
"description": "Al Deyar Villa",
"propertyType": {
"id": 709415277471,
"name": "villa",
"description": "villa"
},
"latitude": "1",
"longitude": "2",
"viewOfWater": null,
"threeDModel":"https://viewer.archilogic.com/?sceneId=80d4b3bb-98de-4279-a867-633bf67c6e72&s=m2fss0p3slst",
"images": [
{"id": 1, "image": "https://neigborhood-images.s3.amazonaws.com/property/1BEEA0B6-A2B1-4D5E-837B-9C2B00F46EE4_2048x2048.jpg"},
{"id": 2, "image": "https://neigborhood-images.s3.amazonaws.com/property/984D2D29-2448-4B68-827F-EC912AB9AF14_2048x2048.jpg"},
{"id": 3, "image": "https://neigborhood-images.s3.amazonaws.com/property/EBARZA-_0002_Layer_11_2048x2048.jpg"},
{"id": 4, "image": "https://neigborhood-images.s3.amazonaws.com/property/EBARZA-_0002_Layer_10_ad2d92e2-3740-4d1d-8e9c-ed41cf89c3b2_2048x2048.jpg"},
{"id": 5, "image": "https://neigborhood-images.s3.amazonaws.com/property/EBARZA-furniture_0001_Layer_21_2048x2048.jpg"},
{"id": 6, "image": "https://neigborhood-images.s3.amazonaws.com/property/EBARZA-furniture_0044_Layer_5_2048x2048.jpg"},
{"id": 7, "image": "https://neigborhood-images.s3.amazonaws.com/property/EBARZA-furniture_0042_Layer_3_2048x2048.jpg"}
]
}
"""
Update your codable class :
struct Property : Codable {
let descriptionField : String?
let images : [Images]?
let latitude : String?
let longitude : String?
let name : String?
let propertyType : PropertyType?
let threeDModel : String?
let viewOfWater : String?
enum CodingKeys: String, CodingKey {
case descriptionField = "description"
case images = "images"
case latitude = "latitude"
case longitude = "longitude"
case name = "name"
case propertyType = "propertyType"
case threeDModel = "threeDModel"
case viewOfWater = "viewOfWater"
}
}
struct PropertyType : Codable {
let descriptionField : String?
let id : Int?
let name : String?
enum CodingKeys: String, CodingKey {
case descriptionField = "description"
case id = "id"
case name = "name"
}
}
struct Images : Codable {
let id : Int?
let image : String?
}
if let jsonData = json.data(using: .utf8) {
let decoder = JSONDecoder()
do {
let jsonModel = try decoder.decode(Property.self, from: jsonData)
print(jsonModel)
} catch {
print("Error = \(error)")
}
}
Create an image type as below,
struct PropertyImage: Decodable {
var id: Int
var image: String?
}
Now, get data from the images string and decode an array of property images as below,
if let data = property.images.data(using: .utf8) {
do {
let images = try JSONDecoder().decode([PropertyImage].self, from: data)
images.forEach { image in
print(image.id)
print(image.image)
}
} catch {
print(error)
}
}

How to get a UIImage from a String with JSON data

I am trying to load an icon image form the API OpenWeatherMap, and display it in an ImageView. I am trying to load it into the imageView 'iconImage'. I have successfully loaded the JSON data for the location and humidity, as they are Strings, but the Icon data is also a String and I cannot get it to display as a UIImage.
Code below:
My JSON Structs below:
struct Coordinate : Decodable {
let lat, lon : Double?
}
struct Weather : Decodable {
var id : Int?
var main, myDescription, icon : String?
enum CodingKeys : String, CodingKey {
case id = "id"
case main = "main"
case icon = "icon"
case myDescription = "description"
}
}
struct Sys : Decodable {
let type, id : Int?
let sunrise, sunset : Date?
let message : Double?
let country : String?
}
struct Main : Decodable {
let temp, tempMin, tempMax : Double?
let pressure, humidity : Int?
}
struct Wind : Decodable {
let speed : Double?
let deg : Int?
}
struct MyWeather : Decodable {
let coord : Coordinate?
let cod, visibility, id : Int?
let name : String?
let base : String?
let weather : [Weather]?
let sys : Sys?
let main : Main?
let wind : Wind?
let dt : Date?
}`enter code here`
View controller below:
guard let APIUrl = URL(string: "https://api.openweathermap.org/data/2.5/weather?q=" + text + "&appid=e7b2054dc37b1f464d912c00dd309595&units=Metric") else { return }
//API KEY
URLSession.shared.dataTask(with: APIUrl) { data, response, error in
guard let data = data else { return }
let decoder = JSONDecoder()
//Decoder
do {
if (self.iconImage != nil)
{
if let gicon = weatherData.weather?.first?.icon {
DispatchQueue.main.async {
self.iconImage.image! = gicon
}
}
}
if (self.LocationLabel != nil)
{
if let gmain = weatherData.name {
print(gmain)
DispatchQueue.main.async {
self.LocationLabel.text! = "Current Weather in: " + String (gmain)
}
}
}
if (self.HumidityLabel != nil)
{
if let ghumidity = weatherData.main?.humidity
{
print(ghumidity, "THIS IS HUMIDITY")
DispatchQueue.main.async {
self.HumidityLabel.text! = String (ghumidity)
}
}
}
Use Kingfisher
It creates an extension in ImageView. You will use self.imageview.kf.setImage(with: "url")
Icon is the id of the image , you need to append it to this url and load it
http://openweathermap.org/img/w/10d.png // here ------ id = 10d
suppose you'll use SDWebImage , then do this
let urlStr = "http://openweathermap.org/img/w/\(gicon).png"
self.iconImage.sd_setImage(with: URL(string:urlStr), placeholderImage: UIImage(named: "placeholder.png"))
See here in Docs

Creating Objects from JSON data using Google Places API in Swift

So I'm creating an app that uses the Google Places API to gather a list of restaurants. Using the API provides me with a JSON file full of details about each location like lat, long, rating, priceLevel, openNow, photos, etc. Below is the code used to gather said JSON from my given parameters:
func performGoogleSearch(radius: Double, type: String, price: Int ) {
let location = locationManager.location?.coordinate
let url: URL = URL(string: "https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=\(location?.latitude ?? 0),\(location?.longitude ?? 0)&radius=\(radius * 1609.34)&type=restaurant&maxprice=\(price)&key=AIzaSyBF0uwjr6BZc-Y-0kPsMBq2zNkl5EArioQ")!
let task = URLSession.shared.dataTask(with: url) {(data, response, error) in
print(NSString(data: data!, encoding: String.Encoding.utf8.rawValue))
}
task.resume()
}
The data returned is a JSON like the following:
{
"html_attributions" : [],
"results" : [
{
"geometry" : {
"location" : {
"lat" : 37.7867167,
"lng" : -122.4111737
},
"viewport" : {
"northeast" : {
"lat" : 37.7881962302915,
"lng" : -122.4098846697085
},
"southwest" : {
"lat" : 37.7854982697085,
"lng" : -122.4125826302915
}
}
},
"icon" : "https://maps.gstatic.com/mapfiles/place_api/icons/lodging-71.png",
"id" : "3344890deedcb97b1c2d64814f92a02510ba39c8",
"name" : "Clift San Francisco",
"opening_hours" : {
"open_now" : false,
"weekday_text" : []
},
"photos" : [
{
"height" : 900,
"html_attributions" : [
"\u003ca href=\"https://maps.google.com/maps/contrib/114937580614387417622/photos\"\u003eClift San Francisco\u003c/a\u003e"
],
"photo_reference" : "CmRaAAAAwpWxfFJMJnK8G-LWJehCyUH5PFtepMF26XkZnXDRDo0wJMe-dAXLZ0zXGDmoEMi9n8YF5rYhgnr-EoDZFUawtiITYYocTJDAAjo1hw0sos4wVpfnx186o6pPgQWEv1f0EhDrydRti0bHEkhY4FNANV_KGhRmH8m7e6mO1sR2FlFxFuo5oSl00g",
"width" : 1155
}
],
"place_id" : "ChIJFUBxSY6AhYARwOaLV7TsLjw",
"price_level" : 4,
"rating" : 4.1,
"reference" : "CmRRAAAAA4IUvt3mHf2_QejiFA1acdgH2pg5h1_6GYDuVt-bzSwHqieSXmCAye5FRGJ0EjIM03WICU82MuKOiHor65j-e8rCDNEkltQnpoUX4AbCfRdybuqIPS5FxNsV_905or7BEhDNu3bKvzktrh2USu0zSNtoGhSkqf9WV1snRVufZ11kN6YgF961YQ",
"scope" : "GOOGLE",
"types" : [
"night_club",
"bar",
"lodging",
"restaurant",
"food",
"point_of_interest",
"establishment"
],
"vicinity" : "495 Geary Street, San Francisco"
},
{
"geometry" : {
"location" : {
"lat" : 37.78988329999999,
"lng" : -122.4091511
},
"viewport" : {
"northeast" : {
"lat" : 37.79135163029149,
"lng" : -122.4078268197085
},
"southwest" : {
"lat" : 37.78865366970849,
"lng" : -122.4105247802915
}
}
},
"icon" : "https://maps.gstatic.com/mapfiles/place_api/icons/lodging-71.png",
"id" : "547ceb15210b70b8734500183410bb10c644c395",
"name" : "Cornell Hotel De France",
"opening_hours" : {
"open_now" : true,
"weekday_text" : []
},
"photos" : [
{
"height" : 315,
"html_attributions" : [
"\u003ca href=\"https://maps.google.com/maps/contrib/114711934191765864568/photos\"\u003eCornell Hotel De France\u003c/a\u003e"
],
"photo_reference" : "CmRaAAAAJ3kTtFbeGT-8NWKbf9TPlN6gL6daO5zKq9DNZnzShZ-CcPUJnxMaVZybHZ0sGefM72WV01VcXr1AJWNKOSifZ63DIxxutKJ0ecqPUkM73LZLM-LO_eqsaWBRH8QN6PLYEhDykcPC3JAyDEDWpdiu3FP8GhRiJrTRNpnYQi1DDztzCRVKAM4N_A",
"width" : 851
}
],
"place_id" : "ChIJs6F3JYyAhYARDiVdBrmivCs",
"price_level" : 4,
"rating" : 4.2,
"reference" : "CmRRAAAAK05VMbTrE3cDxZreuM-Z0rbXcfdT4nflU0D17oCIwaF2RVbF85ch-1qKfRAGtMPxuuBvzw9sO-Y1rwRin-fEmzvgtiPsy8X_R2kfzh7rHX8iS8gJKc1QyTk2H4XU2O4hEhDMcIcjK5fWFvnGrJWxHgC6GhQAOkKXMCm7IjhOeOD__ZqzFlosmg",
"scope" : "GOOGLE",
"types" : [
"clothing_store",
"store",
"lodging",
"restaurant",
"food",
"point_of_interest",
"establishment"
],
"vicinity" : "715 Bush Street, San Francisco"
}
],
"status" : "OK"
}
I have an object called "Location" that hopes to take the JSON as a parameter to fill in its respective values. I would like to get to the point where I can take this JSON and turn it into an array of my "Location" struct populated with one "Location" for each restaurant returned in the Google Places API JSON.
Here is the "Location" struct:
import Foundation
struct Location: Codable {
var lat: Double
var long: Double
var icon: String?
var id: String?
var name: String
var openNow: Bool?
var photos: [String : Any]?
var placeID: String?
var priceLevel: Int?
var rating: Double?
var types: [String]?
init?(json: [String: Any]) {
guard let lat = json["lat"] as? Double,
let long = json["lng"] as? Double,
let icon = json["icon"] as? String,
let id = json["id"] as? String,
let name = json["name"] as? String,
let openNow = json["open_now"] as? Bool,
let photos = json["photos"] as? [String : Any],
let placeID = json ["place_id"] as? String,
let priceLevel = json["price_level"] as? Int,
let rating = json["rating"] as? Double,
let types = json["types"] as? [String]? else {
return nil
}
self.lat = lat
self.long = long
self.icon = icon
self.id = id
self.name = name
self.openNow = openNow
self.photos = photos
self.placeID = placeID
self.priceLevel = priceLevel
self.rating = rating
self.types = types
}
}
This struct is a start but is clearly lacking as I do not know how to go about taking the data from the JSON to make an array of this "Location" struct. Any guidance would be appreciated.
You can use JSONDecoder
let decoder = JSONDecoder()
do {
let locations = try decoder.decode([Location].self, from: yourJson)
print(locations)
} catch {
print(error.localizedDescription)
}
Also you can nest structs to represent your data which you're probably going to have to do. Have a look at "More Complex Nested Response" on this guide to JSON parsing
I eventually changed my "Location" struct to
Not be codable
Take each property as a parameter as opposed to taking the whole JSON dictionary as the parameter
In the "performGoogleSearch()" function, I essentially had to keep converting values of the dictionary into dictionaries themselves in order to reach deeper into the file. I then used each value I could get out of the JSON file and used them to create an object of my class. I then appended each object to my array of results.
func performGoogleSearch(radius: Double, type: String, price: Int ) {
let location = locationManager.location?.coordinate
let url: URL = URL(string: "https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=\(location?.latitude ?? 0),\(location?.longitude ?? 0)&radius=\(radius * 1609.34)&type=restaurant&maxprice=\(price)&key=AIzaSyBF0uwjr6BZc-Y-0kPsMBq2zNkl5EArioQ")!
let task = URLSession.shared.dataTask(with: url) {(data, response, error) in
let jsonObject = try? JSONSerialization.jsonObject(with: data! as Data, options: [])
if let jsonArray = jsonObject as? [String: Any] {
if let results = jsonArray["results"] as! [Any]?{
for result in results {
if let locationDictionary = result as? [String : Any] {
let geometry = locationDictionary["geometry"]! as! [String : Any]
let location = geometry["location"]! as! [String : Any]
let lat = location["lat"]
let long = location["lng"]
let openingHours = locationDictionary["opening_hours"] as? [String : Any]
let openNow = openingHours?["open_now"]
let photos = locationDictionary["photos"] as? [[String : Any]]
let newLocation = Location(lat: lat as! Double, long: long as! Double, id: locationDictionary["id"] as? String, name: locationDictionary["name"] as! String, openNow: openNow as? Int, photos: photos, placeID: locationDictionary["place_id"] as? String, priceLevel: locationDictionary["price_level"] as? Int, rating: locationDictionary["rating"] as? Double, types: locationDictionary["types"] as? [String])
googleResults.append(newLocation!)
}
}
print("Results: \(googleResults.count)")
let randomInt = Int(arc4random_uniform(UInt32(googleResults.count)))
print("RandomInt: \(randomInt)")
if googleResults.count != 0 {
self.googleResult = googleResults[randomInt]
self.annotation = MapBoxAnnotation(coordinate: CLLocationCoordinate2D(latitude: (self.self.googleResult?.lat)!, longitude: (self.self.googleResult?.long)!), title: self.googleResult?.name, subtitle: nil)
}
}
}
DispatchQueue.main.async {
self.animateResultView()
}
}
task.resume()
Location struct:
struct Location {
var lat: Double
var long: Double
var id: String?
var name: String
var openNow: Int?
var photos: [[String : Any]]?
var placeID: String?
var priceLevel: Int?
var rating: Double?
var types: [String]?
init?(lat: Double, long: Double, id: String?, name: String, openNow: Int?, photos: [[String : Any]]?, placeID: String?, priceLevel: Int?, rating: Double?, types: [String]?) {
self.lat = lat
self.long = long
self.id = id
self.name = name
self.openNow = openNow
self.photos = photos
self.placeID = placeID
self.priceLevel = priceLevel
self.rating = rating
self.types = types
}
}

How to create a function to show reviews in a view of my application (Swift-JSON)

I'm following https://developers.google.com/places/web-service/details this doc to add all the information about a place, and for example to add "geometry"
"geometry" : {
"location" : {
"lat" : -33.866651,
"lng" : 151.195827
},
i created this function in my class that work well
private let geometryKey = "geometry"
private let locationKey = "location"
private let latitudeKey = "lat"
private let longitudeKey = "lng"
class EClass: NSObject {
var location: CLLocationCoordinate2D?
init(placeInfo:[String: Any]) {
placeId = placeInfo["place_id"] as! String
// coordinates
if let g = placeInfo[geometryKey] as? [String:Any] {
if let l = g[locationKey] as? [String:Double] {
if let lat = l[latitudeKey], let lng = l[longitudeKey] {
location = CLLocationCoordinate2D.init(latitude: lat, longitude: lng)
}
}
}
}
but but i'm having difficulty adding "reviews"
"reviews" : [
{
"author_name" : "Robert Ardill",
"author_url" : "https://www.google.com/maps/contrib/106422854611155436041/reviews",
"language" : "en",
"profile_photo_url" : "https://lh3.googleusercontent.com/-T47KxWuAoJU/AAAAAAAAAAI/AAAAAAAAAZo/BDmyI12BZAs/s128-c0x00000000-cc-rp-mo-ba1/photo.jpg",
"rating" : 5,
"relative_time_description" : "a month ago",
"text" : "Awesome offices. Great facilities, location and views. Staff are great hosts",
"time" : 1491144016
}
],
i tried to follow the same concept of the function i created for geometry like this
if let t = place.details?["reviews"] as? [String:Any] {
if let n = t["author_name"], let m = t["text"] {
Mylabel.text = "\(t)"
}
but is not working, i also tried to add a breakpoint and only the first line enters. What can i do? How can i create a build to show the review with a label or anything i need?
Take advantage of Codable in Swift 4. You can simply convert your JSON into a specific struct. e.g. Based on your JSON:
let json = """
{
"reviews" : [
{
"author_name" : "Robert Ardill",
"author_url" : "https://www.google.com/maps/contrib/106422854611155436041/reviews",
"language" : "en",
"profile_photo_url" : "https://lh3.googleusercontent.com/-T47KxWuAoJU/AAAAAAAAAAI/AAAAAAAAAZo/BDmyI12BZAs/s128-c0x00000000-cc-rp-mo-ba1/photo.jpg",
"rating" : 5,
"relative_time_description" : "a month ago",
"text" : "Awesome offices. Great facilities, location and views. Staff are great hosts",
"time" : 1491144016
}
]
}
"""
You can convert it into a Response struct using the following code:
struct Response: Codable {
struct Review: Codable, CustomStringConvertible {
let text: String
let authorName: String
var description: String {
return "Review text: \(text) authorName: \(authorName)"
}
enum CodingKeys: String, CodingKey {
case text
case authorName = "author_name"
}
}
let reviews: [Review]
}
do {
if let data = json.data(using: .utf8) {
let decoder = JSONDecoder()
let decoded = try decoder.decode(Response.self, from: data)
print(decoded.reviews)
} else {
print("data is not available")
}
} catch (let e) {
print(e)
}
In your code t is not a Dictionary it is an Array instead. So try doing something like this. Rest of that you can change as per your logic.
if let t = place.details?["reviews"] as? [String:Any] {
for dic in t {
if let n = dic["author_name"], let m = dic["text"] {
Mylabel.text = "\(t)"
}
}
}
Yeah, but You can also try to make it like that:
struct reviews: Codable{
var reviews: [review]?
}
struct review: Codable{
var author_name: String?
var author_url: String?
var language: String?
var profile_photo_url: String?
var rating: Int?
var relative_time_description: String?
var text: String?
var time: Int?
}
And then:
if let dict = place.details?["reviews"] as? [String: Any],
let dataToDecode = dict.data(using: .utf8){
do{
let decodedReviews = try JSONDecoder().decode(reviews.self, from: dataToDecode)
// here you have decoded reviews in array
}catch let err{
print(err)
}
}

Resources