Skip to content
This repository has been archived by the owner on Sep 8, 2023. It is now read-only.

Latest commit

 

History

History
311 lines (206 loc) · 15.1 KB

2020-02-27-xcconfig.md

File metadata and controls

311 lines (206 loc) · 15.1 KB
Error in user YAML: (<unknown>): could not find expected ':' while scanning a simple key at line 6 column 1
---
title: Archivos de configuración de compilación de Xcode
author: Mattt
translator: Nicolás García
category: Xcode
excerpt: >-
Las mejores prácticas de desarrollo de software prescriben la estricta separación entre configuración y código. Aprenda cómo puede utilizar los archivos `xcconfig` para hacer sus proyectos más comprensibles, compactos y potentes.
status:
  swift: 5.2
revisions:
  2019-05-13: Initial Publication
  2020-02-27: Updated for Xcode 11.4
---

Las mejores prácticas de desarrollo de software prescriben la estricta separación entre configuración y código. Sin embargo, los desarrolladores de las plataformas de Apple a menudo luchan con hacerlas cuadrar con el pesado flujo de trabajo de Xcode.

En un proyecto, comprender lo que cada configuración hace y cómo interactúan entre sí, es una habilidad que puede llevar años perfeccionar. Tampoco nos hace ningún favor el hecho de que gran parte de esta información esté enterrada en lo profundo de las GUIs (interfaces gráficas de usuario) de Xcode.

Diríjase a la pestaña "Build Settings" (Configuración de compilación) del editor y será recibido por cientos de configuraciones de compilación, distribuidas en capas de proyectos, targets y configuraciones --- ¡y esto es nada comparado a las otras seis pestañas!

Xcode build settings

Afortunadamente, hay una mejor manera de administrar toda esta configuración, la cual implica no tener que hacer clicks dentro de un laberinto de pestañas.

Esta semana, le mostraremos cómo puede utilizar los archivos de texto de extensión xcconfig para externalizar la configuración de compilación de Xcode, haciendo que sus proyectos sean más compactos, potentes y comprensibles.

{% info %}

Consulte XcodeBuildSettings.com para acceder a una completa referencia de cada configuración de compilación, compatible con la última versión de Xcode.

{% endinfo %}


Los archivos de configuración de compilación Xcode, más comúnmente conocidos por su extensión xcconfig, permiten que la configuración de compilación de su aplicación se declare y administre sin utilizar Xcode. Son archivos de texto simples, lo que significa que son mucho más amigables con los sistemas de control de versiones y que se pueden modificar con cualquier editor de texto.

Fundamentalmente, cada archivo de configuración consiste de una secuencia de asignaciones en formato clave-valor, con la siguiente sintaxis:

<#NOMBRE_DE_CONFIGURACION#> = <#valor#>

Por ejemplo, para especificar la versión de Swift de un proyecto, debe especificar la configuración de compilación SWIFT_VERSION de esta manera:

SWIFT_VERSION = 5.0

{% info %}

Según el estándar POSIX, las variables de entorno deben tener nombres que consisten únicamente de letras mayúsculas, dígitos y guiones bajos (_) - una convención que me gusta llamar SCREAMING_SNAKE_CASE 🐍🗯.

{% endinfo %}


A primera vista, los archivos xcconfig tienen un sorprendente parecido con los archivos .env, debido a su sintaxis simple y delimitada por líneas nuevas. Pero hay más en los archivos de configuración de compilación de Xcode de lo que pareciera. ¡A dar una mirada!

Reteniendo valores existentes

Para añadir, en lugar de reemplazar las definiciones existentes, utilice la variable $(inherited) de la siguiente manera:

<#NOMBRE_DE_CONFIGURACION#> = $(inherited)<#valor adicional#>

Normalmente, usted haría esto para crear listas de valores, como por ejemplo, las rutas donde el compilador busca los frameworks para encontrar los archivos de encabezado incluidos (FRAMEWORK_SEARCH_PATHS):

FRAMEWORK_SEARCH_PATHS = $(inherited) $(PROJECT_DIR)

Xcode asigna los valores heredados en el siguiente orden (de menor a mayor precedencia):

  • Valores predeterminados de la plataforma
  • Archivo xcconfig del proyecto Xcode
  • Configuración de compilación del archivo de proyecto Xcode
  • Archivo xcconfig del Target
  • Configuración de compilación del Target

{% info %}

