Codable/ Decodable is not decoding - ios

Can someone help me out here? I am using Playground for this example, so you can put the whole code into your Playground and see the results.
I found out that when I remove this line:
"address_format": "{{recipient}}\n{{street}}\n{{postalcode}} {{city}}\n{{country}}",
it seems to work.
(The JSON is validated)
import UIKit
struct Country : Decodable {
enum CodingKeys: String, CodingKey {
case continent
case alpha2
case name
}
var name : String?
var continent : String?
var alpha2 : String?
}
let json = """
[
{
"continent": "Europe",
"alpha2": "AD",
"alpha3": "AND",
"country_code": "376",
"currency": "EUR",
"international_prefix": "00",
"ioc": "AND",
"latitude": "42 30 N",
"longitude": "1 30 E",
"name": "Andorra",
"names": [
"Andorre",
"Andorra",
"アンドラ"],
"translations": {
"en": "Andorre",
"it": "Andorra",
"de": "Andorra",
"fr": "Andorre",
"es": null,
"ja": "アンドラ",
"nl": "Andorra",
"ru": "Андорра"},
"national_destination_code_lengths": [2],
"national_number_lengths": [6,7,8,9],
"national_prefix": "None",
"number": "020",
"region": "Europe",
"subregion": "Southern Europe",
"un_locode": "AD",
"languages": ["ca"],
"nationality": "Andorran"},
{
"continent": "Asia",
"address_format": "{{recipient}}\n{{street}}\n{{postalcode}} {{city}}\n{{country}}",
"alpha2": "AE",
"alpha3": "ARE",
"country_code": "971",
"currency": "AED",
"international_prefix": "00",
"ioc": "UAE",
"latitude": "24 00 N",
"longitude": "54 00 E",
"name": "United Arab Emirates",
"names": [
"United Arab Emirates",
"Vereinigte Arabische Emirate",
"Émirats Arabes Unis",
"Emiratos Árabes Unidos",
"アラブ首長国連邦",
"Verenigde Arabische Emiraten"],
"translations": {
"en": "United Arab Emirates",
"it": "Emirati Arabi Uniti",
"de": "Vereinigte Arabische Emirate",
"fr": "Émirats Arabes Unis",
"es": "Emiratos Árabes Unidos",
"ja": "アラブ首長国連邦",
"nl": "Verenigde Arabische Emiraten",
"ru": "Объединенные Арабские Эмираты"},
"national_destination_code_lengths": [2],
"national_number_lengths": [7,8,9],
"national_prefix": "0",
"number": "784",
"region": "Asia",
"subregion": "Western Asia",
"un_locode": "AE",
"languages": ["ar"],
"nationality": "Emirian"}
]
""".data(using: .utf8)!
let decoder = JSONDecoder()
do {
let countries = try decoder.decode([Country].self, from: json)
print(countries)
} catch {
print("erro")
}

If you would print the actually error information
"Unescaped control character around character 722."
rather than meaningless literal string "erro"
the solution is obvious:
The linefeed characters \n must be escaped:
"address_format": "{{recipient}}\\n{{street}}\\n{{postalcode}} {{city}}\\n{{country}}",
You can fix the issue with
let json = """
[
...
]
""".replacingOccurrences(of: "}\n{", with: "}\\n{").data(using: .utf8)!

Related

Creating a Data Model for nested JSON parameters

