iOS SWIFT - JSON object nil after NSJSONSerialization - ios

hello I am trying to read JSON as an array from the server not the dictionary. If I do this
let json: NSArray?
do {
json = try NSJSONSerialization.JSONObjectWithData(data!, options: .AllowFragments) as! NSArray
print("json is \(json)")
}
json object comes nil through this code and also I can't access the variable like this json["code"] I have tried this too
NSJSONSerialization.JSONObjectWithData(data!, options: .MutableLeaves) as! NSArray
But If I don't specify any type and let the variable as AnyObject
do {
let json = try NSJSONSerialization.JSONObjectWithData(data!, options: .AllowFragments)
print("json is \(json["code"]!)")
}
It works But 1st problem here is it prints Optional in the debugger but its least of my worries compared to that I can't do something like this
if json["code"] == 200 {
}
If I do this it says
Binary operator '==' cannot by applied to operands of type Anyobject? and Int
what I want is to get the data in NSArray. I don't want the json variable to be set as AnyObject. Inshort I want this code to work
let json: NSArray?
do {
json = try NSJSONSerialization.JSONObjectWithData(data!, options: .AllowFragments) as! NSArray
print("json is \(json)")
}
if its possible. I don't know what I am doing wrong here
json:
{"code":200,"msg":"login success"}

If printing json["code"] works, then json is doubtless a dictionary.
JSONObjectWithData returns AnyObject that is
"I-have-no-idea-what-it-is-but-it-is-some-kind-of-object-and-not-a-value" (thanks to gnasher729 for the more accurate paraphrase of AnyObject)
Since you know the object is [String:AnyObject] cast the type
let json = try NSJSONSerialization.JSONObjectWithData(data!, options: .AllowFragments) as! [String:AnyObject]
Then get the code – which is an Int – with
if json["code"] as! Int == 200 {
}
or with optional binding if the key is optional
if let code = json["code"] as? Int {
if code == 200 {
}
}

Related

Swift: Conversion from Swift 2 to Swift 3 Failing

