if statement ignores bool value in swift - ios

I must doing something wrong, because inside the NSDictionary the values are 1 or 0 and it always prints "locked"...
let object: NSDictionary = self.collectionObjects?.objectAtIndex(indexPath.row) as! NSDictionary
if let locked = (object.objectForKey("locked") as? NSNumber)?.boolValue {
println("locked")
} else {
println("open")
}

Your not testing anything there
Try,
let object: NSDictionary = self.collectionObjects?.objectAtIndex(indexPath.row) as! NSDictionary
let locked = (object.objectForKey("locked") as? NSNumber)?.boolValue
if locked == true
{
println("locked")
}
else
{
println("open")
}

When you are doing:
if let locked = (object.objectForKey("locked") as? NSNumber)?.boolValue {
...
} else {
...
}
The if statement is checking whether or not your locked variable has evaluated to a nil value. If it has a value then the true statement block will be executed; if it has nil then the false block (else statement) will be executed.
This is called Optional Binding.
You use optional binding to find out whether an optional contains a
value, and if so, to make that value available as a temporary constant
or variable. Optional binding can be used with if and while statements
to check for a value inside an optional, and to extract that value
into a constant or variable, as part of a single action.
As a result, if your variable does not evaluate to nil then you could use the locked variable inside the if block.
For example:
if let locked = (object.objectForKey("locked") as? NSNumber)?.boolValue {
if locked {
println("locked")
} else {
println("open")
}
} else {
println("locked is nil")
}
See this SO post for an example.

What you're doing is unwrapping the conditional value, not checking it, so what you need to do is:
Unwrap it
Test it
Handily, the if-let-where statement can do it for us in one line like this:
if let object = self.collectionObjects?.objectAtIndex(indexPath.row) as? NSDictionary, locked = object.objectForKey("locked")?.boolValue where locked {
// we have a value here and it happens to be true
} else {
// this means we don't have a value
}
Include the first let object in the if statement too - you'll never know, it can be null and then it would unexpectedly crash.
Also, the compiler should be able to handle object -> boolValue directly, if not, add the NSNumber casting back.

I recommend this
let object: NSDictionary = self.collectionObjects?.objectAtIndex(indexPath.row) as! NSDictionary
if let locked = object.objectForKey("locked") as? NSNumber where locked.boolValue == true {
print("locked")
}else {
print("open")
}

You are checking only the value is whether nil or not nil. As the value is not nil you are always getting output as "locked".
Try this
let object: NSDictionary = self.collectionObjects?.objectAtIndex(indexPath.row) as! NSDictionary
if let locked = (object.objectForKey("locked") as? NSNumber)?.boolValue {
If locked == true {
println("locked")
}
else {
println("open")
}
}

Related

How to check nil for variables in swift and avoid app crashing?

