2 puntos por GN⁺ 4 시간 전 | 1 comentarios | Compartir por WhatsApp
  • Después de completar la configuración inicial de la cuenta de Apple y los certificados, es posible automatizar la compilación, firma y distribución de apps para Mac e iOS usando solo las herramientas CLI internas de Xcode.app
  • Con XcodeGen se regenera el .xcodeproj desde project.yml, y con xcodebuild, notarytool, stapler y devicectl se cubre desde la creación del proyecto hasta la notarización en Mac y la instalación en iPhone
  • La distribución para Mac ejecuta en orden, con un solo scripts/release.sh, el archivado, la firma Developer ID, la notarización, el grapado del ticket, la verificación de Gatekeeper y la instalación en /Applications, y se detiene de inmediato si algo falla
  • Para compilaciones y pruebas rápidas se puede usar CODE_SIGNING_ALLOWED=NO, pero esta build con firma temporal no pasa Gatekeeper y los permisos de iCloud KVS y App Group tampoco quedan vinculados al Team ID real
  • La clave privada de firma y la contraseña de notarización se guardan en el llavero de inicio de sesión, y si se documentan los comandos y reglas de distribución en CLAUDE.md o AGENTS.md, agentes como Claude Code pueden ejecutar repetidamente todo el flujo de trabajo

Alcance de lo que se puede hacer sin la GUI de Xcode

  • Xcode en sí debe estar instalado, pero no hace falta ejecutarlo; xcodebuild, notarytool, stapler y devicectl se ejecutan de forma independiente como herramientas de shell dentro de Xcode.app
  • La configuración inicial, como iniciar sesión con Apple ID, crear certificados Developer ID y guardar la contraseña de notarización, requiere la GUI o una terminal interactiva
  • Una vez completada la configuración inicial, se puede manejar sin mouse la creación del proyecto, la compilación, el archivado, la exportación de apps firmadas, la notarización, la verificación, la instalación y hasta la distribución en un iPhone real
  • Si te trabas al escribir configuraciones o scripts, puedes darle a Claude Code u otra herramienta de codificación con LLM los requisitos de la tarea y los valores específicos del proyecto para que genere el flujo de trabajo

Seleccionar la toolchain completa de Xcode

  • Primero hay que verificar que el resultado de xcode-select -p sea /Applications/Xcode.app/Contents/Developer
  • Si están seleccionadas las Command Line Tools independientes, usa el siguiente comando para especificar la toolchain completa de Xcode
sudo xcode-select -s /Applications/Xcode.app/Contents/Developer
  • /Library/Developer/CommandLineTools, instalado con xcode-select --install, incluye clang y git, pero no contiene el SDK de iOS, notarytool, devicectl ni el conjunto completo de herramientas para desarrollo de apps
  • Si Xcode ya está instalado, no hace falta instalar por separado el paquete de Command Line Tools
  • La aceptación de la licencia y la instalación de componentes adicionales también se pueden manejar desde la CLI
sudo xcodebuild -license accept
sudo xcodebuild -runFirstLaunch

Reproducir el proyecto con XcodeGen

  • XcodeGen administra la configuración de proyectos Xcode en YAML y regenera el .xcodeproj cada vez que se compila
  • Se puede descargar desde GitHub o instalar con Homebrew
brew install xcodegen
  • En macOS, un .xcodeproj parece un archivo, pero es una carpeta; como Xcode cambia constantemente sus archivos internos y referencias, puede generar conflictos o cambios innecesarios en el repositorio Git
  • Si solo se commitea project.yml en Git y se ignora el .xcodeproj generado, la configuración del proyecto se mantiene reproducible
xcodegen generate

Configurar la cuenta de desarrollador de Apple y los certificados

  • Hay que agregar el Apple ID en Settings → Accounts de Xcode
  • Para distribuir y notarizar apps se necesita una cuenta paga de Apple Developer
  • El certificado de distribución se crea en Settings → Accounts → Apple ID → Manage Certificates… → + → Developer ID Application
  • Apple Development y Developer ID Application tienen usos distintos
    • Apple Development se usa para ejecutar en tus propios dispositivos o transferir a un iPhone para depuración local
    • Developer ID Application es necesario para distribuir una .app notarizada que pase Gatekeeper también en las Mac de otras personas
  • Al crear un certificado Developer ID Application, el certificado y su clave privada correspondiente se instalan en el llavero de inicio de sesión
  • La clave privada se usa para la firma real y no se puede volver a descargar, así que no la borres y haz backup del llavero

