Apostila completa · do zero ao nível sênior · foco em empregabilidade

Desenvolvimento Android & iOS

Um guia de estudo contínuo: você começa sem saber programar e termina dominando arquitetura, testes, CI/CD e publicação — com módulos dedicados a portfólio, entrevistas e ao que as empresas realmente cobram.

  • Etapa 1 · Fundamentos
  • Etapa 2 · Uma trilha nativa (Android ou iOS)
  • Etapa 3 · Intermediário: dados, rede, arquitetura
  • Etapa 4 · Avançado: testes, DI, performance
  • Etapa 5 · Sênior: CI/CD, segurança, escala
  • Etapa 6 · Portfólio + entrevistas
Como usar esta apostila Não leia de forma linear as duas trilhas ao mesmo tempo. Escolha uma plataforma (módulos 3–6 ou 7–10), vá até o fim dela construindo os projetos propostos, e só então estude a segunda ou o módulo cross-platform. Empresas contratam profundidade, não superficialidade em tudo.
Fundamentos · Módulo 1

O mercado mobile: onde você está entrando

Antes de escrever a primeira linha de código, entenda o jogo. Existem mais de 7 bilhões de smartphones ativos no mundo e praticamente toda empresa média ou grande mantém pelo menos um app. Isso gera demanda constante por três perfis:

  • Desenvolvedor Android nativo — Kotlin + Jetpack Compose. Maior fatia de mercado global (~70% dos aparelhos), muito forte no Brasil, em bancos digitais, e-commerce e fintechs.
  • Desenvolvedor iOS nativo — Swift + SwiftUI. Menos vagas em volume, porém menos concorrência, salários frequentemente mais altos e público de maior poder aquisitivo.
  • Desenvolvedor cross-platform — Flutter, React Native ou Kotlin Multiplatform. Muito procurado por startups e consultorias que precisam entregar nas duas lojas com um time só.

Níveis de carreira e o que se espera de cada um

NívelO que a empresa esperaFoco desta apostila
JúniorConstruir telas e features com supervisão; consumir APIs; usar Git; entender o ciclo de vida do app.Módulos 1–5 ou 1–2 + 7–9
PlenoEntregar features de ponta a ponta; escrever testes; dominar arquitetura (MVVM/Clean); revisar código.Módulos 5–6 / 9–10
SêniorDecidir arquitetura; modularização; CI/CD; performance; segurança; mentorar o time.Módulos 6, 10, 12
Realidade salarial (Brasil, referência aproximada) Júnior mobile: R$ 3.500–6.000 · Pleno: R$ 7.000–12.000 · Sênior: R$ 13.000–22.000+ · Vagas internacionais remotas: US$ 4.000–10.000/mês. Os valores variam muito por região e empresa — use como ordem de grandeza, não como promessa. iOS e vagas com inglês fluente tendem ao topo das faixas.

Nativo ou cross-platform: qual escolher primeiro?

Regra prática usada por quem contrata: quem domina uma plataforma nativa aprende cross-platform em semanas; o contrário é muito mais difícil. Se seu objetivo é empregabilidade sólida, comece por Android nativo (mais vagas e você não precisa de Mac) ou iOS nativo (se já tem um Mac e mira salários maiores). Flutter/React Native entram depois como multiplicadores.

Fundamentos · Módulo 2

Base de programação que todo dev mobile precisa

Se você já programa, passe direto ao módulo 3 ou 7. Se está começando do zero, estes conceitos aparecem todos os dias na vida de um dev mobile:

2.1 Lógica e estruturas essenciais

  • Variáveis e tipos: números, textos (strings), booleanos, listas e mapas (dicionários).
  • Controle de fluxo: if/else, when/switch, laços for e while.
  • Funções: blocos reutilizáveis que recebem parâmetros e devolvem resultados.
  • Orientação a objetos: classes, objetos, herança, interfaces, encapsulamento — a base de todo SDK mobile.
  • Imutabilidade: preferir valores que não mudam (val/let) reduz bugs; é obsessão em código mobile moderno.
  • Null safety: Kotlin e Swift tratam "valor ausente" no sistema de tipos. Entender opcionais/nullables é obrigatório.
  • Assincronismo: apps fazem rede e disco sem travar a interface; você usará coroutines (Kotlin) e async/await (Swift).

2.2 Git e GitHub — inegociáveis para o mercado

Nenhuma empresa contrata dev que não versiona código. O fluxo mínimo:

# iniciar repositório e primeiro commit
git init
git add .
git commit -m "feat: tela de login"

# trabalhar com branches (padrão em equipes)
git checkout -b feature/carrinho
git push -u origin feature/carrinho
# depois: abrir Pull Request, receber review, merge
Dica de mercado Adote Conventional Commits (feat:, fix:, refactor:, test:) desde o primeiro projeto. Recrutadores técnicos olham seu histórico de commits no GitHub.

