Swift: Conversion from Swift 2 to Swift 3 Failing - ios

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).

Related

Swift JSON extraction

I am developing a swift iOS app and getting the following JSON response from web service. I am trying to parse and get nextResponse from it. I am unable to extract it. Could someone guide me to solve this?
listofstudents:
({
studentsList = (
{
data = (
"32872.23",
"38814.87",
"38915.85"
);
label = “name, parents and guardians”;
}
);
dateList = (
"Apr 26, 2017",
"Jun 10, 2017",
"Jul 26, 2017"
);
firstResponse = “This school has 1432 students and 387 teachers.”;
nextResponse = “This school has around 1400 students.”;
})
Swift code:
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
print("json: \(json)")
if let parseJSON = json {
let finalResponse = parseJSON["listofstudents"] as? AnyObject
print("listofstudents:: \(finalResponse)")
let nextResponse = parseJSON["nextResponse"] as? AnyObject
print("nextResponse:: \(nextResponse)")
}
} catch {
print(error)
}
Don't use NSDictionary in Swift, but use its native Swift counterpart, Dictionary. This is how you access dictionaries embedded inside other dictionaries:
do {
guard let json = try JSONSerialization.jsonObject(with: data!) as? [String:Any] else {return}
print("json: \(json)")
guard let finalResponse = parseJSON["listofstudents"] as? [String:Any] else {return}
print("listofstudents:: \(finalResponse)")
guard let nextResponse = finalResponse["nextResponse"] as? [String:Any] else {return}
print("nextResponse:: \(nextResponse)")
} catch {
print(error)
}
nextResponse is part of the JSON structure (it's a nested node). So you should access it using:
typealias JSON = [String: Any]
if let finalResponse = parseJSON["listofstudents"] as? JSON {
let nextResponse = finalResponse ["nextResponse"] as? JSON
print("nextResponse:: \(nextResponse)")
}
Looks like your listofstudents is an array of dictionary so try to iterate it and extract it:-
if let finalResponse = parseJSON["listofstudents"] as? [String: Any] {
//If your finalResponse has list then you can print all the data
for response in finalResponse {
let nextResponse = finalResponse ["nextResponse"] as? AnyObject
print("nextResponse::\(nextResponse)")
}
}

Value of optional type 'NSDictionary??' not unwrapped

I've been following a tutorial that was playing around with API, specifically the openweathermap api, and I ran into a problem and xCode gives me options to "Fix it with ! and ??", which unfortunately does not fix the issue either.
if let urlContent = data {
do {
let jsonResult = try JSONSerialization.jsonObject(with: urlContent, options: JSONSerialization.ReadingOptions.mutableContainers) as? [String: Any]
print(jsonResult)
print(jsonResult?["name"])
if let description = ((jsonResult?["weather"] as? NSArray)?[0] as? NSDictionary)?["description"] as? String {
print(description)
}
} catch {
print("JSON Processing Fail")
}
Im getting an error on if let description = ((jsonResult?["weather"] as? NSArray)?[0] as? NSDictionary)?["description"] as? Stringas a Value of optional type 'NSDictionary??' not unwrapped Error.
Simply use Swift's native type Array instead of NSArray.
do {
if let jsonResult = try JSONSerialization.jsonObject(with: urlContent, options: []) as? [String: Any] {
if let weatherArray = jsonResult["weather"] as? [[String:Any]],
let dic = weatherArray.first, let description = dic["description"] as? String {
print(description)
}
}
} catch {
print("JSON Processing Fail")
}

Type 'Any' doesn't conform to protocol 'Sequence'

I am having troubles in my codes, when trying to parse JSON data (each data of an array, like how it should be done) and trying to set up the for in loop, the error comes out. Here's my code
if let jsonDataArray = try? JSONSerialization.jsonObject(with: data!, options: [])
{
print(jsonDataArray)
var allStops = [busStops]()
for eachData in jsonDataArray
^
//this is where the error is located
{
if let jsonDataDictionary = eachData as? [String : AnyObject]
{
let eachStop = busStops(jsonDataDictiony: jsonDataDictionary)
}
}
}
Specify the type of jsonDataArray to directly [[String: Any]] and try like this.
if let jsonDataArray = try? JSONSerialization.jsonObject(with: data!, options: []) as? [[String: Any]] {
for eachData in jsonDataArray {
let eachStop = busStops(jsonDataDictiony: jsonDataDictionary)
}
}

Can't resolve "Ambiguous use of subscript"

I'm trying to convert a JSON response (from an NSUrlSession) into an array that I can use.
It's weird, this was working last night. However I now have a build error saying "ambiguous use of subscript".
let url = NSURL(string: "http://192.168.0.8/classes/main.php?fn=dogBoardingGet")
let task = NSURLSession.sharedSession().dataTaskWithURL(url!) {(data, response, error) in
print(NSString(data: data!, encoding: NSUTF8StringEncoding))
//var boardings = [String]()
do {
let json = try NSJSONSerialization.JSONObjectWithData(data!, options: .AllowFragments)
if let theDogs = json[0] as? [[String: AnyObject]] {
for dog in theDogs {
if let ID = dog["ID"] as? String {
print(ID + " Safe")
let thisDog = Dog(name: (dog["Name"] as? String)!, surname: (dog["Surname"] as? String)!, id: (dog["ID"] as? String)!, boarding: true)
let newIndexPath = NSIndexPath(forRow: self.dogs.count, inSection: 0)
self.dogs.append(thisDog)
self.tableView.insertRowsAtIndexPaths([newIndexPath], withRowAnimation: .Bottom)
}
}
}
} catch {
print("error serializing JSON: \(error)")
}
// print(names) // ["Bloxus test", "Manila Test"]
}
task.resume()
The error is on this line: if let theDogs = json[0] as? [[String: AnyObject]] {.
From what I could tell while looking other questions, the error is because of AnyObject, so I tried to change it to [String: String]but I still get the same error.
Can anyone see the cause of this error?
Extra Information
The JSON response being received from the server:
[[{"ID":"47","Name":"Sparky","Surname":"McAllister"}]]
Looks like you are using the NSJSONSerialization, however you don't say what type of object you expect ( [AnyObject] or [String : AnyObject] ). They error you are getting is due to the fact you haven't casted to the json to [AnyObject].
PS: You might consider not using a force unwrap for the data (data!)
let json = try NSJSONSerialization.JSONObjectWithData(data!, options: .AllowFragments) as! [AnyObject]

iOS SWIFT - JSON object nil after NSJSONSerialization

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

Resources