Swift optionals and if let statement
When you do
if let num = Int(numTextField.text!){}
It will unwrap the value for you and check if it can set the value of textfield to num. If the value is nil you will be able to handle the error like this
if let num = Int(numTextField.text!){
print("Yes the value is not nil")
}else{
print("Couldn't assign value to num because it's nil")
}
If you do
var num = Int(numTextField.text!)!
and the textfield is nil, you will get a runtime error and your app will crash.
Unwrapping Optionals (Swift Playground)
As you know, ()
can be used to surround an expression and it will have no effect on the evaluation of that expression, and you are asking "why can't I do the same to let forSureNames = strArray
?" Right?
This is because let forSureNames = strArray
is not an expression. You are parsing this statement wrong. The word let
is part of the if let
statement.
You are confusing if let
statements with C-style if statements, where after the if
, there is always an expression that evaluates to Bool
. if let
simply does not work like that. It takes the form:
if let <identifier> = <expression> { }
where expression
must evaluate to an optional type. So putting ()
around let <identifier> = <expression>
makes little sense. You could put ()
around strArray
because that is an expression, but I don't see the point of that.
How does the '?' unwrap an optional in a case let declaration?
This is syntactic sugar for option patterns. The docs on option pattern says:
An optional pattern matches values wrapped in a
some(Wrapped)
case of anOptional<Wrapped>
enumeration. Optional patterns consist of an identifier pattern followed immediately by a question mark and appear in the same places as enumeration case patterns.
Thus, your code is the same as:
var x: Int? = 42
if case .some(let a) = x {
print(a)
}
It's not typical for simple if
statements as you can just do this instead:
if let a = x {
print(a)
}
But consider an enum wrapped in an optional:
enum Foo {
case bar
case baz
}
let y: Foo? = .bar
switch y {
case .none: break
case .some(.bar): break
case .some(.baz): break
}
This switch can be written in a more succinct way using some sugar:
switch y {
case nil: break
case .bar?: break
case .baz?: break
}
Unwrapping optionals to a pre-defined variable in the guard condition without creating a new constant
I would just test for nil
and then force unwrap when I know it's not:
var someString: String? = "hello"
let nonOptionalString: String // note, you don't have to initialize this with some bogus value
guard someString != nil else { return }
nonOptionalString = someString!
Or if someString
was a parameter to some method or closure, you can unwrap in the guard
statement using the same variable name, simplifying life even more:
func foo(someString: String?) {
guard let someString = someString else { return }
// now I can just use local `someString`, which is not optional anymore
}
If you're desperate to unwrap and exit-if-nil
in a single statement, you could theoretically write a function to unwrap if it can or throw an error if it can't:
extension Optional {
enum OptionalError: Error {
case unwrapFailed
}
func unwrap<T>() throws -> T {
if self == nil { throw OptionalError.unwrapFailed }
return self as! T
}
}
Then you can do:
do {
firstNonOptional = try firstOptional.unwrap()
secondNonOptional = try secondOptional.unwrap()
thirdNonOptional = try thirdOptional.unwrap()
} catch {
return
}
I think that's horrible overkill, but if you're desperate to distill it down to one line per unwrap, that's one way to do it.
swift if let statement with as?,but why use question mark?
Your code:
if let foo = object as? String
breaks into two significant parts: object as? String
and if let
.
The first is a downcast which allows for failure by producing an optional type (String?
here). From The Swift Programming Language (Swift 3.1): Typecasting:
Use the conditional form of the type cast operator (
as?
) when you are not sure if the downcast will succeed. This form of the operator will always return an optional value, and the value will benil
if the downcast was not possible. This enables you to check for a successful downcast.
The second, if let
, is an optional binding which unwraps an optional, the body of the if let
is only executed if the optional value being unwrapped is not nil
and with the bound variable being bound to the unwrapped value. See Optional Binding in The Swift Programming Language (Swift 3.1): Basics.
So your sense that something is happening twice here is correct. The as?
cast may fail and produce a nil
optional value, the if let
will test that optional value for nil
. We can only hope that the compiler handles the combination of if let
and as?
together and no unneccesary optionals are actually created and destroyed by the implementation.
HTH
Swift unwrapping for multiple optionals
If you're attempting to print the non-nil
values as a comma-separated list, then I think @MartinR's suggestion of using flatMap()
is the best:
let one: String?
let two: String?
let three: String?
one = "one"
two = nil
three = "three"
let nonNils = [one, two, three].flatMap { $0 }
if !nonNils.isEmpty {
print(nonNils.joinWithSeparator(","))
}
Output:
one,three
Swift: guard let vs if let
if let
and guard let
serve similar, but distinct purposes.
The "else" case of guard
must exit the current scope. Generally that means it must call return
or abort the program. guard
is used to provide early return without requiring nesting of the rest of the function.
if let
nests its scope, and does not require anything special of it. It can return
or not.
In general, if the if-let
block was going to be the rest of the function, or its else
clause would have a return
or abort in it, then you should be using guard
instead. This often means (at least in my experience), when in doubt, guard
is usually the better answer. But there are plenty of situations where if let
still is appropriate.
How is Swift `if let` evaluated?
Essentially the line is saying, "if you can let the new variable name
equal the non-optional version of optionalName
, do the following with it". As Martin pointed out, this is called Optional Binding.
The sole purpose of it is to test if an optional variable contains an actual value and bind the non-optional form to a temporary variable. This is the safe way to "unwrap" an optional or in other words, access the value contained in the optional. It is in no way testing for equality of any kind. It is only testing for the existence of a value within an optional.
Would you ever use the value assigned in a if let statement
In this example No , but imagine a situation where you want to do 10 tasks with the name
and pass it to multiple functions, in those cases yes.If you don't use it you have to write an if check for every single of those tasks and functions (because an optional causes other methods return value to be optional as well) which makes your code less readable and complicated without any reason.
Imaging 10
if name?.somefunction() != nil {
// do something if it is available
}else{
// it's not available. Do something appropriate
}
Related Topics
Updated Approach to Reauthenticate a User
Swift Implement Literalconvertible Protocol
Avaudioplayer.Play() Works But Avaudioplayernode.Play() Fails
How to Increment Exponentially in Swift
Is There a Neat Way to Represent a Fraction as an Attributed String
Prepare for Segue with Array - Xcode 8.0 Swift 3.0
Why Function Return Nil Firebase Swift
How to Use Core Data Value from Picker? #Swiftui #Coredata
Custom Cell with Uitableview Inside Uicollectionviewcell
Accessing Actor Properties Synchronously from Task-Less Context
Self. in Trailing Swift Closures, Meaning and Purpose
Nsurl from String Gets Truncated, Dots on the Uivideoeditorcontroller's Video Path Swift 2 iOS 8
Cfdictionary Get Value for Key in Swift3
Swift 3 Errors with Additional Data
Post Requests in Alamofire Parameter Encoding Returning Get Responses