dezembro 21, 2025
12 min de leitura

Perguntas de entrevista para desenvolvedor iOS júnior: Swift, UIKit e SwiftUI

interview
career-advice
job-search
entry-level
Perguntas de entrevista para desenvolvedor iOS júnior: Swift, UIKit e SwiftUI
Milad Bonakdar

Milad Bonakdar

Autor

Pratique perguntas de entrevista iOS júnior sobre Swift, UIKit, SwiftUI, networking, persistência e código assíncrono, com respostas objetivas.


Introdução

Em uma entrevista para desenvolvedor iOS júnior, normalmente querem saber se você explica fundamentos de Swift, cria UI simples com UIKit ou SwiftUI, busca e decodifica dados, escolhe persistência básica e evita erros comuns de memória ou threading. As melhores respostas são curtas, concretas e ligadas a como você construiria um app pequeno.

Use este guia para praticar as perguntas mais prováveis em uma primeira vaga iOS: o que o conceito significa, quando você usaria e qual risco uma pessoa júnior deve observar.


Fundamentos de Swift (6 Perguntas)

1. Qual é a diferença entre var e let em Swift?

Resposta:

  • var: Declara uma variável mutável. Seu valor pode ser alterado após a inicialização.
  • let: Declara uma constante imutável. Uma vez definido, seu valor não pode ser alterado.
  • Melhor Prática: Use let por padrão para segurança e clareza. Use var somente quando souber que o valor será alterado.
let name = "John"  // Não pode ser alterado
var age = 25       // Pode ser alterado
age = 26           // Válido
// name = "Jane"   // Erro: Não pode atribuir ao valor

Raridade: Muito Comum Dificuldade: Fácil


2. Explique Optionals em Swift. O que é Optional Binding?

Resposta: Optionals representam um valor que pode ser nil (ausência de um valor).

  • Declaração: Use ? após o tipo: var name: String?
  • Métodos de Unwrapping:
    • Force Unwrapping: name! (perigoso, causa crash se for nil)
    • Optional Binding: Desembrulha com segurança usando if let ou guard let
    • Nil Coalescing: name ?? "Default"
    • Optional Chaining: user?.address?.city
var email: String? = "[email protected]"

// Optional Binding
if let unwrappedEmail = email {
    print("Email: \(unwrappedEmail)")
} else {
    print("Sem email")
}

// Guard statement
guard let email = email else { return }
print(email)

Raridade: Muito Comum Dificuldade: Fácil


3. Qual é a diferença entre uma class e uma struct em Swift?

Resposta:

  • Class: Tipo de referência (armazenado no heap). Suporta herança. Passado por referência.
  • Struct: Tipo de valor (armazenado na stack). Sem herança. Passado por cópia.
  • Quando usar:
    • Struct: Para modelos de dados simples, quando você deseja semântica de valor (cópias independentes)
    • Class: Quando você precisa de herança, semântica de referência ou deinitializers
struct Point {
    var x: Int
    var y: Int
}

class Person {
    var name: String
    init(name: String) { self.name = name }
}

var p1 = Point(x: 0, y: 0)
var p2 = p1  // Cópia criada
p2.x = 10    // p1.x ainda é 0

var person1 = Person(name: "John")
var person2 = person1  // Mesma referência
person2.name = "Jane"  // person1.name também é "Jane"

Raridade: Muito Comum Dificuldade: Média


4. O que são Closures em Swift?

Resposta: Closures são blocos de funcionalidade autocontidos que podem ser passados e usados ​​em seu código. Eles são semelhantes a lambdas ou funções anônimas em outras linguagens.

  • Sintaxe: { (parâmetros) -> ReturnType in statements }
  • Capturando Valores: Closures podem capturar e armazenar referências a variáveis ​​de seu contexto circundante.
  • Uso Comum: Completion handlers, callbacks, operações de array.
// Closure simples
let greet = { (name: String) -> String in
    return "Olá, \(name)!"
}
print(greet("Alice"))

// Sintaxe de trailing closure
let numbers = [1, 2, 3, 4, 5]
let doubled = numbers.map { $0 * 2 }
print(doubled)  // [2, 4, 6, 8, 10]

Raridade: Muito Comum Dificuldade: Média


5. Explique a diferença entre referências weak e unowned.

Resposta: Ambos são usados ​​para evitar ciclos de retenção (vazamentos de memória) em tipos de referência.

  • weak: Referência opcional que automaticamente se torna nil quando o objeto referenciado é desalocado. Use quando a referência pode sobreviver ao objeto.
  • unowned: Referência não opcional que assume que o objeto sempre existirá. Causa crash se acessado após a desalocação. Use quando tiver certeza de que a referência não sobreviverá ao objeto.
