O Cocoapods é uma plataforma de gestão de depêndencias para projetos feitos em Cocoa e Cocoa Touch (basicamente OSX e iOS), equivalente ao NuGet para a Microsoft/Visual Studio e o Maven no Android. Criada e mantida pela comunidade, ela tornou-se tão fundamental e importante que dedicamos um módulo exclusivamente a ela em nosso treinamento Introdutório de iOS+Swfit.
Quando a Apple introduziu a linguagem Swift em junho de 2014, ela apresentou uma oportunidade única para os desenvolvedores de componentes, ao trazer recursos de linguagem que permitiam produzir bibliotecas de uso mais simples e conveniente, como o CoreDataContext que eu criei para trabalhar com o Core Data e que com o uso de Generics tornou o manuseio das entidades de dados muito mais simples, ou o AlamoFire, um componente para comunicação Web feito pelos mesmos criadores do AFNetworking, porém totalmente pensando na perspectiva da linguagem Swift.
Um mundo novo de possibilidades se abria, porém como todo novo recurso que surge, ele enfrenta algumas dificuldades em lidar com o legado. Componentes que tivessem código Swift não eram suportados até pouco tempo atrás pelo Cocoapods, e mesmo a inclusão desse suporte tem suas limitações. Nesse artigo vamos entender um pouco melhor sobre isso.
Apresentando o App Countries
Nesse artigo vamos usar como base o App Countries, criado pela Ravero para exemplificar a comunicação com Web Services em iOS, ele apresenta uma listagem dos países do mundo obtidas através de um Web Service público.
O código do App esta disponível neste repositório do GitHub. O App utiliza 3 bibliotecas:
- MBProgressHUD: componente muito popular para apresentar um indicador de status e inibir o input do usuário.
- SBWebImage: componente para carregar um UIImageView com uma imagem baixada pela Internet de maneira assíncrona, ou seja, sem congelar o App.
- SwiftyJSON: um parser de JSON criado totalmente em Swift e tirando proveito de todos os recursos da linguagem.
No projeto original os 2 primeiros componentes são integrados usando o CocoaPods, enquanto o terceiro é inserido manualmente.
O problema
Para entender as limitações que o Cocoapods tem com o Swift, é necessário compreender um pouco da sua arquitetura. Quando incluímos um componente (ou Pod) em nosso projeto, o Cocoapods baixa o código desse componente e de todas as suas dependências. Esses códigos são agrupados em um projeto separado, que é compilado em uma Static Library e depois incorporado ao projeto.
No App de exemplo temos os Pods dos dois componentes citados anteriormente, e abrindo o Project Navigator podemos verificar o projeto específico para eles e sua estrutura:
Abra o as configurações do projeto e selecione o Target do App. Na aba General no final da página podemos verificar uma seção chamada Linked Frameworks and Libraries. Lá nós podemos incluir bibliotecas para serem utilizadas pelo App. Note a existência de um arquivo com o nome libPods-Countries.a. Essa é a Static Library que o CocoaPods gera automaticamente e que contém todos os componentes que incluímos no Podfile.
Dessa maneira o CocoaPods consegue incluir os códigos dos componentes sem bagunçar com o seu projeto. Esse mecanismo é muito eficiente não fosse um pequeno detalhe: Códigos em Swift não podem ser compilados em Static Libraries. Entram os Frameworks…
Static Libraries vs. Cocoa Touch Frameworks
Vamos entender um pouco sobre o que são e quais as diferenças entre esses recursos disponíveis no SDK da Apple:
Static Libraries ou bibliotecas estáticas, é um dos mecanismos mais antigos de re-aproveitando de código, datando da década de 70 quando a linguagem C era uma das mais populares. Quando você usa uma biblioteca estática em seu projeto, o que o compilador faz é incorpoar seu código binário no próprio binário do App. Isso faz com que o código do seu App fique maior, e tende a ter um maior tempo de carga e utilziação de memória. Sua principal restrição é não suportar que códigos escritos em Swift sejam compilados nesse formato.
Cocoa Touch Frameworks são bibliotecas dinâmicas (ou DLL’s: Dynamic Link Libaries). Diferente da anterior, o código de um Framework é carregado dinamicamente durante a execução de um programa e somente quando necessário. Apesar de existirem no Mac a muito tempo só foram introduzidos ao iOS a partir da versão 8, e são fundamentais para suportar recursos como as Extensões, e deve ser usado sempre que possível no lugar das Static Libraries a partir de agora, já que são mais modernos e eficientes. Sua principal restrição é não ser suportada em versões anteriores do iOS.
A Solução
Com base no problema, a solução mais simples que o pessoal do CocoaPods chegou foi, usar Frameworks no lugar de Static Libraries. Vamos ver como isso funciona na prática. No nosso projeto exemplo vamos substituir a inclusão manual que fizemos do componente SwiftyJSON pela integração via CocoaPods.
Se desejar refazer os passos desse exemplo, baixe o repositório e de um Checkout na Tag pre-cocoapods.
Esse componente encontra-se no grupo do projeto Libs. Exclua esse grupo do projeto. Ao tentar compilar novamente o Xcode apresentará um erro, já que não encontra mais a classe da biblioteca.
O suporte para Pods com código em Swift esta disponível no Cocoapods a partir da versão 0.36. Para manter o Cocoapods atualizado com a versão mais recente utilize o comando sudo gem install cocoapods.
Vamos atualizar o Podfile do projeto para incluir o SwiftyJSON:
# Uncomment this line to define a global platform for your project
# platform :ios, ‘8.0’
target 'Countries' do
pod 'MBProgressHUD', '~> 0.8'
pod 'SDWebImage', '~> 3.7'
pod 'SwiftyJSON', '~> 2.1'
end
E execute o comando pod install para atualizar os componentes. Veja que recebemos o seguinte erro:
Analyzing dependencies
Downloading dependencies
Using MBProgressHUD (0.9)
Using SDWebImage (3.7.1)
Installing SwiftyJSON (2.1.3)
[!] Pods written in Swift can only be integrated as frameworks; this feature is still in beta. Add `use_frameworks!` to your Podfile or target to opt into using it.
Apesar do CocoaPods ter incluído o suporte a bibliotecas Swift, é necessário declararmos explicitamente o uso de Frameworks para inclui-las no target. Isso acontece porque os Cocoa Touch Frameworks só estão disponíveis a partir do iOS 8, dessa forma a inclusão de qualquer biblioteca que dependa desse recurso faz com que o projeto automaticamente se torne incompatível com o iOS 7.
Vamos atualizar o Podfile conforme as instruções para entender o que acontece:
# Uncomment this line to define a global platform for your project
platform :ios, '8.0'
use_frameworks!
target 'Countries' do
pod 'MBProgressHUD', '~> 0.8'
pod 'SDWebImage', '~> 3.7'
pod 'SwiftyJSON', '~> 2.1'
end
Executando novamente o comando pod install a instalação acontece normalmente. Note também que eu inclui a instruçãoplatform que estava comentado anteriormente, isso não é obrigatório mas recomendável. Abra o projeto e tente compilar o código.
Abra o projeto e verifique no Project Navigator como o SwiftyJSON foi incluido no projeto dos Pods.
Abra as propriedades do projeto, e verifique que no lugar da referência para libPods-Countries.a temos agoraPods_Countries.framework.
A referência foi substituída! Mas se tentarmos compilar o projeto continuamos com o mesmo erro. O que aconteceu? Quando usamos o Objective-C, antes de usar as bibliotecas era necessário importar o cabeçalho delas. Por conta dessa mecânica o Cocoapods inclua os cabeçalhos de todos os Pods do projeto para usarmos conforme a necessidade. Em Swift não existem arquivos de cabeçalho, mas então como usamos uma classe declarada em uma biblioteca?
Em Swift seu código em compilado em binários, chamados de módulos, que contém metadados descrevendo suas classes e funções públicas. De maneira semelhante aos cabeçalhos do Objective-C, é necessário usar o comando import para incluir um módulo em seu código fonte.
Uma coisa muito conveniente que o CocoaPods faz quando declaramos no Podfile o uso de Frameworks é, compilar cada componente em seu próprio módulo. Ué, mas na seção Linked Frameworks and Libraries? Trata-se apenas de umplaceholder. O segredo esta escondido nas configurações do projeto. Abra a aba Build Phases e verifique que foi criado uma fase chamada Copy Pods Resources, que executa um Shell Script:
Se tiver curiosidade pode abrir esse Script, ele é um pouco extenso mas o que ele faz em resumo é copiar cada um dos Frameworks gerados no projeto Pods. Abra as propriedades desse projeto, e verifique que além do Target placeholder há mais 1 target para cada pod instalado. Note como esses targets são do tipo Framework (é fácil visualizar pelo ícone amarelo).
Vamos finalmente atualizar o código do projeto para eliminar os erros. Abra o arquivo CountryList.swift e inclua no cabeçalho a seguinte declaração:
import SwiftyJSON
Isso resolve o error anterior, mas agora temos um novo erro.
O compilador esta reclamando não encontrar a classe MBProgressHUD. Ela faz parte do componente homônimo. Anteriormente ela ficava disponível em nosso código através do Bridging-Header, mas agora que ele esta incorporado ao projeto como um Framework, esse cabeçalho já não serve mais para encontrar a referência a classe contida no módulo. A correção é feita da mesma forma, no arquivo CountriesViewController.swift inclua a declaração no cabeçalho:
import MBProgressHUD
E por fim atualize o arquivo CountryDetailsViewController.swift. Ele faz uso do componente SDWebImage, então vamos importa-lo no cabeçalho:
import SDWebImage
Agora não precisamos mais do arquivo Bridging-Header, fique a vontade para excluí-lo se desejar. Compile e execute o App, e agora tudo deverá funcionar normalmente!
Atenção: Em alguns casos identifique ser necessário dar um clean ou até mesmo excluir a pasta DerivedData para que a compilação ocorresse normalmente após a inclusão de um Pod Swift.
Conclusão
Com o Cocoapods a partir da versão 0.36 é possível incluir Pods com código Swift em nossos projetos. Isso fará com que esses componentes sejam incorporados ao projeto com Frameworks ao invés de Static Libraries, entretanto perdemos o suporte para o iOS 7.* (lembrando que o próprio Swift não é suportado em versões do iOS anteriores a 7).
Dessa forma é importante verificar se os componentes que você vai utilizar contém códigos em Swift, e nesse caso você precisa avaliar a necessidade de suporte retroativo em seu projeto. Se não for necessário suportar o iOS 7, use esses componentes tranquilamente, do contrário será necessário incluir manualmente as bibliotecas em seu projeto.
fonte: http://ravero.net/2015/03/23/cocoapods-swift-s3/