Kotlin Multiplatform 2026: compartilhar código Android/iOS

Como o Kotlin Multiplatform compartilha a lógica de negócio entre Android e iOS em 2026: configuração do Gradle, expect/actual, rede com Ktor, interface Compose Multiplatform e perguntas comuns de entrevista de KMP.

Kotlin Multiplatform 2026 compartilhando código entre aplicativos Android e iOS

O Kotlin Multiplatform (KMP) permite que uma única base de código Kotlin alimente a lógica de negócio dos aplicativos Android e iOS, enquanto cada plataforma mantém sua interface totalmente nativa, seu desempenho e o acesso ao seu SDK. Em 2026, o KMP deixou de ser um experimento: a tecnologia é estável, tem o apoio oficial do Google e roda em produção na Netflix, no McDonald's, na Forbes e no Cash App. Esta análise detalha como o compartilhamento de código funciona de fato, como configurar um módulo compartilhado e as escolhas que vale a pena entender antes de uma entrevista de KMP.

O que o KMP realmente compartilha

O Kotlin Multiplatform compartilha lógica compilada, não um runtime. O Kotlin comum é compilado para bytecode da JVM no Android e para um binário nativo (via LLVM) no iOS. Não há ponte de JavaScript, nem webview embutida, nem camada de reflexão, então o código compartilhado roda em velocidade nativa nas duas plataformas.

Como o Kotlin Multiplatform compartilha código sem sacrificar o desempenho nativo

O mecanismo central é uma hierarquia de source sets. O commonMain contém o Kotlin independente de plataforma: rede, serialização, regras de negócio e view models. Os source sets de plataforma como androidMain e iosMain guardam o código que acessa as APIs nativas. O compilador produz um artefato JVM a partir de commonMain mais androidMain para o Android, e um framework nativo a partir de commonMain mais iosMain para o iOS.

Em 2026, o Kotlin Multiplatform prioriza compartilhar o que de fato se beneficia de uma única fonte de verdade: modelos, clientes de API, cache e validação. A interface pode ser compartilhada com o Compose Multiplatform ou permanecer totalmente nativa com SwiftUI, tela por tela. Esse modelo de «compartilhar a lógica, escolher a interface» é o motivo pelo qual as equipes adotam o KMP sem reescrever os aplicativos existentes.

O Kotlin Multiplatform é uma tecnologia Kotlin que compila um módulo compartilhado para vários targets (Android na JVM, iOS como nativo, além de desktop e web), de modo que a lógica de negócio é escrita uma única vez enquanto cada plataforma mantém sua interface nativa e o acesso completo ao seu SDK. O Google agora o oferece como uma opção de primeira classe, e bibliotecas do Jetpack como Room, DataStore, ViewModel e Paging fornecem artefatos KMP.

Decidir o que pertence ao módulo compartilhado é a decisão de arquitetura mais importante de um projeto KMP. A tabela a seguir reflete como a maioria das equipes em produção traça a linha em 2026: tudo o que está abaixo da camada de apresentação é um forte candidato ao compartilhamento, enquanto tudo o que toca as convenções de UX da plataforma costuma permanecer nativo.

| Camada | Compartilhar em commonMain? | Motivo | |-------|---------------------|--------| | Modelos de dados e DTOs | Sim | Contrato de API idêntico nas duas plataformas | | Rede e cache | Sim | Uma única pilha HTTP, uma única superfície de bugs | | Regras de negócio e validação | Sim | O domínio não muda conforme o sistema operacional | | View models e estado | Normalmente | Compose e SwiftUI consomem o estado compartilhado | | Renderização da interface | Opcional | Compartilhada com o Compose ou nativa por tela | | APIs de plataforma (câmera, biometria) | Não | Melhor expor via expect/actual |

O retorno aumenta conforme a fatia da metade inferior dessa tabela que um aplicativo abrange. Produtos com muitos dados e interfaces enxutas compartilham o máximo; aplicativos muito personalizados e movidos a animação compartilham o mínimo. Nenhum dos extremos está errado, e é por isso que o KMP combina com uma adoção incremental em vez de uma reescrita de tudo ou nada.

Configurar o módulo compartilhado no Gradle

Um módulo KMP declara seus targets e source sets em Kotlin DSL. O build.gradle.kts a seguir configura o Android e as três arquiteturas de iOS (dispositivo físico, simulador Intel, simulador Apple Silicon) e expõe a saída do iOS como um framework chamado Shared.

shared/build.gradle.ktskotlin
plugins {
    kotlin("multiplatform")
    kotlin("plugin.serialization")
    id("com.android.library")
}

