Swift 1.2 added the ability to unwrap multiple optionals in a single if statement, and the ability to test for conditions at the same time:
if let a = optionalA, b = optionalB where a.foo == b.bar && a.frob == b.nicate {
// unwrapping successful
}
This introduces a dual hierarchy of conditions: evaluation moves forward only if optionals successfully unwrap, and only if each condition is met. My question, then, is how do they interleave? When are the conditions evaluated in this if statement?
I'm asking because I'd like to do something like this:
if let b = foo(i - 1) where i > 0 {
// code
}
In this example, there would be harmful side effects if foo was called with a negative value.
As it looks at the moment, the where clause will be called after all if let checks were done without a nil. To take a closer look into how it works, I suggest Nate Cooks blog. He mentioned this on this blogpost.
Using the new syntax, each binding is evaluated in turn, stopping if
any of the attempted bindings is nil. Only after all the optional
bindings are succesful is the where clause checked.
So it looks like you have to make the i > 0 check as you did it before by doing it before the unwrapping.
Related
I am using if let for getting the object if its not nil. But I also need to check other condition as well i.e., if "treatmentContext.patientTreatment.canWritePermissions.contains(treatmentContext.pathPatientTreatment.owner". That I am able to do by putting comma after the first statement but here the issue is, I need to unwrap the value of treatmentContext.pathPatientTreatment.owner and here I don't know where exactly I need to unwrap that so that my if condition gets pass when it meets all the criteria.
Below is the code for reference.
if let treatmentContext = IoC.resolve(Treatment.self, from: .treatment), treatmentContext.patientTreatment.canWritePermissions.contains(treatmentContext.pathPatientTreatment.owner)
{
self.presentNavigation(isNew: isNew)
}
You already know you can separate the conditions with ,, so just do that again, but this time it's another optional binding clause:
if let treatmentContext = IoC.resolve(Treatment.self, from: .treatment),
let owner = treatmentContext.pathPatientTreatment.owner,
treatmentContext.patientTreatment.canWritePermissions.contains(owner) {
self.presentNavigation(isNew: isNew)
}
You can separate any number of optional binding clauses, Bool conditions, or case ... patterns with , in an if.
Simple question for you folks about unwrapping optionals.
I've read and seen the multiple examples of unwrapping like the following
var strArray: [String]?
strArray = ["John", "Stacy", "Stephanie" ]
if let forSureNames = strArray{
for name in forSureNames{
print("\(name)")
}
} else{
print("Failed unwrapping")
}
However, My question is for if let forSureNames = strArray{...
When typing syntax similar to C++ (and from some swift examples), adding parenthesis
if (let forSureNames = strArray){
Gives the error codes:
'()' is not convertible to 'Bool'
error: MyPlayground.playground:13:4: error: expected expression in list of expressions
if(let forSureName = strArrays){
^
error: MyPlayground.playground:13:4: error: expected '{' after 'if' condition
if(let forSureName = strArrays){
^
Can anyone help explain the difference?
Edit
First time I asked a question on Stack overflow and feedback is awesome. I was trying to use a similar coding style to C++ due to my familiarity for an easy transition. However, you guys made it clear that it’s an incorrect approach. Thank you for a new and technical perspective towards unwrapping. Cheers!
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.
if (let forSureNames = strArray) {
... this is trying to use optional binding. Optional binding allows you to safely unwrap some optional constant/variable which can be nil. If this value is assigned, it satisfies the if statement with this unwrapped (non-optional) constant/variable.
But, it doesn't work with parentheses, since values coming from parentheses have to be of type Bool. An optional binding doesn't return a Bool value directly, it just attempts to assign the constant/variable and implicitly returns a boolean based on whether the assignment occurred.
if (true) {...} // works
if (let value = optional) {...} // doesn't work
So, you have to remove these parentheses
if let forSureNames = strArray {...}
Unlike other languages, conditions in Swift don't have to be inside parentheses and it is recommended not to use them if they aren't required.
Swift is vastly different from C++, so you shouldn't be surprised that C++ syntax doesn't work in Swift.
There are two type of if statements in Swift: the type that takes an expression (if foo.bar), and the type that does pattern matching (if let foo = bar, if case .some(let foo) = bar, etc).
Because Swift supports parenthesized expressions, if foo.bar works the same as if (foo.bar): in the first case, the condition is foo.bar, and in the second case, the condition is (foo.bar). In other words, the parentheses here are part of the condition expression, not part of the if statement. This contrasts with C++, where the parentheses are part of the if statement. In both cases, however, you can add as many parentheses as you want: if (((((((((foo.bar))))))))), though silly, is superficially valid in both languages.
Recall that you can wrap arbitrary expressions in parentheses, but you can't just wrap anything in parentheses. You wouldn't write (if foo.bar) in Swift or C++, because you know that if isn't an expression. Then, the same applies to let foo = bar. It's not an expression either, so you can't wrap it in parentheses. if let foo = bar is fine, but if (let foo = bar) isn't.
C++ supports a similar syntax in some cases:
if (auto foo = make_unique<int>(3)) { ... } else { ... }
This syntax declares foo and tests it (using its operator bool()), and then branches the program appropriately. Since the condition is a declaration statement, not an expression, it can't be wrapped in more parentheses either: if ((auto foo = make_unique<int>(3))) is a compile-time error, just like if (let foo = bar) in Swift.
Can anyone help explain the difference?
The difference is that Swift is not C++. Although they are similar in many ways, they have different grammars. The grammar defines (in part) the language's syntax.
In C++, an if statement is part of the selection-statement production in the grammar:
selection-statement:
if ( condition ) statement
if ( condition ) statement else statement
switch ( condition ) statement
So the parentheses around the condition of the if statement are part of the if statement, and not part of the condition.
In Swift, an if statement is the only production of the if-statement production in the grammar:
if-statement → if condition-list code-block else-clauseopt
Note that there are no parentheses directly in the if-statement's production. So if there are to be parentheses, they must be part of the condition-list production. Here's how condition-list is defined:
condition-list → condition | condition , condition-list
condition → expression | availability-condition | case-condition | optional-binding-condition
So a condition-list is one or more conditions, separated by commas. If you click through each of the four alternatives in condition, you'll find that availability-condition must start with the token #availability, case-condition must start with the token case, and optional-binding-condition must start with either the token let or the token var. None of those three productions can start with a parenthesis token.
The only production of condition that can start with the token ( is the expression production, and an expression production cannot have the token let after the token (.
I was wondering what this code does:
var something: String = "Hi"
if something = "Hello world!" {
// Will this be executed?
}
Will it assign to something variable and do the if body? Or will it set the value of that variable only for the if body and outside it will not change? Or has it anything to do with nil?
Assignments are not expressions that return booleans, so cannot be used inside an if like this. So this won’t compile.
(though you will get a misleading compiler message)
This pattern only works for assignments that can fail — that is, if you're assigning the result of an expression that returns an Optional value. And in that case, you use if let, not just if.
we can't use assignment operator for if condition, you would you if let suppose if you are working with optional types
Here are some operators that helps you get clarity to differentiate from assignment operator
= assignment operator
== is equal to
=== is identical to
I'm very new to swift, but proficient in other languages like Java, JavaScript, C, ... I'm lost with Swift syntax when it comes to create expressions. Look at this basic example where I just try to find out if one string is contained into another by calling String.rangeOfString that returns an Optional Range (Range?)
This works as expected:
let LEXEMA:String="http://"
let longUrl:String="http://badgirls.many/picture.png"
let range=longUrl.rangeOfString(LEXEMA);
if (range? != nil) {
// blah
}
Now I'm trying to combine the expression inside the if, something like:
if (longUrl.rangeOfString(LEXEMA)? !=nil) {
// blah
}
But I always get syntax errors, the above yields a "Expected Separator" and can't understand why. Done some more tests:
if (absolutePath.rangeOfString(URL_LEXEMA) !=nil) { }
Expected Separator before "!"
if absolutePath.rangeOfString(URL_LEXEMA) !=nil { }
Braced block of statements is an unused closure
What am I doing wrong?
If you’re coming from other like Java, you might be thinking of optionals like pointers/references, and so used to equating them to nil and if non-nil, using them. But this is probably going to lead to more confusion. Instead, think of them like a container for a possible result, that you need to unwrap to use. if let combines the test and unwrapping operation.
With this in mind, here’s how you could adapt your code:
let LEXEMA: String="http://"
let longUrl: String="http://badgirls.many/picture.png"
if let range = longUrl.rangeOfString(LEXEMA) {
// use range, which will be the unwrapped non-optional range
}
else {
// no such range, perhaps log an error if this shouldn’t happen
}
Note, that ? suffixing behaviour you were using changes in Swift 1.2 so even the code in your question that compiles in 1.1 won’t in 1.2.
It’s possible that sometimes you are whether there was a value returned, but you don’t actually need that value, just to know it wasn’t nil. In that case, you can compare the value to nil without the let:
if longUrl.rangeOfString(LEXEMA) != nil {
// there was a value, but you don't care what that value was
}
That said, the above is probably better expressed as:
if longUrl.hasPrefix(LEXEMA) { }
For starters:
You don't need parenthesis with if statements unless you have nested parenthetical subexpressions that require it.
You don't need to specify the type on the left side of the = of a let or var declaration if Swift can figure it out from the right side of the =. Very often Swift can figure it out, and you can tell that Swift can figure it out, so you can avoid that redundant clutter.
You do need to specify the type if Swift cannot figure out the type from
the right side. Example:
For example, consider the following lines:
let LEXEMA = "http://"
let longUrl = "http://badgirls.many/picture.png"
Swift can figure out that they're strings.
Similarly for this function or class that returns a UIView:
var myView = ViewReturningClassOrFunc()
Consider this:
#IBOutlet var myView : UIView!
In the above line, Swift cannot figure out ahead of time it will be assigned a UIView, so you have to provide the type. By providing a ! at the end you've made it an implicitly unwrapped optional. That means, like ?, you're indicating that it can be nil, but that you are confident it will never be nil at the time you access it, so Swift won't require you to put a ! after it when you reference it. That trick is a time saver and big convenience.
You should NOT add the ? to the line:
if (longUrl.rangeOfString(URL_LEXEMA) !=nil) {
As another answer pointed out, you're missing the let.
if let longUrl.rangeOfString(URL_LEXEMA) {
println("What do I win? :-)")
}
swift is case sensitive language. you need to check about whitespaces as well
if longUrl.rangeOfString(LEXEMA) != nil {
//your condition
}
there should be space between statement != nil
Just add a space between != and nil like:
if longUrl.rangeOfString(LEXEMA) != nil {
// blah
}
I tested your code in playground, an error of Expected ',' separator reported.
And do not forget the rules that 1s and 0s and Airspeed Velocity said.
Why do assignments involving Swift optionals type check?
For example in,
var foo : Int? = 0
foo = foo!
foo and foo! do not have the same type. Shouldn't you need to wrap the unwrapped value to assign it to an optional type?
This is part of the syntactic sugar behind optionals. Assigning a non-optional value is how you wrap it in the optional type.
Since an optional indicates the presence or absence of a value, you shouldn't have to do anything special to indicate the presence of a value other than provide one. For example, in a function:
func gimmeSomethingMaybe() -> String? {
if arc4random_uniform(10) > 7 {
return "something"
} else {
return nil
}
}
Imagine if every time you wanted to return a real value from a function that's capable of returning nil, you had to write return Optional(value). That'd get old pretty fast, right? Optionals are an important feature of the language — even though they're actually implemented by the standard library, the syntactic sugar / automatic wrapping is there to keep it from being tedious to use them.
Edit: just to go a bit further into this... the sugar also helps to enforce the notion that a real value should not be optional. For example:
let one = 1
one? // error (in Swift 1.2, allowed but meaningless in Swift 1.1)
"two"? // error (ditto)
You can create an optional wrapping a real value with the Optional(one) initializer, but that has little semantic meaning on its own, so you almost never need to.
Optionals should come into play when there's "mystery" as to whether a value is present or absent — that is, when whether one part of a program receives a value (or no value) depends on state unknown to that part of the program. If you know you have a real value, there's no mystery... instead, you let the unknown come into play at the boundary between the code that knows the value and the code that doesn't know — that is, the function/method/property definition that hands that value off to somewhere.
After reading rickster's answer, I came up with a simple laymen terms answer. To me the whole whole gist of his answer is
Since an optional indicates the presence or absence of a value, you
shouldn't have to do anything special to indicate the presence of a
value other than provide one
An optional is an enum. Which has 2 cases a or b.
String?
|
An enum with 2 cases
|
a b
| |
Set notSet
| |
any value like "hi" nil
So you could do either of the things when you want to assign to an optional.
Say the value is either:
a: it's set to "hi"
b: it's not set, so it's nil
c: it just equals to another enum ie another optional
code:
var str : String?
var anotherOptional : String?
str = nil // nil <-- this is like case b
str = "hi" // "hi" <-- this is like case a
str = anotherOptional // nil <-- this is like case c
anotherOptional = "hi again"
str = anotherOptional // "hi again" <-- this is like case c