I'm having my string declared as,
var firstName = String()
and I'm assigning value from parsed JSON content like,
firstName = json["first_name"].stringValue
But sometimes, there might be empty values in the JSON response and the app is crashing, I read about guard statement and if statement to check empty values, but that requires the declaration format to be changed, couldn't find a right way to handle this error without changing the declaration format.
since I have declared all the variables in my app with similar formats, changing that requires time, I'm in the verge of uploading my app, this is my first swift app, if my declaration format is wrong please answer why it is, can someone help me out of this?
Code as of Swift 4:
Keep in mind that when you are using ".stringValue", it is almost the same as using a "!" which will force a crash on nil.
if let firstName = json["first_name"]as? String {
//do stuff like
self.firstName = firstName
}
This will unwrap it to where you can get at the value if it isn't null and can be a string.
Guard let's are really good for this though as you can account for it in the beginning and you can assume that it is not optional for the entire scope.
guard let firstName = json["first_name"]as? String else {return}
self.firstName = firstName
In addition, you could always check for nulls in one line and assign a default value if a nil value occurs.
self.firstName = (json["first_name"]as? String) ?? "Default String"
You can use next statement:
guard let firstName = json["first_name"].stringValue else { // Do sth if nil }
// Do sth if not nil
Or you could use statement, which you wrote, but you should check variable
firstName like this:
guard firstName != nil else { // Do sth if nil }
// Do sth if not nil
Or
if firstName != nil {
// Do sth if not nil
}
You can use guard statement also,
guard let firstName = json["first_name"] else {
print("FirstName is Empty")
return
}
or you can check with if also,
if let firstName = json["first_name"] {
//Your code goes here
}
You can do that the following way:
if let dictionary = json {
if let fName = dictionary["first_name"] {
firstName = fName as? String
}
}
I guest you use SwiftyJSON. The function stringValue always return String object. It's impossible to be nil. It sounds like the response data which is not valid JSON format, so it crashed.
My snippet codes.
// Alarmofire + SwiftyJSON
let request: DataRequest = ...//< Configure Alarmofire DataRequest
request.response() { (resp) in
if let error = resp.error {
// TODO: Error handle
return
}
if (response.data == nil) {
// TODO: error handle for empty data?
return
}
assert(response.data != nil, "In this place, the `data` MUST not be nil.")
let json = try? JSON(data: response.data!)
if (json == nil) {
// TODO: Error handle for invalid JSON format.
// If json is nil, then `json["first_name"]` will lead to crash.
return
}
// TODO: parse json object
let firstNameStr = json["first_name"].stringValue
}

Swift: Unwrapping Optionals and NSNull

if let action = self.info?["action"] {
switch action as! String {
....
}
} else {...}
In this example, "action" always exists as a key in self.info.
Once the second line executes, I get:
Could not cast value of type 'NSNull' (0x1b7f59128) to 'NSString' (0x1b7f8ae8).
Any idea how action can be NSNull even though I unwrapped it? I've even tried "if action != nil", but it still somehow slips through and causes a SIGABRT.
NSNull is a special value typically resulting from JSON processing. It is very different from a nil value. And you can't force-cast an object from one type to another which is why your code fails.
You have a few options. Here's one:
let action = self.info?["action"] // An optional
if let action = action as? String {
// You have your String, process as needed
} else if let action = action as? NSNull {
// It was "null", process as needed
} else {
// It is something else, possible nil, process as needed
}
Try this out. So in the first line, check first if there is a valid value for "action", then if that value is in type String
if let action = self.info?["action"] as? String {
switch action{
....
}
} else {...}
if let action = self.info?["action"] { // Unwrap optional
if action is String { //Check String
switch action {
....
}
} else if action is NSNull { // Check Null
print("action is NSNull")
} else {
print("Action is neither a string nor NSNUll")
}
} else {
print("Action is nil")
}

If filter returns nil, how do I not return empty result?

I'm doing the following filtering that returns a recipe list, filtered by given category name value.
filteredRecipe = filteredRecipe.filter({
if let category = $0.valueForKey("category") as? NSManagedObject {
if let name = category.valueForKey("name") as? String {
return name.rangeOfString(cap) != nil
} else {
return false
}
} else {
return false
}
})
This filter works in association with searchBar textfield so I will possibly have random value in the textfield which will make filteredRecipe to hold unreliable data.
I need to make sure when the filter can't find any recipe from filteredRecipe with given category name value, I leave filteredRecipe untouched.
Currently, when there is no match, it makes filteredRecipe empty array []. I'm not sure what part causes this.
You need to assign the filtering result to a temporary variable and check that it isn't empty.
let filtered = filteredRecipe.filter({
if let category = $0.valueForKey("category") as? NSManagedObject {
if let name = category.valueForKey("name") as? String {
return name.rangeOfString(cap) != nil
}
return false
})
if !filtered.isEmpty {
filteredRecipe = filtered
}
Another approach is to extend Collection with a selfOrNilIfEmpty property, and use the nil coalescing operator:
extension Collection {
var selfOrNilIfEmpty: Self? {
return isEmpty ? nil : self
}
}
and later, on your code:
let filteredRecipe = filteredRecipe.filter { ... }.selfOrNilIfEmpty ?? filteredRecipe

How to check if NSDictionary is not nil in Swift 2

I'm getting NSDictionary as parameter in my function but having problem because don't know how to check if that parameter is not nil.
My function looks like this:
func doSmth(val : NSDictionary)
Inside my function I'm trying to get some values:
let action = val["action"] as! String
But getting error "fatal error: unexpectedly found nil while unwrapping an Optional value" when receive parameter val as nil.
The error is due to assuming (force casting) a value that can sometimes be nil. Swift is awesome, because it allows conditional unwraps and conditional casts in very concise statements. I recommend the following (for Swift 1-3):
Use "if let" to conditionally check for "action" in the dictionary.
Use as? to conditionally cast the value to a String
if let actionString = val["action"] as? String {
// action is not nil, is a String type, and is now stored in actionString
} else {
// action was either nil, or not a String type
}
You can also access the allKeys or alternatively allValues property and check if the array contains any elements like so:
let dic = NSDictionary()
let total = dic.allKeys.count
if total > 0 {
// Something's in there
}
else {
// Nothing in there
}
EDIT
Here is how you can detect if the NSDictionary is nil, if they key you are looking for exists, and if it does attempt to access it's value:
let yourKey = "yourKey"
if let dic = response.someDictionary as? NSDictionary {
// We've got a live one. NSDictionary is valid.
// Check the existence of key - OR check dic.allKeys.containsObject(yourKey).
let keyExists: Bool = false;
for var key as String in dic.allKeys {
if key == yourKey {
keyExists = true;
}
}
// If yourKey exists, access it's possible value.
if keyExists == true {
// Access your value
if let value = dic[yourKey] as? AnyObject {
// We're in business. We have the value!
}
else {
// yourKey does not contain a value.
}
}
else {
// yourKey does not exist in NSDictionary.
}
}
else {
// Call an ambulance. NSDictionary is nil.
}
That's not particularly related to Swift 2.
If the dictionary could be nil declare it as optional
func doSmth(val : NSDictionary?)
Then use optional bindings to check
if let valIsNonOptional = val {
let action = valIsNonOptional["action"] as! String
}
The code assumes that there is a key action containing a String value anyway if the dictionary is not nil
Your dictionary parameter is probably not nil. The problem is probably that your dictionary doesn't contain a value for the key "action".
When you say val["action"], the dictionary (being an NSDictionary) returns an Optional<AnyObject>. If val contains the key "action", it returns Some(value). If val doesn't contain the key "action", it returns None, which is the same as nil.
You can unwrap the Optional in your cast, and choose a course of action based on whether it was nil, using an if-let statement:
if let action = val["action"] as? String {
// action is a String, not an Optional<String>
} else {
// The dictionary doesn't contain the key "action", and
// action isn't declared in this scope.
}
If you really think val itself might be nil, you need to declare your function that way, and you can unwrap val without renaming it using a somewhat confusing guard statement:
func doSmth(val: NSDictionary?) {
guard let val = val else {
// If val vas passed in as nil, I get here.
return
}
// val is now an NSDictionary, not an Optional<NSDictionary>.
...
}

if let variable - Use of unresolved identifier

I am using SwiftyJSON to call some APIs and fetch some data.
When I use:
if let variable = json["response"]["fieldname"] {
} else {
println("error")
}
I am not able to use the variable later on, for example to append the value to an array.
For example:
if let variable1 = json["response"]["fieldname1"] {
} else {
println("error")
}
if let variable2 = json["response"]["fieldname2"] {
} else {
println("error")
}
var currentRecord = structure(variable1, variable2) ---> This line returns an error (use of unresolved identifier variable1) as not able to find variable1 or variable2
myArray.append(currentRecord)
How can I solve this?
The scope of an if let is inside the brackets immediately following it:
if let jo = joseph {
// Here, jo is in scope
} else {
// Here, not in scope
}
// also not in scope
// So, any code I have here that relies on jo will not work
In Swift 2, a new statement, guard was added, that seems to have exactly the kind of behaviour you want:
guard let jo = joseph else { // do something here }
// jo is in scope
If you're stuck in Swift 1, though, an easy way for you to unwrap those variables without a pyramid of doom is:
if let variable1 = json["response"]["fieldname1"], variable2 = json["response"]["fieldname2"] {
var currentRecord = structure(variable1, variable2)
myArray.append(currentRecord)
} else {
println("error")
}
#oisdk already explained that the scope of a variable defined by if let is only inside the braces of that statement.
That's what you want, because if it if let statement fails, the variable is undefined. The whole point of if let is to unwrap your optional safely, so that inside the braces, you can be sure the variable is valid.
Another solution to your problem (in Swift 1.2) is to use multiple if let statements:
if let variable1 = json["response"]["fieldname1"],
let variable2 = json["response"]["fieldname2"]
{
//This code will only run if both variable1 and variable 2 are valid.
var currentRecord = structure(variable1, variable2)
myArray.append(currentRecord)}
else
{
println("error")
}
Your code checks variable2 always even variable 1 fails. But that causes (edited!) not the error.
You can check and assign both variables in the same line. The "true" branch will be executed only if both variables are not nil
let response = json["response"]
if let variable1 = response["fieldname1"], variable2 = response["fieldname2"] {
let currentRecord = structure(variable1, variable2)
myArray.append(currentRecord)
} else {
println("error")
}

Resources