kotlin {
    // Android target compiles commonMain + androidMain to JVM bytecode
    androidTarget()

    // Three iOS targets: device, Intel sim, Apple Silicon sim
    listOf(iosArm64(), iosX64(), iosSimulatorArm64()).forEach { target ->
        target.binaries.framework {
            baseName = "Shared"   // becomes "import Shared" in Swift
            isStatic = true
        }
    }

    sourceSets {
        commonMain.dependencies {
            implementation("io.ktor:ktor-client-core:3.3.0")
            implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.9.0")
            implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2")
        }
        androidMain.dependencies {
            implementation("io.ktor:ktor-client-okhttp:3.3.0")
        }
        iosMain.dependencies {
            implementation("io.ktor:ktor-client-darwin:3.3.0")
        }
    }
}

Cada target puxa um motor Ktor específico da plataforma (okhttp no Android, darwin no iOS), enquanto o código compartilhado depende apenas da API comum ktor-client-core. O conjunto completo de opções está na documentação do Kotlin Multiplatform.

Escrever código específico da plataforma com expect e actual

Parte da lógica não pode ficar no commonMain porque precisa de uma API de plataforma. O mecanismo expect/actual declara o formato no código comum e fornece uma implementação concreta por target, resolvida em tempo de compilação e sem custo em tempo de execução.

commonMain/kotlin/Platform.ktkotlin
// commonMain declares what it needs but cannot implement here
expect class Platform() {
    val name: String
    val osVersion: String
}

// Shared logic can now depend on Platform freely
fun analyticsUserAgent(): String {
    val platform = Platform()
    return "SharpSkill/" + platform.name + " " + platform.osVersion
}

A implementação do Android lê da classe Build e a do iOS lê de UIDevice. Ambas cumprem o mesmo contrato, então o commonMain nunca sabe com qual delas está conversando.

androidMain/kotlin/Platform.ktkotlin
import android.os.Build

// actual supplies the Android-specific implementation
actual class Platform actual constructor() {
    actual val name: String = "Android"
    actual val osVersion: String = Build.VERSION.RELEASE
}
iosMain/kotlin/Platform.ktkotlin
import platform.UIKit.UIDevice

// actual uses UIKit directly, no Objective-C bridging code required
actual class Platform actual constructor() {
    actual val name: String = "iOS"
    actual val osVersion: String = UIDevice.currentDevice.systemVersion
}

O Kotlin/Native traz bindings tipados para todo o SDK do iOS, de modo que UIDevice, NSUserDefaults e os tipos do Foundation podem ser chamados a partir do Kotlin sem código de ligação escrito à mão.

Compartilhar a lógica de rede e serialização

A rede é o código mais valioso para compartilhar, porque os contratos de API são idênticos nas duas plataformas. Um único cliente Ktor somado ao kotlinx.serialization substitui duas implementações paralelas. As funções suspend se apoiam nas corrotinas do Kotlin, que se traduzem de forma limpa em async/await dos dois lados.

commonMain/kotlin/data/InterviewRepository.ktkotlin
import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.client.request.get
import io.ktor.serialization.kotlinx.json.json
import kotlinx.serialization.Serializable

// One serializable model, zero duplication across platforms
@Serializable
data class Question(val id: String, val prompt: String, val difficulty: String)

// One HttpClient, one JSON parser, one repository for both apps
class InterviewRepository {
    private val client = HttpClient {
        install(ContentNegotiation) { json() }
    }

    suspend fun fetchQuestions(tech: String): List<Question> =
        client.get("https://api.sharpskill.dev/questions/" + tech).body()
}

Esse repositório, sua lógica de repetição e seus modelos de dados são escritos uma única vez. Um bug corrigido no parser fica corrigido no Android e no iOS ao mesmo tempo, e é aí que o KMP paga o custo de configuração. O mesmo padrão se estende a um banco de dados compartilhado com o SQLDelight, a um armazenamento chave-valor compartilhado e a uma injeção de dependências compartilhada com o Koin, de modo que toda a camada de dados pode ficar no commonMain por trás de interfaces das quais a interface de usuário depende.

Um erro comum é tentar compartilhar demais cedo demais. O caminho pragmático é começar com um único repositório, validar o build e o pipeline de CI nas duas plataformas e então mover as funcionalidades uma de cada vez conforme a confiança aumenta.

Pronto para mandar bem nas entrevistas de Android?

Pratique com nossos simuladores interativos, flashcards e testes tecnicos.

Construir uma interface compartilhada com o Compose Multiplatform

O Compose Multiplatform atingiu o status estável no iOS em maio de 2025 (versão 1.8.0) e chegou à 1.11.0 em meados de 2026, com entrada de texto nativa e renderização concorrente ativada por padrão. Um @Composable no commonMain é renderizado nas duas plataformas, e os view models compartilhados combinam naturalmente com uma arquitetura MVVM ou MVI.

commonMain/kotlin/ui/QuestionList.ktkotlin
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Card
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.foundation.layout.padding

// This list renders identically on Android and iOS
@Composable
fun QuestionList(questions: List<Question>) {
    LazyColumn {
        items(questions) { question ->
            Card(modifier = Modifier.padding(8.dp)) {
                Text(text = question.prompt, modifier = Modifier.padding(16.dp))
            }
        }
    }
}