2.3 Como um app funciona por dentro

  • UI (interface) roda na main thread; se você bloqueá-la, o app trava.
  • Ciclo de vida: o sistema cria, pausa, retoma e destrói telas conforme o usuário navega — seu código precisa sobreviver a isso (rotação de tela é o teste clássico).
  • Rede: quase todo app consome uma API REST que devolve JSON. Aprenda a ler JSON com naturalidade.
  • Persistência: dados locais vivem em bancos (Room/SQLite, SwiftData/Core Data) ou preferências chave-valor.
Trilha Android · Módulo 3

Kotlin do zero

Kotlin é a linguagem oficial do Android desde 2019 e o que as vagas pedem. É concisa, segura contra null e totalmente interoperável com Java (que você ainda verá em códigos legados).

3.1 Sintaxe essencial

// Variáveis: val (imutável, prefira) e var (mutável)
val nome: String = "Ana"
var idade = 28              // tipo inferido: Int

// Null safety: String? aceita null; String não aceita
var apelido: String? = null
println(apelido?.length ?: 0)   // safe call + elvis

// Funções (com valor padrão e expressão única)
fun saudacao(nome: String, formal: Boolean = false) =
    if (formal) "Prezado(a) $nome" else "Oi, $nome!"

// when: o switch turbinado do Kotlin
fun classificar(nota: Int) = when (nota) {
    in 9..10 -> "Excelente"
    in 7..8  -> "Bom"
    else     -> "Estudar mais"
}

3.2 Classes, data classes e objetos

// data class: gera equals, hashCode, toString e copy — usada o tempo todo
data class Usuario(val id: Long, val nome: String, val email: String)

// sealed class: hierarquia fechada, perfeita para estados de tela
sealed interface UiState {
    object Loading : UiState
    data class Success(val usuarios: List<Usuario>) : UiState
    data class Error(val mensagem: String) : UiState
}

3.3 Coleções e lambdas — o dia a dia

val precos = listOf(19.9, 45.0, 120.0, 8.5)

val caros = precos.filter { it > 40 }          // [45.0, 120.0]
val comDesconto = precos.map { it * 0.9 }
val total = precos.sum()
val maisCaro = precos.maxOrNull()

3.4 Coroutines: assincronismo em Kotlin

Coroutines permitem escrever código assíncrono que parece síncrono. É o tópico de entrevista Android mais recorrente.

suspend fun buscarUsuario(id: Long): Usuario =
    withContext(Dispatchers.IO) {          // sai da main thread
        api.getUsuario(id)                 // chamada de rede
    }

// Em um ViewModel:
viewModelScope.launch {
    val usuario = buscarUsuario(42)
    _state.value = UiState.Success(listOf(usuario))
}
Conceitos para dominar suspend, launch vs async/await, Dispatchers (Main, IO, Default), escopos estruturados (viewModelScope), cancelamento, e Flow (fluxos reativos de dados) — especialmente StateFlow.
Projeto do módulo

No Kotlin Playground (play.kotlinlang.org), escreva um mini sistema de carrinho: data class Produto, lista de produtos, funções para adicionar/remover, total com desconto via fold, e estados modelados com sealed class.

Trilha Android · Módulo 4

Primeiro app: Android Studio e Jetpack Compose

4.1 Preparando o ambiente

  1. Instale o Android Studio (developer.android.com/studio) — Windows, Linux ou macOS.
  2. Crie um projeto com o template "Empty Activity" (já vem com Compose).
  3. Rode no emulador (AVD Manager) ou no seu celular com Depuração USB ativada.

4.2 Anatomia de um projeto Android

Arquivo/pastaPara que serve
app/src/main/java|kotlin/Seu código Kotlin.
AndroidManifest.xmlDeclara telas, permissões e metadados do app.
build.gradle.ktsDependências, versões de SDK, configuração de build.
res/Recursos: imagens, cores, strings (traduções!).

4.3 Jetpack Compose: UI declarativa

Compose é o toolkit moderno de UI do Android: em vez de XML, você descreve a tela com funções Kotlin anotadas com @Composable. Quando o estado muda, a tela se recompõe sozinha.

@Composable
fun TelaContador() {
    // estado observável: mudar o valor redesenha a UI
    var contador by remember { mutableStateOf(0) }

    Column(
        modifier = Modifier.fillMaxSize().padding(24.dp),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Text("Você clicou $contador vezes", style = MaterialTheme.typography.headlineSmall)
        Spacer(Modifier.height(16.dp))
        Button(onClick = { contador++ }) {
            Text("Clique aqui")
        }
    }
}

Componentes que você usará todo dia

TextButtonTextFieldImage / AsyncImage Column / Row / BoxLazyColumn (listas)Scaffold + TopAppBar CardModifier (padding, clique, tamanho)

4.4 Listas com LazyColumn