Guardar credenciales de notarización en el llavero

  • La notarización de apps para Mac es el proceso de subir una app firmada a Apple para que la analice en busca de malware
  • notarytool se autentica con un perfil guardado en el llavero, y al crear el perfil por primera vez hay que ingresar de forma interactiva una contraseña específica de app
xcrun notarytool store-credentials App-Name \
  --apple-id "you@example.com" \
  --team-id YOUR-TEAM-ID
  • No conviene reutilizar el perfil de notarización de otra app: hay que crearlo según el nombre de la app para evitar fallas silenciosas en otros entornos
  • La contraseña específica de app es distinta de la contraseña del Apple ID y se genera en Sign-In & Security → App-Specific Passwords del sitio de Apple ID
  • Si cambias la contraseña del Apple ID, las contraseñas específicas de app pueden expirar sin una advertencia aparte
    • Si durante la notarización aparece 401 invalid credentials, antes de sospechar de toda la configuración conviene revisar si expiró la contraseña específica de app
  • Para verificar si el perfil está guardado, usa el siguiente comando
xcrun notarytool history --keychain-profile App-Name
  • Si guardas la contraseña específica de app en una bóveda de 1Password accesible para Claude Code, también puedes delegarle al agente la creación de perfiles de notarización para nuevas apps
  • Es posible automatizar incluso el ingreso de la contraseña con --password, pero como puede quedar en el historial del shell, se recomienda ingresarla manualmente una vez y luego conservarla en el llavero o en 1Password

Configuración de firma local y separación de secretos

  • El Team ID y el prefijo de bundle necesarios para la firma real se guardan en Local.xcconfig
cp Local.xcconfig.example Local.xcconfig
BUNDLE_PREFIX     = your.real.prefix
DEVELOPMENT_TEAM  = YOUR-TEAM-ID
  • Agrega Local.xcconfig a .gitignore para que la configuración local de cada proyecto no entre al repositorio
  • La clave privada de firma queda en el llavero de inicio de sesión y la contraseña específica de app para notarización queda en el perfil de llavero de notarytool, por lo que los secretos no se registran en Git

Automatizar la distribución para Mac con release.sh

  • El scripts/release.sh del repositorio ejecuta en un solo comando todo el pipeline de distribución de la app para Mac
./scripts/release.sh
  • El script funciona en este orden

    1. Verifica de antemano que existan xcodegen y el perfil de notarización
    2. Regenera el proyecto con xcodegen generate
    3. Crea un archivo Release con xcodebuild archive
    4. Exporta la app firmada con Developer ID usando ExportOptions.plist y xcodebuild -exportArchive
    5. Empaqueta la app como ZIP y la envía a Apple con notarytool submit --wait
    6. Adjunta el ticket de notarización con stapler staple
    7. Verifica con spctl si Gatekeeper la permite
    8. Termina el proceso existente y copia la app a /Applications
    9. Registra la app instalada con lsregister
    10. Vuelve a verificar el ticket y el estado de Gatekeeper del bundle instalado
  • Aunque el modelo conozca el procedimiento general de distribución, el usuario debe proporcionar datos específicos del proyecto como nombre del scheme, Team ID, nombre del perfil de notarización y ubicación de instalación

  • Si el primer script falla, hace falta un proceso iterativo de corrección basado en el resultado de la ejecución, pero una vez estabilizado el flujo de trabajo se puede ejecutar solo el comando de distribución sin modificaciones adicionales

  • Evitar fallas y distribuciones incompletas

    • set -euo pipefail detiene de inmediato el script si falla cualquier comando, para no tratar un estado incompleto como exitoso
    • cd "$(dirname "$0")/.." mueve el script a la raíz del repositorio aunque se lo invoque desde cualquier directorio
    • El bloque de verificaciones previas revisa xcodegen y el perfil de notarización antes del archivado, que puede tardar bastante
    • No solo se inspecciona la app exportada, sino también la app copiada a /Applications, para detectar casos donde el bundle se dañe durante la copia
    • Si hace falta otro perfil de notarización, se puede sobrescribir con una variable de entorno
    TZED_NOTARY_PROFILE=<name> ./scripts/release.sh
    

