i have 3 objects (nationalities, services and packages) and i got them from a JSON response as 3 arrays of these objects.. and i want to parse them from the JSON response ...
Classes are:
struct Root2 : Decodable {
let nationalities : [Nationalities]
let services : [Services]
let packages : [Packages]
}
struct Packages : Decodable {
let id: Int
let name: String
let idService: Int
let nationality: String
let totalPrice: Int
let visitsPerWeek: Int
let excludedDays: String
let excludedShifts: String
let excludedDates: String
let extraVisits: Int
let dateEnabled: String
let dateDisabled: String
let allowedFrom: String
let allowedTo: String
let visitType: String
let createdAt: String?
let updatedAt: String?
}
struct Nationalities : Decodable{
let id: Int
let name: String
let createdAt: String?
let updatedAt: String?
}
struct Services : Decodable{
let id: Int
let name, description: String
let createdAt: String?
let updatedAt: String?
}
and i have for each one another class like:
class Service : NSObject, NSCoding {
var id: Int
var name, desc: String
var createdAt: String?
var updatedAt: String?
init(id: Int, name: String, desc: String) {
self.id = id
self.name = name
self.desc = desc
}
required convenience init(coder aDecoder: NSCoder) {
let id = aDecoder.decodeInteger(forKey: "id")
let name = aDecoder.decodeObject(forKey: "name") as! String
let desc = aDecoder.decodeObject(forKey: "desc") as! String
self.init(id: id, name: name, desc: desc)
}
func encode(with aCoder: NSCoder) {
aCoder.encode(id, forKey: "id")
aCoder.encode(name, forKey: "name")
aCoder.encode(desc, forKey: "desc")
}
}
and i'm using it like this:
func GetServicesPackagesNationalities(){
let link: String = "my link"
guard let url = URL(string: link) else {
print("Error: cannot create URL")
return
}
let urlRequest = URLRequest(url: url)
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
let task = session.dataTask(with: urlRequest) {
(data, response, error) in
guard error == nil else {
print("error calling GET on /public/api/services")
print(error!)
return
}
guard let responseData = data else {
print("Error: did not receive data")
return
}
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let result = try decoder .decode(Root2.self, from: responseData)
var nationalities = [Nationality]()
for nationality in result.nationalities{
let newnationality = Nationality(id: nationality.id, name: nationality.name)
nationalities.append(newnationality)
print(newnationality.name)
}
var services = [Service]()
for service in result.services {
let newservice = Service(id: service.id, name: service.name, desc: service.description)
services.append(newservice)
print(newservice.name)
}
var packages = [Package]()
for package in result.packages{
let newpackage = Package(id: package.id, name: package.name, idService: package.idService, nationality: package.nationality, totalPrice: package.totalPrice, visitsPerWeek: package.visitsPerWeek, excludedDays: package.excludedDays, excludedShifts: package.excludedShifts, excludedDates: package.excludedDates, extraVisits: package.extraVisits ,dateEnabled: package.dateEnabled , dateDisabled: package.dateDisabled, allowedFrom: package.allowedFrom, allowedTo: package.allowedTo ,visitType: package.visitType)
packages.append(newpackage)
print(newpackage.name)
}
}catch {
print("error trying to convert data to JSON")
return
}
}
task.resume()
}
and i will always get this:
error trying to convert data to JSON
and my json is:
{
"nationalities": [
{
"id": 1,
"name": "Saudi Arabia",
"created_at": "2018-04-24 05:50:41",
"updated_at": "2018-04-24 06:35:29",
"deleted_at": null
},
{
"id": 2,
"name": "Bahrain",
"created_at": "2018-04-24 05:52:52",
"updated_at": "2018-04-24 05:52:52",
"deleted_at": null
},
],
"services": [
{
"id": 1,
"name": "Nad",
"description": "Nad ",
"created_at": null,
"updated_at": null,
"deleted_at": null
},
{
"id": 2,
"name": "Reay",
"description": "Re ",
"created_at": null,
"updated_at": null,
"deleted_at": null
},
],
"packages": [
{
"id": 1,
"name": "Gold Package",
"id_service": 1,
"nationality": "4",
"total_price": 1000,
"visits_per_week": 2,
"excluded_weekdays": "null",
"excluded_shifts": "null",
"excluded_dates": "1111-11-11",
"extra_visits": 1,
"date_enabled_from": "2018-04-01",
"date_enabled_to": "2018-04-30",
"date_allowed_from": "2018-04-05",
"date_allowed_to": "2018-04-30",
"visit_type": "Multi",
"created_at": "2018-04-26 11:18:45",
"updated_at": "2018-04-26 11:18:45",
"deleted_at": null
}
]
}
i don't know what i'm doing wrong .. since i tried the same with two objects and worked fine ..
can someone please tell me what im doing wrong?
using them in userdefaults:
storing:
let userDefaults = UserDefaults.standard
let encodedData: Data = NSKeyedArchiver.archivedData(withRootObject: services)
userDefaults.set(encodedData, forKey: "services")
userDefaults.synchronize()
retrieving:
let decoded = userDefaults.object(forKey: "services") as! Data
let decodedService = NSKeyedUnarchiver.unarchiveObject(with: decoded) as! [Service]
for service in decodedService {
print(service.name)
}
Please never ever print a meaningless literal string in a catch clause, print always the actual error, it tells you exactly what's wrong
} catch {
print(error)
}
In Package there are a lot of issues, here are the error messages (and the suggestions to fix them):
"No value associated with key CodingKeys(stringValue: \"excludedDays\", converted to excluded_days."
There is no key excluded_days in the JSON. Delete the property or declare it as optional (String?)
"No value associated with key CodingKeys(stringValue: \"dateEnabled\" converted to date_enabled."
The actual snake_case converted property is supposed to be dateEnabledFrom
"No value associated with key CodingKeys(stringValue: \"dateDisabled\" converted to date_disabled."
There is no key date_disabled in the JSON. Delete the property or declare it as optional (String?)
"No value associated with key CodingKeys(stringValue: \"allowedFrom\", intValue: nil) (\"allowedFrom\"), converted to allowed_from."
The actual snake_case converted property is supposed to be dateAllowedFrom
"No value associated with key CodingKeys(stringValue: \"allowedTo\", intValue: nil) (\"allowedTo\"), converted to allowed_to."
The actual snake_case converted property is supposed to be dateAllowedTo
Note:
Why do you use another extra classes for Nationality, Service, Package? They seem to be redundant. If you really need reference semantics decode the JSON into classes.
See the difference between Nationality and Nationalities
You should use Nationalities to parse your JSON response.
You should do like this, as you have declare Nationalities object in Root2
var nationalities = [Nationalities]()
for nationality in result.nationalities{
let newnationality = Nationalities(id: nationality.id, name: nationality.name)
nationalities.append(newnationality)
print(newnationality.name)
}
same for Services and Packages, Please find it.
Related
#Edit - Thanks to the do-try-catch suggestion from Finn, it turned out Json was containing some null values, which I had missed during testing in postman. After refactoring fields in UserInfoStruct to Optionals, everything works.
I'm trying to unwrap json in my application, but despite getting code 200 from server and receiving bytes of data, printing returns nil. Putting field in struct that returns string with all the information resulted with nil as well, same with printing one of the fields. I've followed tutorials like the one on hackingwithswift.com, but to no avail. Previously checking on reqres.in endpoints, everything works just fine.
Code used to fetch data from api with the build-in json decoder:
func getUserInfo() {
var userInfo: UserInfoStruct?
let token = UserDefaults.standard.string(forKey: ApiUtils().userToken) ?? ""
guard let url = URL(string: EndpointUrls().machineUrl + EndpointUrls().profileUrl) else { return }
var request = URLRequest(url: url)
request.httpMethod = HttpMethodsStruct().get
request.setValue(ApiUtils().appjson, forHTTPHeaderField: ApiUtils().contType)
request.addValue(ApiUtils().bearer + token, forHTTPHeaderField: ApiUtils().auth)
URLSession.shared.dataTask(with: request) {data, response, error in
let httpStatusCode = (response as? HTTPURLResponse)?.statusCode ?? 0
print(httpStatusCode)
if (httpStatusCode) == 200 {
if let data = data {
print(data)
userInfo = try? JSONDecoder().decode(UserInfoStruct.self, from: data)
print(userInfo)
}
}
}.resume()
}
I've replaced all the strings with variables declared in another struct, as some of them are used in the more places. With this, any changes would be easier to implement. To access the endpoints, You need to be logged in a specific vpn.
Here is the example json I'm receiving:
{
"authorities": [
{
"created_at": "2021-01-21T13:49:32.755Z",
"id": 0,
"name": "string",
"updated_at": "2021-01-21T13:49:32.755Z"
}
],
"business_name": "string",
"created_at": "2021-01-21T13:49:32.755Z",
"devices_limit": 0,
"email": "string",
"first_name": "string",
"has_active_call": true,
"id": 0,
"is_business_account": true,
"last_name": "string",
"marketing": true,
"min_to_call": 0,
"paid_to": "2021-01-21T13:49:32.755Z",
"password_reset_active_link": true,
"phone_number": "string",
"translation_types": [
{
"display_name": "string",
"id": 0,
"jabber_destination": "string",
"name": "string"
}
],
"updated_at": "2021-01-21T13:49:32.755Z"
}
Using app.quicktype.io, following structs has been generated:
import Foundation
struct UserInfoStruct: Codable {
let authorities: [Authority]
let business_name: String
let created_at: String
let devices_limit: Int
let email: String
let first_name: String
let has_active_call: Bool
let id: Int
let is_business_account: Bool
let last_name: String
let marketing: Bool
let min_to_call: Int
let paid_to: String
let password_reset_active_link: Bool
let phone_number: String
let translation_types: [TranslationType]
let updated_at: String
}
struct Authority: Codable {
let created_at: String
let id: Int
let name: String
let updated_at: String
}
struct TranslationType: Codable {
let display_name: String
let id: Int
let jabber_destination: String
let name: String
}
Still, in console all, that's printed, is this:
200 <- from print(httpStatusCode)
689 bytes <- from print(data)
nil <- from print(userInfo)
As in some tutorials, I've tried to declare userInfo field as
var userInfo = [UserInfoStruct]()
but it only resulted in
Cannot assign value of type 'UserInfoStruct?' to type '[UserInfoStruct]'
Declaring this field both in the func as well as #State in the main struct gave the same results.
About me: I'm self-taught with background in java. I've been developing in swift since October 2020, so I'm fully aware, that many problems might be trivial and my lack of experience causes me to miss them.
Thanks for all the help.
You can try catching your errors with a do-catch block instead of just using try?. This will enable you to log a error message instead if just getting a nil result.
a do-catch block is formed like this:
do {
userInfo = try JSONDecoder().decode(UserInfoStruct.self, from: data)
print("Success!")
} catch {
print("Unexpected error: \(error).")
}
I have a very long Json array that is full of items that look like this:
[
{
"id": "sm10-1",
"name": "Pheromosa & Buzzwole-GX",
"imageUrl": "https://images.pokemontcg.io/sm10/1.png",
"subtype": "TAG TEAM",
"supertype": "Pokémon",
"hp": "260",
"retreatCost": [
"Colorless",
"Colorless"
],
"convertedRetreatCost": 2,
"number": "1",
"artist": "Mitsuhiro Arita",
"rarity": "Rare Holo GX",
"series": "Sun & Moon",
"set": "Unbroken Bonds",
"setCode": "sm10",
"text": [
"When your TAG TEAM is knocked out, your opponent takes 3 Prize Cards."
],
"types": [
"Grass"
],
"attacks": [
{
"name": "Jet Punch",
"cost": [
"Grass"
],
"convertedEnergyCost": 1,
"damage": "30",
"text": "This attack does 30 damage to 1 of your opponent's Benched Pokémon. (Don't apply Weakness and Resistance for Benched Pokémon.)"
},
{
"name": "Elegant Sole",
"cost": [
"Grass",
"Grass",
"Colorless"
],
"convertedEnergyCost": 3,
"damage": "190",
"text": "During your next turn, this Pokémon's Elegant Sole attack's base damage is 60."
},
{
"name": "Beast Game-GX",
"cost": [
"Grass"
],
"convertedEnergyCost": 1,
"damage": "50",
"text": "If your opponent's Pokémon is Knocked Out by damage from this attack, take 1 more Prize card. If this Pokémon has at least 7 extra Energy attached to it (in addition to this attack's cost), take 3 more Prize cards instead. (You can't use more than 1 GX attack in a game.)"
}
],
"weaknesses": [
{
"type": "Fire",
"value": "×2"
}
],
"imageUrlHiRes": "https://images.pokemontcg.io/sm10/1_hires.png",
"nationalPokedexNumber": 794
}
]
That is just one item of hundreds in the array. What I want to do is grab specific values from each item (i.e. name, imageUrl, supertype, hp, rarity, set) and send them to a struct which will then be added to an array of such structs.
What I currently have prints just prints out all of the json data and I can not figure out how to get individual data and create an array of structs for each individual card.
Here is the code I have currently:
//[TEST] READING JSON FILE LOCALLY
struct card: Decodable {
let name: String
let imageUrl: String
let supertype: String
let artist: String
let rarity: String
let set: String
let types: Array<String>
}
func loadJsonInfo() {
do{
let data = try Data.init(contentsOf: URL.init(fileURLWithPath: Bundle.main.path(forResource: "Unbroken Bonds", ofType: "json")!))
let json = try JSONSerialization.jsonObject(with: data, options: .allowFragments)
print(json)
} catch {
print(error)
}
}
Also, the json file is locally stored in my appData. Thanks in advance for your help!
Give a try to https://quicktype.io/
You put json there. And get all necessary data structures to decode json
To decode a JSON with Decodable type, you need to use JSONDecoder's decode(_:from:) method.
Update your loadJsonInfo() method to,
func loadJsonInfo() {
if let file = Bundle.main.url(forResource: "Unbroken Bonds", withExtension: "json") {
do {
let data = try Data(contentsOf: file)
let arr = try JSONDecoder().decode([Card].self, from: data)
print(arr)
} catch {
print(error)
}
}
}
Note: Use first letter capital while creating a type, i.e. use Card instead of card.
Parsing Code when you have DATA from server. I also removed force unwrapped ! so it won't crash in absence of file
func loadJsonInfo() {
if let path = Bundle.main.path(forResource: "Unbroken Bonds", ofType: "json") {
do {
let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .alwaysMapped)
let result = try JSONDecoder().decode(ResultElement.self, from: data)
} catch let error {
print(error)
}
}
}
Your Model
import Foundation
// MARK: - ResultElement
struct ResultElement: Codable {
let id, name: String?
let imageURL: String?
let subtype, supertype, hp: String?
let retreatCost: [String]?
let convertedRetreatCost: Int?
let number, artist, rarity, series: String?
let resultSet, setCode: String?
let text, types: [String]?
let attacks: [Attack]?
let weaknesses: [Weakness]?
let imageURLHiRes: String?
let nationalPokedexNumber: Int?
enum CodingKeys: String, CodingKey {
case id, name
case imageURL = "imageUrl"
case subtype, supertype, hp, retreatCost, convertedRetreatCost, number, artist, rarity, series
case resultSet = "set"
case setCode, text, types, attacks, weaknesses
case imageURLHiRes = "imageUrlHiRes"
case nationalPokedexNumber
}
}
// MARK: - Attack
struct Attack: Codable {
let name: String?
let cost: [String]?
let convertedEnergyCost: Int?
let damage, text: String?
}
// MARK: - Weakness
struct Weakness: Codable {
let type, value: String?
}
typealias Result = [ResultElement]
I have defined the data model for packages data, but need to define UITableview sections on the basis of
subscriptiontype = 'Yearly', 'Monthly', 'Weekly'
Getting an error of - Cannot assign value of type '[Package]' to type '[[String : String]]?'. How can I assign it to tableview sections.
Code:
var packag = [Package]()
enum TableSection: Int {
case subscriptionType = 0, yearly, monthly, weekly, total
}
var data = [TableSection: [[String: String]]]()
func sortData() {
data[.yearly] = packag.filter({ $0.subscriptionType == "yearly" })
data[.monthly] = packag.filter({ $0.subscriptionType == "monthly" })
data[.weekly] = packag.filter({ $0.subscriptionType == "weekly" })
}
Updated Code - viewdidload():
override func viewDidLoad() {
super.viewDidLoad()
tableview.dataSource = self
tableview.delegate = self
//fetchData()
if let path = Bundle.main.path(forResource: "packageList", ofType: "json") {
do {
let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe)
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .custom{ decoder -> Date in
let container = try decoder.singleValueContainer()
let dateStr = try container.decode(String.self)
return Date(timeIntervalSince1970: TimeInterval(dateStr)!)
}
let jSON = try? decoder.decode(Root.self, from: data)
if let packages = jSON?.packages {
self.sortData(packages: packages)
print(packages)
}
} catch {
// handle error
print(Error.self)
}
}
}
Root Model:
struct Root : Codable {
let packages : [Package]
}
Packages Model:
struct Package : Codable {
let availableUntil : Date
let benefits : [String]?
let desc : String
let didUseBefore : Bool
let name : String
let price : Double
let subscriptionType : String
let tariff : Tariff
}
Traiff Model:
struct Tariff : Codable {
let data : String
let sms : String
let talk : String
}
Updated PackageJson Data:
{ "packages": [
{
"name": "Platinum Maksi 6 GB",
"desc": "Zengin içerikli Platinum Maksi Paketi ile Turkcell Uygulamalarının keyfini sürün!",
"subscriptionType": "monthly",
"didUseBefore": true,
"benefits": [
"TV+",
"Fizy",
"BiP",
"lifebox",
"Platinum",
"Dergilik"
],
"price": 109.90,
"tariff": {
"data": "6144",
"talk": "2000",
"sms": "100"
},
"availableUntil": "1558131150"
},
{
"name": "Platinum Maksi 8 GB",
"desc": "Zengin içerikli Platinum Maksi Paketi ile Turkcell Uygulamalarının keyfini sürün!",
"subscriptionType": "monthly",
"didUseBefore": false,
"benefits": [
"TV+",
"Fizy",
"BiP",
"lifebox",
"Platinum",
"Dergilik"
],
"price": 129.90,
"tariff": {
"data": "8192",
"talk": "2000",
"sms": "100"
},
"availableUntil": "1555060350"
},
{
"name": "Platinum Maksi 12 GB",
"desc": "Zengin içerikli Platinum Maksi Paketi ile Turkcell Uygulamalarının keyfini sürün!",
"subscriptionType": "yearly",
"didUseBefore": false,
"benefits": [
"TV+",
"Fizy",
"BiP",
"lifebox",
"Platinum",
"Dergilik"
],
"price": 109.90,
"tariff": {
"data": "12288",
"talk": "2000",
"sms": "100"
},
"availableUntil": "1555060350"
},
The problem is that you are trying to assign a Package object to a value that is expecting an array of dictionary.
Your data variable is a dictionary that has a TableSection as a key, and an array of dictionaries as the value which you have defined by writing [[String: String]]. Then in your sortData function you're trying to assign a value to various data keys, but you're assigning a Package item to it when it's expecting an array of dictionaries.
What will work is if you change your data definition to
var data = [TableSection: Package]()
Model
import Foundation
struct JSON: Codable {
let packages: [Package]
}
struct Package: Codable {
let name, desc, subscriptionType: String
let didUseBefore: Bool
let benefits: [String]
let price: Double
let tariff: Tariff
let availableUntil: String
}
struct Tariff: Codable {
let data, talk, sms: String
}
Enum
enum TableSection: Int {
case subscriptionType = 0, yearly, monthly, weekly, total
}
Data
var data = [TableSection: [Package]]()
Parsing
if let path = Bundle.main.path(forResource: "document", ofType: "json") {
do {
let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe)
let jSON = try? JSONDecoder().decode(JSON.self, from: data)
if let packages = jSON?.packages {
self.sortData(packages: packages)
}
} catch {
// handle error
}
}
Sorting
private func sortData(packages : [Package]) {
data[.yearly] = packages.filter({ $0.subscriptionType == "yearly" })
data[.monthly] = packages.filter({ $0.subscriptionType == "monthly" })
data[.weekly] = packages.filter({ $0.subscriptionType == "weekly" })
print("Data is \(data)")
}
I use LOCAL JSON, you can use server data.
I'm using JSONDecoder to decode from a JSON file which has nested dictionaries. It fails to decode from the json data to my customized model.
This is what I have tried in my code.
The JSONDecoder looks like this:
let netWorkManager = NetWorkManager(URL: url, httpMethodType: .GET)
netWorkManager.callAPI { (data, status, error) in
guard let data = data else {
onFail(NetWorkError.otherError)
return
}
switch status {
case 200:
do{
if let responseModel = try JSONDecoder().decode(ResonseModel?.self, from: data) {
onSuccess(responseModel)
}
}catch {
onFail(NetWorkError.otherError)
}
default:
onFail(NetWorkError.otherError)
}
}
The model looks like this:
struct ResonseModel: Codable {
let type : String
let format: String
let data: [String: Champion]
struct Champion: Codable {
let version: String
let id: String
let key: Int
let name: String
let title: String
let blurb: String
}
}
The JSON structure looks like this:
{
"type": "champion",
"format": "standAloneComplex",
"version": "9.3.1",
"data": {
"Aatrox": {
"version": "9.3.1",
"id": "Aatrox",
"key": "266",
"name": "Aatrox",
"title": "the Darkin Blade",
"blurb": "Once honored defenders of Shurima against the Void, Aatrox and his brethren would eventually become an even greater threat to Runeterra, and were defeated only by cunning mortal sorcery. But after centuries of imprisonment, Aatrox was the first to find...",
"info": {
"attack": 8,
"defense": 4,
"magic": 3,
"difficulty": 4
},
"tags": [
"Fighter",
"Tank"
],
"partype": "Blood Well",
},
"Ahri": {
"version": "9.3.1",
"id": "Ahri",
"key": "103",
"name": "Ahri",
"title": "the Nine-Tailed Fox",
"blurb": "Innately connected to the latent power of Runeterra, Ahri is a vastaya who can reshape magic into orbs of raw energy. She revels in toying with her prey by manipulating their emotions before devouring their life essence. Despite her predatory nature...",
"info": {
"attack": 3,
"defense": 4,
"magic": 8,
"difficulty": 5
},
"tags": [
"Mage",
"Assassin"
],
"partype": "Mana",
},
...
this is the link for the JSON if you want to look into it: http://ddragon.leagueoflegends.com/cdn/9.3.1/data/en_US/champion.json
I want to decode the "data" property as a dictionary whose key is the name of the champion and value is the champion. But the jsonDecoder seems doesn't recognize my model structure. It ends up catch the error.
The JSON-parameter "key" is not an Integer.
Change it to String and it will work:
struct ResonseModel: Codable {
let type : String
let format: String
let data: [String: Champion]
struct Champion: Codable {
let version: String
let id: String
let key: String
let name: String
let title: String
let blurb: String
}
}
You can switch to manually decoding Champion in order to clean up the data.
struct ResonseModel: Decodable {
let type : String
let format: String
let data: [String: Champion]
struct Champion: Decodable {
let version: String
let id: String
let key: Int
let name: String
let title: String
let blurb: String
enum CodingKeys: String, CodingKey {
case version, id, key, name, title, blurb
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.version = try container.decode(String.self, forKey: .version)
self.id = try container.decode(String.self, forKey: .id)
guard let key = Int(try container.decode(String.self, forKey: .key)) else {
throw DecodingError.valueNotFound(Int.self,
.init(codingPath: decoder.codingPath,
debugDescription: "Bad value for id"))
}
self.key = key
self.name = try container.decode(String.self, forKey: .name)
self.title = try container.decode(String.self, forKey: .title)
self.blurb = try container.decode(String.self, forKey: .blurb)
}
}
}
This is basically the code that the compiler writes for you; it just converts the string into an int because that's what you really wanted.
Given the following sample JSON
{
"filters": [
{ "name" : "First Type",
"types" : ["md", "b", "pb"]},
{ "name" : "Second Type",
"types" : ["pt", "ft", "t"]},
{ "name" : "Third Type",
"types" : ["c", "r", "s", "f"]
}
],
"jobs": [
{ "title":"f",
"description" : "descrip text",
"criteria":[ "md", "ft", "s" ],
"img" : "www1"
},
{ "title":"boa",
"description" : "a description",
"criteria":[ "b", "pb", "f", "ft" ],
"img" : "www2"
},
{ "title":"BK",
"description" : "something here",
"criteria":[ "md", "pt", "ft", "b", "s" ],
"img" : "www3"
}
]
}
(Using Alamofire to create the response)
let responseJSON : JSON = JSON(response.result.value!)
1) I am trying to convert these into two String arrays. One array: let filter = [String : [String]] and another array for the jobs. How do I do it? (aka give a man a fish) The following are some sample code snippets, but none are even close to working.
let filterCategories = responseJSON["filters"].arrayValue.map({
$0["name"].stringValue
})
and
for (key,subJson):(String, JSON) in responseJSON["filters"] {
let object : filterObject = filterObject(category: key, list: subJson.arrayValue.map({ $0.stringValue }))
}
2) How do I learn how to use this properly? (aka teach a man to fish) I have been reading the documentation (https://github.com/SwiftyJSON/SwiftyJSON) but I'm struggling to understand it. I'm guessing the final answer will use .map, .stringValue, and .arrayValue. Ultimately though I'm trying to avoid lots of needless or unmanageable code.
Swift 4 provides JSON parsing support out of the box - maybe start within something like Ultimate Guide to JSON Parsing with Swift 4
Based on your available structure, I threw into a Playground and used...
// I was loading the JSON from a file within the Playground's Resource folder
// But basically, you want to end up with a reference to Data
let filePath = Bundle.main.path(forResource:"Source", ofType: "json")
let data = FileManager.default.contents(atPath: filePath!)
struct Filter: Codable {
let name: String;
let types: [String];
}
struct Job: Codable {
let title: String;
let description: String;
let criteria: [String];
let img: String;
}
struct Stuff: Codable {
let filters: [Filter];
let jobs: [Job];
}
let decoder = JSONDecoder();
let stuff = try! decoder.decode(Stuff.self, from: data!)
print("Filter:")
for filter in stuff.filters {
print(filter.name)
for type in filter.types {
print(" - \(type)")
}
}
print("Jobs:")
for job in stuff.jobs {
print(job.title)
print(job.description)
print(job.img)
for type in job.criteria {
print(" - \(type)")
}
}
to parse the results
You can implement Codable protocol to parse response. use your json response instead of this
let url = Bundle.main.url(forResource: "data", withExtension: "json")
let data = NSData(contentsOf: url!)
i used this for playground for testing.
struct Root: Codable {
let jobs: [Jobs]
let filters: [Filters]
private enum CodingKeys: String, CodingKey {
case jobs = "jobs"
case filters = "filters"
}
}
struct Filters: Codable {
let name: String?
let typees: String?
}
struct Jobs: Codable {
let title: String?
let description: String?
let criteria: [String]?
let img: String?
}
let url = Bundle.main.url(forResource: "data", withExtension: "json")
let data = NSData(contentsOf: url!)
do {
let root = try JSONDecoder().decode(Root.self, from: data as! Data)
if let name = root.jobs.first?.title {
print(name)
}
} catch let error as NSError {
print(error.description)
}