@Composable
fun ListaTarefas(tarefas: List<Tarefa>, onConcluir: (Tarefa) -> Unit) {
    LazyColumn(contentPadding = PaddingValues(16.dp)) {
        items(tarefas, key = { it.id }) { tarefa ->
            Card(Modifier.fillMaxWidth().padding(vertical = 4.dp)) {
                Row(Modifier.padding(16.dp), verticalAlignment = Alignment.CenterVertically) {
                    Checkbox(checked = tarefa.concluida, onCheckedChange = { onConcluir(tarefa) })
                    Text(tarefa.titulo, Modifier.padding(start = 8.dp))
                }
            }
        }
    }
}

4.5 Navegação entre telas

val navController = rememberNavController()
NavHost(navController, startDestination = "lista") {
    composable("lista") { TelaLista(onAbrir = { id -> navController.navigate("detalhe/$id") }) }
    composable("detalhe/{id}") { backStack ->
        TelaDetalhe(id = backStack.arguments?.getString("id"))
    }
}
Projeto do módulo — App de Tarefas (To-Do)

Duas telas (lista + adicionar), estado com mutableStateListOf, marcação de concluídas, contador no topo. Publique o código no GitHub com README e screenshots — este será o primeiro item do seu portfólio.

Trilha Android · Módulo 5

Android intermediário: dados, rede e arquitetura

Aqui você sai do nível "faço telas" e entra no nível contratável: ViewModel, consumo de API, banco local e o padrão MVVM que praticamente todas as empresas usam.

5.1 ViewModel + StateFlow: estado que sobrevive à rotação

class TarefasViewModel(private val repo: TarefasRepository) : ViewModel() {

    private val _state = MutableStateFlow<UiState>(UiState.Loading)
    val state: StateFlow<UiState> = _state.asStateFlow()

    init { carregar() }

    fun carregar() = viewModelScope.launch {
        _state.value = UiState.Loading
        runCatching { repo.listarTarefas() }
            .onSuccess { _state.value = UiState.Success(it) }
            .onFailure { _state.value = UiState.Error("Falha ao carregar") }
    }
}

// Na tela (Compose):
val state by viewModel.state.collectAsStateWithLifecycle()
when (val s = state) {
    UiState.Loading    -> CircularProgressIndicator()
    is UiState.Success -> ListaTarefas(s.usuarios, onConcluir = { })
    is UiState.Error   -> Text(s.mensagem)
}

5.2 Consumindo APIs com Retrofit

// 1. Modelo (com kotlinx.serialization ou Moshi)
@Serializable
data class FilmeDto(val id: Int, val title: String, val poster_path: String?)

// 2. Interface da API
interface FilmesApi {
    @GET("movie/popular")
    suspend fun populares(@Query("page") page: Int = 1): PaginaDto
}

// 3. Instância
val api = Retrofit.Builder()
    .baseUrl("https://api.themoviedb.org/3/")
    .addConverterFactory(json.asConverterFactory("application/json".toMediaType()))
    .build()
    .create(FilmesApi::class.java)

5.3 Banco local com Room

@Entity(tableName = "tarefas")
data class TarefaEntity(
    @PrimaryKey(autoGenerate = true) val id: Long = 0,
    val titulo: String,
    val concluida: Boolean = false
)

@Dao
interface TarefaDao {
    @Query("SELECT * FROM tarefas ORDER BY id DESC")
    fun observarTodas(): Flow<List<TarefaEntity>>   // UI reage sozinha

    @Insert suspend fun inserir(t: TarefaEntity)
    @Update suspend fun atualizar(t: TarefaEntity)
}

5.4 O padrão Repository e o MVVM completo

O fluxo que você verá em 90% das empresas:

UI (Compose)  ⇄  ViewModel (estado + lógica de apresentação)
                     │
                Repository (decide: rede ou cache?)
                 ┌───┴────┐
             Retrofit    Room
             (remoto)   (local)
  • A UI não conhece Retrofit nem Room — só o ViewModel.
  • O ViewModel não sabe de onde os dados vêm — só o Repository.
  • Isso torna cada camada testável e substituível — e é exatamente o que perguntam em entrevista.

Outros tópicos deste nível

Coil (carregar imagens)DataStore (preferências) Permissões em runtimeMaterial 3 e temas Strings traduzíveis (pt/en)Paging 3 (listas infinitas)

Projeto do módulo — Catálogo de Filmes

Consuma a API do TMDB: lista popular com pôsteres (Coil), tela de detalhes, favoritos salvos no Room funcionando offline, estados de loading/erro/vazio bem tratados. Este projeto, bem feito, é forte o suficiente para processos de vaga júnior.

Trilha Android · Módulo 6

Android avançado: o que separa pleno de sênior

6.1 Injeção de dependência com Hilt

DI elimina a criação manual de objetos e é padrão em qualquer base de código profissional.

@HiltAndroidApp
class MeuApp : Application()

