getting only one item from decoding json response iOS - ios

I've a response from MapBox API. I want to store each Feature object of features array in a array then want to display from that array to table view.
{
type: "FeatureCollection",
query: [
"u"
],
features: [{
id: "country.19678805456372290",
type: "Feature",
place_type: [
"country"
],
relevance: 1,
properties: {
wikidata: "Q30",
short_code: "us"
},
text: "United States",
place_name: "United States",
matching_text: "US",
matching_place_name: "US",
bbox: [
-179.9,
18.8163608007951,
-66.8847646185949,
71.4202919997506
],
center: [
-97.9222112121185,
39.3812661305678
],
geometry: {
type: "Point",
coordinates: [
-97.9222112121185,
39.3812661305678
]
}
},
{
id: "country.12405201072814600",
type: "Feature",
place_type: [
"country"
],
relevance: 1,
properties: {
wikidata: "Q145",
short_code: "gb"
},
text: "United Kingdom",
place_name: "United Kingdom",
bbox: [
-8.74974065661991,
49.802416901086,
1.86276379960989,
60.9093517989553
],
center: [
-2.36966957036279,
54.2379333607472
],
geometry: {
type: "Point",
coordinates: [
-2.36966957036279,
54.2379333607472
]
}
},
{
id: "country.11702916565595680",
type: "Feature",
place_type: [
"country"
],
relevance: 1,
properties: {
wikidata: "Q878",
short_code: "ae"
},
text: "United Arab Emirates",
place_name: "United Arab Emirates",
bbox: [
51.4160146192147,
22.6282410017159,
56.4814183948386,
26.094609499407
],
center: [
54.2561723713588,
23.8520599823879
],
geometry: {
type: "Point",
coordinates: [
54.2561723713588,
23.8520599823879
]
}
},
{
id: "country.11871993712476590",
type: "Feature",
place_type: [
"country"
],
relevance: 1,
properties: {
wikidata: "Q212",
short_code: "ua"
},
text: "Ukraine",
place_name: "Ukraine",
bbox: [
22.1375690033684,
44.3152913001972,
40.2255909904191,
52.3793529890401
],
center: [
31.3202829593814,
49.3227937844972
],
geometry: {
type: "Point",
coordinates: [
31.3202829593814,
49.3227937844972
]
}
},
{
id: "country.9140805109496660",
type: "Feature",
place_type: [
"country"
],
relevance: 1,
properties: {
wikidata: "Q77",
short_code: "uy"
},
text: "Uruguay",
place_name: "Uruguay",
matching_text: "URY",
matching_place_name: "URY",
bbox: [
-58.4891219951353,
-35.0552819973662,
-53.1014000735687,
-30.0855740544354
],
center: [
-56.012396238658,
-32.7996455126793
],
geometry: {
type: "Point",
coordinates: [
-56.012396238658,
-32.7996455126793
]
}
}
],
attribution: "NOTICE: © 2021 Mapbox and its suppliers. All rights reserved. Use of this data is subject to the Mapbox Terms of Service (https://www.mapbox.com/about/maps/). This response and the information it contains may not be retained. POI(s) provided by Foursquare."
}
So, I create a model like this -
struct Response: Codable {
var features: [Feature]
}
struct Feature: Codable {
var id: String!
var type: String?
var matching_place_name: String?
var place_name: String?
var geometry: Geometry
var center: [Double]
var properties: Properties
}
struct Geometry: Codable {
var type: String?
var coordinates: [Double]
}
struct Properties: Codable {
var address: String?
}
And Write this code -
var suggestedPlacenames: NSMutableArray = []
func doShowSuggestion(usersQuery: String) {
let urlString = "\(mapbox_api)\(usersQuery).json?access_token=\(mapbox_access_token)"
let url = URL(string: urlString)
let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in
guard let data = data, error == nil else {
print("Error in URLSeesion")
return
}
if let result = try? JSONDecoder().decode(Response.self, from: data) {
self.suggestedPlacenames.add(result)
print(self.suggestedPlacenames.count)
} else {}
But by this I always get 1 length from self.suggestedPlacenames whatever the length of the response in features. How can I get each features object separately in self.suggestedPlacenames?

In your code :
self.suggestedPlacenames.add(result)
You add only one element to your array.
You must append the an array.
// declare as an array of features
var suggestedPlacenames = [Feature]()
self.suggestedPlacenames.append(contentsOf: result.features)
or
self.suggestedPlacenames += result.features

You need to delcare
var allFeatures = [Feature]()
do {
let result = try JSONDecoder().decode(Response.self, from: data)
self.allFeatures = result.features
print(self.allFeatures.count)
}
catch {
print(error)
}
Then

Related

How to create a struct model from a complex JSON response

I am trying to consume data from an api in swift, json data has successfully been delivered back to the app but the json response from my backend is very complex hence forming struct for my model is very difficult. I'm able to only retrieve the simple strings but if I add the objects and arrays everything stops working
[
{
"type": "movie",
"id": "ffff-ddd968-4cf0-a939-8skeu",
"title": "Sorority Row",
"description": "When five laldkdk",
"categories": [],
"genres": [
"Mystery",
"Horror"
],
"links": {
"amazonPrime": [
{
"link": "somelink",
"format": "native_ios"
},
{
"link": "somelink",
"format": "native_ios"
}
],
"appleTvPlus": [],
"disneyPlus": []
"iTunes": [
{
"link": "www.somelink",
"format": "webview_computer"
}
],
"netflix": [],
"youTubePremium": []
},
"promoted": false,
"certification": "18",
"releaseDate": "2009-09-09",
"runTime": 101,
"userRating": null,
"inUserList": false,
"packShot": {
"thumbnail": "imageurl"
},
"backdrop": {
"thumbnail": "imageurl"
}
}
]
struct Responder: Codable {
let type: String
let id: String
let description: String
let title: String
let promoted: Bool
let certification: String
let firstAirDate: String
let lastAirDate: String
let numberEpisodes: Int
let numberSeasons: Int
let userRating: Int?
let inUserList: Bool
let thumbnail: PackShotObj
let amazonPrime: linksObj
}
struct PackShotObj: Codable {
let packShot: [String]
}
struct linksObj: Codable {
let link: String
let format: String
}
struct genres: Codable {
let empty: String
}
Here is the code that works, decoding your json data. Note the differences between my struct models and yours. You will need to consult the docs of the server to determine which fields are optionals and adjust the code for that:
struct ContentView: View {
#State var responders: [Responder] = []
var body: some View {
List(responders) { responder in
Text(responder.title)
Text(responder.description)
Text(responder.releaseDate)
}
.onAppear {
let json = """
[
{
"type": "movie",
"id": "ffff-ddd968-4cf0-a939-8skeu",
"title": "Sorority Row",
"description": "When five laldkdk",
"categories": [],
"genres": [
"Mystery",
"Horror"
],
"links": {
"amazonPrime": [
{
"link": "somelink",
"format": "native_ios"
},
{
"link": "somelink",
"format": "native_ios"
}
],
"appleTvPlus": [],
"disneyPlus": [],
"iTunes": [
{
"link": "www.somelink",
"format": "webview_computer"
}
],
"netflix": [],
"youTubePremium": []
},
"promoted": false,
"certification": "18",
"releaseDate": "2009-09-09",
"runTime": 101,
"userRating": null,
"inUserList": false,
"packShot": {
"thumbnail": "imageurl"
},
"backdrop": {
"thumbnail": "imageurl"
}
}
]
"""
// simulated API data
let data = json.data(using: .utf8)!
do {
self.responders = try JSONDecoder().decode([Responder].self, from: data)
print("\n---> responders: \n \(responders)\n")
} catch {
print("\n---> decoding error: \n \(error)\n")
}
}
}
}
// MARK: - Responder
struct Responder: Identifiable, Codable {
let type, id, title, description: String
let categories: [String]
let genres: [String]
let links: Links
let promoted: Bool
let certification, releaseDate: String
let runTime: Int
let userRating: Int?
let inUserList: Bool
let packShot, backdrop: Backdrop
}
// MARK: - Backdrop
struct Backdrop: Codable {
let thumbnail: String
}
// MARK: - Links
struct Links: Codable {
let amazonPrime: [Provider]
let appleTvPlus: [Provider]
let disneyPlus: [Provider]
let iTunes: [Provider]
let netflix: [Provider]
let youTubePremium: [Provider]
}
struct Provider: Codable {
let link, format: String
}
Just copy and paste this model to file and you are good to go.
struct Responder: Codable {
let type, id, title, welcomeDescription: String
let categories: [String]
let genres: [String]
let links: Links
let promoted: Bool
let certification, releaseDate: String
let runTime: Int
let userRating: Any?
let inUserList: Bool
let packShot, backdrop: Backdrop
enum CodingKeys: String, CodingKey {
case type, id, title
case welcomeDescription = "description"
case categories, genres, links, promoted, certification, releaseDate, runTime, userRating, inUserList, packShot, backdrop
}
}
// MARK: - Backdrop
struct Backdrop: Codable {
let thumbnail: String
}
// MARK: - Links
struct Links: Codable {
let amazonPrime: [AmazonPrime]
let appleTvPlus, disneyPlus: [String]
let iTunes: [AmazonPrime]
let netflix, youTubePremium: [String]
}
// MARK: - AmazonPrime
struct AmazonPrime: Codable {
let link, format: String
}

Realm Swift : List of objects not working

I try to connect Realm to my recipe application in Swift. I have a problem about the ingredients : when I run my app, I have this error coming
There must be a primary key property named '_id' on a synchronized Realm but none was found for type 'recipe_ingredients'
I assume it is because I typed my ingredient object as a Realm Object and it wants a primary key. I tried to add one, but then it says
Primary Key for class 'recipe_ingredients' has been added.
Here is my model file :
import RealmSwift
import Foundation
class recipe: Object {
#Persisted(primaryKey: true) var _id: ObjectId?
#Persisted var name: String?
#Persisted var category: String?
#Persisted var image: String?
#Persisted var note: Double?
#Persisted var likes: Int?
#Persisted var ingredients: List<recipe_ingredients>
#Persisted var steps: List<recipe_steps>
#Persisted var time: String?
#Persisted var units: Int?
convenience init(name: String) {
self.init()
self.name = name
}
}
class recipe_ingredients: Object{
#Persisted(primaryKey: true) var _id: ObjectId?
#Persisted var name: String?
#Persisted var quantity: String?
}
class recipe_steps: Object{
#Persisted(primaryKey: true) var _id: ObjectId?
#Persisted var step_description: String?
#Persisted var step: Int?
}
And here is the Realm Schema
{
"title": "recipe",
"properties": {
"__v": {
"bsonType": "int"
},
"_id": {
"bsonType": "objectId"
},
"category": {
"bsonType": "string"
},
"image": {
"bsonType": "string"
},
"ingredients": {
"bsonType": "array",
"items": {
"bsonType": "object",
"properties": {
"name": {
"bsonType": "string"
},
"quantity": {
"bsonType": "string"
}
}
}
},
"likes": {
"bsonType": "int"
},
"name": {
"bsonType": "string"
},
"note": {
"bsonType": "double"
},
"steps": {
"bsonType": "array",
"items": {
"bsonType": "object",
"properties": {
"description": {
"bsonType": "string"
},
"step": {
"bsonType": "int"
}
}
}
},
"time": {
"bsonType": "string"
},
"units": {
"bsonType": "int"
}
}
}
Thanks for your help !

How do i parse custom JSON Data

Here i provide the code worked sample as per the guideline given below and seems to get null values.
Here is my complete JSON Data,
some: {
"success": true,
"data":
[
{
"15-10-2020": [
{
"id": 100,
"details": {
"_id": 1,
"_title": "My Title"
},
"created_at": "2020-10-15"
},
{
"snf_id": 101,
"details": {
"_id": 1,
"_title": "My Title"
},
"created_at": "2020-10-15"
},
{
"snf_id": 102,
"details": {
"_id": 1,
"_title": "My Title"
},
"created_at": "2020-10-15"
}
],
"30-09-2020": [
{
"snf_id": 301,
"details": {
"_id": 8,
"_title": "My Title"
},
"created_at": "2020-09-30"
}
]
}
],
"message": "Successfully Retrieved"
}
struct Response : Codable {
var success : Bool?
var data : [Data]?
var message : String?
}
struct Data: Codable {
var snf_id: Int?
var details: Details?
var created_at: String?
}
// MARK: - Details
struct Details: Codable {
var _id: Int?
var _title: String?
}
let Response = try JSONDecoder().decode(Response.self, from: data)
Returns null value for data,
▿ Response
▿ success : Optional
- some : true
▿ data : Optional<Array>
▿ some : 1 element
▿ 0 : Data
- snf_id : nil
- details : nil
- created_at : nil
▿ message : Optional
- some : "Successfully Retrieved"
There is no myData key in your json , your json top structure is an array that contains elements where every element value is an array like [[String: [Root]]]
struct Root: Codable {
let id: Int?
let details: Details
let createdAt: String
let snfID: Int?
enum CodingKeys: String, CodingKey {
case id, details
case createdAt = "created_at"
case snfID = "snf_id"
}
}
// MARK: - Details
struct Details: Codable {
let id: Int
let title: String
enum CodingKeys: String, CodingKey {
case id = "_id"
case title = "_title"
}
}
And to decode
let res = try JSONDecoder().decode([[String: [Root]]].self,from:data)
some: {
"success": true,
"data":
[
{
"15-10-2020": [
{
"id": 100,
"details": {
"_id": 1,
"_title": "My Title"
},
"created_at": "2020-10-15"
},
{
"snf_id": 101,
"details": {
"_id": 1,
"_title": "My Title"
},
"created_at": "2020-10-15"
},
{
"snf_id": 102,
"details": {
"_id": 1,
"_title": "My Title"
},
"created_at": "2020-10-15"
}
],
"30-09-2020": [
{
"snf_id": 301,
"details": {
"_id": 8,
"_title": "My Title"
},
"created_at": "2020-09-30"
}
]
}
],
"message": "Successfully Retrieved"
}
struct Response : Codable {
var success : Bool?
**var data : [[String:[Data]]]?**
var message : String?
}
struct Data: Codable {
var snf_id: Int?
var details: Details?
var created_at: String?
}
// MARK: - Details
struct Details: Codable {
var _id: Int?
var _title: String?
}
let Response = try JSONDecoder().decode(Response.self, from: data)

Swift Nested jSON Decode

I have problem with nested json decoding. Im getting no error but response is empty { }. Down bellow is my sample json and struct.
{
"categories": [
{
"ID": 130,
"data": [
{
"en": [
{
"title": "test"
}
],
"fr": [
{
"title": "teste"
}
]
}
],
"lifts": [
{
"ID": 104,
"data": [
{
"en": [
{
"code": "test",
"title": "test"
}
],
"fr": [
{
"code": "test",
"title": "test"
}
]
}
]
},
{
"ID": 105,
"data": [
{
"en": [
{
"code": "test",
"title": "test"
}
],
"fr": [
{
"code": "test",
"title": "test"
}
]
}
]
}
]
}
And this is my struct
struct jsonResponse : Codable {
struct Categories : Codable {
let id : Int
let data : [LanguageData]
let lifts : [Lifts]
struct LanguageData : Codable {
let en, fr : [Data]
struct Data : Codable {
let code : String?
let title : String?
}
}
struct LiftsData : Codable {
let id : Int
let data : [LanguageData]
}
}
Then Im trying to decode JSON like this:
let lifts = try JSONDecoder().decode(jsonResponse.self, from: data)
But when I print lifts, i see only empty {}. Also no error message during decoding, so have no idea what can be wrong.

get position in a api swift Codable and Model data

i am getting data from an api like this
[
{
"internData": {
"id": "abc123",
"name": "Doctor"
},
"author": "Will smith",
"description": "Is an actor",
"url": "https://www",
},
{
"internData": {
"id": "qwe900",
"name": "Constructor"
},
"author": "Edd Bett",
"description": "Is an Constructor",
"url": "https://www3",
}
]
I have my model like this
struct PersonData: Codable {
let author: String?
let description: String?
let url: String?
}
But I dont know how to define the "internData", I tried with another Model "InterData" and define id and name like the PersonData, but i get an error, i tried also with [String:Any] but i get an error for the Codable protocol
I am using
let resP = try JSONSerialization.jsonObject(with: data, options: .init()) as? [String: AnyObject]
print("resP", )
in my script of Service/Network
Thanks if somebody knows
You can't use [String:Any] type in case of Codable. you need to create an another model of InternData, which is used by PersonData.
Code:
JSON Data :
let jsonData =
"""
[
{
"internData": {
"id": "abc123",
"name": "Doctor"
},
"author": "Will smith",
"description": "Is an actor",
"url": "https://www",
},
{
"internData": {
"id": "qwe900",
"name": "Constructor"
},
"author": "Edd Bett",
"description": "Is an Constructor",
"url": "https://www3",
}
]
"""
// Models
struct PersonData: Codable {
let author: String?
let description: String?
let url: String?
let internData : InternData?
}
// New model
struct InternData : Codable {
let id : String?
let name : String?
}
// Parsing
do {
let parseRes = try JSONDecoder().decode([PersonData].self, from: Data(jsonData.utf8))
print(parseRes)
}
catch {
print(error)
}

Resources