Posts

De pbxproj a xcproj: los proyectos de Xcode se pasan a JSON

Arturo Rivas Arias

Añades un archivo, revisas los cambios en Git y descubres que Xcode ha modificado varias partes de project.pbxproj. Si otra persona ha trabajado sobre el mismo proyecto, una operación sencilla puede acabar en un conflicto difícil de interpretar. Xcode 27.2 beta incorpora un formato JSON para la configuración del proyecto, con una estructura pensada para facilitar su lectura y edición. Apple explica el cambio en su documentación sobre el nuevo formato.

Conviene distinguir los nombres: MiApp.xcodeproj sigue siendo el paquete que abres con Xcode. Lo que cambia está dentro: project.xcproj sustituye a project.pbxproj como representación de la configuración. La conversión afecta a ese archivo interno; no convierte todo el proyecto en un único documento JSON.

En este caso la estructura si que importa. El formato tradicional organiza los objetos en una tabla plana, conectada mediante identificadores. Un archivo, su grupo y su participación en una fase de compilación pueden quedar descritos en lugares separados. El nuevo formato acerca esas relaciones a los elementos que describen: un archivo puede indicar directamente a qué target y fase pertenece. La mejora va más allá de cambiar puntos y coma por comillas.

Los identificadores siguen teniendo su función, pero las referencias basadas en nombres permiten reconocer mejor lo que estamos revisando: existe un diccionario por proyecto y por target, con condiciones para los valores que dependen de una configuración. Por ejemplo, este fragmento ilustrativo concentra los ajustes de una app de rutas de senderismo:

{
  "PRODUCT_BUNDLE_IDENTIFIER": "dev.ejemplo.Rutas",
  "SWIFT_OPTIMIZATION_LEVEL[config=Debug]": "-Onone",
  "SWIFT_OPTIMIZATION_LEVEL[config=Release]": "-O"
}

Este es un fragmento de ajustes, no un project.xcproj completo. La condición config=Debug expresa a qué configuración corresponde el valor. Al revisar un cambio, podemos centrarnos en la opción modificada y en su alcance, sin recorrer varias listas de configuraciones.

Para convertir un proyecto existente desde Xcode 27.2, selecciona el proyecto en el navegador, abre el inspector de archivos y elige JSON en Project Document → Project Format. Xcode sustituye el archivo de configuración. Los proyectos nuevos ya utilizan JSON por defecto. Hay un matiz importante: el formato es compatible con Xcode 27 y posteriores, aunque la opción de conversión llegue en 27.2. Apple documenta tanto estos pasos como la posibilidad de deshacer el cambio mediante el control de versiones.

Mi recomendación es hacer esa conversión en un cambio independiente. Si la mezclamos con nuevos targets, dependencias y ajustes de firma, será más difícil saber si una diferencia procede del formato o de una modificación funcional. Primero guardaría el estado anterior, convertiría y comprobaría que el proyecto sigue compilando y ejecutando sus pruebas. Después incorporaría el resto del trabajo.

Apple también presenta el formato como una mejora para los agentes de programación. Es razonable esperar que una configuración más legible simplifique sus modificaciones, pero el resultado sigue necesitando revisión. Por ejemplo, un agente puede escribir un JSON válido y asignar un recurso al target equivocado. La validez sintáctica no garantiza que la configuración haga lo que queremos. Esa distinción importa tanto al automatizar cambios como al editarlos a mano.

La novedad viene acompañada de xcode-project-format, una librería de código abierto de Apple. Proporciona tipos Swift bajo XCSchema para leer y manipular la estructura del proyecto. También incluye la utilidad xcprojformatter. Para una herramienta propia, disponer de ese modelo evita reconstruir la estructura a partir de diccionarios sin tipos.

Imagina que nuestra app de senderismo debe conservar siempre sus targets de aplicación y pruebas. En una herramienta Swift que dependa del producto XcodeProjectFormat, podemos comprobar su presencia así:

import Foundation
import XcodeProjectFormat

enum ProjectAuditError: Error {
    case missingTargets([String])
}

func validateHikingProject(at fileURL: URL) throws {
    let data = try Data(contentsOf: fileURL)
    let project = try XCSchema.Project(jsonRepresentation: data)

    let expected: Set<String> = ["Rutas", "RutasTests"]
    let existing = Set(project.targets.map { $0.name })
    let missing = expected.subtracting(existing)

    guard missing.isEmpty else {
        throw ProjectAuditError.missingTargets(missing.sorted())
    }
}

try validateHikingProject(
    at: URL(fileURLWithPath: "Rutas.xcodeproj/project.xcproj")
)

Esta comprobación responde a una regla concreta del equipo: detectar que falta un target esperado. No verifica sus dependencias, sus fuentes ni su configuración de firma. La usaría como complemento de la compilación y de las pruebas, especialmente si una automatización modifica el proyecto. El valor está en comprobar una intención explícita, además de que el archivo se pueda leer.

Las herramientas que procesan proyectos también necesitan adaptarse. La librería XcodeProj de Tuist ya documenta soporte experimental para leer y escribir ambos formatos mediante su modelo de objetos. Eso no garantiza por sí solo que todas las herramientas que dependen de ella sean compatibles: hay que comprobar la versión que utiliza cada una y cómo localiza el archivo del proyecto. Un script que busca literalmente project.pbxproj requiere atención aunque el resto del proceso siga funcionando.

En un equipo, probaría la migración desde una copia limpia del repositorio y con la misma versión de Xcode que utiliza la integración continua. Revisaría los esquemas compartidos y ejecutaría el proceso habitual de compilación, pruebas y archivado. También buscaría referencias directas a project.pbxproj en los scripts. Así la adopción se apoya en el flujo real del proyecto, con resultados que se pueden comprobar.

Un formato más legible facilita entender qué se ha modificado y reduce el trabajo necesario para automatizar comprobaciones. Seguirán existiendo decisiones sobre targets, recursos, dependencias y configuraciones que requieren conocer la aplicación. La oportunidad de project.xcproj está en dedicar más tiempo a revisar esas decisiones y menos a descifrar cómo las ha guardado Xcode.