I am working with the Spotify API and having trouble with the data model due to the JSON data being more complex than any other data I've seen via tutorials or courses. How would I make my struct for the "items" in this JSON data? I understand a majority of the parameters, for example, "album_group": String and "available_markets": [String] but I don't understand what to do with "artists", "external_urls", and "images". Any help would be appreciated.
Below is the first "item" from the data.
Side Note When I'm creating my struct do I have to include EVERY parameter shown below for my API call to work?
{
"href": "https://api.spotify.com/v1/artists/3qiHUAX7zY4Qnjx8TNUzVx/albums?offset=0&limit=20&include_groups=album,single,compilation,appears_on&locale=en-US,en;q=0.9",
"items": [
{
"album_group": "album",
"album_type": "album",
"artists": [
{
"external_urls": {
"spotify": "https://open.spotify.com/artist/3qiHUAX7zY4Qnjx8TNUzVx"
},
"href": "https://api.spotify.com/v1/artists/3qiHUAX7zY4Qnjx8TNUzVx",
"id": "3qiHUAX7zY4Qnjx8TNUzVx",
"name": "Yeat",
"type": "artist",
"uri": "spotify:artist:3qiHUAX7zY4Qnjx8TNUzVx"
}
],
"available_markets": [
"AD",
"AE",
"AG",
"AL",
"AM",
"AO",
"AR",
"AT",
"AU",
"AZ",
"BA",
"BB",
"BD",
"BE",
"BF",
"BG",
"BH",
"BI",
"BJ",
"BN",
"BO",
"BR",
"BS",
"BT",
"BW",
"BY",
"BZ",
"CA",
"CD",
"CG",
"CH",
"CI",
"CL",
"CM",
"CO",
"CR",
"CV",
"CY",
"CZ",
"DE",
"DJ",
"DK",
"DM",
"DO",
"DZ",
"EC",
"EE",
"EG",
"ES",
"FI",
"FJ",
"FM",
"FR",
"GA",
"GB",
"GD",
"GE",
"GH",
"GM",
"GN",
"GQ",
"GR",
"GT",
"GW",
"GY",
"HK",
"HN",
"HR",
"HT",
"HU",
"ID",
"IE",
"IL",
"IN",
"IQ",
"IS",
"IT",
"JM",
"JO",
"JP",
"KE",
"KG",
"KH",
"KI",
"KM",
"KN",
"KR",
"KW",
"KZ",
"LA",
"LB",
"LC",
"LI",
"LK",
"LR",
"LS",
"LT",
"LU",
"LV",
"LY",
"MA",
"MC",
"MD",
"ME",
"MG",
"MH",
"MK",
"ML",
"MN",
"MO",
"MR",
"MT",
"MU",
"MV",
"MW",
"MX",
"MY",
"MZ",
"NA",
"NE",
"NG",
"NI",
"NL",
"NO",
"NP",
"NR",
"NZ",
"OM",
"PA",
"PE",
"PG",
"PH",
"PK",
"PL",
"PS",
"PT",
"PW",
"PY",
"QA",
"RO",
"RS",
"RU",
"RW",
"SA",
"SB",
"SC",
"SE",
"SG",
"SI",
"SK",
"SL",
"SM",
"SN",
"SR",
"ST",
"SV",
"SZ",
"TD",
"TG",
"TH",
"TJ",
"TL",
"TN",
"TO",
"TR",
"TT",
"TV",
"TW",
"TZ",
"UA",
"UG",
"US",
"UY",
"UZ",
"VC",
"VE",
"VN",
"VU",
"WS",
"XK",
"ZA",
"ZM",
"ZW"
],
"external_urls": {
"spotify": "https://open.spotify.com/album/1x55Z0fYARLdeJVjG2UESs"
},
"href": "https://api.spotify.com/v1/albums/1x55Z0fYARLdeJVjG2UESs",
"id": "1x55Z0fYARLdeJVjG2UESs",
"images": [
{
"height": 640,
"url": "https://i.scdn.co/image/ab67616d0000b273b20fdc3ee4c262693cfdf005",
"width": 640
},
{
"height": 300,
"url": "https://i.scdn.co/image/ab67616d00001e02b20fdc3ee4c262693cfdf005",
"width": 300
},
{
"height": 64,
"url": "https://i.scdn.co/image/ab67616d00004851b20fdc3ee4c262693cfdf005",
"width": 64
}
],
"name": "Up 2 Më",
"release_date": "2021-09-10",
"release_date_precision": "day",
"total_tracks": 22,
"type": "album",
"uri": "spotify:album:1x55Z0fYARLdeJVjG2UESs"
}
]
}
Good rule of thumb is, that you need a separate Decodable object for every JSON object (marked with {}) when working with such API.
You can nest Decodable structs. Just don't forget that here, ExternalUrl has to be Decodable too, otherwise you will get an error
struct SpotifyItem: Decodable {
// ...
let artists: [Artist]
let images: [SpotifyImage]
// ...
}
struct Artist: Decodable {
let external_urls: ExternalUrl
let href: String
let id: String
// ...
}
struct ExternalUrl: Decodable {
let spotify: String
}
struct SpotifyImage: Decodable {
let url: String
let height: Int
let width: Int
}
Side Note Answer: I would advise you to do so. It seems like there are ways to decode JSON elements into swift dictionary, but it goes against the type safety that you are trying to achieve. You are parsing JSON into Decodable structs precisely because you want to know ahead of time what are you dealing with.
Don't forget you can still do optional attributes, for example you could have
struct ExternalUrl: Decodable {
let spotify: String
let instagram: String?
}
In that case, if instagram is not present, it will be set to nil. My advice would be follow this more modern and safe way of doing things. You can of course parse the entire JSON into a huge [String: Any] object, but you will have to write out everything you want to access anyway. Plus there will be a LOT of type casting and checks, which Codable does for you.

