crash when objectForKey("Id") Id is not there? - ios

Im using alamofire for http requests in my iOS app. When i access a value in response like following in Swift it get crash if key is not there or when getting a null value.
var id = (response.result.value?[p-1].objectForKey("Id"))! as? Int
In general how can we check for a value which is not exist in Swift?

You should try this
if let id = (response.result.value?[p-1].objectForKey("Id"))? as? Int {
// some stuff
}
Your application is crashing because you unwrapped nil object: .objectForKey("Id")

You have to unwrap your optionals without forcing it. In your case, you should also think about checking whether response.result.value has at least p-1 elements:
if let value = response.result.value
{
if p-1 < value.count
{
if let id = value[p-1].objectForKey("Id") as? Int
{
// Do whatever you want with id
}
}
}
You can also use guard statement that I personally find clearer:
guard let value = response.result.value
else { return }
guard p-1 < value.count
else { return }
guard let id = value[p-1].objectForKey("Id") as? Int
else { return }
// Do whatever you want with id

Related

[NSNull length]: unrecognized selector sent to instance 0x10f8c6fc0' swift 4 iOS

I am fetching data from the model in this way
if let birthdate = personInfo?.str_Birthdate {
cell.dobTF.text = birthdate
}
But app crash and return this error
'-[NSNull length]: unrecognized selector sent to instance 0x10f8c6fc0'
What you are getting here is NSNull. It is an object representing null in context of objective-C arrays and dictionaries. Specifically in JSONs it distinguishes between receiving field (null) instead of not receiving a field at all. In your case I assume this value is force-unwrapped as a string so the error is a bit late. Please try the following:
if let dateObject = personInfo?.str_Birthdate {
if let nullObject = dateObject as? NSNull {
// A field was returned but is (null)
} else if let stringObject = dateObject as? String {
cell.dobTF.text = stringObject
} else {
// Unknown object
}
} else {
// This field is missing
}
You can actually just convert all NSNull instances to nil using something like:
func removeNSNullInstancesFrom(_ item: Any?) -> Any? {
guard let item = item else { return nil }
if let _ = item as? NSNull {
return nil
} else if let array = item as? [Any] {
return array.compactMap { removeNSNullInstancesFrom($0) }
} else if let dictionary = item as? [String: Any] {
var newDict: [String: Any] = [String: Any]()
dictionary.forEach { item in
guard let value = removeNSNullInstancesFrom(item.value) else { return }
newDict[item.key] = value
}
return newDict
} else {
return item
}
}
You can use this on the whole response object or a specific item. In your case you can do: cell.dobTF.text = removeNSNullInstancesFrom(birthdate).
But this method should in general recursively remove all NSNull instances from fields, arrays and dictionaries which are standard for JSON.

How to access dictionary value by key inside array of dictionary on Swift 3?

I've been away for a while from swift development. And today I got bug report from my client on my old project. The thing is I used to access value from my dictionary with ease just like dictionary[key] then I already got the value, but today I got below error by doing that:
Type 'NSFastEnumerationIterator.Element' (aka 'Any') has no subscript members
and here is my code:
var rollStatusArray = NSMutableArray()
for statusDict in rollStatusArray{
if statusId == "\(statusDict["mark_status_id"]!!)"{
return "\(statusDict["mark_status_name"]!!)"
}
}
How can I access dictionary value on swift 3?
In Swift's use native type Array/[] instead of NS(Mutable)Array.
if let dicArray = rollStatusArray.objectEnumerator().allObjects as? [[String:Any]] {
for statusDict in dicArray {
//Here compiler know type of statusDict is [String:Any]
if let status_id = statusDict["mark_status_id"] as? String, status_id == statusId {
return "\(statusDict["mark_status_name"]!)"
}
}
}
Note I'm force wrapping the mark_status_name value you can use Nil coalescing operator to return empty string to remove force wrapping.
You need to tell compiler the type of your statusDict this way:
for statusDict in rollStatusArray {
if let obj = statusDict as? [String: AnyObject] {
let statusIdFromDict = obj["mark_status_id"] as? String ?? ""
if statusId == statusIdFromDict {
return statusIdFromDict
}
}
}
Using Swift 3, you can access the dictionary value the following way:
var rollStatusArray = NSMutableArray()
for statusDict in rollStatusArray{
if statusId == "\((statusDict as! NSDictionary)["mark_status_id"]!)"{
return "\((statusDict as! NSDictionary)["mark_status_name"]!)"
}
}

How to parse Data from server? App getting crash on read of data from dictionary

