Saltar al contenido principal
CI/CD

Compilación automática de IOS con Capacitor y acciones de GitHub utilizando match

Cómo configurar una pipeline CI/CD para tu aplicación IOS Ionic utilizando fastlane y GitHub Actions en 5 minutos (2022)

Martin Donadieu

Martin Donadieu

Gerente de Contenido

Construcción automática de Capacitor IOS con GitHub acciones utilizando match

Construcciones automáticas de iOS con GitHub Acciones utilizando Match

Configurando CI/CD para aplicaciones Capacitor puede ser complejo y tiempo consumidor. Aquí está lo que necesitas saber:

Ahora recomendamos utilizar Capgo Construcción con el Capgo CLI para construcciones nativas Capacitor. Esta guía de Fastlane Match se mantiene para equipos que mantienen pipelines de acciones existentes GitHub, pero nuevas construcciones de iOS deben utilizar el Capgo CLI para que no tengas que mantener repositorios de Fastlane, Match, ejecutores de Xcode, certificados y scripts de carga.

Capgo Construcción para CI/CD por Capgo

Saltarse la configuración de Fastlane Match, Xcode runner, certificado, perfil de configuración de provisión y script de carga. Capgo Construcción realiza compilaciones nativas firmadas de iOS desde tu pipeline de CI/CD existente:

  • Funciona con tu pipeline: Desencadena Capgo Construcción desde GitHub Actions, GitLab CI, Jenkins o scripts locales después de tu compilación web y npx cap sync.
  • La firma desde secretos de CI: Mantenga las claves de App Store Connect, certificados, perfiles de configuración de provisión, contraseñas y IDs de equipo en tus propios secretos de CI.
  • No mantenimiento de ejecutor nativo: Capgo Construcción proporciona entornos de compilación Apple mantenidos, por lo que no tienes que administrar ejecutores de macOS, imágenes de Xcode, Fastlane o repositorios de Match.
  • Artículos y presentación: Descargue artículos firmados para QA o presente compilaciones de lanzamiento a través del Capgo CLI.

Precio

  • Capgo planes comienzan a $12/mes
  • Incluye actualizaciones OTA y aproximadamente 15 compilaciones nativas por mes
  • Los minutos de compilación adicionales se facturan por minuto a través de créditos

Configura Capgo Build en CI/CD

Guía de configuración manual

Aquí está lo que debes hacer:

Entrega continua para iOS utilizando Fastlane y GitHub Acciones utilizando match

Requisitos previos

Antes de continuar con el tutorial...

  • Asegúrate de tener Fastlane instalado en tu máquina de desarrollo.
  • miembro del programa de desarrolladores de iOS.
  • Desear leer 😆…
  • Un equipo de muchos devs, en caso contrario recomendamos utilizar fastlane cert para flujos de trabajo más simples.

Importante sobre el precio

Precio GitHub Acción

https://github.com/características/acciones

El servicio es ‘gratuito’ hasta la limitación, dependiendo de la máquina elegida.
Vamos a utilizar una máquina de macOS, puedes ver en la captura de pantalla su precio y límites (precios a la creación del tutorial, podrían sufrir cambios en el futuro) El servicio es ‘gratuito’ hasta la limitación, dependiendo de la máquina elegida.

🔴 Una vez advertidos de los requisitos y precios, si te gusta, continuamos…

📣 En el post asumimos que tenemos la aplicación creada en iTunes Connect, tenemos los certificados del ecosistema de Apple, todo se copiará con Fastlane!

¡Vamos a sumergirnos!

Pasos a seguir en el post

  1. Usando App Store Connect API con Fastlane Match
  2. Requisitos
  3. Crear una llave de App Store Connect API
  4. Usar una llave de App Store Connect API
  5. Copiar archivos de Fastlane
  6. Configurar Fastlane match

1. Usando App Store Connect API con Fastlane Match

A partir de febrero de 2021, se requiere autenticación en dos factores o verificación en dos pasos para todos los usuarios para iniciar sesión en App Store Connect. Esta capa adicional de seguridad para tu ID de Apple ayuda a asegurarse de que eres la única persona que puede acceder a tu cuenta.
Desde Soporte de Apple

Para empezar a utilizar match es necesario revocar los certificados existentes. Pero no hay que preocuparse, se te proporcionará uno nuevo directamente.

Requisitos