@Module
@InstallIn(SingletonComponent::class)
object DataModule {
    @Provides @Singleton
    fun provideApi(): FilmesApi = criarRetrofit()
}

@HiltViewModel
class FilmesViewModel @Inject constructor(
    private val repo: FilmesRepository
) : ViewModel() { /* ... */ }

6.2 Clean Architecture e modularização

:app                → monta tudo (DI, navegação)
:feature:filmes     → UI + ViewModel da feature
:feature:favoritos
:domain             → casos de uso + modelos puros (sem Android!)
:data               → repositórios, Retrofit, Room
:core:ui, :core:network, :core:testing
  • UseCases (class BuscarFilmesPopularesUseCase) encapsulam regras de negócio e são triviais de testar.
  • Modularizar melhora tempo de build, isola times e é pergunta certa em entrevistas sênior.
  • Regra de dependência: camadas externas dependem das internas; :domain não depende de ninguém.

6.3 Testes — o divisor de águas nas contratações

// Teste de unidade do ViewModel com corrotinas de teste
@Test
fun `carregar emite Success quando o repositório responde`() = runTest {
    val repo = FakeFilmesRepository(listOf(filme(1, "Duna")))
    val vm = FilmesViewModel(repo)

    advanceUntilIdle()

    assertEquals(UiState.Success(listOf(filme(1, "Duna"))), vm.state.value)
}
  • Unidade: JUnit + Turbine (para Flow) + fakes/MockK.
  • UI: createComposeRule() para testar composables.
  • Instrumentados/E2E: Espresso ou Maestro.
  • Meta profissional: lógica de negócio e ViewModels cobertos; UI crítica com testes de fumaça.

6.4 Performance e qualidade

  • Recomposições desnecessárias: use remember, chaves estáveis em listas e o Layout Inspector; entenda stability em Compose.
  • Baseline Profiles e R8/ProGuard para startup rápido e APK menor.
  • Memory leaks: LeakCanary em debug; cuidado com referências a Context.
  • ANRs e jank: nada de trabalho pesado na main thread; monitore com o Android Vitals.
  • WorkManager para tarefas em background confiáveis (sincronização, uploads).
Checklist de entrevista Android sênior Ciclo de vida e process death · Coroutines/Flow a fundo (cold vs hot, SharedFlow vs StateFlow) · Compose internals (recomposição, side effects: LaunchedEffect, DisposableEffect) · Clean Architecture na prática · estratégia de testes · modularização · CI/CD · segurança de dados locais.
Trilha iOS · Módulo 7

Swift do zero

Swift é a linguagem da Apple: moderna, rápida e com segurança de tipos rigorosa. Você precisará de um Mac com Xcode (gratuito na App Store). Sem Mac? Comece pela trilha Android e migre depois — os conceitos são 80% transferíveis.

7.1 Sintaxe essencial

// let (imutável, prefira) e var (mutável)
let nome = "Ana"
var idade = 28

// Opcionais: o null safety do Swift
var apelido: String? = nil
if let apelido {                 // unwrap seguro
    print("Apelido: \(apelido)")
}
let tamanho = apelido?.count ?? 0  // nil-coalescing

// Funções com rótulos de argumento
func saudacao(para nome: String, formal: Bool = false) -> String {
    formal ? "Prezado(a) \(nome)" : "Oi, \(nome)!"
}
saudacao(para: "Ana")

7.2 Structs, enums e protocolos — o coração do Swift

// Em Swift, prefira structs (value types) a classes
struct Usuario: Identifiable, Codable {
    let id: Int
    let nome: String
    let email: String
}

// Enums com valores associados: perfeitos para estado de tela
enum UiState {
    case loading
    case success([Usuario])
    case error(String)
}

// Protocolos = contratos (equivalem a interfaces)
protocol UsuarioRepository {
    func listar() async throws -> [Usuario]
}

7.3 Coleções e closures

let precos = [19.9, 45.0, 120.0, 8.5]
let caros = precos.filter { $0 > 40 }
let total = precos.reduce(0, +)

7.4 Concorrência moderna: async/await

func buscarUsuarios() async throws -> [Usuario] {
    let url = URL(string: "https://api.exemplo.com/usuarios")!
    let (data, _) = try await URLSession.shared.data(from: url)
    return try JSONDecoder().decode([Usuario].self, from: data)
}

// Task cria um contexto assíncrono; @MainActor protege a UI
Task { @MainActor in
    do { usuarios = try await buscarUsuarios() }
    catch { mensagemErro = "Falha ao carregar" }
}
Conceitos para dominar Opcionais e unwrapping · value vs reference types (struct vs class) · protocolos e extensions · Codable · async/await, Task, actors e @MainActor · gerenciamento de memória com ARC e weak.
Projeto do módulo

Em um Playground do Xcode, modele uma biblioteca: struct Livro, protocolo Acervo, busca com filter, empréstimos com datas (Date) e um enum de estado do livro.

