As 25 perguntas de entrevista Swift mais importantes para desenvolvedores iOS
Preparação para entrevistas técnicas iOS com as 25 perguntas Swift mais frequentes: optionals, closures, ARC, protocolos, async/await e padrões avançados.

As entrevistas técnicas para iOS avaliam profundamente os fundamentos e conceitos avançados de Swift. Estas 25 perguntas cobrem os temas mais solicitados pelos responsáveis pela contratação, desde os conceitos básicos da linguagem até os padrões modernos de concorrência.
Cada pergunta inclui uma resposta detalhada com exemplos de código. As perguntas estão organizadas por nível de dificuldade crescente, desde fundamentos até conceitos avançados.
Fundamentos de Swift
1. Qual é a diferença entre let e var?
let declara uma constante cujo valor não pode ser alterado após a inicialização, enquanto var declara uma variável mutável. Para tipos por referência (classes), let impede a reatribuição da referência, mas não a modificação do objeto em si.
// let = constant, immutable value
let maximumAttempts = 3
// maximumAttempts = 5 // ❌ Compile error
// var = variable, mutable value
var currentAttempt = 0
currentAttempt += 1 // ✅ OK
// Careful with reference types
class User {
var name: String
init(name: String) { self.name = name }
}
let user = User(name: "Alice")
user.name = "Bob" // ✅ OK - modifying object, not reference
// user = User(name: "Charlie") // ❌ Error - reassignment forbiddenBoa prática: recomenda-se usar let por padrão e só mudar para var quando a mutação for necessária. Isso torna o código mais previsível e fácil de entender.
2. O que são optionals em Swift?
Optionals representam a possível ausência de um valor. Um optional pode conter um valor do tipo especificado ou nil. Swift utiliza optionals para garantir segurança em tempo de compilação, forçando o tratamento explícito de casos onde um valor pode não existir.
// Declaring an optional with ?
var username: String? = nil // Can hold a String or nil
// Optional binding with if let (safe unwrapping)
if let name = username {
print("Hello, \(name)") // Only executes if username != nil
} else {
print("Anonymous user")
}
// Guard let for early return
func greet(user: String?) {
guard let name = user else {
print("No user provided")
return // Early exit if nil
}
print("Hello, \(name)") // name is guaranteed non-nil here
}
// Nil-coalescing operator (??) for default value
let displayName = username ?? "Anonymous"
// Force unwrapping (!) - DANGEROUS, avoid this
// let forced = username! // Crashes if nil3. Qual é a diferença entre struct e class?
struct são tipos por valor (copiados na atribuição) enquanto class são tipos por referência (compartilham a mesma instância). Essa distinção fundamental impacta o desempenho, o gerenciamento de memória e o comportamento do código.
// Struct = value type (copy)
struct Point {
var x: Int
var y: Int
}
var p1 = Point(x: 10, y: 20)
var p2 = p1 // Independent copy
p2.x = 100 // Only modifies p2
print(p1.x) // 10 - p1 unchanged
// Class = reference type (shared)
class Rectangle {
var width: Int
var height: Int
init(width: Int, height: Int) {
self.width = width
self.height = height
}
}
let r1 = Rectangle(width: 10, height: 20)
let r2 = r1 // Same shared instance
r2.width = 100 // Modifies the shared instance
print(r1.width) // 100 - r1 also modified!Quando usar cada um:
- Struct: dados simples, valores imutáveis, sem necessidade de herança
- Class: quando a identidade importa, herança é necessária ou comportamento compartilhado
4. Como funciona o pattern matching com switch?
O switch do Swift é exaustivo: deve cobrir todos os casos possíveis. Suporta pattern matching sobre tipos, intervalos, tuplas e condições adicionais com where.
enum NetworkError: Error {
case timeout
case serverError(code: Int)
case noConnection
}
func handleError(_ error: NetworkError) {
switch error {
case .timeout:
print("Request timed out")
case .serverError(let code) where code >= 500:
print("Critical server error: \(code)")
case .serverError(let code):
print("Server error: \(code)")
case .noConnection:
print("No connection")
}
// No default needed: all cases covered
}
// Pattern matching on ranges and tuples
let point = (x: 5, y: 10)
switch point {
case (0, 0):
print("Origin")
case (let x, 0):
print("On X axis at \(x)")
case (0...10, 0...10):
print("In the 10x10 square")
default:
print("Elsewhere")
}5. O que são closures e qual é a sua sintaxe?
Closures são blocos de código autônomos que capturam e armazenam referências a variáveis do contexto circundante. São o equivalente a lambdas ou funções anônimas em outras linguagens.
// Full syntax
let add: (Int, Int) -> Int = { (a: Int, b: Int) -> Int in
return a + b
}
// Shorthand syntax (inferred types, implicit return)
let multiply: (Int, Int) -> Int = { $0 * $1 }
// Trailing closure syntax
let numbers = [3, 1, 4, 1, 5]
let sorted = numbers.sorted { $0 > $1 } // [5, 4, 3, 1, 1]
// Closure capturing a variable
func makeCounter() -> () -> Int {
var count = 0 // Captured variable
return {
count += 1 // Closure "closes over" count
return count
}
}
let counter = makeCounter()
print(counter()) // 1
print(counter()) // 2 - count remembered between callsClosures capturam variáveis por referência por padrão. Para capturar por valor, utiliza-se uma lista de captura: { [count] in ... }.
Gerenciamento de memória e ARC
6. Como funciona o ARC (Automatic Reference Counting)?
O ARC gerencia a memória automaticamente contando as referências fortes para cada instância de classe. Quando a contagem chega a zero, a instância é desalocada. Diferentemente da coleta de lixo, o ARC é determinístico e previsível.
class Person {
let name: String
init(name: String) {
self.name = name
print("\(name) is initialized")
}
deinit {
print("\(name) is deallocated")
}
}
// Demonstrating lifecycle
var person1: Person? = Person(name: "Alice") // refCount = 1
var person2 = person1 // refCount = 2
person1 = nil // refCount = 1 (not deallocated)
person2 = nil // refCount = 0 → deinit called
// Output: "Alice is deallocated"7. O que é um ciclo de retenção e como preveni-lo?
Um ciclo de retenção ocorre quando dois objetos mantêm referências fortes entre si, impedindo a desalocação de ambos. As palavras-chave weak e unowned quebram esses ciclos.
class Department {
let name: String
var manager: Employee? // Strong reference
init(name: String) { self.name = name }
deinit { print("Department \(name) deallocated") }
}
class Employee {
let name: String
// weak prevents retain cycle - can become nil
weak var department: Department?
init(name: String) { self.name = name }
deinit { print("Employee \(name) deallocated") }
}
// Without weak: retain cycle → memory leak
// With weak: proper deallocation
var dept: Department? = Department(name: "Engineering")
var emp: Employee? = Employee(name: "Bob")
dept?.manager = emp
emp?.department = dept
dept = nil // ✅ Deallocated thanks to weak
emp = nil // ✅ Deallocated8. Qual é a diferença entre weak e unowned?
Ambos quebram ciclos de retenção, mas com garantias diferentes. weak é opcional e se torna nil se o objeto referenciado for desalocado. unowned assume que o objeto sempre existe e provoca um crash se acessado após a desalocação.
class Customer {
let name: String
var card: CreditCard?
init(name: String) { self.name = name }
}
class CreditCard {
let number: String
// unowned because a card always exists with its customer
unowned let customer: Customer
init(number: String, customer: Customer) {
self.number = number
self.customer = customer
}
}
// Card cannot exist without customer
let customer = Customer(name: "Alice")
customer.card = CreditCard(number: "1234", customer: customer)
// If customer is deallocated, accessing card.customer would crashRegra: recomenda-se usar weak por padrão. unowned deve ser usado apenas quando o tempo de vida do objeto referenciado é garantidamente igual ou maior.
Pronto para mandar bem nas entrevistas de iOS?
Pratique com nossos simuladores interativos, flashcards e testes tecnicos.
Protocolos e genéricos
9. O que são protocolos em Swift?
Protocolos definem um contrato (propriedades e métodos obrigatórios) que os tipos conformantes devem implementar. Permitem polimorfismo e são a base da Programação Orientada a Protocolos (POP) em Swift.
// Protocol definition
protocol Drawable {
var color: String { get set } // Required property (read/write)
func draw() // Required method
}
// Protocol extension with default implementation
extension Drawable {
func draw() {
print("Default drawing in \(color)")
}
}
// Protocol conformance
struct Circle: Drawable {
var color: String
var radius: Double
// draw() inherits default implementation
}
struct Square: Drawable {
var color: String
var side: Double
// Override default implementation
func draw() {
print("Square \(color) with side \(side)")
}
}
// Polymorphic usage
let shapes: [Drawable] = [Circle(color: "red", radius: 5), Square(color: "blue", side: 10)]
shapes.forEach { $0.draw() }10. O que é um tipo associado (associated type)?
Tipos associados permitem que protocolos definam tipos genéricos que serão especificados pelos tipos conformantes. Esse mecanismo é o que torna protocolos como Collection tão flexíveis.
// Protocol with associated type
protocol Container {
associatedtype Item // Type defined by conformant
var items: [Item] { get set }
mutating func add(_ item: Item)
func count() -> Int
}
// Implementation with Item = String
struct StringBox: Container {
typealias Item = String // Optional, Swift can infer
var items: [String] = []
mutating func add(_ item: String) {
items.append(item)
}
func count() -> Int { items.count }
}
// Implementation with Item = Int
struct IntStack: Container {
var items: [Int] = []
mutating func add(_ item: Int) {
items.append(item)
}
func count() -> Int { items.count }
}11. Como funcionam os genéricos?
Genéricos permitem escrever código flexível e reutilizável que funciona com qualquer tipo. Evitam a duplicação de código mantendo a segurança de tipos em tempo de compilação.
// Generic function
func swap<T>(_ a: inout T, _ b: inout T) {
let temp = a
a = b
b = temp
}
// Type constraint with where
func findIndex<T: Equatable>(of item: T, in array: [T]) -> Int? {
for (index, element) in array.enumerated() {
if element == item { return index } // Equatable required for ==
}
return nil
}
// Generic struct
struct Queue<Element> {
private var elements: [Element] = []
mutating func enqueue(_ element: Element) {
elements.append(element)
}
mutating func dequeue() -> Element? {
guard !elements.isEmpty else { return nil }
return elements.removeFirst()
}
}
var intQueue = Queue<Int>()
intQueue.enqueue(1)
intQueue.enqueue(2)
print(intQueue.dequeue()) // Optional(1)12. O que é o protocolo Codable?
Codable (alias para Encodable & Decodable) permite a serialização automática de tipos Swift para e a partir de formatos como JSON. O compilador gera a implementação se todas as propriedades forem Codable.
struct User: Codable {
let id: Int
let name: String
let email: String
let createdAt: Date
// CodingKeys to map different JSON names
enum CodingKeys: String, CodingKey {
case id
case name
case email
case createdAt = "created_at" // snake_case → camelCase
}
}
// JSON decoding
let json = """
{
"id": 1,
"name": "Alice",
"email": "alice@example.com",
"created_at": "2026-01-15T10:30:00Z"
}
""".data(using: .utf8)!
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
do {
let user = try decoder.decode(User.self, from: json)
print(user.name) // "Alice"
} catch {
print("Decoding error: \(error)")
}
// Encoding to JSON
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let data = try encoder.encode(user)Concorrência e async/await
13. Como funciona async/await em Swift?
async/await simplifica o código assíncrono permitindo escrever operações não bloqueantes de forma sequencial. Uma função async pode ser suspensa sem bloquear a thread, permitindo que outras tarefas sejam executadas.
// Asynchronous function
func fetchUser(id: Int) async throws -> User {
let url = URL(string: "https://api.example.com/users/\(id)")!
// await suspends execution until response
let (data, response) = try await URLSession.shared.data(from: url)
guard let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 200 else {
throw NetworkError.invalidResponse
}
return try JSONDecoder().decode(User.self, from: data)
}
// Calling from async context
func loadUserProfile() async {
do {
let user = try await fetchUser(id: 42)
print("User: \(user.name)")
} catch {
print("Error: \(error)")
}
}
// Calling from synchronous context with Task
func buttonTapped() {
Task {
await loadUserProfile()
}
}14. O que é um Actor?
Actors são tipos por referência que protegem seu estado interno contra acesso concorrente. Garantem que apenas uma tarefa por vez possa acessar suas propriedades mutáveis, eliminando condições de corrida (data races).
// Actor protects its state automatically
actor BankAccount {
private var balance: Double = 0
func deposit(_ amount: Double) {
balance += amount // Automatic thread-safe access
}
func withdraw(_ amount: Double) -> Bool {
guard balance >= amount else { return false }
balance -= amount
return true
}
func getBalance() -> Double {
return balance
}
}
// Usage - await required to access actor
let account = BankAccount()
Task {
await account.deposit(100)
let success = await account.withdraw(30)
let balance = await account.getBalance()
print("Balance: \(balance)") // 70
}15. O que são Task e TaskGroup?
Task cria uma unidade de trabalho assíncrono. TaskGroup permite executar múltiplas tarefas em paralelo e coletar seus resultados.
// Simple Task
let task = Task {
return await fetchUser(id: 1)
}
let user = try await task.value
// TaskGroup for parallelization
func fetchMultipleUsers(ids: [Int]) async throws -> [User] {
try await withThrowingTaskGroup(of: User.self) { group in
// Launch all requests in parallel
for id in ids {
group.addTask {
try await fetchUser(id: id)
}
}
// Collect results as they complete
var users: [User] = []
for try await user in group {
users.append(user)
}
return users
}
}
// All 3 requests run in parallel
let users = try await fetchMultipleUsers(ids: [1, 2, 3])16. Como funciona o @MainActor?
@MainActor garante que o código seja executado na thread principal. Isso é essencial para atualizações de UI, que devem sempre ser executadas na thread principal.
// UI class annotated with @MainActor
@MainActor
class UserViewModel: ObservableObject {
@Published var user: User?
@Published var isLoading = false
@Published var error: String?
func loadUser() async {
isLoading = true // ✅ On main thread automatically
do {
// Network operation on background thread
user = try await fetchUser(id: 42)
} catch {
self.error = error.localizedDescription
}
isLoading = false // ✅ Automatic return to main thread
}
}
// Or for a specific function
func updateUI() async {
await MainActor.run {
// This block runs on main thread
label.text = "Updated"
}
}Padrões e arquitetura
17. O que é o padrão Delegate?
O padrão Delegate permite que um objeto delegue certas responsabilidades a outro objeto. É onipresente no UIKit (UITableViewDelegate, UITextFieldDelegate, etc.).
// 1. Define the delegate protocol
protocol DownloadManagerDelegate: AnyObject {
func downloadDidStart()
func downloadDidProgress(_ progress: Double)
func downloadDidComplete(data: Data)
func downloadDidFail(error: Error)
}
// 2. Class that uses the delegate
class DownloadManager {
// weak to avoid retain cycles
weak var delegate: DownloadManagerDelegate?
func startDownload(url: URL) {
delegate?.downloadDidStart()
// Simulated download
Task {
for progress in stride(from: 0.0, to: 1.0, by: 0.1) {
try await Task.sleep(nanoseconds: 100_000_000)
delegate?.downloadDidProgress(progress)
}
delegate?.downloadDidComplete(data: Data())
}
}
}
// 3. Class that implements the delegate
class ViewController: UIViewController, DownloadManagerDelegate {
let manager = DownloadManager()
override func viewDidLoad() {
super.viewDidLoad()
manager.delegate = self // Register as delegate
}
func downloadDidStart() { print("Started") }
func downloadDidProgress(_ progress: Double) { print("\(progress * 100)%") }
func downloadDidComplete(data: Data) { print("Complete") }
func downloadDidFail(error: Error) { print("Error: \(error)") }
}18. O que é o padrão MVVM?
MVVM (Model-View-ViewModel) separa a lógica de apresentação da view. O ViewModel expõe dados observáveis que a View exibe, sem conhecer os detalhes da View.
// Model
struct Article: Identifiable {
let id: UUID
let title: String
let content: String
let publishedAt: Date
}
// ViewModel
@MainActor
class ArticleListViewModel: ObservableObject {
@Published private(set) var articles: [Article] = []
@Published private(set) var isLoading = false
@Published var errorMessage: String?
private let repository: ArticleRepository
init(repository: ArticleRepository = .shared) {
self.repository = repository
}
func loadArticles() async {
isLoading = true
errorMessage = nil
do {
articles = try await repository.fetchArticles()
} catch {
errorMessage = "Failed to load articles"
}
isLoading = false
}
}
// View (SwiftUI)
struct ArticleListView: View {
@StateObject private var viewModel = ArticleListViewModel()
var body: some View {
Group {
if viewModel.isLoading {
ProgressView()
} else {
List(viewModel.articles) { article in
Text(article.title)
}
}
}
.task { await viewModel.loadArticles() }
}
}Pronto para mandar bem nas entrevistas de iOS?
Pratique com nossos simuladores interativos, flashcards e testes tecnicos.
19. O que é injeção de dependências?
A injeção de dependências consiste em fornecer as dependências de um objeto externamente, em vez de criá-las internamente. Isso melhora a testabilidade e o desacoplamento.
// Protocol for abstraction
protocol UserServiceProtocol {
func fetchUser(id: Int) async throws -> User
}
// Real implementation
class UserService: UserServiceProtocol {
func fetchUser(id: Int) async throws -> User {
// Real API call
let url = URL(string: "https://api.example.com/users/\(id)")!
let (data, _) = try await URLSession.shared.data(from: url)
return try JSONDecoder().decode(User.self, from: data)
}
}
// ViewModel with injection
class ProfileViewModel: ObservableObject {
private let userService: UserServiceProtocol
// Constructor injection
init(userService: UserServiceProtocol = UserService()) {
self.userService = userService
}
func loadProfile(id: Int) async {
// Uses injected service
}
}
// Mock for testing
class MockUserService: UserServiceProtocol {
func fetchUser(id: Int) async throws -> User {
return User(id: id, name: "Test User", email: "test@test.com")
}
}
// In tests
let viewModel = ProfileViewModel(userService: MockUserService())20. Como implementar o padrão Singleton?
Singleton garante que uma classe tenha apenas uma instância acessível globalmente. Em Swift, utiliza-se uma propriedade estática com um inicializador privado.
class NetworkManager {
// Single globally accessible instance
static let shared = NetworkManager()
// Private initializer prevents creating other instances
private init() {
// Initial configuration
}
private let session = URLSession.shared
func request<T: Decodable>(_ url: URL) async throws -> T {
let (data, _) = try await session.data(from: url)
return try JSONDecoder().decode(T.self, from: data)
}
}
// Usage
let user: User = try await NetworkManager.shared.request(url)Singletons criam estado global que dificulta os testes e o desacoplamento. Recomenda-se preferir a injeção de dependências sempre que possível.
Conceitos avançados
21. O que significa @escaping nos closures?
Um closure é @escaping quando pode ser chamado após o retorno da função que o recebe. Isso é comum para callbacks assíncronos e armazenamento de closures.
class DataLoader {
// Storage of completion handlers
private var completionHandlers: [() -> Void] = []
// @escaping because closure is stored and called later
func loadData(completion: @escaping () -> Void) {
completionHandlers.append(completion)
DispatchQueue.global().async {
// Async work...
Thread.sleep(forTimeInterval: 1)
DispatchQueue.main.async {
// Closure called after loadData returns
completion()
}
}
}
// Non-escaping by default: closure called before return
func transform(data: Data, using transformer: (Data) -> String) -> String {
return transformer(data) // Called immediately
}
}
// With @escaping, watch for retain cycles
class ViewController {
var loader = DataLoader()
var data: String?
func load() {
loader.loadData { [weak self] in // [weak self] avoids retain cycle
self?.data = "Loaded"
}
}
}22. O que é @propertyWrapper?
Property wrappers encapsulam a lógica de armazenamento e acesso de uma propriedade. Permitem reutilizar padrões como validação, logging ou persistência.
// Property wrapper for positive values only
@propertyWrapper
struct Positive {
private var value: Int = 0
var wrappedValue: Int {
get { value }
set { value = max(0, newValue) } // Force positive
}
// Projected value accessible via $
var projectedValue: Bool {
value > 0
}
init(wrappedValue: Int) {
self.wrappedValue = wrappedValue
}
}
// Usage
struct Player {
@Positive var score: Int = 0
@Positive var health: Int = 100
}
var player = Player()
player.score = -50 // Becomes 0 (clamped)
print(player.score) // 0
print(player.$score) // false (projectedValue)
player.score = 100
print(player.$score) // true23. O que são result builders?
Result builders permitem construir valores complexos com sintaxe declarativa. Esse é o mecanismo por trás da sintaxe DSL do SwiftUI.
// Result builder definition
@resultBuilder
struct StringBuilder {
static func buildBlock(_ components: String...) -> String {
components.joined(separator: " ")
}
static func buildOptional(_ component: String?) -> String {
component ?? ""
}
static func buildEither(first component: String) -> String {
component
}
static func buildEither(second component: String) -> String {
component
}
}
// Function using the builder
func buildGreeting(@StringBuilder _ content: () -> String) -> String {
content()
}
// Usage with declarative syntax
let greeting = buildGreeting {
"Hello"
"and"
"welcome"
if Bool.random() {
"!"
} else {
"."
}
}
print(greeting) // "Hello and welcome !" or "Hello and welcome ."24. Como funcionam some e os tipos opacos?
some declara um tipo opaco: o tipo exato é conhecido pelo compilador, mas oculto para quem chama. Isso é essencial para protocolos com tipos associados e permite otimizações.
// Without some: error because Collection has associated type
// func makeCollection() -> Collection { ... } // ❌ Error
// With some: exact type is hidden but consistent
func makeArray() -> some Collection {
return [1, 2, 3] // Always returns the same concrete type
}
// Used in SwiftUI for body
struct ContentView: View {
var body: some View { // Exact type inferred but hidden
VStack {
Text("Hello")
Text("World")
}
}
}
// Difference with any (existential)
func processAny(_ collection: any Collection) {
// Can accept different types, runtime overhead
}
func processSome(_ collection: some Collection) {
// Type fixed at compile time, no overhead
}25. O que são macros em Swift?
Macros (Swift 5.9+) geram código em tempo de compilação. Reduzem o código repetitivo mantendo a segurança de tipos e a capacidade de depuração.
// Freestanding macro: generates an expression
let (x, y) = #unwrap(optionalX, optionalY)
// Expands to: guard let x = optionalX, let y = optionalY else { ... }
// Attached macro: modifies a declaration
@Observable // Macro that generates Observable boilerplate
class UserModel {
var name: String = ""
var email: String = ""
}
// Automatically generates @ObservationTracked, ObservationRegistrar, etc.
// Macro for Codable with customization
@Codable
struct Product {
let id: Int
@CodableKey("product_name") let name: String // Renames JSON key
@CodableIgnored var cache: Data? // Excludes from coding
}
// Creating a custom macro
@attached(member, names: named(init))
public macro AutoInit() = #externalMacro(module: "MyMacros", type: "AutoInitMacro")
@AutoInit
struct Point {
let x: Int
let y: Int
// init(x: Int, y: Int) generated automatically
}No Xcode, basta clicar com o botão direito sobre uma macro e selecionar "Expand Macro" para ver o código gerado. Útil para compreender e depurar.
Conclusão
Estas 25 perguntas cobrem os fundamentos que todo desenvolvedor Swift deve dominar para ter sucesso em entrevistas iOS. Desde o gerenciamento de memória com ARC até os padrões modernos de concorrência, cada conceito se integra ao ecossistema Swift.
Lista de verificação
- Dominar optionals e seus diversos métodos de unwrapping
- Compreender a diferença entre tipos por valor e tipos por referência
- Saber identificar e resolver ciclos de retenção com weak/unowned
- Usar async/await e actors para concorrência
- Implementar os padrões Delegate, MVVM e Injeção de Dependências
- Conhecer protocolos, genéricos e Codable
- Entender conceitos avançados: property wrappers, result builders, macros
Recursos adicionais
Para um aprendizado mais aprofundado, a documentação oficial do Swift continua sendo a referência principal. A prática regular com projetos pessoais e exercícios de codificação ajuda a consolidar esses conceitos.
Comece a praticar!
Teste seus conhecimentos com nossos simuladores de entrevista e testes tecnicos.
Tags
Compartilhar
Artigos relacionados

SwiftUI: Construindo Interfaces Modernas para iOS
Guia completo para criar interfaces modernas com SwiftUI: sintaxe declarativa, componentes, animacoes e boas praticas para iOS 18.

SQL para Analistas de Dados: Window Functions, CTEs e Consultas Avancadas
Guia completo de SQL avancado com window functions, CTEs e padroes de consulta analitica. Tecnicas essenciais para entrevistas de analise de dados e projetos reais.

As 25 principais perguntas de entrevista em Data Analytics em 2026
As 25 perguntas mais frequentes em entrevistas de data analytics em 2026: SQL, Python, Power BI, estatística e perguntas comportamentais com respostas detalhadas e exemplos de código.