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.
Related
"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'?
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.
UILabel has a text property, which is an optional String, but it seems to be behaving like an implicitly unwrapped optional. Why can't I assign it another optional String? Thanks.
#IBOutlet weak var tweetContent: UILabel!
...
var unopt: String = "foo"
var opt: String? = "bar"
var opt2: String?
opt2 = opt //Works fine
cell.tweetContent.text? = unopt //Works fine
cell.tweetContent.text? = opt //Compile error: Value of optional type 'String?' not unwrapped
You don't need to unwrap text.
Leaving text as an String? (aka Optional<String>)
cell.tweetContent.text = unopt // Works: String implicitly wraps to Optional<String>
cell.tweetContent.text = opt // Works: Optional<String>
Where as unwrapping text turns String? into a String.
cell.tweetContent.text? = unopt // Works: String
cell.tweetContent.text? = opt // Fails: Optional<String> cannot become String
UPDATE
Maybe there needs to be a bit more of an explanation here. text? is worse than I originally thought and should not be used.
Think of text = value and text? = value as function setText.
text = has the signature func setText(value: String?). Remember, String? is Optional<String>. In addition, it's always called no matter the current value of text.
text? = has the signature func setText(value: String). Here is the catch, it's only called when text has a value.
cell.tweetContent.text = nil
cell.tweetContent.text? = "This value is not set"
assert(cell.tweetContent == nil)
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.
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.