Compartilhar a interface é opcional e feito tela por tela. Uma equipe pode compartilhar toda a interface com o Compose Multiplatform, manter o SwiftUI para o aplicativo inteiro e compartilhar apenas a lógica, ou combinar as duas abordagens. As telas que dependem de uma UX específica da plataforma, como widgets ou App Clips, costumam permanecer nativas.

Consumir o código compartilhado a partir do Swift no iOS

O módulo compartilhado é compilado em um framework que o Swift importa como qualquer biblioteca. Historicamente, o atrito vinha das funções suspend do Kotlin e do Flow, que apareciam como APIs de callback pouco práticas. O SKIE, da Touchlab, resolve isso gerando Swift idiomático: suspend vira um async/await nativo e Flow vira um AsyncSequence.

iosApp/ContentView.swiftswift
import SwiftUI
import Shared   // the KMP framework, baseName = "Shared"

struct ContentView: View {
    @State private var questions: [Question] = []
    private let repository = InterviewRepository()

    var body: some View {
        List(questions, id: \.id) { question in
            Text(question.prompt)
        }
        .task {
            // With SKIE, the Kotlin suspend fun is a Swift async fun
            questions = (try? await repository.fetchQuestions(tech: "android")) ?? []
        }
    }
}

A equipe de iOS escreve Swift e SwiftUI comuns. Ela consome os modelos e repositórios compartilhados exatamente como faria com um pacote Swift nativo, o que mantém o KMP invisível para a maior parte do código de iOS.

Duas arestas ainda valem menção em uma entrevista. Primeiro, os tempos de build: a compilação do Kotlin/Native é mais lenta que um build Swift puro, embora o cache de build do Gradle e a distribuição de um XCFramework pré-compilado suavizem o custo diário. Segundo, a depuração na fronteira Swift-Kotlin está melhorando, mas ainda não é tão fluida quanto permanecer em uma só linguagem, mais um motivo para as equipes manterem a superfície compartilhada deliberada em vez de espalhada.

Perguntas de entrevista sobre Kotlin Multiplatform que se pode esperar

Como as perguntas de entrevista sobre KMP aparecem cada vez mais em vagas de Android e mobile, vale a pena revisar algumas recorrentes junto com uma preparação para entrevistas de Android mais ampla:

  • Em que expect/actual difere de uma interface? O expect/actual é resolvido em tempo de compilação para cada target, sem dispatch em tempo de execução, e pode dar suporte a uma classe, função, propriedade ou typealias. Uma interface é resolvida dinamicamente em tempo de execução. Usa-se expect/actual para as APIs de plataforma e interfaces para o polimorfismo dentro do código compartilhado.
  • Como o Kotlin/Native lida com memória e threads? Desde o Kotlin 1.7.20, o novo gerenciador de memória removeu o antigo modelo de congelamento de objetos, então o estado mutável compartilhado e as corrotinas funcionam entre threads quase como na JVM.
  • Um aplicativo existente pode adotar o KMP de forma incremental? Sim. O módulo compartilhado é entregue como um AAR para o Android e um XCFramework para o iOS, então um repositório ou funcionalidade pode migrar um de cada vez, sem reescrita.
  • Quando a interface deve permanecer nativa em vez de usar o Compose Multiplatform? Quando uma tela se apoia em uma UX específica da plataforma ou quando a equipe de iOS detém e prefere controlar a camada de apresentação.
Cadeia de ferramentas em 2026

Mirar no Kotlin 2.3 com o compilador K2, no Ktor 3.x para a rede, no SQLDelight para um banco de dados compartilhado e no Koin para a injeção de dependências. Para a interoperabilidade com iOS, adicionar o SKIE cedo: incorporá-lo depois, com a camada Swift já escrita, é bem mais penoso.

Conclusão

Em 2026, o Kotlin Multiplatform é uma forma pragmática de cortar a lógica duplicada entre Android e iOS sem abrir mão da interface nem do desempenho nativos:

  • Compartilhar a lógica que tem uma única fonte de verdade (modelos, rede, validação) e escolher interface nativa ou Compose tela por tela.
  • Usar expect/actual para as APIs de plataforma: a resolução ocorre em tempo de compilação, sem sobrecarga em tempo de execução.
  • Escrever a rede uma única vez com o Ktor e o kotlinx.serialization para que as correções cheguem às duas plataformas ao mesmo tempo.
  • Adotar de forma incremental por meio de um AAR e um XCFramework em vez de assumir uma reescrita.
  • Adicionar o SKIE desde o início para que o Swift consuma suspend e Flow como async/await e AsyncSequence nativos.

Comece a praticar!

Teste seus conhecimentos com nossos simuladores de entrevista e testes tecnicos.

Tags

#Kotlin Multiplatform
#KMP
#Android
#iOS
#Cross-platform

Compartilhar

Artigos relacionados