Trilha iOS · Módulo 8

Primeiro app: Xcode e SwiftUI

8.1 Ambiente e projeto

  1. Instale o Xcode e crie um projeto App com interface SwiftUI.
  2. Rode no Simulator (⌘R) — teste em vários iPhones e no modo escuro.
  3. Explore o Preview: a tela renderiza ao lado do código enquanto você digita.

8.2 SwiftUI: UI declarativa

struct TelaContador: View {
    @State private var contador = 0   // estado: mudar redesenha a view

    var body: some View {
        VStack(spacing: 16) {
            Text("Você clicou \(contador) vezes")
                .font(.title2)
            Button("Clique aqui") { contador += 1 }
                .buttonStyle(.borderedProminent)
        }
        .padding()
    }
}

Componentes do dia a dia

TextButtonTextFieldAsyncImage VStack / HStack / ZStackListNavigationStack TabViewModificadores (.padding, .font...)

8.3 Listas e navegação

struct ListaTarefas: View {
    @State private var tarefas = [Tarefa(titulo: "Estudar SwiftUI")]

    var body: some View {
        NavigationStack {
            List($tarefas) { $tarefa in
                NavigationLink(value: tarefa) {
                    Toggle(tarefa.titulo, isOn: $tarefa.concluida)
                }
            }
            .navigationTitle("Tarefas")
            .navigationDestination(for: Tarefa.self) { t in
                DetalheView(tarefa: t)
            }
        }
    }
}

8.4 Fluxo de dados: as property wrappers

WrapperQuando usar
@StateEstado local e simples da própria view.
@BindingView filha lê e escreve estado do pai.
@Observable (iOS 17+)Classes de modelo/ViewModel observáveis — o padrão atual.
@EnvironmentDependências e valores compartilhados na árvore de views.
Projeto do módulo — App de Tarefas (iOS)

O mesmo To-Do da trilha Android, agora em SwiftUI: NavigationStack, lista com swipe-to-delete (.onDelete), formulário de nova tarefa em sheet, e suporte a Dynamic Type e modo escuro. Suba no GitHub com screenshots.

Trilha iOS · Módulo 9

iOS intermediário: dados, rede e arquitetura

9.1 ViewModel observável (MVVM em SwiftUI)

@Observable
final class FilmesViewModel {
    enum Estado { case carregando, sucesso([Filme]), erro(String) }

    private(set) var estado: Estado = .carregando
    private let repo: FilmesRepository

    init(repo: FilmesRepository) { self.repo = repo }

    @MainActor
    func carregar() async {
        estado = .carregando
        do    { estado = .sucesso(try await repo.populares()) }
        catch { estado = .erro("Não foi possível carregar") }
    }
}

// Na view:
.task { await viewModel.carregar() }   // dispara ao aparecer e cancela ao sair

9.2 Camada de rede com URLSession

struct ApiClient {
    let baseURL = URL(string: "https://api.themoviedb.org/3")!

    func get<T: Decodable>(_ path: String) async throws -> T {
        let (data, response) = try await URLSession.shared
            .data(from: baseURL.appending(path: path))
        guard (response as? HTTPURLResponse)?.statusCode == 200 else {
            throw ApiError.respostaInvalida
        }
        return try JSONDecoder().decode(T.self, from: data)
    }
}

9.3 Persistência com SwiftData

@Model
final class TarefaModel {
    var titulo: String
    var concluida: Bool
    init(titulo: String, concluida: Bool = false) {
        self.titulo = titulo; self.concluida = concluida
    }
}

// Na view: consulta reativa, direto do banco
@Query(sort: \TarefaModel.titulo) private var tarefas: [TarefaModel]
@Environment(\.modelContext) private var contexto
// contexto.insert(nova) / contexto.delete(tarefa)

Core Data é o antecessor do SwiftData e ainda domina bases legadas — vale conhecer os conceitos (NSManagedObjectContext, fetch requests) para entrevistas.

Outros tópicos deste nível

Combine (reatividade legada)UserDefaults / Keychain Localização de stringsAcessibilidade (VoiceOver) UIKit básico (legado)SPM — Swift Package Manager

Sobre UIKit SwiftUI é o presente e o futuro, mas grandes empresas mantêm milhões de linhas em UIKit. Saber o básico (UIViewController, Auto Layout, delegates) e como integrar UIKit ↔ SwiftUI (UIViewRepresentable, UIHostingController) aumenta muito sua empregabilidade.
Projeto do módulo — Catálogo de Filmes (iOS)

API do TMDB + AsyncImage, MVVM com @Observable, favoritos persistidos com SwiftData, busca com .searchable, e tratamento de estados vazio/erro/carregando.

Trilha iOS · Módulo 10

iOS avançado: o que separa pleno de sênior

10.1 Concorrência estrita e actors

