SPM: modularização do projeto para aumentar a velocidade de construção

Olá, Habr! Meu nome é Eric Basargin e sou desenvolvedor iOS na Surf .



Em um grande projeto, encontramos uma velocidade de construção baixa - de três minutos ou mais. Normalmente, nesses casos, os estúdios praticam projetos de modularização para não trabalhar com monólitos enormes. Nós da Surf decidimos experimentar e modularizar o projeto usando o Swift Package Manager - o gerenciador de dependências da Apple.



Falaremos dos resultados em outro artigo, e agora responderemos às principais perguntas: por que tudo isso é necessário, por que escolhemos o SPM e como demos os primeiros passos.







Por que SPM



A resposta é simples - é nativa e nova. Ele não cria overheads do xcworkspace, como o Cocoapods, por exemplo. Além disso, o SPM é um projeto de código aberto em desenvolvimento ativo. A Apple e a comunidade estão corrigindo bugs, corrigindo vulnerabilidades, atualizando para seguir o Swift.



Isso torna a construção do projeto mais rápida



Teoricamente, a montagem será acelerada pelo próprio fato de dividir a aplicação em módulos - frameworks. Isso significa que cada módulo só será criado quando alterações forem feitas nele. Mas será possível afirmar com certeza apenas no final do experimento.



Nota: A eficácia da modularização depende diretamente da divisão correta do projeto em módulos.



Como fazer um particionamento eficiente



O método de divisão depende da arquitetura escolhida, do tipo de aplicativo, de seu tamanho e dos planos de desenvolvimento posterior. Portanto, falarei sobre três regras que tentamos seguir quando nos separamos.



Compartilhe funcionalidades genéricas. Cada módulo é responsável por uma categoria, por exemplo:



  • CommonAssets - um conjunto de seus ativos e uma interface pública para acessá-los. Geralmente é gerado usando SwiftGen.
  • CommonExtensions - um conjunto de extensões, por exemplo Foundation, UIKit, dependências adicionais.


Fluxos de aplicativos separados. Considere uma estrutura de árvore, onde MainFlow é o fluxo principal do aplicativo. Digamos que temos um aplicativo de notícias.



  • NewFlow - telas de notícias e visão geral de notícias específicas.
  • FavoritesFlow — .
  • SettingsFlow — , , . .


reusable :



  • CommonUIComponents — , UI-. .
  • UI . , , , . .




Digamos que temos um fluxo específico e queremos adicionar novas funções a ele. Se o componente for reutilizado ou se for teoricamente possível no futuro, é melhor movê-lo para um módulo separado ou um análogo de CommonUIComponents. Em outros casos, você pode deixar o componente local.



Essa abordagem resolve o problema de componentes ausentes. Isso acontece em grandes projetos e, se o componente não for documentado, sua manutenção e depuração se tornarão subseqüentemente não lucrativas.



Crie um projeto usando SPM



Considere a criação de um projeto de teste trivial. Estou usando o projeto Multiplatform App no ​​SwiftUI. A plataforma e a interface são irrelevantes aqui.



Nota: Para criar rapidamente um aplicativo multiplataforma, você precisa do XCode 12.2 beta. Vamos criar um



projeto e ver o seguinte:







Agora vamos criar o primeiro módulo Comum:



  • adicione a pasta Frameworks sem criar um diretório;
  • crie um pacote SPM comum.






  • Adicione as plataformas suportadas ao arquivo Package.swift. Nós temos issoplatforms: [.iOS(.v14), .macOS(.v10_15)]






  • Agora adicionamos nosso módulo a cada destino. Temos este SPMExampleProject para iOS e SPMExampleProject para macOS.






Nota: É suficiente conectar apenas módulos raiz aos destinos. Eles não são adicionados como submódulos.



A conexão está completa. Agora tudo que você precisa fazer é configurar um módulo com uma interface pública - e voila, o primeiro módulo está pronto.



Como adicionar uma dependência para um pacote SPM local



Vamos adicionar o pacote AdditionalInfo - como Common, mas sem adicioná-lo aos destinos. Agora vamos mudar o Package.swift do pacote Common.







Você não precisa adicionar mais nada. Pode ser usado.



Um exemplo próximo da realidade



Vamos conectar o SwiftGen ao nosso projeto de teste e adicionar o módulo Paleta - ele será responsável por acessar a paleta de cores aprovada pelo designer.



  1. Crie um novo módulo raiz seguindo as instruções acima.

  2. Adicione os diretórios raiz de scripts e modelos a ele.

  3. Adicione o arquivo Palette.xcassets à raiz do módulo e anote quaisquer conjuntos de cores.

  4. Adicione um arquivo Palette.swift vazio a Sources / Palette.

  5. Adicione o modelo palette.stencil à pasta Modelos .

  6. Agora você precisa registrar o arquivo de configuração do SwiftGen. Para fazer isso, adicione o arquivo swiftgen.yml à pasta Scripts e escreva o seguinte nela:



xcassets:
  inputs:
    - ${SRCROOT}/Palette/Sources/Palette/Palette.xcassets
  outputs:
    - templatePath: ${SRCROOT}/Palette/Templates/palette.stencil
      params:
        bundle: .module
        publicAccess: true
      output: ${SRCROOT}/Palette/Sources/Palette/Palette.swift




A aparência final do



módulo Paleta Configuramos o módulo Paleta . Agora você precisa configurar o lançamento do SwiftGen para que a paleta seja gerada no início da construção. Para fazer isso, vá para a configuração de cada destino e crie uma nova fase de construção - vamos chamá-la de gerador de paleta. Não se esqueça de mover esta fase de construção para a posição mais alta possível.



Agora escrevemos a chamada para o SwiftGen:



cd ${SRCROOT}/Palette
/usr/bin/xcrun --sdk macosx swift run -c release swiftgen config run --config ./Scripts/swiftgen.yml


Nota: /usr/bin/xcrun --sdk macosxé um prefixo muito importante. Sem ele, a construção irá gerar um erro: "não foi possível carregar a biblioteca padrão para o destino 'x86_64-apple-macosx10.15".





Exemplo de chamada para SwiftGen



Done - as cores podem ser acessadas assim:

Palette.myGreen(tipo de cor em SwiftUI) e PaletteCore.myGreen(UIColor / NSColor).



Rochas subaquáticas



Vou listar o que encontramos.



  • .
  • SwiftLint & SwiftGen SPM. yml.
  • Cocoapods. — , SPM . SPM Cocoapods - : «MergeSwiftModule failed with a nonzero exit code». , .
  • SPM . -L$(BUILD_DIR).


SPM — Bundler?



Nesse sentido, proponho sonhar e discutir nos comentários. O tema precisa ser bem estudado, mas parece muito interessante. A propósito, já existe um artigo bastante interessante sobre os prós e os contras do SPM.



O SPM oferece a capacidade de chamar o swift run adicionando Package.swift à raiz do projeto. O que isso nos dá? Por exemplo, você pode chamar fastlane ou swiftlint. Exemplo de chamada:



swift run swiftlint --autocorrect.



All Articles