Use of an optional value in Swift - ios

While reading the The Swift Programming Language, I came across this snippet:
You can use if and let together to work with values that might be
missing. These values are represented as optionals. An optional value
either contains a value or contains nil to indicate that the value is
missing. Write a question mark (?) after the type of a value to mark
the value as optional.
// Snippet #1
var optionalString: String? = "Hello"
optionalString == nil
// Snippet #2
var optionalName: String? = "John Appleseed"
var greeting = "Hello!"
if let name = optionalName {
greeting = "Hello, \(name)"
}
Snippet #1 is clear enough, but what is happening in the Snippet #2? Can someone break it down and explain? Is it just an alternative to using an if - else block? what is the exact role of let in this case?
I did read this page, but still a little confused.

if let name = optionalName {
greeting = "Hello, \(name)"
}
This does two things:
it checks if optionalName has a value
if it does, it "unwraps" that value and assigns it to the String called name (which is only available inside of the conditional block).
Note that the type of name is String (not String?).
Without the let (i.e. with just if optionalName), it would still enter the block only if there is a value, but you'd have to manually/explicitly access the String as optionalName!.

// this line declares the variable optionalName which as a String optional which can contain either nil or a string.
//We have it store a string here
var optionalName: String? = "John Appleseed"
//this sets a variable greeting to hello. It is implicity a String. It is not an optional so it can never be nil
var greeting = "Hello!"
//now lets split this into two lines to make it easier. the first just copies optionalName into name. name is now a String optional as well.
let name = optionalName
//now this line checks if name is nil or has a value. if it has a value it executes the if block.
//You can only do this check on optionals. If you try using greeting in an if condition you will get an error
if name{
greeting = "Hello, \(name)"
}

String? is a boxed-type, variable optionalName either contains a String value or nothing(that is nil).
if let name = optionalName is an idiom, it unboxes the value out of optionalName and assign it to name. In the meanwhile, if the name is non-nil, the if branch is executed, otherwise the else branch is executed.

Related

strange optional behaviour in Swift