// Actor: protege estado mutável contra data races
actor CacheDeImagens {
    private var cache: [URL: Data] = [:]
    func imagem(para url: URL) -> Data? { cache[url] }
    func salvar(_ data: Data, para url: URL) { cache[url] = data }
}
  • Swift 6 introduz strict concurrency: entender Sendable, isolamento de actors e @MainActor é tema quente de entrevista.
  • Structured concurrency: async let, TaskGroup, cancelamento cooperativo.

10.2 Memória: ARC, retain cycles e ferramentas

// Retain cycle clássico e a correção com [weak self]
service.onUpdate = { [weak self] valor in
    self?.atualizar(valor)
}
  • Use Instruments (Leaks, Allocations, Time Profiler) — cite-o em entrevistas.
  • Entenda weak vs unowned e ciclos em closures/delegates.

10.3 Arquitetura e testes

  • MVVM + Repository é o padrão dominante; Clean/VIPER/TCA aparecem em times grandes — saiba comparar trade-offs.
  • Injeção de dependência via inicializadores e protocolos (ou @Environment).
  • XCTest para unidade; Swift Testing (framework novo com @Test) ganhando espaço; XCUITest para E2E; snapshot testing para UI.
func test_carregar_populaEstadoDeSucesso() async {
    let vm = FilmesViewModel(repo: RepoFake(filmes: [.duna]))
    await vm.carregar()
    XCTAssertEqual(vm.estado, .sucesso([.duna]))
}

10.4 Performance e plataforma

  • Diagnóstico de re-renders no SwiftUI (let _ = Self._printChanges()), identidade de views e EquatableView.
  • Launch time, tamanho do binário, hangs — MetricKit e Xcode Organizer.
  • Recursos de plataforma que valorizam currículo: Push Notifications (APNs), Widgets, App Intents/Siri, Live Activities, StoreKit 2 (assinaturas).
Checklist de entrevista iOS sênior ARC e retain cycles · struct vs class na prática · concorrência (actors, Sendable, MainActor) · ciclo de vida de views SwiftUI e identidade · Core Data/SwiftData · estratégia de testes · integração UIKit↔SwiftUI · assinaturas com StoreKit · CI com Xcode Cloud/fastlane.
Nível sênior · Módulo 11

Cross-platform: Flutter, React Native e KMP

FlutterReact NativeKotlin Multiplatform
LinguagemDartJavaScript/TypeScriptKotlin
UIPrópria (renderiza tudo)Componentes nativos via bridge/JSIUI nativa em cada plataforma (ou Compose Multiplatform)
Ponto forteConsistência visual, performance, toolingEcossistema JS, reaproveita devs webCompartilha lógica mantendo UX 100% nativa
Quem usaNubank, BMW, Google PayMeta, Shopify, MicrosoftNetflix, McDonald's, Forbes
Vagas BRMuitas (forte em fintechs)Muitas (startups/consultorias)Crescendo, ainda nicho

11.1 Gostinho de Flutter

class Contador extends StatefulWidget {
  @override
  State<Contador> createState() => _ContadorState();
}

class _ContadorState extends State<Contador> {
  int contador = 0;

  @override
  Widget build(BuildContext context) => Scaffold(
    body: Center(child: Text('Cliques: $contador')),
    floatingActionButton: FloatingActionButton(
      onPressed: () => setState(() => contador++),
      child: const Icon(Icons.add),
    ),
  );
}

Ecossistema a estudar: Riverpod/Bloc (estado)go_routerdio (rede)drift/Isar (banco)

11.2 Gostinho de React Native

export default function Contador() {
  const [contador, setContador] = useState(0);
  return (
    <View style={styles.center}>
      <Text>Cliques: {contador}</Text>
      <Button title="Clique aqui" onPress={() => setContador(c => c + 1)} />
    </View>
  );
}

Ecossistema: ExpoReact NavigationTypeScript (obrigatório no mercado)Zustand/ReduxReact Query

11.3 Quando escolher o quê (visão de arquiteto)

  • App padrão de produto (CRUD + API): qualquer opção entrega; decida pelo time disponível.
  • UX de plataforma impecável, APIs novas do sistema, hardware: nativo (ou KMP).
  • Time web forte já contratado: React Native.
  • Design system próprio idêntico nas duas lojas: Flutter.
  • Em entrevista, a resposta madura nunca é "X é melhor", e sim trade-offs em função de time, prazo e produto.
Nível sênior · Módulo 12

Tópicos muito avançados (as duas plataformas)

12.1 CI/CD para mobile

Times profissionais não geram builds na mão. Pipeline típico a cada Pull Request:

lint + análise estática  →  testes de unidade  →  testes de UI (fumaça)
        →  build assinado  →  distribuição interna (TestFlight / Firebase App Distribution)
        →  release nas lojas com faixas de rollout (10% → 50% → 100%)
  • Ferramentas: GitHub Actions, Bitrise, fastlane (automação de assinatura, screenshots e upload), Xcode Cloud.
  • Saiba explicar assinatura de apps: keystore (Android) e certificados/provisioning profiles (iOS) — dor clássica que sênior resolve.
