Cómo configurar entornos de desarrollo en iOS

A la hora de crear un nuevo proyecto en iOS, Xcode directamente te facilita dos entornos (en Xcode se llaman configurations pero vamos a llamarlos entornos para que quede más claro), un esquema (scheme) y un target. Aclaremos términos:

  • Entorno. Conjunto de configuraciones que definen el modo de ejecución del proyecto. Por defecto, se generan dos: Debug y Release.
  • Target. Conjunto de configuraciones que definen el ejecutable de nuestro proyecto.
  • Esquema. Es la suma de los entornos y targets de nuestro proyecto. Combina diferentes configuraciones dependiendo del modo de ejecución elegido:
    • Run, Test y Analyze ejecutan el entorno de Debug
    • Profile y Archive ejecutan el entorno de Release

Por decirlo de otra forma, al ejecutar una aplicación se compila un target específico con un entorno específico a través de un esquema. Todo esto, que en un inicio viene dado por defecto, puede ser modificado para adaptar los proyectos a las necesidades que se requieran.

En este post explicaremos cómo modificar estas configuraciones, crear nuevos entornos y crear variables por cada uno de los entornos. Para ello vamos a:

  • Crear un esquema por entorno
  • Crear un nuevo entorno
  • Aplicar configuraciones específicas a cada entorno
  • Creación de variables por cada entorno

Crear un esquema por entorno

Como hemos apuntado, por defecto Xcode nos genera un esquema para dos entornos. Desde nuestra experiencia, creemos que lo más práctico es crear un esquema para cada entorno. ¿Por qué? Porque así, a la hora de ejecutar, tendremos claro qué configuración estamos aplicando.

Para crear un esquema por cada entorno que tengamos en nuestro proyecto seguiremos estos pasos:

  1. Accedemos a la opción Manage Schemes situada en el menú de esquemas.
  2. Pulsamos sobre el botón + para añadir uno nuevo y le damos un nombre. Como recomendación, nombraremos a los esquemas como App –  para que podamos identificarlos rápidamente.
  3. Marcamos todos los tipos de ejecuciones con el entorno para el que se está creando y se marcaremos la opción Shared para que el esquema se cree de forma global. Así, cualquier persona que se descargue el proyecto tendrá acceso a él.

Previa eliminación del esquema que por defecto que nos crea el propio Xcode, el resultado final deberá ser el siguiente:

Crear nuevos entornos

La creación de entornos se basa en la duplicación de otro ya existente. Aunque no puedes partir de cero, Xcode te permitirá posteriormente cambiar toda la configuración para cambiarlo a tus necesidades.

Para crear un nuevo entorno iremos a la configuración del project. Bajo el apartado Info, pulsaremos sobre el icono + seleccionando qué entorno queremos duplicar y le daremos un nuevo nombre.

En nuestro caso, vamos a crear un entorno llamado Preproduction. Será una copia de Release, ya que ambas configuraciones serán muy parecidas.

Después de crear el entorno, tendremos que crear un esquema siguiendo los pasos que ya hemos visto en el apartado anterior.

Aplicar configuraciones específicas a cada entorno

NOTA: Las configuraciones que veremos a continuación también se pueden aplicar directamente sobre el Build Settings del proyecto.

La generación de nuevos entorno cobra un mayor sentido al combinarlo con la personalización de las configuraciones de cada uno de ellos a través de ficheros .xcconfig.

Xcode gestiona la configuración del proyecto introduciendo toda la información dentro del fichero .pbxproj. Al tener tantísima información en un solo fichero, resulta muy complicado ver los cambios que han ocurrido.

En cambio, los ficheros .xcconfig nos permiten tener mejor organizada la información de las variables que debemos configurar en el Build Settings. Para hacer uso de ellos es necesario seguir los siguientes pasos:

  1. Crear los ficheros .xcconfig que deseemos. En nuestro caso, crearemos uno por cada entorno asignándole el nombre app.. Crearemos también uno genérico que llamaremos app.shared. Para ello, selecciona el menú de New File en el directorio deseado y seleccionamos la opción Configuration Settings File.
  2. Indicar a cada entorno cuál es el fichero de configuración que debe cargar. En nuestro caso, cargaremos el fichero correspondiente al entorno al que hace referencia. Para esta acción, nos dirigiremos al apartado project. En la pestaña Info, expandimos los entornos y asignamos al project el fichero correcto.

Con esta configuración, las variables que declaremos en los ficheros .xcconfig estarán disponibles en cada uno de los entornos donde estén declaradas.

Dentro de estos ficheros podemos hacer lo siguiente:

  • Declarar variables nuevas. Estas variables podrán usarse en el Build Settings del proyecto para darle diferentes valores a una misma variable dependiendo del entorno que se ejecute.
  • Modificar valores en variables ya existentes. Por defecto, los proyectos vienen con muchas configuraciones predefinidas que, en algunas ocasiones y dependiendo del entorno, deberían tener algún valor diferente. Desde estos ficheros se pueden modificar estos valores.
  • Importar otros ficheros .xcconfig. Esto nos permite tener un fichero compartido con variables que no dependan del entorno, como es el caso de app.shared.xcconfig. Sólo sería necesario importar dicho fichero en cada uno de los entornos.