Importing JSON or CSV data to Firestore using Swift

It appears there is still no way to import JSON or CSV files directly to a Firestore database. Many of the suggestions are for JavaScript-based apps that do not translate well to Swift. Does anyone have a good Swift solution for adding data to a Firestore database using JSON/CSV?
//example json
[
{
"name": "Stone Cove Marina Inc",
"email": "NOT IN SAMPLE",
"category": "Docks",
"category2": "Marinas",
"category3": "Dock Builders",
"address": "134 Salt Pond Rd",
"city": "Wakefield",
"state": "RI",
"zip": 2879,
"phone": "(401) 783-8990",
"website": "http://stonecovemarinari.com"
},
{
"name": "Bluehaven Homes",
"email": "NOT IN SAMPLE",
"category": "General Contractors",
"category2": "Home Builders",
"category3": "",
"address": "5701 Time Sq",
"city": "Amarillo",
"state": "TX",
"zip": 79119,
"phone": "(806) 452-2545",
"website": "http://www.bluehavenhomes.com/"
}
]
//here is the database structure
//collection is "businesses"; each "business" gets a document id; within each document id set the data
database.collection("businesses").document().setData(/*data here*/)
You can try
let str = """
[
{
"name": "Stone Cove Marina Inc",
"email": "NOT IN SAMPLE",
"category": "Docks",
"category2": "Marinas",
"category3": "Dock Builders",
"address": "134 Salt Pond Rd",
"city": "Wakefield",
"state": "RI",
"zip": 2879,
"phone": "(401) 783-8990",
"website": "http://stonecovemarinari.com"
},
{
"name": "Bluehaven Homes",
"email": "NOT IN SAMPLE",
"category": "General Contractors",
"category2": "Home Builders",
"category3": "",
"address": "5701 Time Sq",
"city": "Amarillo",
"state": "TX",
"zip": 79119,
"phone": "(806) 452-2545",
"website": "http://www.bluehavenhomes.com/"
}
]
"""
do {
let json = try JSONSerialization.jsonObject(with:str.data(using:.utf8)!, options: []) as! [[String: Any]]
for var i in 0...json.count - 1
{
database.collection("businesses").document().setData(json[i])
}
} catch {
print(error)
}

How to parse a json dictionary in swift 4 , each containing different keys with decodable protocol?