I have been at this now for a few days. I am simply trying to convert my project from Swift 2 to Swift 3, and I have fixed quite a few errors.However, I am down to the last 19, all of which are the same error.Basically, I have a request to a server that returns a JSON object.That JSON object has nested objects in side of it.I have been Googling and reading for multiple hours trying different things and unfortunately, nothing has worked.Any help is appreciated.(Note: I have been asked on previous questions to post what I have already tried.I am not going to do that here because I have tried many different ways to fix this issue)
Error: Type 'Any' has no subscript members
if let response = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String:AnyObject] {
if let data = response["data"] as? NSArray {
for (index, item) in data.enumerated() {
let id = item["id"] as! String
}
}
}
Here are a few things I have tried:
if let response = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String:AnyObject] {
if let data = response["data"] as? NSArray {
for (index, item):[String:AnyObject] in data.enumerated() {
let id = item["id"] as! String
}
}
}
if let response = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String:AnyObject] {
if let data = response["data"] as? NSArray {
for (index, item) in data.enumerated() as? NSArray {
let id = item["id"] as! String
}
}
}
if let response = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String:AnyObject] {
if let data = response["data"] as? NSArray {
for item in data as? NSArray {
let id = item["id"] as! String
}
}
}
None of the above fixed the issue. Here are the SOF questions I have referenced:
type any? has no subscript members
Facebook SDK error for subscript members
Type 'Any' has no subscript members (firebase)
Type 'Any' Has no Subscript Members in xcode 8 Swift 3
Type 'Any' has no subscript members after updating to Swift 3
Any help is greatly appreciated!
Hello You can try this:
if let response = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? NSDictionary {
if let data = response["data"] as? [AnyObject] {
for (index, item) {
if let id = item["id"] as! String{
print(id)
}
}
}
}
or
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
if let parseJson = json {
if let data = parseJson["data"] as? [AnyObject] {
for signleArray in data {
if let customObjects = signleArray as? [AnyObject] {
for customObject in customObjects {
let userId = customObject["id"] as! String
}
}
}
}
}
Just let me know how it worked, so we can figure it out :)
Alright, so after some help from 0ndre_, here is how I fixed the problem. Unfortunately, it wasn't a complete fix for multidimensional / nested objects so I had to modify my API that the application is calling.
if let response = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary {
if let data = response["data"] as? [AnyObject] {
for (index, item) in data.enumerated() {
let id = item["id"] as! String
}
}
}
You should not cast to NSArray, but [AnyObject]. When enumerating an NSArray, in Swift 3 that id item type maps to Any, which indeed doesn't have a subscript. You'll find more info on Any vs AnyObject in the standard library documentation, and the rationale for this Swift 3 change for treating id as Any vs AnyObject is explained in this Swift evolution proposal.
let inputData:Data = "{\"data\": [{\"id\":\"x\"},{\"id\":\"y\"},{\"id\":\"z\"}]}".data(using: .utf8)!
if let response = try JSONSerialization.jsonObject(with: inputData, options: []) as? [String:AnyObject],
let responseData = response["data"] as? [AnyObject]
{
for (index, item) in responseData.enumerated()
{
let id = item["id"] as! String
print(id)
}
}
Regarding using 3rd party libraries such as SwiftyJSON for JSON parsing, you may not like 3rd party modules, but your code above will crash on bad input (if item doesn't have key "id" or if it's not a string). It's of course not like libraries like SwiftyJSON or Freddy are the only way to write robust JSON parsing code in Swift, but the Cocoa provided facilities certainly are not using the Swift type system to the benefit of the programmer and it's easy to be complacent to bad input with that API, the more complex the parsing task (which in all likelihood shouldn't really crash your program, but should be reported as an error).

Swift parse JSON formatting

I created an API that has a JSON response in the following format:
[{"name":"xxx","direct_link":"http:\/\/domain.com\/images\/xxx.png","image":"http:\/\/domain.com\/images\/xxx.png"},{"name":"yyy","direct_link":"http:\/\/domain.com\/images\/yyy.png","image":"http:\/\/domain.com\/images\/yyy.png"}]
Notice how the JSON response has no array title.
My Swift code looks like this:
do {
//converting resonse to NSDictionary
var teamJSON: NSDictionary!
teamJSON = try NSJSONSerialization.JSONObjectWithData(data!, options: .MutableContainers) as? NSDictionary
//getting the JSON array teams from the response
let teams: NSArray = teamJSON["teams"] as! NSArray
//looping through all the json objects in the array teams
for i in 0 ..< teams.count{
//getting the data at each index
let teamId:Int = teams[i]["name"] as! String!
let teamName:String = teams[i]["direct_link"] as! String!
let teamMember:Int = teams[i]["image"] as! Int!
//displaying the data
print("name -> ", teamId)
print("direct_link -> ", teamName)
print("image -> ", teamMember)
print("===================")
print("")
}
Notice how the array is looking for a title "teams". How can I ensure the JSON is properly parsed and displays the 3 values I need for the JSON response? I'm new to app coding and have a web background, still trying to wrap my head around this.
As it stands when I try to build and run I get the following error: fatal error unexpectedly found nil while unwrapping an optional value
Try this:
do {
guard let teams = try NSJSONSerialization.JSONObjectWithData(data!, options: .MutableContainers) as? NSArray else {
//Doesn't exist, or isn't an NSArray
return
}
for team in teams {
//getting the data at each index
let teamId = team["name"] as! String
let teamName = team["direct_link"] as! String
let teamMember = team["image"] as! Int
//displaying the data
print("name -> ", teamId)
print("direct_link -> ", teamName)
print("image -> ", teamMember)
print("===================")
print()
}
}
//...
Some notes:
Don't cast to implicitly unwrapped optionals (e.g. String!)
Don't add unnecessary type annotations
Use guard let to enforce preconditions (e.g. the JSON isn't nil, and is castable to NSArray).
Prefer iterating over elements of an Array (for team in teams) over iterating a range (for i in 0..<teams.count)
If you need only the indices, use for i in teams.indices
If you need the indices and elements, use for (index, team) in teams.enumerate()
The problem is this line:
teamJSON = try NSJSONSerialization.JSONObjectWithData(data!, options: .MutableContainers) as? NSDictionary
because you are trying to cast a JSON array to a dictionary.
Just use
teamJSON = try NSJSONSerialization.JSONObjectWithData(data!, options: .MutableContainers) as? NSArray
And then you also won't need to do this:
//getting the JSON array teams from the response
let teams: NSArray = teamJSON["teams"] as! NSArray
Because teamJSON is already the array.
I think the key issue you have to be aware of is that NSJSONSerialization.JSONObjectWithData() can return either an NSDictionary OR an NSArray, depending on whether the root level data in the JSON is an array or a dictionary.
ISSUE:
Your NSJSONSerialization.JSONObjectWithData would return an NSArray and not NSDictionary
SOLUTION:
Update code as below:
var teamJSON = try NSJSONSerialization.JSONObjectWithData(data!, options: .MutableContainers) as? NSArray
//getting the JSON array teams from the response
let teams: NSArray = teamJSON
ALSO
Don't force cast values using !
Always cast it as optional using ?

Swift JSON error : Could not cast value of type '__NSDictionaryM' to 'NSArray'

when decoding JSON from webservice(API) i get error :
Could not cast value of type '__NSDictionaryM' (0x1037ad8a8) to 'NSArray' (0x1037ad470).
My Code :
var kGetURL = "http://bitnami.local/cscart_demo/api/users"
//var kGetURL = "http://localhost/fendy/getjson.php"
var json : Array<AnyObject> = []
override func viewDidLoad() {
super.viewDidLoad()
start()
}
func getData(data : NSData){
//error at this line :
json = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil) as! Array<AnyObject>
//error
tableView.reloadData()
}
func start(){
var url : NSURL = NSURL(string: kGetURL)!
var data : NSData = NSData(contentsOfURL: url)!
getData(data)
}
if i change url to http://localhost/fendy/getjson.php, its working so nice.
i get error if my url is http://bitnami.local/cscart_demo/api/users
Response from webservice http://localhost/fendy/getjson.php :
[{"id":"17","Name":"KFC","Message":"awesome"},
{"id":"18","Name":"McDonald","Message":"good"},
{"id":"23","Name":"Burger King","Message":"tasty"},
{"id":"38","Name":"Pizza hut","Message":"yummy"},
{"id":"39","Name":"Steak","Message":"very Delicious"}]
Response from webservice http://bitnami.local/cscart_demo/api/users :
{"users":
[{"user_id":"4","user_login":"user_4","is_root":"N","timestamp":"1441608048","user_type":"C","status":"A","firstname":"","lastname":"","email":"fendy.w#mvig.net","company":"","company_id":"1","company_name":"Simtech"},
{"user_id":"3","user_login":"customer","is_root":"N","timestamp":"1441604240","user_type":"C","status":"A","firstname":"Customer","lastname":"Customer","email":"customer#example.com","company":"Simtech","company_id":"1","company_name":"Simtech"},
{"user_id":"1","user_login":"admin","is_root":"Y","timestamp":"1441604240","user_type":"A","status":"A","firstname":"John","lastname":"Doe","email":"robby#mvig.net","company":"Your company","company_id":"0","company_name":null}],
"params":{"page":1,"items_per_page":"10","sort_order":"asc","sort_by":"name","sort_order_rev":"desc","total_items":"3"}}
i think it's Style is same, but why not working with url http://bitnami.local/cscart_demo/api/users . anyone can help?
The bitnami response starts with a { and it is therefore a JSON object, which corresponds to an NSDictionary. The other one starts with [ which indicates an array.
You need to change the type of json to Dictionary<String, AnyObject>, and deserialize as follows:
json = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil) as! Dictionary<String, AnyObject>
Your method is casting JSON result to an array. It works fine with the URL that returns an array represented as JSON, but it does not work with the URL that returns a dictionary, not an array, represented as JSON.
Although the "style" of returned values looks the same, the second one is a one-item dictionary. What you probably want is to extract the "users" element from it, which is an array.
If you do not know which of the two URLs you are getting, you could try both styles with as? cast instead of as!:
let tmp : AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil)
if let arr = tmp as? Array<AnyObject> {
json = arr
} else if dict = tmp as? [String: AnyObject] {
json = dict["users"] as! Array<AnyObject>
} else {
// Handle an error: the input was unexpected
}
tableView.reloadData()
try cache for Serialization
do {
if let json = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String : Any] { // as? data type
if let otherDict = json["dataKey"] as? [String : Any] {
//do something
}
}
} catch {
// can't json serialization
}

Check for http response format

I am making http call and receiving JSON response
var jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &err) as! NSDictionary
But when not receiving actual json this line fail with error cannot cast value of NSArray to NSDictionary
I understand why this happen my question is how to properly check what format is the response
I do like this in my api's:
typealias JSONType = (dictionary: [String:AnyObject]?, array: [AnyObject]?)
var error:NSError?
var json:AnyObject? = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: &error)
if let dict = json as? [String:AnyObject] {
return (dict, nil)
}
else if let arr = json as? [AnyObject] {
return (nil, arr)
}
So when I return that JSONType I can change to use it as a dictionary or array by just:
json.array

Swift parsing json doesn't work

I want to parse a json file, this is in my json file:
{
"currentPowerByClient": 0, <- i want to read this
"currentPowerToClient":518,
"tariff":1,
"totalGasDelivered":1061.004,
"totalPowerByClientHigh":10.704,
"totalPowerByClientLow":23.042,
"totalPowerToClientHigh":912.221,
"totalPowerToClientLow": 693.499
}
this is my swift code, the object called JSONResult contains my JSON code
var jsonResult: NSDictionary = NSJSONSerialization.JSONObjectWithData(data, options:
NSJSONReadingOptions.MutableContainers, error: nil) as NSDictionary
if let item = jsonResult as NSDictionary? {
if let currentPowerByClient = item["currentPowerByClient"] as? NSDictionary {
println(currentPowerByClient)
}
}
when i run it, it doesn't print anything
The line
if let currentPowerByClient = item["currentPowerByClient"] as? NSDictionary
should be
if let currentPowerByClient = item["currentPowerByClient"] as? NSNumber
because item["currentPowerByClient"] is expected to be a number, not a dictionary. it works then

Resources