Código mais seguro em Swift com controles de acesso

Rocketseat

Rocketseat

3 min de leitura
swift
Faaala, dev? Tudo pronto para dar mais um passo na sua jornada com Swift?
Sabe quando você está construindo algo incrível, mas começa a sentir que o código está virando uma daquelas gavetas de "coisas aleatórias"? Onde qualquer parte do seu app pode mexer em tudo, e você fica com aquele medinho de que uma mudança aqui quebre algo totalmente inesperado lá na frente? Pois é, organização e segurança são chaves!
É aí que entram os controles de acesso em Swift! Eles são como os seguranças superinteligentes do seu código, definindo quem pode entrar e o que pode fazer em cada parte do seu projeto. Parece complicado? Que nada!
Neste artigo direto ao ponto, vamos desvendar juntos esses "guardiões" do Swift. Você vai entender exatamente o que são, por que são essenciais e como usar cada nível de acesso para deixar seu código mais limpo, seguro e profissional.
Bora desvendar esses segredos e dar um boost nos seus projetos Swift?

O que são controles de acesso?

Imagine seu código como uma casa inteligente. Você não deixaria a porta da rua escancarada, nem daria a chave do seu cofre pessoal para qualquer visitante, certo? Controles de acesso funcionam de forma parecida: eles restringem o acesso a partes do seu código (classes, structs, métodos, propriedades) com base em onde esse código está sendo chamado.
É sobre visibilidade e permissão.

Por que isso é tão importante?

  • Segurança: impede que partes do seu código sejam usadas ou modificadas de forma inadequada, evitando bugs e comportamentos inesperados.
  • Organização: ajuda a definir interfaces claras. Você expõe apenas o necessário, escondendo a complexidade interna. Fica mais fácil entender como usar um componente sem precisar saber todos os seus segredos.
  • Trabalho em equipe: em times, define limites claros. Cada um sabe o que pode usar do código do outro sem medo de quebrar a implementação interna.
  • Manutenção: facilita na hora de refatorar ou melhorar uma parte do código. Se os detalhes internos estão escondidos, você pode mudá-los sem afetar quem usa a parte "pública" do seu componente.
Entendido o porquê? Agora, vamos conhecer os guardiões.

Os 5 guardiões do código Swift

