strange optional behaviour in Swift - ios

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

Related

How can save the text of the label as an integer variable in swift?

I need to save the changing text of my label as a variable, but if write the following code:
var warn = Int(self.dyn.text)
It says:
Value of optional type 'String?' must be unwrapped to a value of type 'String'
Coalesce using '??' to provide a default when the optional value contains 'nil'
Force-unwrap using '!' to abort execution if the optional value contains 'nil'
What code should I use?
var warn = Int(self.dyn.text ?? "") ?? 0
You have to provide a default value, just in case it's not possible to make the cast to Int. You also have to make sure the text value is not nil.
Take a look at optional chaining and optional binding
Another approach is:
if let dynText = self.dyn.text {
if let warn = Int(dynText) {
// warn is an available int variable here.
}
}
2 way of doiing that
1: let warn = Int(self.dyn.text ?? "") ?? 0
2: let warn = Int(self.dyn.text!)!
Good. Because String can be "123" or "Hello,world" so it can be numeric or String text
When you use this
Int(String) the initializer might fail, it returns an optional Int, rather than an Int
Example
let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)
// convertedNumber is inferred to be of type "Int?", or "optional Int"
So you have to Unwrap it
Like that
// unwrap text if TextField is not `!` and then unwrap text when you convert to Int
if let dynText = self.dyn.text , let warn = Int(dynText) {
print(warn)
}

Cannot assign value of type 'String?' to type 'Int'

I am getting the error message Cannot assign value of type 'String?' to type 'Int'
I have browsed through other questions like this but it still shows the error.
if sunscreenName.text != nil && reapplyTime.text != nil {
sunscreen = sunscreenName.text!
reApplyTime = reapplyTime.text
//Some sort of message such as Progress hud
}
Thanks in advance!
I got your problem, actually what happens here Swift is is type safe langauge
So what you are doing is is to store a String value in Int which will not happen automatically you need to convert it to Int
like this
Int(sunscreenName.text)
But there is a catch there not all string are convertible to Int type, fo e.g.
let name = "roshan"
if you try to convert it to Int it will give you a nil
let a = Int(name)
So its better you do a optional Binding here provided by Swift
if let sunValue = Int(sunscreenName.text),let reApplyValue = Int(reapplyTime.text) {
sunscreen = sunValue
reApplyTime = reApplyValue
}
I recommend reading through The Swift Programming Language to get a better understanding of Swift and its fundamental concepts, since this question is fairly basic.
You make several mistakes:
if sunscreenName.text != nil && reapplyTime.text != nil {
This is wrong. In Swift, if you plan to use the value later, you should use if let rather than comparing to nil. Comparing to nil leaves the values optional, but if let unwraps them. So, do this instead:
if let sunscreenText = sunscreenName.text, let reapplyText = reapplyTime.text {
Now you have the sunscreenText and reapplyText variables, which are typed String, not String? (i.e. they are not optional).
Now, there's these two lines.
sunscreen = sunscreenName.text!
reApplyTime = reapplyTime.text
You don't say which one is giving the error, but the issue is the same in either case. First, use our unwrapped sunscreenText and reapplyText variables instead of sunscreenName.text! and reapplyTime.text. Next, if one of these is meant to be an Int instead of a String, cast it. Swift is not like JavaScript, in that it won't automatically convert values from one type to another, so if something is a string and we need an integer, we have to convert it ourselves.
(assuming reapplyTime was the line that was giving the error:)
if let reapplyInt = Int(reapplyText) {
reapplyTime = reapplyInt
}
The reason we have to unwrap is because Int(String) can return nil if the string is something that can't be converted to an integer. Alternately, we could just provide a default value:
reapplyTime = Int(reapplyText) ?? 0 // sets to 0 if it can't parse the string as an integer

Optional Type 'String??' Not Unwrapped

"Value of optional type 'String??' not unwrapped; did you mean to use '!' or '?'?" -
I got this weird compiler error today, which was entirely confusing due to the two question marks after String.
I have a dictionary s, of type [String : String?], and a function which accepts all arguments as String?s. Specifically (from 5813's method of copying user-selected information into a dictionary), I have an elaborated version of the following:
func combine(firstname: String?, lastname: String?) {...}
var text = combine(s["kABPersonFirstNameProperty"], lastname: s["kABPersonLastNameProperty"])
I'm getting the error on the second line, and I'm wondering why it's so. If the values in s are of type String?, shouldn't that be accepted by combine(), since it's arguments are supposed to be of the same type? Why, then, would I get this error and how can I fix it?
Dictionary<T1, T2>[key] returns T2?. This is to return nil in case key doesn't exist.
So if T2 is String?, s[key] returns String??
You cannot pass String?? as String?
You can call like this to unwrap and prepare for non-existing key as well
var text = combine(s["kABPersonFirstNameProperty"] ?? nil, lastname: s["kABPersonLastNameProperty"] ?? nil)
By the way, code below will not set value to nil but remove the entire entry from the dictionary
s[key] = nil
If you want the value to be nil instead of removing entry, you will have to do this
s[key] = nil as String?
It is once optional because your dictionary value is optional. And it is optional again, because dictionary[key] returns optional. So you need to unwrap it twice.
Try this in a playground to understand the problem (and see possible solution):
let oos: String?? = "Hello"
print(oos)
if let os = oos { // Make String?
print(os)
if let s = os { // Make ordinary String
print(s)
}
}
Prints:
Optional(Optional("Hello"))
Optional("Hello")
Hello
But you could use other ways than if let to unwrap, too. For example:
print(oos! ?? "n/a")
Will force unwrap it once and then print either the inner String or n/a in case of nil...
Maybe the easiest solution would be to make the dictionary hold String instead of String?. Then you don't have the unwrapping problems which are described in other solutions.
Or do you really have to store String? types?
Do you want to differentiate between 'key exists but holds nil' and 'key does not exist'?

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.

Use of an optional value in Swift

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.

Resources