Swift 3 error: Type 'Any' has no subscript members - swift

So I know this question has been asked and answered numerous times before, but I just migrated my project to Swift 3 and Im getting a ton of these errors in my code that parses JSON and I couldn't quite find answers that made me understand how to resolve my specific issue.
guard let result = try JSONSerialization.jsonObject(with: data!, options: []) as? [String:AnyObject] else {
return
}
guard let responseData = result["Data"] else { return }
guard let userData = responseData["UserProfile"] else { return }
var userProfileFieldsDict = [String: String]()
if let sessionToken = userData!["CurrentSessionToken"] as? NSString {
userProfileFieldsDict["sessionToken"] = String(sessionToken)
}
}
The if let sessionToken line throws the aforementioned error, but not quite sure how you're supposed to deal with this in Swift 3? Could someone explain and suggest a best practice fix?
Thanks a bunch!

If responseData["UserProfile"] is also a dictionary you'll probably want to cast it as such in you guard by saying guard let userData = responseData["UserProfile"] as? [String : AnyObject] else { return }. I suspect this will solve your problem.
As a small aside, you don't need to force unwrap userData in your if let, because you've already unwrapped it in the guard.

Related

does swift 4 support exception handling?

My Swift program is crashing, to handle the crash is their any way, i tried Exception handling i didn't got it, Does swift 4 supports Exception handling, what i want to do is i don't want my app to be crashed
here is the code i want to catch when ever i don't got the price value
let srt = self.Coin[indexPath.row] as! NSDictionary
var sr2=NSDictionary()
var barr = NSArray()
if srt.value(forKey: "Price") != nil{
do{
barr = try srt.value(forKey: "Price") as! NSArray
if (barr.count>0) {
sr2 = barr[0] as! NSDictionary
}
} catch {
print("Price is empty")
}
}
Not all errors can be caught in Swift. do-catch blocks can only handle errors thrown by throwable functions, whose declarations are marked using the throws keyword and errors that are explicitly thrown from inside those functions using the throw keyword.
All other errors in Swift are uncatchable, especially runtime errors caused by force unwrapping, force casting or force try (try!). When you use ! to force something, you tell the compiler that you know what you are doing and that a runtime error should be thrown in case an error occurs.
You shouldn't use force unwrapping/casting unless you are absolutely sure they won't fail or if you want to catch programming errors early in the development stage. Instead of using useless do-catch blocks with nested forced statements, simply use optional binding to safely cast/unwrap optional values.
Moreover, don't use NSDictionary and NSArray in Swift, use their native equivalents, Dictionary and Array. It seems like you are parsing JSON, so use [String:Any] instead of NSDictionary and [[String:Any]] instead of NSArray.
if let srt = self.Coin[indexPath.row] as? [String:Any] {
if let barrs = srt["Price"] as? [[String:Any]], let barr = barrs.first {
let sr2 = barr
}
As David said, I'd avoid force unwrapping and force casting.
But instead of an if let statement, I'd rather use a guard let in order to avoid the pyramid of doom, like this:
guard let srt = self.Coin[indexPath.row] as? [String:Any], let barr = srt["Price"] as? [[String:Any]], let sr2 = barr.first as? [String:Any] else {
print("Price is empty")
//Need to return, throw or whatever
}
And we can even remove your intermediate variables, but it's much less readable, like so:
guard let sr2 = ((self.Coin[indexPath.row] as? [String : Any])?["Price"] as? [[String : Any]])?.first as? [String : Any] else {
print("Price is empty")
//Need to return, throw or whatever
}
If you'd rather go with the if let statement (in case you don't want to return or throw in your guard statement's else clause) you can also do it with a single if statement, like that
if let srt = self.Coin[indexPath.row] as? [String:Any], let barr = srt["Price"] as? [[String:Any]], let sr2 = barr.first {
//Do something with sr2
}
And a shorter version without intermediate variables:
if let sr2 = ((self.Coin[indexPath.row] as? [String:Any])?["Price"] as? [[String:Any]])?.first {
//Do something with sr2
}
Whatever you choose... Don't force unwrap. Don't force try. Don't force cast. It will come back to bite you some day.

Getting nil value when read JSON data in Swift

if let postString = NSString(data:data!, encoding: NSUTF8StringEncoding) as? String {
guard let jsonData = postString.dataUsingEncoding(NSASCIIStringEncoding) else {
fatalError()
}
guard let jsonObjects = try? NSJSONSerialization.JSONObjectWithData(jsonData,options: [])
,let JSONArray = jsonObjects as? [[String: AnyObject]]
else {
fatalError()
}
print(JSONArray)
}
In postString constant, I am getting "[{\"Name\":\"ABC\",\"Age\":35},{\"Name\":\"CDE\",\"Age\":36‌​}]"
and when I run this code then fatalError() code call.
A couple of other people have explained what you did wrong in this case (Tried to cast the output of deserialization to a dictionary when it actually contains an array.)
Stepping back from the details, when something fails, you need to break your code into smaller pieces and then trace through it to see what's failing.
The "as?" cast says "Try to cast this object to another type. Wrap the results in an Optional." If the cast fails, the result is nil. If it succeeds, the optional contains the new type.
If you rewrote your code as:
let jsonObject = try NSJSONSerialization.JSONObjectWithData(data!,
options: NSJSONReadingOptions.AllowFragments)
guard let jsonDict = jsonObject as [String: Any] else {
print("Cast failed")
return
}
Then the print statement would fire and you'd know that the cast was the problem.
EDIT:
I just noticed that your JSON data contains an array of dictionaries of type [String: Int]. In Swift Ints are not an Object type, so you need to cast your results to [[String:Any]], not [[String:AnyObject]]. I've fixed my code above.
I Wrote the following code in a playground and it works:
let jsonString = "[{\"Name\":\"ABC\",\"Age\":35},{\"surveyName\":\"CDE\",\"Age\":36}]"
guard let jsonData = jsonString.data(using: .ascii) else {
fatalError()
}
guard let jsonObjects = try? JSONSerialization.jsonObject(with: jsonData, options: []),
let JSONArray = jsonObjects as? [[String: Any]]
else {
fatalError()
}
print(String(describing: jsonObjects))
It gives the output:
(
{
Age = 35;
Name = ABC;
},
{
Age = 36;
surveyName = CDE;
}
)
Which is what I would expect.
EDIT #2:
Actually, on further investigation I'm stumped as to why your code isn't working. I just tested it, and the as [[String: AnyObject]] works. It turns out that in Swift 3 if you cast an Int to AnyObject and you've included Foundation (or UIKit) then it gets silently converted to an NSNumber, which IS an Object type.
You're going to need to show your actual JSON data and the code that converts it to an object if you need help debugging it.
EDIT #3:
Below is code I wrote and tested in Swift 2.3:
func parseJSONTest() {
let jsonString = "[{\"Name\":\"ABC\",\"Age\":35},{\"surveyName\":\"CDE\",\"Age\":36}]"
guard let jsonData = jsonString.dataUsingEncoding(NSASCIIStringEncoding) else {
fatalError()
}
//I'm not sure why you take JSON data, convert it to a string, and convert
//It back to NSData, but to prove a point, this is your code being fed
//well-formed JSON data in an NSData object:
if let postString = NSString(data:jsonData,
encoding: NSASCIIStringEncoding) as? String {
guard let jsonData = postString.dataUsingEncoding(NSASCIIStringEncoding) else {
fatalError()
}
guard let jsonObjects = try? NSJSONSerialization.JSONObjectWithData(jsonData,options: []),
let JSONArray = jsonObjects as? [[String: AnyObject]]
else {
fatalError()
}
print(JSONArray)
}
}
In order to provide a complete test I first take a string containing JSON data and convert it to NSData. I then convert that NSData back to JSON objects and cast them to the desired type, and it works. The code above displays:
[["Name": ABC, "Age": 35], ["surveyName": CDE, "Age": 36]]
Which matches the structure you have (an array of dictionaries).
The above is technically not an json object it is a json array.
Try casting as [AnyObject] instead of [String: AnyObject]
Then use the array to access the item you need. Then cast that to
[String:AnyObject]
because they are json objects within a json array

How To Update A Label In Swift 3 With JSON Data Inside Of A Function?

For some reason whenever I try to update my label with the current temperature using self.infoLabel.text = String(temp!) inside of the DispatchQueue code block, I get the following fatal error message:
unexpectedly found nil while unwrapping an Optional value.
I'd appreciate if someone could help me figure out why the code below isn't working. Thanks.
func getCurrentTemp(city: String){
let weatherRequestURL = URL(string: "\(openWeatherMapBaseURL)?APPID=\(openWeatherMapAPIKey)&q=\(city)")!
// The data task retrieves the data.
URLSession.shared.dataTask(with: weatherRequestURL) { (data, response, error) in
if let error = error {
// Case 1: Error
print("Error:\n\(error)")
}
else {
//print("Raw data:\n\(data!)\n")
//let dataString = String(data: data!, encoding: String.Encoding.utf8)
//print("Human-readable data:\n\(dataString!)")
do {
// Try to convert that data into a Swift dictionary
let weather = try JSONSerialization.jsonObject(with: data!, options:.allowFragments) as! [String:AnyObject]
if let main = weather["main"] as? [String: Any] {
let temp = main["temp"] as? Double
print("temp\(temp!)")
DispatchQueue.main.sync(execute: {
self.infoLabel.text = String(temp!)
})
//return temp as? String
//let temp_max = main["temp_max"] as? Double
//print("temp\(temp_max!)")
//let temp_min = main["temp_min"] as? Double
//print("temp\(temp_min!)")
}
}
catch let jsonError as NSError {
// An error occurred while trying to convert the data into a Swift dictionary.
print("JSON error description: \(jsonError.description)")
}
}
}
.resume()
}
There are two possibilities here: 1) either temp is nil (and it shouldn't be because you already force unwrap it in the print statement above) 2) or infoLabel is nil which happens if you broke your outlet connection.
Its easy to check; make a breakpoint above your assignment and in the debug console you can type:
po self.infoLabel
to see if its nil. For good measure you an also check temp.
You can also add a print statement to check self.infoLabel or an assert.
Alright, so I found a makeshift solution to this issue (See Below). Rather than placing the code inside of the function I made, I placed it in the viewDidLoad() function. For whatever reason, self.infoLabel? would be nil anywhere inside of the function I made.
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
print("Sucessful launched weather page.")
let weatherRequestURL = URL(string: "\(openWeatherMapBaseURL)?APPID=\(openWeatherMapAPIKey)&q=\(city)")!
// The data task retrieves the data.
URLSession.shared.dataTask(with: weatherRequestURL) { (data, response, error) in
if let error = error {
// Case 1: Error
print("Error:\n\(error)")
}
else {
//print("Raw data:\n\(data!)\n")
//let dataString = String(data: data!, encoding: String.Encoding.utf8)
//print("Human-readable data:\n\(dataString!)")
do {
// Try to convert that data into a Swift dictionary
let weather = try JSONSerialization.jsonObject(with: data!, options:.allowFragments) as! [String:AnyObject]
if let main = weather["main"] as? [String: Any] {
let temp = main["temp"] as? Double
print("temp\(temp!)")
var tempInFarenheit = ((9/5)*((temp!)-273) + 32).rounded()
DispatchQueue.main.sync(execute: {
self.infoLabel.text = "\(tempInFarenheit) + °"
})
}
}
catch let jsonError as NSError {
// An error occurred while trying to convert the data into a Swift dictionary.
print("JSON error description: \(jsonError.description)")
}
}
}
.resume()
}
Although this isn't the most effective way of doing things, hopefully it can help others who are having the same problem. If I find a more effective way of doing this, I'll be sure to edit this post and include it.

