SIGABRT error in Swift when JSON deserializing - json

I am attempting to make a Swift application with two main focuses. One is to display all the data from an URL in a ScrollView and the other is a button to get a random name of a game.
The button works and I get a random game but when I try to load the application with the UIScrollView, I get a SIGABRT on line 33.
Any help is appreciated
EDIT: I have since fixed the SIGABRT but I can't seem to display any information into the UIScrollView. Anyone see any glaring issues in the code now?
#IBOutlet weak var infoView: UIView!
#IBOutlet weak var label: UILabel!
#IBOutlet weak var labelScroll: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
parseGame()
}
func parseGame() {
let url: URL = URL(string: "https://www.giantbomb.com/api/games/?api_key=5094689370c2cf4ae42a2a268af0595badb1fea8&format=json&field_list=name")!
print(url)
let responseData: Data? = try? Data(contentsOf: url)
if let responseData = responseData {
let json: Any? = try? JSONSerialization.jsonObject(with: responseData, options: [])
print(json ?? "Couldn't get JSON")
if let json = json {
let dictionary: [String: Any]? = json as? [String: Any]
if let dictionary = dictionary {
guard let result = dictionary["results"] as? [String:Any]? else { return }
if let result = result {
let name = result["name"] as? String?
if let name = name {
for i in 1...100 {
labelScroll.text = "Name: \(name)"
}
}
}
}
}
}
}

Oh no! It's the json parsing pyramid of death!
A Neat Little Alternative
If you know the structure of the json you will be receiving, you can create some structs to model your data. Also Swift has a cool protocol called "Codable" which does a lot of heavy lifting in parsing json and converting it to an object you create in code.
Let's Model the Data!
We'll create 2 structs that conform to the Codable Protocol. The first will hold the entire response data
struct ApiGameReponse:Codable {
var error:String
var limit:Int
var offset:Int
var pages:Int
var totalResults:Int
var status:Int
var version:String
var games:[Game]
private enum CodingKeys:String, CodingKey {
//The enum's rawValue needs to match the json's fieldName exactly.
//That's why some of these have a different string assigned to them.
case error
case limit
case offset
case pages = "number_of_page_results"
case totalResults = "number_of_total_results"
case status = "status_code"
case version
case games = "results"
}
}
and the second struct to model our game object. (Which is a single string, yes I know very exciting...) Since this particular json data represents a game with only 1 name property we don't really need a whole struct, but if the json was different and say had a "gamePrice" and "genre" properties, a struct would be nicer to look at.
struct Game:Codable {
var name:String
}
Keepin it pretty
Idk about you, but I don't like to look at ugly code. To keep it clean, we're going to split the function you made into two pieces. It's always better to have a bunch of smaller readable/reusable functions that each complete a single task rather than 1 superSizeMe number 3 with a large coke.
Getting the Data
Piece 1: Get the data from the URL
func getJSONData(_ urlString: String) -> Data? {
guard
let url:URL = URL(string: urlString),
let jsonData:Data = try? Data(contentsOf: url)
else { return nil }
return jsonData
}
Piece 2: Decode the data into something we can use
func getGameNames() -> [String] {
let apiEndpoint = "https://www.giantbomb.com/api/games/?api_key=5094689370c2cf4ae42a2a268af0595badb1fea8&format=json&field_list=name"
guard
let data = getJSONData(apiEndpoint),
let response = try? JSONDecoder().decode(ApiGameReponse.self, from: data)
else { return [] }
//Neat little function "map" allows us to create an array names from the array of game objects.
let nameArray = response.games.map { $0.name }
return nameArray
}
Implementation
And finally we can get the names and use them as needed.
override func viewDidLoad() {
//I recommend putting this somewhere else
//Perhaps create a method called "setup()" and call it in this class's inializer.
let names = getGameNames()
print(names) //Implement the array of names as needed
}

Related

For loop does not go beyond else { continue }

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]
}

Trying to parse json, can't seem to parse arrays inside of an array