# .github/workflows/android.yml (essência)
on: pull_request
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-java@v4
        with: { distribution: temurin, java-version: 17 }
      - run: ./gradlew lint testDebugUnitTest assembleDebug

12.2 Segurança mobile (OWASP MASVS)

  • Nunca guarde segredos no código — chaves vazam via engenharia reversa; use backend ou serviços de configuração remota.
  • Dados sensíveis locais: Keychain (iOS) e EncryptedSharedPreferences/Keystore (Android).
  • Rede: HTTPS sempre; conheça certificate pinning e seus riscos operacionais.
  • Ofuscação (R8) no Android; detecção de root/jailbreak quando o produto exige (bancos).
  • Valide tudo no servidor: o app é território do usuário, não confie nele.

12.3 Offline-first e sincronização

  • Banco local como fonte única da verdade; a UI observa o banco, a rede só o atualiza.
  • Estratégias de cache: stale-while-revalidate, TTL, invalidação por eventos.
  • Fila de operações offline + resolução de conflitos (last-write-wins vs merge).
  • Sincronização em background: WorkManager (Android) / BackgroundTasks (iOS).

12.4 Observabilidade e produto

  • Crashlytics/Sentry para crashes; alertas por taxa de crash-free users (meta: >99,5%).
  • Analytics e funis; feature flags e A/B testing para lançar com segurança.
  • Deep links / App Links / Universal Links — integração com marketing e notificações.
  • Push notifications ponta a ponta: FCM/APNs, tokens, payloads, ações.

12.5 Escala de código e de time

  • Monorepo multi-módulo, convenções de código, lint custom, templates de PR.
  • Design system interno com componentes documentados (catálogo/showcase app).
  • Estratégias de migração incremental (XML→Compose, UIKit→SwiftUI, Java→Kotlin, Obj-C→Swift).
  • Documentar decisões com ADRs (Architecture Decision Records) — impressiona em entrevistas de system design.
Nível sênior · Módulo 13

Publicando nas lojas

13.1 Google Play

  1. Conta de desenvolvedor: US$ 25 (pagamento único).
  2. Gere um Android App Bundle (.aab) assinado com sua keystore (guarde-a com a vida — perder = não atualizar mais o app).
  3. Preencha ficha da loja: título, descrições, screenshots, classificação etária, política de privacidade e formulário de Data safety.
  4. Use as faixas: internal testing → closed → open → produção, com rollout gradual.
  5. Contas pessoais novas exigem um período de teste fechado com testadores antes do lançamento público — planeje isso no cronograma.

13.2 App Store

  1. Apple Developer Program: US$ 99/ano.
  2. Configure certificados e provisioning profiles (o Xcode automatiza o caminho feliz).
  3. Suba o build pelo Xcode/Transporter e distribua beta via TestFlight.
  4. Preencha o App Store Connect: metadados, screenshots por tamanho de tela, privacidade (privacy nutrition labels).
  5. Revisão humana (normalmente 1–2 dias). Motivos comuns de rejeição: crashes, login sem função demo, links quebrados, compras fora do In-App Purchase quando obrigatório, descrição enganosa.
ASO — App Store Optimization Título com palavra-chave, screenshots que contam história, ícone testado em tamanho real, primeiras linhas da descrição vendendo o benefício, e resposta ativa a reviews. Um app publicado com boa ficha vale mais no seu portfólio do que três repositórios sem release.
Mercado de trabalho · Módulo 14

Portfólio, GitHub e currículo que passam no filtro

14.1 Os 3 projetos que formam um portfólio contratável

  1. App CRUD completo (To-Do dos módulos 4/8) — mostra fundamentos e capricho de UI.
  2. App consumindo API real com cache offline (Catálogo de Filmes) — mostra arquitetura MVVM, rede, banco e tratamento de erros. Inclua testes.
  3. Um app publicado na loja, resolvendo um problema real (mesmo que pequeno: conversor, hábito tracker, app para um comércio local). Publicado > perfeito.

14.2 README que recrutador técnico lê em 60 segundos

# CineList — catálogo de filmes offline-first

Screenshots | GIF de 15s do app funcionando

## Stack
Kotlin · Jetpack Compose · MVVM + Clean · Hilt · Retrofit · Room · Coroutines/Flow

## Destaques técnicos
- Offline-first: Room como fonte única da verdade
- 87% de cobertura na camada de domínio (JUnit + Turbine)
- CI com GitHub Actions (lint + testes a cada PR)

## Como rodar
1. Adicione sua chave TMDB em local.properties
2. ./gradlew installDebug
Erros que eliminam candidatos Repositório sem README · commits "update", "fix2", "final" · chave de API commitada · código de tutorial copiado sem nenhuma modificação · 15 projetos abandonados em vez de 3 excelentes.

