"If" statement not working with optional value - swift

My problem is that I have some text fields that the user enters in numbers, the entered numbers then get saved to the corresponding variable.
However if the user doesn't enter a number and leaves it blank, the text field has a value of 'nil' and so would crash if unwrapped.
So I used an if statement to only unwrap if the contents of the test field are NOT nil, however this doesn't work. My program still unwraps it and crashes because the value is nil...
I don't understand how my if statement is not catching this.
On another note, how do I change my if statement to only allow Int values to be unwrapped and stored, strings or anything else would be ignored.
#IBAction func UpdateSettings() {
if CriticalRaindays.text != nil {
crit_raindays = CriticalRaindays.text.toInt()!
}
if EvapLess.text != nil {
et_raindays_lessthan_11 = EvapLess.text.toInt()!
}
if EvapMore.text != nil {
et_raindays_morethan_11 = EvapMore.text.toInt()!
}
if MaxWaterStorage.text != nil {
max_h2Ostore = MaxWaterStorage.text.toInt()!
}
if CarryForward.text != nil {
carry_forward = CarryForward.text.toInt()!
}
}

Your issue is that while the text exists, it doesn't mean toInt() will return a value.
Say the text was abc, CriticalRaindays.text != nil would be true but CriticalRaindays.text.toInt()! can still be nil, because abc cannot be converted to an Int.
The exact cause of your crash is likely that .text is equal to "", the empty string. It's not nil, but definitely not an Int either.
The better solution is to use optional binding to check the integer conversion and see if that passes, instead of merely the string existing:
if let rainDays = CriticalRaindays.text.toInt() {
crit_raindays = rainDays
}
If that doesn't compile, you possibly need to do Optional chaining:
if let rainDays = CriticalRaindays.text?.toInt()
Not on a Mac atm so can't test it for you but hope this makes sense!

Why not use an if let to unwrap on if the the text field's text is non-nil?
if let textString = self.textField.text as String! {
// do something with textString, we know it contains a value
}

In your ViewDidLoad, set CarryForward.text = "" This way it will never be nil
Edit:
To check if a textfield is empty, you can use this:
if (CarryForward.text.isEmpty) {
value = 10
}
else {
value = CarryForward.text
}

Related

How to remove optional from a string value in swift?