class Person {
    var name: String
    weak var apartment: Apartment?  // weak para evitar ciclo de retenção
    init(name: String) { self.name = name }
}

class Apartment {
    var unit: String
    unowned let tenant: Person  // unowned - apartamento sempre tem inquilino
    init(unit: String, tenant: Person) {
        self.unit = unit
        self.tenant = tenant
    }
}

Raridade: Comum Dificuldade: Média


6. O que são Protocols em Swift?

Resposta: Protocols definem um blueprint de métodos, propriedades e outros requisitos que se adequam a uma determinada tarefa ou funcionalidade. Classes, structs e enums podem adotar protocols.

  • Semelhante a: Interfaces em outras linguagens
  • Protocol Extensions: Podem fornecer implementações padrão
  • Programação Orientada a Protocolos: Paradigma preferido do Swift
protocol Drawable {
    func draw()
}

struct Circle: Drawable {
    var radius: Double
    
    func draw() {
        print("Desenhando círculo com raio \(radius)")
    }
}

struct Rectangle: Drawable {
    var width: Double
    var height: Double
    
    func draw() {
        print("Desenhando retângulo \(width)x\(height)")
    }
}

Raridade: Muito Comum Dificuldade: Fácil


Fundamentos de UIKit (5 Perguntas)

7. Qual é a diferença entre UIView e UIViewController?

Resposta:

  • UIView: Representa uma área retangular na tela. Lida com desenho e tratamento de eventos. Exemplos: UILabel, UIButton, UIImageView.
  • UIViewController: Gerencia uma hierarquia de visualizações. Lida com o ciclo de vida da visualização, interações do usuário e navegação. Contém uma view raiz e gerencia suas subviews.
  • Relacionamento: Um view controller gerencia views. Um view controller pode ter muitas views.

Raridade: Muito Comum Dificuldade: Fácil


8. Explique os métodos do ciclo de vida do View Controller.

Resposta: Os view controllers têm métodos de ciclo de vida específicos chamados em ordem:

  1. viewDidLoad(): Chamado uma vez quando a view é carregada na memória. Configuração que precisa acontecer uma vez.
  2. viewWillAppear(_:): Chamado antes da view aparecer na tela. Pode ser chamado várias vezes.
  3. viewDidAppear(_:): Chamado depois que a view aparece na tela. Inicie animações aqui.
  4. viewWillDisappear(_:): Chamado antes da view desaparecer.
  5. viewDidDisappear(_:): Chamado depois que a view desaparece. Limpe os recursos.
override func viewDidLoad() {
    super.viewDidLoad()
    // Configuração única: configure a UI, carregue os dados
}

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    // Atualize os dados, comece a ouvir as notificações
}

Raridade: Muito Comum Dificuldade: Fácil


9. O que é Auto Layout e como você usa constraints?

Resposta: Auto Layout é um sistema de layout baseado em constraints que permite criar UIs adaptáveis ​​que funcionam em diferentes tamanhos e orientações de tela.

  • Constraints: Definem relacionamentos entre views (largura, altura, espaçamento, alinhamento)
  • Métodos:
    • Interface Builder: Editor visual no Xcode
    • Programático: API NSLayoutConstraint ou NSLayoutAnchor
    • Visual Format Language: Baseado em string (menos comum agora)
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(button)

NSLayoutConstraint.activate([
    button.centerXAnchor.constraint(equalTo: view.centerXAnchor),
    button.centerYAnchor.constraint(equalTo: view.centerYAnchor),
    button.widthAnchor.constraint(equalToConstant: 200),
    button.heightAnchor.constraint(equalToConstant: 50)
])

Raridade: Muito Comum Dificuldade: Média


10. Qual é a diferença entre frame e bounds?

Resposta:

  • frame: A localização e o tamanho da view no sistema de coordenadas de sua superview. Usado para posicionar a view.
  • bounds: A localização e o tamanho da view em seu próprio sistema de coordenadas. A origem é geralmente (0, 0). Usado para desenhar conteúdo dentro da view.
  • Diferença Principal: frame é relativo ao pai, bounds é relativo a si mesmo.
let view = UIView(frame: CGRect(x: 50, y: 100, width: 200, height: 150))
print(view.frame)   // (50, 100, 200, 150) - posição na superview
print(view.bounds)  // (0, 0, 200, 150) - próprio sistema de coordenadas

Raridade: Comum Dificuldade: Média


11. Como você lida com a entrada do usuário de um UITextField?

Resposta: Existem várias abordagens:

  • Target-Action: Adicione target para o evento .editingChanged
  • Delegate: Esteja em conformidade com o protocolo UITextFieldDelegate
  • Combine: Use publishers (abordagem moderna)
