Swift: Extensões e Controle de Acesso do Swift

As extensões permitem adicionar novas funcionalidades a classes, structs, enums ou protocolos existentes. O controle de acesso torna as interfaces do seu código mais claras e o encapsulamento mais rígido. Esta lição combina ambos para ajudar você a escrever código Swift mais elegante.

1. O Que Você Vai Aprender


2. Uma História Real de um Desenvolvedor Back-End

(1) Problema: Não É Possível Adicionar Funcionalidade ao String do Foundation

Ao trabalhar na formatação de datas, Bob percebeu que Date não tinha um método para converter para um formato legível de "há quanto tempo". Ele escreveu uma função utilitária:

SWIFT
func timeAgo(from date: Date) -> String {
    let seconds = Date().timeIntervalSince(date)
    if seconds < 60 { return "\(Int(seconds))s ago" }
    if seconds < 3600 { return "\(Int(seconds / 60))m ago" }
    return "\(Int(seconds / 3600))h ago"
}
// Must pass the date as argument every time
print(timeAgo(from: someDate))

Cada chamada exige passar o argumento e invocar uma função global -- o código não parece natural. Bob queria que Date retornasse esse formato por conta própria -- como um método embutido.

(2) A Solução com extension

SWIFT
extension Date {
    func timeAgo() -> String {
        let seconds = Date().timeIntervalSince(self)
        if seconds < 60 { return "\(Int(seconds))s ago" }
        if seconds < 3600 { return "\(Int(seconds / 60))m ago" }
        return "\(Int(seconds / 3600))h ago"
    }
}
// Looks like a built-in Date method
print(someDate.timeAgo())

Extensões dão a Date um novo método -- sem herança, sem modificar o código fonte.

(3) Benefícios: Sintaxe Natural + Alta Coesão

Dimensão Função Global Extension
Estilo de chamada tempoAtras(de: data) data.tempoAtras()
Organização do código Espalhado por toda parte Agrupado com o tipo
Descoberta Deve importar manualmente o módulo utilitário Autocompletar do Xcode
Manutenibilidade Poluição do namespace global Namespace específico do tipo

3. Extensões

(1) Sintaxe de Extension

As extensões podem adicionar o seguinte a tipos existentes:

Pode Adicionar Exemplo Limitação
Propriedades computadas var area: Double { ... } Não pode adicionar propriedades armazenadas
Métodos de instância/tipo func invertido() -> String Permitido
Inicializadores init?(json: [String: Any]) Não pode adicionar deinit
Subscritos subscript(idx: Int) -> T Permitido
Tipos aninhados enum Status { ... } Permitido
Conformidade com protocolo extension Tipo: Protocolo Permitido
100%
graph TB
    A["extension AlgumTipo {"] --> B["Propriedades Computadas"]
    A --> C["Métodos"]
    A --> D["Inicializadores"]
    A --> E["Subscritos"]
    A --> F["Tipos Aninhados"]
    A --> G["Conformidade com Protocolo"]
    A --> H["}"]

▶ Exemplo: Adicionando Extensões a String

SWIFT
// ============================================
// Adding practical extensions to String
// ============================================
extension String {
    // Computed property -- check if this is a valid email
    var isValidEmail: Bool {
        return contains("@") && contains(".")
    }
    // Method -- truncate to first N characters
    func prefix(_ maxLength: Int) -> String {
        guard count > maxLength else { return self }
        return String(self.prefix(maxLength)) + "..."
    }
    // Method -- reverse the string
    func reversed() -> String {
        return String(self.reversed())
    }
}
let email = "alice@example.com"
print("\(email) is valid: \(email.isValidEmail)")
let longText = "This is a very long string that needs truncation"
print(longText.prefix(20))
print("Hello".reversed())

Saída:

TEXT 📖 Somente leitura
alice@example.com is valid: true
This is a very lon...
olleH

Nota: Extensões não podem sobrescrever métodos existentes. Se uma extensão definir um método com o mesmo nome de um existente, o compilador reportará um erro.

(2) Conformidade com Protocolos via Extensões

Separe a conformidade com protocolo da definição do tipo implementando-a em uma extensão:

