Al igual que los dispositivos iOS, cada vez con más velocidad aparece una nueva versión de Swift. Estos 11 consejos para acelerar la compilación con Swift pueden ser especialmente útiles, ya que en muchos casos nos encontramos con una versión antigua de Swift que no podemos actualizar.

¿Listo? ¡Allá Vamos!

Mostrar tiempos de compilación en Xcode

En primer lugar, necesitamos medir los tiempos de compilación con Swift. Este primer paso es crucial para poder verificar si los consejos expuestos en este artículo surten efecto.

Puedes habilitar un temporizador directamente dentro de la interfaz del usuario de Xcode. Este no se encuentra visible de forma predeterminada, pero sí se ejecuta en la línea de comandos.El temporizador aparecerá cada vez que se haga un build de la aplicación.

defaults write com.apple.dt.Xcode ShowBuildOperationDuration YES

Después de habilitar el temporizador, reinicia el Xcode y verás en la barra de estado de Xcode el tiempo que tarda la compilación de la aplicación:

xcode_build

Se recomienda hacer un clean del proyecto y eliminar los datos derivados de la aplicación. Puedes hacerlo desde la línea de comandos:

rm -rf ~/Library/Developer/Xcode/DerivedData

Identificar el código de compilación lento

Los tiempos de compilación con Swift son lentos principalmente debido a la costosa verificación de tipos. Por defecto, Xcode no muestra ningún tiempo. Sin embargo, puedes indicarle que muestre funciones y expresiones de compilación lentas.

Abre el [project’s build settings] y agrega las siguientes configuraciones a tu [Other Swift Flags]:

-Xfrontend -warn-long-function-bodies=100

-Xfrontend -warn-long-expression-type-checking=100

other_swift_flags

El 100 es un número entero que representa el límite de tiempo de compilación que se coloca en sus funciones y expresiones. Se mide en milisegundos (ms).

Cuando compilas tu código, cualquier función o expresión que supere los 100 ms se marcará con un warning. Esto te brinda la oportunidad de refactorizar tu código y reducir esos tiempos de compilación con Swift.

Establecer el nivel de optimización en la configuración de compilación

El compilador de Xcode es lo suficientemente inteligente como para tomar algunas decisiones de optimización, y saber cuándo ignorar resultados de funciones que no tienen uso, o llamar directamente métodos que no fueron subclasificados, así que no es mala idea dejar que el Xcode lo optimice solo.

Ve a [build settings] y busca la sección de [Swift Compiler — Code Generation]. Allí puedes encontrar la configuración de [Optimization Level] que tiene tres opciones: 

  • No Optimization
  • Optimization for Speed
  • Optimization for Size

optimization_level

Si no estás haciendo una gran cantidad de depuración, es mejor configurarlo en el modo [Optimization for Speed]. Esto eventualmente reducirá el tiempo de compilación ya que el compilador omitirá los pasos de adjuntar valores al hilo del depurador.

Optimizar la generación dSYM

Usar un archivo dSYM tiene sentido para los informes de errores. Esto es particularmente útil cuando no tienes el depurador conectado. Sin embargo, tarda tiempo en crearse, por lo que es mejor usarlo solo cuando no está conectado al depurador Xcode.

Asegúrate de configurar tu [Debug Information Format] y crear siempre archivos dSYM para sus versiones de release y para sus versiones de depuración que no se ejecutan en el simulador. No es necesario crearlos cuando se ejecuta en el simulador de iOS.

information_format

Optimizar dependencias

Si usas CocoaPods, puedes optimizar todas tus dependencias agregando lo siguiente al final de tu Podile:

Dependencias de terceros

Una de las formas mas comunes de manejar dependencias de terceros es utilizando CocoaPods. Se trata de un mero uso, ya que cuando se trata de tiempos de compilación no es la mejor opción.

Una alternativa a la que puedes recurrir es Carthage. Es un poco más complicado de usar que CocoaPods, pero mejorará tus tiempos de construcción. Carthage lo logra construyendo solo dependencias externas cuando se agrega una nueva a tu proyecto. Si no la has agregado, tu proyecto no necesitará construir todas sus dependencias externas.

Xcode tiene un nuevo sistema de compilación

En Xcode 9, Apple presentó un nuevo sistema de compilación y no está habilitado por defecto. Uno de los principales beneficios del nuevo sistema de compilación es tiempos de compilación más rápidos.