In the code I'm not able to remove optional from the value inside the lbltotalamount.
The value in lblTotalAmount is not removing its optional value from it.
Why? The value in grandtotal gets optional removed but when I assign it to a label it returns an optional value again.
The lblTottalAmount is getting an optional value. I want to remove it.
if success == false {
var grandtotal: Any? = value["total"]
if grandtotal != nil {
print("O!O!O!O/\(grandtotal!)")
grandtotal = String(describing: grandtotal)
self.lblTotalAmount.text = ([grandtotal]) as! String // (here I am not able to remove optional)
}
The problem is in the line
grandtotal = String(describing: grandtotal)
You check for nil but you don't unwrap the value so it's still an optional.
And you are misusing String(describing. Never use it for types which can be converted to String with an init method.
Use always conditional downcast
if success == false {
if let grandtotal = value["total"] as? Double {
self.lblTotalAmount.text = String(grandtotal)
}
}

Setting variable equal to if statement condition

I have an if statement that checks to see if an array element matches a local variable.
if pinArray.contains(where: {$0.title == restaurantName})
How would I create a variable of this element?
I attempted
let thePin = pinArray.contains(where: {$0.title == restaurantName})
but this comes with "could not cast boolean to MKAnnotation".
I also tried variations of
let pins = [pinArray.indexPath.row]
let pinn = pins(where: pin.title == restaurantName) (or close to it)
mapp.selectAnnotation(thePin as! MKAnnotation, animated: true)
to no avail. What basic step am I missing?
contains(where:) returns a Bool indicating whether a match was found or not. It does not return the matched value.
So thePin is a Bool which you then attempt to force-cast to a MKAnnotation which of course crashes.
If you want the matching value, change your code to:
if let thePin = pinArray.first(where: { $0.title == restaurantName }) {
do {
mapp.selectionAnnotation(thePin, animated: true)
} catch {
}
} else {
// no match in the array
}
No need for contains at all. No need to cast (assuming pinArray is an array of MKAnnotation).

How to check nil for Swift Optionals with out unwrap it?

I knew that safely unwrapping is as follows
var firstName:String?
if let firstName = firstName
{
dictionary.setObject(firstName, forKey: "firstName")
}
else
{
dictionary.setObject("", forKey: "firstName")
}
I want to add firstname to dictionary even it is nil also. I will have 10-15 var's in dictionary.
I do not want to check for this condition 10-15 times for ear var.
I am having more than 1000 optionals through out project.
So thought of writing writing a func will help me duplicate the code and reduce the number of lines.
So implemented this func as below.
func checkNull(str:String) -> String
{
return str.characters.count > 0 ? "\(str)" : ""
}
but while calling the func,
let addressstr = self.checkNull(address?.firstName)
firstname is the var in address model here.
The auto correction sugguests
let addressstr = self.checkNull((address?.firstName)!)
The above line causes the crash.
First of all firstName is an Optional therefore you cannot pass it to a function which only takes String.
In addition this line:
str.characters.count > 0 ? "\(str)" : ""
Is equivalent to just returning str so you don't check whether it is an Optional.
Solution
In this case it is way easier to use the nil coalescing operator:
let addressstr = address?.firstName ?? ""
If address is not nil firstName gets unwrapped and bind to addressstr. Otherwise this string gets assigned to it: ""

How to test the Optionality of a String?

I have two different scenarios where I need to test the "optionality" of an optional type. I have not been able to figure how to explicitly test if the variable is a .None or a .Some other than with an unwieldy switch statement. How can I test for Someness with an if statement?
Scenario 1
I am writing an address formatter and my inputs are a number of String? types. In this example a simple test for (str != nil) will work. However, since my other need is when dealing with a 'double optional' and a nil test can't distinguish between .Some(.None) and .None a solution to this problem will solve that problem too.
Here's a version that works using a switch
let address1:String? = "123 Main St"
let address2:String? = nil
let apt:String? = "101"
let components = [address1, address2, apt].filter( { (c) -> Bool in
switch c {
case .Some: return true
case .None: return false
}
}).map { return $0! } //Had to map because casting directly to [String] crashes
print(", ".join(components)) //"123 Main St, 101"
What's I'd like to see is something like with an if:
let nice = ["123 Main St", nil, "303"].filter { (c) -> Bool in
return (c == .Some)
}
print(", ".join(nice))
Scenario 2
This is where a nil test won't work. If something is a String?? it can be any of .None, .Some(.None), or .Some(.Some(String)). In my case, the variable is carrying the recordID from an api call which might either be missing entirely (.None), a value (.Some(.Some("ABDEFG")), or explicitly NULL (.Some(.None)).
let teamNoneNone: String?? = .None
let teamSomeNone: String?? = .Some(.None)
let teamSomeSome: String?? = "My favorite local sportsball team"
if teamNoneNone == nil {
print("teamNoneNone is nil but is it .None? We don't know!") //prints
} else {
print("teamNoneNone is not nil")
}
if teamSomeNone == nil {
print("teamSomeNone is nil")
} else {
print("teamSomeNone is not nil but is it .Some(.None)? We don't know!") //prints
}
if teamSomeSome == nil {
print("teamSomeSome is nil but is it .None? We don't know!")
} else {
print("teamSomeSome is not nil but is it .Some(.None) or .Some(.Some())? We don't know!") //prints
}
Via another SO post I found a workaround like this, but it's not very clear what's happening to a casual reader:
if let team: String? = teamSomeNone {
print("teamSomeNone is Some(.None)") //prints
} else {
print("teamSomeNone is .Some(.Some())")
}
if let tests if a value is .None, and if it isn’t, it unwraps it and binds it to a local variable within an if statement.
Using switch with .Some and .None is really a secondary way of handling optionals, if if let doesn’t cut it. But it almost always does, especially now you can do multiple if lets in a single statement, following the latest release of Swift 1.2 to production.
Wanting to filter out the nils in a collection is a common-enough task that Haskell has a standard function for it, called catMaybe. Here’s a version, which I’ll call catSome, that would do the trick in Swift:
func catSome<T>(source: [T?]) -> [T] {
var result: [T] = []
// iterate over the values
for maybe in source {
// if this value isn’t nil, unwrap it
if let value = maybe {
// and append it to the array
result.append(value)
}
}
return result
}
let someStrings: [String?] = ["123 Main St", nil, "101"]
catSome(someStrings) // returns ["123 Main St", "101"]
Doubly-wrapped optionals are a bit of a pain, so the best solution is to avoid them in the first place – often, via use of optional chaining or flatMap.
But if you do find yourself with some, and all you care about is the inner value, you can unwrap them using a double if let:
// later parts of the let can rely on the earlier
if let outer = teamSomeSome, teamName = outer {
println("Fully unwrapped team is \(teamName)")
}
If you want to explicitly know if a double-optional has an inner nil inside an outer value, but isn’t nil itself, you can use if let with a where clause:
if let teamSomeMaybe = teamSomeNone where teamSomeMaybe == nil {
// this will be executed only if it was .Some(.None)
println("SomeNone")
}
The where clause is an extra conditional that can be applied to the unwrapped value.

Check empty string in Swift?

In Objective C, one could do the following to check for strings:
if ([myString isEqualToString:#""]) {
NSLog(#"myString IS empty!");
} else {
NSLog(#"myString IS NOT empty, it is: %#", myString);
}
How does one detect empty strings in Swift?
There is now the built in ability to detect empty string with .isEmpty:
if emptyString.isEmpty {
print("Nothing to see here")
}
Apple Pre-release documentation: "Strings and Characters".
A concise way to check if the string is nil or empty would be:
var myString: String? = nil
if (myString ?? "").isEmpty {
print("String is nil or empty")
}
I am completely rewriting my answer (again). This time it is because I have become a fan of the guard statement and early return. It makes for much cleaner code.
Non-Optional String
Check for zero length.
let myString: String = ""
if myString.isEmpty {
print("String is empty.")
return // or break, continue, throw
}
// myString is not empty (if this point is reached)
print(myString)
If the if statement passes, then you can safely use the string knowing that it isn't empty. If it is empty then the function will return early and nothing after it matters.
Optional String
Check for nil or zero length.
let myOptionalString: String? = nil
guard let myString = myOptionalString, !myString.isEmpty else {
print("String is nil or empty.")
return // or break, continue, throw
}
// myString is neither nil nor empty (if this point is reached)
print(myString)
This unwraps the optional and checks that it isn't empty at the same time. After passing the guard statement, you can safely use your unwrapped nonempty string.
In Xcode 11.3 swift 5.2 and later
Use
var isEmpty: Bool { get }
Example
let lang = "Swift 5"
if lang.isEmpty {
print("Empty string")
}
If you want to ignore white spaces
if lang.trimmingCharacters(in: .whitespaces).isEmpty {
print("Empty string")
}
Here is how I check if string is blank. By 'blank' I mean a string that is either empty or contains only space/newline characters.
struct MyString {
static func blank(text: String) -> Bool {
let trimmed = text.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
return trimmed.isEmpty
}
}
How to use:
MyString.blank(" ") // true
You can also use an optional extension so you don't have to worry about unwrapping or using == true:
extension String {
var isBlank: Bool {
return self.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
}
}
extension Optional where Wrapped == String {
var isBlank: Bool {
if let unwrapped = self {
return unwrapped.isBlank
} else {
return true
}
}
}
Note: when calling this on an optional, make sure not to use ? or else it will still require unwrapping.
To do the nil check and length simultaneously
Swift 2.0 and iOS 9 onwards you could use
if(yourString?.characters.count > 0){}
isEmpty will do as you think it will, if string == "", it'll return true.
Some of the other answers point to a situation where you have an optional string.
PLEASE use Optional Chaining!!!!
If the string is not nil, isEmpty will be used, otherwise it will not.
Below, the optionalString will NOT be set because the string is nil
let optionalString: String? = nil
if optionalString?.isEmpty == true {
optionalString = "Lorem ipsum dolor sit amet"
}
Obviously you wouldn't use the above code. The gains come from JSON parsing or other such situations where you either have a value or not. This guarantees code will be run if there is a value.
Check check for only spaces and newlines characters in text
extension String
{
var isBlank:Bool {
return self.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet()).isEmpty
}
}
using
if text.isBlank
{
//text is blank do smth
}
Swift String (isEmpty vs count)
You should use .isEmpty instead of .count
.isEmpty Complexity = O(1)
.count Complexity = O(n)
isEmpty does not use .count under the hood, it compares start and end indexes startIndex == endIndex
Official doc Collection.count
Complexity: O(1) if the collection conforms to RandomAccessCollection; otherwise, O(n), where n is the length of the collection.
Single character can be represented by many combinations of Unicode scalar values(different memory footprint), that is why to calculate count we should iterate all Unicode scalar values
String = alex
String = \u{61}\u{6c}\u{65}\u{78}
[Char] = [a, l, e, x]
Unicode text = alex
Unicode scalar values(UTF-32) = u+00000061u+0000006cu+00000065u+00000078
1 Character == 1 extended grapheme cluster == set of Unicode scalar values
Example
//Char á == extended grapheme cluster of Unicode scalar values \u{E1}
//Char á == extended grapheme cluster of Unicode scalar values \u{61}\u{301}
let a1: String = "\u{E1}" // Unicode text = á, UTF-16 = \u00e1, UTF-32 = u+000000e1
print("count:\(a1.count)") //count:1
// Unicode text = a, UTF-16 = \u0061, UTF-32 = u+00000061
// Unicode text = ́, UTF-16 = \u0301, UTF-32 = u+00000301
let a2: String = "\u{61}\u{301}" // Unicode text = á, UTF-16 = \u0061\u0301, UTF-32 = u+00000061u+00000301
print("count:\(a2.count)") //count:1
For optional Strings how about:
if let string = string where !string.isEmpty
{
print(string)
}
if myString?.startIndex != myString?.endIndex {}
I can recommend add small extension to String or Array that looks like
extension Collection {
public var isNotEmpty: Bool {
return !self.isEmpty
}
}
With it you can write code that is easier to read.
Compare this two lines
if !someObject.someParam.someSubParam.someString.isEmpty {}
and
if someObject.someParam.someSubParam.someString.isNotEmpty {}
It is easy to miss ! sign in the beginning of fist line.
public extension Swift.Optional {
func nonEmptyValue<T>(fallback: T) -> T {
if let stringValue = self as? String, stringValue.isEmpty {
return fallback
}
if let value = self as? T {
return value
} else {
return fallback
}
}
}
What about
if let notEmptyString = optionalString where !notEmptyString.isEmpty {
// do something with emptyString
NSLog("Non-empty string is %#", notEmptyString)
} else {
// empty or nil string
NSLog("Empty or nil string")
}
You can use this extension:
extension String {
static func isNilOrEmpty(string: String?) -> Bool {
guard let value = string else { return true }
return value.trimmingCharacters(in: .whitespaces).isEmpty
}
}
and then use it like this:
let isMyStringEmptyOrNil = String.isNilOrEmpty(string: myString)