I've been trying to parse a part of this JSON file: https://opendata.brussels.be/api/records/1.0/search/?dataset=traffic-volume&rows=3&facet=level_of_service
I wanna get records->fields->geo_shape->coordinates but I can't seem to print these arrays inside of the "coordinates" array.. I thought it might be because the arrays inside of the coordinates do not have names, so I don't know how to make a variable for them. Got this code currently:
import UIKit
import Foundation
struct Geoshape : Codable {
let coordinates: Array<...>
}
struct Field : Codable {
let geo_shape: Geoshape
let level_of_service: String
}
struct Record: Codable {
let fields: Field
}
struct Traffic: Codable{
let records: Array<Record>
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func clickRefresh(_ sender: Any) {
guard let url = URL(string: "https://opendata.brussels.be/api/records/1.0/search/?dataset=traffic-volume&rows=3&facet=level_of_service") else { return }
let session = URLSession.shared
let task = session.dataTask(with: url){ (data, response, error) in
if let response = response {
print(response)
}
if let data = data {
let traffic = try? JSONDecoder().decode(Traffic.self, from: data)
print(traffic)
}
}
task.resume()
}
}
Clearly the Array<...> needs to be changed but I don't know to what. I've tried making an extra struct with just 1 variable (which is another array of the type Double: Double) but that does not seem to work. I was able to print everything just fine up to the point I tried to go into the coordinates array.
Anyone can help me?
Replace
let coordinates: Array<...>
with
let coordinates:[[Double]]
First of all your file in Resource contains a JSON which contains an array or Collection (typically in Swift).
One important thing: If you fail to decode an object in json you get null from all stored properties.
fail occurs when your Coding Keys does not match keys in json or type you are casting is diffrent.
In your code you fail to cast coordinates to its type. coordinates is a collection of collections of Double.
var coordinates: [[Double]]
If you want to fetch data into your models, you should conforms them to Decodable protocol which means that,JSON attributes can decode itself.
Based on Apple developer documentation:
Decodable is a type that can decode itself from an external representation.
also Codable protocol refers to Decodable and Encodable protocols. but current purpose is Decoding data.
typealias Codable = Decodable & Encodable
Your code should look like:
Swift 5
Prepared for Playground, paste this into your playground
import Foundation
struct GeoShape: Decodable {
var coordinates: [[Double]]
}
struct Field: Decodable {
var geo_shape: GeoShape
}
struct Record: Decodable {
var fields: Field
}
struct Traffic: Decodable {
var records: [Record]
}
guard let url = URL.init(string: "https://opendata.brussels.be/api/records/1.0/search/?dataset=traffic-volume&rows=3&facet=level_of_service")
else {fatalError()}
URLSession.shared.dataTask(with: url){ (data, response, error) in
if let data = data {
let traffic = try? JSONDecoder().decode(Traffic.self, from: data)
print("First coordinate is: ",traffic?.records.first?.fields.geo_shape.coordinates.first)
}
}.resume()

How can I update labels and UIImage placeholder from local JSON?

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

Swift 4 JSONSerialization.jsonObject

I am using Xcode 9.2 and Swift 4. How can I check if the returned json data is null?
Now it gives me error Type 'Any' has no subscript members at line if json[0]["data"]
var json: NSMutableArray = []
var newsArray: NSMutableArray = []
let url = URLFactory()
var data = try! NSData(contentsOf: url.getURL()) as Data
do {
json = try JSONSerialization.jsonObject(with: data, options: []) as! NSMutableArray
if json[0]["data"] {
// data is not null
}
} catch let error as NSError {
// handle error
}
My JSON returns something like this:
{
"data":
[
{
"news_id":123,
"title":"title",
"news_date":"2017-02-08 21:46:06",
"news_url":"url",
"short_description":"description",
"category_id":4,
"category_name":"Health",
"latlng":
[
{
"lat":"43.003429",
"lng":"-78.696335"
}
]
}
{ ....
}
If you're using Swift 4, I might suggest JSONDecoder:
First, define the types to hold the parsed data:
struct ResponseObject: Codable {
let data: [NewsItem]
}
struct NewsItem: Codable {
let newsId: Int
let title: String
let newsDate: Date
let newsURL: URL
let shortDescription: String
let categoryID: Int
let categoryName: String
let coordinates: [Coordinate]
// because your json keys don't follow normal Swift naming convention, use CodingKeys to map these property names to JSON keys
enum CodingKeys: String, CodingKey {
case newsId = "news_id"
case title
case newsDate = "news_date"
case newsURL = "news_url"
case shortDescription = "short_description"
case categoryID = "category_id"
case categoryName = "category_name"
case coordinates = "latlng"
}
}
struct Coordinate: Codable {
let lat: String
let lng: String
}
And then you can parse it:
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(secondsFromGMT: 0)
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .formatted(formatter)
do {
let responseObject = try decoder.decode(ResponseObject.self, from: data)
print(responseObject.data)
} catch {
print(error)
}
Clearly, if your JSON is different, you might need to change your objects accordingly (e.g. it struck me odd that latlng was an array of coordinates). Also, you can define custom init(from:) methods if you want to convert some of these strings into numbers, but I'd rather fix the JSON, instead (why are latlng returning string values rather than numeric values).
For more information, see Encoding and Decoding Custom Types.
As an aside, I'd advise against this pattern (note, this is your network request logic, but excised of NSData):
let data = try! Data(contentsOf: url.getURL())
That will retrieve the data synchronously, which can be problematic because
the app will be frozen while the data is being retrieved resulting in a poor UX;
you risk having your app killed by the watchdog process which looks for frozen apps; and
you don't have robust error handling and this will crash if the network request fails.
I'd suggest using URLSession:
let task = URLSession.shared.dataTask(with: url.getURL()) { data, _, error in
guard let data = data, error == nil else {
print(error ?? "Unknown error")
return
}
// now parse `data` like shown above
// if you then need to update UI or model objects, dispatch that back
// to the main queue:
DispatchQueue.main.async {
// use `responseObject.data` to update model objects and/or UI here
}
}
task.resume()

Swift - Reading JSON File

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.