SWIFT
// ============================================
// Conforming to protocols via extensions
// ============================================
// 1. Define the base type
struct User {
    let id: Int
    let name: String
    let email: String
}
// 2. Conform to Hashable via extension (for Set and Dictionary keys)
extension User: Hashable {
    func hash(into hasher: inout Hasher) {
        hasher.combine(id)
    }
}
// 3. Conform to Equatable via extension
extension User: Equatable {
    static func == (lhs: User, rhs: User) -> Bool {
        return lhs.id == rhs.id
    }
}
// 4. Usage
let users: Set<User> = [
    User(id: 1, name: "Alice", email: "alice@example.com"),
    User(id: 1, name: "Alice", email: "alice@example.com"),  // Duplicate -- deduplicated
    User(id: 2, name: "Bob", email: "bob@example.com")
]
print("Users count: \(users.count)")  // 2

Saída:

TEXT 📖 Somente leitura
Users count: 2

Dica: Colocar a conformidade com protocolo em extensões é para organização de código -- a definição do tipo contém apenas os dados principais, e os comportamentos são adicionados via extensões. Este é um padrão comum na biblioteca padrão do Swift.


4. Controle de Acesso

(1) Cinco Níveis de Acesso

O Swift fornece cinco níveis de controle de acesso, do mais aberto ao mais fechado:

100%
graph TB
    A[Pirâmide de Controle de Acesso] --> B["open - Mais aberto, qualquer um pode acessar/herdar/sobrescrever"]
    A --> C["public - Qualquer um pode acessar, mas não pode herdar/sobrescrever"]
    A --> D["internal - Dentro do módulo atual (nível padrão)"]
    A --> E["fileprivate - Dentro do arquivo atual"]
    A --> F["private - Dentro do escopo da declaração envolvente"]
Nível Palavra-chave Escopo de Acesso Usável Em
Mais aberto open Qualquer módulo, pode herdar e sobrescrever Classes, métodos de classe
public Qualquer módulo, mas não pode herdar/sobrescrever Classes, métodos, propriedades
Padrão internal Módulo atual (App ou Framework) Todos os tipos
fileprivate Arquivo atual Tipos, métodos, propriedades
Mais fechado private Escopo de declaração atual (chaves) Propriedades, métodos

▶ Exemplo: Controle de Acesso em Ação

SWIFT
// ============================================
// Using the five access levels
// ============================================
// Public class available to all modules
public class BankAccount {
    // Publicly readable, privately writable
    public private(set) var balance: Double
    // Private field -- only accessible within this class
    private var accountNumber: String
    private var transactionHistory: [String] = []
    // Accessible within this file -- for logging
    fileprivate var lastTransactionDate: Date?
    // Accessible within this module -- for internal auditing
    internal var branchCode: String
    public init(accountNumber: String, balance: Double, branchCode: String) {
        self.accountNumber = accountNumber
        self.balance = balance
        self.branchCode = branchCode
    }
    public func deposit(amount: Double) {
        balance += amount
        transactionHistory.append("Deposit: \(amount)")
        lastTransactionDate = Date()
    }
    // Private helper method
    private func log(_ message: String) {
        print("[Private] \(message)")
    }
}
let account = BankAccount(accountNumber: "123-456", balance: 1000, branchCode: "BR-001")
print("Balance: \(account.balance)")    // public getter is readable
// account.balance = 2000               // Compile error! public private(set) prevents external writes
// print(account.accountNumber)         // Compile error! private property
// print(account.lastTransactionDate)   // Compile error! fileprivate, inaccessible from another file
print("Branch: \(account.branchCode)")  // internal, accessible within the same module

Saída:

TEXT 📖 Somente leitura
Balance: 1000.0
Branch: BR-001

(2) Princípios de Controle de Acesso

Princípio Explicação
Privilégio mínimo Padrão private, afrouxe apenas quando necessário
Interface estável APIs públicas são contratos uma vez publicadas — modifique com cuidado
Limites do módulo public para um framework define o limite do módulo; internal é a implementação interna
getter/setter Padrão public private(set): publicamente legível, privadamente gravável

5. Extensões e Controle de Acesso

(1) Compartilhamento private no Mesmo Arquivo

Membros private são acessíveis dentro de extensões no mesmo arquivo:

