guard let with logical operators - swift

How can I use guard let like:
guard let value = vm.value1 || let value = vm.value2 else { return }
I need to check value1, If it has value, continue to work with it, else check value2, and work with it, else: quit. Only one can have value.

The semantics you are describing seems to be:
guard let value = vm.value1 ?? vm.value2 else { return }
If vm.value1 is not nil, value would be bound to its value, and the code after the guard statement would be executed.
Otherwise, if vm.value2 is not nil, value would be bound to its value, and the code after the guard statement would be executed.
Otherwise, return would be executed.
Similarly, multiple lets could be used to achieve something similar to the semantics of the logical "AND":
guard let value1 = vm.value1, let value2 = vm.value2 else { return }
The code after the guard statement is only executed if vm.value1 is not nil, and vm.value2 is not nil, and value1 and value2 are bound to the corresponding values.
Also note that you can mix arbitrary boolean conditions with the let bindings too:
guard let value = vm.value1, someBool || someOtherBool else { return }

You can't use logical operator with guard statement
But there is another way of performing AND operator
guard let value = vm.value1,
let value = vm.value2 else {
return
}
And OR operator functionality can be achieve by using ternary operator with guard statement
guard let value = ((vm.value1 != nil) ? vm.value1 : vm.value2 else {
return
}
And you can use the value after else statement

Related

Using nil-coalescing operator with try? for function that throws and returns optional

I want to use nil-coalescing operator to set a default value in both the following cases:
function throws an error
function returns nil
Please take a look at the code snippet below. I have the following questions:
Why is item1 nil?
What is the difference between the initialization of item1 vs item2
enum VendingMachineError: Error {
case invalidCode
}
class VendingMachine {
func itemCode(code: Int) throws -> String? {
guard code > 0 else {
throw VendingMachineError.invalidCode
}
if code == 1 {
return nil
} else {
return "Item #" + String(code)
}
}
}
let machine = VendingMachine()
// Question: Why is this nil?
let item1 = try? machine.itemCode(code: 0) ?? "Unknown"
print(item1)
// nil
// What is the difference between the initialization of item1 vs item2
let item2 = (try? machine.itemCode(code: 0)) ?? "Unknown"
print(item2)
// Unknown
Essentially, this has to do with the grammar of the try operator. When used with a binary expression without brackets, try applies to the whole binary expression, so this:
try? machine.itemCode(code: 0) ?? "Unknown"
is the same as:
try? (machine.itemCode(code: 0) ?? "Unknown")
Since itemCode throws an error, the latter part of the expression ?? "Unknown is ignored, and the try? expression evaluates to nil.
On the other hand, the second expression is like this:
(try? machine.itemCode(code: 0)) ?? "Unknown"
The try? expression is evaluated first (to nil), then the ?? is applied, evaluating the whole expression to "Unknown".

Can I use "guard let" if you want to pass over an optional string to "rawValue:" in enum in Swift?

I want to initialize a enum from a variable of type String?, like:
guard let rawId = request.queryParameters["id"] else {
return
}
guard let id = MyIdentifier(rawValue: rawId) else {
return
}
In this case, request.queryParameters["id"] returns String?. Then after I ensure that it is String in rawId, I convert it into an enum instance id.
However, the code is dirty and I want to write it in one-line if at all possible.
However, I don't like to make it unwrapped via forced optional unwrapping, because if it can not be transformed to String, the app would end up with an error, since rawValue: only takes String. I meant something like the following, which I don't like:
guard let id = MyIdentifier(rawValue: request.queryParameters["id"]!) else {
return
}
So is it still possible to define the guard let in one-line, maybe using where and/or case in guard?
You have two conditions there, trying to combine them into one condition is not always possible.
In your exact case I believe an empty id will behave the same as a nil id, therefore nil coalescing can be used:
guard let id = MyIdentifier(rawValue: request.queryParameters["id"] ?? "") else {
return
}
However, there is nothing dirty about splitting two checks into two statements. Code is not written to be short, it's written to be clear:
guard let rawId = request.queryParameters["id"],
let id = MyIdentifier(rawValue: rawId) else
return
}
Also, there is nothing wrong with creating a custom initializer for your enum:
init?(id: String?) {
guard let id = id else {
return nil
}
self.init(rawValue: id)
}
and then
guard let id = MyIdentifier(id: request.queryParameters["id"]) else {
return
}
Try this:
You can simply combine both the statements into a single statement ,i.e,
guard let id = request.queryParameters["id"], let id2 = MyIdentifier(rawValue: id) else {
return
}

Multiple conditions in guard statement in Swift

