Struct Init with JSON and flatMap - json

I'm having a problem with the following code. I'm downloading a list of actors in JSON and I want to populate Struct Actor with the received data. Everything works great until I try to flatMap on the received data and try to initialize the struct Actor. When I try to compile the code i get the error: Cannot assign value of type '()' to type [Actor]. The error corresponds to a line in viewDidLoad actorsList = downloadActors() Would anybody have any recommendation who to solve this?
import UIKit
func downloadActors() {
var request = URLRequest(url: URL(string: "url...")!)
request.httpMethod = "POST"
let postString = "actorGroup=\("Superhero")"
request.httpBody = postString.data(using: .utf8)
let task = URLSession.shared.dataTask(with: request) { data, response, error in
DispatchQueue.main.async {
guard let data = data, error == nil else {
print("error=\(error)")
return
}
if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode != 200 {
print("error : statusCode should be 200 but is \(httpStatus.statusCode)")
print("response = \(response)")
}
if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode == 200 {
do {
let json = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.mutableContainers) as? [String: AnyObject]
guard let actorsJSON = json?["response"] as? [[String : AnyObject]] else {
return
}
} catch {
print("catch error")
}
}
}
}
task.resume()
}
func loadActors() -> [Actor] {
if let actors = actorsJSON as? [[String : AnyObject]] {
return actors.flatMap(Actor.init)
}
}
let actorsArray = loadActors()
class MasterViewController: UITableViewController {
var actorsList = [Actor]()
var detailViewController: DetailViewController? = nil
var objects = [Any]()
override func viewDidLoad() {
super.viewDidLoad()
actorsList = downloadActors()
print(actorsList)
Struct Actors is as follows:
struct Job {
let actorGroup: String
let actorName: String
}
extension Actor: JSONDecodable {
init?(JSON: [String : AnyObject]) {
guard let actorGroup = JSON["actorGroup"] as? String, let actorName = JSON["actorName"] as? String else {
return nil
}
self. actorGroup = actorGroup
self. actorName = actorName
}
}

let listActors = actorsJSON as? [[String : AnyObject]] {
Should be:
if let listActors = actorsJSON as? [[String : AnyObject]] {
Edit: For more info I'd like to add Vadian's comment:
Very confusing code. What does the function in the middle of the do block? Why do you type-check actorsJSON twice? The computed property is let listActors... which should be probably an optional binding (if let ... ). Further .mutableContainers is completely nonsense in Swift. And finally a JSON dictionary is [String:Any] in Swift 3.

Related

Unable to parse JSON from link - no errors, but the function returns after trying to access the URL

I am new to Swift and trying basic JSON parsing by following tutorials. I want to print a field of a JSON file, but it is not working.
Although the link exists, and I am using the same link I used for a previous tutorial, it returns rather than moved on to accessing the JSON.
I understand there is an "easier" way to do it in Swift4 using Decoder, but I received an error when I did it that way.
Here is the structure I am using:
struct Tester {
var userId: Int
var id: Int
var title: String
var body: String
init(json: [String: Any]){
userId = json["userId"] as? Int ?? -10
id = json["id"] as? Int ?? -400
title = json["title"] as? String ?? ""
body = json["body"] as? String ?? ""
}
}
And here is the code that is trying to access the JSON entries
#IBAction func printIDTitle(_ sender: Any) {
guard let url = URL(string: "https://jsonplaceholder.typicode.com/posts") else { return }
let session = URLSession.shared
session.dataTask(with: url) { (data, response, error) in
if let response = response {
print(response)
}
guard let data = data else { return }
do {
print("here 0\n")
guard let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String: Any] else {
print(error)
return
}
print("here 0.5\n")
print("here 1\n")
let d = Tester(json: json)
print(d.id)
print(d.title)
print("here 2\n")
} catch let error {
print(error)
}
}.resume()
}
The "here 0" is the only print that shows up.
What could be my issue?
The root is an array so change
guard let json = try JSONSerialization.jsonObject(with: data, options:[]) as? [[String: Any]] else {
print(error)
return
}
Or better
let res = try! JSONDecoder().decode([Root].self, from:data)
struct Root: Codable {
let userId, id: Int
let title, body: String
}

i am trying to parse json with swift 4, please tell me what is wrong in it?

I am trying to parse JSON using the following method, but XCode is giving me an error where I have declared "data" .
I am new, I don't understand what is wrong. Please help me.
import UIKit
struct Contacts: Decodable {
let id: Int
let name: String
let email: String
}
class ViewController: UIViewController {
override func viewDidLoad()
{
super.viewDidLoad()
let urlString = "https://api.androidhive.info/contacts/"
guard let url = URL(string: urlString) else {return}
URLSession.shared.dataTask(with: url) { (data, response, error) in
}
guard let data = data else {return}
//let datastring = String(data: data, encoding: .utf8)
do
{
let contact = try JSONDecoder().decode([Contacts].self, from: data)
print(contact.name)
} catch let jsonErr {
print("Error deserializing json:", jsonErr)
}
}
}
Three major issues.
You are ignoring the root object which is a dictionary containing the contacts array.
The value for key id is String, not Int.
A hard rule is : Everything in double quotes is String even "12" and "false"
You have to resume the task and put the code to parse the JSON into the completion handler.
struct Root : Decodable {
let contacts : [Contact]
}
struct Contact : Decodable { // It's recommended to name this kind of struct in singular form
let id, name, email: String
}
...
override func viewDidLoad()
{
super.viewDidLoad()
let urlString = "https://api.androidhive.info/contacts/"
guard let url = URL(string: urlString) else {return}
URLSession.shared.dataTask(with: url) { (data, response, error) in
if let error = error { print(error); return }
do {
let result = try JSONDecoder().decode(Root.self, from: data!)
let contacts = result.contacts
for contact in contacts {
print(contact.name)
}
} catch {
print("Error deserializing json:", error)
}
}.resume()
}
//
// ViewController.swift
// PostMethodTest
//
// Created by HABIB UR REHMAN on 12/11/2018.
// Copyright © 2018 HABIB UR REHMAN. All rights reserved.
//
import UIKit
class ViewController: UIViewController {
struct Resturant: Decodable {
var name: String
var deliveryCharges: String
var email: String
init(_ dictionary: [String: Any]) {
self.name = dictionary["name"] as? String ?? ""
self.deliveryCharges = dictionary["deliveryCharges"] as? String ?? ""
self.email = dictionary["email"] as? String ?? ""
}
}
override func viewDidLoad() {
super.viewDidLoad()
guard let url = URL(string: "your Link Here ") else { return }
var request = URLRequest(url: url)
request.httpMethod = "POST"
let task = URLSession.shared.dataTask(with: request) {(data, response, error) in
guard let dataResponse = data,
error == nil else {
print(error?.localizedDescription ?? "Response Error")
return }
do{
//here dataResponse received from a network request
let jsonResponse = try JSONSerialization.jsonObject(with:
dataResponse, options: [])
print(jsonResponse) //Response result
} catch let parsingError {
print("Error", parsingError)
}
}
task.resume()
}
}
Please Try this its working for me.
func getContactListsApiCalling() {
var request = URLRequest(url: URL(string: "https://api.androidhive.info/contacts/")!)
request.httpMethod = "GET"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
let session = URLSession.shared
let task = session.dataTask(with: request, completionHandler: { data, response, error -> Void in
do {
if let Data = data {
let responseJSON = try JSONSerialization.jsonObject(with: Data) as? Dictionary<String, AnyObject> ?? [:]
if let contacts = responseJSON["contacts"] as? [[String :AnyObject]] {
for contact in contacts {
let id = contact["id"] as? String ?? ""
let name = contact["name"] as? String ?? ""
let email = contact["email"] as? String ?? ""
let address = contact["address"] as? String ?? ""
let gender = contact["gender"] as? String ?? ""
print(id,name,email,address,gender)
}
}
}
} catch {
print("error")
}
})
task.resume()
}
}

Json parse array in dictionary

I use json api in my application. It can check a company does use electronic Invoice. I have a json data like that:
{
"ErrorStatus": null,
"Result": {
"CustomerList": [
{
"RegisterNumber": "6320036072",
"Title": "VATAN BİLGİSAYAR SANAYİ VE TİCARET ANONİM ŞİRKETİ",
"Alias": "urn:mail:defaultpk#vatanbilgisayar.com",
"Type": "Özel",
"FirstCreationTime": "2014-01-01T05:35:20",
"AliasCreationTime": "2014-01-01T05:35:20"
}
],
"ISEInvoiceCustomer": true
} }
and i use that fucntion for get json data:
func getClientQuery(authorization:String) {
let url = NSURL(string: URLCustomerCheck+strRegisterNumber)
let request = NSMutableURLRequest(url: url! as URL)
request.httpMethod = "GET"
request.addValue(authorization, forHTTPHeaderField: "Authorization")
let task = URLSession.shared.dataTask(with: request as URLRequest) { data,response,error in
if error != nil {
let alert = UIAlertController(title: "Error", message: error?.localizedDescription, preferredStyle: UIAlertControllerStyle.alert)
let okButton = UIAlertAction(title: "OK", style: UIAlertActionStyle.cancel, handler: nil)
alert.addAction(okButton)
self.present(alert, animated: true, completion: nil)
} else {
if data != nil {
do {
let jSONResult = try JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers) as! Dictionary<String,AnyObject>
DispatchQueue.main.async {
print(jSONResult)
let result = jSONResult["Result"] as! [String:AnyObject]
//let customerList = result["CustomerList"] as! [[String:AnyObject]]
let ISEInvoiceCustomer = String(describing: result["ISEInvoiceCustomer"])
self._lblISEinvoiceCustomer.text = " \(ISEInvoiceCustomer) "
}
} catch {
}
}
}
}
task.resume()
}
My question is how can i parse "RegisterNumber", "Title".. in "CustomerList"? It's a array that have a member. However i can not parse it in my function.
The customerList line you commented out is needed. Then iterate that array and pull out whatever values you want from each dictionary.
And you really should avoid us as! or any other forced unwrapping when working with JSON. You don't want your app to crash when you obtain unexpected data.
And never use String(describing:) to create a value you will display to a user. The result is inappropriate for display. It's only to be used for debugging purposes.
if let jSONResult = try JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers) as? [String:Any]
DispatchQueue.main.async {
print(jSONResult)
if let result = jSONResult["Result"] as? [String:AnyObject],
let customerList = result["CustomerList"] as? [[String:Any]] {
for customer in customList {
let registrationNumber = customer["RegisterNumber"]
// and any others you need
}
let ISEInvoiceCustomer = result["ISEInvoiceCustomer"] as? Bool ?? false
self._lblISEinvoiceCustomer.text = ISEInvoiceCustomer) ? "Yes" : "No"
}
}
}
Better to Map json to Model , this become easy using Codable
import Foundation
struct Client: Codable {
let errorStatus: ErrorStatus?
let result: Result
enum CodingKeys: String, CodingKey {
case errorStatus = "ErrorStatus"
case result = "Result"
}
}
struct ErrorStatus: Codable {
}
struct Result: Codable {
let customerList: [CustomerList]
let iseInvoiceCustomer: Bool
enum CodingKeys: String, CodingKey {
case customerList = "CustomerList"
case iseInvoiceCustomer = "ISEInvoiceCustomer"
}
}
struct CustomerList: Codable {
let registerNumber, title, alias, type: String
let firstCreationTime, aliasCreationTime: String
enum CodingKeys: String, CodingKey {
case registerNumber = "RegisterNumber"
case title = "Title"
case alias = "Alias"
case type = "Type"
case firstCreationTime = "FirstCreationTime"
case aliasCreationTime = "AliasCreationTime"
}
}
// MARK: Convenience initializers
extension Client {
init(data: Data) throws {
self = try JSONDecoder().decode(Client.self, from: data)
}
init(_ json: String, using encoding: String.Encoding = .utf8) throws {
guard let data = json.data(using: encoding) else {
throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
}
try self.init(data: data)
}
}
Get customerList :
func getClientQuery(authorization:String) {
let url = NSURL(string: URLCustomerCheck+strRegisterNumber)
let request = NSMutableURLRequest(url: url! as URL)
request.httpMethod = "GET"
request.addValue(authorization, forHTTPHeaderField: "Authorization")
let task = URLSession.shared.dataTask(with: request as URLRequest) { data,response,error in
if error != nil {
let alert = UIAlertController(title: "Error", message: error?.localizedDescription, preferredStyle: UIAlertControllerStyle.alert)
let okButton = UIAlertAction(title: "OK", style: UIAlertActionStyle.cancel, handler: nil)
alert.addAction(okButton)
self.present(alert, animated: true, completion: nil)
} else {
if data != nil {
if let client = try? Client.init(data: data){
client.result.customerList.forEach { (customer) in
print(customer.registerNumber)
}
}
}
}
}
task.resume()
}
let data = resultData
do {
guard let JSONResult = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.mutableContainers) as? [String : AnyObject],
let resultObject = JSONResult["Result"] as? [String : AnyObject],
let customerList = resultObject["CustomerList"] as? [Anyobject]
else { return }
// Loop the array of objects
for object in customerList {
let registerNumber = object["RegisterNumber"] as? String
let title = object["Title"] as? String
let alias = object["Alias"] as? String
let type = object["Type"] as? String
let firstCreationTime = object["FirstCreationTime"] as? String // Or as a DateObject
let aliasCreationTime = object["AliasCreationTime"] as? String // Or as a DateObject
}
let isEInvoiceCustomer = resultObject["ISEInvoiceCustomer"] as? Bool
} catch {
print(error)
}

