What is this Swift ternary operator? A == B ? C : D - swift

I inherited an iOS app that is crashing at this line (unexpected nil). What is your best interpretation of what is going on here?
indexPath.row.number == selectedItem ? cell.deselectStyle() : cell.dedeselectStyle()
The cell.deselectStyle() and cell.dedeselectStyle() functions don't return anything. I can't seem to find any information on what is going on here. selectedItem is a NSNumber!.

NSNumber could be nil leading to a crash if you try to access it. Add a guard to check that is not nil.
guard let s = selectedItem?.intValue else {
cell.dedeselectStyle
return
}
indexPath.row == s ? cell.deselectStyle() : cell.dedeselectStyle()
I've assumed it's safe to assume the cell is not selected if the NSNumber is nil, however you should really check the logic in your code to be sure.

This is a conditional statement. Think of it like an if statement:
if indexPath.row.number == selectedItem {
cell.deselectStyle()
} else {
cell.dedeselectStyle()
}
If the condition is true, the code between ? and : will be executed. If not, the code after the : will be called. You should know that the ? has nothing to do with Optionals.
In your case, selectedItem seems to be nil. Therefore, you need to either only execute the code if selectedItem is not nil, you could use an if let statement:
if let selectedItem = selectedItem {
indexPath.row.number == selectedItem ? cell.deselectStyle() : cell.dedeselectStyle()
}
Or, you could insert a default value that will be used instead of selectedItem if it is nil:
indexPath.row.number == (selectedItem ?? false) ? cell.deselectStyle() : cell.dedeselectStyle()
The code above will use either the value of selectedItem, or false, if selectedItem is nil. You can leave out the parentheses, I just put them there for better visualization.
I hope this helps :)

Probably The reason the crash is that selectedItem is nil; I think you misunderstand the difference between the ? for optionals and the ? for implementing the ternary operator.
The ternary operator has nothing to do with -implicitly- checking if the value is nil, you might need to implement a guard statement or if let (optional binding) before the step of doing the ternary operator comparison.
It should be similar to:
if let selectedItem = selectedItem {
indexPath.row.number == selectedItem ? cell.deselectStyle() : cell.dedeselectStyle()
} else {
print("selectedItem is nil!")
}
Or, if an early return required:
guard let selectedItem = selectedItem else {
print("selectedItem is nil!")
return
}
indexPath.row.number == selectedItem ? cell.deselectStyle() : cell.dedeselectStyle()
To make it more clear to you, check the following code snippet:
let nilValue: String! = nil
if nilValue == "test" { }
// fatal error: unexpectedly found nil while unwrapping an Optional value
// OR
let isValueTest: Bool = (nilValue == "test") ? true : false
// fatal error: unexpectedly found nil while unwrapping an Optional value
That's why you should unwrap the value before accessing it.

to better understand it, play in your Playground
func f0() -> Int { print(0); return 0 }
func f1() -> Int { print(1); return 1 }
func fa() -> String { print("a"); return "a" }
[true, false, true, true].forEach {
$0 ? f0() : f1() // warning: expression of type 'Int' is unused
}
[true, false, true, true].forEach {
_ = $0 ? f0() : f1() // OK
}
[true, false, true, true].forEach {
$0 ? f0() : fa() // error: result values in '? :' expression have mismatching types 'Int' and 'String'
}
[true, false, true, true].forEach {
_ = $0 ? ( 1 == f0()) : ( "A" == fa()) // OK
}

Related

How to check the string doesn’t contain any letters in swift?