I have to parse this json Dictionary having different keys.
In this example only object_summary key is same while the other keys are different in all the objects.
I want to parse this using Swift 4 Decodable protocol with JSONDecoder(). Please help.
{
"car": {
"object_summary": {
"type": "consumer product",
"name": "ford",
"color": "red",
"description": "A car is a wheeled motor vehicle used for transportation."
"doors": "2",
"price": "$30000",
"milage": "100 miles"
},
"computer": {
"object_summary": {
"type": "hardware",
"name": "mac",
"color": "silver",
"description": "A computer is a device that can be instructed to carry out sequences of arithmetic or s for looms."
},
"purchase_date": "March 4, 2018",
"image": {
"url": "https://seniorsnoworlando.org/wp-content/uploads/2014/05/IMG_0009-1038x576.jpg",
"width": "50px",
"height": "50px"
}
},
"cat": {
"object_summary": {
"type": "animal",
"name": "Max",
"color": "orange",
"description": "The domestic cat carnivorous mammal."
},
"age": "2 years",
"favorite_toy": "ball",
"image": {
"url": "https://www.petful.com/wp-content/uploads/2016/06/american-shorthair-cat-750x434.jpg",
"width": "50px",
"height": "50px"
}
},
"dog": {
"object_summary": {
"type": "animal",
"name": "Jimmy",
"color": "black",
"description": "The domestic dog."
"age": "3 years",
"favorite_toy": "stuff animal",
"image": {
"url": "https://s3.amazonaws.com/cdn-origin-etr.akc.org/wp-content/uploads/2017/11/13000934/Beagle-On-White-08.jpg",
"width": "50px",
"height": "50px"
}
}
}
My data model looks like this:
struct DataModel: Codable{
let objectsummary:ObjectSummary
let doors,price, milage, purchasedate, age, favoritetoy: String
private enum CodingKeys: String, CodingKey {case purchasedate = "purchase_date", favoritetoy = "favorite_toy",objectsummary = "object_summary", doors, price, milage,age}
}
struct ObjectSummary:Codable{
let type: String
let name: String
let color: String
let description: String
}
1: You can construct the structs like you have with ObjectSummary (struct Car, struct Computer, etc..).
2: Have a 'root' struct that will include those as fields.
3: Finally, send that 'root' struct to the decoder.

Argument type 'String' does not conform to expect type 'Sequence'

I am developing a app for ios and I am trying to get each letter of a member of dictionary which I have defined like this :
var Morse = ["a": "01", "b": "1000", "c": "1010", "d": "100", "e": "0", "f": "0010", "g": "110", "h": "0000", "i": "00", "j": "0111", "k": "101", "l": "0100", "m": "11", "n": "10", "o": "111", "p": "0110", "q": "1101", "r": "010", "s": "000", "t": "1", "u": "001", "v": "0001", "w": "011", "x": "1001", "y": "1011", "z": "1100", "1": "01111", "2": "00111", "3": "00011", "4": "00001", "5": "00000", "6": "10000", "7": "11000", "8": "11100", "9": "11110", "0": "11111", " ": "2"]
So for example, if the user enters a I would like to get "0" and then "1". To do this I use a counter :
var counter = 0
var letter: String = ""
var strings_letter: String = ""
letter = Morse[strings_letter]!
var number = Array(letter)[counter]
But this gives me an issue :
Argument type 'String' does not conform to expect type 'Sequence'
What am I doing wrong?
The characters property of a String instance contains a sequence of the characters contained in the String. You could, for a given key (say "a") re-map the .characters of the corresponding value ("01") to single-character String instances to obtain a String array:
if let charsForKeyA = Morse["a"]?.characters.map({ String($0) }) {
charsForKeyA.forEach { print($0) }
} /* 0
1 */
If I got it right, you want to get an array of characters for the value of the inserted key, based on your example, the output should be like:
"a" => ["0", "1"]
"b" => ["1", "0", "0", "0"]
"c" => ["1", "0", "1", "0"]
and so on...
var Morse = ["a": "01", "b": "1000", "c": "1010", "d": "100", "e": "0", "f": "0010", "g": "110", "h": "0000", "i": "00", "j": "0111", "k": "101", "l": "0100", "m": "11", "n": "10", "o": "111", "p": "0110", "q": "1101", "r": "010", "s": "000", "t": "1", "u": "001", "v": "0001", "w": "011", "x": "1001", "y": "1011", "z": "1100", "1": "01111", "2": "00111", "3": "00011", "4": "00001", "5": "00000", "6": "10000", "7": "11000", "8": "11100", "9": "11110", "0": "11111", " ": "2"]
let insertedKey = "a"
if let value = Morse[insertedKey] {
let array = Array(value.characters)
// here is your array!
print(array) // ["0", "1"]
}

How to append value to Multilevel Dictionary in Swift?

I have dictionary in swift
var data = ["GenInfo":Dictionary<String,String>(),"LangInfo":Array<String>(),"EduInfo":Array<Dictionary<String,String>>(),"JobInfo":Array<Dictionary<String,String>>(),"SkillInfo":Array<Dictionary<String,String>>()]
Now I want to add values to this dictionary, How can I do that.
Suppose if I want to add these
"FirstName": "Varun",
"Email": "varun#gmail.com",
"State": "Rajasthan",
"Address": "Plot No. 00, Bhagwan Nagar 31,",
"Zip": "21354",
"Phone": "123456789",
"LastName": "Sharma"
value to the valueForKey "GenInfo"
Your where making a Dictionary (i.e. NSDictionary) whose not mutable after it's declaration (unlike NSMutableDictionary).
That said, you can either do like this :
var data : NSMutableDictionary = ["GenInfo":Dictionary<String,String>(),"LangInfo":Array<String>(),"EduInfo":Array<Dictionary<String,String>>(),"JobInfo":Array<Dictionary<String,String>>(),"SkillInfo":Array<Dictionary<String,String>>()]
data["GenInfo"] = ["FirstName": "Varun",
"Email": "varun#gmail.com",
"State": "Rajasthan",
"Address": "Plot No. 00, Bhagwan Nagar 31,",
"Zip": "21354",
"Phone": "123456789",
"LastName": "Sharma"
]
data["LangInfo"] = ["English", "French", "Italian"]
data["EduInfo"] = [["Degree": "MCA", "School": "University of Kota", "Year": "2013"], ["Degree": "Another degree", "School": "University of London", "Year": "2015"]]
// And so on...
Or like this :
var data = [String: AnyObject]()
data["GenInfo"] = [String: String]() // Dictionary<String,String>()
data["LangInfo"] = [String]() // Array<String>()
data["EduInfo"] = [[String: String]]() // Array<Dictionary<String,String>>()
data["JobInfo"] = [[String: String]]() // Array<Dictionary<String,String>>()
data["SkillInfo"] = [[String: String]]() // Array<Dictionary<String,String>>()
data["GenInfo"] = ["FirstName": "Varun",
"Email": "varun#gmail.com",
"State": "Rajasthan",
"Address": "Plot No. 00, Bhagwan Nagar 31,",
"Zip": "21354",
"Phone": "123456789",
"LastName": "Sharma"
]
data["LangInfo"] = ["English", "French", "Italian"]
data["EduInfo"] = [["Degree": "MCA", "School": "University of Kota", "Year": "2013"], ["Degree": "Another degree", "School": "University of London", "Year": "2015"]]
// And so on...
Like this
var data: NSMutableDictionary = ["GenInfo":Dictionary<String,String>(),"LangInfo":Array<String>(),"EduInfo":Array<Dictionary<String,String>>(),"JobInfo":Array<Dictionary<String,String>>(),"SkillInfo":Array<Dictionary<String,String>>()]
data["GenInfo"] = ["FirstName": "Varun",
"Email": "varun#gmail.com",
"State": "Rajasthan",
"Address": "Plot No. 00, Bhagwan Nagar 31,",
"Zip": "21354",
"Phone": "123456789",
"LastName": "Sharma"
]

Resources