class ViewController: UIViewController, UITextFieldDelegate {
    let textField = UITextField()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // Abordagem Delegate
        textField.delegate = self
        
        // Abordagem Target-Action
        textField.addTarget(self, action: #selector(textChanged), for: .editingChanged)
    }
    
    @objc func textChanged(_ textField: UITextField) {
        print("Texto: \(textField.text ?? "")")
    }
    
    // Método Delegate
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        textField.resignFirstResponder()
        return true
    }
}

Raridade: Muito Comum Dificuldade: Fácil


Fundamentos de SwiftUI (4 Perguntas)

12. O que é SwiftUI e como ele difere do UIKit?

Resposta: SwiftUI é o framework declarativo da Apple para criar interfaces. Você descreve a UI como uma função do estado, e o SwiftUI atualiza a view renderizada quando esse estado muda. UIKit é imperativo: você cria e modifica diretamente objetos como UIView, UILabel e UIViewController.

Em uma entrevista júnior, não apresente isso como se SwiftUI substituísse UIKit em todos os casos. Muitos apps em produção misturam os dois. Uma boa resposta: SwiftUI é conciso para telas novas e UI baseada em estado; UIKit continua importante em bases de código existentes, controles customizados e APIs que o SwiftUI ainda não cobre totalmente.

struct CounterView: View {
    @State private var count = 0

    var body: some View {
        VStack {
            Text("Count: \(count)")
            Button("Increment") {
                count += 1
            }
        }
    }
}

Raridade: Muito Comum Dificuldade: Fácil


13. Explique @State, @Binding e @ObservedObject no SwiftUI.

Resposta: Esses wrappers indicam onde o estado vive e quem pode modificá-lo.

  • @State: Estado privado de uma view, geralmente para estado local simples da UI.
  • @Binding: Conexão bidirecional com estado de uma view pai. A view filha pode ler e escrever sem ser dona desse estado.
  • @ObservedObject: Referência a dados observáveis externos. Use quando outro objeto possui os dados e a view deve atualizar quando eles mudam. Em código SwiftUI mais novo, você também pode ver o framework Observation e @Observable, embora muitas entrevistas ainda perguntem sobre esses wrappers.
class UserData: ObservableObject {
    @Published var name = ""
}

struct ParentView: View {
    @State private var isOn = false
    @ObservedObject var userData = UserData()

    var body: some View {
        VStack {
            ChildView(isOn: $isOn)
            Text(userData.name)
        }
    }
}

struct ChildView: View {
    @Binding var isOn: Bool

    var body: some View {
        Toggle("Switch", isOn: $isOn)
    }
}

Raridade: Comum Dificuldade: Média


14. Como você cria uma lista em SwiftUI?

Resposta: Use a view List para exibir coleções de dados roláveis.

struct ContentView: View {
    let fruits = ["Apple", "Banana", "Orange", "Grape"]
    
    var body: some View {
        List(fruits, id: \.self) { fruit in
            Text(fruit)
        }
    }
}

// Com modelo personalizado
struct Task: Identifiable {
    let id = UUID()
    let title: String
}

struct TaskListView: View {
    let tasks = [
        Task(title: "Comprar mantimentos"),
        Task(title: "Passear com o cachorro")
    ]
    
    var body: some View {
        List(tasks) { task in
            Text(task.title)
        }
    }
}

Raridade: Comum Dificuldade: Fácil


15. O que são View Modifiers em SwiftUI?

Resposta: View modifiers são métodos que criam uma nova view com propriedades modificadas. Eles são encadeáveis.

  • Modificadores Comuns: .padding(), .background(), .foregroundColor(), .font(), .frame()
  • Ordem Importa: Os modificadores são aplicados em sequência
Text("Olá, Mundo!")
    .font(.title)
    .foregroundColor(.blue)
    .padding()
    .background(Color.yellow)
    .cornerRadius(10)
    .shadow(radius: 5)

// Modificador personalizado
struct PrimaryButtonStyle: ViewModifier {
    func body(content: Content) -> some View {
        content
            .padding()
            .background(Color.blue)
            .foregroundColor(.white)
            .cornerRadius(8)
    }
}

extension View {
    func primaryButton() -> some View {
        modifier(PrimaryButtonStyle())
    }
}

Raridade: Comum Dificuldade: Fácil


Dados e Networking (5 Perguntas)

16. Como fazer uma requisição de rede no iOS?

Resposta: Use URLSession. Em Swift moderno, uma resposta júnior pode mostrar async/await e mencionar que bases de código antigas ainda usam completion handlers. Uma resposta mais sólida também valida a resposta HTTP, trata erros, decodifica modelos Codable e atualiza a UI no main actor.

