Otro canal de distribución — la Mac App Store
En la serie anterior cubrimos la configuración inicial para distribuir una app de macOS directamente con Developer ID. Con un certificado, la notarización, las actualizaciones automáticas de Sparkle y el alojamiento del feed de actualizaciones, podías ofrecer la descarga directa de un archivo .dmg sin pasar por la App Store.
Esta serie cubre la configuración inicial para subir esa misma app también a la Mac App Store (MAS). Las dos formas de distribución no son excluyentes. Puedes operar una misma app simultáneamente por el canal de distribución directa y por el canal de la App Store. La App Store tiene la ventaja de que Apple se encarga del cobro, los reembolsos y la visibilidad en las búsquedas, y de que inspira más confianza a los usuarios, así que es habitual usarla en paralelo a la distribución directa.
Esta serie parte de la premisa de que tu app ya se distribuye directamente con Developer ID y ya incorpora las actualizaciones automáticas de Sparkle. Esa configuración se cubrió desde la Parte 1 de la serie “Distribuir una app de macOS por tu cuenta”. Si todavía no la has leído, te recomendamos hacerlo antes.
Por qué MAS necesita “otro target”
La app que distribuyes directamente incluye las actualizaciones automáticas de Sparkle. Es la función que hace que la app compruebe por su cuenta el feed de actualizaciones, descargue la nueva versión y se reemplace a sí misma.
Sin embargo, las normas de revisión de la Mac App Store prohíben ese comportamiento. Una app publicada en la App Store no puede descargar código desde el exterior para actualizarse a sí misma; las actualizaciones deben producirse únicamente a través de la App Store. Es decir, la compilación que subas a MAS no puede incluir Sparkle.
Pero la compilación de distribución directa necesita obligatoriamente Sparkle. No puedes satisfacer ambos requisitos con un mismo resultado de compilación. Por eso la solución es esta:
El mismo código base, dos targets de compilación.
| Canal | Target de compilación | Sparkle | Lugar de distribución |
|---|---|---|---|
| Developer ID | FocusTimer | Incluido | Distribución directa (.dmg) |
| Mac App Store | FocusTimer MAS | Excluido | App Store |
El código fuente se comparte como uno solo, pero divides el target de compilación en dos y solo enlazas Sparkle en uno de ellos. Esta serie cubre la configuración inicial para crear ese segundo target (FocusTimer MAS).
Lo que vamos a construir en esta serie
A lo largo de 3 partes conseguirás lo siguiente.
- (Parte 1, este artículo) Registrar un Bundle ID exclusivo para MAS + duplicar el target de compilación en Xcode
- (Parte 2) Los archivos de configuración (entitlements e Info.plist) y la bifurcación de código que separan ambos canales
- (Parte 3) Un
ExportOptions-MAS.plistpara la subida + registro en App Store Connect + verificación de la compilación
Al terminar la serie deberías tener lo siguiente.
- Un Bundle ID exclusivo para MAS registrado en el Apple Developer Portal
- Un target de compilación
FocusTimer MASsin la dependencia de Sparkle - Un archivo de entitlements y un archivo Info.plist exclusivos para MAS
- Código bifurcado con
#if canImport(Sparkle) - Un
ExportOptions-MAS.plistpara subir a la App Store
La app de ejemplo — FocusTimer
Igual que en la serie anterior, tomamos como ejemplo una app de macOS ficticia llamada FocusTimer (una sencilla app de temporizador para gestionar el tiempo de concentración). FocusTimer, el identificador de paquete com.example.FocusTimer, el Team ID ABCDE12345, etc., son todos valores de ejemplo. En la práctica, sustitúyelos por la información de tu propia app y de tu cuenta.
Este artículo se ha escrito tomando como referencia Xcode 26.
Paso 1 — Registrar un Bundle ID exclusivo para MAS
Por qué un Bundle ID aparte
Si el Bundle ID del canal de distribución directa es com.example.FocusTimer, en el canal MAS usas un Bundle ID distinto.
| Canal | Bundle ID |
|---|---|
| Developer ID | com.example.FocusTimer |
| Mac App Store | com.example.FocusTimer.mas |
Se trata de añadir .mas al final del Bundle ID existente. ¿Por qué molestarse en separarlos?
- Permite que ambos canales coexistan en un mismo Mac — si el Bundle ID es el mismo, macOS reconoce ambas apps como la misma app y entran en conflicto. Si son distintos, puedes tener instaladas a la vez la versión de distribución directa y la de la App Store en una misma máquina (útil para desarrollo y pruebas).
- Separa el dominio de
UserDefaults— el espacio donde se guardan los ajustes se divide según el Bundle ID, así que las preferencias de ambos canales no se mezclan. - Evita conflictos en Launch Services — previene la confusión que surge cuando macOS tiene que decidir a qué app enviar una petición como “abre esta app”.
Mantén tal cual el Bundle ID del canal de distribución directa. Es el criterio que identifica a tus usuarios actuales, así que no debes cambiarlo. Lo que haces es registrar un ID nuevo más para MAS.
Procedimiento de registro
- Inicia sesión en developer.apple.com/account (cuenta del Apple Developer Program)
- Certificates, Identifiers & Profiles → menú izquierdo Identifiers → botón
+ - Selecciona App IDs → Continue → el tipo es App → Continue
- Description: introduce un nombre fácil de reconocer (por ejemplo,
FocusTimer MAS) - Bundle ID:
- Selecciona Explicit (explícito)
- Valor a introducir:
com.example.FocusTimer.mas
- Capabilities: desactiva todas las funciones que tu app no use. Si no usas iCloud, notificaciones push, compras dentro de la app, Sign in with Apple, etc., no necesitas activar nada. (App Sandbox lo gestionan los entitlements del lado de Xcode, así que aquí no hace falta activarlo.)
- Continue → Register
Verificación
Si en la lista de Identifiers aparece la entrada FocusTimer MAS — com.example.FocusTimer.mas, el registro está completo.
Paso 2 — Duplicar el target de compilación en Xcode
Ahora vamos a crear el target de compilación FocusTimer MAS en el proyecto de Xcode. La forma más rápida es duplicar el target existente.
2-1. Duplicar el target (Duplicate)
- Abre
FocusTimer.xcodeprojen Xcode - Haz clic en el icono del proyecto (azul), arriba del todo en el Project navigator (panel izquierdo)
- En la lista TARGETS de la zona central de edición, haz clic derecho sobre
FocusTimer→ Duplicate - Cuando aparezca el cuadro de diálogo, selecciona Duplicate Only
- El nuevo target se crea con el nombre
FocusTimer copy. Haz doble clic en el nombre y cámbialo aFocusTimer MAS - Si aparece el aviso “¿Quieres añadir un nuevo Scheme?”, elige Activate (o añádelo más tarde desde Manage Schemes)
2-2. Cambiar el Bundle ID de inmediato
Lo primero que hay que hacer justo después de duplicar es reemplazar el Bundle ID. Si lo dejas como está, ambos targets tendrán el mismo ID.
Cambia TARGETS → FocusTimer MAS → General → Identity → Bundle Identifier por el valor que registraste en el Paso 1.
com.example.FocusTimer.mas
2-3. Un escollo clave — el “trabajo de limpieza” que deja Duplicate
Aquí está la parte más delicada de esta parte. El Duplicate Target de Xcode funciona con “valores predeterminados seguros”, pero esa seguridad deja a cambio un trabajo de limpieza posterior. Justo después de duplicar tienes que revisar los cuatro puntos siguientes.
① Hacer que el nuevo target incluya los archivos de código fuente
Por seguridad, Duplicate excluye automáticamente todos los archivos de código fuente del nuevo target. Si lo dejas así, el target FocusTimer MAS es una cáscara vacía y, aunque lo compiles, no incluye ningún código. Hay que ajustar la pertenencia (membership) para que el nuevo target vuelva a incluir los archivos de código fuente existentes.
② Eliminar la dependencia de Sparkle del target MAS
Duplicate copia tal cual las dependencias de Swift Package del original. Por eso el target FocusTimer MAS también arrastra Sparkle. Como el punto de partida de esta serie era precisamente que “la compilación de MAS no debe tener Sparkle”, elimina Sparkle de la lista de dependencias de paquetes del target MAS y de su fase de compilación de Frameworks.
③ Limpiar los archivos Info.plist temporales
Duplicate crea archivos Info.plist temporales como FocusTimer copy-Info.plist. Como en la Parte 2 vamos a crear aparte un Info.plist exclusivo para MAS, elimina estos archivos temporales tanto de las referencias del proyecto como del propio archivo en disco.
④ Actualizar los ajustes de compilación del target MAS
Hay que ajustar para MAS los ajustes de compilación: el identificador de paquete, la ruta del Info.plist, la ruta de los entitlements, etc. Este trabajo lo abordaremos todo de una vez en la Parte 2, después de crear los archivos de configuración específicos.
Los puntos ① y ② se pueden resolver en su mayoría desde las pantallas de pertenencia a targets y de dependencias de paquetes de la interfaz de Xcode, pero si las referencias duplicadas que deja el proceso de duplicación no se borran del todo limpiamente, a veces hay que examinar directamente el archivo del proyecto (
.pbxproj). Cuando creas que has terminado la limpieza, la verificación más fiable es compilar cada uno de los dos targets y comprobar si en la compilación del target MAS fallaimport Sparkle(en la Parte 2 cubrimos esta bifurcación de código).
Resumen de la Parte 1
Si has seguido hasta aquí, ahora tienes lo siguiente.
- ✅ Un Bundle ID exclusivo para MAS (
com.example.FocusTimer.mas) registrado en el Apple Developer Portal - ✅ El target de compilación
FocusTimer MAScreado por duplicación - ✅ Una comprensión del trabajo de limpieza que deja la duplicación (pertenencia del código fuente, dependencia de Sparkle, archivos temporales)
El “recipiente” que es el target ya está creado. Pero, por ahora, este target no es más que una copia de FocusTimer; todavía no ha llegado a ser de verdad “para MAS”. La compilación de MAS debe usar unos entitlements distintos y un Info.plist distinto de los de la compilación de distribución directa, y el código también tiene que bifurcarse para no romperse cuando Sparkle no está presente.
En la siguiente parte cubriremos esos archivos de configuración y la bifurcación de código que separan ambos canales.