SWIFT
// ============================================
// Visibility of private in extensions
// ============================================
struct UserAccount {
    private var token: String = "secret-token"
    let name: String
    func getToken() -> String {
        return token
    }
}
// Extensions in the same file can access private members
extension UserAccount {
    func resetToken() {
        token = "new-token"  // Accessible! Same file
    }
}
var account = UserAccount(name: "Alice")
print(account.getToken())
account.resetToken()
print(account.getToken())

Saída:

TEXT 📖 Somente leitura
secret-token
new-token

▶ Exemplo: Padrão de Organização com Extensões

SWIFT
// Core data definition
struct Product {
    let id: Int
    let name: String
    let price: Double
}
// MARK: - Equatable Conformance
extension Product: Equatable {
    static func == (lhs: Product, rhs: Product) -> Bool {
        return lhs.id == rhs.id
    }
}
// MARK: - Codable Conformance
extension Product: Codable { }
// MARK: - Utility Methods
extension Product {
    func formattedPrice() -> String {
        return "$\(String(format: "%.2f", price))"
    }
    var priceWithTax: Double {
        return price * 1.08
    }
}

▶ Example: Adding Nested Types and Convenience Initializers via Extensions

Extensions can add not only methods and computed properties, but also nested types and initializers:

SWIFT
// ============================================
// Adding nested types and convenience initializers via extensions
// ============================================
// Base type
struct Book {
    let title: String
    let author: String
    let year: Int
}
// Extension: add nested types and convenience initializers
extension Book {
    // Nested enum -- genres
    enum Genre: String {
        case fiction = "Fiction"
        case nonfiction = "Non-fiction"
        case science = "Science"
        case history = "History"
    }
    // Nested struct -- reading stats
    struct ReadingStats {
        var pagesRead: Int
        var totalPages: Int
        var progress: Double {
            totalPages > 0 ? Double(pagesRead) / Double(totalPages) * 100 : 0
        }
    }
    // Convenience initializer -- create from a JSON dictionary
    init?(from dict: [String: Any]) {
        guard let title = dict["title"] as? String,
              let author = dict["author"] as? String,
              let year = dict["year"] as? Int else {
            return nil
        }
        self.init(title: title, author: author, year: year)
    }
}
// Usage
let book1 = Book(title: "Swift Programming", author: "Apple", year: 2024)
let genre = Book.Genre.science
print("\(book1.title) - Genre: \(genre.rawValue)")
let stats = Book.ReadingStats(pagesRead: 120, totalPages: 350)
print("Reading progress: \(String(format: "%.1f", stats.progress))%")
// Create from dictionary
let dict = ["title": "Advanced Swift", "author": "Alice", "year": 2025] as [String: Any]
if let book2 = Book(from: dict) {
    print("Created from dictionary: \(book2.title) (\(book2.author), \(book2.year))")
}

Output:

TEXT 📖 Somente leitura
Swift Programming - Genre: Science
Reading progress: 34.3%
Created from dictionary: Advanced Swift (Alice, 2025)

6. Exemplo Completo: Sistema de Gerenciamento de Usuários (Extension + Controle de Acesso)

