getting crash on null data with callback function [closed] - ios

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 years ago.
Improve this question
Here is my code:
func getProduct(productId: String, callback: #escaping (String) -> () ){
Transport.sharedInstance.getProduct(productId: productId) { (data, err) in
if let _ = err{
}else{
if let dt = data as? String{
let pro = Mapper<SubCategoryProducts>().map(JSONString: dt)
let com = ComboData()
com.price = pro?.Price ?? 0
com.type = pro?.type ?? ""
com.amount = pro?.quantity ?? ""
if (pro?.combodata == nil) {
pro?.combodata?.append(com)
} else if ((pro?.combodata?.contains(where: {$0.type == pro?.type})))! {
} else {
pro?.combodata?.append(com)
}
self.arrayForCombo.removeAll()
pro?.combodata?.forEach({ (com) in
let str = "\(com.amount) " + "\(com.type)" + " - " + "₹ \(com.price)"
self.arrayForCombo.append(str)
})
callback(self.arrayForCombo[0])
}
}
}
}
I am getting nil pro?.combodata so only I have checked wether I am getting nil value or not and then when it comes to callback it getting crash on here callback(self.arrayForCombo[0]). Because its not appending the values. I don't know why, but not able to achieve that. Any solution it would be helpful.

Check if forEach is even stepped into.
At this point it can be that either pro or pro.combodata are nil and forEach is not called on nil collection.
Probably you're trying to access [0] index element after self.arrayForCombo.removeAll().

Try using this:
guard let pro = Mapper<SubCategoryProducts>().map(JSONString: dt) else { return }
let com = ComboData()
com.price = pro.Price ?? 0
com.type = pro.type ?? ""
com.amount = pro.quantity ?? ""
if (pro.combodata == nil) {
pro.combodata = [com]
}
The problem is that when combodata of pro is nil than you are trying to append the object that means you are trying nil.append() while it should be [ComboData].append().
I hope this will help you.

When the optional array is nil then calling append will do nothing, you need to initialize it first
if (pro?.combodata == nil) {
pro?.combodata = []
pro?.combodata?.append(com)
}

After executing self.arrayForCombo.removeAll() statement, arrayForCombo become empty. If your pro?.combodata is nil then forEach statement could not execute. So still your arrayForCombo is empty.
But in the callBack statement you forcefully try to access the first item of arrayForCombo whether it's empty or not. That's why it crushes when the array is empty.
You can safely by pass the cruhes like that:
if let firstItem = arrayForCombo.first {
callback(firstItem)
}

Related

