Por que o valor de retorno de String.addingPercentEncoding () é opcional?

A assinatura do método String para escape percentual é:

 func addingPercentEncoding(withAllowedCharacters: CharacterSet) -> String? 

(Isso era stringByAddingPercentEncodingWithAllowedCharacters no Swift 2.)

Por que esse método retorna um opcional?

A documentação diz que o método retorna nulo “se a transformação não for possível”, mas não está claro sob quais circunstâncias a transformação de escape pode falhar:

  • Os caracteres são escapados usando UTF-8, que é uma codificação Unicode completa. Qualquer caractere Unicode válido pode ser codificado usando UTF-8 e, portanto, pode ser escapado.

  • Pensei que talvez o método tenha aplicado algum tipo de verificação de integridade para interações ruins entre o conjunto de caracteres permitidos e os caracteres usados ​​para escape, mas esse não é o caso: o método é bem-sucedido, independentemente de o conjunto de caracteres permitidos conter “%” e também terá sucesso se o conjunto de caracteres permitido estiver vazio.

Da forma como está, o valor de retorno não opcional parece estar forçando uma verificação de erro sem sentido.

Eu arquivei um relatório de bug com a Apple sobre isso, e ouvi de volta – com uma resposta muito útil, não menos!

Acontece (para minha surpresa) que é possível criar com êxito as sequências Swift que contêm Unicode inválido na forma de caracteres substitutos UTF-16 não pareados. Essa string pode causar falha na codificação UTF-8. Aqui está um código que ilustra esse comportamento:

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

Baseado na resposta de Paul Cantrell, pequena demonstração de que também é possível para o mesmo método retornar também null em Objective-C, apesar de String e NSString serem bestas diferentes quando se trata de codificações:

 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 se divertir, https://r12a.github.io/app-conversion/ , o percentual será o mesmo que:

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