Since beta 8.3, zillions warnings "String interpolation produces a debug description for an optional value; did you mean to make this explicit?" appeared in my code.
For example, the warning popped in the following situation up, where options could lead to nil:
let msg = "*** Error \(options["taskDescription"]): cannot load \(sUrl) \(error)"
As previously designed, it was ok for me (and the compiler) the optionals to be interpolated as 'nil'. But compiler changed its mind.
What the compiler suggests is to add a String constructor with description as follows:
let msg = "*** Error \(String(describing: options["taskDescription"])): cannot load \(sUrl) \(error)"
Obviously, the results is explicit but also very very cumbersome in my opinion. Is there a better option? Do I have to fix all those warning or better wait for the next beta?
https://i.stack.imgur.com/llT41.png
Swift 3
broke my own log
and I made a mistake by simply using print
instead. Should always create your own wrapper otherwise you'll be screwed by this sort of "new feature".
This is a change that was made in this pull request due to the fact that interpolating Optional(...)
into the resultant string is often undesirable, and can be especially surprising in cases with implicitly unwrapped optionals. You can see the full discussion of this change on the mailing list here.
As mentioned in the pull request discussion (although unfortunately not by Xcode) – one slightly nicer way to silence the warning than the use of String(describing:)
is to add a cast to the optional type of whatever you're interpolating, so for example:
var i: Int? = 5
var d: Double? = nil
print("description of i: \(i as Int?)") // description of i: Optional(5)
print("description of d: \(d as Double?)") // description of d: nil
Which can also be generalised to as Optional
:
print("description of i: \(i as Optional)") // description of i: Optional(5)
print("description of d: \(d as Optional)") // description of d: nil
In Swift 5, with the new string interpolation system introduced by SE-0228, another option is to add a custom appendInterpolation
overload for DefaultStringInterpolation
:
extension DefaultStringInterpolation {
mutating func appendInterpolation<T>(optional: T?) {
appendInterpolation(String(describing: optional))
}
}
var i: Int? = 5
var d: Double? = nil
print("description of i: \(optional: i)") // description of i: Optional(5)
print("description of d: \(optional: d)") // description of d: nil
And, if desired, you could even remove the argument label to disable the warning entirely within a module (or within a particular file if you mark it as fileprivate
):
extension DefaultStringInterpolation {
mutating func appendInterpolation<T>(_ optional: T?) {
appendInterpolation(String(describing: optional))
}
}
var i: Int? = 5
var d: Double? = nil
print("description of i: \(i)") // description of i: Optional(5)
print("description of d: \(d)") // description of d: nil
Though personally I would prefer to keep the argument label.
Two easier ways of dealing with this issue.
Option 1:
The first would be by "force-unwrapping" the value you would like to return using a bang (!)
var someValue: Int? = 5
print(someValue!)
Output:
5
Option 2:
The other way, which could be the better way - is to "safely-unwrap" the value you want returned.
var someValue: Int? = 5
if let newValue = someValue {
print(newValue)
}
Output:
5
Would recommend to go with option 2.
Tip: Avoid force unwrapping (!) where possible as we are not sure if we will always have the value to be unwrapped.
seems using String(describing:optional) is simplest.
default value ?? makes no sense for non-Strings e.g Int. If Int is nil then you want the log to show 'nil' not default to another Int e.g. 0.
Some playground code to test:
var optionalString : String? = nil
var optionalInt : Int? = nil
var description_ = ""
description_ = description_ + "optionalString: \(String(describing: optionalString))\r"
description_ = description_ + " optionalInt: \(String(describing: optionalInt))\r"
print(description_)
Output
optionalString: nil
optionalInt: nil
After updating to Xcode 8.3 and getting a lot of warning messages, I came up with the following that is more like the original output behavior, easy to add in, reduces the verboseness of using "String(describing:)" both in code and output.
Basically, add an Optional extension that gives a String describing the thing in the optional, or simply "nil" if not set. In addition, if the thing in the optional is a String, put it in quotes.
extension Optional {
var orNil : String {
if self == nil {
return "nil"
}
if "\(Wrapped.self)" == "String" {
return "\"\(self!)\""
}
return "\(self!)"
}
}
And usage in a playground:
var s : String?
var i : Int?
var d : Double?
var mixed = "s = \(s.orNil) i = \(i.orNil) d = \(d.orNil)" // "s = nil i = nil d = nil"
d = 3
i = 5
s = ""
mixed = "s = \(s.orNil) i = \(i.orNil) d = \(d.orNil)" // "s = "" i = 5 d = 3.0"
s = "Test"
d = nil
mixed = "s = \(s.orNil) i = \(i.orNil) d = \(d.orNil)" // "s = "Test" i = 5 d = nil"
Thanks for help from following link:
check-if-variable-is-an-optional-and-what-type-it-wraps
a?.b?.c.orNil
.
See Ole Begeman's fix for this. I love it. It creates a ???
operator which you can then use like this:
var someValue: Int? = 5
print("The value is \(someValue ??? "unknown")")
// → "The value is 5"
someValue = nil
print("The value is \(someValue ??? "unknown")")
// → "The value is unknown"
Double click on the yellow triangle displayed on line containing this warning. This will show FixIt with two solutions.
https://i.stack.imgur.com/kyzTQ.png
Use String(describing:) to silence this warning : Using this it will become String(describing:
Swift 5
My solution is making an extension
which unwrap Optional
object to Any
.
When you log the object or print it out, you can see the actual object
or <nil>⭕️
(combination from text and visual character). It's useful to look at, especially in the console log.
extension Optional {
var logable: Any {
switch self {
case .none:
return "<nil>|⭕️"
case let .some(value):
return value
}
}
}
// sample
var x: Int?
print("Logging optional without warning: \(x.logable)")
// → Logging optional without warning: <nil>|⭕️
Create an interpolation method that accepts an optional generic Type with an unnamed parameter. All your annoying warnings will magically disappear.
extension DefaultStringInterpolation {
mutating func appendInterpolation<T>(_ optional: T?) {
appendInterpolation(String(describing: optional))
}
}
Success story sharing
?? "nil"
to silence the warning, which seemed to be mildly popular, so might show up in another proposal in the near future. I do agree that this workaround is less than ideal – personally, I feel it's rather obvious to expectOptional(...)
to be interpolated into the string for a strong optional – it was only really the case of IUOs that needed this warning IMO. But Swift is constantly evolving, so this may all change later down the line. But for now, it's what we've got.guard result == nil else { print("result was \(result as Optional)") return }
if let
? i.eif let result = result { print("result was \(result)"); return }
. Not all early returns need to be done with guards.