Is there a way to include multiple conditions in a guard statement of Swift?
For example, if I want to check two optional values are nil using a guard, how should I do it using single guard statement?
Check this code
func demo(){
var str = [String: String]()
str["status"] = "blue"
str["asd"] = nil
guard let var2 = str["asd"], let var1 = str["status"]
else
{
print("asdsfddffgdfgdfga")
return
}
print("asdasdasd")
}
Guard will check one by one condition. If the first is true then it will check the next. Otherwise, it will execute the else part.
To answer Prabhav's question, yes, you are correct, each condition in a guard statement must be true in order to proceed (i.e., not go into the else block). In this sense, it is indeed like separating conditions with AND logic.
You can implement OR logic, not by using commas, but by using a Boolean condition:
guard
true || false // this guard statement evaluates to true
else
{
print("no, not all values in the guard were true")
return
}
print("yes, all of the values in the guard were true") // this is printed
or a combination of OR and AND logic, by using a combination of Boolean conditions and optional bindings:
let testString: String? = nil
guard
true || false,
let x = testString, // this guard statement evaluates to false
true
else
{
print("no, not all values in the guard were true") // this is printed
return
}
print("yes, all of the values in the guard were true")
This summary from Apple, written about optional bindings in if statements is equally applicable to guard statements:
You can include as many optional bindings and Boolean conditions in a
single if statement as you need to, separated by commas. If any of the
values in the optional bindings are nil or any Boolean condition
evaluates to false, the whole if statement’s condition is considered
to be false. The following if statements are equivalent:
if let firstNumber = Int("4"), let secondNumber = Int("42"),
firstNumber < secondNumber && secondNumber < 100
{
print("\(firstNumber) < \(secondNumber) < 100")
} // Prints "4 < 42 < 100"
if let firstNumber = Int("4")
{
if let secondNumber = Int("42")
{
if firstNumber < secondNumber && secondNumber < 100
{
print("\(firstNumber) < \(secondNumber) < 100")
}
}
} // Prints "4 < 42 < 100"

Swift-y boolean expression - case outside if/switch

To continue to the next screen, a patron must have one of two identifiers. The code I've got to do this is:
let identifier1Entered = !patron.identifier1.isEmpty
let identifier2Entered = patron.identifier2 != nil && !patron.identifier2!.isEmpty
guard identifier1Entered || identifier2Entered else { return }
But it's not Swifty, I'm force-unwrapping the optional identifier2, because I don't want to expand this to a longer, and IMO messier
var identifier2Entered = false
if let identifier2 = patron.identifier2 where !identifier2.isEmpty {
identifier2Entered = true
}
What I thought might work is just taking the expression out of the if statement, like:
let id2Entered = let identifier2 = patron.identifier2 where !identifier2.isEmpty
or
let id2Entered = case .Some(let id2) = patron.identifier2 where !id2.isEmpty
But it appears that these expressions are only allowed within if statements.
The other more Swifty solution I thought of is this:
let identifier1Entered = !patron.identifier1.isEmpty
guard let id2 = patron.identifier2 where !id2.isEmpty || identifier1Entered
else { return }
But it requires that identifier2 is not nil, which is incorrect, and as far as I know, there's no way to use optional binding with || in if or guard statements. Not to mention that I feel it's less clear and readable than the force-unwrapping.
Anyone have a clear, more Swifty solution?
Two possible solutions using optional chaining:
let identifier2Entered = patron.identifier2?.isEmpty == false
let identifier2Entered = !(patron.identifier2?.isEmpty ?? true)
If patron.identifier2 is nil then patron.identifier2?.isEmpty
evaluates to nil, and you'll get false as a result.

Using guard with a non-optional value assignment

This is not a question about optional arrays, as one can see in the answers.
I like using guard because it makes your intensions clear. I've used it both for the optional version like this...
guard let c = MyOptionalArray else { return }
as well as for more traditional bounds checking on non-optionals...
guard MyArray.count > 0 else { return }
But now I'd like to use that count in following code. So I did...
guard let c = MyArray.count > 0 else { return }
which doesn't work, obviously, so I did what should...
guard let c = parts.count where c > 1 else { return }
But that says Initializer for conditional binding must have Optional type, not 'Int'. Now I understand the error, and tried a bunch of seemingly obvious changes to the format, but no go. Is there no way to use guard as an assignment on a non-optional value? This seems like something it should be able to do.
If you throw a case in there, it'll work. So as follows:
guard case let c = parts.count where c > 1 else { return }
You can initialize an optional wrapping the non-optional:
guard let count = Optional([42].count), count > 0 else {
return
}
guard let count = .some([42].count), count > 0 else {
return
}
or cast it to an optional:
guard let count = [42].count as Int?, count > 0 else {
return
}
As mentioned in other answers, guard case let also works:
guard case let count = [42].count, count > 0 else {
return
}
guard case let count = [42].count where count > 0 else {
return
}