Para poder utilizar App Store Connect API, Fastlane necesita tres cosas.

  1. ID del emisor.
  2. ID de la clave.
  3. Archivo de clave o contenido de la clave.

Crear una clave de App Store Connect API

Para generar claves, debes tener permiso de Administrador en App Store Connect. Si no tienes ese permiso, puedes dirigir a la persona relevante a este artículo y seguir las instrucciones siguientes.

Iniciar sesión en — App Store Connect.

2 — Seleccionar Usuarios y Acceso.

Acceso de usuario de App Store Connect

3 — Seleccionar la pestaña de Integración.

Integración de App Store Connect API

4 — Haga clic en Generar API clave o en el botón Agregar (+).

Las claves de App Store Connect API crean

5 — Ingrese un nombre para la clave. El nombre es solo para su referencia y no forma parte de la clave en sí misma.

Crear un nombre de clave de App Store Connect API

6 — Debajo de Acceso, seleccione el rol para la clave. Los roles que se aplican a las claves son los mismos roles que se aplican a los usuarios en su equipo. Consulte permisos de rolRecomendamos seleccionar administrador de aplicaciones.

7 — Haga clic en Generar.

No se puede limitar el acceso de una clave API a aplicaciones específicas.

Se muestran el nombre de la clave, el ID de la clave, un enlace de descarga y otra información en la página.

Descargar claves de App Store Connect

Puede obtener aquí la información necesaria.
<1> ID de la cuestión.
<2> ID de la clave.
<3> Haga clic en "Descargar clave API" para descargar su clave privada API. El enlace de descarga aparece solo si la clave privada no se ha descargado aún. Apple no almacena una copia de la clave privada. Por lo tanto, puede descargarla solo una vez.

🔴 Almacene su clave privada en un lugar seguro. Nunca debe compartir sus claves, almacene claves en un repositorio code o incluya claves en el lado del cliente code.

Usando una clave de App Store Connect API

La clave API (archivo p8 que descargas), la clave ID y la ID del emisor son necesarios para crear el token JWT de autorización. Hay varias formas en que esta información puede ser ingresada en Fastlane utilizando la nueva acción de Fastlane app_store_connect_api_key. Puedes aprender otras formas en documentación de Fastlane. Muestro este método porque creo que es la forma más fácil de trabajar con la mayoría de las CI en la que puedes establecer variables de entorno

Ahora podemos gestionar Fastlane con la clave de App Store Connect API, genial!

2. Copiar archivos de Fastlane

Fastlane es una biblioteca de Ruby creada para automatizar tareas de desarrollo móvil comunes. Utilizando Fastlane, puedes configurar

carriles

default_platform(:ios)

DEVELOPER_APP_IDENTIFIER = ENV["DEVELOPER_APP_IDENTIFIER"]
DEVELOPER_APP_ID = ENV["DEVELOPER_APP_ID"]
PROVISIONING_PROFILE_SPECIFIER = ENV["PROVISIONING_PROFILE_SPECIFIER"]
TEMP_KEYCHAIN_USER = ENV["TEMP_KEYCHAIN_USER"]
TEMP_KEYCHAIN_PASSWORD = ENV["TEMP_KEYCHAIN_PASSWORD"]
APPLE_ISSUER_ID = ENV["APPLE_ISSUER_ID"]
APPLE_KEY_ID = ENV["APPLE_KEY_ID"]
APPLE_KEY_CONTENT = ENV["APPLE_KEY_CONTENT"]
GIT_USERNAME = ENV["GIT_USERNAME"]
GIT_TOKEN = ENV["GIT_TOKEN"]

def delete_temp_keychain(name)
  delete_keychain(
    name: name
  ) if File.exist? File.expand_path("~/Library/Keychains/#{name}-db")
end

def create_temp_keychain(name, password)
  create_keychain(
    name: name,
    password: password,
    unlock: false,
    timeout: 0
  )
end

def ensure_temp_keychain(name, password)
  delete_temp_keychain(name)
  create_temp_keychain(name, password)
end