IMPORTANTE: Las variables definidas en los ficheros .xcconfig pueden ser usadas solamente en el Build Settings o en el Info.plist. Fuera de estos ámbitos dichas variables no existen.

Creación de variables por cada entorno

Ya hemos visto cómo crear nuevos entornos, un esquema asociado para ejecutarlos y sus ficheros .xcconfig. Ahora veremos cómo crear y usar las variables.

¿En qué caso me interesa indicar una variable para un entorno en concreto? Pues en todos los que te puedas imaginar. Por ejemplo, puede ser útil para indicar un paquete de aplicación diferente por entorno o que la app tenga un nombre diferente.

A continuación, vamos a indicar algunos ejemplos para que lo veas más claro. Estos son las variables que incluimos:

  • APP_NAME. Nombre de la aplicación. Este valor se forma fruto de la concatenación de otra variable.
  • PRODUCT_PACKAGE_NAME. Paquete de la aplicación.
  • GCC_PREPROCESSOR_DEFINITIONS. Variable ya existente a la cual vamos a añadirle un nuevo valor. Para ello, es muy importante indicar como parte del valor “$(inherited)” ya que esto indicará que debe cargar el valor que tenía anteriormente.
  • FACEBOOK_TOKEN. Token de Facebook.
  • VERSION_NUMBER. Versión de la aplicación.
  • BUILD_NUMBERBuild de la aplicación.

Fichero app.shared.xcconfig

Contiene configuración personalizada del proyecto pero que es común a todos los entornos.

BASE_APP_NAME = BaseProject
BASE_PRODUCT_PACKAGE_NAME = es.sdos.baseProject
VERSION_NUMBER = 1.0.0
BUILD_NUMBER = 1

Fichero app.debug.xcconfig

Contiene la configuración específica del entorno de Debug.

#include "app.shared.xcconfig"

APP_NAME = DEV $(BASE_APP_NAME)
PRODUCT_PACKAGE_NAME = $(BASE_PRODUCT_PACKAGE_NAME).dev
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) APP_DEBUG=1
FACEBOOK_TOKEN=838375678303084847

Fichero app.preproduction.xcconfig

Contiene la configuración específica del entorno de Preproduction.

#include "app.shared.xcconfig"

APP_NAME = PRE $(BASE_APP_NAME)
PRODUCT_PACKAGE_NAME = $(BASE_PRODUCT_PACKAGE_NAME).test
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) APP_PREPRODUCTION=1
FACEBOOK_TOKEN=77438783399383893

Fichero app.release.xcconfig

Este fichero contiene la configuración específica del entorno de Release.

#include "app.shared.xcconfig"

APP_NAME = $(BASE_APP_NAME)
PRODUCT_PACKAGE_NAME = $(BASE_PRODUCT_PACKAGE_NAME)
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) APP_RELEASE=1
FACEBOOK_TOKEN=1127899277393

Como vemos en los ficheros anteriores, se han declarado nuevas variables que ahora deberemos usar dentro del proyecto dependiendo del entorno. Estas variables son de ejemplo y en tus proyectos puedes poner tantas como necesites.

Para usar la variable sólo es necesario escribir el nombre de la variable entre paréntesis y comenzando con el símbolo $. Por ejemplo: $(APP_NAME).

En la siguiente captura se puede ver el uso de las variables que acabamos de crear.

Por último, vamos a ver cómo aplicar esta misma filosofía en código para que podamos usar una misma constante para el endpoint de los servicios web sin necesidad de estar comentando y descomentando código.

Para esto, utilizamos la variable anteriormente creada GCC_PREPROCESSOR_DEFINITIONS a la cual se le ha añadido un valor diferente dependiendo del entorno. Los valores que hemos definido pueden ser usados como variables en tiempo de ejecución en el propio proyecto con cláusulas #if, por ejemplo. Siguiendo este planteamiento se definirá en la misma constante el endpoint de los servicios web pero con valores diferentes para cada entorno como vemos a continuación:

#if APP_DEBUG
    #define WS_ENDPOINT @"https://endpoint.debug.es"
#elif APP_PREPRODUCTION
    #define WS_ENDPOINT @"https://endpoint.preproduction.es"
#else
    #define WS_ENDPOINT @"https://endpoint.release.es"
#endif

Básicamente, preguntamos si existe la variable del entorno definida y, dentro del bloque, asignamos todo aquello que dependa del entorno. Si ejecutas el proyecto de ejemplo (adjuntado abajo) en el log, podrás ver qué variable usa por cada entorno.

Estas cláusulas #if se resuelven en tiempo de compilación. Por este motivo son muy útiles también para ejecutar ciertos trozos de código en situaciones en las que nos encontremos en entorno de debug pero no queremos que se ejecuten en entornos de producción.

Te dejamos un enlace a un proyecto de ejemplo en el que se aplican todas las configuraciones que hemos visto en este post. Esperamos que te sea de ayuda.

Rafael Fernández,
iOS Tech Lider