Comunicar las reglas de distribución al agente

  • Si release.sh realiza la distribución con un solo comando, CLAUDE.md o AGENTS.md hacen que el agente use ese comando en cada sesión sin instrucciones adicionales
  • El documento debe registrar la regeneración del proyecto, las pruebas unitarias, la build rápida para macOS y el comando de distribución real
xcodegen generate
swift test

xcodebuild -project YOUR-APP-NAME.xcodeproj \
  -scheme YOUR-APP-NAME-macOS \
  -destination 'platform=macOS' \
  CODE_SIGNING_ALLOWED=NO build

./scripts/release.sh
  • También debe explicitar la diferencia entre las dos rutas de build
    • La build con CODE_SIGNING_ALLOWED=NO es adecuada para CI y verificaciones locales rápidas
    • Para la distribución real de una app de barra de menú, pasar atributos de cuarentena y sincronizar con iCloud, se necesita release.sh, que realiza la firma Developer ID y la notarización
  • Si le indicas que use como referencia el método creado para una app en otro repositorio, Claude Code puede replicar la misma configuración en apps posteriores

Builds y pruebas rápidas sin firma

  • Las pruebas unitarias basadas en Swift Package Manager se ejecutan sin una build de Xcode
swift test
  • La verificación rápida de compilación de una app para macOS se hace con la firma desactivada
xcodebuild -project TZed.xcodeproj \
  -scheme TZed-macOS \
  -destination 'platform=macOS' \
  CODE_SIGNING_ALLOWED=NO build
  • Las builds para simulador de apps iOS y extensiones de widgets también pueden ejecutarse del mismo modo
xcodebuild -project TZed.xcodeproj \
  -scheme TZed-iOS \
  -destination 'generic/platform=iOS Simulator' \
  CODE_SIGNING_ALLOWED=NO build
  • Una build con firma temporal creada con CODE_SIGNING_ALLOWED=NO puede usarse para compilar y ejecutar en el simulador
  • Gatekeeper rechazará esta build y, al no tener prefijo de equipo, permisos como iCloud KVS y App Group no quedarán vinculados, por lo que no puede usarse para distribución

Distribuir de forma headless en un iPhone real

  • Para distribuir en dispositivos iOS no hay una etapa de notarización como en las apps para Mac; se usan xcodebuild y devicectl
  • La app para dispositivo real se compila y firma con un certificado Apple Development y un perfil de provisioning de desarrollo
xcodebuild -project TZed.xcodeproj \
  -scheme TZed-iOS \
  -destination 'generic/platform=iOS' \
  -allowProvisioningUpdates \
  -derivedDataPath build/ios \
  archive -archivePath build/TZed-iOS.xcarchive
  • La .app generada se instala especificando el UDID del dispositivo conectado
xcrun devicectl device install app \
  --device <DEVICE-UDID> build/ios/…/TZed.app
  • devicectl list devices muestra los dispositivos conectados y emparejados, junto con sus UDID
  • Al especificar -allowProvisioningUpdates, se obtienen los perfiles de provisioning de desarrollo necesarios

Cómo funciona la firma de código sin GUI

  • Cuando se crea un certificado Developer ID Application, Apple emite el certificado y la Mac genera la clave privada correspondiente; ambos se guardan en el llavero de inicio de sesión

  • codesign, invocado por xcodebuild, firma el binario con la clave privada e incluye en la app el certificado que encadena hasta el certificado raíz de Apple, para que pueda verificarse el firmante

  • Si se usa signingStyle: automatic, xcodebuild selecciona un certificado que coincida con el Team ID y obtiene de Apple los perfiles de provisioning necesarios

  • Por lo tanto, no hace falta commitear perfiles de provisioning en el repositorio

  • Permisos, notarización y stapling

    • El archivo .entitlements de cada target contiene permisos como sandbox, cliente de red, iCloud KVS y App Group
    • Los permisos se vinculan al firmar con el Team ID real, así que en builds con firma temporal iCloud y App Group no funcionan correctamente
    • La firma y la notarización son etapas distintas
    • La firma demuestra quién creó la app
    • La notarización es el procedimiento mediante el cual Apple analiza una app firmada en busca de malware y emite un ticket
    • El stapling adjunta ese ticket a la app para que Gatekeeper confíe en ella incluso sin conexión
    • En apps LSUIElement, como las de barra de menú que usan una UI oculta, la notarización es necesaria para evitar detecciones de XProtect
  • Comandos de verificación manual

    codesign -dv --verbose=4 /Applications/TZed.app
    spctl -a -vvv -t exec /Applications/TZed.app
    stapler validate /Applications/TZed.app
    
    • codesign revisa el firmante y el certificado
    • spctl verifica si Gatekeeper permitirá la ejecución
    • stapler validate comprueba si el ticket de notarización está adjunto