Swift : football-data API doesn't work

I'm trying to use football-data.org api. I wrote some code same with before I did sample. But this api is using token and I didn't figured out how to add and do that.
I did these code and nothing happens :
func getData(){
let url = NSMutableURLRequest(URL: NSURL(string: "http://api.football-data.org/v1/soccerseasons/424/fixtures"))
url.addValue("my token is here", forHTTPHeaderField: "X-Auth-Token")
url.HTTPMethod = "GET"
let task = NSURLSession.sharedSession().dataTaskWithRequest(url) { (data, response, error) in
self.setLabels(data!)
}
task.resume()
}
func setLabels(MatchData: NSData){
//var jsonError: NSError?
do{
let json = try NSJSONSerialization.JSONObjectWithData(MatchData, options: NSJSONReadingOptions.MutableContainers) as! NSDictionary
if let sys = json["soccerseason"] as? NSDictionary{
if (sys["href"] as? String) != nil{
let seasonsUrl = sys["href"] as! String
print(seasonsUrl)
}
}
}
catch{
//error
}
}
I'm not getting value or something. I'm new with json. What's wrong in my code?
"soccerseason" is in "_links", like this:
if let links = json["_links"] as? [String:AnyObject],
sys = links["soccerseason"] as? [String:String],
seasonsUrl = sys["href"] {
print(seasonsUrl)
}
This is for the main one.
I've also noticed there's one (the same one, actually) in each dictionary in the main array:
if let fixtures = json["fixtures"] as? [[String:AnyObject]] {
for fixture in fixtures {
if let links = fixture["_links"] as? [String:AnyObject],
sys = links["soccerseason"] as? [String:String],
seasonsUrl = sys["href"] {
print(seasonsUrl)
}
}
}
The URLs are in the _links part in each dictionary in the fixtures array:
if let fixtures = json["fixtures"] as? [[String:AnyObject]] {
for fixture in fixtures {
if let links = fixture["_links"] as? [String:[String:String]],
season = links["soccerseason"],
seasonsUrl = season["href"],
awayTeam = links["awayTeam"],
awayTeamUrl = awayTeam["href"] {
print(seasonsUrl)
print(awayTeamUrl)
}
}
}
And awayTeamName and homeTeamName are at the same level than _links inside the array of dictionaries:
if let fixtures = json["fixtures"] as? [[String:AnyObject]] {
for fixture in fixtures {
if let awayTeamName = fixture["awayTeamName"] as? String,
homeTeamName = fixture["homeTeamName"] as? String {
print(awayTeamName)
print(homeTeamName)
}
}
}