i have trouble during making the letter checker, my code is like this: if !containLetters(“1234h”){print(“pass”)}
my function is
func containsOnlyNum(input: String) -> Bool {
var ok = false
for chr in input {
for check in "1234567890.-"{
if chr == check{
ok = true
}
}
if ok != true{
return false
}
}
return true
}
If I check for “h” then didn’t pass, but if i check for ”1h” then it still pass! Please help me to fix this problem. I will give a big thank for anyone who helped me
The simplest way to fix the algorithm is this way:
func containsOnlyNum(input: String) -> Bool {
// check every character
for chr in input {
var isNum = false
for check in "1234567890.-"{
if chr == check {
isNum = true
// if we have found a valid one, we can break the iteration
break
}
}
if !isNum {
return false
}
}
return true
}
print(containsOnlyNum(input: "1234")) // true
print(containsOnlyNum(input: "1234h")) // false
However, then you can directly simplify it to:
func containsOnlyNum(input: String) -> Bool {
return input.allSatisfy { chr in
"1234567890.-".contains(chr)
}
}
which does exatly the same but uses allSatisfy and contains functions, which represent the logical operators ALL and EXISTS.
However, programmers normally use regular expressions for similar tasks:
func containsOnlyNum(input: String) -> Bool {
return input.range(of: "^[0-9.\\-]+$", options: .regularExpression) != nil
}
You can check that a string contains only the characters you're interested in like this:
extension String {
var containsOnlyNum: Bool {
let wanted = CharacterSet.decimalDigits
.union(CharacterSet(charactersIn: "-."))
return unicodeScalars
.allSatisfy(wanted.contains)
}
}
"-12.34".containsOnlyNum // true
"A1234".containsOnlyNum // false
But if you are interested in numbers, then this is a problem:
"-12.-34.".containsOnlyNum // true
Instead, you can just try casting the string to a double and see if it is a number or not
Double("1234") != nil // true, a number
Double("-1.234") != nil // true, a number
Double("A1234") != nil // false, not a number
Double("-12.-34.") != nil // false, not a number
Which is almost right unless you don't want this case:
Double("1234e2") != nil // true, a number
But you can use both checks if you don't want to allow that, or else if you are able to parse a Double from the input you can just do the cast.

why (self.account.text?.isEmpty)! is required '!'

if (self.account.text?.isEmpty)! {
}
I just write down this function in ViewDidLoad().
(account is a textfield)
I think that 'self.account.text' can be nil, and 'self.account.text?' is okay.
but I don't know why 'self.account.text?.isEmpty' require '!' - unwrapping.
'self.account.text?.isEmpty' is right, isn't it....?
I always appreciate all answers.
It's because the expression self.account.text?.isEmpty has a value of type bool or the value nil, and you can't use nil as an if-condition.
Using ! here is wrong. If text is nil then the code will crash.
Instead write:
if self.account.text?.isEmpty == true { ... }
// or equivalently:
if self.account.text?.isEmpty ?? false { ... }
if you want to skip the if when text is nil, or:
if self.account.text?.isEmpty != false { ... }
// or equivalently:
if self.account.text?.isEmpty ?? true { ... }
if you want to enter the if when text is nil.
If text should never be nil here then you can just use ! directly after text:
if self.account.text!.isEmpty { ... }
self.account.text
is optional boolean value so it will be nil or boolean. unwrapping the boolean must check it is equal to true or false like in this way.otherwise it will crash because of nil value.
import UIKit
var isempty: Bool?
if isempty == true
{
print("i am empty")
}
else
{
print("hy i am not empty")
}
//it will print "Hy i am not empty" because isempty has nil

Swift: Setting variables if not equalling nil

I'm using Swift and I'm wondering how to achieve this in 1 line of code:
If a != nil, myVariable = a
else if a == nil, myVariable = b
I need to do this so it fits with
var myVariable = a {
didSet{
//Do something
}
}
Is there a way to do this in 1 line so I don't have to change it elsewhere and trigger the didSet{}?
Something like this:
if var myVariable = a
else myVariable = b {
didSet{
//Do something
}
}
Or...
var if a != nil {myVariable = a}
else {myVariable = b} {
didSet{
//Do something
}
}
I know this may take some time to realise what I mean, I tried my best to explain it.
EDIT --->
The compiler says:
Left side of nil coalescing operator '??' has a non-optional type '[[Bool]]', so the right side is never used
and
Treating a forced downcast to '[[Bool]]' as optional will never produce 'nil'
This is what it looks like using a coalescing operator:
var purchased =
UserDefaults.standard.array(forKey: "purchased") as! [[Bool]] ??
[[true, false], [true, false], [true, false], [true, false], [true, false]] { //a is left, b is right
didSet {
//Do something
}
}
Any ideas would be greatly appreciated. Thank you.
How about
var myVariable = a ?? b
This sets myVariable to a except for the case when a == nil, then it sets myVariable to b. didSet would be called once with the respective value. It is called a "nil coalescing operator".
Your edited questions shows that a in your case cannot be nil because you force unwrap the array (with as!). Instead you should conditionally unwrap it (as?), so that your code can check for nil and use the hard-coded default instead.
//Sets my variable to a if a != nil, else b.
var myVaraiable = (a != nil ? a : b)
Since a is optional and b is not, it would be more like
var myVariable = (a != nil ? a! : b)