14.3 Currículo e LinkedIn

  • Título claro: "Desenvolvedor Android | Kotlin · Jetpack Compose · MVVM".
  • Bullets com resultado, não tarefa: "Reduzi o tempo de startup em 40% com Baseline Profiles" > "trabalhei com performance".
  • Sem experiência formal? Trate seus projetos como experiência: contexto, decisões técnicas, métricas (downloads, crash-free, cobertura).
  • Uma página, PDF, link do GitHub e da loja no topo.
  • No LinkedIn: poste o processo (aprendizados, before/after de telas). Consistência gera convites de recrutadores.

14.4 Inglês: o multiplicador salarial

Documentação, issues e as melhores vagas (remotas, em dólar) são em inglês. Meta mínima: ler documentação sem tradutor e manter uma daily de 15 minutos. Estude com o próprio material técnico — dois coelhos.

Mercado de trabalho · Módulo 15

Entrevistas técnicas: o que cai e como responder

15.1 Formato típico de um processo mobile

  1. Triagem com recrutador (fit, pretensão, inglês).
  2. Entrevista técnica de conceitos (a lista abaixo).
  3. Desafio técnico: app pequeno consumindo API (3–7 dias) ou live coding.
  4. System design mobile (pleno/sênior): "desenhe o app de feed do Instagram".
  5. Comportamental (use o método STAR: Situação, Tarefa, Ação, Resultado).

15.2 Perguntas clássicas — Android

  • Ciclo de vida de Activity/Composable; o que acontece na rotação? E em process death?
  • launch vs async; StateFlow vs SharedFlow vs LiveData; o que é structured concurrency?
  • Como o Compose decide recompor? O que é estabilidade? Para que servem remember e derivedStateOf?
  • Por que Repository? Como você testaria esse ViewModel? O que mockar e o que usar fake?
  • Como evitar memory leaks? O que o R8 faz?

15.3 Perguntas clássicas — iOS

  • Struct vs class — quando cada um? O que é copy on write?
  • Como o ARC funciona? Monte e corrija um retain cycle.
  • @State vs @Binding vs @Observable; o que define a identidade de uma view?
  • O que um actor resolve? Para que serve @MainActor? O que é Sendable?
  • GCD vs async/await; como cancelar uma Task?

15.4 System design mobile: roteiro de resposta

  1. Requisitos: funcionais (feed, like, offline?) e não-funcionais (latência, consumo de dados, escala).
  2. Camadas: UI → ViewModel → Repository → (API paginada + banco local).
  3. Pontos críticos: paginação (cursor), cache de imagens, offline-first, sincronização, pré-carregamento.
  4. Trade-offs em voz alta — é isso que avaliam, não a resposta "certa".
  5. Extras sênior: feature flags, analytics, tratamento de erros por camada, testes por camada.

15.5 No desafio técnico, capriche no que diferencia

  • README explicando decisões ("usei X em vez de Y porque...").
  • Testes de unidade nas regras de negócio (mesmo poucos, bem escolhidos).
  • Estados de erro/vazio/carregando tratados — a maioria dos candidatos ignora.
  • Commits pequenos e descritivos contando a história da solução.
Mercado de trabalho · Módulo 16

Roadmap de 12 meses (estudando 2h/dia)

MesesFocoEntregável
1–2Módulos 1–3 (ou 7): lógica, Git, linguagemExercícios diários + GitHub ativo
3–4Módulo 4 (ou 8): UI declarativa, navegaçãoApp To-Do publicado no GitHub
5–6Módulo 5 (ou 9): API, banco, MVVMCatálogo de Filmes com testes
7Módulo 13: publicar um app autoralApp na Play Store/App Store
8–9Módulo 6 (ou 10): DI, testes, arquiteturaRefatorar o catálogo p/ Clean + CI
10Módulos 14–15: portfólio + simulados de entrevistaCurrículo, LinkedIn, 20 candidaturas/semana
11–12Módulo 12 conforme as entrevistas pedirem + segunda plataforma ou FlutterPrimeiro emprego / freelas

Hábitos que aceleram

  • Construa > assista: para cada hora de vídeo/leitura, duas de código.
  • Leia código de apps open source de referência (Now in Android, da Google; apps da comunidade iOS).
  • Participe de comunidades (Android Dev BR, iOS Devs BR, GDGs, CocoaHeads) — a maioria das primeiras vagas vem de indicação.
  • Documentação oficial primeiro: developer.android.com e developer.apple.com são as melhores fontes que existem.
  • Freelas pequenos e apps para comércios locais contam como experiência real no currículo.
A regra de ouro Você não precisa saber tudo desta apostila para ser contratado. Precisa de uma trilha nativa sólida até o módulo intermediário, três projetos bem-acabados e constância. O avançado você consolida trabalhando — e esta apostila continua servindo de referência.