What does "??" stand for in the context of: let x = y ?? "Candy bar" - swift

I'm new to Swift. Could someone please tell me what "??" stands for and what is its function in the context of
let snackName = favoriteSnacks[person] ?? "Candy bar"
I have included the complete code below:
struct Item{
var price: Int
var count: Int
}
class VendingMachine {
var inventory = [
"Candy bar": Item(price: 12, count: 7),
"Chips": Item(price: 10, count: 4),
"Pretzels": Item(price: 7, count: 11)
]
var coinsDeposited = 0
func dispenseSnack(snack: String){
print("dispensing \(snack)")
}
func vend(itemNamed name: String) throws {
guard var item = inventory[name] else {
throw VendingMachineError.InvalidSelection
}
guard item.count > 0 else {
throw VendingMachineError.OutOfStock
}
guard item.price <= coinsDeposited else {
throw VendingMachineError.InsufficientFunds(coinsNeeded: item.price - coinsDeposited)
}
coinsDeposited -= item.price
--item.count
inventory[name] = item
dispenseSnack(name)
}
}
let favoriteSnacks = [
"Alice": "Chips",
"Bob": "Licorice",
"Eve": "Pretzels",
]
func buyFavoriteSnack(person: String, vendingMachine: VendingMachine) throws {
let snackName = favoriteSnacks[person] ?? "Candy bar"
try vendingMachine.vend(itemNamed: snackName)
}

Its used to checking nil, if your value will be nil, you will assign default value.
let favoriteSnacks = [
"Alice": "Chips",
"Bob": "Licorice",
"Eve": "Pretzels",
]
from your code,
suppose person = "Eve"
let snackName = favoriteSnacks["Eve"] ?? "Candy bar"
it will first try to find value from dictionary, i.e. favoriteSnacks["Eve"] will give you value "Pretzels".
so it will assign value Pretzels to snackName variable.
suppose person = "Allen"
let snackName = favoriteSnacks["Allen"] ?? "Candy bar"
it will first try to find value from dictionary, i.e. favoriteSnacks["Allen"] will give you value nil.
In this case it will assign defalut value "Candy bar" to your variable snackName.

Let me please assume here based on its meaning in other languages:
let snackName = favoriteSnacks[person] ?? "Candy bar"
?? checks if the value of favoriteSnacks[person] is null. If it is NOT then the value of favoriteSnacks[person] will be assigned to snackName. If the value of favoriteSnacks[person] is null then "Candy bar" will be assigned to snackName.
It is used for having a default value in case favoriteSnacks[person] comes null.

Its the Nil Coalescing Operator basically giving a default for when the value does not exist.
see also Providing a default value for an Optional in Swift?

Ex. if you get only 5 keys and values in dictionary, but some time getting nil key or value, then error will be come for nil value.
So, here solution for that,
var strName = jsonObjectName ?? "Unknown"
var intAge = jsonObjectAge ?? 25
it means, if jsonObjectName is nil then automatically "Unknown" will be set to strName.
Note: default value provided by "?? value"

Simply explained,
let value = x ?? y
Is similar to writing,
let value = x != nil ? x! : y
It checks if x contains nil, if it doesn't then it force unwraps it, otherwise returns y in the above case.

Related

How to remove optional from a string value in swift?

