如何解决“字符串插值产生一个可选值的调试描述; 您的意思是将其显式化吗?”在 Xcode 8.3测试版里?

从 Beta 8.3开始,就有无数的警告“ String 插值为一个可选值生成一个调试描述; 您是否打算将其显式化?”出现在我的代码中。

例如,在下列情况下出现警告,其中选项可能导致零:

let msg = "*** Error \(options["taskDescription"]): cannot load \(sUrl) \(error)"

正如以前设计的那样,我(和编译器)可以将选项插入为‘ nil’。但是编译器改变了主意。

编译器建议添加一个 String 构造函数,其描述如下:

let msg = "*** Error \(String(describing: options["taskDescription"])): cannot load \(sUrl) \(error)"

显然,结果是明确的,但也非常非常麻烦,在我看来。还有更好的选择吗?我必须修复所有这些警告,还是最好等到下一个测试版?

Screenshot for description

53716 次浏览

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.

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

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.

Screenshot added

  1. Use String(describing:) to silence this warning :

    Using this it will become String(describing:<Variable>)

    Eg. : String(describing: employeeName)

  2. Provide a default value to avoid this warning :

    Using this it will become (<Variable> ?? default value)

    Eg.: employeeName ?? “Anonymous” as! String

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.

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))
}
}