I have created my own class in Swift as below.
class Product: NSObject {
var product_id:Int?
var product_number:String?
var product_price:Float?
var product_descrption:String?
}
Now i am setting value in each property like this
let p=Product()
p.product_id=1
p.product_price=220.22
p.productdescrption="Some description"
p.product_number="W2_23_233"
But when i get the value from price then for price i get value like "Optional 220.22" But i don't get appended word "Optional" in description".So to resolve this i added "!" for unwrapping the value of float but i did not have to do this for String please tell why this is happening?
If you are printing any of these values should say Optional(...). If you are assigning the values to a label, that will not include the Optional(...), The reason that it shows Optional(...) when you print the value using print(), is just to show you its an optional. For safety, instead of using the !, try using if lets.
An example with your code,
if let id = p.product_id {
print(id) //Does not contain Optional()
}
You can also combine them, to do them all at one time. (Only do this if you don't want to print unless all values are non-nil)
if let id = p.product_id,
let price = p.product_price,
let description = p.productdescrption,
let productNumber = p.product_number {
//Enter code here that does something with these values
}
Note, if you aren't on swift 3, I believe you only have to write let on the first condition.
If you print any optional variable without unwrapping no matter what type it is, Optional will be appended to the variable's value.
print(p.product_price) will print Optional(220.220001)
print(p.product_descrption) will print Optional("Some description")
To print only value you need to unwrap the optional variables.
print(p.product_price!) will print 220.22
print(p.product_descrption!) will print Some description
This forced unwrapping will only work if the optionals does not contain nil. Otherwise it will give you a runtime error.
So to check for nil you can use if let statement.
No matter what type of variable. If you assign a value to an optional variable, It always enclosed with Optional(...)
Optional without forced unwrapping:
print("product_price = \(p.product_price) \n product_descrption = \(p.product_descrption)")
Output:
product_price = Optional(220.22)
product_descrption = Optional(Some description)
Optional with forced unwrapping:
print("product_price = \(p.product_price!) \n product_descrption = \(p.product_descrption!)")
Output:
product_price = 220.22
product_descrption = Some description

How to take out parts of a string in swift

I want to take out a part of my String and afterwords I want to save it in a new String.
var key = "Optional("rFchd9DqwE")"
So my String equals now to Optional("rFchd9DqwE"). Now I want to take out the part rFchd9DqwE from this String and save it back in the variable "key". Is there a simple way to do this?
If you have code like this:
var aString: String? = "rFchd9DqwE"
let b = "\(aString)"
Then the problem is that you are doing it wrong. The "Optional()" bit in your output is telling you that you passed in an optional type instead of a string type.
What you should do is something like this instead:
var aString: String? = "rFchd9DqwE"
if let requiredString = aString
{
let b = "\(requiredString)"
}
the "if let" part is called "optional binding". It converts aString from an optional to a regular String object and saves the result in the new constant requiredString. If aString contains a nil, the code inside the braces is skipped.
Another way to do this would be to use the nil coalescing operator:
var aString: String? = "rFchd9DqwE"
let b = "\(aString ?? "")"
The construct aString ?? "" returns the string value of aString if it's not nil, or replaces it with the value after the ?? if it is nil. In this case it replaces nil with a blank.

What is going on in Apple's first example of an optional value in the Swift tour?

In Apple's "Swift Tour" introduction to swift, they give the following example of optionals:
var optionalString: String? = "Hello"
optionalString == nil
var optionalName: String? = "John Appleseed"
var greeting = "Hello!"
if let name = optionalName {
greeting = "Hello, \(name)"
}
Does the second line serve any purpose?
optionalString == nil
is an expression, and in a compiled program the expressions's value (in this case: false)
is ignored.
However, the Swift Guided Tour is downloadable as a Playground file, and in a Playground
any expression value is displayed in the right column.
You are encouraged to edit the code listings and see the the results immediately.
The first bit of code
var optionalString: String? = "Hello"
optionalString == nil
Is just comparing the optional to nil, when this code is run in a playground false will show up on the right hand side.
The second chunk.
var optionalName: String? = "John Appleseed"
var greeting = "Hello!"
if let name = optionalName {
greeting = "Hello, \(name)"
}
Is using what is called optional binding. The author could just as easily have written
var optionalName: String? = "John Appleseed"
var greeting = "Hello, \(optionalName!)"
Note the exclamation mark at the end of optionalName! this is whats known as Forced Unwrapping, In this particular case it would have been fine since we set the value of the variable immediately before hand. In general though Forced Unwrapping is considered to be unsafe since you're basically saying that you know better than the compiler. Forced Unwrapping is sometimes affectionally referred to as the crash operator ;)
You can find the section on optionals here
var optionalString: String? = nil
optionalString == nil
change your code as above,and you can see the use of second line.
It tell you the optionalValue can be set to nil,and compare to nil.Most of the classes can't do these.

How to use (?) and (!) in Swift

I'm new to swift and I'm having some difficulties understanding on how to use (!) and (?)
As far as I know, we can use (?) when there are instances that a variable can be nil.
And use (!) when you are 100% sure that item is not nil.
1. Working Fine - Optionals
var john:String?
john = "Is my name"
println(john!)
2. Crashes on Runtime - ! must not be nil - means this is correct
var john:String?
println(john!)
3. Works Fine
var dict: [String:AnyObject] = Dictionary()
dict["name"] = "John"
var str: String = dict["name"]! as String <--- Taking away the (!) says it's not convertible to String
4. Cannot Run/Build - for me it's similar to 1.
var dict: [String:AnyObject]? = Dictionary() ---error says does not have a member named 'subscript'
dict["name"] = "John"
var str: String = dict["name"]! as String
5. Unexpectedly found nil while unwrapping an optional value
var dict: [String:AnyObject] = Dictionary()
dict["name"]? = "John"
var str: String = dict["name"]! as String
Would be great if someone can help me understand these things. Thanks!
it is a bit misleading interpretation believing when an ! 'marks' an ivar then that 100% cannot be nil. it can be. you can say only, you got the value as already unwrapped, so you don't need to force unwrapping it again – but it can be nil.
try this example for instance:
var text: String! = "hello"
text = nil;
println(text)
it prints a nil for you.
the reason why your app can crash is you force unwrapping an optional which is nil, that is invalid operand.
#4
line-by-line:
var dict: [String:AnyObject]? = Dictionary() // from OP
your dict is an optional, let us see what you are doing here:
dict["name"] = "John" // from OP
var str: String = dict["name"]! as String // from OP
you have an optional dict and you'd like to use it somehow, you have two possible ways to do it:
(A) via optional chaining;
(B) via forced unwrapping;
(A)
dict?["name"] = "John" // optional chaining
it is quite straightforward, it assigns the new value for the key name if the dictionary is not nil, otherwise the chain generously falls and nothing happens in runtime.
in perspective of this line:
var str: String = dict!["name"]! as String // forcibly unwrapped
it crashes in runtime if either the dictionary or the value for the key was nil (as per the first paragraph says: invalid operand to force unwrapping a nil), but the str would be John if the dictionary and the key both do valid objects.
(B)
dict!["name"] = "John" // forcibly unwrapped
it works like a charm and assigns the new value for the key name if the dict exists; but if the dict was nil, that is a termination point in runtime (aka crash), because nil cannot be unwrapped forcibly (see above).
#5
line-by-line:
var dict: [String:AnyObject] = Dictionary() // from OP
your dict is not optional and not even nil, but the dictionary is literally empty, so no key does exist in it, including the name.
dict["name"]? = "John" // from OP
var str: String = dict["name"]! as String // from OP
the optional chaining always falls when any of the element of the chain falls – therefore no new value will be assigned in your code, but the falling happens gracefully, so you bypass the first line about assigning the new value, but the app crashes in the second line because the value does not exists and you try to force unwrapping it (see above about invalid operand).
so, you need to drop the optional chaining from the first line, if you want to assign a new value for a non-existing key:
dict["name"] = "John"
the optional chaining is useful if you would not like to change the original dictionary with adding a new key/value, but you would like to override an existing one only:
dict["name"] = "John"
dict["name"]? = "Jack"
in that case the new value will be Jack, because the optional chaining won't fall as the key name is already existing with a different value, so it can be and will be overridden; but:
dict["name"] = nil
dict["name"]? = "Jack"
the optional chaining will falls and no new value is assigned here for the key.
NOTE: there would be many other things and ideas which can be told about the concept. the original documentation is available on Apple site under section Swift Resources.

Difference between optional and normal String

Difference between String?,String! and String
I am using this code :
var myString:String? = nil
var myString1:String = ""
var myString2:String! = nil
println(myString2)
Here it's giving nil in myString2 instead of run-time error.
There are better explanations out there, but the simple version is:
String is a String. It holds a String value.
String? is an Optional String. It can hold a String value or nil. You can unwrap the optional and immediately try to access the String value by using ! like this:
var str: String? = nil
str!.length
That code will result in a runtime error because str is nil. A safer way to get the value of the optional is to use an if let:
var str: String? = nil
if let myStr = str{
myStr.length
}
That has the same functionality as the above code, but won't crash on nil values.
String! is an implicitly unwrapped optional. It works that same way as a regular optional but it is assumed to be non-nil, so when you call it it tries to access the value like it was a regular optional with ! after.
The reason println works on all these types is because it can take Strings, String Optionals, and nil values and handles them all.
var str: String? = "hello world"
println(str)
println(str!)
println(nil)
All should work.
#connor's answer is correct.
var Amy:Wife? // Amy is your girlfriend, she may or may not be your wife.
var Amy:Wife // Amy is your wife.
var Amy:Wife! // Amy will be your wife, no matter you like it or not.

Resources