How do I get values from a complex JSON object?

Is it possible that someone could show me how to get the names of these pizza places printing out? My application prints out the expected "Status Code: 200". However, my console only shows empty brackets []. I suspect that I am not pulling values from my JSON object properly.
I'm using this link for my API.
Link For API
Question
How can I properly fetch values from my serialized JSON object?
relevant code:
// Response
if let httpResponse = response as? NSHTTPURLResponse where httpResponse.statusCode == 200, let data = data {
print("Status Code: \(httpResponse.statusCode)")
do {
let json = try NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers)
if let pizzaPlaces = json["response"] as? [[String: AnyObject]] {
for place in pizzaPlaces {
if let name = place ["name"] as? String {
self.PizzaClass.append(name)
}
}
}
} catch {
print("Error Serializing JSON Data: \(error)")
}
print(self.PizzaClass)
}
}).resume()
You need to cast your NSJSONSerialization.JSONObjectWithData result as a [String:AnyObject].
let jsonObject = try NSJSONSerialization.JSONObjectWithData(returnedData, options: .MutableLeaves) as! [String: AnyObject]
Once you have that all you need to do is pay attention to what you're casting. Take the code below for an example. If we want to get our response object using jsonObject["response"] what kind of data structure do we have?
"response": {
"venues": [{
//... continues
}]
}
On the left we have "response" which is a string, on the right we have {} which is an AnyObject. So we have [String: AnyObject]. You just need to think about what object your dealing with piece by piece. Below is a working example that you can just paste into your application.
full working code:
func getJson() {
let request = NSMutableURLRequest(URL: NSURL(string: "https://api.foursquare.com/v2/venues/search?client_id=0F5M0EYOOFYLBXUOKTFKL5JBRZQHAQF4HEM1AG5FDX5ABRME&client_secret=FCEG5DWOASDDYII4U3AAO4DQL2O3TCN3NRZBKK01GFMVB21G&v=20130815%20&ll=29.5961,-104.2243&query=burritos")!)
let session = NSURLSession.sharedSession()
request.HTTPMethod = "GET"
request.addValue("application/json", forHTTPHeaderField: "Accept")
let task = session.dataTaskWithRequest(request) { (data: NSData?, response: NSURLResponse?, error: NSError?) -> Void in
guard let testResponse = response as? NSHTTPURLResponse else {
print("\(response)")
return
}
guard let status = HTTPStatusCodes(rawValue: testResponse.statusCode) else {
print("failed to unwrap status")
return
}
print(status)
switch status {
case .Created:
print("ehem")
case .BadRequest:
print("bad request")
case .Ok:
print("ok")
guard let returnedData = data else {
print("no data was returned")
break
}
do {
let jsonObject = try NSJSONSerialization.JSONObjectWithData(returnedData, options: .MutableLeaves) as! [String: AnyObject]
guard let response = jsonObject["response"] as? [String: AnyObject] else { return }
guard let venues = response["venues"] as? [AnyObject] else { return }
guard let location = venues[0]["location"] as? [String:AnyObject] else { return }
guard let formattedAddress = location["formattedAddress"] else { return }
print("response: \n\n \(response)\n------")
print("venues : \n\n \(venues)\n-------")
print("location : \n\n \(location)\n------")
print("formatted address : \n \(formattedAddress)")
} catch let error {
print(error)
}
// update user interface
dispatch_sync(dispatch_get_main_queue()) {
print("update your interface on the main thread")
}
}
}
task.resume()
}
place this either in its own file our outside of the class declaration,
enum HTTPStatusCodes : Int {
case Created = 202
case Ok = 200
case BadRequest = 404
}
Not that this was what you are looking for, but since you are new to Swift take a look at Alamofire. It handles JSON serialization for you. And when you need to chain calls PromiseKit is super slick.
Alamofire.request(.GET, url).responseJSON {response in
switch (response.result) {
case .Success(let value):
let pizzas = JSON(value).arrayValue
for place in pizzaPlaces {
if let name = place ["name"] as? String {
self.PizzaClass.append(name)
}
}
case .Failure(let error):
if let data = response.data, let dataString = String(data: data, encoding: NSUTF8StringEncoding) {
print("ERROR data: \(dataString)")
}
print("ERROR: \(error)")
}
}