Swift: Entrada, Saída e Depuração no Swift
Escrever código é apenas metade do trabalho. Esta lição ensina como usar print, asserções e pontos de interrupção para encontrar e corrigir erros no seu código.
1. O Que Você Vai Aprender
- Parâmetros avançados do print: separator e terminator
- A diferença entre debugPrint e dump e quando usar cada um
- Asserções assert e verificações precondition
- Formatação de string e saída alinhada
- Visualização ao vivo no Playground e noções básicas de depuração com pontos de interrupção
2. Uma História Real: Um Iniciante Autodidata
(1) O Problema: Código Logicamente Correto, Resultados Errados
Mike é um iniciante autodidata em Swift trabalhando em um programa para calcular a nota média:
let scores = [85, 92, 78, 90, 88]
var total = 0
for i in 0...5 {
total += scores[i]
}
let average = total / 5
print("Average: \(average)")
O programa travou imediatamente. Mike não conseguia descobrir o porquê. Ele passou 30 minutos pesquisando online, tentando várias correções. Cada modificação exigia reexecutar, mas ele ainda não conseguia identificar o problema.
(2) A Solução: Depuração Baseada em Print
Um amigo sugeriu usar print para exibir variáveis intermediárias e localizar o problema:
let scores = [85, 92, 78, 90, 88]
print("Array length: \(scores.count)") // Output: 5
var total = 0
for i in 0..<scores.count { // Use count instead of hardcoding
print("Processing index \(i): value = \(scores[i])")
total += scores[i]
}
print("Total: \(total)")
let average = total / 5
print("Average: \(average)")
Saída:
TEXT 📖 Somente leituraArray length: 5 Processing index 0: value = 85 Processing index 1: value = 92 Processing index 2: value = 78 Processing index 3: value = 90 Processing index 4: value = 88 Total: 433 Average: 86
Ao exibir variáveis intermediárias, Mike descobriu imediatamente que os índices do array são 0-4, mas ele havia usado 0...5 (que inclui o índice 5).
(3) O Resultado: Eficiência de Depuração Após Dominar o Print
| Dimensão | Antes (Adivinhando) | Depois (Depuração com Print) |
|---|---|---|
| Tempo para localizar o problema | 30+ minutos | 1-2 minutos |
| Precisão da correção | 50% (frequentemente errada) | 95% |
| Compreensão do código | "Não faço ideia do porquê travou" | "Sei o estado em cada etapa" |
| Confiança para resolver novos problemas | 3/10 | 8/10 |
3. Uso Avançado do Print
print é a ferramenta de depuração mais usada do Swift, mas seus parâmetros fazem mais do que apenas exibir um valor.
graph TB
A[Função print] --> B["items: Valores a exibir"]
A --> C["separator: Delimitador"]
A --> D["terminator: Caractere final"]
A --> E["to: Destino da saída"]
B --> F[Múltiplos valores separados por vírgulas]
C --> G["Padrão: espaço"]
C --> H["Personalizado: | ou , etc."]
D --> I["Padrão: nova linha"]
D --> J["Personalizado: string vazia"]
| Parâmetro | Tipo | Padrão | Finalidade |
|---|---|---|---|
items |
Any... |
Obrigatório | Conteúdo a ser exibido |
separator |
String |
" " |
Separador entre múltiplos itens |
terminator |
String |
"\n" |
Caractere de nova linha ao final |
to |
TextOutputStream |
nil |
Destino da saída (padrão: console) |
(1) separator e terminator
// Default: space-separated, newline-terminated
print("Hello", "Swift", "World")
// Hello Swift World
// Custom separator
print("Hello", "Swift", "World", separator: ", ")
// Hello, Swift, World
// Custom terminator (no newline)
print("Loading", terminator: "...")
print("Done")
// Loading...Done
// Combined usage
print("A", "B", "C", separator: " | ", terminator: ".\n")
// A | B | C.
(2) debugPrint e dump
debugPrint exibe informações de depuração (com aspas e informações de tipo); dump exibe estruturas detalhadas:
let name = "Alice"
let numbers = [1, 2, 3]
print(name) // Alice
debugPrint(name) // "Alice"
dump(name) // - "Alice"
print(numbers) // [1, 2, 3]
debugPrint(numbers) // [1, 2, 3]
dump(numbers)
// ▿ 3 elements
// - 0 : 1
// - 1 : 2
// - 2 : 3
| Função | Finalidade | Saída de String | Saída de Array |
|---|---|---|---|
print |
Saída normal | Alice | [1, 2, 3] |
debugPrint |
Saída de depuração (mostra info do tipo) | "Alice" | [1, 2, 3] |
dump |
Saída detalhada da estrutura | - "Alice" | Um elemento por linha |
▶ Exemplo: Saída de Log Formatada
// ============================================
// Simulate system log output
// Demonstrates advanced print parameters and debugPrint
// ============================================
let event = "USER_LOGIN"
let user = "Alice"
let statusCode = 200
let duration = 0.045
// 1. Use separator to format the log
print("[\(event)]", user, "Status: \(statusCode)", separator: " | ", terminator: "")
print(" (\(duration)s)")
// [USER_LOGIN] | Alice | Status: 200 (0.045s)
// 2. Output tabular data
print()
print("=== Report ===")
print("Item", "Price", "Qty", separator: " | ")
print("-----", "-----", "---", separator: " | ")
print("Book", "12.99", "3", separator: " | ")
print("Pen", "1.50", "10", separator: " | ")
print("Bag", "49.99", "1", separator: " | ")
// 3. debugPrint for development debugging
let input: String? = "test"
debugPrint("Debug: input = \(input)")
// "Debug: input = Optional(\"test\")"
Saída:
TEXT 📖 Somente leitura[LOGIN_USUARIO] | Alice | Status: 200 (0.045s) === Relatório === Item | Preço | Qtd ----- | ----- | --- Livro | 12.99 | 3 Caneta | 1.50 | 10 Mochila | 49.99 | 1 Depuração: entrada = Optional("teste")
4. Asserções e Precondições
Asserções e precondições são as ferramentas de programação defensiva embutidas do Swift, capturando erros de lógica precocemente durante o desenvolvimento.
graph LR
A[Verificações em Tempo de Execução] --> B[assert]
A --> C[precondition]
B --> D[Apenas modo Debug]
B --> E[Capturar problemas durante o desenvolvimento]
C --> F[Debug + Release]
C --> G[Erros irrecuperáveis]
| Função | Ativa Em | Finalidade | Exemplo |
|---|---|---|---|
assert |
Apenas Debug | Verificações de consistência interna durante o desenvolvimento | assert(idade > 0) |
assertionFailure |
Apenas Debug | Disparo incondicional de asserção | assertionFailure("Não deveria chegar aqui") |
precondition |
Todos os modos | Verificações de pré-condição | precondition(!nome.isEmpty) |
preconditionFailure |
Todos os modos | Encerramento incondicional | preconditionFailure("Erro fatal") |
(1) Asserções de Depuração assert
assert só tem efeito no modo Debug; é removida no modo Release, portanto, não há impacto no desempenho:
func calculateDiscount(price: Double, percent: Double) -> Double {
assert(price > 0, "Price must be greater than 0")
assert(percent >= 0 && percent <= 100, "Discount must be between 0-100")
let discount = price * percent / 100.0
return price - discount
}
let finalPrice = calculateDiscount(price: 100.0, percent: 20)
print(finalPrice) // 80.0
// The following would trigger assertion failures in Debug mode:
// calculateDiscount(price: -10, percent: 20) // ❌ assert fails
// calculateDiscount(price: 100, percent: 150) // ❌ assert fails
(2) Verificações precondition
precondition tem efeito tanto no modo Debug quanto no Release — encerra o programa imediatamente quando uma condição não pode ser satisfeita:
func sendEmail(to address: String, message: String) {
precondition(address.contains("@"), "Invalid email address: \(address)")
precondition(!message.isEmpty, "Message cannot be empty")
print("Sending email to \(address): \(message)")
}
sendEmail(to: "alice@example.com", message: "Hello!")
// Sending email to alice@example.com: Hello!
// The following would trigger precondition failures (all modes):
// sendEmail(to: "invalid", message: "Hi")
▶ Exemplo: Validação de Parâmetros
// ============================================
// User registration parameter checks
// Demonstrates assert and precondition usage
// ============================================
import Foundation
func registerUser(name: String, age: Int, email: String) {
// precondition: Public API contract (active in all modes)
precondition(name.count >= 2, "Username must be at least 2 characters")
precondition(age >= 18, "User must be at least 18 years old")
precondition(email.contains("@"), "Invalid email format")
// assert: Internal logic check (Debug only)
let emailParts = email.split(separator: "@")
assert(emailParts.count == 2, "Email should contain exactly one @ symbol")
let domain = String(emailParts[1])
assert(domain.contains("."), "Email domain is invalid")
// Actual registration logic
print("Registration successful: \(name), age \(age)")
print("Confirmation email sent to: \(email)")
}
// Valid call
registerUser(name: "Alice", age: 28, email: "alice@example.com")
print("---")
// Calls that would trigger precondition failures (commented out to avoid crashing)
// registerUser(name: "A", age: 20, email: "test@test.com")
Saída:
TEXT 📖 Somente leituraRegistration successful: Alice, age 28 Confirmation email sent to: alice@example.com ---
5. Depuração no Playground
O Playgrounds oferece capacidades de depuração mais poderosas que o print, incluindo visualizações ao vivo e pontos de interrupção.
(1) Visualização ao Vivo no Playground
A barra lateral no Playgrounds mostra o resultado de cada linha em tempo real:
// Run in Playground — the sidebar shows each step's result
let name = "Alice" // "Alice"
var score = 0 // 0
score += 85 // 85
score += 92 // 177
let average = score / 2 // 88
graph TB
A[Depuração no Playground] --> B[Painel de Resultados ao Vivo]
A --> C[Depuração com Pontos de Interrupção]
A --> D[Histórico de Valores]
B --> E[Resultados exibidos por linha]
C --> F[Pausar / Avançar / Continuar]
D --> G[Curvas de mudança de valor das variáveis]
| Recurso | Como Usar | Finalidade |
|---|---|---|
| Visualização ao vivo | A edição de código exibe resultados automaticamente à direita | Ver rapidamente o resultado de cada etapa |
| Pontos de interrupção | Clique no número da linha para adicionar um ponto de interrupção | Pausar a execução, rastrear passo a passo |
| Histórico de valores | Passe o mouse sobre uma variável | Ver como uma variável muda ao longo do tempo |
| Visualização de expressão | Selecione o código | Avaliar rapidamente uma expressão selecionada |
(2) Depuração com Pontos de Interrupção
Clique no lado esquerdo de um número de linha para definir um ponto de interrupção. Quando o programa atinge essa linha, ele pausa para que você possa inspecionar todos os valores atuais das variáveis:
func calculateTotal(items: [Double], tax: Double) -> Double {
var subtotal = 0.0
// Set a breakpoint on this line
for item in items {
subtotal += item
}
let taxAmount = subtotal * tax
let total = subtotal + taxAmount
return total
}
let cart = [29.99, 49.99, 15.00]
let final = calculateTotal(items: cart, tax: 0.08)
print("Total: $\(final)")
▶ Exemplo: Depuração no Playground na Prática
// ============================================
// Practice Playground debugging techniques
// Paste this code into a Playground and run it
// ============================================
import Foundation
// 1. Set a breakpoint on the line below to observe variable values
let data: [String: Any] = [
"product": "Swift Book",
"price": 39.99,
"quantity": 3,
"inStock": true
]
// 2. Step through the following code
let productName = data["product"] as? String ?? "Unknown"
let price = data["price"] as? Double ?? 0.0
let quantity = data["quantity"] as? Int ?? 0
let inStock = data["inStock"] as? Bool ?? false
print("Product: \(productName)")
print("Price: $\(price)")
print("Quantity: \(quantity)")
print("In Stock: \(inStock)")
// 3. Observe the conditional check results
if inStock {
let totalCost = price * Double(quantity)
print("Total Cost: $\(totalCost)")
} else {
print("Item is out of stock")
}
// 4. Use dump to inspect complex data
print("\n=== Debug Info ===")
dump(data)
Saída:
TEXT 📖 Somente leituraProduto: Livro Swift Preço: R$39.99 Quantidade: 3 Em Estoque: true Custo Total: R$119.97 === Informações de Depuração === ▿ 4 pares chave/valor ▿ (2 elementos) - chave: "produto" - valor: "Livro Swift" ▿ (2 elementos) - chave: "preco" - valor: 39.99 ▿ (2 elementos) - chave: "quantidade" - valor: 3 ▿ (2 elementos) - chave: "emEstoque" - valor: true
6. Exemplo Completo: Ferramenta de Depuração para Análise de Notas
// ============================================
// Grade analysis tool
// Combines print debugging + assertion checks + formatted output
// ============================================
import Foundation
// 1. Student grade data
let studentName = "Alice"
let scores = [85.0, 92.0, 78.0, 90.0, 88.0]
// 2. Debug assertions to validate data
assert(scores.count > 0, "Score list cannot be empty")
for score in scores {
assert(score >= 0 && score <= 100, "Score must be between 0-100: \(score)")
}
// 3. Calculate statistics
var total = 0.0
var highest = scores[0]
var lowest = scores[0]
// Set a breakpoint here to observe the loop process
for (index, score) in scores.enumerated() {
print("[DEBUG] Index \(index): \(score)")
total += score
if score > highest { highest = score }
if score < lowest { lowest = score }
}
let average = total / Double(scores.count)
// 4. Formatted output
print(String(repeating: "=", count: 35))
print("Student: \(studentName)")
print(String(repeating: "-", count: 35))
print("Subject", "Score", "Grade", separator: " | ")
print(String(repeating: "-", count: 35))
for (index, score) in scores.enumerated() {
let grade = score >= 90 ? "A" : score >= 80 ? "B" : score >= 70 ? "C" : "D"
print("Subject \(index + 1)", score, grade, separator: " | ")
}
print(String(repeating: "-", count: 35))
print("Total: \(total)")
print("Average: \(String(format: "%.1f", average))")
print("Highest: \(highest)")
print("Lowest: \(lowest)")
print(String(repeating: "=", count: 35))
// 5. Precondition to ensure reasonable results
precondition(average >= 0 && average <= 100, "Average is outside expected range")
precondition(highest >= lowest, "Highest score should not be lower than lowest")
Saída:
TEXT 📖 Somente leitura[DEBUG] Index 0: 85.0 [DEBUG] Index 1: 92.0 [DEBUG] Index 2: 78.0 [DEBUG] Index 3: 90.0 [DEBUG] Index 4: 88.0 =================================== Student: Alice ----------------------------------- Subject | Score | Grade ----------------------------------- Subject 1 | 85.0 | B Subject 2 | 92.0 | A Subject 3 | 78.0 | C Subject 4 | 90.0 | A Subject 5 | 88.0 | B ----------------------------------- Total: 433.0 Average: 86.6 Highest: 92.0 Lowest: 78.0 ===================================
❓ Perguntas Frequentes
P: Qual é a diferença real entre print e debugPrint? R:
Alice), enquantodebugPrintexibe em formato orientado à depuração (ex.:"Alice"com aspas). Tipos personalizados podem implementar os protocolosCustomStringConvertibleeCustomDebugStringConvertiblepara controlar ambas as saídas. P: O assert é removido no modo Release? R: Sim.assertsó tem efeito no modo Debug (-Onone). No modo Release, o compilador ignora a avaliação e execução dos asserts. Portanto, nunca coloque lógica com efeitos colaterais dentro de um assert. P: Quando devo usar precondition em vez de assert? R:preconditiontem efeito em todos os modos e é para erros fatais irrecuperáveis. Exemplos: verificação de limite de array, parâmetro obrigatório sendo nil, um ramo que logicamente nunca deveria ser alcançado. Use precondition para validação de parâmetros de API pública. P: Como usar pontos de interrupção no Playgrounds? R: Clique no lado esquerdo de um número de linha para definir um ponto de interrupção. Quando o programa atinge um ponto de interrupção, ele pausa. Passe o mouse sobre qualquer variável para ver seu valor. Pressione o botão continuar ou F6 para avançar para a próxima linha. P: Por que não consigo ver a saída no console mesmo tendo usado print? R: Verifique se seu Playground está com o modo ".enableResults" ativado. No Xcode, certifique-se de que a Área de Depuração está aberta (Shift+Cmd+Y). No app Swift Playgrounds, a saída aparece no painel inferior.
📖 Resumo
- O
separatordoprintpersonaliza o delimitador entre itens;terminatorcontrola o caractere final debugPrintmostra a saída com informações de tipo;dumpmostra estruturas detalhadasassertverifica a consistência lógica interna no modo Debugpreconditionverifica pré-condições irrecuperáveis em todos os modos- A barra lateral do Playground exibe o resultado de cada linha em tempo real
- A depuração com pontos de interrupção permite avançar pelo código linha por linha e observar as mudanças nas variáveis
📝 Exercícios
- Iniciante: Use
printpara gerar uma tabuada simples (1-3), controlando o formato comseparatoreterminatorpara exibir um estilo tabular. - Intermediário: Escreva uma função
dividir(_ a: Double, por b: Double) -> Double, usandopreconditionpara verificar se o divisor não é 0 eassertpara verificar se o resultado está dentro de um intervalo razoável. Em seguida, useprintcom formatação para exibir o resultado. - Desafio: Simule um programa de saque de caixa eletrônico. Use
assertpara verificar se o valor do saque é múltiplo de 100 epreconditionpara verificar se há saldo suficiente. Useprintcomseparatoreterminatorpara formatar os detalhes da transação, incluindo hora, valor e saldo.