Para usar el nuevo sistema de compilación, tienes que habilitarlo en el menú File y seleccionar [Workspace Settings] (o [Project Settings] si no estás utilizando un [Workspace]).

Los consejos anteriores se centran en sacar el provecho del Xcode para tener un mayor rendimiento en la velocidad de la compilación, pero muchos problemas con la rapidez de compilación se pueden resolver escribiendo el código de otra manera, y aquí te daré unos consejos para ello.

Evitar Print o debugPrint

Las funciones [print] o [debugPrint] son muy útiles para la depuración del código. El error más común entre los programadores es dejar muchos prints flotando en todo el código. Si a esto le sumamos proyectos muy grandes podrían ser cientos o miles:

En el test anterior se puede apreciar que la funcion testWithOutPrint() 15 veces mas rápido que testWithPrint(), dado que el comando [print] escribe en la consola. Esto hace un uso del disco que tiene un costo en el rendimiento. Este uso impide que el compilador Swift elimine gran cantidad de código innecesario como:

  • Llamadas de funciones vacías
  • Crear objetos que no se usan
  • Bucles vacíos

Por ejemplo:

En el anterior ejemplo se aprecia que el código será eliminado sin dejar alguno para compilación, pero si se agrega un print en el método doNothing el código no se eliminará en la optimización.

El mejor consejo es que no dejes estos comandos en tu código si no son necesarios. Cambiar la lógica en su uso puede servir para eliminar todos los print en el build de lanzamiento.  

Existen otras dependencias de terceros que ayudan con este problema como el SwiftyBeaver.

Algunas pautas de codificación

El uso de métodos final o private, variables let y declaración del tipo de variable aumentan tu rendimiento y disminuyen el tiempo de compilación con Swift.

Xcode normalmente te dice que debes usar let en lugar de var, así que usa let siempre que puedas.

Como probablemente sepas, Swift es un lenguaje orientado a objetos, lo que significa que puede hacer subclases y sobrescribir métodos para ampliar la funcionalidad. Para que esto funcione, Swift usa algo llamado [Dynamic Dispatch].

[Dynamic Dispatch] es el algoritmo que decide qué método debe invocarse cada vez que se envía un mensaje a un objeto. Utiliza una vTable (tabla virtual).

Cuando se llama un método de una clase parece una acción obvia, pero acuérdate que tambien busca subclases y si el método esta sobrescrito por otra subclase, esto debe hacerlo una y otra vez hasta llegar a fondo. Para ayudar a Swift a optimizar esta costosa tarea, puedes agregar el atributo final al comienzo del método, variable o incluye toda la clase.

Por último, deja de usar variables sin declarar su tipo. Si no lo haces, el compilador aumentará su tiempo de compilación:

Solo agregando final al inicio, el método testFunction() mejora a un 5% el tiempo de compilación y al solo agregar el tipo a las variables y cambiar let por var mejora a un 4%.

Usar valores (estructuras) y no referencias (clases) en Array

Otro buen consejo. Cuando una Array en Swift contiene referencias, obtiene automáticamente las propiedades de NSArray, por lo tanto, no se puede optimizar. Pero si solo mantiene valores como Int o Structs en Array, el compilador puede optimizarlos fácilmente. Si tiene que elegir entre una estructura y una clase, esta es una gran ventaja de las estructuras: mantener estructuras en un Array es mucho más eficiente que mantener clases.

Protocolo de clases si es posible

Si sabes que el protocolo que estás definiendo es solo para clases, márcalo como class protocol (protocolo de clase). Cuando el compilador sabe que el protocolo es solo para clases, puedes optimizar el ARC (recuento automático de referencias) haciendo que tu código se ejecute más rápido.

Conclusión

Hemos visto una serie de propuestas para mejorar el rendimiento de compilación con Swift en Xcode. El mismo Xcode proporciona múltiples opciones de configuración, pero las mejores optimizaciones dependen de la forma en la que escribamos el código. Está en manos de cada uno decidir si estas funciones son útiles o no y elegir los que se adapten mejor a cada proyecto. La mejor manera de descubrirlo y decidir es probar todas las opciones y usar la combinación óptima.

Como nada es gratis, en especial las mejoras de velocidad, algunas de estas opciones tienen un precio, como ciertas restricciones o el aumento del tamaño de la build.

Seguramente, como desarrollador ya habrías usado la mayoría de estas opciones. Pero si encontraste y aprendiste algo nuevo y útil de este artículo, ¡compártelo en tu red y permite que otros también aprendan de estos trucos!