Herramientas estándar que ejecuta el agente

  • No hace falta un servidor MCP separado para builds ni un plugin dedicado; Claude Code ejecuta la CLI estándar en un shell no interactivo
  • Las herramientas usadas son xcodebuild, xcrun notarytool, xcrun stapler, spctl, codesign, devicectl, xcodegen y swift
  • CLAUDE.md le comunica al agente las reglas de nomenclatura de perfiles de notarización, la diferencia entre build rápida y distribución real, y la regla de usar release.sh para distribuir
  • Solo el primer notarytool store-credentials queda como etapa interactiva; todo el proceso posterior puede automatizarse

Reemplazar tareas de la GUI de Xcode por la CLI

  • La creación del .xcodeproj que Xcode administra se reemplaza con project.yml y xcodegen generate
  • ⌘B o el botón Run corresponden a xcodebuild … build
  • Product → Archive se maneja con xcodebuild … archive
  • La distribución de apps desde Organizer puede reemplazarse con xcodebuild -exportArchive
  • La carga desde Organizer se realiza con xcrun notarytool submit --wait
  • Para adjuntar automáticamente el ticket se usa xcrun stapler staple
  • El proceso de instalación de arrastrar y soltar a /Applications se maneja con cp -R y lsregister
  • La acción Run en un iPhone real se reemplaza con xcodebuild archive y devicectl device install
  • Una vez instalados Xcode y XcodeGen, completada la configuración inicial de credenciales, y escritos release.sh y CLAUDE.md, las nuevas builds se pueden distribuir con una frase o un comando