Provide a catch if index is out of range (Swift 5) [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
When I fetch data through a GET method in Swift it is presented as an Array:
URLSession.shared.dataTask(with: request as URLRequest) { (data, response, error) in
guard let data = data else { return }
do {
let obj: Response = try JSONDecoder().decode(Response.self, from: data)
let cit = obj.assetProfile?.city
let coun = obj.assetProfile?.country
let negociosLocacion = ("\(cit ?? "N/A"), \(coun ?? "N/A")")
let object = EObject(marketOpen: (obj.price?.regularMarketOpen?.fmt ?? "N/A"))
self.e.append(object)
But how do I create a catch method where if the index is out of range will present a segue, for example, as to not crash the app?
You can use indices to check if an array contains an index prior to accessing it:
guard myArray.indices.contains(1) else { return }
let value = myArray[1]
Apple documentation: indices
You can handle it through various ways for example I am showing you some code snippets below. See what case is similar to yours.
If you are getting items by index from an array then you can avoid array index out of range error like:
if yourIndex <= yourArray.endIndex {
//Then do your desired operation by getting items by your index
} else {
//Do not get items by your index because it is out of range.
//Do anything else
}
You can check that your array is empty or not? like:
if yourArray.isEmpty {
// Don't do any array item get operations because your array is empty
} else {
// Your array is not empty
}
You can check that at least your array has a fixed number of elements or not? like:
if yourArray.count >= yourDesiredNumberOfElements {
// Your array have at least your desired number of elements.
} else {
// Your array does not have your desired number of elements.
// your array has less elements than you desire.
}
A good solution is to use a safe subscript with returns optional element.
extension Array {
public subscript(safeIndex index: Int) -> Element? {
guard index >= startIndex, index < endIndex else {
return nil
}
return self[index]
}
}
In order to check if the index is out of bounds so as to not cause the app to crash and to handle the situation in another way,
if (yourArray.count - 1) >= yourIndex {
//Not out of bounds, carry on.
} else {
//This is when your app would crash due to being out of bounds.
}

Couchbase lite, Search query taking very long time

When I try to search the couchbase documents of size around 10K, the searching is taking very long time. Below are the code snippet. Can anyone optimize it or suggest me any alternative approach. Thank you.
1) Search function
func search(keyword:String) -> [[String:AnyObject]] {
var results:[[String:AnyObject]]=[]
let searchView = database.viewNamed(AppConstants().SEARCH)
if searchView.mapBlock == nil {
startIndexing()
}
let query = searchView.createQuery()
var docIds = Set<String>()
let result = try query.run()
while let row = result.nextRow() {
let key = "\(row.key)"
let keyArr = keyword.characters.split(" ")
for (index, element) in keyArr.enumerate() {
let keyItem = String(element)
if key.lowercaseString.containsString(keyItem.lowercaseString) {
let value = row.value as! [String:AnyObject]
let id = value["_id"] as? String
if id != nil && !docIds.contains(id!) {
results.append(value)
docIds.insert(id!)
}
}
}
}
}
2) Indexing
func startIndexing() {
let searchView = database.viewNamed(AppConstants().SEARCH)
if searchView.mapBlock == nil {
searchView.setMapBlock({ (doc, emit) in
let docType = doc[AppConstants().DOC_TYPE] as! String
if AppConstants().DOC_TYPE_CONTACT.isEqual(docType) {
self.parseJsonToKeyValues(doc)
for value in self.fields.values {
emit(value, doc)
}
self.fields.removeAll()
}
}, version: "1")
}
}
self.parseJsonToKeyValues(doc) will return me the key value store of my documents to index.
You're emitting the entire document along with every field for your view. This could easily cause your queries to be slow. It also seems unlikely you want to do this, unless you really need to be able to query against every field in your document.
It's considered best practice to set your map function right after opening the database. Waiting until right before you query may or may not slow you down.
See https://developer.couchbase.com/documentation/mobile/current/guides/couchbase-lite/native-api/view/index.html for more, especially the section labeled "Development Considerations".

Guard statement in swift for error handling [duplicate]

This question already has answers here:
How to check if a text field is empty or not in swift
(16 answers)
Closed 6 years ago.
I tried running below statement, but it's skipped right over in the code.
guard let num1 = num1Input.text else
{
show("No input in first box")
return
}
Can somebody tell me why this statement isn't running when the text field is blank?
You could use the where clause to check for non-nil and non-empty
guard let num1 = num1Input.text where !num1.isEmpty else {
show("No input in first box")
return
}
You should check the length of the text, not if its nil i.e:
guard num1Input.text.characters.count > 0 else {
...
}
If the text is optional, you can do.
guard let num1 = num1Input.text where num1.characters.count > 0 else {
...
}
This statement is testing whether the text is nil but you probably want to test whether the string is empty, therefore
guard let input = num1Input.text where input.characters.count > 0 else {
print("empty")
return
}
or just simply
guard num1Input.text?.characters.count > 0 else {
print("empty")
return
}

If let condition true when value is missing in optional type, swift

I have parser in Objc, parser returns NSDictionary. I am using this parser in swift class. But when some value is missing on that dictionary, it shows nil value. e.g. ->
wirlessData = {
"anon" = {
};
"channel" = {
"text" = 1;
};
}
I am checking through
if let wepauthValue = wirlessData["wepauth"] {
if let value = wepauthValue["text"] {
print("\(value)") // nil
}
}
I don't how it satisfy the if let condition. Any one faced this types of problem can help me out.
Thanks,
vikash
You don't need any special code to do this, because it is what a dictionary already does. When you fetch dict[key] you know whether the dictionary contains the key, because the Optional that you get back is not nil (and it contains the value).
So, if you just want to answer the question whether the dictionary contains the key, ask:
let keyExists = dict[key] != nil
If you want the value and you know the dictionary contains the key, say:
let val = dict[key]!
But if, as usually happens, you don't know it contains the key - you want to fetch it and use it, but only if it exists - then use something like if let:
if let val = dict[key] {
// now val is not nil and the Optional has been unwrapped, so use it
}
I have tested it and found that value is still optional.Take a look at screenshot below to understand it better.
"anon" would be an empty dictionary. An empty dictionary is not nil, it is a dictionary. Just an empty one. A JSON parser will never, ever give nil values unless you ask for a key that is not in a dictionary. For example wirlessData ["nonexistingkey"] would give you nil.
If you be more type-strong about it with the if..let's then:
if let anonValue = wirlessData["anon"] {
if let value = anonValue["text"] as? String {
// This won't execute if value isn't converted from `anonvalue["text"]` to String specifically. This includes null been a false match too
print("\(value)") // nil
}else{
print("Value did't match string at all")
}
}
or even more specifically in your case:
if let anonValue = wirlessData["anon"] {
if let value = anonValue["text"] as? Int {
// This won't execute if value isn't converted from `anonvalue["text"]` to String specifically. This includes null been a false match too
print("\(value)") // nil
}else{
print("Value did't match int at all")
}
}
The value your parser is returning not nil, its empty so you need to check on count if inner data type is dictionary or array, I have past 1 sample here
Please use below code and correct your logic accordingly to get it work properly
let wirlessData:[String:AnyObject] = [
"anon" : [],
"channel" : [
"text" : 1
]
]
if wirlessData["anon"]?.count > 0 {
if let value = wirlessData["anon"]!["text"] {
print("\(value)") // nil
}
}
Try this below code using type check operator (is) -
if wirlessData["anon"] is [String:AnyObject]
{
let anon = wirlessData["anon"]!
print(anon)
if anon["random"] is String {
let stringValue = anon["random"]!
print("\(stringValue)")
}
else if anon["random"] is Int
{
let intValue = anon["random"]!
print("\(intValue)") // nil
}
else
{
print(" may be value did't match string & Int or nil ")
}
}

Code Crashes when loading an empty attribute from Cloudkit - using Swift

I am trying to get access to a record value in CloudKit, here MyPin, it has a title & subtitle attribute/field value.
However it may happen that sometimes the record value is empty(here subtitle), and it crashes at the line when I call:
var tempS: String = Annot["Subtitle"] as! String
because Annot["Subtitle"] doesn exist ...
When I do
println(Annot["Subtitle"])
it returns nil
but if I do :
if (Annot["Subtitle"] == nil) {
println("just got a nil value")
}
I never enter the if statement:
Can someone help me how to identify if the record has an empty value?
Here is my line of codes:
let container = CKContainer.defaultContainer()
let publicData = container.publicCloudDatabase
let query = CKQuery(recordType: "MyPin", predicate: NSPredicate(format: "TRUEPREDICATE", argumentArray: nil))
publicData.performQuery(query, inZoneWithID: nil) { results, error in
if error == nil { // There is no error
for Annot in results {
var tempS: String = Annot["Subtitle"] as! String
}}
when you get Annot["Subtitle"] it will give you a CKRecordValue? return which has a base class of NSObjectProtocol. So in your case the field does exist but it's not a String so casting it using as! String will crash your app. Since the field exists the CKRecordValue will not be nil. However the content of that field is nil. When you print the field, it will output the .description of that field. In your case that is nil. Could you try this code instead:
if let f = Annot["Subtitle"] {
print("f = \(f) of type \(f.dynamicType)")
}
then set a breakpoint on the print line and when it stops try the following three statements in your output window:
po Annot
po f
p f
After the po Annot you should see what's in that record. Including your subtitle field. The po f is not so interesting. It will just output a memory address. The p f however will show you the actual type. If it's a string you should see something like: (__NSCFConstantString *) $R3 = 0xafdd21e0
P.S. Maybe you should call it record instead of Annot. It's a local variable so it should start with a lower case character. And it's still a record and not an Annot.
I think you are doing the right thing, but you don't see the println as it is executed in another thread (the completion part is executed asynchronously).
Try this:
if (Annot["Subtitle"] == nil) {
dispatch_async(dispatch_get_main_queue()) {
println("just got a nil value")
}
}
and see if it works!
The way I get values from cloudkit is this way. This both take care of the nil values and all other eventualities. Just note I have implemented a delegate to get my results back to the calling object asynchronously
privateDB.performQuery(query, inZoneWithID: nil) { (result, error) -> Void in
if error == nil{
for record in result{
let rec = record as! CKRecord
if let xxxVar = rec.valueForKey("fieldName") as? String{
myArray.append( xxxVar! ) //append unwrapped xxxVar to some result or whatever
}else{
//handle nil value
}
}
dispatch_async(dispatch_get_main_queue()) {
//do something with you data
self.delegate?.myResultCallBack(myArray)
return
}
}else{
dispatch_async(dispatch_get_main_queue()) {
self.delegate?.myErrorCallBack(error)
return
}
}
}
Beware, there are some changes in Swift2

Resources