Los espacios se utilizan para delimitar elementos en listas de rutas y texto. Para especificar un elemento que contenga espacios en blanco, debe encerrarlo entre comillas (").

{% endinfo %}

Referenciando valores

Puede sustituir valores de otras configuraciones mediante el nombre con el cual fueron declaradas usando la siguiente sintaxis:

<#NOMBRE_DE_CONFIGURACION#> = $(<#OTRO_NOMBRE_DE_CONFIGURACION#>)

Las sustituciones se pueden usar para definir nuevas variables de acuerdo con los valores existentes o en línea para construir nuevos valores dinámicamente.

OBJROOT = $(SYMROOT)
CONFIGURATION_BUILD_DIR = $(BUILD_DIR)/$(CONFIGURATION)-$(PLATFORM_NAME)

Estableciendo valores por defecto para configuraciones de compilación referenciadas

Desde Xcode 11.4 en adelante, puede utilizar el operador de evaluación default para especificar un valor por defecto a usar en caso de que la configuración de compilación referenciada se evalúe como vacía.

$(<#NOMBRE_DE_CONFIGURACION#>:default=<#valor#>)

Condicionando las configuraciones de compilación

Puede condicionar la configuración de compilación según sus SDK (sdk), arquitectura (arch) y/o la configuración (config) utilizando la siguiente sintaxis:

<#NOMBRE_DE_CONFIGURACION#>[sdk=<#sdk#>] = <#valor para el SDK especificado#>
<#NOMBRE_DE_CONFIGURACION#>[arch=<#architecture#>] = <#valor para la arquitectura especificada#>
<#NOMBRE_DE_CONFIGURACION#>[config=<#configuration#>] = <#valor para la configuración especificada#>

Dada la posibilidad de elegir entre varias definiciones de una misma configuración de compilación, el compilador resolverá según la especificidad dada.

<#NOMBRE_DE_CONFIGURACION#>[sdk=<#sdk#>][arch=<#architecture#>] = <#valor para el SDK y arquitectura especificados#>
<#NOMBRE_DE_CONFIGURACION#>[sdk=*][arch=<#architecture#>] = <#valor para todos los otros SDK con la arquitectura especificada#>

Por ejemplo, puede especificar la siguiente configuración de compilación para acelerar las compilaciones locales compilando solo para la arquitectura activa:

ONLY_ACTIVE_ARCH[config=Debug][sdk=*][arch=*] = YES

Incluyendo configuraciones de compilación desde otros archivos de configuración

Un archivo de configuración de compilación puede incluir configuraciones de otros archivos de configuración usando la misma sintaxis #include como en la directiva equivalente en C, sobre la cual se basa esta funcionalidad:

#include "<#ruta/de/Archivo.xcconfig#>"

Como veremos más adelante en el artículo, puede aprovechar esto para crear listas en cascada de configuraciones de compilación de maneras muy poderosas.

{% info %}

Normalmente, cuando el compilador encuentra una directiva #include que no se puede resolver, indicará un error. Pero los archivos xcconfig también admiten la directiva #include?, la cual no indicará un error si el archivo no se puede encontrar.

No hay muchos casos en los que usted quisiera que la existencia o inexistencia de un archivo cambie el comportamiento en tiempo de compilación; después de todo, las compilaciones son mejores cuando son predecibles. Pero, usted podría usar esta opción en herramientas de desarrollo opcionales como Reveal, la cual requiere la siguiente configuración:

# Reveal.xcconfig
OTHER_LDFLAGS = $(inherited) -weak_framework RevealServer
FRAMEWORK_SEARCH_PATHS = $(inherited) /Applications/Reveal.app/Contents/SharedSupport/iOS-Libraries

{% endinfo %}

Creando archivos de configuración de compilación

Para crear un archivo de configuración de compilación, vaya al menú "File > New File..." (N), desplácese hacia abajo hasta la sección etiquetada "Other", y seleccione la plantilla del archivo de ajustes de configuración (Configuration Settings File). Luego, guárdelo en algún lugar del directorio de su proyecto, asegurándose de no agregarlo a ningún target.

Xcode new configuration file

Una vez que haya creado el archivo xcconfig, puede asignarlo a una o más configuraciones de compilación mediante sus targets asociados.

Xcode project configuration

{% info %}

Los archivos de configuración de compilación no deben ser incluidos en ninguno de los targets de su proyecto. Si encuentra archivos .xcconfig en el archivo .ipa de su aplicación, asegúrese de que no sean miembros de ningún target y que no aparecen en ninguna fase de compilación en "Copy Bundle Resources".

{% endinfo %}


Ahora que hemos cubierto los conceptos básicos del uso de archivos de configuración de compilación de Xcode, veamos un par de ejemplos de cómo puede usarlos para gestionar entornos de desarrollo, integración y producción.


Customizando el ícono y nombre de la app para compilaciones internas

El desarrollo de una aplicación para iOS generalmente implica hacer malabarismos con varias compilaciones internas en los simuladores y dispositivos de pruebas (así como la última versión de la App Store).

Puede facilitarse las cosas con archivos xcconfig, asignando en cada configuración un ícono y nombre distintivos para la aplicación.

// Development.xcconfig
PRODUCT_NAME = $(inherited) α
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon-Alpha

//////////////////////////////////////////////////

// Staging.xcconfig
PRODUCT_NAME = $(inherited) β
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon-Beta

Gestionando constantes a través de distintos entornos

Si sus desarrolladores back-end actúan de acuerdo con la filosofía 12 Factor App mencionada anteriormente, tendrán entonces endpoints separados para entornos de desarrollo, integración y producción.

En iOS, quizás el enfoque más común para administrar estos entornos es usar sentencias de compilación condicional, con configuraciones de compilación tales como DEBUG.

import Foundation

#if DEBUG
let apiBaseURL = URL(string: "https://api.staging.example.com")!
#else
let apiBaseURL = URL(string: "https://api.example.com")!
#endif

Esto hace el trabajo, pero entra en conflicto con el canon de la separación entre código y configuración.

Un enfoque alternativo es tomar estos valores específicos para el entorno y ponerlos donde pertenecen - en archivos xcconfig.

// Development.xcconfig
API_BASE_URL = api.staging.example.com

//////////////////////////////////////////

// Production.xcconfig
API_BASE_URL = api.example.com

{% warning %}

Los archivos xcconfig tratan la secuencia // como un delimitador de comentarios, independientemente de si está o no entre comillas. Si intenta utilizar barras invertidas \/\/, estas apareceran literalmente y deberán ser eliminadas del valor resultante. Esto es especialmente inconveniente al especificar constantes de tipo URL por cada entorno.

Si prefiere evitar este desafortunado comportamiento, siempre puede omitir el esquema de la URL y anteponer https:// directamente en el código. (Está usando https... ¿verdad?)

{% endwarning %}

Sin embargo, para extraer estos valores programáticamente, tendremos que dar un paso adicional:

Accediendo a las configuraciones de compilación desde Swift

Las configuraciones de compilación definidas por el archivo del proyecto Xcode, los archivos xcconfig y las variables de entorno solo están disponibles en tiempo de compilación. Cuando ejecute la aplicación, ya compilada, nada de ese contexto circundante estará disponible. (¡Y gracias a Dios por eso!)

Pero, espere un segundo - ¿No recuerda haber visto algunas de esas configuraciones antes? ¿En una de esas otras pestañas? En "Info", ¿verdad?

Sucede que esa pestaña de información es en realidad solo una presentación elegante del archivo Info.plist del target. Al momento de compilar, ese archivo Info.plist es compilado de acuerdo con la configuración de compilación proporcionada y luego copiado en el bundle resultante de la aplicación. Por lo tanto, al añadir referencias a $(API_BASE_URL), puede acceder a los valores para esas configuraciones a través de la propiedad infoDictionary de la API Bundle de Foundation. ¡Genial!

Xcode Info.plist

Siguiendo este enfoque, podríamos hacer algo como lo siguiente:

import Foundation

enum Configuration {
    enum Error: Swift.Error {
        case missingKey, invalidValue
    }

    static func value<T>(for key: String) throws -> T where T: LosslessStringConvertible {
        guard let object = Bundle.main.object(forInfoDictionaryKey:key) else {
            throw Error.missingKey
        }

        switch object {
        case let value as T:
            return value
        case let string as String:
            guard let value = T(string) else { fallthrough }
            return value
        default:
            throw Error.invalidValue
        }
    }
}

enum API {
    static var baseURL: URL {
        return try! URL(string: "https://" + Configuration.value(for: "API_BASE_URL"))!
    }
}

Visto desde el lugar de llamada, encontramos que este enfoque armoniza maravillosamente con nuestras mejores prácticas - ¡Ninguna constante definida programáticamente a la vista!

let url = URL(string: path, relativeTo: API.baseURL)!
var request = URLRequest(url: url)
request.httpMethod = method

{% error %}

No utilice los archivos xcconfig para almacenar secretos como claves de API u otras credenciales. Para más información, consulte nuestro artículo sobre gestión de secretos en iOS.

{% enderror %}


Los proyectos de Xcode son monolíticos, frágiles y opacos. Son una fuente de fricción para la colaboración entre los miembros de un equipo y, generalmente, complicados de trabajar.

Afortunadamente, los archivos xcconfig ayudan mucho para abordar estos puntos débiles. Mover la configuración de Xcode a los archivos xcconfig confiere una multitud de beneficios y ofrece una manera de distanciar su proyecto de las particularidades de Xcode, sin abandonar el "camino feliz" aprobado por Cupertino.