1 comentarios

 
GN⁺ 4 시간 전
Opiniones en Hacker News
  • Ejecuto agentes de IA directamente en mi Mac porque hay muchas cosas que no se pueden resolver en un sandbox, pero el incidente en el que xAI subió un directorio home que incluso incluía claves SSH me hace reconsiderar seriamente esa decisión
    No me preocupa demasiado que la máquina en sí se rompa. La mayoría de las cosas las tengo respaldadas o confirmadas en Git, así que podría recuperarlas en unas horas. Pero definitivamente no quiero que mi directorio .ssh se envíe a un agente de IA, y eso no debería intentar bloquearse con prompts, sino a nivel del sistema. Estoy considerando crear un usuario separado y aplicar permisos 700 al directorio home. La seguridad parece haber retrocedido a los años 90, pero la otra cara es que a cambio se puede terminar el trabajo a una velocidad sin precedentes. Yo también uso este método riesgoso, así que no pretendo criticar a los demás

    • No necesariamente tiene que ejecutarse en la Mac anfitriona. Las Mac recientes admiten virtualización con una pérdida de rendimiento casi nula, así que se puede usar fácilmente una máquina virtual de macOS o Linux de la misma arquitectura para desarrollo
      Como soluciones empaquetadas se pueden usar las open source Tart https://tart.run/ o VirtualBuddy https://github.com/insidegui/VirtualBuddy, o también puedes hacer vibe coding de tu propia herramienta que envuelva las API del sistema operativo https://developer.apple.com/documentation/virtualization. Recientemente también apareció Apple Container https://github.com/apple/container
    • Estoy creando una herramienta wrapper que ejecuta Claude como un usuario separado llamado agent. Hace un año fracasé por OAuth y Keychain, pero recientemente logré implementarlo gracias a la indicación de Claude de que se puede usar CLAUDE_CODE_OAUTH_TOKEN
      Se necesita una herramienta auxiliar para configurar permisos con ACL, pero actualmente funciona en macOS. El nivel de riesgo queda más o menos a medio camino entre el sandbox básico de Claude y el aislamiento completo por contenedor o máquina. A medida que el propio sandbox de Claude ha mejorado, dudé del retorno de inversión de este proyecto de defensa en profundidad, pero si consideramos la posibilidad de filtración del directorio home, hay motivos suficientes para seguir. Todavía no lo publiqué como open source, pero estoy dispuesto a hablar con quien tenga interés
    • Ejecuto la IA en un contenedor Docker headless y solo le doy acceso a Git para que contribuya únicamente con código. Si necesita secretos, se le proporciona un contenedor Docker guardado en un repositorio; si necesita infraestructura, el flujo de trabajo es que cree un ticket en Jira
    • Me da curiosidad cómo se maneja el registro de un bundle identifier sin abrir Xcode
  • Por diversión estoy compilando y probando apps de iOS usando solo Linux, y es sorprendentemente sencillo; https://github.com/xtool-org/xtool funciona muy bien
    Incluso en Linux se puede instalar directamente en un iPhone por USB, sin necesidad de subirla a TestFlight ni al App Store. Si no estás seguro, puedes empezar fácilmente pidiéndole a un agente de programación que cree y suba una app Hello World para iOS

    • Trabajo de la misma manera, salvo que ejecuto Linux en WSL sobre Windows. Paso el IPA al teléfono con Sideloadly y funciona bien
    • Después de transferir la app por USB, ¿hay que mantener el iPhone conectado?
    • ¿Los agentes de programación ya conocen xtool?
  • Mi proyecto open source Axiom ayuda a que las herramientas de ejecución de código realicen eficazmente tareas de desarrollo para sistemas operativos de Apple
    Incluye varias tecnologías y agentes, además de herramientas para LLM como xclog, xcprof, xcsym y xcui. Estas exponen las funciones necesarias de forma eficiente en tokens y también son útiles para tecnologías o agentes fuera de Axiom. Axiom: https://charleswiltgen.github.io/Axiom/, herramientas CLI: https://charleswiltgen.github.io/Axiom/tools/

    • ¿Funcionaría bien también con una base de código Kotlin Multiplatform? Aunque hay algo de código nativo en Swift, me da curiosidad cómo serían el rendimiento y el comportamiento al compartir parte de la lógica
    • Creo que la próxima frontera de las herramientas para apps de iOS son los servidores de compilación Linux autoalojables; me interesa saber qué opinas al respecto
    • ¿Funciona también con Flutter?
  • Leer la frase “Le hice a Claude Code crear un script que, sin abrir Xcode, realizara el archivado, la firma con Developer ID, la notarización, el stapling y la instalación en /Applications, y que se detuviera claramente si fallaba cualquier paso” se siente extraño. Porque el texto que estamos leyendo también parece como si Claude nos estuviera hablando
    También se siente raro que en casi todos los pasos del artículo se repita la indicación de volver a preguntarle al LLM, con frases como “muéstrale este artículo a Claude Code o a la herramienta de programación con LLM que prefieras y deja que lo resuelva”, “si no sabes, pídele al LLM que te ayude a configurarlo”, “el objetivo de usar un LLM es evitar el trabajo manual no deseado” o “pídele que cree un flujo de trabajo”

    • Este artículo no lo escribió Claude; lo escribí yo mismo
    • Parece una estructura circular en la que le muestras a Claude un sitio web escrito por Claude y haces que Claude cree el entorno de compilación
    • Por los errores del artículo pensé que no lo había escrito una IA, pero ahora me pregunto si tal vez la IA comete errores a propósito
  • Desde mi experiencia desarrollando apps para Mac e iOS con agentes durante más de un año, creo que este artículo da un mal consejo. En particular, el excelente Xcode MCP requiere que Xcode esté en ejecución
    Con las herramientas MCP se puede acceder a varias funciones de forma mucho más rápida y concisa que con xcodebuild, y también se pueden generar y renderizar #Preview, algo imposible si Xcode está cerrado. El MCP y el agente integrado de Xcode 27 también controlan muy bien los simuladores mediante DeviceHub. DeviceHub reemplaza a Simulator.app, y parece que el agente puede entenderlo y manejarlo sin tener que manipular de forma indirecta las API de accesibilidad con herramientas como axe
    Xcode MCP no es perfecto. El problema de que aparezca una ventana de permisos cada vez que se ejecuta el agente lo esquivé aprobándola automáticamente con Keyboard Maestro. Aun así, Xcode 27 mejoró mucho la usabilidad del vibe coding en Xcode. Ahora ejecuto Codex desde la propia UI de agente de Xcode, sin manejar MCP por separado, y ese entorno de ejecución guía mejor al modelo para que use las herramientas disponibles. Al final se obtiene lo más importante: feedback más rápido y verificación más robusta

    • Existe Xcode-mcp-proxy, que actúa como proxy de Xcode MCP, hace clic en los cuadros de diálogo y también recupera la conexión. Me pregunto si para probar apps de macOS usa Peekaboo o automatización común de capturas de pantalla
    • También vale la pena probar Xcode MCP de Sentry. Es mucho más completo y ni siquiera hace falta dejar Xcode en ejecución
  • Si quieres tocar lo menos posible los IDE de iOS y Android, React Native y Expo también son una buena opción
    Xcode y Android Studio solo se usan para instalar los simuladores de dispositivos, y el resto, incluida la distribución en las tiendas, lo maneja la cadena de herramientas de Expo. Incluso en Windows, donde no se puede instalar Xcode, con una app administrada de Expo puedes ejecutar una app de iOS directamente en un iPhone. Como punto de partida basado en RN y Expo, Ignite es excelente y está bien mantenido: https://github.com/infinitered/ignite

  • Estrictamente hablando, se sigue usando Xcode. Xcode es una GUI muy llena de bugs que envuelve varias utilidades y apps UNIX a nivel de sistema, por lo general estables
    Lanzar apps desde la CLI es una práctica antigua y, al menos, ha sido posible desde 2012, cuando publiqué mi primera app con Xcode

    • Eso ya lo sé. Pero el punto clave es que quien usa Xcode no soy yo, sino Claude
    • Después de abrir Xcode y ver la barrera de entrada, ni siquiera había considerado crear un programa para macOS
  • Al menos para parte del debugging, todo el mundo no tiene más remedio que usar Xcode
    Los agentes ya pueden manejar la mayor parte del build desde la línea de comandos, y si se les dan instrucciones explícitas según el caso, pueden compilar más rápido. Si usas los grupos de sincronización del sistema de archivos de Xcode, XcodeGen quizá sea trabajo adicional innecesario
    En iOS, lo que más recomendaría es agregarle al agente funcionalidad de App Store Connect: https://github.com/rorkai/App-Store-Connect-CLI. Así no solo no hace falta tener Xcode siempre abierto, sino que tampoco necesitas estar cerca de la MacBook. Puedes hacer cambios con Codex desde el iPhone, indicarle a asc que compile y suba a TestFlight, descargar la nueva versión, ejecutarla e iterar

    • Casi terminé una app completa de iOS con un agente, y el agente también se encargó del lanzamiento en App Store y la promoción en redes sociales. Solo usé Xcode para revisar previamente el build en el dispositivo y orientar el siguiente prompt
  • Me sigue preocupando la realidad de que los desarrolladores sin presupuesto para comprar una Mac quedan prácticamente excluidos del desarrollo iOS. Cada vez que suben los precios de los productos Apple, aumenta la cantidad de personas que no pueden participar en el desarrollo

    • Es una pena que haya gente que no pueda acceder a la tecnología, pero hoy Apple también ofrece equipos baratos en comparación con productos competidores de especificaciones similares o inferiores. Un ejemplo es una MacBook Air con Apple Silicon con una cantidad razonable de memoria
  • Si vas a hacer vibe coding de una app, a largo plazo Expo habría sido una mejor opción. Al estar basado en React, hay muchos más datos de entrenamiento de LLM con código React que con Swift, y la calidad del código resultante también es mayor
    Todo se puede hacer desde la línea de comandos y, al ser una vista web, el debugging también es fácil. Una vez compilada como app nativa de iOS, se siente similar a otras apps nativas. Con la combinación de Expo y Fastlane se puede automatizar por completo el envío y la distribución en iOS, y subir una nueva versión a App Store con un solo comando

    • ¿No hay pasos que no se pueden automatizar, como registrar la app en sí al crear una app nueva?
    • ¿El Expo que mencionas es https://expo.dev/? Al entrar ahora aparece un aviso que dice: “el código se rompió y se produjo un error que requiere revisión”