swift - extension - ¿Cómo agregar una extensión de cadena opcional?
swift protocol extension (8)
Extensiones en
Optional
que devuelven una
String
A partir de Swift 3, no puede restringir directamente un método de extensión a una
String
opcional.
Puede lograr el resultado equivalente con protocolos como explica la respuesta de Daniel Shin.
Sin embargo, puede crear un método de extensión en un Opcional de cualquier tipo y he encontrado algunos métodos útiles que tienen un valor de retorno de
String
.
Estas extensiones son útiles para registrar valores en la consola.
He usado asStringOrEmpty () en una
String
opcional cuando quiero reemplazar un posible nulo con una cadena vacía.
extension Optional {
func asStringOrEmpty() -> String {
switch self {
case .some(let value):
return String(describing: value)
case _:
return ""
}
}
func asStringOrNilText() -> String {
switch self {
case .some(let value):
return String(describing: value)
case _:
return "(nil)"
}
}
}
Ejemplo de uso:
var booleanValue: Bool?
var stringValue: String?
var intValue: Int?
print("booleanValue: /(booleanValue.asStringOrNilText())")
print("stringValue: /(stringValue.asStringOrNilText())")
print("intValue: /(intValue.asStringOrNilText())")
booleanValue = true
stringValue = "text!"
intValue = 41
print("booleanValue: /(booleanValue.asStringOrNilText())")
print("stringValue: /(stringValue.asStringOrNilText())")
print("intValue: /(intValue.asStringOrNilText())")
Salida de consola:
booleanValue: (nil)
stringValue: (nil)
intValue: (nil)
booleanValue: true
stringValue: text!
intValue: 41
Optional
diferente al puntero nulo
Estas extensiones ilustran que un
Optional
es diferente que un puntero nulo.
Un
Optional
es una
enum
de un tipo especificado (
Wrapped
) que indica que contiene o no un valor.
Puede escribir una extensión en el "contenedor"
Optional
aunque no contenga ningún valor.
Extracto de la Declaración Opcional Swift
enum Optional<Wrapped> : ExpressibleByNilLiteral {
/// The absence of a value.
case none
/// The presence of a value, stored as `Wrapped`.
case some(Wrapped)
...
}
En el código, la ausencia de un valor generalmente se escribe utilizando el literal
nil
lugar del caso de enumeración explícito
.none
.
Puede crear una extensión de cadena de esta manera:
extension String {
func someFunc -> Bool { ... }
}
pero ¿y si quieres que se aplique a una cadena opcional?
var optionalString :String? = ""
optionalString!.someFunc() /* String? does not have a member someFunc */
Desde Xcode 9.3, puede usar esta ligera modificación de la respuesta de @ Vladyslav:
extension Optional where Wrapped == String {
var isEmpty: Bool {
return self?.isEmpty ?? true
}
}
En Swift 3.1 también puede agregar una extensión a los valores opcionales:
extension Optional where Wrapped == String {
var isBlank: Bool {
return self?.isBlank ?? true
}
}
En Swift 4.1, estaba obteniendo un
Optional is ambiguous for type lookup in this context
error de compilación de
Optional is ambiguous for type lookup in this context
.
Para solucionarlo, debe agregar explícitamente el espacio de nombres Swift al tipo:
extension Swift.Optional where Wrapped == String {
var isBlank: Bool {
return self?.isBlank ?? true
}
}
Puedes hacerlo así:
protocol OptionalType { typealias A; var opt: A? { get } }
extension Optional: OptionalType { var opt: A? { return self } }
protocol StringType { var get: String { get } }
extension String: StringType { var get: String { return self } }
extension Optional where Wrapped: StringType {
func getOrElse(s: String) -> String {
return self.opt?.get ?? s
}
}
Y:
let optStr: String? = nil
optStr.getOrElse("hello world")
La razón por la que no puede restringir
Optional
o
String
para ese asunto, es porque son
struct
.
Al hacer un pseudo-protocolo para cada uno, ahora podemos restringir lo que queramos.
Siento que Swift ha renunciado a muchas cosas solo para facilitar que los principiantes aprendan o tal vez el idioma aún no ha madurado lo suficiente.
encontrado un truco rápido 3
class A{
var name:String!;
init(_ name:String?){
self.name = name;
}
}
extension Optional where Wrapped == String {
func compareText(_ other:String?)->Bool{
switch (self,other){
case let(a?,b?):
return a < b;
case (nil,_):
return true;
default:
return false;
}
}
}
let words:[A] = [A("a"),A(nil),A("b"),A("c"),A(nil)];
// let sorted = words.sorted{ 0.name.compareText($1.name) }
// trick
let sorted = words.sorted{ ($0.name as String?).compareText($1.name) }
print(sorted.map{$0.name});
Actualización: para una solución alternativa que funciona con Swift 2 y superior, vea la respuesta de Daniel Shin
Una cadena opcional no es en sí misma un tipo, por lo que no puede crear una extensión en un tipo opcional.
En Swift, un
Optional
es solo una enumeración (más un poco de azúcar sintáctica) que puede ser
None
o
Some
que envuelve un valor.
Para usar su método de Cadena, debe desenvolver su Cadena
optionalString
.
Puede usar fácilmente el encadenamiento opcional para lograr esto:
optionalString?.someFunc()
Si
optionalString
no es
nil
, se
someFunc
a
someFunc
.
Una forma alternativa (menos concisa) de hacer esto es usar el enlace opcional para establecer si
optionalString
tiene o no un valor antes de intentar llamar al método:
if let string = optionalString {
string.someFunc() // `string` is now of type `String` (not `String?`)
}
En su ejemplo de los comentarios a continuación, no necesita anidar múltiples declaraciones
if
, puede verificar si la cadena opcional es una cadena vacía en una sola
if
:
if optionalString?.isEmpty == true {
doSomething()
}
Esto funciona porque la expresión
optionalString?.isEmpty
devuelve un Bool opcional (es decir,
true
,
false
o
nil
).
Entonces,
doSomething()
solo se llamará si
optionalString
no
es
nil
,
y
si esa cadena está vacía.
Otra alternativa sería:
if let string = optionalString where string.isEmpty {
doSomethingWithEmptyString(string)
}
extension Optional where Wrapped == String {
var isNil: Bool {
return self == nil
}
La respuesta anterior (escrita por @Vlad Hatko) funciona bien pero en Swift 4 hay algunos problemas, así que la cambié a esto.