Como resolver “A interpolação de strings produz uma descrição de depuração para um valor opcional; você quis tornar isso explícito? ” no Xcode 8.3 beta?

87

Desde o beta 8.3, zilhões de avisos "A interpolação de strings produz uma descrição de depuração para um valor opcional; você pretendia tornar isso explícito?" apareceu no meu código.

Por exemplo, o aviso apareceu na seguinte situação, em que as opções podem levar a zero:

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

Conforme projetado anteriormente, estava tudo bem para mim (e para o compilador) que os opcionais fossem interpolados como 'nulo'. Mas o compilador mudou de ideia.

O que o compilador sugere é adicionar um construtor String com a descrição a seguir:

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

Obviamente, os resultados são explícitos, mas também muito complicados na minha opinião. Existe uma opção melhor? Tenho que corrigir todos esses avisos ou melhor esperar pelo próximo beta?

Captura de tela para descrição

Stéphane de Luca
fonte
26
Que aviso realmente irritante ...
Jonny
Swift 3quebrou o meu próprio loge cometi um erro ao simplesmente usar print. Deve sempre criar seu próprio invólucro, caso contrário você ficará confuso com esse tipo de "novo recurso".
superarts.org

Respostas:

105

Esta é uma alteração que foi feita nesta solicitação de pull devido ao fato de que a interpolação Optional(...)na string resultante é frequentemente indesejável e pode ser especialmente surpreendente em casos com opcionais não embalados implicitamente . Você pode ver a discussão completa sobre essa mudança na lista de discussão aqui .

Conforme mencionado na discussão de solicitação pull (embora infelizmente não seja pelo Xcode) - uma maneira um pouco melhor de silenciar o aviso do que o uso de String(describing:)é adicionar um elenco ao tipo opcional de tudo o que você está interpolando, por exemplo:

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

Que também pode ser generalizado para 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

No Swift 5, com o novo sistema de interpolação de string introduzido por SE-0228 , outra opção é adicionar uma appendInterpolationsobrecarga personalizada para 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

E, se desejar, você pode até mesmo remover o rótulo do argumento para desativar o aviso inteiramente em um módulo (ou em um arquivo específico, se você marcá-lo como 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

Embora pessoalmente eu prefira manter o rótulo do argumento.

Hamish
fonte
A partir da proposta, não está claro se essa mudança será permanente? O que você acha? @Hamish
Stéphane de Luca
@ StéphanedeLuca Houve bastante discussão na lista de discussão sobre outras soluções, como permitir ?? "nil"silenciar o aviso, que parecia ser ligeiramente popular, por isso pode aparecer em outra proposta no futuro próximo. Eu concordo que essa solução alternativa é menos do que ideal - pessoalmente, acho que é bastante óbvio esperar Optional(...)ser interpolado na string para um opcional forte - era apenas o caso de IUOs que precisavam desse aviso IMO. Mas o Swift está em constante evolução, então tudo isso pode mudar mais tarde. Mas, por enquanto, é o que temos.
Hamish
Eu também me deparei com um problema um tanto 'relacionado' em se não descompactarmos mais aqui stackoverflow.com/questions/42543512/… se você pode dar uma olhada? @Hamish
Stéphane de Luca
... em qualquer caso, este código é uma loucura:guard result == nil else { print("result was \(result as Optional)") return }
loretoparisi
1
@loretoparisi Por que não usar if let? ie if let result = result { print("result was \(result)"); return }. Nem todos os retornos antecipados precisam ser feitos com guardas.
Hamish
29

Duas maneiras mais fáceis de lidar com esse problema.

Opção 1:

A primeira seria "desembrulhar à força" o valor que você gostaria de retornar usando um estrondo (!)

var someValue: Int? = 5
print(someValue!)

Resultado:

5

Opção 2:

A outra forma, que poderia ser a melhor - é "desembrulhar com segurança" o valor que você deseja que seja retornado.

var someValue: Int? = 5

if let newValue = someValue {
    print(newValue)
}

Resultado:

5

Recomendaria ir com a opção 2.

Dica: Evite forçar o desempacotamento (!) Sempre que possível, pois não temos certeza se sempre teremos o valor a ser desembrulhado.

Mo Iisa
fonte
1
Sou novo, mas gosto da opção 2 para validar a embalagem antes de imprimir e você sempre tem a opção de imprimir outra coisa quando estiver desembrulhada
AbuTaareq
16

parece usar String (descrevendo: opcional) é mais simples.

valor padrão ?? não faz sentido para não Strings, por exemplo, Int.
Se Int for nulo, então você deseja que o log mostre 'nil' e não o padrão para outro Int, por exemplo, 0.

Alguns códigos de playground para testar:

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

Resultado

optionalString: nil
optionalInt: nil
brian.clear
fonte
13

Depois de atualizar para o Xcode 8.3 e receber muitas mensagens de aviso, eu vim com o seguinte que é mais parecido com o comportamento de saída original, fácil de adicionar, reduz o detalhamento do uso de "String (descrevendo :)" tanto no código quanto na saída .

Basicamente, adicione uma extensão opcional que forneça uma string descrevendo a coisa no opcional, ou simplesmente "nil" se não for definida. Além disso, se o item opcional for uma String, coloque-o entre aspas.

extension Optional {
    var orNil : String {
        if self == nil {
            return "nil"
        }
        if "\(Wrapped.self)" == "String" {
            return "\"\(self!)\""
        }
        return "\(self!)"
    }
}

E uso em um 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"

Obrigado pela ajuda no seguinte link:

check-if-variable-is-an-optional-and-what-type-it-wraps

Anorskdev
fonte
Esta solução não funciona em cadeia opcional. Gosto a?.b?.c.orNil.
Vincent Sit
8

Clique duas vezes no triângulo amarelo exibido na linha que contém este aviso. Isso mostrará FixIt com duas soluções.

Captura de tela adicionada

  1. Use String(describing:)para silenciar este aviso:

    Usando isso, ele se tornará String(describing:<Variable>)

    Por exemplo. :String(describing: employeeName)

  2. Forneça um default valuepara evitar este aviso:

    Usando isso, ele se tornará (<Variable> ?? default value)

    Por exemplo.: employeeName ?? “Anonymous” as! String

Jayprakash Dubey
fonte
1
Sim, eu também escolheria o Operador Nil-Coalescing: developer.apple.com/library/content/documentation/Swift/…
kevinius
1
Ótima resposta! Nil-coalescing funciona bem com isso se você tiver um valor de string alternativo para fornecer
Lance Samaria
1

Swift 5

Minha solução é fazer um objeto extensionque desembrulhe .OptionalAny

Quando você registra o objeto ou imprime-o, você pode ver o real objectou <nil>⭕️(combinação de texto e caráter visual). É útil olhar, especialmente no log do console.

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>|⭕️
Nahung89
fonte
0

Crie um método de interpolação que aceite um Type genérico opcional com um parâmetro sem nome. Todos os seus avisos irritantes irão desaparecer magicamente.

extension DefaultStringInterpolation {
  mutating func appendInterpolation<T>(_ optional: T?) {
    appendInterpolation(String(describing: optional))
  }
}
ScottyBlades
fonte