Product flavors y Build types en iOS

Configuración de múltiples entornos

Cuando se diseña una solución es necesario adicionar la capacidad para generar múltiples versiones de la aplicación con diferentes variables y condiciones de entorno. Por ejemplo, seguramente será necesario una versión para el equipo QA con la aplicación consumiendo los servicios del ambiente servicios.qa.environment-1 y otra versión para QA consumiendo los servicios del ambiente servicios.qa.environment-2

Quizás también sea necesario generar una versión previa a la productiva para someterla a certificación para su respectiva aprobación y quizás usando otro tipo de servicios servicios.preproductivo.

Todas estas diferentes versiones de la aplicación pueden ser generadas a través de configuración en Xcode, usando herramientas de apoyo tales como scripts de ejecución e incluso automatizarlas a través de las herramientas de integración continua.

Product flavors y Build types

Los diferentes tipos de versiones de una aplicación pueden ser agrupadas en flavors y build types. Son dos conceptos que también son conocidos en las aplicaciones Android

Product flavors, hace referencia a versiones de la aplicación que agrupan un conjunto de funcionalidades destinadas a un conjunto de stakeholders. Por ejemplo, una versión de la aplicación destinada al equipo de pruebas internas (versión para QA) o una versión de la aplicación destinada al público general (versión productiva) podrían ser citados como dos casos válidos.

Build types, hace referencia a las características de configuración con la que se genera la versión de la aplicación. Los principales tipos reconocidos son debug, stage y release.

Un video práctico que muestra a mayor detalle la configuración de los archivos de propiedades a través de Xcode se encuentra en Set up environment in iOS using Product flavors and Build types.

Esta configuración forma parte de la implementación de patrón Plugin en las soluciones iOS, tema que hace parte del libro Clean Architecture en iOS y que se detalla en la sección "Explorando detalles de implementación".

Environments files

Cuando la aplicación requiere configurar las variables de entorno que caracterizan a un product flavor, se apoya en archivos de propiedades o archivos de configuración que contiene la definición de cada variable y su posible valor. Por ejemplo un archivo de propiedades de variables de entorno llamado development.xcconfig podría tener las siguientes variables configuradas:

ROOT_URL = http:/$()/localhost:3000
API_KEY = tg54dxz28a_hkzos7jd803001nd
APP_NAME = CleanArchitecture (dev)
APP_BUNDLE_ID = com.clean.architecture.MainApp.dev

Ahora, hay varias estrategias para incluir los archivos de propiedades en la aplicación, sin embargo aspectos como seguridad y protección de datos sensibles deben ser tenidos en cuenta. 

Se presentará a continuación las opciones posible con las ventajas y desventajas en las posibles estrategias de configuración.

1. En una clase o estructura dentro del proyecto

Esta estrategia consiste en crear un archivo para definir una clase o estructura (class o struct) para mantener las propiedades del ambiente. Al quedar dentro del proyecto se hace fácil la lectura directa de las variables, sin embargo se cuenta con los siguientes contras:
  • La información sensible puede quedar expuesta en el código de la aplicación y probablemente publicada en el repositorio del proyecto a menos que dichos archivos se encuentren en el gitignore del proyecto lo cual hará que los archivos no se suban al repositorio y estarán ocultos.
  • Al quedar las propiedades del lado de una clase o estructura cada vez que exista un cambio será necesario recompilar el proyecto para que el cambio en la propiedad se vea reflejado.
  • Si la clase o estructura se incluye en el gitignore del proyecto para que no quede visible en el repositorio, cada vez que se descargue o clone el proyecto por primera vez, será necesario incluir la clase o la estructa, ya que de lo contrario generará un error de archivo no encontrado.
  • Cuando la aplicación sea incluida a un mecanismo de integración continua los archivos deberán estar presentes en la plataforma de integración, ya que de lo contario también presentará errores de archivos no encontrados.
  • Si se decide incluir los archivos en el gitignore podría generarse inconsistencia entre las versiones de los archivos de propiedades compartidos con los otros integrantes del equipo.

2. En un espacio local en máquina

Es similar a la estrategia anterior con la diferencia de que los archivos de propiedades de ambiente no se ubican dentro de los directorios del proyecto si que no se ubican en un directorio local externo por fuera del proyecto. En esta caso los archivos estarán un poco más ocultos y protegidos sin embargo sigue presentando las siguientes desventajas:
  • Cada vez que se instale el proyecto por primera vez en una máquina, no se debe olvidar crear y ubicar la carpeta local que contendrá los archivos de propiedades de tal forma que se pueda compilar sin error de archivos no encontrados.
  • Cada vez que se realice una actualización de una de las propiedades o variable de entorno en el archivo de propiedades, se deberá notificar a los otros integrantes del equipo para que actualicen el archivo de lo contrario estarán trabajando sobre versiones diferentes que generarán inconsistencias y errores.
  • Cuando se aplica integración continua seguramente se presentará errores de archivos no encontrados.

