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.
Related
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
unable to build swift project because of this error.
// showing error with inputs.flatmap
fileprivate func makeShippingAddressDictWith(inputs: [TextFieldData]) -> [String: String] {
var shippingDict: [String: String] = [:]
let _ = inputs.flatMap { input in
if let shippingFieldType = input.type as? ShippingDictKeyable.Type {
shippingDict[shippingFieldType.shippingDictKey] = input.text
}
return nil
}
// FIXME: these empty values are the result of a poorly designed request in GDKECommerce
shippingDict["email"] = ""
shippingDict["second_name"] = ""
shippingDict["suffix"] = ""
shippingDict["title"] = ""
shippingDict["salutation"] = ""
shippingDict["company_name"] = ""
return shippingDict
}
}
You could use .forEach instead of .flatMap. Then you would not have to worry about a return type that you are ignoring anyway (with let _ =).
Combining this with a filter would produce a cleaner functional statement if that's what you're after:
inputs.map{ ( $0.text, $0.type as? ShippingDictKeyable.Type) }
.filter{ $1 != nil }
.forEach{ shippingDict[$1!.shippingDictKey] = $0 }
// FIXME: these empty values are the result of a poorly designed request in GDKECommerce
let blankAttributes = ["email", "second_name", "suffix", "title", "salutation", "company_name"]
blankAttributes.forEach{ shippingDict[$0] = "" }
Or use a for loop as suggested by Hamish.
If performance is a factor, the compiler will produce faster code with the for loop than with map/filter/forEach.
Note that, if you want to go crazy with functional style, Swift 4 will let you return the whole dictionary in a single line:
return [String:String]( uniqueKeysWithValues:
inputs.map{ ($0.type as? ShippingDictKeyable.Type, $0.text) }
.filter{ $0.0 != nil }
.map{($0!.shippingDictKey,$1)}
+ ["email", "second_name", "suffix", "title", "salutation", "company_name"]
.map{($0,"")}
)
This may only work in the playground though cause real projects tend to complain about expressions being too complex more often.
I am having trouble with this method; I am getting a "fatal error: unexpectedly found nil while unwrapping an Optional value" error.
let filtererArr = structArrayWithNoOptionals.filter({
return getFloatNumberFromPriceString(removeCommaFromPriceString($0.totalFare!)) >= minPrice && getFloatNumberFromPriceString(removeCommaFromPriceString($0.totalFare!)) <= maxPrice
})
How do I fix this to prevent it from crashing?
I always try to avoid force unwrapping when possible, in your case you can use guard let or if let to unwrap it
Change your code this
let filtererArr = structArrayWithNoOptionals.filter({
guard let totalFare = $0.totalFare else { return false }
return getFloatNumberFromPriceString(removeCommaFromPriceString(totalFare)) >= minPrice &&
getFloatNumberFromPriceString(removeCommaFromPriceString(totalFare)) <= maxPrice
})
Like Niko said (+1), you should avoid ! forced unwrapping. Perform optional binding via if let or guard let instead.
Personally, rather than using removeCommaFromPriceString and getFloatNumberFromPriceString, I'd probably use a NSNumberFormatter.
For example, in Swift 3:
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
let filtererArr = structArrayWithNoOptionals.filter {
guard let string = $0.totalFare, let totalFareValue = formatter.number(from: string)?.doubleValue else { return false }
return totalFareValue >= minPrice && totalFareValue <= maxPrice
}
The number formatter will automatically handle strings with thousands separators (which I assume is your intent of removing the commas), but it will also handle international number formats (e.g. in Germany, the thousands separator is . and the decimal point is ,).
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"
I wish to use guard-let to assign a variable to an expression, but I want to modify the expression before assigning. If the expression is nil, then the else block should be entered, otherwise the variable should be assigned to f(expression). Here is an example of what I would like to do:
let arr: [Int] = []
// Do stuff, maybe add elements to arr
guard let x = abs(arr.first) else { return } // Syntax error
// If arr was nonempty, then we want x = abs(arr.first!)
But Swift does not allow this syntax because abs requires a non-optional argument, and arr.first is optional. So is there any way to evaluate arr.first, and then if it is not nil to assign abs(arr.first!) to x? I know that I could do this with if-let or by using two variables (one from the guard-let and then one that gets assigned to the absolute value of that variable). But guard-let seems like the tool for the job, if only there were some way to accomplish this.
let arr:[Int] = [-1,1,3,-9]
guard let x = arr.first.flatMap({ $0 < 0 ? -$0: $0 }) else { return }
// ...
or (UPDATE based on dfri's notes)
// ....
let arr:[Int] = [-1,1,3,-9]
guard let x = arr.first.map(abs) else { return }
Optional(Some<Int>) -> Int -> Optional<abs(Some<Int)> -> Int ... meh
You could do a dirty guard let ..., let ... else fix as follows (forcing the binded certainly-not-nil value of x to become an optional which you subsequently immediately unwrap and bind to xAbs)
func foo() {
let arr: [Int] = [-1, 2, -3, 4]
guard let x = arr.first,
let xAbs = Optional(abs(xAbs)) else { return }
print(xAbs, xAbs.dynamicType)
}
foo() // 1 Int
This doesn't look very pretty however, and I would, personally, prefer adding an Int extension and make use of optional chaining, as I will cover next.
Instead: use extensions and optional chaining
Unless you explicitly need to store x as well as xAbs, an alternative and more Swifty approach is to use optional chaining in combination with a simple extension to Int:
extension Int {
var absValue: Int { return abs(self) }
}
func foo() {
let arr: [Int] = [-1, 2, -3, 4]
guard let xAbs = arr.first?.absValue else { return }
print(xAbs, xAbs.dynamicType)
}
foo() // 1 Int
Since arr.first is an optional Int variable, you can implement whatever method/computed property you wish onto self as an extension to Int, and access that method/property using optional chaining arr.first?.someMethod()/arr.first?.someProperty (as .absValue above).
Or, simply modify your arr.first (unwrapped) value after the guard let ... else block
I see no reason, however (other than the technical discussion) not to introduce an additional immutable holding the absolute value of x. This will also increase code readability, at least w.r.t. to the dirty guard let ..., let ... else fix above.
// ...
guard let x = arr.first else { return }
let xAbs = abs(x)
Or, if you find it acceptable for your xAbs property to be mutable, out of a theoretical perspective your could remove the middle-man immutable by using a guard var ... block rather than guard let ...
guard var xAbs = arr.first else { return }
xAbs = abs(xAbs)
This should probably only be used, however, if xAbs is to be mutated again (i.e., use immutables whenever you really don't need mutables, and never the other way around).
I think the cleanest and simplest solution would be like this:
guard let first = arr.first else { return }
let x = abs(first)
Now the calculation abs(first) is only reached if arr.first != nil.
What you want can be achieved using case let.
let arr: [Int] = [1,2,3,4]
guard let first = arr.first, case let absolute = abs(first) else { return }
// use `absolute`