Swift 3.0 - Core Data / Unexpectedly found nil

I am not able to figure this one out by my self. I am retrieving some settings stored in Core Data, and print these setting to some UITextFields. This works fine in another VC in the same project but here I get "unexpexpectedly found nil while unwrapping optional value".
I XCode I can see that the values are there? Why do I get this crash?
Please see attached screenshot.
This is the current code I am down to now. Still the same error message in XCode
func getSettingsFromCoreData() {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "DeathMatchSettings")
do{
let results = try context.fetch(request)
let managedObject = results as! [NSManagedObject]
let getDMSettings = managedObject[0]
guard let playerOne = getDMSettings.value(forKey: "playerOne") else {
return
}
print(playerOne)
txtPlayerOne.text = String(describing: playerOne)
}catch{
fatalError("Error in retreiving settings from CoreData")
}
}
Player1 can be nil. You are trying to force downcast it to a value, but it is a fatal error in swift. Use an if let statement to test the value:
if let playerOne = getDMSSettings.value(forKey: "playerOne") as? String {
print(playerOne)
txtPlayerOne.text = playerOne
}
Read more about type casting in docs:
https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/TypeCasting.html
You can also use guard statement to unwrap your optional variable. It is better to use forced unwrapping only if you are confident that variable has non-optional value.
do {
guard let playerOne = getDMSettings.value(forKey:"playerOne") else {
return
}
print(playerOne)
txtPlayerOne.text = playerOne
}

