
Um dos desafios que encontramos foi a otimização da utilização de componentes para desenvolvimento, sejam eles internos ou de terceiros. Aparentemente uma tarefas simples, mas pode se tornar um pesadelo se não for feito de maneira organizada.
Neste post descreveremos a experiência que tivemos, os prós e contras de cada tipo de distribuição, tanto para criação quanto para uso, e como estamos trabalhando atualmente.
O objetivo principal da utilização de componentes é o reuso de código, agilizando assim o desenvolvimento de projetos. Nossa maior preocupação aqui é com relação ao controle de versões, para que não hajam componentes modificados em projetos específicos, resolver a inter-dependência entre bibliotecas e facilitar e simplificar o processo de adição de componentes no projeto.
Em geral as bibliotecas para iOS são disponibilizadas em forma de código-fonte, static library ou static framework. Descreveremos a seguir cada uma delas.
Código-fonte
Basta adicionar os arquivos do código-fonte ao projeto e compilá-los como se pertencessem ao projeto principal.
Prós
- - facilidade de utilização;
- - facilidade de criação;
- - facilidade de depuração.
Contras
- - dificuldade no controle de versão;
- - separação entre componente e projeto;
- - edição do componente dentro do projeto;
- - inter-dependência entre bibliotecas sob responsabilidade do usuário.
Apesar de ser simples criar e utilizar componentes dessa forma, é difícil saber qual versão do componente estamos utilizando. Além disso todo o código-fonte do componente é adicionado ao projeto sendo de responsabilidade do usuário organizar e separar o código “externo”.
Outro problema deste tipo de distribuição é a alteração do código-fonte do componente pelo usuário. Caso isso aconteça e não seja identificado, a atualização do componente será comprometida.
Library
São bibliotecas disponibilizadas já compiladas. Possui a biblioteca (arquivo .a), headers (arquivos .h) e, em alguns casos, alguns resources.
Por serem compiladas sua distribuição não permite que o usuário tenha acesso ao código-fonte da biblioteca e não precisam ser compiladas juntamente com o projeto principal, diminuindo o tempo de compilação.
Prós
- - facilidade de utilização;
- - separação entre componente e projeto.
Contras
- - bug com Categories;
- - requer mudança na configuração do projeto;
- - inter-dependência entre bibliotecas sob responsabilidade do usuário;
- - criação do pacote para distribuição;
- - dificuldade de depuração.
Quando a Static Library utiliza Categories é necessário adicionar o argumento -ObjC na configuração “Other Linker Flags” do projeto (mais informações aqui) para que as mesmas sejam carregadas no Linker. No entanto há um bug conhecido no iOS e em aplicações 64 bits que faz com que Categories sem Classe não sejam carregadas no Linker. Há um workaround que consiste em utilizar a flag -all_load juntamente com a -ObjC, fazendo com que o Linker carregue todos os arquivos.
As dependências da Static Library devem ser adicionadas pelo usuário ou incluídas na própria Static Library, porém incluí-las no componente não é uma boa opção, pois além de ficarem limitadas a uma versão específica da dependência, podemos ter problemas com símbolos duplicados (devido ao uso das flags -ObjC e -all_load).
Ao final da criação do componente, para cada versão, devemos criar um pacote com a library (arquivo .a), todos os headers públicos e todos os resources.
Static Framework
Infelizmente no Cocoa Touch não é possível criar Dynamic Frameworks. Somente a Apple pode criar esse tipo de framework como por exemplo o UIKit e o Foundation. No entanto há a possibilidade de criar-se Static Frameworks que nada mais são do que Static Libraries com uma estrutura de diretórios com headers e resources.
Não há templates para criação de Static Frameworks para iOS disponível nativamente no XCode, porém há um projeto no github que disponibiliza scripts que habilitam esta opção.
Prós
- - facilidade de utilização;
- - separação entre componente e projeto;
- - criação do pacote para distribuição.
Contras
- - bug com Categories;
- - requer mudança na configuração do projeto;
- - inter-dependência entre bibliotecas sob responsabilidade do usuário;
- - dificuldade de depuração;
- - necessidade de adicionar templates no XCode.
Esta alternativa possui as mesmas vantagens e problemas da Static Library, porém facilita a criação do componente para distribuição, pois o template para Static Framework já cria a estrutura de diretórios com os headers e resources do projeto.
Dentre as alternativas mostradas acima ainda não tínhamos uma que resolvesse de forma simples a inter-dependência entre bibliotecas e que fosse “plug-and-play” para o usuário até que descobrimos o CocoaPods.
CocoaPods
O CocoaPods é um projeto Open source de um gerenciador de dependências de bibliotecas para Objective-C assim como o RubyGems para Ruby. Ele tem como objetivo simplificar a adição de dependências em projetos e centralizar as bibliotecas disponíveis atualmente.
Com o CocoaPods basta o usuário descrever quais são as dependências do seu projeto e executar o comando para que o gerenciador busque as bibliotecas e suas dependências e instale-as no projeto. Com isso as dependências entre bibliotecas não ficam mais sob responsabilidade do usuário e podemos manter os componentes atualizados com maior facilidade.
Sua instalação é simples: basta instalar a Gem cocoapods para poder utilizar a ferramenta. Mais detalhes podem ser vistos na página do projeto no github.
A criação de componentes (Pods) também é simples: basta que ele esteja em algum servidor git ou svn e criar um arquivo com suas especificações. É possível deixar o componente com o código aberto ou disponibilizá-lo como uma Static Library.
Podemos citar alguns prós e contras do CocoaPods:
Prós
- - controle de versão;
- - controle de inter-dependência de bibliotecas;
- - facilidade de uso;
- - separação entre projeto principal e componentes;
- - facilidade de depuração;
Contras
- - projeto recente com alguns bugs;
- - muitas bibliotecas ainda não disponíveis.
O CocoaPods ainda possui alguns bugs, pois trata-se de um projeto recente. Por esse motivo, ainda não foram criadas specs de muitas libraries disponíveis para Objective-C, mas qualquer desenvolvedor pode criá-las e contribuir com o desenvolvimento do projeto. Após a aceitação da sua primeira spec é dado o acesso de escrita ao repositório para que você possa contribuir mais rapidamente.
Atualmente estamos utilizando o CocoaPods como gerenciador de dependências dos projetos com um repositório de specs modificado para nossos componentes internos, mas isso é assunto para um outro post.
Este post também possui uma versão em ingles – “How to – Organizing and versioning your iOS Components”