Interoperabilidade = A capacidade de sistemas distintos conversarem e compartilhar dados.

Talvez isso seja óbvio para alguns devs mobile, mas para quem desenvolve projetos que utilizam Kotlin Multiplatform e ainda não deu a devida atenção e realmente parou para ler sobre a interoperabilidade entre Kotlin e Swift, esse post é minha chance de te convencer a dominar esse tema.

No desenvolvimento com Kotlin Multiplatform, dominar a interoperabilidade do Kotlin com Swift é uma peça chave pra não gerar problemas em produção.

Tudo que vai no commonMain vale pro Android e iOS, portanto nós temos como expectativa que o código seja essencialmente a mesma coisa entre as plataformas: tenha as mesmas funções, classes e se comporte igual.

Esse compartilhamento de código entre a commonMain e código Swift é totalmente transparente para nós devs, tudo que eu escrevo em um aparece “igual” no outro… e após várias iterações disso é fácil esquecer que o código do commonMain passa por uma “etapa intermediária”, onde pode sofrer mudanças para que o Swift consiga entender nativamente.

De uma forma simplista, o código que o Swift vê é o produto da soma commonMain + iosMain + um processo de multiplas compilações, e não somente commonMain + iosMain.

Esse “processo de multiplas compilações” ou essa “etapa intermediária” que eu disse foi apenas uma forma de resumir, se você tiver interesse em como isso funciona vou deixar um link que dá explicações e detalhes técnicos sobre isso:

Quantas vezes você já fez um código que no Kotlin ficou belíssimo, mas quando você abriu o XCode notou que esse mesmo código tava péssimo pra usar? Ou até mesmo tinha coisas a mais nele que você não desenvolveu? Ou coisas faltando… “Cadê os parametros default que configurei??”

Vou te falar um bug que eu causei em produção por não ter conhecimento 100% de como meu código Kotlin ficava para o Swift.

Para quem não manja de swift, quando você faz qualquer código que pode gerar uma exception você é obrigado a declarar isso com uma palavra chave no código: throws. Se sua função tem essa palavra chave, quem chamar ela também é obrigado a tratar senão o projeto não compila.

func canThrowErrors() throws -> String

func cannotThrowErrors() -> String

Tem cenários onde você sabe que não vai gerar uma exception, como por exemplo carregar uma imagem local… Nesse caso voce ainda precisa manualmente desabilitar a propagação do erro.

let photo = try! loadImage(atPath: "./Resources/John Appleseed.jpg")

link para o restante da documentação da apple sobre isso

Qualquer exception lançada onde não está explícito que pode gerar uma exception (ou seja, não está marcada por throws) faz com que o app feche imediatamente. Mesmo quando ela estiver dentro de um do-catch (vulgo try-catch)!!! Esse é o comportamento padrão.

Eu sabia que existia uma annotation chamada @Throws, mas meu pensamento era: “poxa, se eu chamo meu código que vem do Kotlin dentro de um try catch no Swift, ele ja vai ser capturado e tratado - não tem necessidade de colocar essa annotation no Kotlin”.

Era uma exception esperada, ela de fato poderia acontecer eventualmente e ta tudo bem, inclusive foi pensando nisso que eu coloquei o try-catch. No final das contas minha função gerava essa exception em um cenário específico, e justamente por lançar uma exception que o Swift entendeu como inesperada, o aplicativo encerrava pois pra ele foi um erro fatal.

Você pode argumentar que eu só pensava assim pois tenho mais experiência em linguagens que o try-catch pode capturar todos os erros, não pela falta de conhecimento da interoperabilidade em si. E olha, você provavelmente está certo.

Mas num olhar pragmático, KMP para devs mobile que já trabalhavam na área cai EXATAMENTE nesse mesmo cenário: temos uma bagagem/conhecimentos de outras linguagens que acabamos trazendo junto, e isso de um lado é ótimo, mas se não tivermos cuidado isso pode nos sabotar (vide evidência do bug que eu causei).

A realidade é que:

as plataformas/linguagens que se beneficiam com nosso código KMP tem demandas específicas, que ao subestimadas eventualmente vão trazer dores de cabeça para devs e consequentemente para os usuários finais.

Eu poderia simplesmente sair estudando Swift e todas suas particularidades, isso certamente iria fazer diferença para esse erro que comentei mais acima do post. Porém esse caminho apesar de funcionar acaba sendo bem ineficiente.

Se meu objetivo é melhorar o conhecimento e habilitade em desenvolver códigos multiplataforma, estudar uma linguagem inteira seria o equivalente a atirar para todos os lados, eu vou acertar o alvo, mas gastarei toda minha munição no processo.

Seria excelente se tivesse uma documentação ou algo que me alertasse sobre essa particularidade do Swift e ainda me da uma solução? É ai que entra a documentação de interoperabilidade com Swift/ObjC.

Olha aqui ela dizendo exatamente sobre o problema que eu tive e como resolver ele

Se você tem interesse em evoluir as habilidades em KMP, fica como minha sugestão: Estude a interoperabilidade entre as plataformas. Com esse conhecimento você vai saber o que deve evitar no seu código multiplataforma para não causar bugs em produção igual eu fiz.