Swift nos oferece cinco níveis de controle de acesso, do mais restrito ao mais aberto. Vamos conhecer cada um:
private
  • Acesso: só pode ser acessado de dentro da mesma definição (classe, struct, enum) e de extensões dessa definição que estejam no mesmo arquivo.
  • Uso típico: para detalhes de implementação super internos, variáveis auxiliares que só fazem sentido dentro daquele bloco específico. É o seu diário trancado na gaveta.
    • struct CofreDoMayk { private var combinacaoSecreta: String = "123456" // Só o Mayk aqui dentro sabe func tentarAbrir(senha: String) -> Bool { // Pode acessar combinacaoSecreta aqui dentro return senha == self.combinacaoSecreta } } let meuCofre = CofreDoMayk() // print(meuCofre.combinacaoSecreta) // ERRO! Não pode acessar de fora. print(meuCofre.tentarAbrir(senha: "123456")) // OK, usa o método público
fileprivate
  • Acesso: pode ser acessado de qualquer lugar dentro do mesmo arquivo fonte (.swift).
  • Uso típico: quando uma funcionalidade precisa ser compartilhada entre diferentes classes ou structs, mas apenas dentro daquele arquivo específico. É uma conversa na sala de estar, só a família (o arquivo) ouve.
    • // Arquivo: UtilsDaIsabela.swift fileprivate func logInternoDoArquivo(mensagem: String) { print("[LOG ISABELA]: \(mensagem)") } class GerenciadorDeLogin { func logarUsuario(nome: String) { // ... lógica de login ... logInternoDoArquivo(mensagem: "Tentativa de login para: \(nome)") // OK, mesmo arquivo } } struct ValidadorDeSenha { func validar(senha: String) -> Bool { logInternoDoArquivo(mensagem: "Validando senha...") // OK, mesmo arquivo return senha.count > 6 } } // Em outro arquivo .swift, a função logInternoDoArquivo seria inacessível.
internal
  • Acesso: pode ser acessado de qualquer lugar dentro do mesmo módulo (seu aplicativo ou framework).
  • Uso típico: a grande maioria do código do seu aplicativo. Classes, funções e propriedades que precisam interagir entre si dentro do seu projeto, mas não precisam ser expostas para fora (caso você estivesse criando um framework).
    • // Não precisa escrever 'internal', é o padrão! class PerfilUsuario { var nome: String // internal por padrão var email: String // internal por padrão init(nome: String, email: String) { self.nome = nome self.email = email } func exibirBoasVindas() { // internal por padrão print("Boas-vindas, \(nome)!") } } // Em qualquer outro arquivo DENTRO do mesmo app/módulo: let perfilDaFernanda = PerfilUsuario(nome: "Fernanda", email: "fernanda@rocketseat.team") perfilDaFernanda.exibirBoasVindas() // OK print(perfilDaFernanda.nome) // OK
      ⚠️
      Este é o nível padrão! Se você não escrever nada (private, public, etc.), o Swift assume que é internal.
public
  • Acesso: pode ser acessado de qualquer lugar, inclusive de fora do módulo (por exemplo, se outro app importar seu framework).
  • Uso típico: para definir a interface pública de um framework ou biblioteca.
    • // Em um Framework "ComponentesDoRodrigo.framework" public class BotaoCustomizado { public var titulo: String = "Clique Aqui" public init() {} // O inicializador precisa ser public também! public func acaoDoClique() { print("Botão clicado!") } } // Em um App que importa "ComponentesDoRodrigo.framework": // let meuBotao = BotaoCustomizado() // OK // meuBotao.titulo = "Meu Botão" // OK // meuBotao.acaoDoClique() // OK // class MeuBotaoEspecial : BotaoCustomizado {} // ERRO! Não pode subclassificar public fora do módulo.
      ⚠️
      Entidades public não podem ser subclassificadas (para classes) ou sobrescritas (para métodos) fora do módulo onde foram definidas. Pense como uma API: você pode usar, mas não pode alterar a estrutura fundamental dela por fora.
open
  • Acesso: o mais permissivo. Pode ser acessado de qualquer lugar, dentro ou fora do módulo.
  • Uso típico: usado principalmente em frameworks que são projetados para serem extensíveis por quem os utiliza. Pense em classes base que outros devs podem querer herdar e customizar. Se você está apenas desenvolvendo seu app e não um framework, provavelmente nunca vai precisar usar open
    • // Em um Framework "FrameworkBaseDoDiego.framework" open class ViewControllerBase { open var nomeDaTela: String = "Tela Genérica" open func viewDidAppear() { // Método aberto para ser sobrescrito print("\(nomeDaTela) apareceu!") } } // Em um App que importa "FrameworkBaseDoDiego.framework": class MinhaTelaPrincipal : ViewControllerBase { // OK! Pode subclassificar open. override func viewDidAppear() { // OK! Pode sobrescrever open. self.nomeDaTela = "Tela Principal do App" super.viewDidAppear() // Chama a implementação original print("Lógica adicional da minha tela!") } }
      ⚠️
      Classes open podem ser subclassificadas fora do módulo, e métodos open podem ser sobrescritos fora do módulo.
Ufa! Esses são os 5 níveis. Parece muito, mas a prática leva à perfeição!

Colocando em prática: gerenciando tarefas

Vamos ver um exemplo mais integrado, usando diferentes níveis para gerenciar tarefas:
// Arquivo: TarefaManager.swift // Função auxiliar só para este arquivo fileprivate func registrarLog(id: UUID, acao: String) { print("[\(Date())] Tarefa \(id): \(acao)") } public struct Tarefa { // Pública: pode ser usada em qualquer lugar do app public let id: UUID // Pública e imutável: todos veem, ninguém muda public var descricao: String // Pública e mutável: descrição pode ser editada public var concluida: Bool = false // Pública: status pode ser alterado internal let projetoAssociado: String // Interna: só código do módulo sabe o projeto private var notasSecretas: String = "" // Privada: só a struct Tarefa manipula // Inicializador público public init(descricao: String, projeto: String) { self.id = UUID() self.descricao = descricao self.projetoAssociado = projeto // Definido na criação registrarLog(id: self.id, acao: "Criada no projeto '\(projeto)' por Laís") // Usa func fileprivate } // Método público para marcar como concluída public mutating func marcarConcluida() { self.concluida = true registrarLog(id: self.id, acao: "Marcada como concluída") limparNotasSecretas() // Chama método privado } // Método público para adicionar notas (que ficam privadas) public mutating func adicionarNotaSecreta(_ nota: String) { self.notasSecretas += "\(nota)\n" registrarLog(id: self.id, acao: "Nota secreta adicionada") } // Método privado para limpar notas ao concluir private mutating func limparNotasSecretas() { self.notasSecretas = "" print("Notas secretas da tarefa \(id) limpas.") } } // Classe interna para gerenciar a lista de tarefas internal class GerenciadorDeTarefas { internal var tarefas: [Tarefa] = [] internal func adicionar(_ tarefa: Tarefa) { self.tarefas.append(tarefa) print("Gerenciador: Tarefa \(tarefa.id) adicionada.") // Poderia acessar tarefa.projetoAssociado aqui, pois é internal } // ... outros métodos internos para buscar, remover tarefas, etc. }
Analisando as escolhas
  • Tarefa (struct): public porque queremos criar e usar tarefas em várias partes do nosso app (ex: na UI, na lógica de negócios).
  • id, descricao, concluida: public porque são informações essenciais que outras partes do app precisam ler e, em alguns casos, modificar (descricao, concluida). id é let (constante) para garantir unicidade após a criação.
  • projetoAssociado: internal talvez porque a associação com um projeto seja uma lógica gerenciada apenas dentro deste módulo de gerenciamento, não exposta diretamente para a UI, por exemplo.
  • notasSecretas: private porque são detalhes internos da tarefa. A struct oferece um método public (adicionarNotaSecreta) para interagir com elas de forma controlada. Ninguém de fora pode ler ou limpar as notas diretamente.
  • registrarLog (func): fileprivate porque é uma função auxiliar usada apenas pelas entidades (Tarefa, GerenciadorDeTarefas) definidas neste arquivo TarefaManager.swift.
  • limparNotasSecretas (método): private pois é uma ação interna da Tarefa, chamada apenas pelo método marcarConcluida.
  • GerenciadorDeTarefas (class): internal (padrão) porque toda a lógica de gerenciamento da lista de tarefas acontece dentro deste módulo. A UI, por exemplo, interagiria com ele, mas ele não é feito para ser usado por outro módulo/framework.
 
Viu como cada nível tem seu propósito para criar um sistema mais organizado e seguro?

Regra de ouro: qual nível escolher?

Na dúvida, siga o princípio do menor privilégio:
  1. Comece sempre com private. É o mais seguro.
  1. Se precisar acessar de outra função ou classe no mesmo arquivo, mude para fileprivate.
  1. Se precisar acessar de outros arquivos dentro do seu app/módulo, use internal (ou simplesmente não coloque nada, já que é o padrão).
  1. Só use public ou open se você estiver criando um framework ou biblioteca que será usado por outros módulos ou apps, e você precisa definir uma interface externa clara.
Para a vasta maioria do código que você escreverá para seus aplicativos iOS, internal, fileprivate e private serão seus melhores amigos!

E no universo iOS?

Dominar os controles de acesso é FUNDAMENTAL no desenvolvimento iOS! Por quê?
  • Arquitetura limpa (MVVM, MVC, VIPER...): ajuda a separar as responsabilidades. Sua View não deve acessar detalhes privados do seu ViewModel ou Service. Controles de acesso ajudam a impor isso.
  • Testabilidade: componentes com interfaces bem definidas (graças aos controles de acesso) são mais fáceis de testar isoladamente.
  • Reutilização: facilita criar componentes reutilizáveis e seguros dentro do seu próprio app.
  • Menos bugs: evita que uma tela altere um estado interno de outra parte do app sem querer, causando comportamentos bizarros.
O código mais robusto, mais fácil de manter e menos propenso a quebrar. Tudo o que a gente quer!

Conclusão

Parabéns! Você desvendou os segredos dos controles de acesso em Swift! Agora você sabe que private, fileprivate, internal, public e open não são só palavras bonitas, mas ferramentas poderosas para:
  • Proteger seu código contra usos indevidos.
  • Organizar melhor seus projetos.
  • Facilitar a colaboração e a manutenção.
Dominar esses fundamentos do Swift é o primeiro passo para criar aplicativos iOS incríveis que impactam o mundo. É a base para construir interfaces fluidas, lógicas de negócio sólidas e experiências que encantam os usuários.
Se você quer acelerar sua jornada, ir do zero à App Store dominando não só Swift em profundidade, mas também Xcode, arquiteturas como MVVM, integração com back-end e todos os segredos para publicar seu app, a Formação iOS com Swift da Rocketseat foi feita sob medida para você! Lá, você terá um guia completo, passo a passo, para se tornar um desenvolvedor iOS de destaque no mercado.
Nunca pare de estudar e codar! O universo da programação é vasto e cheio de possibilidades. Continue explorando, construindo e, claro, controlando o acesso ao seu código como um mestre!
 
Artigos_

Explore conteúdos relacionados

Descubra mais artigos que complementam seu aprendizado e expandem seu conhecimento.

Aprenda programação do zero e DE GRAÇA

No Discover você vai descomplicar a programação, aprender a criar seu primeiro site com a mão na massa e iniciar sua transição de carreira.

COMECE A ESTUDAR AGORA