Article cover image

Nunca confie em Strings para condicionais

Quase sempre não é uma boa decisão utilizar Strings em condicionais porque você não terá ajuda do compilador para te alertar quando escrever incorretamente.

Jogue a primeira pedra o desenvolvedor que nunca perdeu horas de trabalho debugando código que não funciona até descobrir que ele digitou uma String incorretamente...

// uma função utilitária em algum lugar
func isTheMain(route: String) -> Bool {
	return route == "Main"
}

// utilizada em outro lugar...
let route = "main"
if isTheMain(route: route) {
	...
}

Você consegue encontrar o problema no código acima? Apenas uma letra minúscula diferente, suficiente para esconder bugs difíceis de encontrar. Nós podemos fazer melhor. Nós precisamos escrever códigos mais confiáveis!

Ok, nós podemos usar constantes para definir a String apenas uma vez e ficar seguros ao reutilizar ela. Mas eu particularmente prefiro utilizar Enums porque eles adicionam grande flexibilidade no meu código. Vamos ver isso com mais detalhes.

Enum

Este tipo especial de estrutura de dados é muito útil para várias necessidades, especialmente em Swift onde ele é extremamente poderoso quando utilizado com condicionais Switch, por exemplo. Falarei mais sobre isso depois.

Primeiro, vamos corrigir o código anterior usando enum:

enum Routes: String {
	case Main
	case AnotherRoute
}
func isTheMain(route: Routes) -> Bool {
	return route == .Main
}

let route: Routes = .Main
if isTheMain(route: route) {
	...
}

Nós agora temos uma fonte segura para qualquer coisa relacionada à rotas no app e podemos utiliza-la com confiança porque não estamos mais escrevendo textos (Strings) manualmente. Note que em Swift nós podemos omitir o nome da Enum ao utiliza-la, apenas usando ponto e o valor. O compilador infere o tipo para nós.

Além disso, se a String vier de uma API ou banco de dados, por exemplo, nós podemos facilmente criar nossa rota utilizando o inicializador do Enum:

// confiamos que esta String vem de uma fonte segura (sempre correta)
let routeStringFromDatabase = "Main"

let route = Routes.init(rawValue: routeStringFromDatabase)

EM Swift, Enum funciona muito bem com Switch

Outro benefício de Enum pode ser percebido ao utiliza-lo em condicionais Switch porque o compilador nos obriga a definir todas as ações (case) para cada opção do Enum se não utilizarmos o default case. Vamos ver:

enum Routes: String {
	case About
	case Home
}

func doSomethingWith(route: Routes) -> Void {
	switch route {
		case .About:
			print("Vamos ver a página Sobre")
		case .Home:
			print("A entediante página inicial")
	}
}

Note no código acima que nós temos 2 cases no enum Routes e na função doSomethingWith nós utilizamos um condicional Switch que define cases para todas as opções (apenas um print básico neste exemplo). Isso é conhecido como exhaustive switch.

A melhor parte disso é quando você precisa adicionar outra rota e o compilador imediatamente joga erros em você porque o condicional switch não é mais exaustivo:

not exhaustive switch.jpg

Nesse momento teremos o compilador trabalhando para nós para escrevermos códigos mais confiáveis.

Olhe os seus métodos prepareForSegue

Eu aposto que você vai encontrar algo para refatorar neles porque é bastante comum abrir projetos Swift e encontrar códigos parecidos isso:

override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
	guard let identifier = segue.identifier else { return }

	if identifier == "About" {
		...
	} else if identifier == "Home" {
		...
	} else if identifier == "Login" {
		...
	}
}

Eu sei, o parâmetro identifier vem do UIKit e nós temos que trabalhar com ele assim, sem modificar. Mas você pode facilmente melhorar essa condicional if else para ser mais confiável utilizando nosso enum Routes:

override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
	guard let identifier = segue.identifier else { return }

	if identifier == Routes.About.rawValue {
		...
	} else if identifier == Routes.Home.rawValue {
		...
	} else if identifier == Routes.Login.rawValue {
		...
	}
}

O propriedade rawValue retorna o valor da String do enum e nós eliminamos a chance de escrever errado. Incrível!

Conclusão

É uma boa prática nunca confiar em Strings em condicionais porque Ei nós somos humanos e nós falhamos o tempo todo. Vamos ajudar o compilador a nos ajudar prevenindo nossos próprios bugs.

Espero que você tenha gostado do artigo e se tiver alguma coisa para discutir ou quiser dar algum feedback fique à vontade para comentar abaixo. Ficarei feliz em conversar com você!