Combine Multiple NSMutableDictionary

I have a recursive function that's adding the previous dictionary to the new dictionary but using addEntriesFromDictionary gives me error
Here's my sample code (EDITED)
func fetchAllSWApiList(strUrl: String, strArrayCallback: NSMutableDictionary){
let swApiHandler = SwApiHandler()
let url = strUrl
swApiHandler.requestSWPApi(url, completionHandler: {(response, error) in
if let responseDictionary = response as? Dictionary<String, AnyObject> {
print("Did Detect a dictionary")
strArrayCallback.addEntriesFromDictionary(responseDictionary)
}
guard let nextPage: String = response!["next"]! as? String else {
print("End Of Page")
print(strArrayCallback.description)
return
}
print(nextPage)
self.fetchAllSWApiList(nextPage, strArrayCallback: strArrayCallback)
})
}
If you have a better solution in my code that will return the combined list from the fetched data recursively the better.
EDIT: here's the error
NSDictionary is not implicitly convertible to '[NSObject: AnyObject]';
did you mean to use 'as' to explicitly convert?
after that my code will become should I leave it as it is or edit the NSpbject part?
strArrayCallback.addEntriesFromDictionary(response! as [NSObject : AnyObject])
EDIT2:
incase you're wondering what's the JSON model looks like here's a pic of it
You should cast your response to the Dictionary type that you're expecting, by doing something like:
if let responseDictionary = response as? Dictionary<String, AnyObject> {
strArrayCallback.addEntriesFromDictionary(responseDictionary)
}