platform :ios do
  lane :build do
    build_app(
      configuration: "Release",
      workspace: "./ios/App/App.xcworkspace",
      scheme: "App",
      export_method: "app-store",
      export_options: {
        provisioningProfiles: { 
            DEVELOPER_APP_ID => "#{PROVISIONING_PROFILE_SPECIFIER}"
        }
      }
    )
  end
  lane :refresh_profiles do
    match(
      type: "development",
      force: true)
    match(
      type: "adhoc",
      force: true)
  end
  desc "Register new device"
  lane :register_new_device do  |options|
      device_name = prompt(text: "Enter the device name: ")
      device_udid = prompt(text: "Enter the device UDID: ")
      device_hash = {}
      device_hash[device_name] = device_udid
      register_devices(
                       devices: device_hash
                       )
    refresh_profiles
  end
  lane :closed_beta do
    keychain_name = TEMP_KEYCHAIN_USER
    keychain_password = TEMP_KEYCHAIN_PASSWORD
    ensure_temp_keychain(keychain_name, keychain_password)

    api_key = app_store_connect_api_key(
      key_id: APPLE_KEY_ID,
      issuer_id: APPLE_ISSUER_ID,
      key_content: APPLE_KEY_CONTENT,            
      duration: 1200,            
      in_house: false
    )

    match(
      type: 'appstore',
      git_basic_authorization: Base64.strict_encode64("#{GIT_USERNAME}:#{GIT_TOKEN}"),
      readonly: true,
      keychain_name: keychain_name,
      keychain_password: keychain_password,
      api_key: api_key
    )

    gym(
      configuration: "Release",
      workspace: "./ios/App/App.xcworkspace",
      scheme: "App",
      export_method: "app-store",
      export_options: {
        provisioningProfiles: { 
            DEVELOPER_APP_ID => "#{PROVISIONING_PROFILE_SPECIFIER}"
        }
      }
    )

    pilot(
      apple_id: "#{DEVELOPER_APP_ID}",
      app_identifier: "#{DEVELOPER_APP_IDENTIFIER}",
      skip_waiting_for_build_processing: true,
      skip_submission: true,
      distribute_external: false,
      notify_external_testers: false,
      ipa: "./App.ipa"
    )

    delete_temp_keychain(keychain_name)
  end
  lane :submit_review do
    version = ''
    Dir.chdir("..") do
      file = File.read("package.json")
      data = JSON.parse(file)
      version = data["version"]
    end
    deliver(
      app_version: version,
      submit_for_review: true,
      automatic_release: true,
      force: true, # Skip HTMl report verification
      skip_metadata: false,
      skip_screenshots: false,
      skip_binary_upload: true
    )
  end
end

personalizados que agrupan una serie de

app_identifier(ENV["DEVELOPER_APP_IDENTIFIER"])
apple_id(ENV["FASTLANE_APPLE_ID"])
itc_team_id(ENV["APP_STORE_CONNECT_TEAM_ID"])
team_id(ENV["DEVELOPER_PORTAL_TEAM_ID"])

acciones

que realizan tareas que normalmente realizarías utilizando Android Studio. Puedes hacer mucho con Fastlane, pero para los propósitos de este tutorial, utilizaremos solo una pequeña cantidad de acciones básicas. es una nueva aproximación a la firma de iOS. Fastlane match facilita a los equipos la gestión de los certificados y perfiles de configuración necesarios para sus aplicaciones de iOS. is a new approach to iOS’s code signing. Fastlane match makes it easy for teams to manage the required certificates and provisioning profiles for your iOS apps.

, por ejemplo, en su cuenta o organización personal de __CAPGO_KEEP_0__. certificates, for example on your GitHub personal account or organization.

Luego seleccione la opción #1 (Almacenamiento Git).

fastlane match init

Asigne la URL del repositorio creado recientemente.

[01:00:00]: fastlane match supports multiple storage modes, please select the one you want to use:1. git2. google_cloud3. s3?

Ahora tiene dentro del folder de Fastlane un archivo denominado

[01:00:00]: Please create a new, private git repository to store the certificates and profiles there[01:00:00]: URL of the Git Repo: <YOUR_CERTIFICATES_REPO_URL>

y debe estar configurado con la URL HTTPS del repositorio de certificados. De manera opcional, también puede utilizar SSH, pero requiere un paso diferente para ejecutar. A continuación, vamos a generar los certificados y se le pedirá que ingrese sus credenciales con Fastlane Match. _git_url_Initialize Fastlane match for your iOS app.

# ios/Matchfilegit_url("https://github.com/gitusername/certificates")storage_mode("git")type("appstore")