In the code I'm not able to remove optional from the value inside the lbltotalamount.
The value in lblTotalAmount is not removing its optional value from it.
Why? The value in grandtotal gets optional removed but when I assign it to a label it returns an optional value again.
The lblTottalAmount is getting an optional value. I want to remove it.
if success == false {
var grandtotal: Any? = value["total"]
if grandtotal != nil {
print("O!O!O!O/\(grandtotal!)")
grandtotal = String(describing: grandtotal)
self.lblTotalAmount.text = ([grandtotal]) as! String // (here I am not able to remove optional)
}
The problem is in the line
grandtotal = String(describing: grandtotal)
You check for nil but you don't unwrap the value so it's still an optional.
And you are misusing String(describing. Never use it for types which can be converted to String with an init method.
Use always conditional downcast
if success == false {
if let grandtotal = value["total"] as? Double {
self.lblTotalAmount.text = String(grandtotal)
}
}

Check for nil in dictionary

I have a class in my app, where the user inputs values and i set them to an instance of the class, then i upload this data to Database, but i have to convert the class to something the database accepts and i'm converting to dictionary using Mirror Reflection. Some properties in my class can be nil, because by design not all properties are required. But i can't pass nil values to the database.
I have recreated my example is very simplified playground. i didn't set a value for the name property of the class
I tried to check for nil before adding the key, value pair to the dictionary
Below is my code
import UIKit
class Color: NSObject {
var name: String?
var code: Int?
var shade: String?
}
let cl = Color()
cl.code = 3456
cl.shade = "DARK"
var colorDict = [String: Any]()
for x in Mirror(reflecting: cl).children.makeIterator() {
if let val = x.value as Any? {
print(type(of: val))
colorDict[x.label!] = val
}
}
print (colorDict)
the output in console is as below
Optional<String>
Optional<Int>
Optional<String>
["name": nil, "code": Optional(3456), "shade": Optional("DARK")]
how can i check for nil values and skip adding that property to the Dictionary
i have tried to loop through the dictionary after i add all values including nils and check for values too but i get the below warning
Comparing non-optional value of type 'Any' to nil always returns false
declaring the dictionary as below
var colorDict = [String: Any?]()
for x in colorDict {
if x.value == nil {
colorDict.removeValue(forKey: x.key)
}
}
removes the warning but it doesn't remove anything.
I would really appreciate your help.
The way of unwrapping objects of type Any that contain optionals is kind of weird but you can check that the values aren't nil in your mirror like this:
for x in Mirror(reflecting: cl).children {
if case Optional<Any>.some(let val) = x.value {
print(type(of: val))
colorDict[x.label!] = val
}
}
You can do this really easily in a one-liner, using filter:
let dict: [String : Any?] = ["Foo" : 3, "Bar" : nil, "Baz" : "Qux"]
let noNils = dict.filter { $0.value != nil }
print(noNils) // prints ["Foo": Optional(3), "Baz": Optional("Qux")]
As i have suggested, initialise all values.
If you decide not to store the nil values you will end up with children that some of them will have 1, some 2 and some 3 nodes, nothing wrong with that, BUT what happens when you go to read them?
You havent shared any info as to how these values will be used by the app, but assuming you have one function to read the properties/nodes of stored colors, it will go to read all 3 :
ref.child("colors").child("someSpecificColor").observeSingleEvent(of: .value, with: { (snapshot) in
// Get color values
let value = snapshot.value as? NSDictionary
let name = value?["name"] as? String ?? ""
let code = value?["code"] as? String ?? ""
let shade = value?["shade"] as? String ?? ""
// ...
}) { (error) in
print(error.localizedDescription)
}
See the issue?
Here is a simple solution :
var colorDict = [String: Any?]()
for x in Mirror(reflecting: cl).children.makeIterator() {
if let val = x.value, val != nil {
print(type(of: val))
colorDict[x.label!] = val
}
}
Here before to print and add you val, you check if the val is different than nil. As your output suggests in your console log you print :
Optional<String>
Optional<Int>
Optional<String>
val is an optional. So, if it's nil it won't be added. If not, you enter into the if statement and that's it.

Optional value in swift 3 Optimised way

I have create class Testing model which has 4 dataMember it should not be null when accessing (means return default value)
extension Double {
/// Rounds the double to decimal places value
func roundTo(places:Int = 2) -> Double
{
let divisor = pow(10.00, Double(places))
return (self * divisor).rounded() / divisor
}
}
class TestingModel{
var id : String!
var name : String! = "abc" /*It is not working*/
var price : Double! = 0.00
var uniqueId : Int! = 1
/**
* Instantiate the instance using the passed dictionary values to set the properties values
*/
init(dictionary: [String:Any])
{
id = (dictionary["id"] as? String) ?? "" //I dont want to do like this way
name = dictionary["name"] as? String
price = (dictionary["price"] as? Double)?.roundTo() ?? 0.00
uniqueId = dictionary["unique_id"] as? Int
}
}
let t:TestingModel = TestingModel.init(dictionary: ["x id" : "x012y12345z45","x name":"test1","x price":100.0,"uniqueId":1236.0])
let testString = "Jd " + t.id
print(testString) //Perfect
print(t.name)
print(t.price) /* Only one decemal point is printed */
Getting Output
Jd
nil
0.0
Expected output
Jd
abc /Should return abc instead of nil/
0.00 /Two decimal point complulsury/
What i actually mean in
if i assign nil value to variable then it should remain with its default value without writing this Optional chaining ?? "abc" in constructor
price is a Double type and what you are asking to do is to print that double value to 2 decimal places. Then you should make use of the following.
let a = 0.0
print(String(format: "%.2f", a))
this prints:
0.00
If you are planning to round it to decimal places, then also the above code will return that. But if you need it to round and return a double type then you can check this answer
Based on your updated question, I suggest to use the model as follows:
class TestingModel{
var id : String = ""
var name : String = "abc"
var price : Double = 0.0
var uniqueId : Int = 1
/**
* Instantiate the instance using the passed dictionary values to set the properties values
*/
init(dictionary: [String:Any])
{
id = (dictionary["id"] as? String) ?? ""
name = dictionary["name"] as? String ?? "abc"
price = (dictionary["price"] as? Double) ?? 0.0
uniqueId = dictionary["unique_id"] as? Int ?? 1
}
}
You seem to have asked two different questions here. The first one regarding doubles have already been answered by adev. I will answer the second one, which is:
if i assign nil value to variable then it should remain with its default value without writing this Optional chaining ?? "abc" in constructor
If you want to do this then it means that the variable shouldn't be optional at all, as nil isn't one of its valid values. Make the variable a non-optional type and give it a default value.
class TestingModel{
var id : String = ""
var name : String = "abc"
var price : Double = 0.00
var uniqueId : Int = 1
}
You can't really avoid using ?? in the constructor because of the nature of dictionaries. They will always return a nil value if the key does not exist. You have to check it. It does not make sense even if this is possible anyway. Imagine something like this:
someVariable = nil // someVariable is now 0
This is extremely confusing. someVariable is 0, even though it appears that nil is assigned to it.
A workaround will be to add a dictionary extension. Something like this:
extension Dictionary {
func value(forKey key: Key, defaultValue: Value) -> Value {
return self[key] ?? defaultValue
}
}
But I still recommend that you use ?? instead.

How to test if a key doesn't exist in a Dictionary in Swift?

I tried that but event if the value is nil, it doesn't act correctly (the key "CODE_REQUEST1 doesn't exist).
form.souhait1_1 = contactDetails["CODE_REQUEST1"] != nil ? String(formatIntToString(contactDetails["CODE_REQUEST1"])) : ""
You can use the map(_:) method of Optional to conditionally use the (possibly existing key:s) value for a given key in your dictionary:
form.souhait1_1 = contactDetails["CODE_REQUEST1"]
.map { String(formatIntToString($0)) } ?? "N/A"
If the key doesn't exist, the call to contactDetails["CODE_REQUEST1"] will be nil, and the .map call will return nil, in which case the nil coalescing operator ?? will be used to supply a default value ("N/A" above).
You can use ?? for swift3
var dic = ["CODE_REQUEST1":"success"]
var value = dic["CODE_REQUEST1"] ?? "" // value = success
dic = ["qq":"fail"]
value = dic["CODE_REQUEST1"] ?? "" // value = ""

Trouble converting a string to an Int

The following works in Playground:
func stringToInt(numberStr: String!) -> Int {
print(numberStr)
return Int(numberStr)!
}
let strNum1: String?
strNum1 = "1"
let result = stringToInt(numberStr: strNum1)
It returns 1 as expected.
In Xcode, a similar approach fails:
func stringToInt(numberStr: String!) -> Int {
print("\(numberStr!)")
let str = "\(numberStr!)"
print(Int(str))
return Int(str)!
}
The first print produces: Optional(1)
The second print produces: nil
The return statement fails because it is attempting to create an Int from a nil.
It must be something simple but I haven't been able to determine why it's not working. This is in Swift 3 and Xcode 8 BTW.
#Hamish:
In Xcode, I have a string with a numeric value. This:
print("number: (selectedAlertNumber) - unit: (selectedAlertUnit)")
...produces this:
number: Optional(1) - unit: Day
Then, I'm checking to see if either selectedAlertNumber of selecterAlertUnit != "-"
if selectedAlertNumber != "-" && selectedAlertUnit != "-" {
// set alert text
var unitStr = selectedAlertUnit
let alertNumber = stringToInt(numberStr: selectedAlertNumber)
if alertNumber > 1 {
unitStr.append("s")
}
let alertText = "...\(selectedAlertNumber) \(unitStr) before event."
alertTimeCell.setAlertText(alertText: alertText)
// set alert date/time
}
The let alertNumber = stringToInt... line is how I'm calling the function. I could just attempt the conversion there but I wanted to isolate the problem by wrapping the conversion in it's own function.
Using string interpolation to convert values to a String is usually not advised since the output may differ depending on optional status of the value. For example, consider these two functions:
func stringToInt(numberStr: String!) -> Int
{
print("\(numberStr!)")
let str = "\(numberStr!)"
return Int(str)!
}
func otherStringToInt(numberStr: String!) -> Int
{
print(numberStr)
let str = "\(numberStr)"
return Int(str)!
}
The only difference between these two is the ! in the second function when using string interpolation to get a String type value from numberStr. To be more specific, at the same line in function 1 compared to function 2, the string values are very different depending on whether or not the interpolated value is optional:
let str1: String = "1"
let str2: String! = "1"
let str3: String? = "1"
let otherStr1 = "\(str1)" // value: "1"
let otherStr2 = "\(str2)" // value: "Optional(1)"
let otherStr3 = "\(str2!)" // value: "1"
let otherStr4 = "\(str3)" // value: "Optional(1)"
let otherStr5 = "\(str3!)" // value: "1"
Passing otherStr2 or otherStr4 into the Int initializer will produce nil, since the string "Optional(1)" is not convertible to Int. Additionally, this will cause an error during the force unwrap. Instead of using string interpolation in your function, it would be better to just use the value directly since it's already a String.
func stringToInt(numberStr: String!) -> Int
{
return Int(numberStr)!
}
Let me know if this makes sense.
Also, my own personal feedback: watch out force unwrapping so frequently. In many cases, you're running the risk of getting an error while unwrapping a nil optional.