SPM: modularización del proyecto para aumentar la velocidad de construcción

¡Hola, Habr! Mi nombre es Eric Basargin y soy desarrollador de iOS en Surf .



En un proyecto grande, encontramos una velocidad de construcción baja, de tres minutos o más. Por lo general, en tales casos, los estudios practican la modularización de proyectos para no trabajar con grandes monolitos. En Surf, decidimos experimentar y modularizar el proyecto utilizando Swift Package Manager, el administrador de dependencias de Apple.



Hablaremos de los resultados en otro artículo, pero ahora responderemos las principales preguntas: por qué es necesario todo esto, por qué elegimos SPM y cómo dimos los primeros pasos.







Por qué SPM



La respuesta es simple: es nativa y nueva. No crea gastos generales de xcworkspace como Cocoapods, por ejemplo. Además, SPM es un proyecto de código abierto que se está desarrollando activamente. Apple y la comunidad están arreglando errores, arreglando vulnerabilidades, actualizando para seguir a Swift.



¿Esto hace que la construcción del proyecto sea más rápida?



Teóricamente, el ensamblaje se acelerará debido al hecho mismo de dividir la aplicación en módulos: marcos. Esto significa que cada módulo solo se construirá cuando se le hayan realizado cambios. Pero será posible afirmarlo con seguridad solo al final del experimento.



Nota: La eficacia de la modularización depende directamente de la correcta división del proyecto en módulos.



Cómo hacer particiones eficientes



El método de partición depende de la arquitectura elegida, el tipo de aplicación, su tamaño y los planes para un mayor desarrollo. Por lo tanto, hablaré de tres reglas que tratamos de cumplir al dividir.



Comparta la funcionalidad genérica. Cada módulo es responsable de una categoría, por ejemplo:



  • CommonAssets: un conjunto de sus Activos y una interfaz pública para acceder a ellos. Por lo general, se genera con SwiftGen.
  • CommonExtensions: un conjunto de extensiones, por ejemplo Foundation, UIKit, dependencias adicionales.


Flujos de aplicación separados. Considere una estructura de árbol, donde MainFlow es el flujo principal de la aplicación. Digamos que tenemos una aplicación de noticias.



  • NewFlow: pantallas de noticias y resumen de noticias específicas.
  • FavoritesFlow — .
  • SettingsFlow — , , . .


reusable :



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




Digamos que tenemos un flujo específico y queremos agregarle nuevas funciones. Si el componente se reutilizará o si es teóricamente posible en el futuro, es mejor moverlo a un módulo separado o un análogo de CommonUIComponents. En otros casos, puede dejar el componente local.



Este enfoque resuelve el problema de los componentes faltantes. Esto sucede en proyectos grandes, y si el componente no está documentado, su mantenimiento y depuración posteriormente dejarán de ser rentables.



Crea un proyecto usando SPM



Considere la posibilidad de crear un proyecto de prueba trivial. Estoy usando el proyecto de aplicación multiplataforma en SwiftUI. La plataforma y la interfaz no importan aquí.



Nota: Para crear rápidamente una aplicación multiplataforma, necesita XCode 12.2 beta. Creemos un



proyecto y veamos lo siguiente:







Ahora creemos el primer módulo común:



  • agregue la carpeta Frameworks sin crear un directorio;
  • crear un paquete SPM Common.






  • Agregue las plataformas compatibles al archivo Package.swift. Lo tenemosplatforms: [.iOS(.v14), .macOS(.v10_15)]






  • Ahora agregamos nuestro módulo a cada objetivo. Tenemos este SPMExampleProject para iOS y SPMExampleProject para macOS.






Nota: basta con conectar solo módulos raíz a los destinos. No se agregan como submódulos.



La conexión está completa. Ahora todo lo que tiene que hacer es configurar un módulo con una interfaz pública, y listo, el primer módulo está listo.



Cómo agregar una dependencia para un paquete de SPM local



Agreguemos el paquete AdditionalInfo, como Común, pero sin agregarlo a los destinos. Ahora cambiemos el Package.swift del paquete Common.







No es necesario agregar nada más. Puede ser usado.



Un ejemplo cercano a la realidad



Conectemos SwiftGen a nuestro proyecto de prueba y agreguemos el módulo Paleta; será responsable de acceder a la paleta de colores aprobada por el diseñador.



  1. Cree un nuevo módulo raíz siguiendo las instrucciones anteriores.

  2. Agregue los directorios raíz de Scripts y Templates.

  3. Agregue el archivo Palette.xcassets a la raíz del módulo y anote los conjuntos de colores.

  4. Agregue un archivo Palette.swift vacío a Sources / Palette.

  5. Agregue la plantilla palette.stencil a la carpeta Templates .

  6. Ahora debe registrar el archivo de configuración de SwiftGen. Para hacer esto, agregue el archivo swiftgen.yml a la carpeta Scripts y escriba lo siguiente en él:



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




La apariencia final del



módulo Paleta Hemos configurado el módulo Paleta . Ahora necesitamos configurar el lanzamiento de SwiftGen para que la paleta se genere al inicio de la compilación. Para hacer esto, vaya a la configuración de cada objetivo y cree una nueva fase de construcción, llamémosla generador de paleta. No olvide mover esta fase de construcción a la posición más alta posible.



Ahora escribimos la convocatoria de 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 macosxes un prefijo muy importante. Sin él, la compilación generará un error: "no se puede cargar la biblioteca estándar para el destino 'x86_64-apple-macosx10.15".





Llamada de muestra para SwiftGen



Done: se puede acceder a los colores de esta manera:

Palette.myGreen(Tipo de color en SwiftUI) y PaletteCore.myGreen(UIColor / NSColor).



Rocas submarinas



Enumeraré lo que hemos encontrado.



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


SPM — Bundler?



En este asunto, propongo soñar y discutir en los comentarios. El tema debe estudiarse bien, pero parece muy interesante. Por cierto, ya hay un interesante artículo bastante cercano sobre los pros y los contras de SPM.



SPM le permite llamar a Swift Run agregando Package.swift a la raíz de su proyecto. ¿Qué nos aporta? Por ejemplo, puede llamar a Fastlane o Swiftlint. Ejemplo de llamada:



swift run swiftlint --autocorrect.



All Articles