struct User: Codable {
    let id: Int
    let name: String
}

func fetchUser(id: Int) async throws -> User {
    let url = URL(string: "https://api.example.com/users/\(id)")!
    let (data, response) = try await URLSession.shared.data(from: url)

    guard let http = response as? HTTPURLResponse, (200...299).contains(http.statusCode) else {
        throw URLError(.badServerResponse)
    }

    return try JSONDecoder().decode(User.self, from: data)
}

Raridade: Muito Comum Dificuldade: Média


17. O que é Codable em Swift?

Resposta: Codable é um alias de tipo para os protocolos Encodable & Decodable. Ele permite a conversão fácil entre tipos Swift e representações externas como JSON.

  • Síntese Automática: Swift gera automaticamente o código de codificação/decodificação se todas as propriedades forem Codable.
  • Chaves Personalizadas: Use o enum CodingKeys para mapear diferentes nomes de propriedades.
struct User: Codable {
    let id: Int
    let name: String
    let email: String
}

// Decodificando JSON
let json = """
{
    "id": 1,
    "name": "John Doe",
    "email": "[email protected]"
}
""".data(using: .utf8)!

let decoder = JSONDecoder()
let user = try decoder.decode(User.self, from: json)

// Codificando para JSON
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let jsonData = try encoder.encode(user)

Raridade: Muito Comum Dificuldade: Fácil


18. Como você persiste dados localmente no iOS?

Resposta: Várias opções para persistência de dados local:

  • UserDefaults: Armazenamento simples de chave-valor para pequenas quantidades de dados (configurações, preferências)
  • File System: Salve arquivos no diretório Documents ou Cache
  • Core Data: O framework da Apple para gerenciamento e persistência de gráficos de objetos
  • SQLite: Banco de dados relacional
  • Keychain: Armazenamento seguro para dados confidenciais (senhas, tokens)
// UserDefaults
UserDefaults.standard.set("John", forKey: "username")
let username = UserDefaults.standard.string(forKey: "username")

// File System
let documentsPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let filePath = documentsPath.appendingPathComponent("data.json")

// Escrever
try data.write(to: filePath)

// Ler
let loadedData = try Data(contentsOf: filePath)

Raridade: Muito Comum Dificuldade: Fácil


19. Qual é a diferença entre operações síncronas e assíncronas?

Resposta: Uma operação síncrona bloqueia a thread atual até terminar. Na main thread, isso é perigoso porque o app pode parar de responder. Uma operação assíncrona inicia o trabalho e deixa a thread continuar; o resultado chega depois por async/await, um completion handler, um delegate ou um publisher.

Em entrevistas iOS, conecte a resposta à fluidez da UI: chamadas de rede, trabalho com arquivos e processamento pesado não devem bloquear a main thread, e mudanças de UI devem rodar no main actor.

func loadProfile() async {
    do {
        let user = try await fetchUser(id: 42)
        await MainActor.run {
            self.nameLabel.text = user.name
        }
    } catch {
        await MainActor.run {
            self.errorLabel.text = "Could not load profile"
        }
    }
}

Raridade: Comum Dificuldade: Fácil


20. O que é Grand Central Dispatch (GCD)?

Resposta: GCD é a API de baixo nível da Apple para agendar trabalho em dispatch queues. A main queue é usada para UI, e as global queues podem executar trabalho em segundo plano com diferentes prioridades de quality of service.

Em Swift moderno, muitas vezes você usará Swift concurrency (async/await, Task e actors) primeiro porque ela torna o código assíncrono mais legível. GCD continua importante porque muitos projetos UIKit, bases antigas e APIs com callbacks usam DispatchQueue.main.async ou filas em segundo plano.

DispatchQueue.global(qos: .userInitiated).async {
    let image = renderThumbnail()

    DispatchQueue.main.async {
        self.imageView.image = image
    }
}

Raridade: Comum Dificuldade: Média


Newsletter subscription

Dicas de carreira semanais que realmente funcionam

Receba as últimas ideias diretamente na sua caixa de entrada

Crie um Currículo que Te Contrate 60% Mais Rápido

Em minutos, crie um currículo personalizado e compatível com ATS comprovado para conseguir 6 vezes mais entrevistas.

Crie um currículo melhor

Compartilhar esta publicação

Supere a Taxa de Rejeição de 75% do ATS

3 em cada 4 currículos nunca chegam a um olho humano. Nossa otimização de palavras-chave aumenta sua taxa de aprovação em até 80%, garantindo que os recrutadores realmente vejam seu potencial.