Ainda há profissionais de desenvolvimento iOS que acreditam que o principal objetivo do Swift é tornar o Objective-C obsoleto, ambas sendo linguagens de programação do iOS. Porém, não é bem assim. Por isso, nesse artigo vamos ver como realizar a interoperabilidade entre Swift e Objective-C.
Breve resumo sobre Objective-C e Swift
A linguagem de programação Objective-C foi criada nos anos 80 por Brad Cox na empresa StepStone Corporation. Quando Stepstone foi encerrada em 1994, todos os direitos do Objective-C foram adquiridos pela NeXT. Isso acabou sendo transferido mais uma vez quando a NeXT foi adquirida pela Apple em 1997. Com isso o Objective-C se tornou a base para as plataformas MacOS da Apple e iOS.
Mais tarde, em 2014, a Apple anunciou sua nova linguagem de programação, Swift. Ela foi desenvolvida com o intuito de modernizar, facilitar e melhorar a usabilidade para profissionais de desenvolvimento iOS.
Por mais que pareça que o Objective-C já está se aposentando, em alguns casos ele ainda se faz necessário. Principalmente se tratando de acesso e controle de recursos de baixo nível e é exatamente por isso que Swift e Objective-C ainda devem coexistir por bastante tempo.
Por isso, nesse artigo vamos entender como interoperabilizar códigos Swift com códigos Objective-C.
O que é interoperabilidade e por que utilizar?
Interoperabilidade é a capacidade de um sistema se comunicar de forma transparente com outro sistema. No nosso contexto, trabalharemos interoperabilidade como sendo a capacidade de uma linguagem de programação trabalhar em conjunto com outra linguagem de programação.
Em outras palavras, você pode usar códigos escritos em Swift e Objective-C juntos em um único projeto.
Imagine que você criou uma Software Development Kit (SDK) que possui funções ou serviços originalmente escritos em Swift e agora surgiu uma demanda específica onde será necessário utilizar recursos de Objective-C. Se nesse caso não houvesse interoperabilidade entre as linguagens, teríamos que reescrever toda base de código originalmente criada em Swift para Objective-C. Já imaginou o retrabalho que isso daria?
Mas como esse não é o caso, precisaremos apenas alterar alguns detalhes no nosso código Swift para que seja possível se comunicar com códigos em Objective-C. Pode até parecer algo ultrapassado e muito específico mas, existem ainda muitos projetos que utilizam a linguagem Objective-C.
Use classes quando precisar de interoperabilidade com Objective-C
Lembre-se de usar classes no Swift para que seja possível fazer interoperabilidade no seu código.
Seguindo a documentação da Apple, não é possível expor Strucs e variáveis globais em Objective-C. Porém, existem algumas formas de utilizá-los, mas muito cuidado para não gerar um “code smell”, ou seja, podemos criar uma classe pública estendendo de NSObject na SDK Swift, criando métodos que retorne os valores da Strucs ou Enum. Iremos ver um exemplo claro mas a frente deste post.
Exatamente por isso não vamos nos aprofundar no assunto neste momento, mas é muito importante se lembrar disto.
Qual a diferença entre @Objc e @objcMembers?
Antes de começar, vamos entender um pouco o uso entre @objc e @objcMembers.
Por padrão, um projeto Swift gera códigos que estão disponíveis apenas para códigos Swift.
Neste caso, vamos precisar interagir em tempo de execução com Objective-C e vamos precisar dizer para o Swift quais funções ou classes necessitam ser expostas em Objective-C.
Para expor apenas um método ou propriedade, iremos utilizar o @objc antes da criação da sua função, como no exemplo abaixo:
Embed exemplo:
Caso seja necessário que todos os métodos sejam expostos a Objective-C, basta colocar o atalho @objcMembers antes de criar a classe, como no exemplo abaixo:
Embed exemplo:
O método foo() será automaticamente exposto em Objective-C, da mesma forma como se tivesse sido marcado com @objc. Assim todos os métodos declarados nestas classes são um @objc, porque declaramos a classe como @objcMembers em sua criação.
Como compartilhar códigos entre Swift e Objective-C
Vamos trabalhar com um exemplo simples, a complexidade da sua SDK não é relevante, queremos apenas nos concentrar na interoperabilidade. Para isso, iremos usar uma SDK chamada ‘CalculatorPod’, nela existem apenas dois métodos, um realiza a soma de dois números e a outra realiza a subtração de dois números. Como podemos ver no exemplo abaixo:
Para utilizar a SDK em um projeto Swift, basta importar e acessar a classe. Assim é possível acessar os nossos métodos, pois ambos foram declarados como públicos. Podemos ver no exemplo abaixo do uso da SDK no projeto sample:
Criamos duas funções showSome() e showSubtract():
• showSome() irá executar a soma e exibirá o resultado no log.
• showSubtract() irá executar a subtração e exibirá o resultado no log.
Como utilizar a SDK escrita em Swift no projeto em Objective-C
E agora, como utilizar a SDK no nosso projeto Objective-C sendo que ela está escrita em Swift? Para que isso funcione, faremos alguns ajustes na nossa SDK para conseguir acessar todas as funcionalidades igual usamos no Swift.
Tente importar a SDK para o novo projeto que usa como padrão a linguagem Objective-C.
Abaixo segue o exemplo da tentativa de importar a SDK e de executar os métodos que temos para exibir no Log como fizemos no exemplo anterior:
O Xcode nos alerta dizendo que o identificador Calculator (nome da nossa classe) não está declarado, ou seja, ainda não temos acesso a classe Calculator. Para que ela fique acessível precisamos estender a classe Calculator a NSObject, como no exemplo abaixo:
Mas isso não é o suficiente para que possamos acessar os métodos. Nesse passo estamos deixando apenas a classe Calculator acessível, pois agora, estendendo-se de NSObject conseguimos fazer com que a classe seja interpretada pelo Objective-C.
Mas e para acessar os métodos, como vamos fazer?
Isso também é algo simples de se resolver. Lembra que falamos sobre a diferença entre @objc e @objcMembers? É agora que iremos utilizá-los.
Neste caso veremos o @objcMember que deixa nossos métodos acessíveis ao Objective-C, como no exemplo abaixo:
Ao executar nosso projeto em Objective-C podemos ver que ele acessou a classe e executou o método trazendo o resultado no log. A interoperabilidade entre Swift e Objective-C não é muito complexa e ajuda quando houver a necessidade de mesclar as duas linguagens.
Na nossa SDK a linguagem principal continua em Swift apenas demos o acesso para que o Objective-C saiba interpretar nossos métodos e trazer o mesmo resultado para ambas linguagens.
Interoperabilidade com enum
Vimos como acessar as classes e métodos, mas e se caso eu tiver um enum que retorna uma String? Como vamos acessar os valores no Objective-C?
Neste exemplo usaremos um enum customizado, caso queira se aprofundar mais ao assunto indico que leia este artigo: enum customizados em Swift.
Podemos ver o exemplo do nosso enum, que simplesmente retorna uma mensagem se está somando ou subtraindo os valores:
Nesse projeto Swift ficaria algo parecido com o exemplo abaixo:
Podemos ver no log que as mensagens foram impressas quando chamamos o CalculatorEnum.some.rawValue ou CalculatorEnum.substract.rawValue.
Vamos aplicar a interoperabilidade no enum que é bem parecido com o exemplo que vimos anteriormente.
Adicionando o @objc no enum, logo o Xcode irá nos alertar que o @objc deve declarar um tipo primitivo inteiro. Para o Objective-C, um enum não pode ser genérico ou usar valores associados. Como os enums do Objective-C não tem espaço de nome, os casos de enum são importados para o Objective-C, como a concatenação do nome do enum e do nome do caso. Seria algo parecido como CalculatorEnumSome ou CalculatorEnumSubstract.
Mas queremos que ele nos retorne a mensagem de valor que ele possui para ter o resultado igual do nosso projeto Swift. O nosso enum ficará como o exemplo abaixo:
Adicionamos o @objc no nosso enum e demos um tipo Int para que o Objective-C consiga acessar os valores. No objective-C só conseguimos acessar o enum do tipo Int, mas isso irá retornar nossa mensagem do rawValue?
Ainda não, no caso ele apenas irá retornar o valor inteiro do nosso enum. Para exibir a mensagem vamos criar uma classe que estenda o NSObject, logo usaremos novamente o @objcMembers para criar dois métodos que seriam SomeMsg() e SubstractMsg() e retornará NSString.
Veja o exemplo abaixo como deveria ficar nossa nova classe.
Agora vamos implementar isso em nosso projeto em Objective-C e o retorno deverá ser igual ao do nosso projeto em Swift. Nosso projeto deve estar parecido com o exemplo abaixo:
Interoperabilidade entre Swift e Objective-C: mais praticidade para o seu dia a dia
Neste artigo vimos como podemos fazer a interoperabilidade entre a linguagem de programação Swift e Objective-C. Além disso, entendemos a importância de usar classes no Swift, pois o Objective-C não pode expor struc, nem variáveis globais.
Entendemos @objc e @objcMembers. Se quisermos dar acesso a todos valores da classe podemos usar o @objcMembers. Agora, caso for necessário acessar um único valor ou método da classe, usaremos o @objc.
Outro ponto importante é que para que o Objective-C tenha acesso a sua classe, ela deve se estender a NSObject.
E por fim, caso haja a necessidade de usar enum, vimos como utilizar ele e conseguir imprimir a mensagem de valor necessários.
Bacana, não?
Referências