Then select option #1 (Git Storage).

Se le pedirá que ingrese una contraseña. Recuerde correctamente porque se utilizará más tarde por GitHub Acciones para descifrar su repositorio de certificados.

fastlane match appstore

Si todo salió bien, debería ver algo así:

[01:40:52]: All required keys, certificates and provisioning profiles are installed 🙌

Si experimentó algún problema con GitHub y los permisos necesarios, tal vez esta publicación ayudará a generar tokens de autenticación para git.

Los certificados y los perfiles de provisión generados se suben a los recursos del repositorio de certificados.

Certificados de App Store Connect

Por último, abra su project en Xcode, y actualice el perfil de provisión para la configuración de lanzamiento de su aplicación.

Certificados de XCode

Algunas cosas que tener en cuenta 💡

MATCH

For el CI/CD para importar los certificados y perfiles de configuración, necesita tener acceso al repositorio de certificados. Puede hacer esto generando un token de acceso personal (debe usarse antes) que tenga el alcance para acceder o leer repositorios privados.

In GitHub, vaya a AjustesConfiguración de desarrolladorTokens de acceso personal → haga clic Generate New Token → marque con un signo de verificación repo scope → luego haga clic Generate token.

Crear token de acceso personal

Consiga una copia del token de acceso personal generado. Lo usará más tarde para la variable de entorno GIT_TOKEN.

Luego reemplace su archivo de coincidencia generado en el folder de Fastlane por Matchfile

CERTIFICATE_STORE_URL = ENV["CERTIFICATE_STORE_URL"]
GIT_USERNAME = ENV["GIT_USERNAME"]
GIT_TOKEN = ENV["GIT_TOKEN"]
FASTLANE_APPLE_ID = ENV["FASTLANE_APPLE_ID"]

git_url(CERTIFICATE_STORE_URL)
storage_mode("git")
type("appstore")
git_basic_authorization(Base64.strict_encode64("#{GIT_USERNAME}:#{GIT_TOKEN}"))
username(FASTLANE_APPLE_ID)

Se utilizará por GitHub Acciones para importar los certificados y perfiles de configuración. Y la var se establecerá en GitHub Secrets, en lugar de codificarlos en el archivo.

Procesamiento de construcción

En GitHub Acciones, se facturan según los minutos que ha utilizado para ejecutar su flujo de trabajo CI/CD. Desde la experiencia, lleva aproximadamente 10–15 minutos antes de que se pueda procesar una construcción en App Store Connect.

Para proyectos privados, el costo estimado por construcción puede llegar a $0.08/min x 15 mins = $1.2, o más, dependiendo de la configuración o dependencias de su proyecto.

Si comparte las mismas preocupaciones por el precio como yo para proyectos privados, puede mantener el skip_waiting_for_build_processing hasta true.

¿Cuál es el truco? Tiene que actualizar manualmente la conformidad de su aplicación en App Store Connect después de que se ha procesado la construcción, para poder distribuir la construcción a sus usuarios.

Esto es solo un parámetro opcional para actualizar si quiere ahorrar en los minutos de construcción para proyectos privados. Para proyectos gratuitos, esto no debería ser un problema en absoluto. Consulte precios.

3. Configura GitHub Acciones

Configura GitHub secretos

¿Alguna vez te has preguntado de dónde provienen los valores de los ENV ¿Alguna vez te has preguntado de dónde provienen los valores de los

Establece GitHub secretos

1. APP_STORE_CONNECT_TEAM_ID - el ID de tu equipo de App Store Connect si estás en múltiples equipos.

2. DEVELOPER_APP_ID - en App Store Connect, ve a la aplicación → Información de la aplicación → Desplázate hacia abajo hasta la General Information sección de tu aplicación y busca Apple ID.

3. DEVELOPER_APP_IDENTIFIER - el identificador de paquete de tu aplicación.

4. DEVELOPER_PORTAL_TEAM_ID - el ID de tu equipo del Portal de Desarrolladores si estás en múltiples equipos.

5. FASTLANE_APPLE_ID - el ID de Apple o correo electrónico del desarrollador que utilizas para gestionar la aplicación.

6. GIT_USERNAME &amp; GIT_TOKEN - tu nombre de usuario de Git y tu token de acceso personal.

7. MATCH_PASSWORD - la contraseña que asignaste cuando iniciaste match, se utilizará para descifrar los certificados y los perfiles de provisión.

