I am trying to use JSON data for the first time and I am really struggling with it. I want to show the data in a UIView with some labels but when I add some breakpoints after the line else { continue } the step thru process goes right past else { continue } and continues on past all of my code after that point and so the employee is never added to my object and I can't ever confirm if my code even works by printing it with the print() method. I also am encountering the following error: Cannot convert value of type '[String : Any]' to expected argument type '[String]?'
I'm not really sure what I could do to fix this and any advice would be greatly appreciated.
Here is my ViewController.swift file's code:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var empName: UILabel!
#IBOutlet weak var empUsername: UILabel!
#IBOutlet weak var empSkills: UILabel!
#IBOutlet weak var pastEmployers: UILabel!
#IBOutlet weak var textFieldNote: UITextField!
var employees = [Employee]() // An empty array of employees
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
// Get the path to the EmployeeData.json file
if let path = Bundle.main.path(forResource: "EmployeeData", ofType: ".json" ) {
let url = URL(fileURLWithPath: path)
do {
// Create a data object from the files url
// After this line executes, our file is in binary format inside the "data" constant
let data = try Data.init(contentsOf: url)
// Create a json Object from the binary Data file.
// Catch it as an array of Any type objects
let jsonObj = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [Any]
// At this point we have an array of Any objects that represents our JSON data from our file
// We can now parse through the jsonObj and start instantiating our Employee objects.
Parse(jsonObject: jsonObj)
}
catch {
print(error)
}
}
}
func Parse(jsonObject: [Any]?) {
guard let json = jsonObject
else {print("Parse failed to unwrap the optional."); return }
// Looping through the first level objects
for firstLevelItems in json {
// Try to convert first level object into a [String: Any]
guard let object = firstLevelItems as? [String: Any],
let ename = object["employeename"] as? String,
let eusername = object["username"] as? String,
let emacaddress = object["macaddress"] as? String,
let ecurrentTitle = object["current_title"] as? String,
let eskills = object["skills"] as? [String],
let epastEmployers = object["past_employers"] as? [String: Any],
let pcompany = epastEmployers["company"] as? String,
let presponsibilities = epastEmployers["responsibilities"] as? String,
let ecompany = object["company"] as? String,
let eresponsibilities = object["responsibilities"] as? [String]
// if anything in the guard fails, immediately continue to the next iteration of the loop
else { continue }
employees.append(Employee(name: ename, username: eusername, macaddress: emacaddress, currentTitle: ecurrentTitle, skills: eskills, pastEmployers: epastEmployers, currentCompany: ecompany, responsibilities: eresponsibilities))
// Build Object here
//Check to see if this current employee exists yet
// Filter is a fast and elegant way to make this comparison
// Looping through each object would be tedious and slow
//
// var tempString = "\(ename) \(eusername) \(emacaddress) \(ecurrentTitle) \(eskills) \(ecompany) \(eresponsibilities)"
//
// if let pastEmployer = epastEmployers["company"] as? String {
//
// tempString += "\(pastEmployer)"
// }
// else if let currentResponsibilities = eresponsibilities["responsibilities"] as? String {
//
// tempString += "\(currentResponsibilities)"
// }
}
}
// func addEmployee(_employee: Employee) {
// if let currentSkills = eskills["skills"] as? String {
//
// _employee.skills.append()
// }
// }
func displayEmployee() {
empName.text = employees[0].name.description
}
#IBAction func currentEmpChanged(_ sender: UIButton) {
}
}
And here is my Employee.swift file's code:
//
// Employee.swift
// BoschBrookeCE02
//
// Created by Brooke Bosch on 7/3/21.
//
import Foundation
class Employee {
// Stored properties
let name: String
let username: String
let macaddress: String
let currentTitle: String
let skills: [String]
let pastEmployers: [String]!
let currentCompany: String
let responsibilities: [String]
// computed properties
// Initializers
init(name: String, username: String, macaddress: String, currentTitle: String, skills: [String], pastEmployers: [String]!, currentCompany: String, responsibilities: [String]) {
self.name = name
self.username = username
self.macaddress = macaddress
self.currentTitle = currentTitle
self.skills = skills
self.pastEmployers = pastEmployers
self.currentCompany = currentCompany
self.responsibilities = responsibilities
}
// Methods
}
And finally, my EmployeeData.json file
[{"employeename":"Shawn Cunningham","username":"scunningham0","macaddress":"41-6A-75-B0-2E-73","current_title":"Programmer Analyst IV","skills":["VPython","EES","CD covers","RED MX"],"past_employers":[{"company":"Zboncak-O'Conner","responsibilities":["Extended next generation database"]},{"company":"Friesen, Runolfsson and Kautzer","responsibilities":["Switchable discrete conglomeration","Synergized client-driven project","Universal full-range website","Secured optimal productivity","Balanced multimedia solution"]},{"company":"Zieme-Cronin","responsibilities":["Cloned empowering solution"]}]},
{"employeename":"Martin Wallace","username":"mwallace1","macaddress":"AE-B2-85-08-74-8F","current_title":"Associate Professor","skills":["FBT","Theory","CMMI Level 5","MQL"],"past_employers":[]},
{"employeename":"Roy Young","username":"ryoung2","macaddress":"7C-BD-D4-15-65-ED","current_title":"Mechanical Systems Engineer","skills":["EPA"],"past_employers":[{"company":"Hauck Group","responsibilities":[]},{"company":"DuBuque, Wyman and Kuvalis","responsibilities":[]},{"company":"Hickle and Sons","responsibilities":["Visionary zero defect customer loyalty","Decentralized responsive implementation","Inverse user-facing data-warehouse"]}]},
{"employeename":"Anthony Berry","username":"aberry3","macaddress":"AE-EE-3F-5E-2F-07","current_title":"Help Desk Operator","skills":["KOL Identification","Web Content","JDeveloper","DB2"],"past_employers":[{"company":"Champlin-Emard","responsibilities":["Cross-platform 3rd generation service-desk","Synergized 6th generation paradigm"]}]},
{"employeename":"Jerry Murphy","username":"jmurphy4","macaddress":"0F-BA-ED-D5-95-D5","current_title":"Sales Representative","skills":["Ehcache","FNMA"],"past_employers":[{"company":"Heidenreich Inc","responsibilities":["Digitized encompassing service-desk","Sharable transitional instruction set","Re-engineered tertiary flexibility","Multi-lateral composite moratorium","Multi-tiered methodical orchestration"]}]},
{"employeename":"Gregory Lawrence","username":"glawrence5","macaddress":"E5-BC-80-A4-A4-3E","current_title":"Financial Analyst","skills":["Gutters","Karl Fisher"],"past_employers":[{"company":"Buckridge-Armstrong","responsibilities":["Robust 5th generation core"]},{"company":"Medhurst-Kovacek","responsibilities":["Operative disintermediate middleware","Optional dedicated installation","Multi-tiered foreground artificial intelligence","Automated attitude-oriented algorithm"]},{"company":"Kuhic, Beer and Runolfsson","responsibilities":["Assimilated explicit adapter","Ameliorated maximized benchmark"]}]},
{"employeename":"Linda Myers","username":"lmyers6","macaddress":"BF-9D-78-1C-8E-E1","current_title":"Financial Advisor","skills":["Special Effects","PCI DSS"],"past_employers":[{"company":"Weber-Ernser","responsibilities":[]},{"company":"Homenick, Nicolas and Kassulke","responsibilities":["Right-sized attitude-oriented instruction set","Customizable scalable help-desk","Secured mission-critical intranet","Versatile stable help-desk","Synergized composite initiative"]},{"company":"Zemlak Inc","responsibilities":["Seamless upward-trending circuit","Advanced object-oriented software","Business-focused asymmetric project"]}]},
{"employeename":"Alice Hunter","username":"ahunter7","macaddress":"A9-65-3D-10-01-8C","current_title":"Nurse","skills":["Estate Jewelry","Legal Research","Social Media Blogging"],"past_employers":[{"company":"Abernathy Group","responsibilities":["Business-focused empowering toolset","Object-based systemic application","Triple-buffered demand-driven analyzer","Cross-platform didactic intranet"]}]},
{"employeename":"Lois Elliott","username":"lelliott8","macaddress":"03-93-D1-52-7D-91","current_title":"Research Assistant IV","skills":["DDR2","BCM","Airline Management","IT Infrastructure Management","Helicopters"],"past_employers":[{"company":"Lesch-Kunze","responsibilities":["Ergonomic analyzing contingency","Triple-buffered radical moratorium","Realigned zero tolerance functionalities","Vision-oriented mission-critical access"]}]},
{"employeename":"Sandra Alvarez","username":"salvarez9","macaddress":"C4-BF-22-9C-02-44","current_title":"Paralegal","skills":["DSE Assessments"],"past_employers":[{"company":"Murphy-Zemlak","responsibilities":["Optional transitional intranet","Managed bi-directional functionalities","Synergized uniform toolset"]},{"company":"Howe LLC","responsibilities":["Self-enabling content-based intranet","Enterprise-wide tertiary project"]},{"company":"Dare and Sons","responsibilities":["Decentralized upward-trending capacity","Operative discrete ability","Right-sized bottom-line orchestration","User-centric exuding conglomeration"]}]},
{"employeename":"Diane Russell","username":"drussella","macaddress":"5C-2D-32-7D-01-7F","current_title":"Registered Nurse","skills":["KnockoutJS","Wufoo","IAS 39","Childhood Obesity","Amazon RDS"],"past_employers":[]},
{"employeename":"Amanda Grant","username":"agrantb","macaddress":"FC-B6-7B-B3-77-47","current_title":"Software Test Engineer IV","skills":["AVEVA PDMS"],"past_employers":[{"company":"Miller Group","responsibilities":["Assimilated hybrid migration","Future-proofed regional capacity","Re-contextualized system-worthy migration"]},{"company":"Smitham-Kunze","responsibilities":["Programmable 24/7 firmware","Networked systematic throughput","Streamlined interactive orchestration","Enterprise-wide 24/7 circuit","Organized didactic Graphic Interface"]},{"company":"Harvey Group","responsibilities":["Triple-buffered hybrid strategy","Switchable intangible hierarchy","Synergistic coherent Graphic Interface"]}]},
{"employeename":"Philip Stewart","username":"pstewartc","macaddress":"02-22-FB-BA-F9-DE","current_title":"Paralegal","skills":["WiMAX","Tubing"],"past_employers":[{"company":"Abernathy, Murazik and Gerhold","responsibilities":["Future-proofed object-oriented secured line","Distributed didactic portal","Reactive coherent challenge","Proactive transitional success"]},{"company":"Kassulke and Sons","responsibilities":["Programmable web-enabled open system","Grass-roots bi-directional encryption","Streamlined neutral projection","Diverse client-server protocol"]},{"company":"Wiegand, Powlowski and Flatley","responsibilities":["Operative disintermediate forecast"]}]},
{"employeename":"Jack Johnston","username":"jjohnstond","macaddress":"C4-0E-4D-8C-DE-7D","current_title":"Nurse Practicioner","skills":["BDD","Risk Assessment","KPI Reports"],"past_employers":[]},
{"employeename":"Ralph Bishop","username":"rbishope","macaddress":"D5-0D-1B-D8-97-E2","current_title":"Programmer IV","skills":[],"past_employers":[]},
{"employeename":"Martin Smith","username":"msmithf","macaddress":"B3-A1-20-AC-FA-ED","current_title":"Automation Specialist IV","skills":["QNX","Integrated Circuit Design"],"past_employers":[{"company":"Denesik-Hamill","responsibilities":["Compatible leading edge pricing structure","Realigned maximized help-desk","User-friendly foreground productivity"]}]},
{"employeename":"Laura Lawson","username":"llawsong","macaddress":"21-73-52-CD-50-1C","current_title":"Senior Developer","skills":["International Sales","ODI","TCD"],"past_employers":[{"company":"Brekke-Schmeler","responsibilities":[]}]},
{"employeename":"Lisa Mills","username":"lmillsh","macaddress":"E8-31-0D-D1-C8-22","current_title":"Technical Writer","skills":["Joomla","GNU C","PWS","Aerial Lifts"],"past_employers":[]},
{"employeename":"Louis Perez","username":"lperezi","macaddress":"42-35-3F-2A-A9-52","current_title":"Environmental Specialist","skills":["RPM","Nutritional Counseling","Zendesk"],"past_employers":[{"company":"Rutherford, Okuneva and Steuber","responsibilities":["Devolved attitude-oriented intranet","Stand-alone disintermediate budgetary management","Decentralized value-added function","Multi-channelled dedicated intranet"]},{"company":"DuBuque-Schroeder","responsibilities":["Automated stable circuit","Virtual coherent groupware"]},{"company":"Jast, Rippin and Nicolas","responsibilities":["Multi-layered intangible moratorium","Right-sized web-enabled emulation","Business-focused uniform capacity","Organized eco-centric toolset","Seamless solution-oriented moderator"]}]},
{"employeename":"Bonnie Snyder","username":"bsnyderj","macaddress":"5F-A8-ED-1E-DF-AF","current_title":"Account Coordinator","skills":[],"past_employers":[{"company":"Champlin, Ritchie and Wiegand","responsibilities":["Persevering fresh-thinking focus group","Visionary stable core"]}]}]
Your past_employers is an Array<Dictionary>, NOT a Dictionary.
"past_employers":[
{"company":"Zboncak-O'Conner","responsibilities":["Extended next generation database"]},
{"company":"Friesen, Runolfsson and Kautzer","responsibilities":["Switchable discrete conglomeration","Synergized client-driven project","Universal full-range website","Secured optimal productivity","Balanced multimedia solution"]},
{"company":"Zieme-Cronin","responsibilities":["Cloned empowering solution"]}
]
This line is failing.
let epastEmployers = object["past_employers"] as? [String: Any]
If you change it to
let epastEmployers = object["past_employers"] as? [[String: Any]]
It will now force you to access all the employer information from the Array NOT Dictionary. So all of the following lines would have to be inside a for loop.
// Wrong, `epastEmployers` is an Array
let pcompany = epastEmployers["company"] as? String,
let presponsibilities = epastEmployers["responsibilities"] as? String,
let ecompany = object["company"] as? String,
let eresponsibilities = object["responsibilities"] as? [String]
// Right
for epastEmployer in epastEmployers {
let ecompany = epastEmployer["company"] as? String
let eresponsibilities = epastEmployer["responsibilities"] as? [String]
}
So I have a JSON file that I need to parse and update labels and image. In my storyboard I have 4 labels (image of an animal, region, it's weight and length) and uiimage where I need to put it's picture.
I need to update labels and image by parsing JSON.
This is how far I was able to get to...
My JSON look like this:
"data":[
{
"name":"Lion",
"thumbnail":"https://kopelion.org/wp-content/uploads/2016/10/Kimani.jpg",
"region":"Africa",
"stats":{
"max_weight":180,
"length":250
}
}
]
I tried to get into this by writing:
override func viewDidLoad() {
super.viewDidLoad()
guard let path = Bundle.main.path(forResource: "data", ofType: "json") else { return }
let url = URL(fileURLWithPath: path)
do {
let data = try Data(contentsOf: url)
let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers)
print(json)
guard let array = json as? [Any] else { return }
for animal in array {
guard let animalDict = animal as? [String: Any] else { return }
guard let animalName = animalDict["name"] as? String else { return }
guard let animalRegion = animalDict["region"] as? String else { return }
guard let animalStats = animalDict["stats"] as? String else { return }
print(animalName)
print(animalRegion)
print(animalStats)
}
} catch {
print(error)
}
}
First thing first, most of the times the main culprit is json file itself, as in your case.
You need to fix the json code first, by adding "{" at the top and "}" at the bottom of your json file's code.
This will make it valid json
And then do the following
Replace your code lines:
// 1
guard let array = json as? [Any] else { return }
// 2
guard let animalStats = animalDict["stats"] as? String else { return }
with this:
// 1
guard let dictionary = json as? [String:[Any]] else { return }
guard let array = dictionary["data"] else { return }
//2
guard let animalRegion = animalDict["region"] as? String else { return }
Quik tip : 1. Check your json using online tools like https://codebeautify.org/jsonviewer
Try to use JSONDecoder and JSONEncoder instead of JSONSerialization
You'll need to have properties in your class for the storyboard items you want to set. e.g.,:
#IBOutlet weak var name: UILabel! // outside functions but inside class, and hooked up in Storyboard
// inside viewDidLoad
name.text = animalDict["name"] as? String
I wouldn't bother with the intermediate variables unless you really need them.
As you are responsible for the JSON delete the enclosing dictionary data, it's not needed.
[{
"name":"Lion",
"thumbnail":"https://kopelion.org/wp-content/uploads/2016/10/Kimani.jpg",
"region":"Africa",
"stats":{
"max_weight":180,
"length":250
}
}]
Create two structs
struct Animal: Decodable {
let name: String
let thumbnail: URL
let region: String
let stats: Stats
}
struct Stats: Decodable {
let maxWeight, length: Int
}
In the view controller declare a data source array
var animals = [Animal]()
In viewDidLoad parse the data with JSONDecoder and assign the result to the data source array
override func viewDidLoad() {
super.viewDidLoad()
let url = Bundle.main.url(forResource: "data", withExtension: "json")!
let data = try! Data(contentsOf: url)
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
self.animals = try! decoder.decode([Animal].self, from: data)
}
All guards and trys are actually not needed. The file must exist at compile time and cannot be modified at runtime so the code must not crash.
You can get the animal properties with
let animal = animals[0]
let name = animal.name
let weight = animal.stats.weight
print(name, weight)
Assuming there are more animals in the JSON use a loop
for animal in animals {
let name = animal.name
let weight = animal.stats.weight
print(name, weight)
}
How to update the labels is unclear because there is no significant information about the design in your question.
To get the image load it asynchronously with URLSession
I'm new to Swift - trying to read a JSON file from a URL. My attempt below.
The JSON looks valid - I tested it with JSONLint but it keeps crashing.
Thoughts?
func getRemoteJsonFile() -> NSDictionary {
//Create a new url
let remoteUrl:NSURL? = NSURL(string: "http://nfl-api.azurewebsites.net/myplayers.json")
//check if its nil
if let actualRemoteUrl = remoteUrl {
//try to get the data
let filedata:NSData? = NSData(contentsOfURL: actualRemoteUrl)
//check if its nil
if let actualFileData = filedata {
//parse out the dictionaries
let jsonDict = NSJSONSerialization.JSONObjectWithData(actualFileData, options: NSJSONReadingOptions.AllowFragments, error: nil) as NSDictionary
return jsonDict
}
}
return NSDictionary()
}
This took me a second to figure out, so I don't blame you for missing it.
The JSON you linked to is minified, so it's difficult to see the structure. Let's take a look at (a fragment of) it after piping it through a prettifier:
[
{
"PlayerId":2501863,
"PlayerName":"Peyton Manning",
"PlayerTeam":"DEN",
"PlayerPosition":"QB",
"PlayerPassingYards":4727,
"PlayerPassingTDs":39,
"PlayerInterceptions":15,
"PlayerRushingYards":-24,
"PlayerRushingTDs":0,
"PlayerReceivingYards":0,
"PlayerReceivingTDs":0,
"PlayerReturnYards":0,
"PlayerReturnTDs":0,
"PlayerFumbleTDs":0,
"PlayerTwoPointConversions":2,
"PlayerFumblesLost":2,
"PlayerTeamLogo":"http://i.nflcdn.com/static/site/7.0/img/logos/teams-gloss-81x54/den.png"
}
]
Huh. It's encased in brackets, which means that it's an array.
It's an array, so you can't cast it as an NSDictionary. Instead, you could cast it as an NSArray, but why not use native Swift types?
Well, if you don't like types, you're about to find out, but I still think that this is a better way, because it forces you to think about the data you're parsing.
So we have the first part of our type definition for this function; it's an array ([]). What components is our array made up of? We could go with a simple NSDictionary, but we're doing full native types here, so let's use a native Swift dictionary.
To do that, we have to know the types of the dictionary (the syntax for a native dictionary type is [KeyType: ValueType]). Examining the JSON shows that all of the keys are Strings, but the values are of varying types, so we can use AnyObject.
That gives us a dictionary type of [String: AnyObject], and our entire JSON is an array of that, so the final type is [[String: AnyObject]] (wow).
Now that we have the proper type, we can modify the function you're using to parse the JSON a bit.
First of all, let's use our new type for the return and cast values. Then, let's make the return type optional in case something goes wrong and add an error variable to document that.
A cleaned up function would look something like this:
func getData() -> [[String: AnyObject]]? {
let data: NSData? = NSData(contentsOfURL: NSURL(string: "http://nfl-api.azurewebsites.net/myplayers.json")!)
if let req: NSData = data {
var error: NSError?
if let JSON: [[String: AnyObject]] = NSJSONSerialization.JSONObjectWithData(req, options: NSJSONReadingOptions.AllowFragments, error: &error) as? [[String: AnyObject]] {
return JSON
}
}
return nil
}
That's it!
We can now call the function and extract values from our [[String: AnyObject]] (again, wow) like this:
if let data: [[String: AnyObject]] = getData() {
println(data[0]["PlayerName"]!) // Peyton Manning
}
Update your code with this:
func getRemoteJsonFile() -> [NSDictionary] {
// Create a new URL
let remoteUrl:NSURL? = NSURL(string: "http://nfl-api.azurewebsites.net/myplayers.json")
let urlString:String = "\(remoteUrl)"
// Check if it's nil
if let actualRemoteUrl = remoteUrl {
// Try to get the data
let fileData:NSData? = NSData(contentsOfURL: actualRemoteUrl)
// Check if it's nil
if let actualFileData = fileData {
// Parse out the dictionaries
let arrayOfDictionaries:[NSDictionary]? = NSJSONSerialization.JSONObjectWithData(actualFileData, options: NSJSONReadingOptions.MutableContainers, error: nil) as [NSDictionary]?
if let actualArrayOfDictionaries = arrayOfDictionaries {
// Successfully parsed out array of dictionaries
return actualArrayOfDictionaries
}
}
}
return [NSDictionary]()
}
This is working fine for me.