I read that the ternary operator ??
unwraps an optional if it is not nil, but if I do:
var type: String?
type = "milk"
let certainType = type ?? "melon"
then certainType will still be a String?
, and if I do
println("it's a \(certainType)")
it will print:
it's a Optional("milk")
Thoughts?
Update:
Sorry for the confusion - I meant var type: String?
I get that it should print "it's a milk", but what I saw in console is a "it's a Optional("milk")" - anyone else experienced the same issue? Could that be caused by string interpolation?
Asked by @Antonio, here is more context and real code and logging snapshot - type is from Note, which is a NSManagedObject class used to deal with xcdatamodel
class Note: NSManagedObject {
@NSManaged var type: String?
}
And I have the type set to 'todo' at some point, then have the following code to print them out:
println("type class:\(_stdlib_getDemangledTypeName(note.type))")
let type1 = note.type ?? "note"
println("type1:\(type1)")
let type2: String = note.type ?? "note"
println("type2:\(type2)")
And the output:
type class:Swift.Optional
type1:Optional("todo")
type2:todo
As you can see, if I don't explicitly mark the type1's type as String, it will print undesired result Optional("todo") - I use the type in a string interpolation to construct a path so it matters
Swift's nil coalescing operator helps you solve this problem by either unwrapping an optional if it has a value, or providing a default if the optional is empty. Because name is an optional string, we need to unwrap it safely to ensure it has a meaningful value.
The nil-coalescing and ternary conditional operators are what's known as syntatic sugar. They sugar-coat verbose code in more concise code, and make your Swift code more readable.
Optional chaining is a process for querying and calling properties, methods, and subscripts on an optional that might currently be nil . If the optional contains a value, the property, method, or subscript call succeeds; if the optional is nil , the property, method, or subscript call returns nil .
Like C, Swift provides compound assignment operators that combine assignment ( = ) with another operation. One example is the addition assignment operator ( += ): var a = 1. a += 2. // a is now equal to 3.
I can't prove what I am going to say, so any feedback is very well appreciated.
The OP asserts that code similar to this:
var type: String? = "milk"
let certainType = type ?? "melon"
println("it's a \(certainType)")
prints an unexpected string:
"it's a Optional("milk")"
whereas it should be:
"it's a milk"
It turns out that happens when the variable is actually a property with the @NSManaged
attribute.
I suspect that there is a bug in type inference. The OP states that:
let certainType = type ?? "melon"
prints the wrong result, whereas:
let certainType: String = type ?? "melon"
prints the correct one.
So for some reason, without explicitly indicating the variable type, the nil coalescing operator is returning an optional.
If I change the type of the type
variable to either AnyObject
or AnyObject?
, it actually prints the unexpected result:
var type: AnyObject = "milk"
let certainType = type ?? "melon"
println("it's a \(certainType)")
"it's a Optional(milk)"
My guess is: because the @NSManaged
attribute is used, the property is inferred with the wrong type (AnyObject?
) when used, unless the correct type is explicitly indicated.
As to why that happens, no idea (besides thinking it's a bug)
Feel free to up or down vote this answer, and most important don't consider it as a solution - I would appreciate feedback though, because I'm curios to know what's going on and whether it's really a bug or not.
This is (I think) by design. If you do this:
let type = "mil"
println("it's a \(type)")
If I am not mistaken, this will print it's a "milk"
which is far more useful when debugging than it's a String
. Note that \(...)
is similar to %@
in Objective-C: classes can override their string representation.
Also, as Antonio already pointed out:
let
).let type: String? = "milk"
because it clearly is never going to be nil).I read that the ternary operator ?? unwraps an optional if it is not nil, but...
It does unwrap it.
let type: String? type = "milk" let certainType = type ?? "melon"
Your code won't compile, but if you change the let to var, it will work and certainType will be of type String.
var type: String?
type = "milk"
let certainType = type ?? "melon"
println("it's a \(certainType)") // prints "it's a milk"
This is known as the Nil Coalescing Operator.
If you have an Optional a
, the result of a ?? b
will be a!
if a
is not nil
and b
if it is. It's shorthand for this expression:
a != nil ? a! : b
So, you are using the operator correctly, but you must initialize a constant on the same line that you declare it:
let type: String? = "milk"
However, I'm assuming that you don't want that to be a constant in this context (since you're checking what it is), so if you use var
to declare it, your existing code should work.
I was having a similar problem.
I have a dictionary of type [String : AnyObject]
, but the entry I was after turns out to be a String
value. Fearing it may not always be present. I was retrieving it with the following code:
let message = dictionary["message"] ?? "No message"
However, when the entry is present, that would still give the string:
Optional(the actual message)
i.e., the variable message
is of type String?
.
I changed it to:
let message = (dictionary["message"] as? String) ?? "No message"
...and now the retrieved value is an unwrapped, non-optional string.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With