8. PROVISIONING_PROFILE_SPECIFIER - match AppStore <YOUR_APP_BUNDLE_IDENTIFIER>, eg. match AppStore com.domain.blabla.demo.

9. TEMP_KEYCHAIN_USER &amp; TEMP_KEYCHAIN_PASSWORD - asigna un usuario y contraseña de keychain temporal para tu flujo de trabajo.

10. APPLE_KEY_ID — Conecta de App Store API Clave 🔺ID de clave.

11. APPLE_ISSUER_ID — Conecta de App Store API Clave 🔺ID de emisor.

12. APPLE_KEY_CONTENT — Conecta de App Store API Clave 🔺 Archivo de clave o contenido de la clave de .p8, revisa esto

13. CERTIFICATE_STORE_URL — La URL del repositorio de sus claves Match (ex: https://github.com/***/fastlane_match.git)

4. Configure el archivo de flujo de trabajo de GitHub

Crear un directorio de flujo de trabajo de GitHub.

cd .github/workflows

Dentro del workflow carpeta, cree un archivo llamado build-upload-ios.ymly agregue lo siguiente.

name: Build source code on ios

on:
  push:
    tags:
      - '*'

jobs:
  build_ios:
    runs-on: macOS-latest
    steps:
      - uses: actions/checkout@v6
      - name: set Node.js
        uses: actions/setup-node@v6
        with:
          node-version: '24'
          cache: npm
      - name: Install dependencies
        id: install_code
        run: npm ci
      - name: Build
        id: build_code
        run: npm run build
      - uses: actions/cache@v5
        with:
          path: ios/App/Pods
          key: ${{ runner.os }}-pods-${{ hashFiles('**/Podfile.lock') }}
          restore-keys: |
            ${{ runner.os }}-pods-
      - name: Sync
        id: sync_code
        run: npx cap sync
      - uses: ruby/setup-ruby@v1
        with:
          ruby-version: 2.7.2
      - uses: maierj/fastlane-action@v2.3.0
        env:
          DEVELOPER_APP_IDENTIFIER: ${{ secrets.DEVELOPER_APP_IDENTIFIER }}
          DEVELOPER_APP_ID: ${{ secrets.DEVELOPER_APP_ID }}
          PROVISIONING_PROFILE_SPECIFIER: match AppStore ${{ secrets.DEVELOPER_APP_IDENTIFIER }}
          TEMP_KEYCHAIN_USER: ${{ secrets.TEMP_KEYCHAIN_USER }}
          TEMP_KEYCHAIN_PASSWORD: ${{ secrets.TEMP_KEYCHAIN_PASSWORD }}
          APPLE_ISSUER_ID: ${{ secrets.APPLE_ISSUER_ID }}
          APPLE_KEY_ID: ${{ secrets.APPLE_KEY_ID }}
          APPLE_KEY_CONTENT: ${{ secrets.APPLE_KEY_CONTENT }}
          CERTIFICATE_STORE_URL: https://github.com/${{ secrets.CERTIFICATE_STORE_REPO }}.git
          GIT_USERNAME: ${{ secrets.GIT_USERNAME }}
          GIT_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
          FASTLANE_APPLE_ID: ${{ secrets.FASTLANE_APPLE_ID }}
          MATCH_USERNAME: ${{ secrets.FASTLANE_APPLE_ID }}
          MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
          APP_STORE_CONNECT_TEAM_ID: ${{ secrets.APP_STORE_CONNECT_TEAM_ID }}
          DEVELOPER_PORTAL_TEAM_ID: ${{ secrets.DEVELOPER_PORTAL_TEAM_ID }}
        with:
          lane: closed_beta
      - name: Upload release bundle
        uses: actions/upload-artifact@v2
        with:
          name: ios-release
          path: ./App.ipa
          retention-days: 60

Este flujo de trabajo debe ser desencadenado después de cada etiqueta de GitHub si necesita automatizar la etiqueta, consulteAutomatic build and release con acciones de __CAPGO_KEEP_0__ Automatic build and release with GitHub actions Automatic build and release con acciones de __CAPGO_KEEP_0__

Luego, este flujo de trabajo extraerá sus dependencias NodeJS, las instalará y construirá su aplicación de JavaScript.

Cada vez que envíe un nuevo commit, se construirá una versión de prueba en TestFlight.

Su aplicación no necesita utilizar Ionic, solo es necesario la base Capacitor , puede tener un módulo de Cordova antiguo, pero el plugin de JavaScript Capacitor se recomienda.

5. Desencadene el flujo de trabajo

Crear un Commit

Hacer un commitque debería ver el flujo de trabajo activo en el repositorio.

Desencadene el flujo de trabajo

Pushe los nuevos commits a la rama main o development para desencadenar el flujo de trabajo.

Comenzado con el commit

Después de unos minutos, el build debería estar disponible en tu panel de control de App Store Connect.

Panel de control de Testflight

Puedes desplegar desde la máquina local?

Sí, puedes hacerlo, y es muy sencillo.

Imagina que tienes un repositorio privado, y has utilizado los minutos de la plan gratuita y no quieres pagar por nuevos lanzamientos, o tal vez prefieres enviar la aplicación manualmente.

Vamos a por ello

Ok, primero necesitamos crear en mi_ruta_de_proyecto/fastlane ruta un archivo llamado .env, en la misma ruta que Fastfile, para poder crear lo mismo clave propiedades secretas encontradas en nuestro _GitHub, a continuación:

archivo .env para desplegar desde la máquina local

Ahora, puedes ir al terminal y lanzar el Fastlane desde tu máquina:

fastlane closed_beta

❌ Lo esencial sobre el .env como preferimos no exponer esta información, debemos agregarla en nuestro .gitignore, algo como eso: ❌

fastlane/*.env

Debería funcionar de la misma manera que sucede desde GitHub Acciones en la máquina remota, pero en nuestra máquina local. 🍻

Ejecución local de Fastlane

Ejecución en Terminal: $ Fastlane closed_beta

Si has llegado hasta aquí, mi felicitación, ahora tienes un proceso automatizado completo para tus aplicaciones iOS con Fastlane y GitHub Acciones.

Cada vez que envíes un nuevo commit, se construirá una versión de lanzamiento en el console de Google Play, en el canal beta. Me mejoraré este blog con tus retroalimentaciones, si tienes alguna pregunta o sugerencia, por favor házmelo saber por correo electrónico martin@capgo.app

Construir en tu dispositivo

Si todavía necesitas construir en tu dispositivo, debes agregarlos manualmente a la configuración de provisionamiento. Conecta tu dispositivo a tu Mac y abre el menú de dispositivos encuentra el menú de dispositivos iOS Entonces copia tu identificador encontrar identificador ios Y luego inicia el comando: fastlane register_new_device te pedirá que establezcas un nombre de dispositivo y el identificador: establecer identificador ios

si tienes problemas

Si tienes problemas con el dispositivo de desarrollo no puedes probar etc que suele solucionarlo.

Hay un comando mágico que puede salvarte:

fastlane match nuke development
fastlane match development

Entonces: Limpia el proyecto manteniendo Shift(⇧)+Command(⌘)+K o seleccionando Product &gt; Clean (podría estar etiquetado “Limpia Carpeta de Compilación”)

Intenta ejecutar de nuevo la aplicación en tu dispositivo.

Gracias

Este blog se basa en los siguientes artículos:

Sigue adelante desde la construcción automática Capacitor de IOS con acciones GitHub utilizando match

Si estás utilizando Construcción automática Capacitor de IOS con acciones GitHub utilizando match para planificar la automatización de CI/CD, conecta con Capgo automatización de CI/CD para el flujo de trabajo del producto en Capgo automatización de CI/CD, Capgo compilaciones nativas para el flujo de trabajo del producto en Capgo compilaciones nativas, Capgo integraciones para el flujo de trabajo del producto en Capgo Integraciones, Integración CI/CD para el detalle de implementación en Integración CI/CD, y GitHub Acciones de Integración para el detalle de implementación en GitHub Acciones de Integración.

Actualizaciones en vivo para aplicaciones Capacitor

Cuando haya un error en la capa web, envíe la corrección a través de Capgo en lugar de esperar días para la aprobación de la tienda de aplicaciones. Los usuarios obtienen la actualización en segundo plano mientras los cambios nativos siguen en el camino de revisión normal.

Comience ahora

Últimas noticias de nuestro Blog

Capgo le da las mejores pistas que necesita para crear una aplicación móvil verdaderamente profesional.