3. En un repositorio privado externo

Esta es la estrategia recomendada. Consiste en ubicar los archivos de propiedades de ambientes en un repositorio privado, crear un script en Xcode que realice la tarea de descargar los archivos cada vez que sea necesario, así cada máquina local que instale la aplicación tendrá los archivos actualizados.

Por otro lado, para la integración continua se crea un paso en el job que permita de igual forma descargar los archivos de propiedades antes del paso de compilación. Con esta estrategia se resuelven todas las limitantes anteriormente mencionadas, teniendo así las siguientes ventajas:

  • La información sensible contenida en las variables de ambientes estarán mayormente protegidas, ya que estarán centralizadas en un repositorio privado con acceso restringido.
  • Los archivos almacenados en el repositorio pueden ser cifrados para reforzar así la seguridad.
  • Al estar los archivos centralizados se garantiza que los equipos tengan versiones consistentes y actualizadas.
  • Cuando se requiera instalar por primera vez las fuentes de la aplicación en un ambiente local, la configuración de los archivos de propiedades de ambiente se hará de forma automatizada a través de un script en Xcode.
  • En el proceso de automatización de la aplicación también se hará de forma automática la instalación de los archivos de propiedades de ambiente a través de una tarea en el job que se encargue de descargar los archivos del repositorio privado.

Esta estrategia podrá ser aplicada independientemente de la plataforma de integración continua que se esté utilizando. A continuación se muestra un ejemplo de code snippet implementado para CircleCI.

steps:
- checkout
- run:
    name: Download xcconfig files
    command: git clone git@bitbucket.org:my-repo/xcconfig.git
- run:
    name: See xcconfig files
    command: ls xcconfig
- run:
    name: Copy development xcconfig files
    command: cp xcconfig/Development.xcconfig /Users/distiller/project/MainApp/MainApp/Environment/
- run:
    name: Copy production xcconfig files
    command: cp xcconfig/Production.xcconfig /Users/distiller/project/MainApp/MainApp/Environment/

Ahora, se podría automatizar una tarea para que la primera vez que se instale el ambiente se obtengan del repositorio los archivos de propiedades localmente.

Dicha tarea se podría automatizar a través de la configuración de un script en Xcode que básicamente haría los siguientes pasos:
  • Verifica si los archivos de propiedades se encuentra configurado.
  • En caso de no encontrarse configurado procede a descargar los archivos del repositorio remoto y los guarda localmente en un espacio externo al proyecto.
  • Después se procede a copiar los archivos descargados a una ubicación interna del proyecto. Para garantizar que dichos archivos copiados al interior del proyecto no sean publicados en el repositorio, se emplea el gitignore para su respectiva omisión.


Un ejemplo de script para la ejecución de dichas tareas podría ser:

environment_files_location="/Users/yaircarreno/Documents/development/ios/environment-files/"
environment_files_in_project=${PROJECT_DIR}/${TARGET_NAME}/Environment/

# Make sure the environment directory exists in the project
echo "Looking for ${environment_files_in_project}"
if [ ! -d $environment_files_in_project ]; then
    mkdir -p $environment_files_in_project
    echo "Looking for ${environment_files_location}"
    if [ ! -d $environment_files_location ]; then
        mkdir -p $environment_files_location
        git clone git@bitbucket.org:qualityfull/xcconfig.git $environment_files_location
        echo "clone repository and copy in environment file"
        cp -r "${environment_files_location}" "${environment_files_in_project}"
    fi
fi

Tener en cuenta que para su correcta ejecución deberá estar configurada el acceso por SSH del usuario al repositorio.


Un ejemplo para evitar subir al repositorio los archivos de propiedades podría ser agregando en gitignore una configuración tal como:

# Xcode
.DS_Store
build/
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
*.xcworkspace
!default.xcworkspace
xcuserdata
*.moved-aside
DerivedData
.idea/
# Pods - for those of you who use CocoaPods
Pods
# Environments files
Development.xcconfig
Production.xcconfig

Y finalmente se podría hacer seguimiento a las operaciones ejecutadas por el script configurado en Build Phases:




Previous
Next Post »
Thanks for your comment