
A interpolação de cadeias estava no Swift de versões anteriores, mas no Swift 5.0 essa funcionalidade foi expandida, tornou-se mais rápida e muito mais poderosa.
Neste artigo, abordaremos as novas possibilidades de interpolação de cadeias e consideraremos como isso pode ser aplicado em nosso próprio código. Você também pode baixar as fontes deste artigo
aqui.O básico
Usamos interpolação básica de strings como esta:
let age = 38 print("You are \(age)")
Tomamos isso como garantido, mas ao mesmo tempo foi um alívio significativo em comparação com o que tínhamos de lidar antes:
[NSString stringWithFormat:@"%ld", (long)unreadCount];
Há também um ganho de desempenho significativo, pois a alternativa foi:
let all = s1 + s2 + s3 + s4
Sim, o resultado final seria o mesmo, mas Swift precisaria adicionar s1 a s2 para obter s5, adicionar s5 a s3 para obter s6 e adicionar s6 a s4 para obter s7, antes de atribuir tudo.
A interpolação de strings mal mudou com o Swift 1.0, a única mudança significativa ocorreu com o Swift 2.1, onde tivemos a oportunidade de usar
literais de strings na interpolação:
print("Hi, \(user ?? "Anonymous")")
Como você sabe, o Swift está se desenvolvendo em grande parte graças às sugestões da comunidade. As idéias são discutidas, desenvolvidas e aceitas ou rejeitadas.
Então, cinco anos depois, o desenvolvimento de Swift chegou à interpolação de linha. O Swift 5.0 apresenta novos super recursos que nos permitem controlar o processo de interpolação de strings.
Para tentar, considere o seguinte cenário. Se definirmos uma nova variável inteira como esta:
let age = 38
é óbvio que podemos usar a interpolação de strings da seguinte maneira:
print("Hi, I'm \(age).")
Mas e se quisermos formatar o resultado de uma maneira diferente?
Usando o novo sistema de interpolação de strings no Swift 5.0, podemos escrever a extensão String.StringInterpolation para adicionar nosso próprio método de interpolação:
extension String.StringInterpolation { mutating func appendInterpolation(_ value: Int) { let formatter = NumberFormatter() formatter.numberStyle = .spellOut if let result = formatter.string(from: value as NSNumber) { appendLiteral(result) } } }
Agora, o código exibirá toda a variável como texto: "Oi, tenho trinta e oito".
Podemos usar uma técnica semelhante para corrigir a formatação da data, pois a exibição de data padrão como uma sequência não é muito atraente:
print("Today's date is \(Date()).")
Você verá que o Swift exibe a data atual na forma de algo como: "2019-02-21 23:30:21 +0000". Podemos torná-lo mais bonito usando nossa própria formatação de data:
mutating func appendInterpolation(_ value: Date) { let formatter = DateFormatter() formatter.dateStyle = .full let dateString = formatter.string(from: value) appendLiteral(dateString) }
Agora, o resultado parece muito melhor, algo como: "21 de fevereiro de 2019 às 23:30:21".
Nota: para evitar possíveis confusões ao trabalhar juntos em equipe, você provavelmente não deve substituir os métodos Swift padrão. Portanto, forneça aos parâmetros os nomes de sua escolha para evitar confusão:
mutating func appendInterpolation(format value: Int) {
Agora vamos chamar esse método com um parâmetro nomeado:
print("Hi, I'm \(format: age).")
Agora ficará claro que estamos usando nossa própria implementação do método.
Interpolação com parâmetros
Essa alteração mostra que agora temos controle total sobre como ocorre a interpolação de strings.
Por exemplo, podemos reescrever o código para processar as mensagens do Twitter:
mutating func appendInterpolation(twitter: String) { appendLiteral("<a href=\"https://twitter.com/\(twitter)\">@\(twitter)</a>") }
Agora podemos escrever assim:
print("You should follow me on Twitter: \(twitter: "twostraws").")
Mas por que devemos nos limitar a um parâmetro? Para o nosso exemplo de formatação de números, não faz sentido forçar os usuários a usar um parâmetro de conversão (.spellOut) - portanto, mudaremos o método adicionando um segundo parâmetro:
mutating func appendInterpolation(format value: Int, using style: NumberFormatter.Style) { let formatter = NumberFormatter() formatter.numberStyle = style if let result = formatter.string(from: value as NSNumber) { appendLiteral(result) } }
E use-o assim:
print("Hi, I'm \(format: age, using: .spellOut).")
Você pode ter quantos parâmetros quiser, de qualquer tipo. Exemplo usando
@autoclosure para o valor padrão:
extension String.StringInterpolation { mutating func appendInterpolation(_ values: [String], empty defaultValue: @autoclosure () -> String) { if values.count == 0 { appendLiteral(defaultValue()) } else { appendLiteral(values.joined(separator: ", ")) } } } let names = ["Malcolm", "Jayne", "Kaylee"] print("Crew: \(names, empty: "No one").")
Usar o atributo
@autoclosure significa que, para o valor padrão, podemos usar valores simples ou chamar funções complexas. No método, eles se tornarão um fechamento.
Agora, você pode estar pensando que podemos reescrever o código sem usar a funcionalidade de interpolação, algo como isto:
extension Array where Element == String { func formatted(empty defaultValue: @autoclosure () -> String) -> String { if count == 0 { return defaultValue() } else { return self.joined(separator: ", ") } } } print("Crew: \(names.formatted(empty: "No one")).")
Mas agora complicamos a ligação - porque obviamente estamos tentando formatar algo, esse é o ponto de interpolação. Lembre-se da regra Swift - evite palavras desnecessárias.
Erica Sadun ofereceu um exemplo muito breve e bonito de como simplificar o código:
extension String.StringInterpolation { mutating func appendInterpolation(if condition: @autoclosure () -> Bool, _ literal: StringLiteralType) { guard condition() else { return } appendLiteral(literal) } } let doesSwiftRock = true print("Swift rocks: \(if: doesSwiftRock, "(*)")") print("Swift rocks \(doesSwiftRock ? "(*)" : "")")
Adicionando interpolação de string para tipos personalizados
Podemos usar a interpolação de strings para nossos próprios tipos:
struct Person { var type: String var action: String } extension String.StringInterpolation { mutating func appendInterpolation(_ person: Person) { appendLiteral("I'm a \(person.type) and I'm gonna \(person.action).") } } let hater = Person(type: "hater", action: "hate") print("Status check: \(hater)")
A interpolação de cadeias de caracteres é útil porque não tocamos nas informações de depuração sobre um objeto. Se olharmos para ele no depurador ou o exibirmos, veremos dados intocados:
print(hater)
Podemos combinar um tipo personalizado com vários parâmetros:
extension String.StringInterpolation { mutating func appendInterpolation(_ person: Person, count: Int) { let action = String(repeating: "\(person.action) ", count: count) appendLiteral("\n\(person.type.capitalized)s gonna \(action)") } } let player = Person(type: "player", action: "play") let heartBreaker = Person(type: "heart-breaker", action: "break") let faker = Person(type: "faker", action: "fake") print("Let's sing: \(player, count: 5) \(hater, count: 5) \(heartBreaker, count: 5) \(faker, count: 5)")
Obviamente, você pode usar todos os recursos do Swift para criar sua própria formatação. Por exemplo, podemos escrever uma implementação que aceite qualquer
Encodable e a imprima em JSON:
mutating func appendInterpolation<T: Encodable>(debug value: T) { let encoder = JSONEncoder() encoder.outputFormatting = .prettyPrinted if let result = try? encoder.encode(value) { let str = String(decoding: result, as: UTF8.self) appendLiteral(str) } }
Se tornarmos
Person compatível com o protocolo
Encodable , podemos fazer o seguinte:
print("Here's some data: \(debug: faker)")
Você pode usar recursos como um número variável de parâmetros, até marcar sua implementação de interpolação como
lançada . Por exemplo, nosso sistema de formatação JSON não responde no caso de um erro de codificação, mas podemos corrigir isso para analisar o erro no futuro:
mutating func appendInterpolation<T: Encodable>(debug value: T) throws { let encoder = JSONEncoder() encoder.outputFormatting = .prettyPrinted let result = try encoder.encode(value) let str = String(decoding: result, as: UTF8.self) appendLiteral(str) } print(try "Status check: \(debug: hater)")
Tudo o que vimos até agora são apenas modificações nos métodos de interpolação de strings.
Criando tipos personalizados usando a interpolação
Como você viu, era uma questão de como formatar os dados em seu aplicativo de uma maneira realmente conveniente, mas também podemos criar nossos próprios tipos usando a interpolação de strings.
Para demonstrar isso, criaremos um novo tipo que é inicializado a partir de string usando interpolação de string.
struct ColoredString: ExpressibleByStringInterpolation {
De fato, há um açúcar sintático sob o capô. Poderíamos escrever a parte final manualmente:
var interpolation = ColoredString.StringInterpolation(literalCapacity: 10, interpolationCount: 1) interpolation.appendLiteral("Hello") interpolation.appendInterpolation(message: "Hello", color: .red) interpolation.appendLiteral("Hello") let valentine = ColoredString(stringInterpolation: interpolation)
Conclusão
Como você viu, a interpolação de string personalizada nos permite colocar a formatação em um único local, para que as chamadas de método se tornem mais simples e claras. Também nos oferece uma grande flexibilidade para criar os tipos necessários da maneira mais natural possível.
Lembre-se de que essa é apenas uma das possibilidades - e não a única. Isso significa que algumas vezes usamos interpolação, outras funções ou algo mais. Como muito no desenvolvimento, você sempre precisa escolher a melhor maneira de resolver o problema.