Melhores práticas e ferramentas do projeto iOS

Com um modelo de projeto de código aberto do Xcode

Ao trabalhar em projetos greenfield para iOS, muitas vezes tive que iniciar um novo projeto do zero. Enquanto isso, eu e minha equipe sempre gastávamos muito tempo na configuração básica do projeto, como integrar ferramentas, configurar a estrutura do projeto, escrever classes básicas, integrar bibliotecas externas etc.

Decidi que o tempo gasto na inicialização do projeto poderia ser economizado e o processo, na maior parte, automatizado. Anotei todas as melhores práticas e ferramentas usuais que usamos e preparei um modelo de projeto que eu e minha equipe poderíamos usar ao iniciar novos projetos. Este modelo deve economizar tempo de configuração do projeto e também fornecer uma base comum com a qual cada membro da equipe estará acostumado, para que você não precise pensar e explorar a estrutura e os fundamentos do projeto. Eles sempre serão os mesmos.

Todas as ferramentas ou práticas recomendadas incluídas no modelo merecem um artigo por si só, mas eu queria tentar resumir cada ponto e dar uma breve explicação sobre por que os incluí.

Cocoapods

Eu não acho que este precise de uma introdução. Esta é a biblioteca para gerenciar dependências externas para projetos iOS. Ele já existe há muito tempo e é robusto e testado em milhares (se não milhões) de projetos. Existem gerenciadores de dependência alternativos por aí, como Carthage, mas eu decidi ir com o Cocoapods porque ele tem a maior variedade de projetos de código aberto que suporta. O uso do Cocoapods é muito fácil e vem com um índice de pesquisa que permite descobrir facilmente os pacotes necessários.

O projeto do modelo vem com um Podfile simples que inclui Swiftlint e R.swift. O modelo também inclui um Gemfile para gerenciar a versão do Cocoapods usada para resolver as dependências. Esse é um aprimoramento muitas vezes esquecido que evita problemas que surgem quando os desenvolvedores da sua equipe instalam dependências usando versões diferentes do próprio Cocoapods. O Gemfile impõe o uso da mesma versão do Cocoapods em toda a equipe.

Swiftlint

O Swiftlint é uma ferramenta muito útil para impor certas regras e estilo de codificação para todos os programadores da equipe. Você pode pensar nisso como um sistema automatizado de revisão de código que avisa o programador sobre coisas perigosas, como forçar a abertura, forçar lançamentos, forçar tentativas etc., mas também impõe um estilo de codificação comum, garantindo que todos os programadores sigam as mesmas regras relacionadas ao "estilo de código" como regras de recuo ou espaçamento. Isso traz enormes benefícios, além de poupar tempo de revisão de código, fazendo essas verificações básicas, mas também faz com que todos os arquivos do projeto pareçam familiares, o que aumenta sua legibilidade e, como resultado, sua compreensão por todos os desenvolvedores. Você pode encontrar uma lista de todas as regras aqui. No modelo, o Swiftlint é instalado via Cocoapods e incluído na etapa Fases de compilação, de modo que ele se conecta e avisa o desenvolvedor a cada compilação do projeto.

R.swift

O R.swift é uma ferramenta para obter recursos de preenchimento automático fortemente tipados, como imagens, fontes segues e localizações. Isso é feito varrendo seu projeto e gerando classes rápidas necessárias para obter os recursos. O maior ponto de venda dessa biblioteca é que, ao usar recursos, ele cria seu código:

  • Totalmente digitado - menos conversão e adivinhar o que um método retornará
  • Tempo de compilação verificado - não há mais seqüências incorretas que causam pane no aplicativo em tempo de execução
  • Preenchimento automático - nunca precise adivinhar o nome da imagem / ponta / storyboard novamente

Considere o seguinte código usando a API oficial da string:

deixe icon = UIImage (nomeado: “ícone personalizado”)

Se você digitar incorretamente o nome da imagem, receberá um valor zero aqui. Se algum membro da sua equipe alterar o nome do recurso de imagem, esse código retornará nulo ou falhará se você forçar a desembrulhar a imagem. Ao usar R.swift, isso se torna:

deixe icon = R.image.customIcon ()

Agora você pode ter certeza de que o ícone realmente existe (o compilador avisará se não houver, graças às verificações do tempo de compilação) e você está certo de que não fará um erro de digitação no nome do ícone, pois estará usando o preenchimento automático.

O R.swift é instalado via Cocoapods e integrado no modelo como uma Fase de Construção e gerará as classes de wrapper Swift em cada construção. Isso significa que se você adicionar um arquivo / imagem / localização / fonte / cor / ponta etc., ele estará disponível para você usando R.swift depois de compilar o projeto.

AppDelegate separado para testes

Uma boa prática frequentemente negligenciada é ter uma classe TestAppDelegate separada ao executar testes. Por que é uma boa ideia? Bem, geralmente a classe AppDelegate trabalha bastante na inicialização do aplicativo. Ele poderia configurar uma janela, criar a estrutura básica da interface do usuário do aplicativo, registrar-se para receber notificações, configurar um banco de dados e, às vezes, fazer chamadas de API para algum serviço de back-end. Os testes de unidade não devem ter efeitos colaterais. Você realmente não deseja fazer chamadas aleatórias da API e configurar toda a estrutura da interface do usuário do seu aplicativo apenas para executar alguns testes de unidade?

