Reputation: 3015
I am having a very weird problem which I am not able to figure it out of what is going wrong here.
Here is my code
func updateSearchResultsForSearchController(searchController: UISearchController) {
filterTableData.removeAll(keepCapacity: false)
let searchWord = searchController.searchBar.text!
getCountriesNamesFromServer(searchWord)
let searchPredict = NSPredicate(format: "SELF CONTAINS[c] %@", searchController.searchBar.text!)
newTableData = [String]()
for var i = 0; i < self.dict.count - 1; i++ {
let cityName = (((self.dict["\(i)"] as?NSDictionary)!["City"] as?NSDictionary)!["name"] as?NSString)! as String
let countryName = (((self.dict["\(i)"] as?NSDictionary)!["Country"] as?NSDictionary)!["name"] as?NSString)! as String
print("countryName is \(countryName)")
newTableData.append(cityName)
}
print("newTableData is \(newTableData)" )
let array = (newTableData as NSArray).filteredArrayUsingPredicate(searchPredict)
print("array is\(array)")
filterTableData = array as! [String]
self.tableView.reloadData()
}
Program is crashing at this line
let countryName = (((self.dict["\(i)"] as?NSDictionary)!["Country"] as?NSDictionary)!["name"] as?NSString)! as String
when I type first character in the search box all works fine but as soon as I type second character in the searchBox
program crashes and it gives me this error
Could not cast value of type 'NSNull' (0x1a03c3768) to 'NSString' (0x1a03cd798).
and for your Information countryNames exist in the dict
variable but I don't know why its giving me null value and why city name is successfully working because country also exist in the same array from which I am getting the city
UPDATE:
This line prints country name successfully
print("countryName is \(countryName)")
Upvotes: 3
Views: 8836
Reputation: 11
@gnasher729 Explained it very well, but to solve your problem you should use the "?" instead of "!"
so while parsing the data from the code below
let countryName = (((self.dict["\(i)"] as?NSDictionary)!["Country"] as NSDictionary)!["name"] as?NSString)! as String
use the code as
let countryName = (((self.dict["\(i)"] as?NSDictionary)!["Country"] as?NSDictionary)!["name"] as? NSString)? as String ?? ""
or
let countryName = ((self.dict["\(i)"] as?NSDictionary)!["Country"] as?NSDictionary)!["name"] as? String ?? ""
Thanks
Upvotes: 1
Reputation: 4187
You received a list of country from your server at this line
getCountriesNamesFromServer(searchWord)
But I think that its returning you a list without all datas completely set.
for var i = 0; i < self.dict.count - 1; i++ {
let cityName = (((self.dict["\(i)"] as?NSDictionary)!["City"] as?NSDictionary)!["name"] as?NSString)! as String
let countryName = (((self.dict["\(i)"] as?NSDictionary)!["Country"] as?NSDictionary)!["name"] as?NSString)! as String
print("countryName is \(countryName)")
newTableData.append(cityName)
}
On the first iteration of your loop the name
value is set correctly so it works but at the second iteration, it is, in my opinion, null
and you cannot cast NSNull
to NSString
so the application crashes.
Before casting to NSString
, if you are not sure of the server response, you must try if the dictionary contains the name
key and if the value is set. If it is, you can try to cast it.
If you want, you can also use an external library SwiftyJSON
to simplify the parsing of JSON
response.
Upvotes: 0
Reputation: 52632
Well, they tell you what the problem is: You've got a result of type NSNull, and NSNull cannot be converted to NSString.
Most likely you are processing JSON, and JSON data often contains null values.
Go away from your labyrinth of ? and !
Write some code that helps you. Remember that any ! will crash your application if you don't get what you expect.
When you access the key "name" in a dictionary, you need to handle the case that there is a string (nice for you), nothing (key doesn't exist, very common), null (the server tells you explicitly that there is nothing, very common), a number, bool, dict or array.
For each of these cases, tell me what you want as a result: A crash? An optional string that is nil? An empty string? Typically you want a string if it is there, possibly a number converted to a string if it is a number, and either nil or an empty string if the key isn't there or null, and either a crash or nil if you get a bool, dictionary or array.
Then write a function that returns exactly that. And then you use it. When you write (... as? NSString)! you tell the compiler that you want a crash if it's not a string, and that's what you got.
Upvotes: 4