¿Por qué es opcional el valor de retorno de String.addingPercentEncoding ()?

La firma del método String para el porcentaje de escape es:

 func addingPercentEncoding(withAllowedCharacters: CharacterSet) -> String? 

(Esto era stringByAddingPercentEncodingWithAllowedCharacters en Swift 2.)

¿Por qué este método devuelve un opcional?

La documentación dice que el método devuelve nil “si la transformación no es posible”, pero no está claro en qué circunstancias podría fallar la transformación de escape:

  • Los caracteres se escapan usando UTF-8, que es una encoding Unicode completa. Cualquier carácter Unicode válido se puede codificar utilizando UTF-8 y, por lo tanto, se puede escapar.

  • Pensé que tal vez el método aplicaba algún tipo de comprobación de cordura para las malas interacciones entre el conjunto de caracteres permitidos y los caracteres utilizados para escapar, pero este no es el caso: el método tiene éxito sin importar si el conjunto de caracteres permitidos contiene “%”, y también tiene éxito si el conjunto de caracteres permitido está vacío.

Tal como está, el valor de retorno no opcional parece forzar una comprobación de error sin sentido.

Archivé un informe de error con Apple sobre esto, y escuché de nuevo, ¡con una respuesta muy útil, nada menos!

Resulta (para mi sorpresa) que es posible crear con éxito cadenas Swift que contengan Unicode no válido en forma de caracteres sustitutos UTF-16 sin emparejar. Tal cadena puede hacer que la encoding UTF-8 falle. Aquí hay un código que ilustra este comportamiento:

 // Succeeds (wat?!): let str = String( bytes: [0xD8, 0x00] as [UInt8], encoding: String.Encoding.utf16BigEndian)! // Returns nil: str.addingPercentEncoding(withAllowedCharacters: CharacterSet.alphanumerics) 

Basado en la respuesta de Paul Cantrell, pequeña demostración de que también es posible que el mismo método también devuelva nulo en Objective-C, a pesar de que String y NSString son bestias diferentes cuando se trata de codificaciones:

 uint8_t bytes[2] = { 0xD8, 0x00 }; NSString *string = [[NSString alloc] initWithBytes:bytes length:2 encoding:NSUTF16BigEndianStringEncoding]; // \ud800 NSLog(@"%@", string); NSString *escapedString = [string stringByAddingPercentEncodingWithAllowedCharacters:NSCharacterSet.URLHostAllowedCharacterSet]; // (null) NSLog(@"%@", escapedString); 

Para divertirse, https://r12a.github.io/app-conversion/ escapará por ciento lo mismo que:

Error% 20in% 20convertUTF162Char% 3A% 20low% 20surrogate% 20expected% 2C% 20b% 3D0% 21% 00