Here is my JSON data which I am getting from server.
{
"result": "SUCCESS",
"resultcode": "000",
"balance": "-32020",
"available": "-32020",
"reserved": 0
}
When I am trying to parse these JSON data, App crashed and throws Could not cast value of type '__NSCFNumber' (0x1a17dab60) to 'NSString' (0x1a17e5798).
I know the issue is about data type. But when I get Positive value for reserved key in above JSON data, it shows string value in JSON data, but when i get Negative value of reserved key, it return Numeric data type.
Here is the code Which i am reading data.
self.response?.objectForKey("reserved") as! NSString
So How to deal with this kind of issue?
You can parse data using if let statements. Check below
if let reservedNum = JSON["reserved"] as? Double {
print(reservedNum)
} else if let reservedString = JSON["reserved"] as? String {
print(reservedString)
} else {
print("Error Parsing Data")
}
if let safeResult = JSON["result"] as? String {
print(safeResult)
}
And same for the rest and handle the response in a Modal.
What about something like this:
var reserved:Double?
if let reserved_string=response.value(forKey: "reserved") as? NSString{
reserved=reserved_string.integerValue
}
if let reserved_float=response.value(forKey: "reserved") as? Double{
reserved=reserved_float
}
Its because in your json response , reserved key is NSCFNumber type so you can't directly force wrap this into NSString so use this way :
if let mReserved = self.response?.objectForKey("reserved") as? Int {
print(mReserved)
}
You have to check what kind of data type is with Optional.
if self.response?.objectForKey("reserved") as? NSString{
//Do something with NSString
}else if self.response?.objectForKey("reserved") as? NSNumber{
//Do something with NSNumber
}else {
print("Error")
}
Try this below code
let reservedStr = String(response.value(forKey: "reserved") as! Double)

How to detect an empty Dictionary without crashing in Swift 2.0?

I am trying to detect if the dictionary coming from API is empty or has values but whenever i am trying to do Dict.count it crashes.
if let personalInfo = self.scanResult?.fields { // personalInfo has 0 values but not nil
let image = NSData(base64EncodedString: (personalInfo["Photo"] as? String)!, options: NSDataBase64DecodingOptions(rawValue: 0)) // Crashes
}
I also tried isEmpty and it crashes there as well.
You should unwrap personalInfo["Photo"] before trying to use it. This way you can ensure you're not trying to instantiate NSData without a value for the Photo key being present
if let personalInfo = self.scanResult?.fields{
if let encodedString = personalInfo["Photo"] as? String{
let image = NSData(base64EncodedString: encodedString,options: NSDataBase64DecodingOptions(rawValue:0))
}
}
I think the simplest solution is to check dictionary keys count is greater than 0. For example:
let dictionary: Dictionary<String, AnyObject> = Dictionary()
if dictionary.keys.count > 0 {
// This mean dictionary has values.
}
The problem was that the self.scanResult.fields was coming out as NSNull. so i found these 3 methods which resolves the problem.
func isNotNull(object:AnyObject?) -> Bool {
guard let object = object else {
return false
}
return (isNotNSNull(object) && isNotStringNull(object))
}
func isNotNSNull(object:AnyObject) -> Bool {
return object.classForCoder != NSNull.classForCoder()
}
func isNotStringNull(object:AnyObject) -> Bool {
if let object = object as? String where object.uppercaseString == "NULL" {
return false
}
return true
}
And i just call like this :
if self.isNotNSNull((self.scanResult?.fields)!
{
}
You can use the "count" of dictionary. See the code.
if let personalInfo = self.scanResult?.fields {
if personalInfo.count > 0 {
if let base64ImageString = personalInfo["Photo"] as? String {
if let image = NSData(base64EncodedString: base64ImageString, options: NSDataBase64DecodingOptions(rawValue: 0)) {
// do your stuff with image
}
}
}
}

if let with OR condition

Is it possible to use an "OR" condition using Swift's if let?
Something like (for a Dictionary<String, AnyObject> dictionary):
if let value = dictionary["test"] as? String or as? Int {
println("WIN!")
}
This would make no sense, how would you be able to tell whether value is an Int or a String when you only have one if statement? You can however do something like this:
let dictionary : [String : Any] = ["test" : "hi", "hello" : 4]
if let value = dictionary["test"] where value is Int || value is String {
print(value)
}
(Tested in Swift 2.0)
You can also do this if you need to do different things depending on the types:
if let value = dictionary["test"] {
if let value = value as? Int {
print("Integer!")
} else if let value = value as? String {
print("String!")
} else {
print("Something else")
}
}
Unfortunately to my knowledge you cannot. You will have to use two separate if statements.
if let value = dictionary["test"] as? String {
doMethod()
} else if let value = dictionary["test"] as? Int {
doMethod()
}
There are multiple ways of getting around this problem. This is just one of them.
Please refer to the Apple documentation on Optional Chaining for more information on this special type of if statement. This is with using Swift 1.2

Resources