"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'?
Related
I am declaring a array:
var array:[String] = []
assigning values:
array.append(uniqueId as String)
and then pass it to a function:
static var levels_array : [String] = []
class func setLevelsArray(arr:[String])
{
GamePrefrences.levels_array = arr
println(GamePrefrences.levels_array)
}
My problem is that when i print the array i get:
[Optional(88868658), Optional(112051080), Optional(95274974)]
Why is that optional? i only want Strings in the array so why is "optional" added?
I come from Java can someone explain why optional is created and how to remove it
Very likely the actual strings are "Optional(88868658)", etc. You have probably created this strings from an optional accidentally. In the above code, you definitely have an array of strings, not an array of optionals, and when you print an array of strings, they don't include the double-quotes (which can be confusing).
It may is happening here:
array.append(uniqueId as String)
What type is uniqueId? as is not used to convert types. It's just used to cast them if possible (though this should really fail if that's the case; so I'm suspecting you're doing a string interpolation somewhere).
The Strings themselves are not optional -- the uniqueId is. Basically, optionals mean that a stored value may or may not exist.
I'd recommend using an if let statement to unwrap your optional uniqueID before converting it into a String so the program in fact knows that the optional exists before storing it as a String, ex:
if let uniqueId = uniqueId {
array.append(uniqueId as String)
}
This question already has answers here:
Downcasting in Swift with as and as?
(3 answers)
Closed 8 years ago.
Is there a difference between as? String vs. as String? in Swift? If so, what's the difference and when should I use one vs. another?
There's a subtle but important difference:
variable as? String: variable can be any type, such as an array, an integer, etc. Cast to string if it's a string, set to nil otherwise.
variable as String?: variable is a String?, but stored in an opaque type, such as AnyObject?, or it's a non optional string. If it's something different, a runtime exception is generated.
Some examples:
var x: AnyObject? = "Test"
x as String? // OK, the result is an optional string
x as? String // OK, evaluates to an optional string
"string" as String? // OK, evaluates to optional string
x as? Int // OK, evaluates to nil, it's not an Int
x as Int? // Runtime exception, it's not castable to optional Int
So:
as? Type means: cast to this type, if possible, otherwise evaluate to nil
as Type? means: cast to an optional Type, because I know it's an optional Type. I understand that if it's not that, a runtime exception is generated
However, the real difference is between as? and as: the former is an attempt to cast, the latter is a forced cast, resulting in runtime error if not possible.
Update Dec 14, 2015 Since Swift 1.2, there are 3 variations of the as operator:
as? is an attempt to cast, evaluating to nil if cast fails
as! is a forced cast, resulting to an runtime exception if cast fails (this is what as previously did)
as is now a special type of cast to be used when casting to equivalent types, usually bridged types, such as Swift's String and NSString.
From The Swift Programming Language book,
as is a type cast operator which we use to downcast to the subclass and as? is used for an optional form, when we are not sure if the downcast will succeed. Consider the following example
for item in library {
if let movie = item as? Movie {
println("Movie: '(movie.name)', dir. (movie.director)")
} else if let song = item as? Song {
println("Song: '(song.name)', by (song.artist)")
}
}
The example starts by trying to downcast the current item as a Movie. Because item is a MediaItem instance, it’s possible that it might be a Movie; equally, it’s also possible that it might be a Song, or even just a base MediaItem.
String? An optional value either contains a value or contains nil to indicate that the value is missing.
From this,
as? String means when you don't know what you're downcasting, you are assuming that as a String, but it might me Integer or Float or Array or Dictionary
as String? means it's an Optional Value, it may either contain a String or Nil value.
Yes there is a difference.
In the first case, you are doing an optional cast to the type String. This will return a value if the object you are attempting to cast is indeed a String or nil if it is not.
In the second case, you are doing a forced cast to the type String?. If the value you are casting is not a string, it will crash your program.
YES, there is diffrence.
variable as String? downcast to optional String.If variable is not String? it will cause run-time exception.
while variable as? String will return nil if your variable is not String type or return downcast variable to String. This is conditional downcasting, if you not sure about down-casting you need to use this .
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 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.
I think I'm confused about the concept of 'unwrapping' an optional value, and any guidance would be much appreciated!
According to the Swift Programming Language Guide, an optional requires unwrapping before it can be used, unless it is implicitly unwrapped using an !.
From: Apple Inc. “The Swift Programming Language.” iBooks. https://itun.es/gb/jEUH0.l:
let possibleString: String? = "An optional string."
println(possibleString!) // requires an exclamation mark to access its value
whereas
let assumedString: String! = "An implicitly unwrapped optional string."
println(assumedString) // no exclamation mark is needed to access its value
However, in Xcode, I can do the following and still print the value of an optional string:
let teamname: String? = "Liverpool!"
println("Come on \(teamname)")
What am I missing?
First of all, an optional requires unwrapping only if you are using it somewhere that does not allow an optional. (e.g. assigning a String? to a String)
var optionalString: String? = "Some string"
var absolutelyAString: String = "Another string"
// absolutelyAString = optionalString // Error. Needs to unwrap
absolutelyAString = optionalString! // OK.
"Unwrap" basically means to "guarantee that the value is not nil". Just think of it as casting to a non-nullable type.
Second, println() accepts optionals just fine but because it checks your variable for you. If the value of your variable is nil, it prints "nil"; otherwise it prints the string implemented by the Streamable, Printable, or DebugPrintable protocols.
An optional requires unwrapping, but you don't always have to do it yourself.
var teamname: String? = "Liverpool"
println("Come on \(teamname)")
In this case, the string interpolation will check the optional then unwrap it and add its value to the String. If the Optional is nil, it will add "nil" to the String instead.
In order to access the properties and methods of String you will have to explicitly unwrap it. If you don't know if it is nil or not, you should check with an if statement.
if teamname != nil {
var newName = teamname!.capitalizedString
}