SWIFT
// ============================================
// Full example: User management system
// Features: comprehensive use of extension + access control
// ============================================
import Foundation
// 1. Core user type
public class User {
    public let id: Int
    public let name: String
    public let email: String
    // Publicly readable, internally writable
    public private(set) var score: Int = 0
    public private(set) var isActive: Bool = true
    // Accessible within the module
    internal var lastLogin: Date?
    // Private
    private var loginHistory: [Date] = []
    public init(id: Int, name: String, email: String) {
        self.id = id
        self.name = name
        self.email = email
    }
    public func login() {
        let now = Date()
        lastLogin = now
        loginHistory.append(now)
        isActive = true
    }
    public func addScore(_ points: Int) {
        // Internal validation
        guard points > 0 else { return }
        score += points
    }
}
// MARK: - Description Extension
extension User {
    public func description() -> String {
        return "User(\(id): \(name), score: \(score), active: \(isActive))"
    }
    // Determine based on last login time
    public var isOnline: Bool {
        guard let last = lastLogin else { return false }
        return Date().timeIntervalSince(last) < 300
    }
}
// MARK: - Activity Log Extension
extension User {
    // fileprivate -- accessible only within this file
    fileprivate func generateActivityReport() -> String {
        return """
        User Report - \(name)
        =================
        Score: \(score)
        Active: \(isActive)
        Last Login: \(lastLogin?.description ?? "Never")
        """
    }
    public func printReport() {
        print(generateActivityReport())
    }
}
// 3. Usage example
let user = User(id: 1, name: "Alice", email: "alice@example.com")
user.login()
user.addScore(50)
user.addScore(30)
print(user.description())
print("Online: \(user.isOnline)")
user.printReport()
> Saída:
> ```text
> User(1: Alice, score: 80, active: true)
> Online: true
> User Report - Alice
> =================
> Score: 80
> Active: true
> Last Login: 2026-07-30 12:00:00 +0000
> ```
---
## ❓ Perguntas Frequentes
> **P: Qual é a diferença entre extension e herança?** **R: Extensões adicionam funcionalidade a *tipos existentes* (incluindo tipos do sistema como String e Date) sem criar um novo tipo. A herança só funciona com suas próprias classes e é usada para alterar o comportamento. Extensões não podem adicionar propriedades armazenadas; a herança pode.**
> **P: Posso adicionar propriedades armazenadas em uma extension?** **R: Não. Extensões só podem adicionar propriedades computadas, métodos, inicializadores, subscritos e tipos aninhados. Propriedades armazenadas devem estar na definição original do tipo. Esta é uma decisão de design da linguagem Swift -- para evitar quebrar o layout de memória dos tipos existentes.**
> **P: Qual é o nível de acesso padrão?** **R: `internal`. Se você não escrever nenhum modificador de controle de acesso, o padrão é `internal` -- acessível dentro do módulo atual. Para desenvolvimento de App (módulo único), `internal` equivale a `public`. Para desenvolvimento de Framework, você deve escrever explicitamente `public` ou `open` para expor a módulos externos.**
> **P: Qual é a diferença entre open e public?** **R: `public` permite acesso de módulo externo, mas não permite herança ou sobrescrita. `open` permite que módulos externos herdem classes e sobrescrevam métodos. Apenas classes e métodos de classe podem usar `open`. Princípio de segurança: não use `open` a menos que necessário.**
> **P: Quando devo dividir o código em extensões?** **R: Quando a definição de um tipo ficar grande (mais de 200 linhas), agrupe por funcionalidade em múltiplas extensões (cada uma com um comentário MARK). Padrão comum: definição principal, conformidade com protocolo, métodos utilitários, inicializadores de conveniência. Uma preocupação por extensão.**
---
## 📖 Resumo
- `extension` pode adicionar propriedades computadas, métodos, inicializadores, subscritos e tipos aninhados a tipos existentes
- Extensões não podem adicionar propriedades armazenadas ou sobrescrever métodos existentes
- Estar em conformidade com protocolos via extensões é o padrão de organização de código padrão do Swift
- Cinco níveis de acesso: open > public > internal > fileprivate > private
- O padrão `public private(set)` permite "publicamente legível, privadamente gravável"
- Extensões no mesmo arquivo podem acessar membros `private`
---
## 📝 Exercícios
1. **Básico:** Adicione uma `extension` a `Int` com uma propriedade computada `aoQuadrado` e uma propriedade computada `ehPar`. Teste `4.aoQuadrado` (exibe 16) e `3.ehPar` (exibe false).
2. **Intermediário:** Adicione uma `extension` a `Date` com um método `dataFormatada` (retorna uma string formatada "yyyy-MM-dd") e uma propriedade computada `ehHoje`. Use `extension` para conformidade com `Comparable` se ainda não estiver em conformidade.
3. **Desafio:** Crie uma classe `ArmazenamentoSeguro` que usa controle de acesso para gerenciar dados sensíveis. Requisitos: métodos `public` `salvar(chave:valor:)` e `ler(chave:)`; método `internal` de limpeza de cache; lógica `private` de criptografia/descriptografia; método `fileprivate` de logging. Use extensões para agrupar conformidade com protocolo e métodos utilitários em diferentes blocos MARK.
Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%