Checking values within tuple that contains only booleans

I've constructed a tuple:
var groupUsersFlags = (false, false, false, false)
Throughout the screen these values get flipped over to true. So:
groupUsersFlags.0 = true
groupUsersFlags.1 = true
etc..
Is there any way to check if all the values are true at some point?
I would agree with the idea of using another data type in this case.
However, to answer the question, you can check if all the values are true like this:
groupUsersFlags.0 && groupUsersFlags.1 && groupUsersFlags.2 && groupUsersFlags.3
Or, using reflection (use as your own risk):
Mirror(reflecting: groupUsersFlags).children.reduce(true) { $0 && $1.value as! Bool }
let x = Mirror(reflecting: groupUsersFlags)
var isAllTrue = true
for value in x.children.enumerate() where value.element.value as? Bool != nil{
if value.element.value as! Bool == false {
isAllTrue = false
break
}
}
Try out this solution.

swift reflection causes impossible nil value for any

I'm trying to use swift reflection to check for changes in objects so I can send only changed properties up to the server. Some of my properties are optional. To compare those values, I need to unwrap them but, of course, you can ONLY unwrap actual values, not nil values. So, I need to check if one of the values is nil before I compare them.
In my playground, I tried the following:
import UIKit
class myClass
{
var fieldOne:String?
var fieldTwo:Int?
var fieldThree:Float?
}
var oneMyClass = myClass()
oneMyClass.fieldOne = "blah"
oneMyClass.fieldThree = 3.5
var oneOtherClass = myClass()
oneOtherClass.fieldOne = "stuff"
oneOtherClass.fieldTwo = 3
let aMirror = Mirror(reflecting: oneMyClass)
let bMirror = Mirror(reflecting: oneOtherClass)
for thing in aMirror.children
{
for thing2 in bMirror.children
{
if thing.label! == thing2.label!
{
print("property: \(thing.label!)")
print("before: \(thing.value)")
print("after: \(thing2.value)")
print("")
//let myTest = thing.value == nil ? "nil" : "not nil"
}
}
}
And it generates the following output:
property: fieldOne
before: Optional("blah")
after: Optional("stuff")
property: fieldTwo
before: nil
after: Optional(3)
property: fieldThree
before: Optional(3.5)
after: nil
As you can see, the expected properties are displayed as "nil". However, if you uncomment the let statement, you get an error stating:
playground52.swift:37:38: error: value of type 'Any' (aka 'protocol<>') can never be nil, comparison isn't allowed
And yet, we know from the output that it IS nil. How can this be and what can I do about it?
Based on this answer, I recommend using if case Optional<Any>.some(_).
For example:
aMirror.children.forEach {
guard let propertyName = $0.label else { return }
if case Optional<Any>.some(_) = $0.value {
print("property: \(propertyName) is not nil")
} else {
print("property: \(propertyName) is nil")
}
}
Thats look like some sort of bug. Look at that
let x = childMirror.value == nil ? "Nil" : "Not Nil" //dont compile.
let y = { (value:Any?) in
return value == nil ? "Nil" : "Not Nil"
}
let z = y(childMirror.value) //compile, but doesn't evaluate.
I guess the problem is because Any can store a Optional, but can't be wrapped around one. Try this:
func getValue(unknownValue:Any) -> Any {
let value = Mirror(reflecting: unknownValue)
if value.displayStyle != .Optional || value.children.count != 0 {
return "Not Nil"
} else {
return "Nil"
}
}