O TestAppDelegate também é um ótimo local para ter código que você deseja executar apenas uma vez durante a execução do conjunto de testes. Pode conter código que gera zombarias, stubs solicitações de rede etc.

O modelo contém um arquivo main.swift, que é o principal ponto de entrada do aplicativo. Esse arquivo possui métodos que verificam qual é o ambiente em que o aplicativo está sendo executado no momento e, se for o ambiente de teste, chama o TestAppDelegate.

Sinalizadores de perfil de desempenho do compilador

O Swift é um ótimo idioma, mais fácil de usar e muito mais seguro que o Objective-C (IMO). Mas quando foi introduzido pela primeira vez, houve uma grande desvantagem - os tempos de compilação. Nos dois dias do Swift, eu estava trabalhando em um projeto que possuía aproximadamente 40 mil linhas de código Swift (um projeto de tamanho médio). O código era muito pesado com genéricos e inferência de tipo e levou quase 5 minutos para compilar uma compilação limpa. Quando você fazia uma pequena alteração, o projeto era recompilado e levava cerca de 2 minutos para ver a alteração. Essa foi uma das piores experiências de desenvolvedor que já tive e quase parei de usar o Swift por causa disso.

A única solução naquela época era tentar criar um perfil dos tempos de compilação do projeto e alterar seu código de maneira a tornar o compilador mais rápido. Para ajudar, a Apple apresenta alguns sinalizadores não oficiais do compilador que avisariam quando a compilação de um corpo de método ou a resolução do tipo de expressão demorou muito. Eu adicionei esses sinalizadores ao projeto do modelo para que você seja avisado desde o início sobre longos tempos de compilação para seu aplicativo.

Atualmente, os tempos de compilação foram dramaticamente aprimorados e você raramente precisa ajustar seu código apenas para melhorar os tempos de compilação. Ainda assim, é melhor saber com antecedência do que tentar resolver o problema quando o projeto ficar grande demais.

Configurações de Dev / Staging / Production

Outra boa prática (ou, se necessário) é ter configurações e variáveis ​​de ambiente separadas para os ambientes de desenvolvimento, preparação e produção. Atualmente, quase todos os aplicativos precisam se conectar a algum tipo de serviço de back-end e, geralmente, esses serviços são implantados em vários ambientes. O ambiente de desenvolvimento é usado para implantações diárias e para os desenvolvedores testarem seu código. O ambiente de temporariedade é usado para liberações estáveis ​​para testadores e clientes testarem. Todos sabemos para que serve o ambiente de produção.

Uma maneira de oferecer suporte a vários ambientes em um projeto iOS é adicionar configurações no nível do projeto.

Configurações no nível do projeto

Depois de definir as configurações, você pode criar um arquivo Configuration.plist contendo as variáveis ​​para cada ambiente.

Configuration.plist

Ao executar o projeto, você pode especificar qual configuração deve ser usada. Você pode fazer isso no esquema de construção.

Você precisará adicionar uma propriedade adicional no arquivo Info.plist do projeto. O valor dessa propriedade será resolvido dinamicamente no tempo de execução com o nome da configuração atual.

Tudo isso está pré-configurado para você no modelo.

A única coisa que resta é escrever uma classe que possa recuperar essas variáveis ​​em tempo de execução, dependendo da configuração selecionada no esquema de construção. O modelo contém uma classe ConfigurationManager que pode recuperar as variáveis ​​para o ambiente atual. Você pode verificar a implementação dessa classe no Github para ver como ela funciona.

Leia-me

Todo projeto deve ter um Leia-me básico que tenha pelo menos instruções sobre como instalar dependências e executar o projeto. Também deve conter uma descrição da arquitetura e dos módulos do projeto. Infelizmente, os desenvolvedores não gostam de escrever documentação (um leia-me faz parte disso) e eu vi projetos que estavam sendo desenvolvidos há meses e eles tinham até um Leia-me básico. Para eliminar o ônus de escrever este leia-me básico, o modelo contém um leia-me padrão que abrange a instalação e a estrutura do projeto. Ao configurar um novo projeto usando o modelo, você terá o Leia-me incluído automaticamente.

Gitignore

Atualmente, a maioria dos projetos usa o GIT como seu sistema de controle de versão. Ao usar o GIT, você geralmente não deve ignorar alguns arquivos ou pastas no projeto, como a pasta de construção ou a pasta de dados derivada. Para poupar o trabalho de procurar um arquivo gitignore adequado ao seu projeto iOS, o modelo inclui um gitignore padrão fornecido pelos colaboradores do Github.

Classes base para lidar com links diretos e notificações

Atualmente, quase todos os aplicativos precisam lidar com ligações e notificações profundas. Para fazer isso, um desenvolvedor precisa escrever uma certa quantidade de código padrão na classe AppDelegate. O modelo abordou e também fornece classes de base que facilitam o trabalho com links de profundidade e notificações.

Sumário

Para resumir, o modelo tenta incluir práticas recomendadas e integra ferramentas úteis de terceiros. Isso poupará a você e à nossa equipe as horas gastas na configuração do novo projeto e também fornecerá uma base comum e sólida para o restante do projeto. Que lhe sirva bem!

PS: Se você tiver algum problema ou solicitação de recurso para o modelo, deixe-me um problema no Github. Vou tentar resolvê-lo no meu tempo livre.