This is the full developer documentation for capgo # Willkommen zur Capgo-Dokumentation > Meistern Sie Capgo Cloud für sofortige App-Updates und erkunden Sie unsere umfassende Sammlung von Capacitor-Plugins zur Verbesserung Ihrer mobilen Entwicklung ## 🚀 Capgo Cloud - Live-Updates leicht gemacht [Section titled “🚀 Capgo Cloud - Live-Updates leicht gemacht”](#-capgo-cloud---live-updates-leicht-gemacht) Sofortige Updates Stellen Sie JavaScript-, HTML- und CSS-Updates direkt an Benutzer bereit, ohne App-Store-Verzögerungen. Beheben Sie Fehler und liefern Sie Funktionen in Minuten statt Tagen. 3-Schritte-Integration Starten Sie mit nur `npx @capgo/cli@latest init [APIKEY]` und beginnen Sie sofort mit dem Pushen von Updates dank unserer einfachen Integration. Erweiterte Funktionen Kanäle für A/B-Tests, automatische Rollbacks, Update-Analysen, verschlüsselte Bundles und Enterprise-Sicherheitsfunktionen. Vollständiger Leitfaden Lernen Sie alles von der [Schnelleinrichtung](/docs/getting-started/quickstart/) bis zu fortgeschrittenen Bereitstellungsstrategien in unserer umfassenden Dokumentation. ## 📚 Was ist in dieser Dokumentation enthalten [Section titled “📚 Was ist in dieser Dokumentation enthalten”](#-was-ist-in-dieser-dokumentation-enthalten) [Capgo Cloud-Einrichtung ](/docs/getting-started/quickstart/)Vollständige Anleitungen zur Integration von Live-Updates, Verwaltung von Kanälen, CI/CD-Integration und Überwachung Ihrer Bereitstellungen. [20+ Capacitor-Plugins ](/docs/plugins/)Erkunden Sie unsere Sammlung produktionsbereiter Plugins für Biometrie, Käufe, Kamera, Speicher und weitere native Funktionen. [CLI & Öffentliche API ](/docs/cli/overview/)Automatisieren Sie Ihren Workflow mit unseren CLI-Tools und integrieren Sie Capgo in Ihre bestehenden Systeme mit unserer REST-API. [Enterprise-Lösungen ](/enterprise/)Dedizierter Support, SLAs, benutzerdefinierte Funktionen und erweiterte Sicherheitsoptionen für Teams, die mehr benötigen. ## 🎯 Schnellzugriff [Section titled “🎯 Schnellzugriff”](#-schnellzugriff) * **Erstes Mal?** Beginnen Sie mit dem [5-Minuten-Schnellstart](/docs/getting-started/quickstart/) * **Brauchen Sie ein Plugin?** Durchsuchen Sie unsere [Plugin-Sammlung](/docs/plugins/) oder fordern Sie [individuelle Entwicklung](/consulting/) an * **Haben Sie Probleme?** Schauen Sie in die [FAQ](/docs/faq/) oder treten Sie unserem [Discord](https://discord.capgo.app) bei * **Enterprise-Bedürfnisse?** Sehen Sie sich unsere [Enterprise-Lösungen](/enterprise/) an oder [kontaktieren Sie uns](mailto:support@capgo.app) # Android Builds > Android-Apps mit Capgo Cloud Build konfigurieren und erstellen Erstellen und übermitteln Sie Android-Apps an den Google Play Store mithilfe der sicheren Cloud-Infrastruktur von Capgo. ## Bevor Sie erstellen [Section titled “Bevor Sie erstellen”](#bevor-sie-erstellen) ⚠️ Richten Sie zuerst Android-Zugangsdaten ein **Erforderlich:** Sie müssen Ihre Android-Zugangsdaten speichern, bevor Sie Release-Apps erstellen. [Android-Zugangsdaten einrichten →](/docs/cli/cloud-build/credentials/#saving-android-credentials) ## Wie Android Builds funktionieren [Section titled “Wie Android Builds funktionieren”](#wie-android-builds-funktionieren) Android-Builds laufen in sicheren Cloudflare-Sandboxen: * **Infrastruktur**: Cloudflare Workers mit containerisiertem Android SDK * **Build-Tool**: Gradle mit Android Build Tools * **Ausführung**: Isolierte Sandbox-Umgebung pro Build * **Bereinigung**: Sofortige Löschung nach Build-Abschluss * **Sicherheit**: Kein persistenter Speicher, vollständige Isolation zwischen Builds ## Voraussetzungen [Section titled “Voraussetzungen”](#voraussetzungen) Bevor Sie für Android erstellen, benötigen Sie: ### 1. Entwicklungsumgebung (Lokales Testen) [Section titled “1. Entwicklungsumgebung (Lokales Testen)”](#1-entwicklungsumgebung-lokales-testen) * Android Studio lokal installiert (für die anfängliche Keystore-Einrichtung) * Ihre App erstellt erfolgreich mit `npx cap open android` * Java JDK 17 oder höher ### 2. Signatur-Keystore [Section titled “2. Signatur-Keystore”](#2-signatur-keystore) Für Release-Builds benötigen Sie einen Signatur-Keystore: | Build-Typ | Keystore erforderlich | Zweck | | ----------- | --------------------- | ------------------------------------ | | **Debug** | Nein | Nur für Tests, automatisch generiert | | **Release** | Ja | Play Store-Übermittlung | ## Einen Keystore erstellen [Section titled “Einen Keystore erstellen”](#einen-keystore-erstellen) Tip Für detaillierte Schritt-für-Schritt-Anleitungen mit Screenshots besuchen Sie: * [Android-Zertifikate-Anleitung](https://capgo.app/cloud/native-builds/certificates/android/) * [Blog: Automatische Android-Builds mit GitHub Actions](https://capgo.app/blog/automatic-capacitor-android-build-github-action/) Wenn Sie noch keinen Keystore haben, erstellen Sie einen: ```bash keytool -genkey -v \ -keystore my-release-key.keystore \ -alias my-key-alias \ -keyalg RSA \ -keysize 2048 \ -validity 10000 ``` Beantworten Sie die Aufforderungen: * **Passwort**: Wählen Sie ein starkes Passwort (sicher aufbewahren!) * **Name**: Ihr Name oder Firmenname * **Organisation**: Ihr Firmenname * **Standort**: Ihre Stadt, Bundesland, Land Caution **Wichtig**: Bewahren Sie Ihre Keystore-Datei und Passwörter sicher auf! Wenn Sie diese verlieren, können Sie Ihre App im Play Store nicht aktualisieren. ### Keystore-Komponenten verstehen [Section titled “Keystore-Komponenten verstehen”](#keystore-komponenten-verstehen) Beim Erstellen eines Keystores müssen Sie sich Folgendes merken: 1. **Keystore-Passwort** (`KEYSTORE_STORE_PASSWORD`): Das Passwort für die Keystore-Datei selbst 2. **Key-Alias** (`KEYSTORE_KEY_ALIAS`): Der Name/Bezeichner für Ihren Signaturschlüssel innerhalb des Keystores 3. **Key-Passwort** (`KEYSTORE_KEY_PASSWORD`): Das Passwort für den spezifischen Schlüssel (kann identisch mit dem Store-Passwort sein) **Beispiel-Workflow:** ```bash # Aliase in Ihrem Keystore auflisten, um zu überprüfen keytool -list -keystore my-release-key.keystore # Detaillierte Informationen über Ihren Schlüssel anzeigen keytool -list -v -keystore my-release-key.keystore -alias my-key-alias ``` ## Build-Konfiguration [Section titled “Build-Konfiguration”](#build-konfiguration) ### Erforderliche Umgebungsvariablen [Section titled “Erforderliche Umgebungsvariablen”](#erforderliche-umgebungsvariablen) Für **Release-Builds** legen Sie diese Zugangsdaten fest: ```bash # Android-Signatur (Erforderlich für Release) ANDROID_KEYSTORE_FILE="" KEYSTORE_KEY_ALIAS="my-key-alias" KEYSTORE_KEY_PASSWORD="" KEYSTORE_STORE_PASSWORD="" # Play Store-Veröffentlichung (Optional, für automatische Übermittlung) PLAY_CONFIG_JSON="" ``` ### Base64-Zugangsdaten generieren [Section titled “Base64-Zugangsdaten generieren”](#base64-zugangsdaten-generieren) **Keystore-Datei:** ```bash base64 -i my-release-key.keystore | pbcopy ``` **Play Store Service Account JSON:** ```bash base64 -i play-store-service-account.json | pbcopy ``` Die Base64-Zeichenfolge befindet sich jetzt in Ihrer Zwischenablage. ### Play Store Service Account einrichten [Section titled “Play Store Service Account einrichten”](#play-store-service-account-einrichten) Um automatische Play Store-Uploads zu ermöglichen, müssen Sie ein Google Cloud Service Account mit entsprechenden Berechtigungen erstellen. Tip Für detaillierte Anweisungen mit Screenshots siehe die [Android-Zertifikate-Anleitung](https://capgo.app/cloud/native-builds/certificates/android/). 1. **Service Account in Google Cloud erstellen** * Gehen Sie zu [Google Play Console](https://play.google.com/console) → Einrichtung → API-Zugriff * Klicken Sie auf “Neues Service Account erstellen” * Folgen Sie dem Link zur Google Cloud Console * Klicken Sie auf “Service Account erstellen” * Geben Sie einen Namen ein (z. B. “Capgo CI/CD”) * Erteilen Sie die Rolle “Service Account User” * Klicken Sie auf “Fertig” 2. **JSON-Schlüssel erstellen** * Finden Sie in der Google Cloud Console Ihr Service Account * Klicken Sie auf die Service Account-E-Mail * Gehen Sie zum Tab “Schlüssel” * Klicken Sie auf “Schlüssel hinzufügen” → “Neuen Schlüssel erstellen” * Wählen Sie das Format “JSON” * Laden Sie die JSON-Datei herunter (sicher aufbewahren!) 3. **Berechtigungen in der Play Console erteilen** * Gehen Sie zurück zur Google Play Console → Einrichtung → API-Zugriff * Finden Sie Ihr Service Account in der Liste * Klicken Sie auf “Zugriff gewähren” * Wählen Sie unter “App-Berechtigungen” Ihre App aus * Erteilen Sie unter “Kontoberechtigungen”: * **Releases**: “App-Informationen anzeigen und Massenberichte herunterladen (schreibgeschützt)” * **Releases**: “Entwurfsreleases erstellen, bearbeiten und löschen” * **Releases**: “In Produktion veröffentlichen, Geräte ausschließen und Play App Signing verwenden” * Klicken Sie auf “Benutzer einladen” 4. **Einladung annehmen** * Das Service Account erhält eine Einladungs-E-Mail * Akzeptieren Sie die Einladung, um die Berechtigungen zu aktivieren Caution Die Service Account JSON-Datei enthält vertrauliche Zugangsdaten. Committen Sie sie niemals in die Versionskontrolle oder teilen Sie sie öffentlich. ## Für Android erstellen [Section titled “Für Android erstellen”](#für-android-erstellen) ### Debug-Build [Section titled “Debug-Build”](#debug-build) Perfekt zum Testen ohne Signatur: ```bash npx @capgo/cli@latest build com.example.app \ --platform android \ --build-mode debug ``` Dies erstellt ein Debug-APK, das auf jedem Gerät zum Testen installiert werden kann. ### Release-Build [Section titled “Release-Build”](#release-build) Für Play Store-Übermittlung: ```bash npx @capgo/cli@latest build com.example.app \ --platform android \ --build-mode release ``` Erfordert, dass Signaturzugangsdaten als Umgebungsvariablen konfiguriert sind. ## CI/CD-Integration [Section titled “CI/CD-Integration”](#cicd-integration) ### GitHub Actions-Beispiel [Section titled “GitHub Actions-Beispiel”](#github-actions-beispiel) ```yaml name: Build Android App on: push: branches: [main] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - name: Setup Node.js uses: actions/setup-node@v6 with: node-version: '24' - name: Install dependencies run: npm ci - name: Build web assets run: npm run build - name: Sync Capacitor run: npx cap sync android - name: Build Android app env: CAPGO_TOKEN: ${{ secrets.CAPGO_TOKEN }} ANDROID_KEYSTORE_FILE: ${{ secrets.ANDROID_KEYSTORE }} KEYSTORE_KEY_ALIAS: ${{ secrets.KEYSTORE_ALIAS }} KEYSTORE_KEY_PASSWORD: ${{ secrets.KEYSTORE_KEY_PASSWORD }} KEYSTORE_STORE_PASSWORD: ${{ secrets.KEYSTORE_STORE_PASSWORD }} PLAY_CONFIG_JSON: ${{ secrets.PLAY_STORE_CONFIG }} run: | npx @capgo/cli@latest build ${{ secrets.APP_ID }} \ --platform android \ --build-mode release ``` ## Details zum Build-Prozess [Section titled “Details zum Build-Prozess”](#details-zum-build-prozess) ### Was passiert während eines Android-Builds [Section titled “Was passiert während eines Android-Builds”](#was-passiert-während-eines-android-builds) 1. **Sandbox-Initialisierung** (\~5 Sekunden) * Sicherer Container wird gestartet * Android SDK und Gradle geladen * Isoliertes Dateisystem erstellt 2. **Projekt-Setup** (\~20 Sekunden) * Projekt-Zip von R2 heruntergeladen * In Build-Verzeichnis extrahiert * Signaturzugangsdaten eingefügt 3. **Gradle-Build** (2-4 Minuten) * Abhängigkeiten heruntergeladen * APK/AAB-Kompilierung * ProGuard/R8-Optimierung (Release-Modus) * Code-Signatur angewendet 4. **Play Store-Upload** (30 Sekunden, falls konfiguriert) * AAB zur Play Console hochgeladen * Release-Track konfiguriert * Übermittlung initiiert 5. **Bereinigung** (sofort) * Alle Dateien gelöscht * Container zerstört * Keine Artefakte gespeichert ### Build-Stack [Section titled “Build-Stack”](#build-stack) Unsere Android-Build-Umgebung umfasst: * **Java**: OpenJDK 17 * **Android SDK**: Neueste stabile Version * **Gradle**: 8.x * **Build Tools**: 34.x * **Node.js**: 18.x (LTS) * **NPM**: Neueste stabile Version ## Build-Ausgabe [Section titled “Build-Ausgabe”](#build-ausgabe) ### APK vs AAB [Section titled “APK vs AAB”](#apk-vs-aab) * **APK** (Android Package): Installierbare Datei für direkte Installation * **AAB** (Android App Bundle): Von Google Play empfohlenes Format (kleinere Downloads für Benutzer) Standardmäßig erstellen Capgo-Builds: * **Debug-Modus**: APK * **Release-Modus**: AAB (optimiert für Play Store) ## Build-Zeiten [Section titled “Build-Zeiten”](#build-zeiten) Typische Android-Build-Zeiten: | Build-Typ | Durchschnittliche Zeit | | ----------------------- | ---------------------- | | Debug | 2-3 Minuten | | Release (ohne ProGuard) | 3-4 Minuten | | Release (mit ProGuard) | 4-6 Minuten | Note Android-Builds kosten **1× den Basistarif** (halb so viel wie iOS-Builds). ## Build-Varianten [Section titled “Build-Varianten”](#build-varianten) ### Benutzerdefinierte Build-Varianten [Section titled “Benutzerdefinierte Build-Varianten”](#benutzerdefinierte-build-varianten) Wenn Ihre App benutzerdefinierte Build-Varianten hat (z. B. `staging`, `production`), verwenden Sie `build-config`: ```bash npx @capgo/cli@latest build com.example.app \ --platform android \ --build-mode release \ --build-config '{"variant":"staging"}' ``` Dies erstellt die Variante `stagingRelease`. ### Flavor-Dimensionen [Section titled “Flavor-Dimensionen”](#flavor-dimensionen) Für Apps mit Flavor-Dimensionen: ```bash --build-config '{"flavor":"premium","variant":"production"}' ``` Dies erstellt die Variante `premiumProductionRelease`. ## Fehlerbehebung [Section titled “Fehlerbehebung”](#fehlerbehebung) ### Häufige Probleme [Section titled “Häufige Probleme”](#häufige-probleme) **“Keystore password incorrect”** * Überprüfen Sie, ob KEYSTORE\_STORE\_PASSWORD mit Ihrem Keystore übereinstimmt * Stellen Sie sicher, dass KEYSTORE\_KEY\_PASSWORD mit Ihrem Key-Alias-Passwort übereinstimmt * Prüfen Sie auf zusätzliche Leerzeichen oder Sonderzeichen **“Key alias not found”** * Überprüfen Sie, ob KEYSTORE\_KEY\_ALIAS genau übereinstimmt (Groß-/Kleinschreibung beachten) * Aliase auflisten: `keytool -list -keystore my-release-key.keystore` **“Gradle build failed”** * Überprüfen Sie die Build-Protokolle auf den spezifischen Fehler * Stellen Sie sicher, dass Ihre App lokal mit `./gradlew assembleRelease` erstellt wird * Überprüfen Sie, ob alle nativen Abhängigkeiten in `build.gradle` sind **“Play Store upload failed”** * Überprüfen Sie, ob die Service Account JSON gültig ist * Stellen Sie sicher, dass das Service Account korrekte Berechtigungen in der Play Console hat * Prüfen Sie, ob die App ordnungsgemäß in der Play Console eingerichtet ist **“Build timeout”** * Große Apps benötigen möglicherweise Optimierung * Prüfen Sie, ob unnötige Abhängigkeiten entfernt werden können * Kontaktieren Sie den Support, wenn Builds ständig ein Timeout erreichen ### Debug-Protokolle [Section titled “Debug-Protokolle”](#debug-protokolle) Achten Sie auf diese wichtigen Phasen in den Build-Protokollen: ```plaintext → Downloading dependencies... → Running Gradle assembleRelease... → Signing APK/AAB... → Uploading to Play Store... ✔ Build succeeded ``` Wenn ein Build fehlschlägt, wird der spezifische Gradle-Fehler in den Protokollen angezeigt. ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) ### 1. Zuerst lokal testen [Section titled “1. Zuerst lokal testen”](#1-zuerst-lokal-testen) Stellen Sie immer sicher, dass Ihr Android-Build lokal funktioniert: ```bash cd android ./gradlew assembleRelease # oder ./gradlew bundleRelease ``` ### 2. Sichern Sie Ihren Keystore [Section titled “2. Sichern Sie Ihren Keystore”](#2-sichern-sie-ihren-keystore) * Committen Sie niemals Keystores in die Versionskontrolle * Speichern Sie in sicherer Secrets-Verwaltung (1Password, AWS Secrets Manager usw.) * Bewahren Sie Backup-Kopien an mehreren sicheren Orten auf * Dokumentieren Sie Passwörter in einem sicheren Passwort-Manager ### 3. Versionsverwaltung [Section titled “3. Versionsverwaltung”](#3-versionsverwaltung) Capgo liest die Version aus Ihrer `capacitor.config.json`: ```json { "appId": "com.example.app", "appName": "My App", "version": "1.0.0", "build": "1" } ``` Erhöhen Sie die `build`-Nummer für jedes Release. ### 4. ProGuard-Konfiguration [Section titled “4. ProGuard-Konfiguration”](#4-proguard-konfiguration) Für Release-Builds stellen Sie sicher, dass ProGuard-Regeln ordnungsgemäß konfiguriert sind: android/app/proguard-rules.pro ```plaintext -keep class com.getcapacitor.** { *; } -keep @com.getcapacitor.annotation.CapacitorPlugin public class * { @com.getcapacitor.annotation.PluginMethod public ; } ``` ### 5. Build-Größe überwachen [Section titled “5. Build-Größe überwachen”](#5-build-größe-überwachen) Behalten Sie die APK/AAB-Größe im Auge, um sicherzustellen, dass sie optimiert ist: ```plaintext Die CLI zeigt die endgültige Größe an: → APK size: 12.4 MB ``` Wenn Ihre App groß ist (>50 MB), erwägen Sie: * ProGuard/R8 aktivieren * AAB-Format verwenden (dynamische Bereitstellung) * Bilder und Assets optimieren ## Play Store-Übermittlung [Section titled “Play Store-Übermittlung”](#play-store-übermittlung) ### Automatische Übermittlung [Section titled “Automatische Übermittlung”](#automatische-übermittlung) Mit konfiguriertem PLAY\_CONFIG\_JSON werden Builds automatisch zum internen Test-Track der Play Console hochgeladen. ### Manuelle Übermittlung [Section titled “Manuelle Übermittlung”](#manuelle-übermittlung) Wenn Sie manuelle Übermittlung bevorzugen: 1. Führen Sie den Build ohne PLAY\_CONFIG\_JSON aus 2. Laden Sie das AAB von Build-Artefakten herunter (falls konfiguriert) 3. Laden Sie es manuell zur Play Console hoch ## Nächste Schritte [Section titled “Nächste Schritte”](#nächste-schritte) * [iOS Builds](/docs/cli/cloud-build/ios/) - iOS-Builds konfigurieren * [Fehlerbehebung](/docs/cli/cloud-build/troubleshooting/) - Häufige Build-Probleme * [CLI-Referenz](/docs/cli/reference/build/) - Vollständige Befehlsdokumentation ## Brauchen Sie Hilfe? [Section titled “Brauchen Sie Hilfe?”](#brauchen-sie-hilfe) * [Fehlerbehebungsanleitung](/docs/cli/cloud-build/troubleshooting/) * [Discord-Community](https://discord.com/invite/VnYRvBfgA6) * E-Mail: # Verwaltung von Zugangsdaten > Speichern und verwalten Sie Build-Zugangsdaten lokal für iOS- und Android-Builds Verwalten Sie Ihre iOS- und Android-Build-Zugangsdaten lokal für bequeme Cloud-Builds. ## Übersicht [Section titled “Übersicht”](#übersicht) Die Capgo CLI ermöglicht es Ihnen, Build-Zugangsdaten lokal auf Ihrem Computer im Ordner `.capgo-credentials` zu speichern. Wenn Sie einen Build ausführen, werden diese Zugangsdaten automatisch verwendet und sicher an die Build-Server von Capgo gesendet. Benötigen Sie Hilfe beim Erhalt von Zugangsdaten? Wenn Sie Ihre Zertifikate und Zugangsdaten noch nicht haben, lesen Sie diese umfassenden Anleitungen: **iOS:** * [So erhalten Sie iOS-Zertifikate](/docs/cli/cloud-build/ios/#how-to-get-ios-certificates-and-provisioning-profiles) - Schritt-für-Schritt-Anleitung * [iOS-Zertifikate-Anleitung (Capgo)](https://capgo.app/cloud/native-builds/certificates/ios/) - Detailliertes Tutorial mit Screenshots * [Blog: Automatische iOS-Builds](https://capgo.app/blog/automatic-capacitor-ios-build-github-action/) - Vollständige CI/CD-Einrichtung **Android:** * [Einen Keystore erstellen](/docs/cli/cloud-build/android/#creating-a-keystore) - Schritt-für-Schritt-Anleitung * [Android-Zertifikate-Anleitung (Capgo)](https://capgo.app/cloud/native-builds/certificates/android/) - Detailliertes Tutorial mit Screenshots * [Blog: Automatische Android-Builds](https://capgo.app/blog/automatic-capacitor-android-build-github-action/) - Vollständige CI/CD-Einrichtung Sicherheitsgarantie **Ihre Zugangsdaten werden NIEMALS dauerhaft auf Capgo-Servern gespeichert:** * ✅ Werden NUR während des aktiven Build-Prozesses verwendet * ✅ Automatisch nach Build-Abschluss gelöscht * ✅ Maximale Aufbewahrung: 24 Stunden (auch bei fehlgeschlagenem Build) * ✅ Apps werden direkt an App Store/Play Store gesendet - wir speichern NICHTS * ✅ Sicher über HTTPS übertragen ## Befehle [Section titled “Befehle”](#befehle) ### Zugangsdaten speichern [Section titled “Zugangsdaten speichern”](#zugangsdaten-speichern) Speichern Sie Ihre Build-Zugangsdaten lokal zur automatischen Verwendung: ```bash npx @capgo/cli build credentials save --platform [optionen] ``` ### Zugangsdaten auflisten [Section titled “Zugangsdaten auflisten”](#zugangsdaten-auflisten) Zeigen Sie derzeit gespeicherte Zugangsdaten an (Passwörter sind maskiert): ```bash npx @capgo/cli build credentials list ``` ### Zugangsdaten löschen [Section titled “Zugangsdaten löschen”](#zugangsdaten-löschen) Entfernen Sie gespeicherte Zugangsdaten von Ihrem lokalen Computer: ```bash # Alle Zugangsdaten löschen npx @capgo/cli build credentials clear # Nur iOS-Zugangsdaten löschen npx @capgo/cli build credentials clear --platform ios # Nur Android-Zugangsdaten löschen npx @capgo/cli build credentials clear --platform android ``` ## iOS-Zugangsdaten speichern [Section titled “iOS-Zugangsdaten speichern”](#ios-zugangsdaten-speichern) Note **Haben Sie noch keine iOS-Zertifikate?** Siehe die [iOS Builds-Anleitung](/docs/cli/cloud-build/ios/#how-to-get-ios-certificates-and-provisioning-profiles) für Anweisungen zum Erstellen von Zertifikaten und Bereitstellungsprofilen. ### Vollständiges Beispiel [Section titled “Vollständiges Beispiel”](#vollständiges-beispiel) ```bash npx @capgo/cli build credentials save \ --platform ios \ --certificate ./cert.p12 \ --p12-password "YourP12Password" \ --provisioning-profile ./profile.mobileprovision \ --apple-key ./AuthKey_ABC1234567.p8 \ --apple-key-id "ABC1234567" \ --apple-issuer-id "00000000-0000-0000-0000-000000000000" \ --apple-team-id "TEAM123456" ``` ### iOS-Optionen [Section titled “iOS-Optionen”](#ios-optionen) | Option | Beschreibung | Erforderlich | | ------------------------------------ | --------------------------------------------------------- | ----------------- | | `--certificate ` | Pfad zur .p12-Zertifikatsdatei | Ja (Release) | | `--p12-password ` | Passwort für das .p12-Zertifikat | Ja (Release) | | `--provisioning-profile ` | Pfad zur .mobileprovision-Datei | Ja (Release) | | `--provisioning-profile-prod ` | Produktions-Bereitstellungsprofil (optional) | Nein | | `--apple-key ` | Pfad zum App Store Connect API .p8-Schlüssel | Ja (Übermittlung) | | `--apple-key-id ` | App Store Connect API Key ID | Ja (Übermittlung) | | `--apple-issuer-id ` | App Store Connect API Issuer ID (UUID) | Ja (Übermittlung) | | `--apple-team-id ` | App Store Connect Team ID | Ja (Übermittlung) | | `--apple-id ` | Apple ID E-Mail (alternative Authentifizierung) | Nein | | `--apple-app-password ` | App-spezifisches Passwort (alternative Authentifizierung) | Nein | ### Was wird gespeichert [Section titled “Was wird gespeichert”](#was-wird-gespeichert) Wenn Sie iOS-Zugangsdaten speichern, führt die CLI folgende Schritte aus: 1. Liest die Zertifikats- und Bereitstellungsprofildateien 2. Konvertiert sie in Base64-Kodierung 3. Speichert die Zugangsdaten im Ordner `.capgo-credentials` 4. Speichert Passwörter und IDs als Klartext (nur lokale Dateien) Die gespeicherte Dateistruktur: ```json { "ios": { "BUILD_CERTIFICATE_BASE64": "...", "BUILD_PROVISION_PROFILE_BASE64": "...", "APPLE_KEY_CONTENT": "...", "P12_PASSWORD": "...", "APPLE_KEY_ID": "ABC1234567", "APPLE_ISSUER_ID": "...", "APP_STORE_CONNECT_TEAM_ID": "TEAM123456" } } ``` ## Android-Zugangsdaten speichern [Section titled “Android-Zugangsdaten speichern”](#android-zugangsdaten-speichern) Note **Haben Sie noch keinen Keystore?** Siehe die [Android Builds-Anleitung](/docs/cli/cloud-build/android/#creating-a-keystore) für Anweisungen zum Erstellen eines Keystores und Einrichten von Play Store-Zugangsdaten. ### Vollständiges Beispiel [Section titled “Vollständiges Beispiel”](#vollständiges-beispiel-1) ```bash npx @capgo/cli build credentials save \ --platform android \ --keystore ./release.keystore \ --keystore-alias "my-key-alias" \ --keystore-key-password "KeyPassword123" \ --keystore-store-password "StorePassword123" \ --play-config ./play-store-service-account.json ``` ### Android-Optionen [Section titled “Android-Optionen”](#android-optionen) | Option | Beschreibung | Erforderlich | | -------------------------------------- | ---------------------------------------- | ----------------- | | `--keystore ` | Pfad zur .keystore- oder .jks-Datei | Ja (Release) | | `--keystore-alias ` | Key-Alias im Keystore | Ja (Release) | | `--keystore-key-password ` | Passwort für den Key-Alias | Ja (Release) | | `--keystore-store-password ` | Passwort für den Keystore | Ja (Release) | | `--play-config ` | Pfad zur Play Store Service Account JSON | Ja (Übermittlung) | ### Was wird gespeichert [Section titled “Was wird gespeichert”](#was-wird-gespeichert-1) Wenn Sie Android-Zugangsdaten speichern, führt die CLI folgende Schritte aus: 1. Liest den Keystore und die Service Account JSON-Dateien 2. Konvertiert sie in Base64-Kodierung 3. Speichert die Zugangsdaten im Ordner `.capgo-credentials` 4. Speichert Passwörter und Alias als Klartext (nur lokale Dateien) Die gespeicherte Dateistruktur: ```json { "android": { "ANDROID_KEYSTORE_FILE": "...", "PLAY_CONFIG_JSON": "...", "KEYSTORE_KEY_ALIAS": "my-key-alias", "KEYSTORE_KEY_PASSWORD": "...", "KEYSTORE_STORE_PASSWORD": "..." } } ``` ## Gespeicherte Zugangsdaten verwenden [Section titled “Gespeicherte Zugangsdaten verwenden”](#gespeicherte-zugangsdaten-verwenden) Sobald Sie Zugangsdaten gespeichert haben, werden sie beim Build automatisch verwendet: ```bash # Zugangsdaten werden automatisch aus dem .capgo-credentials-Ordner geladen npx @capgo/cli build com.example.app --platform ios ``` Sie können gespeicherte Zugangsdaten auch mithilfe von Umgebungsvariablen überschreiben: ```bash # Umgebungsvariablen haben Vorrang vor gespeicherten Zugangsdaten BUILD_CERTIFICATE_BASE64="..." \ P12_PASSWORD="different-password" \ npx @capgo/cli build com.example.app --platform ios ``` **Rangfolge:** 1. Umgebungsvariablen (höchste Priorität) 2. Gespeicherte Zugangsdaten im Ordner `.capgo-credentials` 3. Keine Zugangsdaten (niedrigste Priorität) ## Gespeicherte Zugangsdaten anzeigen [Section titled “Gespeicherte Zugangsdaten anzeigen”](#gespeicherte-zugangsdaten-anzeigen) Listen Sie auf, welche Zugangsdaten Sie gespeichert haben: ```bash npx @capgo/cli build credentials list ``` Beispielausgabe: ```plaintext 📋 Gespeicherte Build-Zugangsdaten: iOS-Zugangsdaten: ✓ Zertifikat (base64) ✓ Bereitstellungsprofil (base64) ✓ Apple Key Content (base64) ✓ P12-Passwort: ******** ✓ Apple Key ID: ABC1234567 ✓ Apple Issuer ID: 00000000-0000-0000-0000-000000000000 ✓ Team ID: TEAM123456 Android-Zugangsdaten: ✓ Keystore (base64) ✓ Play Store Config (base64) ✓ Keystore-Alias: my-key-alias ✓ Key-Passwort: ******** ✓ Store-Passwort: ******** Speicherort: .capgo-credentials/ 🔒 Diese Zugangsdaten werden nur lokal auf Ihrem Computer gespeichert. Beim Erstellen werden sie an Capgo gesendet, aber NIEMALS dort gespeichert. Sie werden nach Build-Abschluss automatisch gelöscht (max. 24 Stunden). ``` ## Bewährte Methoden für Sicherheit [Section titled “Bewährte Methoden für Sicherheit”](#bewährte-methoden-für-sicherheit) ### Sicherheit bei lokaler Speicherung [Section titled “Sicherheit bei lokaler Speicherung”](#sicherheit-bei-lokaler-speicherung) 1. **Dateiberechtigungen** ```bash # Stellen Sie sicher, dass der Zugangsdatenordner nicht von anderen lesbar ist chmod 700 .capgo-credentials chmod 600 .capgo-credentials/* ``` 2. **Niemals Zugangsdaten committen** ```bash # Zu .gitignore hinzufügen echo ".capgo-credentials/" >> .gitignore ``` 3. **Zugangsdaten trennen** * Verwenden Sie unterschiedliche Zugangsdaten für lokale Entwicklung vs. CI/CD * Rotieren Sie Zugangsdaten regelmäßig * Teilen Sie keine Zugangsdaten zwischen Teammitgliedern ### CI/CD-Verwendung [Section titled “CI/CD-Verwendung”](#cicd-verwendung) Für CI/CD-Umgebungen **bevorzugen Sie Umgebungsvariablen** gegenüber gespeicherten Zugangsdaten: ```yaml # GitHub Actions env: BUILD_CERTIFICATE_BASE64: ${{ secrets.IOS_CERTIFICATE }} P12_PASSWORD: ${{ secrets.P12_PASSWORD }} # ... weitere Secrets ``` Dies ist sicherer, weil: * Secrets von Ihrer CI/CD-Plattform verwaltet werden * Keine Zugangsdatendateien auf Runnern * Einfache Rotation und Zugriffskontrolle * Audit-Trails für Secret-Verwendung ### Zugangsdaten-Rotation [Section titled “Zugangsdaten-Rotation”](#zugangsdaten-rotation) Rotieren Sie Ihre Zugangsdaten regelmäßig: 1. **iOS**: Generieren Sie jährlich neue Zertifikate und API-Schlüssel 2. **Android**: Ändern Sie Keystore-Passwörter jährlich 3. **Nach Teamänderungen**: Rotieren Sie, wenn Teammitglieder ausscheiden Gespeicherte Zugangsdaten aktualisieren: ```bash # Führen Sie den save-Befehl mit neuen Zugangsdaten erneut aus npx @capgo/cli build credentials save --platform ios --certificate ./new-cert.p12 ... ``` ## Fehlerbehebung [Section titled “Fehlerbehebung”](#fehlerbehebung) ### ”No credentials found” [Section titled “”No credentials found””](#no-credentials-found) Wenn der Build sagt, dass keine Zugangsdaten gefunden wurden: 1. **Prüfen Sie, ob Zugangsdaten gespeichert sind**: ```bash npx @capgo/cli build credentials list ``` 2. **Zugangsdaten speichern, falls fehlend**: ```bash npx @capgo/cli build credentials save --platform ios ... ``` 3. **Überprüfen Sie, ob der Zugangsdatenordner existiert**: ```bash ls -la .capgo-credentials/ ``` ### “Permission denied” beim Lesen von Zugangsdaten [Section titled ““Permission denied” beim Lesen von Zugangsdaten”](#permission-denied-beim-lesen-von-zugangsdaten) Dateiberechtigungen korrigieren: ```bash chmod 700 .capgo-credentials chmod 600 .capgo-credentials/* ``` ### Zugangsdaten werden nicht verwendet [Section titled “Zugangsdaten werden nicht verwendet”](#zugangsdaten-werden-nicht-verwendet) Überprüfen Sie, dass die korrekte Plattform angegeben ist: ```bash # Stellen Sie sicher, dass --platform mit gespeicherten Zugangsdaten übereinstimmt npx @capgo/cli build com.example.app --platform ios # Verwendet iOS-Zugangsdaten npx @capgo/cli build com.example.app --platform android # Verwendet Android-Zugangsdaten ``` ### Zugangsdaten löschen und erneut speichern [Section titled “Zugangsdaten löschen und erneut speichern”](#zugangsdaten-löschen-und-erneut-speichern) Wenn Zugangsdaten beschädigt zu sein scheinen: ```bash # Alle Zugangsdaten löschen npx @capgo/cli build credentials clear # Erneut speichern npx @capgo/cli build credentials save --platform ios ... ``` ## Migration von Umgebungsvariablen [Section titled “Migration von Umgebungsvariablen”](#migration-von-umgebungsvariablen) Wenn Sie derzeit Umgebungsvariablen verwenden, können Sie zu gespeicherten Zugangsdaten migrieren: 1. **Extrahieren Sie Ihre aktuellen Umgebungsvariablen** ```bash echo $BUILD_CERTIFICATE_BASE64 # Überprüfen Sie, ob sie existieren ``` 2. **Dekodieren Sie Base64-Dateien zurück zu Originaldateien** (falls erforderlich) ```bash echo "$BUILD_CERTIFICATE_BASE64" | base64 -d > cert.p12 echo "$BUILD_PROVISION_PROFILE_BASE64" | base64 -d > profile.mobileprovision ``` 3. **Speichern Sie mithilfe der CLI** ```bash npx @capgo/cli build credentials save \ --platform ios \ --certificate ./cert.p12 \ --provisioning-profile ./profile.mobileprovision \ --p12-password "$P12_PASSWORD" \ --apple-key-id "$APPLE_KEY_ID" \ --apple-issuer-id "$APPLE_ISSUER_ID" \ --apple-team-id "$APP_STORE_CONNECT_TEAM_ID" ``` 4. **Testen Sie den Build** ```bash npx @capgo/cli build com.example.app --platform ios ``` 5. **Entfernen Sie Umgebungsvariablen** (optional) ```bash unset BUILD_CERTIFICATE_BASE64 BUILD_PROVISION_PROFILE_BASE64 ``` ## Dateispeicherort [Section titled “Dateispeicherort”](#dateispeicherort) Zugangsdaten werden im Ordner `.capgo-credentials` gespeichert: * **macOS/Linux**: `.capgo-credentials/` (in Ihrem Projektverzeichnis oder Home-Verzeichnis) * **Windows**: `.capgo-credentials\` (in Ihrem Projektverzeichnis oder Home-Verzeichnis) Der Ordner wird automatisch erstellt, wenn Sie zum ersten Mal Zugangsdaten speichern. ## Nächste Schritte [Section titled “Nächste Schritte”](#nächste-schritte) * [Erste Schritte](/docs/cli/cloud-build/getting-started/) - Erstellen Sie Ihren ersten Build * [iOS Builds](/docs/cli/cloud-build/ios/) - iOS-spezifische Build-Konfiguration * [Android Builds](/docs/cli/cloud-build/android/) - Android-spezifische Build-Konfiguration * [Fehlerbehebung](/docs/cli/cloud-build/troubleshooting/) - Häufige Probleme und Lösungen ## Brauchen Sie Hilfe? [Section titled “Brauchen Sie Hilfe?”](#brauchen-sie-hilfe) * 📚 [Fehlerbehebungsanleitung](/docs/cli/cloud-build/troubleshooting/) * 💬 [Discord-Community](https://discord.com/invite/VnYRvBfgA6) * 📧 E-Mail: # Erste Schritte > Erstellen Sie Ihren ersten nativen Build mit Capgo Cloud Build Beginnen Sie mit Capgo Cloud Build und erstellen Sie Ihren ersten iOS- oder Android-nativen Build in Minuten. ## Was Sie benötigen [Section titled “Was Sie benötigen”](#was-sie-benötigen) Bevor Sie beginnen, stellen Sie sicher, dass Sie Folgendes haben: * Eine Capacitor-App, die lokal erfolgreich erstellt wird * Node.js 20 oder höher installiert * Ein Capgo-Konto mit aktivem Abonnement * Ihre App bereits in Capgo registriert (führen Sie `npx @capgo/cli@latest app add` aus, falls nicht) * **Build-Zugangsdaten konfiguriert** (Zertifikate, Keystores) - siehe unten ## Vor Ihrem ersten Build [Section titled “Vor Ihrem ersten Build”](#vor-ihrem-ersten-build) ⚠️ Richten Sie zuerst Zugangsdaten ein **Erforderlich vor dem Erstellen:** Sie müssen Ihre Build-Zugangsdaten konfigurieren (Zertifikate für iOS, Keystores für Android). [Zugangsdaten einrichten →](/docs/cli/cloud-build/credentials/) ## Schnellstart [Section titled “Schnellstart”](#schnellstart) 1. **Build-Zugangsdaten einrichten** Bevor Sie erstellen können, müssen Sie Ihre Zugangsdaten lokal speichern: **Für iOS:** ```bash npx @capgo/cli build credentials save \ --platform ios \ --certificate ./cert.p12 \ --p12-password "password" \ --provisioning-profile ./profile.mobileprovision \ --apple-key ./AuthKey.p8 \ --apple-key-id "KEY123" \ --apple-issuer-id "issuer-uuid" \ --apple-team-id "team-id" ``` **Für Android:** ```bash npx @capgo/cli build credentials save \ --platform android \ --keystore ./release.keystore \ --keystore-alias "my-key" \ --keystore-key-password "key-pass" \ --keystore-store-password "store-pass" ``` Siehe die [vollständige Zugangsdatenanleitung](/docs/cli/cloud-build/credentials/) für Details. 2. **Lokalen Build überprüfen** Stellen Sie zunächst sicher, dass Ihre App lokal ohne Fehler erstellt wird: ```bash # Erstellen Sie Ihre Web-Assets npm run build # Mit Capacitor synchronisieren npx cap sync # Lokalen Build testen (optional, aber empfohlen) npx cap open ios # Für iOS npx cap open android # Für Android ``` 3. **Bei Capgo authentifizieren** Legen Sie Ihren Capgo API-Schlüssel fest (falls noch nicht konfiguriert): ```bash npx @capgo/cli@latest login ``` Oder setzen Sie die Umgebungsvariable: ```bash export CAPGO_TOKEN=your_api_key_here ``` 4. **Führen Sie Ihren ersten Build aus** Beginnen Sie mit einem Android-Debug-Build (am schnellsten zum Testen): ```bash npx @capgo/cli@latest build com.example.app \ --platform android \ --build-mode debug ``` Sie sehen Echtzeit-Protokolle, während Ihr Build fortschreitet: ```plaintext ✔ Creating build job... ✔ Uploading project (15.2 MB)... ✔ Build started 📝 Build logs: → Installing dependencies... → Running Gradle build... → Signing APK... ✔ Build succeeded in 3m 42s ``` 5. **Build-Status überprüfen** Die CLI fragt automatisch ab und zeigt den Build-Status an. Sobald abgeschlossen, sehen Sie: * Build-Zeit * Erfolgs-/Fehlerstatus * App an App Store/Play Store übermittelt (falls Zugangsdaten konfiguriert) ## Den Build-Prozess verstehen [Section titled “Den Build-Prozess verstehen”](#den-build-prozess-verstehen) Wenn Sie den Build-Befehl ausführen, passiert Folgendes: ```mermaid graph LR A[Ihr Computer] -->|1. Projekt zippen| B[Lokaler Temp] B -->|2. Hochladen| C[Capgo Cloud] C -->|3. Erstellen| D[Build-Server] D -->|4. Protokolle streamen| A D -->|5. Bereinigung| E[Automatisches Löschen] ``` 1. **Lokale Vorbereitung** - Ihr Projekt wird gezippt (ohne `node_modules` und Dotfiles) 2. **Upload** - Das Zip wird zu sicherem Cloud-Speicher hochgeladen (Cloudflare R2) 3. **Build-Ausführung** - Ihre App wird auf dedizierter Infrastruktur erstellt 4. **Protokoll-Streaming** - Echtzeit-Protokolle werden über Server-Sent Events zu Ihrem Terminal gestreamt 5. **Automatische Bereinigung** - Build-Artefakte werden gelöscht (Android: sofort, iOS: 24 Stunden) ## Ihr erster Produktions-Build [Section titled “Ihr erster Produktions-Build”](#ihr-erster-produktions-build) Sobald Sie überprüft haben, dass der Prozess funktioniert, erstellen Sie einen Produktions-Build: ### Android [Section titled “Android”](#android) ```bash npx @capgo/cli@latest build com.example.app \ --platform android \ --build-mode release ``` Sie müssen zuerst Signaturzugangsdaten konfigurieren. Siehe [Android Build-Konfiguration](/docs/cli/cloud-build/android/). ### iOS [Section titled “iOS”](#ios) ```bash npx @capgo/cli@latest build com.example.app \ --platform ios \ --build-mode release ``` iOS-Builds erfordern Signaturzertifikate und Bereitstellungsprofile. Siehe [iOS Build-Konfiguration](/docs/cli/cloud-build/ios/). ## Was wird erstellt [Section titled “Was wird erstellt”](#was-wird-erstellt) **Wichtig:** Capgo Cloud Build erstellt nur die **nativen Teile** Ihrer App (nativer iOS- und Android-Code). Sie sind verantwortlich für: * Erstellen Ihrer Web-Assets (`npm run build`) * Ausführen von `npx cap sync` vor dem Build * Sicherstellen, dass alle Abhängigkeiten in `package.json` sind Wir kümmern uns um: * Native iOS-Kompilierung (Xcode, Fastlane) * Native Android-Kompilierung (Gradle) * Code-Signatur * App Store-Übermittlung (falls konfiguriert) ## Build-Zeit & Kosten [Section titled “Build-Zeit & Kosten”](#build-zeit--kosten) Die Build-Zeit wird vom Start bis zur Fertigstellung gemessen: * **Android**: Typischerweise 3-5 Minuten (1× Abrechnungsmultiplikator) * **iOS**: Typischerweise 5-10 Minuten (2× Abrechnungsmultiplikator aufgrund von Mac-Hardwarekosten) Sie zahlen nur für die tatsächlich genutzte Build-Zeit. Keine versteckten Gebühren. ## Häufige Anwendungsfälle [Section titled “Häufige Anwendungsfälle”](#häufige-anwendungsfälle) ### CI/CD-Integration [Section titled “CI/CD-Integration”](#cicd-integration) Zu Ihrem GitHub Actions-Workflow hinzufügen: ```yaml - name: Build native app env: CAPGO_TOKEN: ${{ secrets.CAPGO_TOKEN }} run: | npm run build npx cap sync npx @capgo/cli@latest build ${{ secrets.APP_ID }} \ --platform both \ --build-mode release ``` ### Lokale Entwicklung [Section titled “Lokale Entwicklung”](#lokale-entwicklung) Builds lokal testen, bevor Sie committen: ```bash # Schneller Debug-Build zum Testen npm run build && npx cap sync npx @capgo/cli@latest build com.example.app \ --platform android \ --build-mode debug ``` ### Multi-Plattform-Builds [Section titled “Multi-Plattform-Builds”](#multi-plattform-builds) Gleichzeitig für beide Plattformen erstellen: ```bash npx @capgo/cli@latest build com.example.app \ --platform both \ --build-mode release ``` ## Nächste Schritte [Section titled “Nächste Schritte”](#nächste-schritte) Nachdem Sie Ihren ersten Build erstellt haben: * [iOS-Builds konfigurieren](/docs/cli/cloud-build/ios/) - Zertifikate und Profile einrichten * [Android-Builds konfigurieren](/docs/cli/cloud-build/android/) - Keystores und Play Store einrichten * [Fehlerbehebung](/docs/cli/cloud-build/troubleshooting/) - Häufige Probleme und Lösungen * [CLI-Referenz](/docs/cli/reference/build/) - Vollständige Befehlsdokumentation ## Brauchen Sie Hilfe? [Section titled “Brauchen Sie Hilfe?”](#brauchen-sie-hilfe) * Lesen Sie die [Fehlerbehebungsanleitung](/docs/cli/cloud-build/troubleshooting/) * Treten Sie unserer [Discord-Community](https://discord.com/invite/VnYRvBfgA6) bei * E-Mail-Support unter # iOS Builds > iOS-Apps mit Capgo Cloud Build konfigurieren und erstellen Erstellen und übermitteln Sie iOS-Apps an TestFlight und den App Store mithilfe der dedizierten Mac-Infrastruktur von Capgo. ## Bevor Sie erstellen [Section titled “Bevor Sie erstellen”](#bevor-sie-erstellen) ⚠️ Richten Sie zuerst iOS-Zugangsdaten ein **Erforderlich:** Sie müssen Ihre iOS-Zugangsdaten speichern, bevor Sie erstellen. [iOS-Zugangsdaten einrichten →](/docs/cli/cloud-build/credentials/#saving-ios-credentials) ## Wie iOS Builds funktionieren [Section titled “Wie iOS Builds funktionieren”](#wie-ios-builds-funktionieren) iOS-Builds laufen auf dedizierten Mac-Maschinen (Scaleway Mac minis M4), die bei Bedarf bereitgestellt werden: * **Hardware**: Apple Silicon Mac minis mit macOS 15 * **Build-Tool**: Xcode mit Fastlane (benutzerdefinierte Capgo-Konfiguration) * **Isolation**: Jeder Build läuft als separates macOS-Benutzerkonto * **Lebensdauer**: Maschinen haben 24-Stunden-Leases und werden automatisch bereinigt * **Sicherheit**: Alle Dateien und Benutzerkonten werden gelöscht, nachdem die Maschine freigegeben wurde ## Voraussetzungen [Section titled “Voraussetzungen”](#voraussetzungen) Bevor Sie für iOS erstellen, benötigen Sie: ### 1. Entwicklungsumgebung [Section titled “1. Entwicklungsumgebung”](#1-entwicklungsumgebung) * Mac-Computer mit installiertem Xcode (für anfängliche Zertifikatseinrichtung) * Gültiges Apple Developer-Konto ($99/Jahr) * Ihre App erstellt erfolgreich lokal mit `npx cap open ios` ### 2. Code-Signatur-Zertifikate [Section titled “2. Code-Signatur-Zertifikate”](#2-code-signatur-zertifikate) Sie benötigen einen dieser Zertifikatstypen je nach Build: | Build-Typ | Erforderliches Zertifikat | Bereitstellungsprofil | | --------------- | ------------------------- | --------------------- | | **Development** | Apple Development | Development Profile | | **Ad Hoc** | Apple Distribution | Ad Hoc Profile | | **App Store** | Apple Distribution | App Store Profile | #### So erhalten Sie iOS-Zertifikate und Bereitstellungsprofile [Section titled “So erhalten Sie iOS-Zertifikate und Bereitstellungsprofile”](#so-erhalten-sie-ios-zertifikate-und-bereitstellungsprofile) Tip Für detaillierte Schritt-für-Schritt-Anleitungen mit Screenshots besuchen Sie: * [iOS-Zertifikate-Anleitung](https://capgo.app/cloud/native-builds/certificates/ios/) * [Blog: Automatische iOS-Builds mit GitHub Actions](https://capgo.app/blog/automatic-capacitor-ios-build-github-action/) **Schnellübersicht:** 1. **Certificate Signing Request (CSR) erstellen** * Öffnen Sie Schlüsselbundverwaltung auf Ihrem Mac * Gehen Sie zu Schlüsselbundverwaltung → Zertifikatsassistent → Zertifikat einer Zertifizierungsinstanz anfordern * Geben Sie Ihre E-Mail und Ihren Namen ein, wählen Sie “Auf Festplatte gesichert” * Speichern Sie die `.certSigningRequest`-Datei 2. **Zertifikat im Apple Developer Portal generieren** * Gehen Sie zu [Apple Developer Certificates](https://developer.apple.com/account/resources/certificates) * Klicken Sie auf ”+”, um ein neues Zertifikat zu erstellen * Wählen Sie den Zertifikatstyp (iOS Distribution für App Store-Builds) * Laden Sie Ihre CSR-Datei hoch * Laden Sie das Zertifikat (`.cer`-Datei) herunter 3. **Zertifikat als .p12 exportieren** * Doppelklicken Sie auf die heruntergeladene `.cer`-Datei, um sie zum Schlüsselbund hinzuzufügen * Suchen Sie in der Schlüsselbundverwaltung Ihr Zertifikat unter “Meine Zertifikate” * Rechtsklick → “Apple Distribution…” exportieren * Speichern Sie im `.p12`-Format und legen Sie ein Passwort fest (dieses Passwort speichern!) 4. **Bereitstellungsprofil erstellen** * Gehen Sie zu [Apple Developer Profiles](https://developer.apple.com/account/resources/profiles) * Klicken Sie auf ”+”, um ein neues Profil zu erstellen * Wählen Sie den Profiltyp (App Store für Produktions-Builds) * Wählen Sie Ihre App ID aus * Wählen Sie das gerade erstellte Zertifikat aus * Laden Sie die `.mobileprovision`-Datei herunter ### 3. App Store Connect API-Schlüssel (Empfohlen) [Section titled “3. App Store Connect API-Schlüssel (Empfohlen)”](#3-app-store-connect-api-schlüssel-empfohlen) Für automatische TestFlight-Übermittlung erstellen Sie einen API-Schlüssel: 1. Gehen Sie zu [App Store Connect](https://appstoreconnect.apple.com/) → Benutzer und Zugriff → Schlüssel 2. Klicken Sie auf die Schaltfläche ”+”, um einen neuen Schlüssel zu erstellen 3. Geben Sie ihm einen Namen (z. B. “Capgo CI”) und wählen Sie die Rolle “Developer” 4. Laden Sie die `.p8`-Datei herunter (Sie können sie nur einmal herunterladen!) 5. Notieren Sie die **Key ID** und **Issuer ID** Note Sie finden Ihre **Team ID** auch in Ihrem Apple Developer-Konto unter Mitgliedschaftsdetails. ## Build-Konfiguration [Section titled “Build-Konfiguration”](#build-konfiguration) ### Erforderliche Umgebungsvariablen [Section titled “Erforderliche Umgebungsvariablen”](#erforderliche-umgebungsvariablen) Legen Sie diese Zugangsdaten vor dem Erstellen fest: ```bash # iOS-Signatur (Erforderlich) BUILD_CERTIFICATE_BASE64="" BUILD_PROVISION_PROFILE_BASE64="" P12_PASSWORD="" # App Store Connect API (für Übermittlung) APPLE_KEY_ID="ABC1234567" APPLE_ISSUER_ID="00000000-0000-0000-0000-000000000000" APPLE_KEY_CONTENT="" # Zusätzliche Konfiguration APP_STORE_CONNECT_TEAM_ID="1234567890" APPLE_PROFILE_NAME="App Store com.example.app" ``` ### Base64-Zugangsdaten generieren [Section titled “Base64-Zugangsdaten generieren”](#base64-zugangsdaten-generieren) Tip Verwenden Sie diese Befehle, um Ihre Zertifikate für Capgo zu kodieren: **Zertifikat (.p12):** ```bash base64 -i YourCertificate.p12 | pbcopy ``` **Bereitstellungsprofil (.mobileprovision):** ```bash base64 -i YourProfile.mobileprovision | pbcopy ``` **App Store Connect Schlüssel (.p8):** ```bash base64 -i AuthKey_ABC1234567.p8 | pbcopy ``` Die Base64-Zeichenfolge befindet sich jetzt in Ihrer Zwischenablage - fügen Sie sie in Ihre Umgebungsvariable oder CI/CD-Secrets ein. ## Für iOS erstellen [Section titled “Für iOS erstellen”](#für-ios-erstellen) ### Debug-Build (Development) [Section titled “Debug-Build (Development)”](#debug-build-development) ```bash npx @capgo/cli@latest build com.example.app \ --platform ios \ --build-mode debug ``` Dies erstellt einen Development-Build, der auf registrierten Geräten installiert werden kann. ### Release-Build (App Store) [Section titled “Release-Build (App Store)”](#release-build-app-store) ```bash npx @capgo/cli@latest build com.example.app \ --platform ios \ --build-mode release ``` Dies erstellt einen App Store-Build und übermittelt ihn automatisch an TestFlight, wenn Sie die App Store Connect API-Zugangsdaten konfiguriert haben. ## CI/CD-Integration [Section titled “CI/CD-Integration”](#cicd-integration) ### GitHub Actions-Beispiel [Section titled “GitHub Actions-Beispiel”](#github-actions-beispiel) ```yaml name: Build iOS App on: push: branches: [main] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - name: Setup Node.js uses: actions/setup-node@v6 with: node-version: '24' - name: Install dependencies run: npm ci - name: Build web assets run: npm run build - name: Sync Capacitor run: npx cap sync ios - name: Build iOS app env: CAPGO_TOKEN: ${{ secrets.CAPGO_TOKEN }} BUILD_CERTIFICATE_BASE64: ${{ secrets.IOS_CERTIFICATE }} BUILD_PROVISION_PROFILE_BASE64: ${{ secrets.IOS_PROVISION_PROFILE }} P12_PASSWORD: ${{ secrets.P12_PASSWORD }} APPLE_KEY_ID: ${{ secrets.APPLE_KEY_ID }} APPLE_ISSUER_ID: ${{ secrets.APPLE_ISSUER_ID }} APPLE_KEY_CONTENT: ${{ secrets.APPLE_KEY_CONTENT }} APP_STORE_CONNECT_TEAM_ID: ${{ secrets.TEAM_ID }} run: | npx @capgo/cli@latest build ${{ secrets.APP_ID }} \ --platform ios \ --build-mode release ``` ## Details zum Build-Prozess [Section titled “Details zum Build-Prozess”](#details-zum-build-prozess) ### Was passiert während eines iOS-Builds [Section titled “Was passiert während eines iOS-Builds”](#was-passiert-während-eines-ios-builds) 1. **Maschinenbereitstellung** (1-2 Minuten) * Scaleway Mac mini wird bereitgestellt oder zugewiesen * macOS 15 mit vorinstalliertem Xcode * Bootstrap-Skripte laufen (bei erster Verwendung) 2. **Benutzerisolation** (\~10 Sekunden) * Eindeutiger macOS-Benutzer erstellt: `job-` * Dediziertes Home-Verzeichnis: `/Users/job-` * Isolierter Workspace erstellt 3. **Projekt-Setup** (\~30 Sekunden) * Projekt-Zip von R2 heruntergeladen * In Workspace extrahiert * Zugangsdaten als Umgebungsvariablen eingefügt 4. **Fastlane-Build** (3-8 Minuten) * Schlüsselbund mit Signaturzertifikat erstellt * Bereitstellungsprofil installiert * Xcode-Build-Befehl ausgeführt * IPA-Datei generiert 5. **App Store-Übermittlung** (1-2 Minuten, falls konfiguriert) * IPA zu App Store Connect hochgeladen * An TestFlight übermittelt * Verarbeitung auf Apple-Seite beginnt 6. **Bereinigung** (sofort) * Benutzerkonto beendet und gelöscht * Workspace-Dateien entfernt * Temporäre Dateien gelöscht 7. **Maschinenfreigabe** (nach 24 Stunden) * Mac-Maschine wird zerstört * Alle Daten werden dauerhaft gelöscht ### Build-Stack [Section titled “Build-Stack”](#build-stack) Unsere iOS-Build-Umgebung umfasst: * **macOS**: 15 (neueste stabile Version) * **Xcode**: Neueste stabile Version * **Fastlane**: Neueste stabile Version * **CocoaPods**: Neueste stabile Version * **Node.js**: 18.x (LTS) * **Ruby**: System-Ruby mit Bundler ## Build-Zeiten [Section titled “Build-Zeiten”](#build-zeiten) Typische iOS-Build-Zeiten: | Build-Typ | Erster Build | Nachfolgende Builds\* | | --------- | ------------ | --------------------- | | Debug | 5-7 Minuten | 4-6 Minuten | | Release | 7-10 Minuten | 5-8 Minuten | \*Nachfolgende Builds können schneller sein, wenn dieselbe Maschine innerhalb des 24-Stunden-Fensters wiederverwendet wird. Note iOS-Builds kosten **2× den Basistarif** aufgrund dedizierter Mac-Hardwareanforderungen. ## Fehlerbehebung [Section titled “Fehlerbehebung”](#fehlerbehebung) ### Häufige Probleme [Section titled “Häufige Probleme”](#häufige-probleme) **“Code signing failed”** * Überprüfen Sie, ob Ihr Zertifikat für den korrekten Distributionstyp ist * Stellen Sie sicher, dass das Bereitstellungsprofil zu Ihrer App ID passt * Prüfen Sie, ob P12\_PASSWORD korrekt ist **“Provisioning profile doesn’t include signing certificate”** * Generieren Sie Ihr Bereitstellungsprofil neu, einschließlich des Zertifikats * Laden Sie das Profil erneut herunter und kodieren Sie es neu **“App Store Connect authentication failed”** * Überprüfen Sie APPLE\_KEY\_ID, APPLE\_ISSUER\_ID und APPLE\_KEY\_CONTENT * Stellen Sie sicher, dass der API-Schlüssel nicht widerrufen wurde * Prüfen Sie, ob der Schlüssel die Rolle “Developer” oder höher hat **“Build timeout after 10 minutes”** * Prüfen Sie, ob Ihre App große native Abhängigkeiten hat * Erwägen Sie, Ihr Podfile zu optimieren * Kontaktieren Sie den Support, wenn Builds ständig ein Timeout erreichen ### Debug-Protokolle [Section titled “Debug-Protokolle”](#debug-protokolle) Alle Build-Protokolle werden in Echtzeit gestreamt. Achten Sie auf diese wichtigen Phasen: ```plaintext ✔ Machine assigned: m-abc123 → Creating user: job-abc123 → Installing CocoaPods dependencies... → Building iOS app... → Code signing with certificate... → Uploading to App Store Connect... ✔ Build succeeded ``` Wenn ein Build fehlschlägt, wird der Fehler in den Protokollen mit der spezifischen Fastlane/Xcode-Fehlermeldung klar angezeigt. ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) ### 1. Zuerst lokal testen [Section titled “1. Zuerst lokal testen”](#1-zuerst-lokal-testen) Stellen Sie immer sicher, dass Ihr iOS-Build lokal funktioniert, bevor Sie Cloud Build verwenden: ```bash npx cap open ios # In Xcode erstellen ``` ### 2. Umgebungsvariablen verwenden [Section titled “2. Umgebungsvariablen verwenden”](#2-umgebungsvariablen-verwenden) Committen Sie niemals Zertifikate oder Schlüssel in Ihr Repository. Verwenden Sie immer: * CI/CD-Secrets (GitHub, GitLab) * Umgebungsvariablen * Sichere Secret-Verwaltung ### 3. Abhängigkeiten zwischenspeichern [Section titled “3. Abhängigkeiten zwischenspeichern”](#3-abhängigkeiten-zwischenspeichern) Für schnellere Builds stellen Sie sicher, dass Ihre `package.json` und `Podfile.lock` in die Versionskontrolle committed sind. ### 4. Build-Zeit überwachen [Section titled “4. Build-Zeit überwachen”](#4-build-zeit-überwachen) Behalten Sie die Build-Dauer im Auge, um Kosten zu optimieren: ```bash # Die CLI zeigt die Build-Zeit am Ende an Build succeeded in 6m 42s (13.4 billing minutes at 2× rate) ``` ## Nächste Schritte [Section titled “Nächste Schritte”](#nächste-schritte) * [Android Builds](/docs/cli/cloud-build/android/) - Android-Builds konfigurieren * [Fehlerbehebung](/docs/cli/cloud-build/troubleshooting/) - Häufige Build-Probleme * [CLI-Referenz](/docs/cli/reference/build/) - Vollständige Befehlsdokumentation ## Brauchen Sie Hilfe? [Section titled “Brauchen Sie Hilfe?”](#brauchen-sie-hilfe) * [Fehlerbehebungsanleitung](/docs/cli/cloud-build/troubleshooting/) * [Discord-Community](https://discord.com/invite/VnYRvBfgA6) * E-Mail: # Fehlerbehebung > Häufige Probleme und Lösungen für Capgo Cloud Build Lösungen für häufige Probleme beim Erstellen nativer Apps mit Capgo Cloud Build. ## Build-Fehler [Section titled “Build-Fehler”](#build-fehler) ### ”Upload failed” oder “Connection timeout” [Section titled “”Upload failed” oder “Connection timeout””](#upload-failed-oder-connection-timeout) **Symptome:** * Build schlägt während des Projekt-Uploads fehl * Timeout-Fehler nach 60 Sekunden **Lösungen:** 1. **Überprüfen Sie Ihre Internetverbindung** ```bash # Verbindung zu Capgo testen curl -I https://api.capgo.app ``` 2. **Projektgröße reduzieren** * Stellen Sie sicher, dass `node_modules/` nicht hochgeladen wird (sollte automatisch ausgeschlossen werden) * Überprüfen Sie auf große Dateien in Ihrem Projekt: ```bash find . -type f -size +10M ``` 3. **Upload-URL-Ablauf überprüfen** * Upload-URLs laufen nach 1 Stunde ab * Wenn Sie einen Fehler wegen abgelaufener URL erhalten, führen Sie den Build-Befehl erneut aus Tip Große Projekte (>200MB) können Timeout-Limits erreichen. Kontaktieren Sie den Support für Enterprise-Optionen. ### ”Build timeout after 10 minutes” [Section titled “”Build timeout after 10 minutes””](#build-timeout-after-10-minutes) **Symptome:** * Build überschreitet die maximal zulässige Zeit * Status zeigt `timeout` **Lösungen:** 1. **Abhängigkeiten optimieren** * Entfernen Sie nicht verwendete npm-Pakete * Verwenden Sie `npm prune --production` vor dem Erstellen 2. **Auf Netzwerkprobleme im Build prüfen** * Einige Abhängigkeiten laden möglicherweise große Dateien während des Builds herunter * Erwägen Sie Vor-Caching mit einer Lock-Datei 3. **Native Abhängigkeiten überprüfen** ```bash # iOS - Podfile auf schwere Abhängigkeiten prüfen cat ios/App/Podfile # Android - build.gradle prüfen cat android/app/build.gradle ``` 4. **Support kontaktieren** * Wenn Ihre App wirklich mehr Zeit benötigt * Wir können Limits für spezifische Anwendungsfälle anpassen ## Authentifizierungsprobleme [Section titled “Authentifizierungsprobleme”](#authentifizierungsprobleme) ### ”API key invalid” oder “Unauthorized” [Section titled “”API key invalid” oder “Unauthorized””](#api-key-invalid-oder-unauthorized) **Symptome:** * Build schlägt sofort mit Authentifizierungsfehler fehl * 401 oder 403 Fehler **Lösungen:** 1. **API-Schlüssel überprüfen** ```bash # Mit einem einfachen Befehl testen npx @capgo/cli@latest app list ``` 2. **API-Schlüssel-Berechtigungen prüfen** * Schlüssel muss `write`- oder `all`-Berechtigungen haben * Im Capgo-Dashboard unter API-Schlüssel überprüfen 3. **Sicherstellen, dass API-Schlüssel gelesen wird** ```bash # Umgebungsvariable überprüfen echo $CAPGO_TOKEN # Oder lokale .capgo-Datei überprüfen cat .capgo ``` 4. **Erneut authentifizieren** ```bash npx @capgo/cli@latest login ``` ### ”App not found” oder “No permission for this app” [Section titled “”App not found” oder “No permission for this app””](#app-not-found-oder-no-permission-for-this-app) **Symptome:** * Authentifizierung funktioniert, aber App-spezifischer Fehler **Lösungen:** 1. **App-Registrierung überprüfen** ```bash npx @capgo/cli@latest app list ``` 2. **App ID-Übereinstimmung prüfen** * `capacitor.config.json` appId überprüfen * Sicherstellen, dass Befehl korrekte App ID verwendet 3. **Organisationszugriff überprüfen** * Überprüfen Sie, dass Sie in der richtigen Organisation sind * API-Schlüssel muss Zugriff auf die Organisation der App haben ## iOS Build-Probleme [Section titled “iOS Build-Probleme”](#ios-build-probleme) ### ”Code signing failed” [Section titled “”Code signing failed””](#code-signing-failed) **Symptome:** * Build schlägt während der Code-Signaturphase fehl * Xcode-Fehler über Zertifikate oder Profile **Lösungen:** 1. **Zertifikatstyp mit Build-Typ abgleichen** * Development-Builds benötigen Development-Zertifikate * App Store-Builds benötigen Distribution-Zertifikate 2. **Zertifikat und Profil auf Übereinstimmung prüfen** ```bash # Zertifikat dekodieren und inspizieren echo $BUILD_CERTIFICATE_BASE64 | base64 -d > cert.p12 openssl pkcs12 -in cert.p12 -nokeys -passin pass:$P12_PASSWORD | openssl x509 -noout -subject ``` 3. **Bereitstellungsprofil auf Gültigkeit prüfen** * Ablaufdatum überprüfen * Überprüfen, dass es Ihre App ID enthält * Bestätigen, dass es das Zertifikat enthält 4. **Zugangsdaten neu generieren** * Altes Zertifikat/Profil löschen * Neue im Apple Developer Portal erstellen * Neu kodieren und Umgebungsvariablen aktualisieren ### ”Provisioning profile doesn’t include signing certificate” [Section titled “”Provisioning profile doesn’t include signing certificate””](#provisioning-profile-doesnt-include-signing-certificate) **Symptome:** * Xcode kann Zertifikat im Profil nicht finden **Lösungen:** 1. **Neuestes Profil von Apple herunterladen** * Zu Apple Developer → Zertifikate, IDs & Profile gehen * Bereitstellungsprofil herunterladen * Sicherstellen, dass es Ihr Zertifikat enthält 2. **Zertifikat im Profil überprüfen** ```bash # Profil extrahieren echo $BUILD_PROVISION_PROFILE_BASE64 | base64 -d > profile.mobileprovision # Profilinhalte anzeigen security cms -D -i profile.mobileprovision ``` 3. **Profil mit korrektem Zertifikat neu erstellen** * Im Apple Developer Portal Profil bearbeiten * Sicherstellen, dass Ihr Distribution-Zertifikat ausgewählt ist * Herunterladen und neu kodieren ### ”App Store Connect authentication failed” [Section titled “”App Store Connect authentication failed””](#app-store-connect-authentication-failed) **Symptome:** * Upload zu TestFlight schlägt fehl * API-Schlüssel-Fehler **Lösungen:** 1. **API-Schlüssel-Zugangsdaten überprüfen** * APPLE\_KEY\_ID prüfen (sollte 10 Zeichen sein) * APPLE\_ISSUER\_ID prüfen (sollte UUID-Format sein) * Überprüfen, dass APPLE\_KEY\_CONTENT korrekt base64-kodiert ist 2. **API-Schlüssel lokal testen** ```bash # Schlüssel dekodieren echo $APPLE_KEY_CONTENT | base64 -d > AuthKey.p8 # Mit fastlane testen (falls installiert) fastlane pilot list ``` 3. **API-Schlüssel-Berechtigungen prüfen** * Schlüssel benötigt “Developer”-Rolle oder höher * In App Store Connect → Benutzer und Zugriff → Schlüssel überprüfen 4. **Sicherstellen, dass Schlüssel nicht widerrufen wurde** * In App Store Connect prüfen * Neuen Schlüssel generieren, falls erforderlich ### ”Pod install failed” [Section titled “”Pod install failed””](#pod-install-failed) **Symptome:** * Build schlägt während CocoaPods-Installation fehl * Podfile-Fehler **Lösungen:** 1. **Podfile.lock ist committed überprüfen** ```bash git status ios/App/Podfile.lock ``` 2. **Pod install lokal testen** ```bash cd ios/App pod install ``` 3. **Auf inkompatible Pods prüfen** * Podfile auf Versionskonflikte überprüfen * Sicherstellen, dass alle Pods Ihr iOS-Deployment-Ziel unterstützen 4. **Pod-Cache löschen** ```bash cd ios/App rm -rf Pods rm Podfile.lock pod install # Dann neuen Podfile.lock committen ``` ## Android Build-Probleme [Section titled “Android Build-Probleme”](#android-build-probleme) ### ”Keystore password incorrect” [Section titled “”Keystore password incorrect””](#keystore-password-incorrect) **Symptome:** * Build schlägt während der Signatur fehl * Gradle-Fehler über Keystore **Lösungen:** 1. **Keystore-Passwort überprüfen** ```bash # Keystore lokal testen keytool -list -keystore my-release-key.keystore # Passwort bei Aufforderung eingeben ``` 2. **Umgebungsvariablen prüfen** ```bash # Sicherstellen, dass keine zusätzlichen Leerzeichen oder Sonderzeichen vorhanden sind echo "$KEYSTORE_STORE_PASSWORD" | cat -A echo "$KEYSTORE_KEY_PASSWORD" | cat -A ``` 3. **Base64-Kodierung überprüfen** ```bash # Dekodieren und testen echo $ANDROID_KEYSTORE_FILE | base64 -d > test.keystore keytool -list -keystore test.keystore ``` ### ”Key alias not found” [Section titled “”Key alias not found””](#key-alias-not-found) **Symptome:** * Signatur schlägt mit Alias-Fehler fehl **Lösungen:** 1. **Keystore-Aliase auflisten** ```bash keytool -list -keystore my-release-key.keystore ``` 2. **Alias stimmt genau überein überprüfen** * Alias beachtet Groß-/Kleinschreibung * Auf Tippfehler in KEYSTORE\_KEY\_ALIAS prüfen 3. **Korrekten Alias aus Keystore verwenden** ```bash # Umgebungsvariable aktualisieren, um zu übereinstimmen export KEYSTORE_KEY_ALIAS="the-exact-alias-name" ``` ### ”Gradle build failed” [Section titled “”Gradle build failed””](#gradle-build-failed) **Symptome:** * Allgemeine Gradle-Fehler * Kompilierungs- oder Abhängigkeitsprobleme **Lösungen:** 1. **Build zuerst lokal testen** ```bash cd android ./gradlew clean ./gradlew assembleRelease ``` 2. **Auf fehlende Abhängigkeiten prüfen** * build.gradle-Dateien überprüfen * Sicherstellen, dass alle Plugins in Abhängigkeiten aufgelistet sind 3. **Gradle-Versionskompatibilität überprüfen** ```bash # Gradle-Version prüfen cat android/gradle/wrapper/gradle-wrapper.properties ``` 4. **Gradle-Cache löschen** ```bash cd android ./gradlew clean rm -rf .gradle build ``` ### ”Play Store upload failed” [Section titled “”Play Store upload failed””](#play-store-upload-failed) **Symptome:** * Build erfolgreich, aber Upload schlägt fehl * Service Account-Fehler **Lösungen:** 1. **Service Account JSON überprüfen** ```bash # Dekodieren und Format prüfen echo $PLAY_CONFIG_JSON | base64 -d | jq . ``` 2. **Service Account-Berechtigungen prüfen** * Zu Play Console → Einrichtung → API-Zugriff gehen * Sicherstellen, dass Service Account Zugriff auf Ihre App hat * Berechtigung “Release to testing tracks” erteilen 3. **App in Play Console eingerichtet überprüfen** * App muss zuerst in Play Console erstellt werden * Mindestens ein APK muss anfangs manuell hochgeladen werden 4. **API ist aktiviert prüfen** * Google Play Developer API muss aktiviert sein * In Google Cloud Console prüfen ## Allgemeine Probleme [Section titled “Allgemeine Probleme”](#allgemeine-probleme) ### ”Job not found” oder “Build status unavailable” [Section titled “”Job not found” oder “Build status unavailable””](#job-not-found-oder-build-status-unavailable) **Symptome:** * Build-Status kann nicht überprüft werden * Job-ID-Fehler **Lösungen:** 1. **Kurz warten und erneut versuchen** * Build-Jobs benötigen möglicherweise einige Sekunden zur Initialisierung 2. **Job-ID ist korrekt prüfen** * Job-ID aus der anfänglichen Build-Antwort überprüfen 3. **Build ist nicht abgelaufen prüfen** * Build-Daten sind 24 Stunden verfügbar ### ”Project sync failed” [Section titled “”Project sync failed””](#project-sync-failed) **Symptome:** * Build schlägt vor Kompilierung fehl * Fehlende Dateien-Fehler **Lösungen:** 1. **Capacitor-Sync lokal ausführen** ```bash npx cap sync ``` 2. **Alle nativen Dateien sind committed sicherstellen** ```bash git status ios/ android/ ``` 3. **Auf gitignorierte native Dateien prüfen** * .gitignore überprüfen * Sicherstellen, dass wichtige Config-Dateien nicht ignoriert werden ### ”Build succeeded but I don’t see output” [Section titled “”Build succeeded but I don’t see output””](#build-succeeded-but-i-dont-see-output) **Symptome:** * Build zeigt Erfolg, aber kein Download-Link **Lösungen:** 1. **Build-Konfiguration prüfen** * Artefaktspeicher ist möglicherweise nicht konfiguriert * Für öffentliche Beta, Support wegen Artefaktzugriff kontaktieren 2. **Für iOS TestFlight-Übermittlung** * App Store Connect prüfen * Verarbeitung kann 5-30 Minuten nach Upload dauern 3. **Für Android Play Store** * Play Console → Testing → Internes Testing prüfen * Verarbeitung kann einige Minuten dauern ## CI/CD-spezifische Probleme [Section titled “CI/CD-spezifische Probleme”](#cicd-spezifische-probleme) ### GitHub Actions: “Command not found” [Section titled “GitHub Actions: “Command not found””](#github-actions-command-not-found) **Symptome:** * `npx @capgo/cli` schlägt in CI fehl **Lösungen:** 1. **Node.js ist installiert sicherstellen** ```yaml - uses: actions/setup-node@v6 with: node-version: '24' ``` 2. **CLI explizit installieren** ```yaml - run: npm install -g @capgo/cli ``` ### GitHub Actions: “Secrets not found” [Section titled “GitHub Actions: “Secrets not found””](#github-actions-secrets-not-found) **Symptome:** * Umgebungsvariablen im Build leer **Lösungen:** 1. **Secrets sind gesetzt überprüfen** * Zu Repo Settings → Secrets and variables → Actions gehen * Alle erforderlichen Secrets hinzufügen 2. **Korrekte Syntax verwenden** ```yaml env: CAPGO_TOKEN: ${{ secrets.CAPGO_TOKEN }} ``` 3. **Secret-Namen stimmen überein prüfen** * Namen beachten Groß-/Kleinschreibung * Keine Tippfehler in Secret-Referenzen ## Mehr Hilfe erhalten [Section titled “Mehr Hilfe erhalten”](#mehr-hilfe-erhalten) ### Ausführliche Protokollierung aktivieren [Section titled “Ausführliche Protokollierung aktivieren”](#ausführliche-protokollierung-aktivieren) ```bash # Debug-Flag hinzufügen (falls verfügbar) npx @capgo/cli@latest build com.example.app --verbose ``` ### Build-Informationen sammeln [Section titled “Build-Informationen sammeln”](#build-informationen-sammeln) Beim Kontaktieren des Supports folgendes einschließen: 1. **Verwendeter Build-Befehl** ```bash npx @capgo/cli@latest build com.example.app --platform ios ``` 2. **Fehlermeldung** (vollständige Ausgabe) 3. **Job-ID** (aus Build-Ausgabe) 4. **Build-Protokolle** (vollständige Terminal-Ausgabe kopieren) 5. **Umgebungsinformationen** ```bash node --version npm --version npx @capgo/cli --version ``` ### Support kontaktieren [Section titled “Support kontaktieren”](#support-kontaktieren) * **Discord**: [Unserer Community beitreten](https://discord.com/invite/VnYRvBfgA6) * **E-Mail**: * **Dokumentation**: [Capgo Docs](/docs/) ### Bekannte Einschränkungen [Section titled “Bekannte Einschränkungen”](#bekannte-einschränkungen) Aktuelle Einschränkungen während der öffentlichen Beta: * Maximale Build-Zeit: 10 Minuten * Maximale Upload-Größe: \~500MB * iOS-Builds erfordern 24-Stunden-Mac-Leases, Build auf Mac wird in die Warteschlange gestellt, um optimale Nutzung sicherzustellen * Build-Artefakt-Download ist möglicherweise nicht verfügbar Diese Einschränkungen können basierend auf Feedback angepasst werden. ## Zusätzliche Ressourcen [Section titled “Zusätzliche Ressourcen”](#zusätzliche-ressourcen) * [Erste Schritte](/docs/cli/cloud-build/getting-started/) - Anfängliche Einrichtungsanleitung * [iOS Builds](/docs/cli/cloud-build/ios/) - iOS-spezifische Konfiguration * [Android Builds](/docs/cli/cloud-build/android/) - Android-spezifische Konfiguration * [CLI-Referenz](/docs/cli/reference/build/) - Vollständige Befehlsdokumentation # Befehle > Capgo CLI-Dokumentation ### Verwendung [Section titled “Verwendung”](#verwendung) Alle Befehle sollten in Ihrem App-Ordner mit ordnungsgemäß initialisiertem Capacitor-Projekt ausgeführt werden [Capacitor plattformübergreifende native Laufzeit für Web-Apps ](https://capacitorjs.com/docs/getting-started/) ### **Init** [Section titled “Init”](#init) `npx @capgo/cli@latest init [apikey]` Diese Methode führt Sie Schritt für Schritt durch den Onboarding-Prozess Sie fügt Ihre App zu Capgo hinzu Sie fügt den Code zur Validierung des Updates in Ihre App ein Ebenso wird sie Ihre App erstellen Außerdem wird sie Ihre App zu Capgo hochladen Und sie hilft Ihnen zu überprüfen, ob das Update funktioniert ### **Login** [Section titled “Login”](#login) `npx @capgo/cli login [apikey]` Diese Methode speichert den `apikey` für Sie Note verwenden Sie `--apikey=********` in jedem Befehl, um ihn zu überschreiben **Optional können Sie angeben:** `--local` Dies speichert Ihren **apikey** im lokalen Repository und ignoriert ihn in Git ## **Doctor** [Section titled “Doctor”](#doctor) `npx @capgo/cli doctor` Befehl zur Überprüfung, ob Sie mit den Capgo-Paketen auf dem neuesten Stand sind Dieser Befehl ist auch für Fehlerberichte nützlich ## App [Section titled “App”](#app) ### **Add** [Section titled “Add”](#add) `npx @capgo/cli app add [appId]` `[appId]` Ihre App-ID, das Format `comtestapp` wird [hier](https://capacitorjs.com/docs/cli/commands/init/) erklärt > 💡 Alle Optionen werden aus Ihrer Konfiguration erraten, wenn nicht angegeben Optional können Sie angeben: * `--icon [/path/to/my/icon]` für ein benutzerdefiniertes Icon in der Capgo Web-App * `--name [test]` für einen benutzerdefinierten Namen in der Liste * `--apikey [key]` API-Schlüssel zur Verknüpfung mit Ihrem Konto * `--retention [retention]` Aufbewahrungszeitraum des App-Bundles in Tagen, 0 standardmäßig = unendlich Beispiel einer `capacitorconfigjson` für appId und AppName, das Icon wird im resources-Ordner gesucht ```json { "appId": "eeforgrcapacitor_go", "appName": "Capgo", "webDir": "dist" } ``` ### **Set** [Section titled “Set”](#set) `npx @capgo/cli app set [appId]` `[appId]` ist Ihre App-ID, das Format wird [hier](https://capacitorjs.com/docs/cli/commands/init/) erklärt Optional können Sie angeben: * `--icon [/path/to/my/icon]` für ein benutzerdefiniertes Icon in der Capgo Web-App * `--name [test]` für einen benutzerdefinierten Namen in der Liste * `--retention [retention]` Aufbewahrungszeitraum des App-Bundles in Tagen, 0 standardmäßig = unendlich * `--apikey [key]` API-Schlüssel zur Verknüpfung mit Ihrem Konto ### **List** [Section titled “List”](#list) `npx @capgo/cli app list [appId]` `[appId]` Ihre App-ID, das Format `comtestapp` wird [hier](https://capacitorjs.com/docs/cli/commands/init/) erklärt Optional können Sie angeben: * `--apikey [key]` API-Schlüssel zur Verknüpfung mit Ihrem Konto ### **Delete** [Section titled “Delete”](#delete) `npx @capgo/cli app delete [appId]` `[appId]` Ihre App-ID, das Format `comtestapp` wird [hier](https://capacitorjs.com/docs/cli/commands/init/) erklärt Optional können Sie angeben: * `--apikey [key]` API-Schlüssel zur Verknüpfung mit Ihrem Konto * `--bundle` mit der Versionsnummer löscht nur diese Version ### Debug [Section titled “Debug”](#debug) `npx @capgo/cli app debug [appId]` `[appId]` Ihre App-ID, das Format `comtestapp` wird [hier](https://capacitorjs.com/docs/cli/commands/init/) erklärt Optional können Sie angeben: * `--apikey [key]` API-Schlüssel zur Verknüpfung mit Ihrem Konto * `--device` mit dem spezifischen Gerät, das Sie debuggen möchten ### Setting [Section titled “Setting”](#setting) `npx @capgo/cli app setting [path]` Bearbeiten der Capacitor-Konfiguration `[path]` - Pfad der Einstellung, die Sie ändern möchten Zum Beispiel, um die `appId` zu ändern, geben Sie `appId` an Wenn Sie das automatische Update in `capacitor-updater` deaktivieren möchten, geben Sie `pluginsCapacitorUpdaterautoUpdate` an Sie MÜSSEN entweder `--string` oder `--bool` angeben! Optionen: * `--string ` - setzt die Einstellung auf einen String * `--bool ` - setzt die Einstellung auf einen Boolean ## Bundle [Section titled “Bundle”](#bundle) ### Upload [Section titled “Upload”](#upload) `npx @capgo/cli bundle upload [appId]` `[appId]` ist Ihre App-ID, das Format wird [hier](https://capacitorjs.com/docs/cli/commands/init/) erklärt Optional können Sie angeben: * `--apikey ` API-Schlüssel zur Verknüpfung mit Ihrem Konto * `--path ` Pfad des hochzuladenden Ordners * `--channel ` Zu verknüpfender Kanal * `--external ` Link zu externer URL anstelle des Uploads zur Capgo Cloud * `--iv-session-key ` IV und Sitzungsschlüssel für externe Bundle-URL festlegen * `--s3-endpoint ` URL des S3-Endpunkts Funktioniert nicht mit partiellem Upload oder externer Option * `--s3-region ` Region für Ihren S3-Bucket * `--s3-apikey ` API-Schlüssel für Ihren S3-Endpunkt * `--s3-apisecret ` API-Geheimnis für Ihren S3-Endpunkt * `--s3-bucket-name ` Name für Ihren AWS S3-Bucket * `--s3-port ` Port für Ihren S3-Endpunkt * `--no-s3-ssl` SSL für S3-Upload deaktivieren * `--key ` Benutzerdefinierter Pfad für öffentlichen Signierungsschlüssel (v1-System) * `--key-data ` Öffentlicher Signierungsschlüssel (v1-System) * `--key-v2 ` Benutzerdefinierter Pfad für privaten Signierungsschlüssel (v2-System) * `--key-data-v2 ` Privater Signierungsschlüssel (v2-System) * `--bundle-url` Gibt Bundle-URL in stdout aus * `--no-key` Signierungsschlüssel ignorieren und klares Update senden * `--no-code-check` Überprüfung ignorieren, ob notifyAppReady() im Quellcode aufgerufen wird und Index im Stammverzeichnis vorhanden ist * `--display-iv-session` IV und Sitzungsschlüssel in der Konsole anzeigen, die zur Verschlüsselung des Updates verwendet werden * `--bundle ` Bundleversion der hochzuladenden Version * `--min-update-version ` Minimale Version, die für ein Update auf diese Version erforderlich ist Wird nur verwendet, wenn auto update auf metadata im Kanal gesetzt ist * `--auto-min-update-version` Minimale Update-Version basierend auf nativen Paketen festlegen * `--ignore-metadata-check` Ignoriert die Metadaten-Prüfung (node\_modules) beim Hochladen * `--ignore-checksum-check` Ignoriert die Prüfsummen-Prüfung beim Hochladen * `--timeout ` Timeout für den Upload-Prozess in Sekunden * `--partial` Lädt keine partiellen Dateien zur Capgo-Cloud hoch * `--tus` Lädt das Bundle mit dem tus-Protokoll hoch * `--multipart` Verwendet das Multipart-Protokoll zum Hochladen von Daten zu S3, Veraltet, verwenden Sie stattdessen TUS * `--encrypted-checksum ` Eine verschlüsselte Prüfsumme (Signatur) Wird nur beim Hochladen eines externen Bundles verwendet * `--package-json ` Ein Pfad zu packagejson Nützlich für Monorepos * `--auto-set-bundle` Bundle in capacitorconfigjson setzen * `--node-modules ` Eine Liste von Pfaden zu node\_modules Nützlich für Monorepos (kommagetrennt z.B.: //node\_modules,/node\_modules) > ⭐️ Die externe Option hilft zwei Fälle zu lösen: Unternehmen mit Datenschutzbedenken, die keinen Code an Dritte senden möchten und Apps größer als 200 MB Mit dieser Einstellung speichert Capgo nur den Link zur ZIP-Datei und sendet den Link an alle Apps > 👀 Capgo Cloud schaut nie, was sich im Link (für externe Option) oder im Code befindet, wenn gespeichert > 🔑 Sie können eine zweite Sicherheitsebene durch Verschlüsselung hinzufügen, dann kann Capgo nichts einsehen oder modifizieren, es wird “vertrauenslos” Beispiel einer `packagejson` für Version ```json { "version": "102" } ``` > ⛔ Version sollte größer als “000” sein > 💡 Vergessen Sie nicht, die Versionsnummer bei jedem Senden zu aktualisieren, Versionsnummern können aus Sicherheitsgründen nicht überschrieben oder nach dem Löschen wiederverwendet werden ### **List** [Section titled “List”](#list-1) `npx @capgo/cli bundle list [appId]` `[appId]` Ihre App-ID, das Format `comtestapp` wird [hier](https://capacitorjs.com/docs/cli/commands/init/) erklärt Optional können Sie angeben: * `--apikey [key]` API-Schlüssel zur Verknüpfung mit Ihrem Konto ### **Delete** [Section titled “Delete”](#delete-1) `npx @capgo/cli bundle delete [appId]` `[appId]` Ihre App-ID, das Format `comtestapp` wird [hier](https://capacitorjs.com/docs/cli/commands/init/) erklärt Optional können Sie angeben: * `--apikey [key]` API-Schlüssel zur Verknüpfung mit Ihrem Konto * `--bundle` mit der Versionsnummer löscht nur diese Version ### Cleanup [Section titled “Cleanup”](#cleanup) in einem SemVer-Bereich für eine Hauptversion in der Cloud `npx @capgo/cli bundle cleanup [appId] --bundle=[majorVersion] --keep=[numberToKeep]` `[appId]` Ihre App-ID, das Format `comtestapp` wird [hier](https://capacitorjs.com/docs/cli/commands/init/) erklärt Optional können Sie angeben: * `--apikey [key]` API-Schlüssel zur Verknüpfung mit Ihrem Konto * `--bundle [majorVersion]` eine Version, für die Sie vorherige Pakete entfernen möchten, es wird die letzte Version + `numberToKeep` behalten\* `--keep [numberToKeep]` die Anzahl der Pakete, die Sie behalten möchten (Standard 4) Zum Beispiel: Wenn Sie 10 Versionen von 1001 bis 10011 haben und Sie `npx @capgo/cli cleanup [appId] --bundle=1000` verwenden, werden 1001 bis 1006 entfernt, 1007 bis 10011 werden behalten Wenn Sie insgesamt 20 Versionen haben und keine Bundlenummer angeben, wie hier: `npx @capgo/cli cleanup [appId] --keep=2`, werden 18 Versionen entfernt und die letzten 2 behalten > Dieser Befehl wird um Bestätigung bitten und zeigt eine Tabelle mit den zu behaltenden und zu entfernenden Versionen Note Dieser Befehl ignoriert Bundles, die derzeit in einem Kanal verwendet werden ### **Encrypt** [Section titled “Encrypt”](#encrypt) > **Warnung**: Dieser Befehl ist veraltet und wird in der nächsten Hauptversion entfernt. Bitte verwenden Sie das neue Verschlüsselungssystem `npx @capgo/cli bundle encrypt [path/to/zip]` Dieser Befehl wird verwendet, wenn Sie externe Quellen zum Speichern Ihres Codes verwenden oder zu Testzwecken Optional können Sie angeben: `--key [/path/to/my/private_key]` der Pfad zu Ihrem privaten Schlüssel `--key-data [privateKey]` die privaten Schlüsseldaten, wenn Sie sie inline verwenden möchten Der Befehl gibt Ihren `ivSessionKey` aus und generiert eine verschlüsselte ZIP-Datei zur Verwendung mit dem Upload- oder Decrypt-Befehl ### **Encrypt V2** [Section titled “Encrypt V2”](#encrypt-v2) `npx @capgo/cli bundle encryptV2 [path/to/zip] [checksum]` Dieser Befehl wird verwendet, wenn Sie externe Quellen zum Speichern Ihres Codes verwenden oder zu Testzwecken Die Prüfsumme ist der SHA256-Hash des Bundles (generiert durch —key-v2) und wird verwendet, um die Integrität der Datei nach der Entschlüsselung zu überprüfen Sie wird mit dem privaten Schlüssel verschlüsselt und zusammen mit dem Bundle gesendet In Verschlüsselung v2 wird die Prüfsumme zu einer “Signatur” des Bundles aufgewertet Optional können Sie angeben: `--key [/path/to/my/private_key]` der Pfad zu Ihrem privaten Schlüssel `--key-data [privateKey]` die privaten Schlüsseldaten, wenn Sie sie inline verwenden möchten `--json` um Informationen als JSON auszugeben Der Befehl gibt Ihren `ivSessionKey` aus und generiert eine verschlüsselte ZIP-Datei zur Verwendung mit dem Upload- oder Decrypt-Befehl ### **Decrypt** [Section titled “Decrypt”](#decrypt) `npx @capgo/cli bundle decrypt [path/to/zip] [ivSessionKey]` Optional können Sie angeben: `--key [/path/to/my/private_key]` der Pfad zu Ihrem privaten Schlüssel `--key-data [privateKey]` die privaten Schlüsseldaten, wenn Sie sie inline verwenden möchten Dieser Befehl wird hauptsächlich zu Testzwecken verwendet, er entschlüsselt die ZIP-Datei und gibt den base64-decodierten Sitzungsschlüssel in der Konsole aus ### **Decrypt V2** [Section titled “Decrypt V2”](#decrypt-v2) `npx @capgo/cli bundle decryptV2 [path/to/zip] [ivSessionKey]` Optional können Sie angeben: `--key [/path/to/my/private_key]` der Pfad zu Ihrem privaten Schlüssel `--key-data [privateKey]` die privaten Schlüsseldaten, wenn Sie sie inline verwenden möchten Dieser Befehl wird hauptsächlich zu Testzwecken verwendet, er entschlüsselt die ZIP-Datei und gibt den base64-decodierten Sitzungsschlüssel in der Konsole aus `--checksum [checksum]` die Prüfsumme der Datei, sie wird nach der Entschlüsselung überprüft ### **Zip** [Section titled “Zip”](#zip) `npx @capgo/cli bundle zip [appId]` `[appId]` ist Ihre App-ID, das Format wird [hier](https://capacitorjs.com/docs/cli/commands/init/) erklärt Optional können Sie angeben: * `--path [/path/to/my/bundle]` um einen bestimmten Ordner hochzuladen * `--bundle [100]` um die Bundle-Versionsnummer des Dateinamens festzulegen * `--name [myapp]` um den Dateinamen zu überschreiben * `--json` um Informationen als JSON auszugeben * `--no-code-check` um die Code-Prüfung zu ignorieren und das Bundle trotzdem zu senden * `--key-v2` um das neue Verschlüsselungssystem zu verwenden Dies ist erforderlich, da das neue Verschlüsselungssystem bessere Prüfsummen zur Überprüfung der Dateiintegrität verwendet ### **Compatibility** [Section titled “Compatibility”](#compatibility) `npx @capgo/cli bundle compatibility [appId] -c [channelId]` `[appId]` ist Ihre App-ID, das Format wird [hier](https://capacitorjs.com/docs/cli/commands/init/) erklärt `[channelId]` der Name Ihres neuen Kanals Optional können Sie angeben: * `--apikey [key]` API-Schlüssel zur Verknüpfung mit Ihrem Konto * `--text` Text statt Emojis in der Tabelle verwenden * `--channel [channel]` der Kanal, dessen Kompatibilität überprüft werden soll * `--package-json ` Ein Pfad zur package.json Nützlich für Monorepos * `--node-modules ` Eine Liste von Pfaden zu node\_modules Nützlich für Monorepos (kommagetrennt z.B.: //node\_modules,/node\_modules) ## Channel [Section titled “Channel”](#channel) ### **Add** [Section titled “Add”](#add-1) `npx @capgo/cli channel add [channelId] [appId]` `[channelId]` der Name Ihres neuen Kanals `[appId]` Ihre App-ID, das Format `comtestapp` wird [hier](https://capacitorjs.com/docs/cli/commands/init/) erklärt ### **Delete** [Section titled “Delete”](#delete-2) `npx @capgo/cli channel delete [channelId] [appId]` `[channelId]` der Name des Kanals, den Sie löschen möchten `[appId]` Ihre App-ID, das Format `comtestapp` wird [hier](https://capacitorjs.com/docs/cli/commands/init/) erklärt ### **List** [Section titled “List”](#list-2) `npx @capgo/cli channel list [appId]` `[appId]` Ihre App-ID, das Format `comtestapp` wird [hier](https://capacitorjs.com/docs/cli/commands/init/) erklärt Optional können Sie angeben: * `--apikey [key]` API-Schlüssel zur Verknüpfung mit Ihrem Konto ### **Set** [Section titled “Set”](#set-1) `npx @capgo/cli channel set [channelId] [appId]` `[appId]` ist Ihre App-ID, das Format wird [hier](https://capacitorjs.com/docs/cli/commands/init/) erklärt Optional können Sie angeben: * `--bundle [123]` Ihr bereits in die Cloud gesendetes App-Bundle, um es mit einem Kanal zu verknüpfen * `--latest` holt die Bundle-Version aus `packagejson:version`, kann nicht mit `--bundle` verwendet werden * `--state [ normal | default ]` setzt den Kanalstatus, kann `normal` oder `default` sein Ein Kanal muss `default` sein * `--downgrade` erlaubt dem Kanal, Downgrade-Versionen an Geräte zu senden * `--no-downgrade` verbietet dem Kanal, Downgrade-Versionen an Geräte zu senden * `--upgrade` erlaubt dem Kanal, Upgrade (Major) Versionen an Geräte zu senden * `--no-upgrade` verbietet dem Kanal, Upgrade (Major) Versionen an Geräte zu senden * `--ios` erlaubt dem Kanal, Versionen an iOS-Geräte zu senden * `--no-ios` verbietet dem Kanal, Versionen an iOS-Geräte zu senden * `--android` erlaubt dem Kanal, Versionen an Android-Geräte zu senden * `--no-android` verbietet dem Kanal, Versionen an Android-Geräte zu senden * `--self-assign` erlaubt Geräten, sich selbst diesem Kanal zuzuweisen * `--no-self-assign` verbietet Geräten, sich selbst diesem Kanal zuzuweisen * `--disable-auto-update STRATEGY` Deaktiviert die Auto-Update-Strategie für diesen Kanal Die möglichen Optionen sind: major, minor, metadata, none * `--apikey [key]` API-Schlüssel zur Verknüpfung mit Ihrem Konto ## Deaktivieren der Update-Strategien [Section titled “Deaktivieren der Update-Strategien”](#deaktivieren-der-update-strategien) Es gibt mehrere Möglichkeiten, Updates für zu alte Versionen zu deaktivieren\ Capgo kann nativen Code nicht aktualisieren, daher sollte ein Update von einer Version mit altem nativen Code auf eine Version mit aktualisiertem nativen Code nicht möglich sein Es gibt mehrere Möglichkeiten, dies zu erreichen Erstens die `major` Strategie Sie verhindert ein Update von `000` -> `100` Die Hauptversion ist die hervorgehobene Nummer (**1**00 und **0**00)\ Zweitens die `minor` Strategie Sie verhindert ein Update von `000` -> `110` oder ein Update von `110` auf `120` **ACHTUNG** diese Strategie verhindert nicht ein Update von `010` -> `110` Drittens die `patch` Strategie Sie wurde als sehr strenger Modus in Capgo eingeführt Es wird nicht empfohlen, sie zu verwenden, es sei denn, Sie verstehen vollständig, wie sie funktioniert Damit ein Update akzeptiert wird, müssen folgende Bedingungen erfüllt sein: * Die Hauptversion ist zwischen der neuen und alten Version gleich * Die Nebenversion ist zwischen der neuen und alten Version gleich * Die Patch-Version der neuen Version ist größer als die Patch-Version der alten Version Hier ein Beispiel, welche Szenarien für Updates erlaubt oder abgelehnt werden: * 00311 -> 00314 ✅ * 000 -> 00314 ✅ * 00316 -> 00314 ❌ * 01312 -> 00314 ❌ * 10312 -> 00314 ❌ Zuletzt die komplizierteste Strategie Die `metadata` Strategie\ Zuerst müssen Sie wissen, dass die Updates anfangs **FEHLSCHLAGEN** werden, da dem Kanal die erforderlichen Metadaten fehlen\ Wenn dem Kanal Metadaten fehlen, sehen Sie eine Nachricht wie diese: ![Cannot find metadata](/fail-metadata.webp) Wenn Sie so etwas sehen, wissen Sie, dass Sie zum aktuellen Bundle für den fehlschlagenden Kanal gehen und die Metadaten setzen müssen\ Ermitteln Sie zunächst, welcher Kanal fehlschlägt Sie können das in der Spalte `misconfigured` sehen ![Misconfigured table](/misconfigured-table.webp) Gehen Sie dann zum fehlerhaften Kanal und klicken Sie auf `Bundle number`. Dies sollte Sie zur Bundle-Seite führen. ![Locate failing channel](/fail-channel-show.webp) Füllen Sie dort das Feld `Minimal update version` aus. Dies sollte ein [semver](https://devhints.io/semver/) sein.\ Wenn der eingegebene Wert kein semver ist, erhalten Sie eine Fehlermeldung. Bei korrekter Eingabe sollten Sie etwa Folgendes sehen: ![Set min version](/set-min-update-version.webp) Sie möchten diese Daten wahrscheinlich nicht bei jedem Update manuell festlegen. Glücklicherweise verhindert die CLI das Senden eines Updates ohne diese Metadaten. ![CLI fail no metadata](/cli-fail-no-metadata.webp) Um ein Bundle mit der Option `metadata` korrekt hochzuladen, müssen Sie `--min-update-version` mit einem gültigen semver übergeben. Etwa so: ![CLI upload with metadata](/cli-upload-with-metadata.webp) Die `--min-update-version` ist nicht der EINZIGE Weg für Kompatibilität. Es gibt auch `--auto-min-update-version`. So funktioniert es: 1. Es prüft die aktuell im Kanal hochgeladene Version und überprüft die Kompatibilität wie der Befehl `bundle compatibility`. 2. Wenn die neue Version 100% kompatibel ist, wird die `min_update_version` der neuesten Version im Kanal wiederverwendet. Wenn nicht, wird die `min_update_version` auf die Bundle-Nummer der neu hochgeladenen Version gesetzt. Sie erhalten bei Verwendung dieser Option immer eine Information über die `min_update_version`. Es wird etwa so aussehen: ![Min update version](/min_update_version_info.webp) Wenn die neue Version nicht kompatibel ist, sollte es etwa so aussehen: ![Min update version not compatible](/min_update_version_not_compatible.webp) ## Ende-zu-Ende-Verschlüsselung (Trustless) [Section titled “Ende-zu-Ende-Verschlüsselung (Trustless)”](#ende-zu-ende-verschlüsselung-trustless) Capgo unterstützt Ende-zu-Ende-Verschlüsselung, das bedeutet, dass Ihr Bundle (Code) vor dem Senden in die Cloud verschlüsselt und auf dem Gerät entschlüsselt wird. Dafür müssen Sie ein RSA-Schlüsselpaar generieren, Sie können den folgenden Befehl verwenden, um es zu generieren. Das Verschlüsselungssystem ist eine Kombination aus RSA und AES, der RSA-Schlüssel wird verwendet, um den AES-Schlüssel zu verschlüsseln, und der AES-Schlüssel wird verwendet, um die Datei zu verschlüsseln. Weitere Informationen zum Verschlüsselungssystem finden Sie unten ![How crypto works](/crypto_explained.webp) Verschlüsselungsschema ### Schlüssel für Ihre App erstellen [Section titled “Schlüssel für Ihre App erstellen”](#schlüssel-für-ihre-app-erstellen) `npx @capgo/cli key create` Optional können Sie `--force` verwenden, um den vorhandenen Schlüssel zu überschreiben. Dieser Befehl erstellt ein Schlüsselpaar in Ihrer App und fordert Sie auf, den privaten Schlüssel an einem sicheren Ort zu speichern. Es wird empfohlen, den privaten Schlüssel nicht in Git zu committen und mit niemandem zu teilen. > Nach Ihrem lokalen Test entfernen Sie den Schlüssel aus der Konfigurationsdatei und fügen Sie ihn im CI-Schritt mit `key save` hinzu. ### Schlüssel in Ihrer App-Konfiguration speichern [Section titled “Schlüssel in Ihrer App-Konfiguration speichern”](#schlüssel-in-ihrer-app-konfiguration-speichern) `npx @capgo/cli key save` Optional können Sie angeben: `--key [/path/to/my/private_key]` der Pfad zu Ihrem privaten Schlüssel `--key-data [privateKey]` die privaten Schlüsseldaten, wenn Sie sie inline verwenden möchten. Dieser Befehl ist nützlich, wenn Sie der Empfehlung gefolgt sind und den Schlüssel nicht in Ihrer App und in der Konfiguration committet haben. ## CI-Integration [Section titled “CI-Integration”](#ci-integration) Um Ihre Arbeit zu automatisieren, empfehle ich Ihnen, GitHub Actions die Aufgabe des Pushens zu unserem Server zu überlassen. [GitHub Action Tutorial](https://capgo.app/blog/automatic-build-and-release-with-github-actions/) ## Unsere Demo-App [Section titled “Unsere Demo-App”](#unsere-demo-app) [GitHub - Cap-go/demo-app](https://github.com/Cap-go/demo-app/) Vergessen Sie nicht, die CI-Umgebungsvariable mit Ihrem API-Schlüssel zu konfigurieren. # CLI de 0.x a 1.x > Upgrade-Anleitung von 0.x auf 1.x Es gibt keine wesentlichen Änderungen in der CLI Die wichtigste Änderung ist die Umbenennung des Arguments `--version` zu `--bundle`, um Konflikte zu vermeiden und der neuen Namensgebung überall zu folgen # Verschlüsselung > So verschlüsseln Sie Ihre Daten mit neuer Verschlüsselung Diese Dokumentation erklärt, wie Sie Ihre Daten mit dem neuen Verschlüsselungssystem verschlüsseln und das alte entfernen können Erfahren Sie mehr über das neue Verschlüsselungssystem im [Blog-Beitrag](/blog/introducing-end-to-end-security-to-capacitor-updater-with-code-signing) *** Erstellen Sie zunächst ein neues Schlüsselpaar mit folgendem Befehl: ```bash npx @capgo/cli key create ``` Dieser Befehl erstellt ein neues Schlüsselpaar in Ihrer App; es ist zwingend erforderlich, den privaten Schlüssel an einem sicheren Ort aufzubewahren. Der private Schlüssel darf niemals in die Versionskontrolle übernommen oder mit nicht vertrauenswürdigen Parteien geteilt werden Dieser Befehl entfernt auch den alten Schlüssel aus Ihrer Capacitor-Konfiguration, entfernt jedoch nicht die alten Schlüsseldateien. Die CLI behält diese bei, damit Sie weiterhin Live-Updates für Apps senden können, die noch kein App-Store-Update erhalten haben und das alte Plugin verwenden. Dies erleichtert die Migration Wenn Sie bei der Migration gefragt werden: “Möchten Sie die Verschlüsselung mit dem neuen Kanal einrichten, um alte Apps zu unterstützen und die Migration zu erleichtern?”, stimmen Sie bitte zu. Dies fügt eine neue “defaultChannel”-Option zu Ihrer Capacitor-Konfiguration hinzu. Dadurch verwendet Ihre App den Kanal “encryption\_v2”. Dies stellt sicher, dass die neue Verschlüsselung nur von Apps verwendet wird, die sie unterstützen. Apps, die noch kein App-Store-Update erhalten haben, verwenden weiterhin den vorherigen Standardkanal *** Nun müssen Sie Ihr JS-Bundle erstellen und es in den neuen Kanal hochladen. Führen Sie dazu folgenden Befehl aus: ```bash npx @capgo/cli bundle upload --channel encryption_v2 ``` *** Führen Sie dann diesen Befehl aus, um Apps zu erlauben, sich selbst dem Kanal “encryption\_v2” zuzuweisen: Caution Dies ist notwendig, damit die neue “defaultChannel”-Option funktioniert ```bash npx @capgo/cli channel set encryption_v2 --self-assign ``` *** Sie können die App jetzt ausführen; sie wird das neue Verschlüsselungssystem verwenden Um das neue JS-Bundle in den alten Kanal hochzuladen, müssen Sie nur folgenden Befehl ausführen: ```bash npx @capgo/cli bundle upload --channel production ``` *** Sie müssen sich keine Sorgen um die Capacitor-Konfiguration machen, sie wird nie zu Capgo hochgeladen Wenn alle Benutzer ihre Apps aktualisiert haben (dies kann bis zu 3/4 Monate dauern), können Sie “defaultChannel” aus Ihrer Capacitor-Konfiguration entfernen Dann können Sie den alten Kanal mit folgendem Befehl entfernen: ```bash npx @capgo/cli channel delete encryption_v2 ``` *** Nach dem Löschen des “encryption\_v2”-Kanals werden alle Apps, die ihn als Standard verwenden, beginnen, den “production”-Kanal zu verwenden # Übersicht > Dieses Dokument bietet einen umfassenden Überblick über die Capgo CLI und wie sie genutzt werden kann, um Ihren App-Entwicklungsprozess durch nahtlose Live-Updates zu verbessern Nutzen Sie Capgos Live-Updates-Funktion, um die JavaScript-Bundles Ihrer App aus der Ferne in Echtzeit zu aktualisieren. Senden Sie JS-Updates direkt an Ihre Nutzer, ohne den App-Store-Überprüfungsprozess zu durchlaufen, um sofort Fehler zu beheben und neue Funktionen bereitzustellen. Note Live-Updates sind auf JavaScript-Bundle-Änderungen beschränkt. Wenn Sie nativen Code aktualisieren müssen, wie das Hinzufügen oder Entfernen eines Plugins oder das Ändern der nativen Projektkonfiguration, müssen Sie einen neuen nativen Binary-Build an die App Stores übermitteln. ## Wie Live-Updates funktionieren [Section titled “Wie Live-Updates funktionieren”](#wie-live-updates-funktionieren) Das Live-Update-System von Capgo hat zwei Schlüsselkomponenten: 1. Das Capgo SDK, das Sie in Ihrer App installieren. Das SDK prüft auf verfügbare Updates und lädt sie im Hintergrund herunter. 2. Kanäle, mit denen Sie Updates für bestimmte Benutzergruppen bereitstellen können. Sie können Kanäle verwenden, um verschiedene Release-Tracks wie `Production`, `Staging` und `Dev` zu verwalten. Wenn Sie ein neues JS-Bundle auf Capgo hochladen und einem Kanal zuweisen, erkennt das Capgo SDK in Apps, die für diesen Kanal konfiguriert sind, das Update und lädt es herunter. Beim nächsten Neustart der App wird das neue Bundle geladen. ## Erste Schritte [Section titled “Erste Schritte”](#erste-schritte) Folgen Sie diesen Schritten, um mit Live-Updates zu beginnen: 1. Schließen Sie den [Capgo Quickstart](/docs/getting-started/quickstart) ab, um Ihre App in Capgo einzurichten und das Capgo SDK zu installieren. 2. Rufen Sie in Ihrem App-Code `CapacitorUpdater.notifyAppReady()` auf, nachdem Ihre App initialisiert wurde. Dies teilt dem Capgo SDK mit, dass Ihre App bereit ist, Updates zu empfangen. 3. Erstellen Sie Ihr JS-Bundle und laden Sie es auf Capgo hoch: ```shell npm run build npx @capgo/cli@latest bundle upload --channel=production ``` 4. Öffnen Sie Ihre App und warten Sie, bis das Update heruntergeladen ist. Sie können den Status überprüfen mit: ```shell npx @capgo/cli@latest app debug ``` 5. Sobald das Update heruntergeladen ist, schließen und öffnen Sie Ihre App erneut, um das neue Bundle zu laden. Weitere Details finden Sie im [Leitfaden für Live-Updates-Bereitstellung](/docs/getting-started/deploy). ## Die Capgo CLI [Section titled “Die Capgo CLI”](#die-capgo-cli) Die Capgo CLI ist ein leistungsstarkes Tool, das Entwicklern ermöglicht, mit Capgos Diensten aus ihren eigenen CI/CD-Pipelines zu interagieren. Mit der CLI haben Sie detaillierte Kontrolle darüber, wann Builds erstellt und bereitgestellt werden, sodass Sie Capgo in Ihre bestehenden Enterprise-Workflows integrieren können. ### Wofür ist die Capgo CLI gedacht? [Section titled “Wofür ist die Capgo CLI gedacht?”](#wofür-ist-die-capgo-cli-gedacht) Die Capgo CLI ist für Entwickler und Teams konzipiert, die mehr Kontrolle und Flexibilität in ihren Live-Update-Workflows benötigen. Durch die Verwendung der CLI in Ihren CI/CD-Pipelines können Sie: * Genau festlegen, wann Updates erstellt und bereitgestellt werden sollen, anstatt sich auf Capgos integrierte Automatisierung zu verlassen * Eigene Prozesse wie Code-Signierung, QA-Tests oder Manager-Genehmigungen zwischen den Build- und Bereitstellungsschritten einfügen * Capgo in Ihre bestehenden DevOps-Tools und -Workflows integrieren ### Authentifizierung [Section titled “Authentifizierung”](#authentifizierung) Um die Capgo CLI zu nutzen, müssen Sie sich mit Ihrem API-Schlüssel authentifizieren. Sie können einen API-Schlüssel in Ihren Capgo-Kontoeinstellungen generieren. Um sich anzumelden und Ihren API-Schlüssel sicher zu speichern, führen Sie aus: ```shell npx @capgo/cli@latest login [API_KEY] ``` Dieser Befehl wird dann für zukünftige Verwendungen gespeichert. Nach der Anmeldung müssen Sie Ihren API-Schlüssel nicht bei jedem Befehl erneut angeben. ### Wichtige Unterschiede zu anderen CLI-Tools [Section titled “Wichtige Unterschiede zu anderen CLI-Tools”](#wichtige-unterschiede-zu-anderen-cli-tools) Wenn Sie mit anderen Live-Update-CLI-Tools vertraut sind, gibt es einige wichtige Besonderheiten der Capgo CLI zu beachten: * Capgo verwendet eine einzige CLI sowohl für Entwicklungs- als auch für CI/CD-Anwendungsfälle, da Capgo sich ausschließlich auf Live-Update-Funktionen konzentriert * Die Capgo CLI erfordert keinen separaten Installationsschritt. Sie ist im `@capgo/cli`-Paket enthalten und kann direkt mit `npx` ausgeführt werden * Die Capgo CLI ist speziell für den Live-Update-Workflow konzipiert und enthält möglicherweise nicht alle Funktionen oder Befehle, die in allgemeineren CLI-Tools zu finden sind. ## Nächste Schritte [Section titled “Nächste Schritte”](#nächste-schritte) [ Kanäle](/docs/live-updates/channels/) [Erfahren Sie, wie Sie Kanäle verwenden, um verschiedene Release-Tracks zu verwalten und Updates für bestimmte Benutzer bereitzustellen.](/docs/live-updates/channels/) [ Rollbacks](/docs/live-updates/rollbacks/) [Entdecken Sie, wie Sie zu einer vorherigen JS-Bundle-Version zurückkehren können, wenn ein Update Probleme verursacht.](/docs/live-updates/rollbacks/) [ Update-Verhalten](/docs/live-updates/update-behavior/) [Passen Sie an, wie und wann Updates in Ihrer App heruntergeladen und angewendet werden.](/docs/live-updates/update-behavior/) [ Schnelle Updates](/docs/live-updates/differentials/) [Erfahren Sie, wie Sie schnelle Updates verwenden können, um den Update-Prozess zu beschleunigen.](/docs/live-updates/differentials/) # Übersicht > Detaillierte Dokumentation für Capgo CLI-Befehle Die Capgo CLI stellt eine Reihe von Befehlen zur Verwaltung Ihrer Capgo-Apps und Deployments bereit. Diese Referenz bietet detaillierte Informationen zu jedem verfügbaren Befehl, einschließlich seiner Optionen und Anwendungsbeispiele. ## Befehle [Section titled “Befehle”](#befehle) [ init](/docs/cli/reference/init/) [Eine neue Capgo-App initialisieren](/docs/cli/reference/init/) [ login](./login/) [Mit dem Capgo-Service authentifizieren](./login/) [ doctor](/docs/cli/reference/doctor/) [Überprüfen Sie Ihre Capgo-Einrichtung auf mögliche Probleme](/docs/cli/reference/doctor/) [ app](./app/) [Verwalten Sie Ihre Capgo-Apps](./app/) [ bundle](/docs/cli/reference/bundle/) [Verwalten Sie Ihre App-Bundles](/docs/cli/reference/bundle/) [ channel](/docs/cli/reference/channel/) [Verwalten Sie Ihre Release-Kanäle](/docs/cli/reference/channel/) [ key](/docs/cli/reference/key/) [Verwalten Sie Ihre App-Signierungsschlüssel](/docs/cli/reference/key/) ## CI-Integration [Section titled “CI-Integration”](#ci-integration) Um Ihre Arbeit zu automatisieren, empfehlen wir die Verwendung von GitHub Actions, um Ihre Updates an Capgo zu übertragen. Weitere Informationen finden Sie in unserem [GitHub Actions Tutorial](https://capgo.app/blog/automatic-build-and-release-with-github-actions/) Vergessen Sie nicht, Ihre CI-Umgebungsvariablen mit Ihrem Capgo API-Schlüssel zu konfigurieren. ## Demo-App [Section titled “Demo-App”](#demo-app) Ein vollständiges Beispiel einer Capgo-App mit CI-Integration finden Sie in unserer [Demo-App auf GitHub](https://github.com/Cap-go/demo-app/) # Konto Der `account`-Befehl ermöglicht die Verwaltung Ihres Capgo-Kontos ### id [Section titled “id”](#id) `npx @capgo/cli account id` Ruft Ihre Konto-ID ab Optionen: * `-a, --apikey `: API-Schlüssel zur Verknüpfung mit Ihrem Konto # App Der `app`-Befehl ermöglicht die Verwaltung Ihrer Capgo-Apps ### add [Section titled “add”](#add) `npx @capgo/cli app add [appId]` Fügt eine neue App zu Ihrem Capgo-Konto hinzu `[appId]` ist Ihre App-ID im Format `com.example.app`. Weitere Informationen finden Sie in der [Capacitor-Dokumentation](https://capacitorjs.com/docs/cli/commands/init/) > 💡 Alle Optionen werden aus Ihrer `capacitor.config.json` ermittelt, wenn sie nicht angegeben werden Optionen: * `--icon [path]`: Pfad zu einem benutzerdefinierten Symbol, das in der Capgo-Webanwendung angezeigt wird * `--name [name]`: Benutzerdefinierter Name, der in der App-Liste angezeigt wird * `--apikey [key]`: API-Schlüssel zur Verknüpfung mit Ihrem Konto * `--retention [days]`: Aufbewahrungszeitraum für App-Bundles in Tagen (Standard: 0 = unbegrenzt) ### set [Section titled “set”](#set) `npx @capgo/cli app set [appId]` Aktualisiert eine bestehende App in Ihrem Capgo-Konto Optionen: * `--icon [path]`: Pfad zu einem benutzerdefinierten Symbol, das in der Capgo-Webanwendung angezeigt wird * `--name [name]`: Benutzerdefinierter Name, der in der App-Liste angezeigt wird * `--retention [days]`: Aufbewahrungszeitraum für App-Bundles in Tagen (Standard: 0 = unbegrenzt) * `--apikey [key]`: API-Schlüssel zur Verknüpfung mit Ihrem Konto ### list [Section titled “list”](#list) `npx @capgo/cli app list [appId]` Listet alle Apps in Ihrem Capgo-Konto auf Optionen: * `--apikey [key]`: API-Schlüssel zur Verknüpfung mit Ihrem Konto ### delete [Section titled “delete”](#delete) `npx @capgo/cli app delete [appId]` Löscht eine App aus Ihrem Capgo-Konto Optionen: * `--apikey [key]`: API-Schlüssel zur Verknüpfung mit Ihrem Konto * `--bundle`: Löscht nur eine bestimmte Bundle-Version ### debug [Section titled “debug”](#debug) `npx @capgo/cli app debug [appId]` Zeigt Debug-Informationen für eine App an Optionen: * `--apikey [key]`: API-Schlüssel zur Verknüpfung mit Ihrem Konto * `--device`: Debug eines bestimmten Geräts ### setting [Section titled “setting”](#setting) `npx @capgo/cli app setting [path]` Bearbeitet die Capacitor-Konfiguration für eine App `[path]` ist der Pfad zur Einstellung, die Sie ändern möchten (z.B. `appId` oder `plugins.CapacitorUpdater.autoUpdate`) Sie müssen entweder `--string` oder `--bool` angeben: * `--string `: Setzt die Einstellung auf einen String-Wert * `--bool `: Setzt die Einstellung auf einen Boolean-Wert # 🏗️ build > Erstellen Sie native Mobile Apps in der Cloud und übermitteln Sie sie direkt über Ihre CLI an App Store und Play Store. 🏗️ Erstellen Sie native Mobile Apps in der Cloud und übermitteln Sie sie automatisch an den App Store und Play Store. ## Übersicht [Section titled “Übersicht”](#übersicht) Der `build`-Befehl ermöglicht es Ihnen, Ihre Capacitor-App für iOS und Android in der Cloud-Infrastruktur von Capgo zu erstellen, ähnlich wie Expo Builds handhabt. Ihre App wird auf dedizierter Infrastruktur erstellt und kann automatisch an die App Stores übermittelt werden. **Aktueller Status:** Öffentliche Beta ## Warum Cloud Build verwenden? [Section titled “Warum Cloud Build verwenden?”](#warum-cloud-build-verwenden) Das traditionelle Erstellen nativer mobiler Apps erfordert: * **Mac-Hardware** für iOS-Builds ($1000+ oder teure CI-Minuten) * **Komplexe CI/CD**-Einrichtung mit Caching und Zugangsdaten * **Wartungsaufwand** für Xcode, Android Studio und SDKs Mit Capgo Cloud Build erhalten Sie: * ✅ **Kein Mac erforderlich** - Erstellen Sie iOS-Apps von jedem Gerät aus * ✅ **Keine Einrichtung** - Keine CI/CD-Konfiguration erforderlich * ✅ **Kampferprobt** - Basiert auf 3 Jahren interner Produktionsnutzung * ✅ **Sicher** - Keine Protokollspeicherung, automatische Bereinigung * ✅ **Nur nativ** - Ihr JavaScript bleibt privat ## Schnellstart [Section titled “Schnellstart”](#schnellstart) ```bash npx @capgo/cli@latest build com.example.app ``` Das ist alles! Ihre App wird in der Cloud erstellt und Sie sehen Echtzeit-Protokolle. ## Dokumentationsabschnitte [Section titled “Dokumentationsabschnitte”](#dokumentationsabschnitte) ⚠️ Richten Sie ZUERST Zugangsdaten ein **Erforderlich vor dem Erstellen:** Speichern Sie Ihre iOS/Android-Zugangsdaten lokal. [Zugangsdaten einrichten →](/docs/cli/cloud-build/credentials/) Erste Schritte Erstellen Sie Ihren ersten nativen Build in Minuten. [Anleitung lesen →](/docs/cli/cloud-build/getting-started/) iOS Builds Konfigurieren Sie Zertifikate und erstellen Sie für den App Store. [iOS konfigurieren →](/docs/cli/cloud-build/ios/) Android Builds Richten Sie Keystores ein und erstellen Sie für den Play Store. [Android konfigurieren →](/docs/cli/cloud-build/android/) Fehlerbehebung Lösungen für häufige Build-Probleme. [Hilfe erhalten →](/docs/cli/cloud-build/troubleshooting/) ## Befehlsreferenz [Section titled “Befehlsreferenz”](#befehlsreferenz) ### Grundlegende Verwendung [Section titled “Grundlegende Verwendung”](#grundlegende-verwendung) ```bash npx @capgo/cli@latest build [appId] [optionen] ``` ### Beispiele [Section titled “Beispiele”](#beispiele) Für beide Plattformen erstellen: ```bash npx @capgo/cli@latest build com.example.app ``` Nur für iOS erstellen: ```bash npx @capgo/cli@latest build com.example.app --platform ios ``` Für Android im Debug-Modus erstellen: ```bash npx @capgo/cli@latest build com.example.app --platform android --build-mode debug ``` Aus einem bestimmten Verzeichnis erstellen: ```bash npx @capgo/cli@latest build com.example.app --path ./my-app ``` ### Optionen [Section titled “Optionen”](#optionen) | Option | Typ | Standard | Beschreibung | | ------------------------- | ------ | ----------------------- | -------------------------------------------------------------- | | `appId` | string | capacitor.config | Anwendungs-ID (z. B. com.example.app) | | `--path ` | string | Aktuelles Verzeichnis | Pfad zu Ihrem Projektverzeichnis | | `--platform ` | string | both | Zielplattform: `ios`, `android` oder `both` | | `--build-mode ` | string | release | Build-Modus: `debug` oder `release` | | `--build-config ` | string | - | Zusätzliche Build-Konfiguration als JSON-String | | `-a, --apikey ` | string | - | API-Schlüssel (oder `CAPGO_TOKEN` Umgebungsvariable verwenden) | | `--supa-host ` | string | | Benutzerdefinierte Supabase-Host-URL | | `--supa-anon ` | string | - | Benutzerdefinierter Supabase-Anon-Schlüssel | ## Wie es funktioniert [Section titled “Wie es funktioniert”](#wie-es-funktioniert) ```mermaid sequenceDiagram participant Dev as Entwickler participant CLI as Capgo CLI participant Cloud as Capgo Cloud participant iOS as Mac Builder participant Android as Android Builder Dev->>CLI: build com.example.app CLI->>CLI: Projekt lokal zippen CLI->>Cloud: Zu R2 hochladen Cloud->>iOS: Mac bereitstellen (iOS) Cloud->>Android: Sandbox starten (Android) iOS-->>CLI: Protokolle streamen (SSE) Android-->>CLI: Protokolle streamen (SSE) iOS->>Cloud: Build abgeschlossen Android->>Cloud: Build abgeschlossen Cloud->>iOS: Bereinigung (24h später) Cloud->>Android: Bereinigung (sofort) CLI->>Dev: Build erfolgreich ``` ### Build-Prozess [Section titled “Build-Prozess”](#build-prozess) 1. **Lokale Vorbereitung** - Ihr Projekt wird lokal gezippt (ohne `node_modules`, Dotfiles) 2. **Upload** - Zip-Datei wird zu sicherem Cloud-Speicher hochgeladen (Cloudflare R2) 3. **Build-Ausführung**: * **iOS**: Dedizierte Mac-Maschine bereitgestellt, Fastlane erstellt und signiert * **Android**: Sichere Sandbox erstellt, Gradle kompiliert und signiert 4. **Protokoll-Streaming** - Echtzeit-Protokolle über Server-Sent Events (nicht gespeichert!) 5. **Automatische Bereinigung**: * **iOS**: Dateien nach 24 Stunden gelöscht, wenn Maschine freigegeben wird * **Android**: Alles sofort nach Build gelöscht ## Unsere Expertise [Section titled “Unsere Expertise”](#unsere-expertise) Capgo Cloud Build ist keine neue Infrastruktur - wir nutzen sie seit **3 Jahren** intern: * ✅ **Benutzerdefiniertes Fastlane** - Speziell für Capacitor-Apps entwickelt * ✅ **Tausende von Builds** - Kampferprobt in Produktion * ✅ **Capacitor-Experten** - Tiefes Wissen über das Wesentliche * ✅ **Nur-nativ-Fokus** - Ihr JavaScript berührt nie unsere Server ## Sicherheit & Datenschutz [Section titled “Sicherheit & Datenschutz”](#sicherheit--datenschutz) * **Keine Protokollspeicherung** - Protokolle streamen nur zu Ihrem Terminal, werden niemals gespeichert * **Keine Artefaktspeicherung** - Apps werden direkt an App Store/Play Store gesendet, wir behalten nichts * **Zugangsdaten automatisch gelöscht** - Nur während Build verwendet, danach gelöscht (max. 24h) * **Isolierte Builds** - Jeder Build läuft isoliert * **Ihr Code bleibt Ihrer** - Wir erstellen nur native Teile, JavaScript bleibt lokal ## CI/CD-Integration [Section titled “CI/CD-Integration”](#cicd-integration) Funktioniert überall - GitHub Actions, GitLab CI oder jede CI/CD-Plattform: ```yaml - name: Build native app env: CAPGO_TOKEN: ${{ secrets.CAPGO_TOKEN }} run: | npm run build npx cap sync npx @capgo/cli@latest build com.example.app \ --platform both \ --build-mode release ``` Nicht erforderlich: * Mac-Runner * Android SDK-Installation * Xcode-Installation * Komplexes Caching * Zugangsdatenverwaltung ## Preisgestaltung [Section titled “Preisgestaltung”](#preisgestaltung) Build-Zeit wird basierend auf tatsächlicher Nutzung abgerechnet: * **Android**: 1× Multiplikator (\~$0.XX pro Minute) * **iOS**: 2× Multiplikator (\~$0.XX pro Minute, aufgrund von Mac-Hardware) **Typische Kosten:** * Android Debug: 3 min × 1× = \~$X.XX * iOS Release: 7 min × 2× = \~$X.XX Zahlen Sie nur für das, was Sie nutzen. Keine Mindestbeträge, keine Überraschungen. ## Vergleich mit anderen Lösungen [Section titled “Vergleich mit anderen Lösungen”](#vergleich-mit-anderen-lösungen) | Funktion | Capgo Cloud Build | GitHub Actions (Mac) | Expo EAS | | --------------------------- | --------------------- | -------------------- | ----------------- | | **Mac lokal erforderlich** | ❌ Nein | ✅ Ja | ❌ Nein | | **Einrichtungskomplexität** | ⭐ Einzelner Befehl | ⭐⭐⭐ Komplexes YAML | ⭐⭐ Config-Dateien | | **Capacitor nativ** | ✅ Optimiert | ⚠️ Generisch | ❌ Nur Expo | | **Ihr Code-Datenschutz** | ✅ Niemals gespeichert | ⚠️ In Runnern | ⚠️ Hochgeladen | | **Kosten (iOS)** | 💰 2× Basis | 💰💰💰 10× teuer | 💰💰 Premium | ## Was wird erstellt [Section titled “Was wird erstellt”](#was-wird-erstellt) **Wichtig:** Capgo erstellt **nur native Teile**. ✅ **Wir erstellen:** * iOS-nativer Code (Swift, Objective-C, Xcode-Projekte) * Android-nativer Code (Java, Kotlin, Gradle-Projekte) * Code-Signatur und App Store-Übermittlung ❌ **Sie erstellen (lokal):** * JavaScript, HTML, CSS (`npm run build`) * Capacitor-Sync (`npx cap sync`) * Ihre Web-Assets Diese Trennung gewährleistet: * **Bessere Sicherheit** - Ihre App-Logik bleibt privat * **Schnellere Builds** - Keine doppelten Web-Builds * **Klare Verantwortung** - Sie kontrollieren Ihren Code ## Einschränkungen [Section titled “Einschränkungen”](#einschränkungen) Aktuelle Einschränkungen während der öffentlichen Beta: * **Build-Timeout**: 10 Minuten maximal * **Upload-Timeout**: 1 Stunde für Upload-URL * **iOS-Maschine**: 24-Stunden-Lease-Anforderung, Build auf Mac wird in Warteschlange gestellt, um optimale Nutzung sicherzustellen * **Zugriff**: Nur öffentliche Beta ## Erste Schritte [Section titled “Erste Schritte”](#erste-schritte) Bereit, ohne Aufwand zu erstellen? Erstellen Sie Ihren ersten Build Schritt-für-Schritt-Anleitung für Ihren ersten Cloud-Build. [Jetzt starten →](/docs/cli/cloud-build/getting-started/) An öffentlicher Beta teilnehmen Cloud Build ist in der öffentlichen Beta. Treten Sie unserer Community bei, um Zugriff zu erhalten. [Discord beitreten →](https://discord.com/invite/VnYRvBfgA6) ## Mehr erfahren [Section titled “Mehr erfahren”](#mehr-erfahren) * [Erste-Schritte-Anleitung](/docs/cli/cloud-build/getting-started/) - Erstellen Sie Ihren ersten Build * [iOS-Konfiguration](/docs/cli/cloud-build/ios/) - iOS-Builds einrichten * [Android-Konfiguration](/docs/cli/cloud-build/android/) - Android-Builds einrichten * [Fehlerbehebung](/docs/cli/cloud-build/troubleshooting/) - Häufige Probleme und Lösungen * [Blog: Cloud Build vorstellen](/blog/introducing-capgo-cloud-build/) - Feature-Ankündigung ## Brauchen Sie Hilfe? [Section titled “Brauchen Sie Hilfe?”](#brauchen-sie-hilfe) * 📚 [Fehlerbehebungsanleitung](/docs/cli/cloud-build/troubleshooting/) * 💬 [Discord-Community](https://discord.com/invite/VnYRvBfgA6) * 📧 E-Mail: # Bündel Der `bundle`-Befehl ermöglicht die Verwaltung Ihrer App-Bundles ### upload [Section titled “upload”](#upload) `npx @capgo/cli bundle upload [appId]` Lädt ein neues Bundle für eine App hoch Optionen: * `-a, --apikey `: API-Schlüssel zur Verknüpfung mit Ihrem Konto * `-p, --path `: Pfad zum hochzuladenden Ordner (standardmäßig das `webDir` in `capacitorconfig`) * `-c, --channel `: Channel, mit dem das Bundle verknüpft werden soll * `-e, --external `: Link zu einer externen URL statt Upload zu Capgo Cloud * `--iv-session-key `: IV und Session-Schlüssel für externe Bundle-URL festlegen * `--s3-region `: Region für Ihren S3-Bucket * `--s3-apikey `: API-Schlüssel für Ihren S3-Endpunkt * `--s3-apisecret `: API-Secret für Ihren S3-Endpunkt * `--s3-endpoint `: URL des S3-Endpunkts * `--s3-bucket-name `: Name Ihres S3-Buckets * `--s3-port `: Port für Ihren S3-Endpunkt * `--no-s3-ssl`: SSL für S3-Uploads deaktivieren * `--key `: Benutzerdefinierter Pfad für den öffentlichen Signaturschlüssel (v1-System) * `--key-data `: Öffentliche Signaturschlüsseldaten (v1-System) * `--key-v2 `: Benutzerdefinierter Pfad für den privaten Signaturschlüssel (v2-System) * `--key-data-v2 `: Private Signaturschlüsseldaten (v2-System) * `--bundle-url`: Bundle-URL in stdout ausgeben * `--no-key`: Signaturschlüssel ignorieren und unsigniertes Update senden * `--no-code-check`: Überprüfung auf `notifyAppReady()` im Quellcode und `indexhtml` im Root-Verzeichnis überspringen * `--display-iv-session`: IV und Session-Schlüssel zur Verschlüsselung des Updates anzeigen * `-b, --bundle `: Hochzuladende Bundle-Versionsnummer * `--min-update-version `: Mindestversion der App für dieses Update (nur verwendet wenn Auto-Update über Metadaten deaktiviert ist) * `--auto-min-update-version`: Automatische Festlegung der Mindest-Update-Version basierend auf nativen Paketversionen * `--ignore-metadata-check`: Metadaten-Prüfung (node\_modules) beim Hochladen ignorieren * `--ignore-checksum-check`: Prüfsummen-Prüfung beim Hochladen ignorieren * `--timeout `: Timeout für den Upload-Prozess in Sekunden * `--multipart`: Multipart-Protokoll für S3-Upload verwenden (veraltet, nutzen Sie stattdessen `--tus`) * `--tus`: Bundle mit dem tus-Protokoll hochladen * `--tus-chunk-size `: Chunk-Größe für den tus-Upload * `--partial`: Nur geänderte Dateien zu Capgo Cloud hochladen * `--partial-only`: Nur partielle Dateien zu Capgo Cloud hochladen, ZIP-Datei überspringen (nützlich für große Bundles) * `--encrypted-checksum `: Verschlüsselte Prüfsumme (Signatur) für externes Bundle * `--auto-set-bundle`: Bundle-Version automatisch in `capacitorconfigjson` setzen * `--dry-upload`: Testlauf des Upload-Prozesses ohne tatsächlichen Upload (nützlich zum Testen) * `--package-json `: Kommagetrennte Liste von Pfaden zu `packagejson`-Dateien (nützlich für Monorepos) * `--node-modules `: Kommagetrennte Liste von Pfaden zu `node_modules`-Verzeichnissen (nützlich für Monorepos) * `--encrypt-partial`: Partielle Update-Dateien verschlüsseln * `--delete-linked-bundle-on-upload`: Aktuell verknüpftes Bundle im Ziel-Channel vor dem Upload löschen ### compatibility [Section titled “compatibility”](#compatibility) `npx @capgo/cli bundle compatibility [appId]` Prüft die Kompatibilität eines Bundles mit einem bestimmten Channel Optionen: * `-a, --apikey `: API-Schlüssel zur Verknüpfung mit Ihrem Konto * `-c, --channel `: Zu prüfender Channel * `--text`: Ergebnisse als Text statt Emoji ausgeben * `--package-json `: Kommagetrennte Liste von Pfaden zu `packagejson`-Dateien (nützlich für Monorepos) * `--node-modules `: Kommagetrennte Liste von Pfaden zu `node_modules`-Verzeichnissen (nützlich für Monorepos) ### delete [Section titled “delete”](#delete) `npx @capgo/cli bundle delete [bundleId] [appId]` Löscht ein Bundle aus einer App Optionen: * `-a, --apikey `: API-Schlüssel zur Verknüpfung mit Ihrem Konto ### list [Section titled “list”](#list) `npx @capgo/cli bundle list [appId]` Listet alle Bundles einer App auf Optionen: * `-a, --apikey `: API-Schlüssel zur Verknüpfung mit Ihrem Konto ### cleanup [Section titled “cleanup”](#cleanup) `npx @capgo/cli bundle cleanup [appId]` Bereinigt alte Bundles einer Hauptversion und behält die angegebene Anzahl der neuesten Bundles Optionen: * `-b, --bundle `: Zu bereinigende Hauptversionsnummer * `-a, --apikey `: API-Schlüssel zur Verknüpfung mit Ihrem Konto * `-k, --keep `: Anzahl der zu behaltenden Bundles (Standard: 4) * `-f, --force`: Erzwungenes Entfernen ohne Bestätigung ### decrypt [Section titled “decrypt”](#decrypt) `npx @capgo/cli bundle decrypt [zipPath] [sessionKey]` Entschlüsselt ein signiertes ZIP-Bundle Optionen: * `--key `: Benutzerdefinierter Pfad für den privaten Signaturschlüssel * `--key-data `: Private Signaturschlüsseldaten ### encrypt [Section titled “encrypt”](#encrypt) `npx @capgo/cli bundle encrypt [zipPath]` Verschlüsselt ein ZIP-Bundle Optionen: * `--key `: Benutzerdefinierter Pfad für den privaten Signaturschlüssel * `--key-data `: Private Signaturschlüsseldaten ### encryptV2 [Section titled “encryptV2”](#encryptv2) `npx @capgo/cli bundle encryptV2 [zipPath] [checksum]` Verschlüsselt ein ZIP-Bundle mit der neuen Verschlüsselungsmethode Optionen: * `--key `: Benutzerdefinierter Pfad für den privaten Signaturschlüssel * `--key-data `: Private Signaturschlüsseldaten * `-j, --json`: Ergebnisse als JSON ausgeben ### decryptV2 [Section titled “decryptV2”](#decryptv2) `npx @capgo/cli bundle decryptV2 [zipPath] [checksum]` Entschlüsselt ein ZIP-Bundle mit der neuen Verschlüsselungsmethode Optionen: * `--key `: Benutzerdefinierter Pfad für den privaten Signaturschlüssel * `--key-data `: Private Signaturschlüsseldaten * `--checksum `: Prüfsumme des Bundles zur Integritätsprüfung ### zip [Section titled “zip”](#zip) `npx @capgo/cli bundle zip [appId]` Erstellt eine ZIP-Datei für ein Bundle Optionen: * `-p, --path `: Pfad zum zu zippenden Ordner (standardmäßig das `webDir` in `capacitorconfig`) * `-b, --bundle `: Bundle-Versionsnummer für den Dateinamen * `-n, --name `: Benutzerdefinierter Dateiname für die ZIP-Datei * `-j, --json`: Ergebnisse als JSON ausgeben * `--no-code-check`: Überprüfung auf `notifyAppReady()` im Quellcode und `indexhtml` im Root-Verzeichnis überspringen * `--key-v2`: Neue Verschlüsselungsmethode (v2) verwenden * `--package-json `: Kommagetrennte Liste von Pfaden zu `packagejson`-Dateien (nützlich für Monorepos) # Kanal Der `channel`-Befehl ermöglicht die Verwaltung Ihrer Release-Kanäle ### add [Section titled “add”](#add) `npx @capgo/cli channel add [channelId] [appId]` Erstellt einen neuen Kanal für eine App Optionen: * `-d, --default`: Setzt den neuen Kanal als Standardkanal * `-a, --apikey `: API-Schlüssel zur Verknüpfung mit Ihrem Konto ### delete [Section titled “delete”](#delete) `npx @capgo/cli channel delete [channelId] [appId]` Löscht einen Kanal aus einer App Optionen: * `-a, --apikey `: API-Schlüssel zur Verknüpfung mit Ihrem Konto * `--delete-bundle`: Löscht das mit dem Kanal verbundene Bundle ### list [Section titled “list”](#list) `npx @capgo/cli channel list [appId]` Listet alle Kanäle einer App auf Optionen: * `-a, --apikey `: API-Schlüssel zur Verknüpfung mit Ihrem Konto ### currentBundle [Section titled “currentBundle”](#currentbundle) `npx @capgo/cli channel currentBundle [channel] [appId]` Ruft das aktuelle Bundle für einen bestimmten Kanal ab Optionen: * `-c, --channel `: Kanal, von dem das aktuelle Bundle abgerufen werden soll * `-a, --apikey `: API-Schlüssel zur Verknüpfung mit Ihrem Konto * `--quiet`: Gibt nur die Bundle-Version aus ### set [Section titled “set”](#set) `npx @capgo/cli channel set [channelId] [appId]` Legt die Eigenschaften eines Kanals fest Optionen: * `-a, --apikey `: API-Schlüssel zur Verknüpfung mit Ihrem Konto * `-b, --bundle `: Bundle-Versionsnummer für den Kanal festlegen * `-s, --state `: Setzt den Status des Kanals (`default` oder `normal`) * `--latest`: Verwendet die neueste Version aus `packagejson` als Bundle-Version * `--downgrade`: Erlaubt Downgrades auf Versionen unter der nativen Version * `--no-downgrade`: Deaktiviert Downgrades auf Versionen unter der nativen Version * `--upgrade`: Erlaubt Upgrades auf Versionen über der nativen Version * `--no-upgrade`: Deaktiviert Upgrades auf Versionen über der nativen Version * `--ios`: Erlaubt das Senden von Updates an iOS-Geräte * `--no-ios`: Deaktiviert das Senden von Updates an iOS-Geräte * `--android`: Erlaubt das Senden von Updates an Android-Geräte * `--no-android`: Deaktiviert das Senden von Updates an Android-Geräte * `--self-assign`: Erlaubt Geräten die Selbstzuweisung zu diesem Kanal * `--no-self-assign`: Deaktiviert die Selbstzuweisung von Geräten zu diesem Kanal * `--disable-auto-update `: Deaktiviert die Auto-Update-Strategie für diesen Kanal (Optionen: `major`, `minor`, `metadata`, `patch`, `none`) * `--dev`: Erlaubt das Senden von Updates an Entwicklungsgeräte * `--no-dev`: Deaktiviert das Senden von Updates an Entwicklungsgeräte * `--emulator`: Erlaubt das Senden von Updates an Emulator-Geräte * `--no-emulator`: Deaktiviert das Senden von Updates an Emulator-Geräte * `--package-json `: Kommagetrennte Liste von Pfaden zu `packagejson`-Dateien (nützlich für Monorepos) # Arzt `npx @capgo/cli doctor` Dieser Befehl überprüft, ob Sie die neueste Version der Capgo-Pakete verwenden Er ist auch nützlich für die Meldung von Fehlern # initialisieren `npx @capgo/cli@latest init [apikey]` Dieser Befehl führt Sie Schritt für Schritt durch: * Hinzufügen Ihrer App zu Capgo * Hinzufügen des Codes zu Ihrer App zur Validierung des Updates * Erstellen Ihrer App * Hochladen Ihrer App zu Capgo * Hilft Ihnen zu überprüfen, ob das Update funktioniert # Schlüssel Mit dem Befehl `key` können Sie Ihre App-Signierungsschlüssel verwalten ### save [Section titled “save”](#save) `npx @capgo/cli key save` Speichert einen Base64-codierten Verschlüsselungsschlüssel in der Capacitor-Konfiguration (nützlich für CI) Options: * `-f, --force`: Erzwingt die Generierung eines neuen Schlüssels * `--key`: Pfad zur Schlüsseldatei, die in der Capacitor-Konfiguration gespeichert werden soll * `--key-data`: Schlüsseldaten, die direkt in der Capacitor-Konfiguration gespeichert werden sollen ### create [Section titled “create”](#create) `npx @capgo/cli key create` Erstellt einen neuen Verschlüsselungsschlüssel Options: * `-f, --force`: Erzwingt die Generierung eines neuen Schlüssels ### delete\_old [Section titled “delete\_old”](#delete_old) `npx @capgo/cli key delete_old` Löscht den alten Verschlüsselungsschlüssel # Login `npx @capgo/cli login [apikey]` Dieser Befehl speichert Ihren Capgo API-Schlüssel für die zukünftige Verwendung Note Sie können den gespeicherten API-Schlüssel überschreiben, indem Sie `--apikey=` zu einem beliebigen Befehl hinzufügen Optionen: * `--local`: Speichert den API-Schlüssel im lokalen Repository und fügt ihn zur `gitignore` hinzu # 🔹 organisation > 🏢 Verwalten Sie Ihre Organisationen in Capgo Cloud für Teamzusammenarbeit und App-Verwaltung. 🏢 Verwalten Sie Ihre Organisationen in Capgo Cloud für Teamzusammenarbeit und App-Verwaltung. ### []()📋 **List** [Section titled “ 📋 List”](#--list) **Alias:** `l` ```bash npx @capgo/cli@latest organisation list ``` 📋 Auflisten aller Organisationen, auf die Sie in Capgo Cloud Zugriff haben. **Beispiel:** ```bash npx @capgo/cli@latest organisation list ``` **Optionen:** | Parameter | Typ | Beschreibung | | -------------- | -------- | ------------------------------------------------------------------------------ | | **-a,** | `string` | API-Schlüssel, um mit Ihrem Konto zu verknüpfen | | **—supa-host** | `string` | Benutzerdefinierte Supabase-Host-URL (für Self-Hosting oder Capgo-Entwicklung) | | **—supa-anon** | `string` | Benutzerdefinierter Supabase-Anon-Schlüssel (für Self-Hosting) | ### []()➕ **Add** [Section titled “ ➕ Add”](#--add) **Alias:** `a` ```bash npx @capgo/cli@latest organisation add ``` ➕ Erstellen Sie eine neue Organisation in Capgo Cloud für Teamzusammenarbeit. **Beispiel:** ```bash npx @capgo/cli@latest organisation add --name "My Company" --email admin@mycompany.com ``` **Optionen:** | Parameter | Typ | Beschreibung | | -------------- | -------- | ------------------------------------------------------------------------------ | | **-n,** | `string` | Organisationsname | | **-e,** | `string` | Verwaltungs-E-Mail für die Organisation | | **-a,** | `string` | API-Schlüssel, um mit Ihrem Konto zu verknüpfen | | **—supa-host** | `string` | Benutzerdefinierte Supabase-Host-URL (für Self-Hosting oder Capgo-Entwicklung) | | **—supa-anon** | `string` | Benutzerdefinierter Supabase-Anon-Schlüssel (für Self-Hosting) | ### []()⚙️ **Set** [Section titled “ ⚙️ Set”](#-️-set) **Alias:** `s` ```bash npx @capgo/cli@latest organisation set ``` ⚙️ Aktualisieren Sie Organisationseinstellungen wie Name und Verwaltungs-E-Mail. **Beispiel:** ```bash npx @capgo/cli@latest organisation set ORG_ID --name "Updated Company Name" ``` **Optionen:** | Parameter | Typ | Beschreibung | | -------------- | -------- | ------------------------------------------------------------------------------ | | **-n,** | `string` | Organisationsname | | **-e,** | `string` | Verwaltungs-E-Mail für die Organisation | | **-a,** | `string` | API-Schlüssel, um mit Ihrem Konto zu verknüpfen | | **—supa-host** | `string` | Benutzerdefinierte Supabase-Host-URL (für Self-Hosting oder Capgo-Entwicklung) | | **—supa-anon** | `string` | Benutzerdefinierter Supabase-Anon-Schlüssel (für Self-Hosting) | ### []()🗑️ **Delete** [Section titled “ 🗑️ Delete”](#-️-delete) **Alias:** `d` ```bash npx @capgo/cli@latest organisation delete ``` 🗑️ Löschen Sie eine Organisation aus Capgo Cloud. Diese Aktion kann nicht rückgängig gemacht werden. Nur Organisationsinhaber können Organisationen löschen. **Beispiel:** ```bash npx @capgo/cli@latest organisation delete ORG_ID ``` **Optionen:** | Parameter | Typ | Beschreibung | | -------------- | -------- | ------------------------------------------------------------------------------ | | **-a,** | `string` | API-Schlüssel, um mit Ihrem Konto zu verknüpfen | | **—supa-host** | `string` | Benutzerdefinierte Supabase-Host-URL (für Self-Hosting oder Capgo-Entwicklung) | | **—supa-anon** | `string` | Benutzerdefinierter Supabase-Anon-Schlüssel (für Self-Hosting) | # Plugins hinzufügen oder aktualisieren > Vollständige Anleitung für Mitwirkende und Agenten zum Hinzufügen neuer Plugins oder Aktualisieren bestehender Plugins in der Capgo-Dokumentation. Dieser Leitfaden erklärt, wie Sie neue Capacitor-Plugins zur Capgo-Website hinzufügen oder bestehende Plugin-Dokumentation aktualisieren. Dies ist nützlich für Mitwirkende, Wartungspersonen und KI-Agenten, die bei der Pflege der Dokumentation helfen. ## Übersicht [Section titled “Übersicht”](#übersicht) Beim Hinzufügen eines neuen Plugins zum Capgo-Ökosystem müssen Sie mehrere Dateien und Stellen auf der Website aktualisieren, um sicherzustellen, dass das Plugin an allen relevanten Stellen korrekt angezeigt wird: 1. **Plugin-Listen-Konfiguration** - Plugin-Metadaten zur Masterliste hinzufügen 2. **Plugin-Indexseite** - Plugin zur kategorisierten Plugin-Listenseite hinzufügen 3. **Seitenleisten-Navigation** - Plugin zur Dokumentations-Seitenleiste hinzufügen 4. **Plugin-Dokumentation** - Übersichts- und Erste-Schritte-Seiten erstellen 5. **Plugin-Tutorial** - Ein umfassendes Tutorial erstellen ## Dateispeicherorte [Section titled “Dateispeicherorte”](#dateispeicherorte) ### Wichtige zu aktualisierende Dateien [Section titled “Wichtige zu aktualisierende Dateien”](#wichtige-zu-aktualisierende-dateien) | Datei | Zweck | | ----------------------------------------------- | -------------------------------------- | | `/src/config/plugins.ts` | Master-Plugin-Liste mit Metadaten | | `/src/content/docs/docs/plugins/index.mdx` | Plugin-Indexseite mit Kategorien | | `/astro.config.mjs` | Seitenleisten-Navigationskonfiguration | | `/src/content/docs/docs/plugins/[plugin-name]/` | Plugin-Dokumentationsverzeichnis | | `/src/content/plugins-tutorials/en/` | Englische Tutorial-Dateien | ## Schritt-für-Schritt-Anleitung [Section titled “Schritt-für-Schritt-Anleitung”](#schritt-für-schritt-anleitung) 1. ### Plugin zur Masterliste hinzufügen [Section titled “Plugin zur Masterliste hinzufügen”](#plugin-zur-masterliste-hinzufügen) Öffnen Sie `/src/config/plugins.ts` und fügen Sie Ihr Plugin zum `actions`-Array hinzu: ```typescript // Zuerst ein passendes Heroicon importieren import YourIconName from 'astro-heroicons/mini/IconName.astro' // Dann zum actions-Array hinzufügen { name: '@capgo/your-plugin-name', author: 'github.com/Cap-go', description: 'Kurze Beschreibung, was das Plugin tut', href: 'https://github.com/Cap-go/your-plugin-name/', title: 'Anzeigename', icon: YourIconName, } ``` **Verfügbare Icons**: Prüfen Sie `/node_modules/astro-heroicons/mini/` für verfügbare Icons. 2. ### Plugin zur Indexseite hinzufügen [Section titled “Plugin zur Indexseite hinzufügen”](#plugin-zur-indexseite-hinzufügen) Öffnen Sie `/src/content/docs/docs/plugins/index.mdx` und fügen Sie Ihr Plugin unter der entsprechenden Kategorie hinzu: ```mdx ``` **Kategorien**: * ⭐ Featured Plugins * 📱 Device & System Plugins * 🎥 Media & Camera Plugins * 🛠️ Utility Plugins * 🤖 AI & Advanced Media * 📍 Location & Background Services * 📞 Communication & Analytics * 🔐 Security & System * 📊 Android-Specific Features * 📥 Download & Navigation 3. ### Zur Seitenleisten-Navigation hinzufügen [Section titled “Zur Seitenleisten-Navigation hinzufügen”](#zur-seitenleisten-navigation-hinzufügen) Öffnen Sie `/astro.config.mjs` und fügen Sie Ihr Plugin zur Seitenleisten-Konfiguration hinzu (um Zeile 540): ```javascript { label: 'Ihr Plugin-Name', items: [ { label: 'Übersicht', link: '/docs/plugins/your-plugin-name/' }, { label: 'Erste Schritte', link: '/docs/plugins/your-plugin-name/getting-started' }, ], collapsed: true, } ``` Plugins sind alphabetisch in der Seitenleiste aufgelistet. 4. ### Plugin-Dokumentationsverzeichnis erstellen [Section titled “Plugin-Dokumentationsverzeichnis erstellen”](#plugin-dokumentationsverzeichnis-erstellen) Erstellen Sie ein neues Verzeichnis für Ihre Plugin-Dokumentation: ```bash mkdir -p /src/content/docs/docs/plugins/your-plugin-name/ ``` 5. ### Plugin-Übersichtsseite erstellen [Section titled “Plugin-Übersichtsseite erstellen”](#plugin-übersichtsseite-erstellen) Erstellen Sie `/src/content/docs/docs/plugins/your-plugin-name/index.mdx`: ```mdx --- title: "@capgo/your-plugin-name" description: Kurze Beschreibung des Plugin-Zwecks tableOfContents: false next: false prev: false sidebar: order: 1 label: "Einführung" hero: tagline: Detaillierte Tagline, die erklärt, was das Plugin macht image: file: ~public/your-plugin-icon.svg actions: - text: Erste Schritte link: /docs/plugins/your-plugin-name/getting-started/ icon: right-arrow variant: primary - text: Github link: https://github.com/Cap-go/your-plugin-name/ icon: external variant: minimal --- import { Card, CardGrid } from '@astrojs/starlight/components'; Beschreibung des ersten Hauptfeatures Beschreibung des zweiten Hauptfeatures Funktioniert sowohl auf iOS als auch auf Android 📱 Schauen Sie sich die [Dokumentation](/docs/plugins/your-plugin-name/getting-started/) an, um das Plugin zu meistern. ``` 6. ### Erste-Schritte-Anleitung erstellen [Section titled “Erste-Schritte-Anleitung erstellen”](#erste-schritte-anleitung-erstellen) Erstellen Sie `/src/content/docs/docs/plugins/your-plugin-name/getting-started.mdx`: ```mdx --- title: Erste Schritte description: Erfahren Sie, wie Sie das Plugin in Ihrer Capacitor-App installieren und verwenden. sidebar: order: 2 --- import { Steps } from '@astrojs/starlight/components'; import { PackageManagers } from 'starlight-package-managers' 1. **Paket installieren** 2. **Mit nativen Projekten synchronisieren** ## Konfiguration ### iOS-Konfiguration [iOS-spezifische Einrichtungsanweisungen] ### Android-Konfiguration [Android-spezifische Einrichtungsanweisungen] ## Verwendung [Grundlegende Verwendungsbeispiele] ## API-Referenz [Detaillierte API-Dokumentation] ## Vollständiges Beispiel [Vollständiges funktionierendes Beispiel] ## Best Practices [Empfohlene Praktiken und Tipps] ## Plattformhinweise [Plattformspezifische Hinweise und Einschränkungen] ``` 7. ### Tutorial-Datei erstellen [Section titled “Tutorial-Datei erstellen”](#tutorial-datei-erstellen) Erstellen Sie `/src/content/plugins-tutorials/en/your-plugin-name.md`: ```markdown --- locale: en --- # Verwendung des @capgo/your-plugin-name Pakets Das `@capgo/your-plugin-name` Paket [kurze Beschreibung]. In diesem Tutorial führen wir Sie durch Installation, Konfiguration und Verwendung dieses Pakets in Ihrer Ionic Capacitor App. ## Installation [Installationsschritte] ## Konfiguration [Konfigurationsschritte für iOS und Android] ## API-Verwendung [Detaillierte API-Verwendungsbeispiele] ## Vollständiges Beispiel [Vollständiges funktionierendes Beispiel] ## Best Practices [Tipps und Best Practices] ## Fehlerbehebung [Häufige Probleme und Lösungen] ## Fazit [Zusammenfassung und Links zu zusätzlichen Ressourcen] ``` ## Plugin-Dokumentationsstruktur [Section titled “Plugin-Dokumentationsstruktur”](#plugin-dokumentationsstruktur) ### Erforderliche Dateien [Section titled “Erforderliche Dateien”](#erforderliche-dateien) ```plaintext src/content/docs/docs/plugins/your-plugin-name/ ├── index.mdx # Übersichtsseite mit Hero und Feature-Karten └── getting-started.mdx # Installations- und Verwendungsanleitung src/content/plugins-tutorials/en/ └── your-plugin-name.md # Umfassendes Tutorial ``` ### Optionale Dateien [Section titled “Optionale Dateien”](#optionale-dateien) Für komplexe Plugins können Sie zusätzliche Dokumentationsseiten hinzufügen: ```plaintext src/content/docs/docs/plugins/your-plugin-name/ ├── index.mdx ├── getting-started.mdx ├── api-reference.mdx # Detaillierte API-Dokumentation ├── examples.mdx # Zusätzliche Beispiele ├── troubleshooting.mdx # Fehlerbehebungsanleitung └── migrations.mdx # Migrationsanleitungen ``` ## Inhaltsrichtlinien [Section titled “Inhaltsrichtlinien”](#inhaltsrichtlinien) ### Plugin-Beschreibungen schreiben [Section titled “Plugin-Beschreibungen schreiben”](#plugin-beschreibungen-schreiben) * **Seien Sie prägnant**: Halten Sie Beschreibungen unter 100 Zeichen * **Seien Sie spezifisch**: Erklären Sie, was das Plugin tut, nicht was es ist * **Verwenden Sie Aktionswörter**: Beginnen Sie mit Verben wie “Steuern”, “Integrieren”, “Aktivieren” **Gute Beispiele**: * “Steuern Sie Geräte-Taschenlampe und Fackel mit einfachem Ein/Aus-Schalter” * “Integrieren Sie Crisp Live-Chat und Kundensupport in Ihre App” * “Aktivieren Sie sichere Authentifizierung mit Face ID und Touch ID” **Schlechte Beispiele**: * “Ein Plugin für Blitz” * “Dies ist ein Crisp-Plugin” * “Biometrisches Plugin” ### Dokumentation schreiben [Section titled “Dokumentation schreiben”](#dokumentation-schreiben) 1. **Beginnen Sie mit Installation**: Beginnen Sie immer mit klaren Installationsschritten 2. **Konfiguration bereitstellen**: Plattformspezifische Einrichtungsanforderungen einschließen 3. **Verwendungsbeispiele zeigen**: Funktionierende Code-Beispiele bereitstellen 4. **API-Referenz einschließen**: Alle Methoden und Parameter dokumentieren 5. **Vollständige Beispiele hinzufügen**: Reale Verwendungsmuster zeigen 6. **Bewährte Methoden auflisten**: Tipps für optimale Nutzung teilen 7. **Plattformunterschiede dokumentieren**: iOS vs. Android-Verhalten klären 8. **Fehlerbehebung hinzufügen**: Häufige Probleme ansprechen ### Code-Beispiele [Section titled “Code-Beispiele”](#code-beispiele) * TypeScript für alle Code-Beispiele verwenden * Imports oben einschließen * Kommentare hinzufügen, die Schlüsselschritte erklären * Fehlerbehandlung zeigen * Sowohl grundlegende als auch fortgeschrittene Verwendung demonstrieren ## Checkliste [Section titled “Checkliste”](#checkliste) Verwenden Sie diese Checkliste beim Hinzufügen eines neuen Plugins: * [ ] Plugin zu `/src/config/plugins.ts` hinzugefügt * [ ] Passendes Icon aus Heroicons ausgewählt * [ ] Plugin zu `/src/content/docs/docs/plugins/index.mdx` unter korrekter Kategorie hinzugefügt * [ ] Seitenleisteneintrag in `/astro.config.mjs` hinzugefügt * [ ] Plugin-Dokumentationsverzeichnis erstellt * [ ] `index.mdx` Übersichtsseite erstellt * [ ] `getting-started.mdx` Anleitung erstellt * [ ] Tutorial in `/src/content/plugins-tutorials/en/` erstellt * [ ] Installationsanweisungen eingeschlossen * [ ] iOS-Konfiguration dokumentiert * [ ] Android-Konfiguration dokumentiert * [ ] Verwendungsbeispiele bereitgestellt * [ ] API-Referenz hinzugefügt * [ ] Vollständiges funktionierendes Beispiel eingeschlossen * [ ] Bewährte Methoden aufgelistet * [ ] Plattformspezifische Hinweise hinzugefügt * [ ] Getestet, dass alle Links korrekt funktionieren ## Icon-Referenz [Section titled “Icon-Referenz”](#icon-referenz) Häufig verwendete Icons für Plugins (aus `astro-heroicons/mini/`): | Icon | Anwendungsfall | | ------------------------ | ------------------------------------------ | | `BoltIcon` | Blitz, Energie, Strom | | `CameraIcon` | Kamera, Foto, Video | | `ChatBubbleLeftIcon` | Chat, Messaging, Kommunikation | | `FingerPrintIcon` | Biometrisch, Sicherheit, Authentifizierung | | `MapPinIcon` | Standort, Geolokation, Karten | | `SpeakerWaveIcon` | Audio, Ton, Musik | | `VideoCameraIcon` | Video, Aufnahme, Streaming | | `CreditCardIcon` | Zahlungen, Käufe | | `PlayCircleIcon` | Mediaplayer, Videoplayer | | `SignalIcon` | Konnektivität, Netzwerk, Beacon | | `RadioIcon` | Beacon, Broadcast, Wireless | | `ChatBubbleOvalLeftIcon` | Soziale Medien, WeChat | ## Bestehende Plugins aktualisieren [Section titled “Bestehende Plugins aktualisieren”](#bestehende-plugins-aktualisieren) Beim Aktualisieren eines bestehenden Plugins: 1. **Versionsnummern aktualisieren** in der Dokumentation 2. **Migrationsanleitungen hinzufügen**, falls Breaking Changes existieren 3. **API-Referenz aktualisieren** mit neuen Methoden 4. **Neue Beispiele hinzufügen** für neue Features 5. **Plattformanforderungen aktualisieren**, falls geändert 6. **Bewährte Methoden überarbeiten** basierend auf neuen Features 7. **Tutorial aktuell halten** mit neuester API ## Mehrsprachige Unterstützung [Section titled “Mehrsprachige Unterstützung”](#mehrsprachige-unterstützung) Die Website unterstützt mehrere Sprachen. Nach Erstellung der englischen Dokumentation: 1. Übersetzungsskript ausführen: ```bash bun run plugins:translate_all ``` 2. Generierte Übersetzungen überprüfen in: * `/src/content/plugins-tutorials/de/` (Deutsch) * `/src/content/plugins-tutorials/es/` (Spanisch) * `/src/content/plugins-tutorials/fr/` (Französisch) * `/src/content/plugins-tutorials/it/` (Italienisch) * `/src/content/plugins-tutorials/ja/` (Japanisch) * `/src/content/plugins-tutorials/ko/` (Koreanisch) * `/src/content/plugins-tutorials/id/` (Indonesisch) ## Ihre Änderungen testen [Section titled “Ihre Änderungen testen”](#ihre-änderungen-testen) Nach dem Hinzufügen oder Aktualisieren von Plugin-Dokumentation: 1. **Website lokal erstellen**: ```bash npm run build ``` 2. **Auf Fehler prüfen**: * Überprüfen, ob alle Links funktionieren * Sicherstellen, dass Bilder korrekt geladen werden * Code-Beispiele auf Gültigkeit überprüfen * Navigation testen 3. **Website-Vorschau**: ```bash npm run dev ``` 4. **Ihr Plugin erscheint überprüfen**: * Plugin-Listenseite prüfen * Seitenleisten-Navigation überprüfen * Alle Dokumentationsseiten testen * Tutorial-Seite bestätigen ## Häufige Fehler [Section titled “Häufige Fehler”](#häufige-fehler) Caution **Vermeiden Sie diese häufigen Fehler:** 1. **Sync vergessen**: Führen Sie in Beispielen immer `npx cap sync` aus 2. **Inkonsistente Benennung**: Verwenden Sie überall denselben Plugin-Namen 3. **Fehlende Plattform-Konfiguration**: Sowohl iOS- als auch Android-Setup dokumentieren 4. **Fehlerhafte Links**: Relative Links verwenden und Funktionalität überprüfen 5. **Keine Fehlerbehandlung**: Immer try-catch in Beispielen zeigen 6. **Fehlende Imports**: Alle notwendigen Imports in Beispiele einschließen 7. **Unklare Beschreibungen**: Spezifisch sein, was das Plugin tut ## Hilfe erhalten [Section titled “Hilfe erhalten”](#hilfe-erhalten) Wenn Sie Hilfe beim Hinzufügen oder Aktualisieren von Plugin-Dokumentation benötigen: * **Discord**: Treten Sie unserer [Discord-Community](https://discord.capgo.app) bei * **GitHub**: Öffnen Sie ein Issue im [Website-Repository](https://github.com/Cap-go/website) * **E-Mail**: Kontaktieren Sie das Team unter ## Beispiele [Section titled “Beispiele”](#beispiele) Zur Referenz schauen Sie sich diese gut dokumentierten Plugins an: * **Updater**: `/src/content/docs/docs/plugins/updater/` (komplexes Plugin mit mehreren Seiten) * **Flash**: `/src/content/docs/docs/plugins/flash/` (einfaches Plugin, gutes Starter-Beispiel) * **Social Login**: `/src/content/docs/docs/plugins/social-login/` (Plugin mit Unterseiten) ## Zusammenfassung [Section titled “Zusammenfassung”](#zusammenfassung) Das Hinzufügen eines Plugins zur Capgo-Dokumentation umfasst: 1. Metadaten zur Master-Konfiguration hinzufügen 2. Plugin zur kategorisierten Indexseite hinzufügen 3. Seitenleisten-Navigation konfigurieren 4. Umfassende Dokumentationsseiten erstellen 5. Detailliertes Tutorial schreiben 6. Alle Änderungen lokal testen Durch Befolgung dieses Leitfadens stellen Sie sicher, dass Plugins konsistent dokumentiert und von Benutzern leicht auffindbar sind. # Häufig gestellte Fragen > Häufig gestellte Fragen zu Capgo Wenn Sie hier nicht beantwortete Fragen haben, fragen Sie bitte! Sowohl das Erstellen eines Issues als auch das Fragen auf [Discord](https://discordcom/invite/VnYRvBfgA6) funktionieren. ### Was ist “Code Push”? [Section titled “Was ist “Code Push”?”](#was-ist-code-push) Code Push, auch bekannt als “Over the Air Updates” (OTA), ist ein Cloud-Service, der es Capacitor-Entwicklern ermöglicht, Updates für ihre Apps in der Produktion bereitzustellen. Capgo funktioniert derzeit auf Android und iOS und wird letztendlich überall dort funktionieren, wo Capacitor funktioniert. “Code Push” ist eine Referenz auf den Namen einer Deploy-Funktion, die von der React Native Community von [Microsoft](https://appcenterms/) und [Expo](https://expodev/) verwendet wird, die beide Capacitor nicht unterstützen. ### Was ist der Unterschied zwischen einem Bundle und einem Release? [Section titled “Was ist der Unterschied zwischen einem Bundle und einem Release?”](#was-ist-der-unterschied-zwischen-einem-bundle-und-einem-release) Wir verwenden den Begriff “Release” für die Vorbereitung einer Binärdatei für die App Stores. Um später ein Bundle generieren zu können, muss Capgo die exakte Binärdatei kennen, die an die App Stores gesendet wurde. Wir verwenden den Begriff “Bundle” für ein Patch, das auf ein Release angewendet werden kann, um es mit neuem Code zu aktualisieren. Der Befehl `npx @capgo/cli app update` wird verwendet, um aus Ihrem neuen lokalen Code ein Bundle zu generieren, das dann an Ihre Nutzer ausgeliefert wird. ### Wie sieht die Roadmap aus? [Section titled “Wie sieht die Roadmap aus?”](#wie-sieht-die-roadmap-aus) Unsere Projekt-Boards sind öffentlich und zu finden unter: [https://github.com/orgs/Cap-go/projects](https://github.com/orgs/Cap-go/projects/) Unser Team arbeitet auch öffentlich, sodass Sie jederzeit sehen können, woran wir arbeiten. Wir beantworten gerne Ihre Fragen zu unserer Roadmap oder Prioritäten über Github Issues oder [Discord](https://discordcom/invite/VnYRvBfgA6). ### Kann ich Capgo mit meinem Team nutzen? [Section titled “Kann ich Capgo mit meinem Team nutzen?”](#kann-ich-capgo-mit-meinem-team-nutzen) Ja! Alle Pläne unterstützen unbegrenzte Entwickler. Wir beschränken nur App-Metriken (MAU, Speicher und Bandbreite) für jede Organisation. Weitere Informationen finden Sie unter [Teams](https://capgo.app/pricing/). ### Speichert Capgo meinen Quellcode? [Section titled “Speichert Capgo meinen Quellcode?”](#speichert-capgo-meinen-quellcode) Nein. Capgo-Server sehen niemals Ihren Quellcode. Wenn Sie `npx @capgo/cli app update` ausführen, lädt das `npx @capgo/cli`-Tool nur den kompilierten Code hoch, den Sie auch an die App Stores senden. Wenn Sie zusätzliche Sicherheit wünschen, können Sie Ende-zu-Ende-Verschlüsselung verwenden, um Ihr Bundle vor dem Upload auf Capgo-Server zu verschlüsseln. Siehe auch unsere Datenschutzrichtlinie: [https://capgo.app/privacy](https://capgo.app/privacy/) ### Kann ich Capgo von meinem CI-System aus nutzen? [Section titled “Kann ich Capgo von meinem CI-System aus nutzen?”](#kann-ich-capgo-von-meinem-ci-system-aus-nutzen) Ja. Capgo ist für die Verwendung in CI-Systemen gedacht. Wir haben eine Anleitung für [Android und Github Actions](https://capgo.app/blog/automatic-capacitor-android-build-github-action/) und [IOS](https://capgo.app/blog/automatic-capacitor-ios-build-github-action/) sowie für [Gitlab](https://capgo.app/blog/setup-ci-and-cd-gitlab/) veröffentlicht, andere CI-Systeme sollten ähnlich sein. Zögern Sie nicht, sich bei Problemen über GitHub Issues oder Discord zu melden. ### Wie verhält sich das zu Firebase Remote Config oder Launch Darkly? [Section titled “Wie verhält sich das zu Firebase Remote Config oder Launch Darkly?”](#wie-verhält-sich-das-zu-firebase-remote-config-oder-launch-darkly) Code Push ermöglicht das Hinzufügen neuen Codes/Ersetzen von Code auf dem Gerät. Firebase Remote Config und Launch Darkly sind beides Konfigurationssysteme. Sie ermöglichen es Ihnen, die Konfiguration Ihrer App zu ändern, ohne eine neue Version ausliefern zu müssen. Sie sind nicht dafür gedacht, Code zu ersetzen. ### Wie groß ist der Dependency-Footprint? [Section titled “Wie groß ist der Dependency-Footprint?”](#wie-groß-ist-der-dependency-footprint) Ich habe es kürzlich nicht gemessen, aber ich erwarte, dass die Code-Push-Bibliothek weniger als ein Megabyte zu Capacitor-Apps hinzufügt. Wir kennen Möglichkeiten, dies kleiner zu machen, wenn es Priorität wird. Wenn die Größe ein Blocker für Sie ist, lassen Sie es uns wissen! ### Funktioniert Code Push mit großen Anwendungen? [Section titled “Funktioniert Code Push mit großen Anwendungen?”](#funktioniert-code-push-mit-großen-anwendungen) Ja. Es gibt keine Begrenzung für die Größe der Anwendung, die mit Code Push aktualisiert werden kann. Wie [unten](https://capgo.app/docs/faq/#what-types-of-changes-does-capgo-code-push-support) erwähnt, kann Capgo jeden JS-Code in Ihrer Anwendung ändern, unabhängig von der Größe. Zu beachten: Eine größere Größe erschwert es Benutzern, Updates herunterzuladen. Wir empfehlen, Ihre App so klein wie möglich zu halten. ### Wofür kann ich Capgo Code Push verwenden? [Section titled “Wofür kann ich Capgo Code Push verwenden?”](#wofür-kann-ich-capgo-code-push-verwenden) Wir haben verschiedene Anwendungsfälle gesehen, darunter: * Notfall-Fixes für Produktions-Apps * Auslieferung von Fehlerbehebungen an Benutzer älterer Versionen Ihrer App * Kontinuierliche Auslieferung (z.B. stündlich) Beachten Sie, dass die meisten App Stores das Ausliefern von Code verbieten, der das Verhalten der App wesentlich ändert. Weitere Informationen finden Sie [unten](https://capgo.app/docs/faq/#how-does-this-relate-to-the-appplay-store-review-process-or-policies). ### Was zählt als “MAU” für Capgo? [Section titled “Was zählt als “MAU” für Capgo?”](#was-zählt-als-mau-für-capgo) Ein MAU ist ein “Monthly Active User”. Wir zählen einen MAU als jedes Gerät, das in den letzten 30 Tagen unsere Server kontaktiert hat. Wir zählen keine Geräte, die unsere Server in den letzten 30 Tagen nicht kontaktiert haben. **Wichtig**: Ab Plugin-Version **v6.25.0** und **v7.25.0** bleibt die Geräte-ID über App-Neuinstallationen hinweg erhalten. Vor diesen Versionen erzeugte jede App-Neuinstallation eine neue Geräte-ID und zählte als neuer MAU. Mit den aktuellen Versionen: * Geräte-ID bleibt über App-Neuinstallationen hinweg erhalten (sicher gespeichert im Keychain auf iOS und EncryptedSharedPreferences auf Android) * Das Aktualisieren der App erstellt keine neue Geräte-ID * Während der Entwicklung, wenn Sie eine ältere Plugin-Version (< v6.25.0 / v7.25.0) verwenden, erstellt jede Neuinstallation immer noch einen neuen MAU Hinweis: TestFlight-Downloads und Kanalwechsel in Android können je nach Konfiguration immer noch neue Geräteregistrierungen erzeugen. > Wir empfehlen nach dem ersten Setup, Entwicklungsgeräte und Emulatoren zu deaktivieren, um die Anzahl doppelter Geräte zu reduzieren. ### Wofür können wir Capgo Code Push nicht verwenden? [Section titled “Wofür können wir Capgo Code Push nicht verwenden?”](#wofür-können-wir-capgo-code-push-nicht-verwenden) Wie oben erwähnt, sollte Capgo nicht verwendet werden, um gegen App Store-Richtlinien zu verstoßen. Weitere Informationen finden Sie [unten](https://capgo.app/docs/faq/#does-capgo-comply-with-play-store-guidelines). Außerdem unterstützt Capgo keine Änderungen am nativen Code (z.B. Java/Kotlin auf Android oder Objective-C/Swift auf iOS). Das Tool warnt Sie während eines Aktualisierungsversuchs, wenn Sie nativen Code geändert haben. ### Kann ich Änderungen an capacitor.config.ts über Capgo aktualisieren?[](https://capgo.app/docs/faq/#can-i-update-capacitorconfigts-changes-via-capgo "Direct link to Can I update capacitor.config.ts changes via Capgo?") [Section titled “Kann ich Änderungen an capacitor.config.ts über Capgo aktualisieren?”](#kann-ich-änderungen-an-capacitorconfigts-über-capgo-aktualisieren) Nein. Änderungen an `capacitor.config.ts` können nicht über Capgo Live-Updates gesendet werden. Die Capacitor-Konfigurationsdatei wird zur nativen Build-Zeit gelesen und in die native App-Binary kompiliert. Das bedeutet, dass Änderungen an `capacitor.config.ts` (wie Plugin-Konfigurationen, App-ID, Server-Einstellungen oder native Plugin-Optionen) eine neue native Version über den App Store oder Google Play erfordern. Capgo kann nur Web-Assets (HTML, CSS, JavaScript) aktualisieren, die zur Laufzeit geladen werden. Wenn Sie Ihre Capacitor-Konfiguration ändern müssen: 1. Aktualisieren Sie `capacitor.config.ts` lokal 2. Bauen Sie Ihre native App neu (`npx cap sync` gefolgt von einem nativen Build) 3. Reichen Sie die neue Binary bei den App Stores ein ### Reicht Capgo die Apps für mich bei den Stores ein? [Section titled “Reicht Capgo die Apps für mich bei den Stores ein?”](#reicht-capgo-die-apps-für-mich-bei-den-stores-ein) Capgo unterstützt derzeit nicht das Einreichen bei den App Stores in Ihrem Namen. Wir planen, dies in Zukunft hinzuzufügen, aber vorerst müssen Sie weiterhin Ihre bestehenden Prozesse für die Einreichung bei den App Stores verwenden. Sie können unseren [CI-Leitfaden für Android](https://capgo.app/blog/automatic-capacitor-android-build-github-action/) und [CI-Leitfaden für iOS](https://capgo.app/blog/automatic-capacitor-ios-build-github-action/) verwenden, um diesen Prozess zu automatisieren. ### Was speichert Capgo auf der Festplatte und wo? [Section titled “Was speichert Capgo auf der Festplatte und wo?”](#was-speichert-capgo-auf-der-festplatte-und-wo) Der Capgo-Updater (der in Ihrer Anwendung enthalten ist, wenn Sie Ihre App erstellen) speichert das zuletzt heruntergeladene Bundle im einzigen Verzeichnis, das Capacitor zum Laden von Code erlaubt. Auf Android befindet sich dies in `/data/user/0/comexampleapp/code_cache/capgo_updater`, wobei der Basispfad vom Android-System bereitgestellt wird und sich zur Laufzeit dynamisch ändern kann. Auf iOS-Geräten werden Daten unter `Library/Application Support/capgo` gespeichert. Die Capgo-Kommandozeilentools (z.B.`npx @capgo/cli app update`) werden auf der Festplatte in npm-Caches installiert, Ihre Anmeldedaten werden in Ihrem Home-Verzeichnis in `~/capgo` gespeichert ### Wie verhält sich das zum Capacitor Hot Reload? [Section titled “Wie verhält sich das zum Capacitor Hot Reload?”](#wie-verhält-sich-das-zum-capacitor-hot-reload) Capacitor Hot Reload ist ein Feature ausschließlich für die Entwicklungszeit. Code Push ist für die Produktion. Hot Reload ist eine Funktion von Capacitor, die es ermöglicht, Code während der Entwicklung auf dem Gerät zu ändern. Dies erfordert das Erstellen der Capacitor-App mit einem Proxy zur Verbindung mit Ihrem lokalen Rechner. Code Push ist eine Funktion, die es ermöglicht, Code auf dem Gerät in der Produktion zu ändern. Wir verwenden verschiedene Techniken, um dies je nach Plattform zu ermöglichen. ### Welche Arten von Änderungen unterstützt Capgo Code Push? [Section titled “Welche Arten von Änderungen unterstützt Capgo Code Push?”](#welche-arten-von-änderungen-unterstützt-capgo-code-push) Capgo kann jeden JS-Code in Ihrer Anwendung ändern. Dies umfasst App-Code und generierten Code. Sie können auch Abhängigkeiten in `package.json` aktualisieren, solange diese keine nativen Code-Änderungen erfordern. Wir haben nicht vor, Änderungen am nativen Code zu unterstützen (z.B. Java/Kotlin auf Android oder Objective-C/Swift auf iOS), und das Tool wird Sie warnen, wenn es erkennt, dass Sie nativen Code geändert haben, da dieser nicht im Bundle enthalten sein wird. ### Unterstützt dies Web? [Section titled “Unterstützt dies Web?”](#unterstützt-dies-web) Code Push wird für Web nicht benötigt, da Web bereits so funktioniert. Wenn ein Benutzer eine Web-App öffnet, lädt sie bei Bedarf die neueste Version vom Server herunter. Wenn Sie einen Anwendungsfall für Code Push mit Web haben, würden wir das gerne wissen! ### Wird dies auf iOS, Android, Mac, Windows, Linux usw. funktionieren? [Section titled “Wird dies auf iOS, Android, Mac, Windows, Linux usw. funktionieren?”](#wird-dies-auf-ios-android-mac-windows-linux-usw-funktionieren) Ja. Bisher haben wir uns auf die Android- und iOS-Unterstützung konzentriert, aber Code Push wird letztendlich überall funktionieren, wo Capacitor funktioniert. Wir stellen sicher, dass wir zuerst die gesamte Infrastruktur aufbauen, die benötigt wird, um Code Push zuverlässig und sicher bereitzustellen, bevor wir auf weitere Plattformen expandieren. ### Welche Betriebssystemversionen unterstützt Capgo? [Section titled “Welche Betriebssystemversionen unterstützt Capgo?”](#welche-betriebssystemversionen-unterstützt-capgo) Capgo unterstützt die gleichen Android-Versionen, die auch Capacitor unterstützt. Capacitor unterstützt derzeit Android API Level 22+ und iOS 13.0+: [https://capacitorjs.com/docs/main/reference/support-policy](https://capacitorjs.com/docs/main/reference/support-policy/) ### Welche Versionen von Capacitor unterstützt Capgo? [Section titled “Welche Versionen von Capacitor unterstützt Capgo?”](#welche-versionen-von-capacitor-unterstützt-capgo) Capgo unterstützt derzeit nur aktuelle stabile Versionen von Capacitor. Wir könnten auch ältere Versionen von Capacitor unterstützen, haben aber noch nicht die nötige Infrastruktur aufgebaut, um diese über die Zeit zu pflegen. Wir beabsichtigen, in Zukunft mehr Versionen von Capacitor zu unterstützen, einschließlich jeder Version für unsere Enterprise-Kunden [https://github.com/Cap-go/capgo/issues/1100](https://github.com/Cap-go/capgo/issues/1100/) Capgo verfolgt die stabilen Versionen und aktualisiert in der Regel innerhalb weniger Stunden nach einer stabilen Veröffentlichung. Unser System für diese Aktualisierungen ist automatisiert und benötigt nur wenige Minuten. Danach führen wir einen zusätzlichen manuellen Verifizierungsschritt durch, bevor wir auf unsere Server veröffentlichen. ### Wie verhält sich dies zum App/Play Store Überprüfungsprozess oder deren Richtlinien? [Section titled “Wie verhält sich dies zum App/Play Store Überprüfungsprozess oder deren Richtlinien?”](#wie-verhält-sich-dies-zum-appplay-store-überprüfungsprozess-oder-deren-richtlinien) Entwickler sind an ihre Vereinbarungen mit Store-Anbietern gebunden, wenn sie diese Stores nutzen. Code Push ist so konzipiert, dass Entwickler ihre Apps aktualisieren und dabei die Store-Richtlinien auf iOS und Android einhalten können. Ähnlich wie bei den verschiedenen kommerziellen Produkten, die für React Native verfügbar sind (z.B. [Microsoft](https://appcenter.ms/), [Expo](https://expo.dev/)) Microsoft veröffentlicht auch einen Leitfaden darüber, wie ihre Lösung die App Store-Richtlinien erfüllt: [https://github.com/microsoft/react-native-code-push#store-guideline-compliance](https://github.com/microsoft/react-native-code-push/#store-guideline-compliance) Code Push ist eine weit verbreitete Technik in den App Stores. Alle großen Apps, die ich kenne, verwenden Code Push. Die wichtigste Richtlinie ist, das Verhalten der App nicht wesentlich zu ändern. Weitere Informationen finden Sie [unten](https://capgo.app/docs/faq/#does-capgo-comply-with-play-store-guidelines). ### Erfüllt Capgo die Play Store-Richtlinien? [Section titled “Erfüllt Capgo die Play Store-Richtlinien?”](#erfüllt-capgo-die-play-store-richtlinien) Ja. Der Play Store bietet zwei Einschränkungen in Bezug auf Update-Tools: 1. Updates müssen einen Interpreter oder eine virtuelle Maschine verwenden (Capgo verwendet die Dart Virtual Machine) [https://support.google.com/googleplay/android-developer/answer/9888379?hl=en](https://support.google.com/googleplay/android-developer/answer/9888379/?hl=en) ```plaintext Eine über Google Play verteilte App darf sich nicht selbst modifizieren, ersetzen oder aktualisieren mit anderen Methoden als dem Update-Mechanismus von Google Play. Ebenso darf eine App keinen ausführbaren Code (wie dex-, JAR-, .so-Dateien) von einer anderen Quelle als Google Play herunterladen. *Diese Einschränkung gilt nicht für Code, der in einer virtuellen Maschine oder einem Interpreter läuft*, wo entweder indirekten Zugriff auf Android-APIs bietet (wie JavaScript in einer WebView oder Browser) Apps oder Drittanbieter-Code, wie SDKs, mit interpretierten Sprachen (JavaScript, Python, Lua, etc.), die zur Laufzeit geladen werden (zum Beispiel nicht mit der App gebündelt), dürfen keine potenziellen Verstöße gegen Google Play-Richtlinien ermöglichen ``` 2. Änderungen an der App dürfen nicht täuschend sein (z.B. Ändern des Zwecks der App durch ein Update) [https://support.google.com/googleplay/android-developer/answer/9888077](https://support.google.com/googleplay/android-developer/answer/9888077/) Bitte seien Sie transparent gegenüber Ihren Benutzern bezüglich dessen, was Sie mit Ihrer Anwendung bereitstellen, und verletzen Sie nicht ihre Erwartungen durch wesentliche Verhaltensänderungen durch die Verwendung von Capgo. Capgo ist so konzipiert, dass es mit den Play Store-Richtlinien kompatibel ist. Allerdings ist Capgo ein Werkzeug und kann wie jedes Werkzeug missbraucht werden. Die absichtliche missbräuchliche Verwendung von Capgo zur Verletzung der Play Store-Richtlinien verstößt gegen die Capgo [Nutzungsbedingungen](https://capgo.app/tos/) und kann zur Kündigung Ihres Kontos führen. Schließlich werden Code Push-Dienste in der Branche weit verbreitet eingesetzt (alle großen Apps, die ich kenne, verwenden sie) und es gibt mehrere andere öffentlich verfügbare Code Push-Dienste (z.B. expo.dev & appcenter.ms). Dies ist ein gut ausgetretener Pfad. Microsoft veröffentlicht auch einen Leitfaden darüber, wie ihre React Native “codepush”-Bibliothek die App Store-Richtlinien erfüllt: [https://github.com/microsoft/react-native-code-push#store-guideline-compliance](https://github.com/microsoft/react-native-code-push/#store-guideline-compliance) ### Erfüllt Capgo die App Store-Richtlinien? [Section titled “Erfüllt Capgo die App Store-Richtlinien?”](#erfüllt-capgo-die-app-store-richtlinien) Ja. Ähnlich wie der Play Store bietet der App Store sowohl technische als auch Richtlinien-Einschränkungen. ```plaintext 3.2.2 interpretierter Code kann in eine Anwendung heruntergeladen werden, jedoch nur solange dieser Code: (a) den primären Zweck der Anwendung nicht durch Bereitstellung von Funktionen oder Funktionalität ändert, die nicht mit dem beabsichtigten und beworbenen Zweck der Anwendung, wie sie im App Store eingereicht wurde, übereinstimmen, (b) keinen Store oder Storefront für anderen Code oder Anwendungen erstellt, und (c) keine Signierung, Sandbox oder andere Sicherheitsfunktionen des Betriebssystems umgeht ``` Capgo verwendet einen benutzerdefinierten Dart-Interpreter, um die Interpreter-Only-Beschränkung für Updates auf iOS einzuhalten. Solange Ihre Anwendung kein täuschendes Verhalten durch Updates zeigt (z.B. Änderung des App-Zwecks durch Update), ist das Aktualisieren über Capgo (oder eine andere Code-Push-Lösung) gängige Branchenpraxis und entspricht den App Store-Richtlinien. Der absichtliche Missbrauch von Capgo zur Verletzung der App Store-Richtlinien verstößt gegen die [Nutzungsbedingungen](https://capgo.app/tos/) von Capgo und kann zur Kündigung Ihres Kontos führen. Microsoft veröffentlicht auch einen Leitfaden, wie ihre React Native “CodePush”-Bibliothek die App Store-Richtlinien einhält: [https://github.com/microsoft/react-native-code-push#store-guideline-compliance](https://github.com/microsoft/react-native-code-push/#store-guideline-compliance) ### Kann ich Capgo in meinem Land nutzen?[](https://capgo.app/docs/faq/#can-i-use-capgo-in-my-country "Direct link to Can I use Capgo in my country?") [Section titled “Kann ich Capgo in meinem Land nutzen?”](#kann-ich-capgo-in-meinem-land-nutzen) Wir haben den Zugriff auf Capgo aus keinem Land eingeschränkt. Wir erkennen an, dass einige Länder Einschränkungen haben, welche URLs innerhalb des Landes aufgerufen werden können. Capgo verwendet derzeit Cloudflare Cloud für das Hosting, einschließlich R2 Storage und Cloudflare Workers. Folgende URLs werden von Capgo verwendet: * [https://apicapgo.app](https://apicapgo.app/) — verwendet von den `npx @capgo/cli` Command Line Tools für die Interaktion mit den Capgo-Servern sowie vom Capgo-Updater auf den Geräten der Benutzer zur Überprüfung von Updates * [https://\*r2cloudflarestoragecom](https://*r2cloudflarestoragecom/) — verwendet vom `npx @capgo/cli` Command Line Tool zum Hoch- und Herunterladen von Bundles Wenn all diese URLs aus Ihrem Land erreichbar sind, sollte Capgo funktionieren. Wenn Ihre Region den Zugriff auf eine dieser URLs blockieren muss, lassen Sie es uns wissen und wir können mit Ihnen an einer Lösung arbeiten. Proxy-Server sind eine Option. ### Kann ich Capgo selbst hosten?[](https://capgo.app/docs/faq/#can-i-self-host-capgo "Direct link to Can I self-host Capgo?") [Section titled “Kann ich Capgo selbst hosten?”](#kann-ich-capgo-selbst-hosten) Ja, Sie können Capgo selbst hosten. Die Anleitung ist noch nicht geschrieben, aber der Code ist Open Source und verfügbar unter [https://github.com/cap-go/capgo](https://github.com/cap-go/capgo/) ### Benötigt Code Push Internet zum Funktionieren?[](https://capgo.app/docs/faq/#does-code-push-require-the-internet-to-work "Direct link to Does code push require the internet to work?") [Section titled “Benötigt Code Push Internet zum Funktionieren?”](#benötigt-code-push-internet-zum-funktionieren) Ja. Man könnte sich vorstellen, einen Server zu betreiben, um die Updates getrennt vom allgemeinen Internet zu verteilen, aber irgendeine Form von Netzwerkverbindung ist erforderlich, um Updates auf die Geräte zu übertragen. ### Wie wird Capgo von fehlender Netzwerkverbindung beeinflusst?[](https://capgo.app/docs/faq/#how-is-capgo-affected-by-lack-of-network-connectivity "Direct link to How is Capgo affected by lack of network connectivity?") [Section titled “Wie wird Capgo von fehlender Netzwerkverbindung beeinflusst?”](#wie-wird-capgo-von-fehlender-netzwerkverbindung-beeinflusst) Der Capgo-Updater (der in Ihrer Anwendung enthalten ist, wenn Sie Ihre App mit Capgo erstellen) ist darauf ausgelegt, resistent gegen Netzwerkverbindungsprobleme zu sein. Im Standard-Update-Verhalten benachrichtigt die Anwendung beim Start den Capgo-Updater, der einen separaten Thread startet, um eine Netzwerkanfrage an die Capgo-Server zu stellen und nach einem Update zu fragen. Wir verwenden absichtlich einen separaten Thread, um andere Aktivitäten der Anwendung nicht zu blockieren. Wenn die Netzwerkanfrage fehlschlägt oder eine Zeitüberschreitung auftritt, wird der Updater beim nächsten Start der Anwendung erneut prüfen. Capgo Command Line Tools (z.B. `npx @capgo/cli app update`) benötigen eine Netzwerkverbindung zum Funktionieren. Wenn Sie Capgo zur Verteilung Ihrer App verwenden, sollten Sie sicherstellen, dass Ihr CI-System eine Netzwerkverbindung hat. ### Was passiert, wenn ein Benutzer lange Zeit nicht aktualisiert und ein Update verpasst?[](https://capgo.app/docs/faq/#what-happens-if-a-user-doesnt-update-for-a-long-time-and-misses-an-update "Direct link to What happens if a user doesn't update for a long time and misses an update?") [Section titled “Was passiert, wenn ein Benutzer lange Zeit nicht aktualisiert und ein Update verpasst?”](#was-passiert-wenn-ein-benutzer-lange-zeit-nicht-aktualisiert-und-ein-update-verpasst) Unsere Implementierung sendet immer ein speziell auf das anfragende Gerät zugeschnittenes Update, das den Anfrager immer auf die neueste verfügbare Version aktualisiert. Wenn ein Benutzer also eine Weile nicht aktualisiert, wird er Zwischenupdates “verpassen”. Der Update-Server könnte geändert werden, um je nach Bedarf Ihrer Anwendung entweder die nächste inkrementelle Version oder die neueste Version zu unterstützen. Bitte lassen Sie uns wissen, wenn alternative Update-Verhaltensweisen für Sie wichtig sind. ### Wie verhält sich Capgo zu Capacitor?[](https://capgo.app/docs/faq/#how-does-capgo-relate-to-capacitor "Direct link to How does Capgo relate to Capacitor?") [Section titled “Wie verhält sich Capgo zu Capacitor?”](#wie-verhält-sich-capgo-zu-capacitor) Capgo ist ein Plugin für Capacitor, das Code Push hinzufügt. Capgo ist kein Ersatz für Capacitor. Sie können weiterhin die Capacitor-Tools verwenden, die Sie bereits kennen und schätzen. Wir verfolgen die neueste stabile Version von Capacitor und aktualisieren unser Code Push Plugin entsprechend. ### Wann finden Updates statt?[](https://capgo.app/docs/faq/#when-do-updates-happen "Direct link to When do updates happen?") [Section titled “Wann finden Updates statt?”](#wann-finden-updates-statt) Standardmäßig prüft der Capgo-Updater beim App-Start auf Updates. Er läuft in einem Hintergrund-Thread und blockiert nicht den UI-Thread. Updates werden installiert, während der Benutzer die App verwendet, und beim nächsten Neustart der App angewendet. Es ist auch möglich, den Capgo-Updater manuell mit [package:capgo\_code\_push](https://pubdev/packages/capgo_code_push/) auszuführen, wodurch Updates jederzeit ausgelöst werden können, auch über Push-Benachrichtigungen. Der Capgo-Updater ist so konzipiert, dass die App normal weiterläuft, wenn das Netzwerk nicht verfügbar ist oder der Server nicht erreichbar ist. Sollten Sie sich jemals entscheiden, ein Update von unseren Servern zu löschen, werden alle Ihre Clients normal weiterlaufen. Wir haben die Möglichkeit hinzugefügt, Patches zurückzurollen. Am einfachsten ist es, einfach ein vorheriges Bundle an Ihren Kanal anzuhängen, um dies rückgängig zu machen. ### Muss ich meine app\_id geheim halten?[](https://capgo.app/docs/faq/#do-i-need-to-keep-my-app_id-secret "Direct link to Do I need to keep my app_id secret?") [Section titled “Muss ich meine app\_id geheim halten?”](#muss-ich-meine-app_id-geheim-halten) Nein. Die `app_id` ist in Ihrer App enthalten und kann öffentlich sein. Sie können sie in die Versionskontrolle einchecken (auch öffentlich) und müssen sich keine Sorgen machen, dass jemand anderes darauf zugreift. Jemand mit Ihrer `app_id` kann die neueste Version Ihrer App von Capgo-Servern abrufen, kann aber keine Updates für Ihre App pushen oder auf andere Aspekte Ihres Capgo-Kontos zugreifen. ### Welche Informationen werden an Capgo-Server gesendet?[](https://capgo.app/docs/faq/#what-information-is-sent-to-capgo-servers "Direct link to What information is sent to Capgo servers?") [Section titled “Welche Informationen werden an Capgo-Server gesendet?”](#welche-informationen-werden-an-capgo-server-gesendet) Obwohl Capgo sich mit dem Netzwerk verbindet, werden keine personenbezogenen Daten gesendet. Das Einbinden von Capgo sollte Ihre Erklärungen für den Play Store oder App Store nicht beeinflussen. Anfragen, die von der App an Capgo-Server gesendet werden, enthalten: * app\_id (angegeben in `capacitorconfigjson`) * channel (optional in `capacitorconfigjson`) * release\_version (versionName aus AndroidManifestxml oder CFBundleShortVersionString aus Infoplist oder `capacitorconfigjson` wenn in [`CapacitorUpdaterversion`](/docs/plugin/settings/#version) gesetzt) * version\_number (generiert als Teil von `npx @capgo/cli app update`) * os\_version (z.B. ‘1121’) * platform (z.B. ‘android’, notwendig um den richtigen Patch zu senden) Das wars. Der Code dafür ist in `updater/library/src/networkrs` * device\_id (beim ersten Start auf dem Gerät generiert, wird verwendet um Geräte-spezifische Installationen zu deduplizieren und ermöglicht uns die Abrechnung basierend auf installierten Benutzern)monatlich aktive Nutzer), anstatt der Gesamtanzahl der Patches oder Patch-Installationen) * custom\_id (optional, zur Laufzeit vom Entwickler festgelegt, um ein Gerät mit einem Benutzer in Ihrem System zu verknüpfen) ### Welche Plattformen unterstützt Capgo?[](https://capgo.app/docs/faq/#what-platforms-does-capgo-support "Direct link to What platforms does Capgo support?") [Section titled “Welche Plattformen unterstützt Capgo?”](#welche-plattformen-unterstützt-capgo) Derzeit unterstützt Capgo iOS und Android. Beide sind produktionsreif. Die Nutzung von Capgo für iOS oder Android kann unabhängig voneinander entschieden werden. Sie können in Ihrem Kanal festlegen, ob Sie an Android oder eine in den App Store gebaute IPA ausliefern möchten oder umgekehrt. Capgo kann (relativ einfach) für Desktop- oder Embedded-Ziele erweitert werden. Wenn diese für Sie wichtig sind, lassen Sie es uns bitte wissen. ### Wie interagiert Capgo mit Play Testing Tracks oder Apple TestFlight?[](https://capgo.app/docs/faq/#how-does-capgo-interact-with-play-testing-tracks-or-apple-testflight "Direct link to How does Capgo interact with Play Testing Tracks or Apple TestFlight?") [Section titled “Wie interagiert Capgo mit Play Testing Tracks oder Apple TestFlight?”](#wie-interagiert-capgo-mit-play-testing-tracks-oder-apple-testflight) Jeder App Store hat eigene Mechanismen zur Verteilung von Apps an begrenzte Benutzergruppen (z.B. “internes Testen”, “geschlossene Beta” usw.). Dies sind alles Mechanismen zur Segmentierung Ihrer Benutzer in Gruppen und zur Verteilung spezifischer App-Versionen. Leider erlauben nicht alle diese Mechanismen Drittanbietern zu erkennen, wenn Apps in einem bestimmten Test Track oder über TestFlight installiert sind. Daher haben wir keine zuverlässige Einsicht in die Zusammensetzung dieser Gruppen und können den Zugriff auf Capgo-Patches nicht zuverlässig auf Basis dieser Gruppen steuern. [https://stackoverflow.com/questions/53291007/can-an-android-application-identify-the-test-track-within-google-play](https://stackoverflow.com/questions/53291007/can-an-android-application-identify-the-test-track-within-google-play/) [https://stackoverflow.com/questions/26081543/how-to-tell-at-runtime-whether-an-ios-app-is-running-through-a-testflight-beta-i](https://stackoverflow.com/questions/26081543/how-to-tell-at-runtime-whether-an-ios-app-is-running-through-a-testflight-beta-i/) Wenn Sie die Verfügbarkeit von Capgo-Bundles segmentieren möchten, gibt es 4 mögliche Optionen: 1. Verwenden Sie separate Kanäle für jede Gruppe. Dies ist der einfachste Ansatz, erfordert aber die Verwaltung mehrerer Kanäle. Möglicherweise haben Sie bereits Dev-Kanäle und Prod-Kanäle mit unterschiedlicher Verfügbarkeit. Sie können also Ihre Dev-Kanäle aktualisieren, überprüfen und dann separat Ihre Prod-Kanäle aktualisieren. Wir empfehlen die Verwendung von Branches/Tags in Ihrer Versionskontrolle, um den Überblick über die Quellen der einzelnen Releases zu behalten. 2. Verfolgen Sie Ihre eigene Liste von Opt-in-Benutzern, deaktivieren Sie automatische Updates und lösen Sie Updates nur für bestimmte Benutzer über package:capgo\_code\_push aus. Dies funktioniert heute, erfordert aber die Verwaltung Ihrer eigenen Opt-in-Liste. 3. Capgo ermöglicht einen eigenen Opt-in-Mechanismus auf Geräteebene (ähnlich wie Test Tracks oder TestFlight, nur plattformunabhängig). Dies ermöglicht Ihrem QA-Team, sich für Bundles anzumelden, bevor sie der Öffentlichkeit zur Verfügung gestellt werden. 4. Capgo hat prozentbasierte Rollouts. Dies lässt Sie zwar nicht auswählen, an welche Geräte gesendet wird, kann aber helfen, inkrementell auszurollen und bei Problemen zurückzurollen. ## Abrechnung[](https://capgo.app/docs/faq/#billing "Direct link to Billing") [Section titled “Abrechnung”](#abrechnung) ### Wie kann ich meinen Plan upgraden oder downgraden?[](https://capgo.app/docs/faq/#how-do-i-upgrade-or-downgrade-my-plan "Direct link to How do I upgrade or downgrade my plan?") [Section titled “Wie kann ich meinen Plan upgraden oder downgraden?”](#wie-kann-ich-meinen-plan-upgraden-oder-downgraden) Sie können Ihren Plan jederzeit in Ihrem Dashboard upgraden oder downgraden: [https://console.capgo.app/settings/organization/plans](https://console.capgo.app/settings/organization/plans/) ### Wann wird meine Abrechnungsperiode zurückgesetzt?[](https://capgo.app/docs/faq/#when-does-my-billing-period-reset "Direct link to When does my billing period reset?") [Section titled “Wann wird meine Abrechnungsperiode zurückgesetzt?”](#wann-wird-meine-abrechnungsperiode-zurückgesetzt) Abrechnungsperioden werden automatisch jeden Monat am Tag Ihrer ersten Capgo-Anmeldung zurückgesetzt. Wenn Sie sich zum Beispiel am 15. des Monats angemeldet haben, wird Ihre Abrechnungsperiode am 15. jedes Monats zurückgesetzt. ### Wie kann ich mein Abonnement kündigen?[](https://capgo.app/docs/faq/#how-do-i-cancel-my-subscription "Direct link to How do I cancel my subscription?") [Section titled “Wie kann ich mein Abonnement kündigen?”](#wie-kann-ich-mein-abonnement-kündigen) Sie können Ihr Abonnement jederzeit in Ihrem Dashboard kündigen: [https://console.capgo.app/settings/organization/plans](https://console.capgo.app/settings/organization/plans/) ### Kann ich für ein Jahr im Voraus bezahlen?[](https://capgo.app/docs/faq/#can-i-pay-for-a-year-in-advance "Direct link to Can I pay for a year in advance?") [Section titled “Kann ich für ein Jahr im Voraus bezahlen?”](#kann-ich-für-ein-jahr-im-voraus-bezahlen) Ja, Sie können jederzeit in Ihrem Dashboard für ein Jahr im Voraus bezahlen: [https://console.capgo.app/settings/organization/plans](https://console.capgo.app/settings/organization/plans/) ### Statistiken und Analysen[](https://capgo.app/docs/faq/#stats-and-analytics "Direct link to Stats and analytics") [Section titled “Statistiken und Analysen”](#statistiken-und-analysen) Die Statistiken in Ihrem Dashboard werden jeden Tag um Mitternacht UTC aktualisiert. Die Statistiken basieren auf der Anzahl der [MAU](https://capgo.app/docs/faq/#what-is-the-difference-between-a-bundle-and-a-release "Direct link to What is the difference between a bundle and a release?"), die auf Ihren Geräten installiert wurden. # Wie wird die Geräte-ID generiert[](https://capgo.app/docs/faq/#how-device-id-is-generated "Direct link to How device ID is generated") [Section titled “Wie wird die Geräte-ID generiert”](#wie-wird-die-geräte-id-generiert) Die Geräte-ID wird beim ersten Start auf dem Gerät generiert und wird verwendet, um Installationen pro Gerät zu deduplizieren und uns zu ermöglichen, basierend auf installierten Benutzern (z.B. monatlich aktive Nutzer) abzurechnen, anstatt der Gesamtanzahl der Patches oder Patch-Installationen. MAU ist eine bessere Lösung als die Anzahl der Installationen für die Preisgestaltung von Capgo, da es genauer ist und die tatsächlichen Kosten von Capgo pro Gerät widerspiegelt. **Persistenz der Geräte-ID (Aktualisiert in v6.25.0 und v7.25.0)**: * **Aktuelles Verhalten**: Die Geräte-ID bleibt nun über App-Neuinstallationen hinweg erhalten. Sie wird sicher im Keychain des Geräts (iOS) oder EncryptedSharedPreferences (Android) gespeichert, sodass wir dasselbe Gerät auch nach Deinstallation/Neuinstallation verfolgen können. * **Vorheriges Verhalten** (vor v6.25.0/v7.25.0): Aus Datenschutzgründen im Zusammenhang mit Apple- und Google Store-Richtlinien wurde die Geräte-ID bei jeder App-Neuinstallation zurückgesetzt, was es unmöglich machte, dasselbe Gerät über Neuinstallationen hinweg zu verfolgen. Die Datenschutzregeln werden von Apple und Google durchgesetzt, und die Implementierung von Capgo entspricht deren Bewährte Methoden für die Geräteidentifikation. Geräte-IDs werden erst in Ihrer Geräteliste angezeigt, wenn sie ihren ersten Patch installiert haben. # Warum unterscheidet sich meine Geräteanzahl von meinen MAU?[](https://capgo.app/docs/faq/#why-my-device-number-is-different-than-my-mau "Direct link to Why my device number is different than my MAU?") [Section titled “Warum unterscheidet sich meine Geräteanzahl von meinen MAU?”](#warum-unterscheidet-sich-meine-geräteanzahl-von-meinen-mau) Derzeit wird die Geräteliste nicht so häufig aktualisiert wie die MAU. Die Geräteliste wird nur aktualisiert, wenn ein Gerät ein Update installiert. Die MAU wird bei jedem App-Start aktualisiert. Dies ist eine aktuelle Einschränkung der Plattform. Unsere Analyseplattform unterstützt keine Raw-Updates, daher verwenden wir konventionelle Datenbanken für die Geräteliste. Um die Anzahl der Datenbankabfragen zu begrenzen, aktualisieren wir Zeilen nur bei App-Updates. Diese Einschränkung wird in Zukunft behoben. # Wie kann ich verschiedene Updates pro Plattform haben?[](https://capgo.app/docs/faq/#how-to-have-different-update-by-platform "Direct link to How to have different update by platform?") [Section titled “Wie kann ich verschiedene Updates pro Plattform haben?”](#wie-kann-ich-verschiedene-updates-pro-plattform-haben) Sie können einen Kanal für jede Plattform erstellen und plattformspezifische Updates in jedem Kanal deaktivieren. Im iOS-Kanal Android-Updates deaktivieren und im Android-Kanal iOS-Updates deaktivieren. Dann laden Sie ein Bundle in jeden Kanal hoch, um verschiedene Updates für jede Plattform zu haben. Wenn Sie dasselbe Update für beide Plattformen benötigen, können Sie ein Bundle mit mehreren Kanälen verknüpfen. Das Bundle muss nicht dupliziert werden. # Tech Support für Capgo > So erhalten Sie Tech Support für Capgo und unseren Updater. Bitte folgen Sie dieser Anleitung, um Hilfe zu erhalten, wenn die Dokumentation und unsere Artikel nicht ausreichen ## Support über Discord [Section titled “Support über Discord”](#support-über-discord) Capgo hat einen offiziellen [Discord Server](https://discord.capgo.app). Dort technischen Support zu erhalten ist wahrscheinlich einer der schnellsten Wege, eine Antwort zu bekommen. Hier ist eine Kurzanleitung: Schritt 1 - Gehe zum `questions` Kanal ![Ask on discord](/discord-questions.webp) Schritt 2 - Erstelle deinen Thread ![Create a question on discord](/discord-newquestion.webp) Schritt 3 - Beschreibe dein Problem und wähle die relevanten Tags aus ![Create a post on discord](/discord-new-post.webp) Schritt 4 - Teile deine sichere Konto-ID (optional) Dies ermöglicht es dem Capgo-Team, einen Blick auf dein Konto zu werfen. Das Teilen dieser ID ist sicher, da sie dafür entwickelt wurde, öffentlich geteilt zu werden. Um dies zu teilen, gehe bitte zu den [Capgo-Einstellungen](https://console.capgo.app/dashboard/settings/account/). Klicke dort bitte auf `copy account id`. ![Share your id without leaking your info](/share-secure-id.webp) Dies kopiert die sichere Konto-ID in die Zwischenablage. Bitte füge diese in deinen Discord-Beitrag ein. ## Support per E-Mail [Section titled “Support per E-Mail”](#support-per-e-mail) Dies ist der langsamste Weg, Support zu erhalten. Bitte nutze zuerst den Discord-Server. Wenn du uns per E-Mail kontaktieren musst, sende bitte eine E-Mail an . # App hinzufügen > Fügen Sie eine App zu Ihrem Capgo-Konto hinzu und installieren Sie das Plugin in Ihrer App ## Voraussetzungen [Section titled “Voraussetzungen”](#voraussetzungen) Bevor Sie mit Capgo beginnen, stellen Sie sicher, dass Sie: * Eine Capacitor-App installiert und konfiguriert haben. [Erfahren Sie, wie Sie Capacitor einrichten](https://capacitorjs.com/docs/getting-started/) * Node.js 20 oder neuer installiert haben * Eine der folgenden Entwicklungsumgebungen: * **macOS** mit Xcode (für iOS-Entwicklung) und/oder Android Studio (für Android-Entwicklung) * **Linux** mit Android Studio (für Android-Entwicklung) * **Windows** mit Android Studio (für Android-Entwicklung) ## Einführung in Capgo [Section titled “Einführung in Capgo”](#einführung-in-capgo) [Play](https://youtube.com/watch?v=NzXXKoyhTIo) ## Live-Updates sind 3 Schritte entfernt [Section titled “Live-Updates sind 3 Schritte entfernt”](#live-updates-sind-3-schritte-entfernt) ### Geführtes Setup [Section titled “Geführtes Setup”](#geführtes-setup) 1. Erstellen Sie Ihr Konto unter . ![signup screenshot](/signup.webp "signup screenshot") 2. Verwenden Sie die Init-Befehle, um zu beginnen ```bash npx @capgo/cli@latest init [APIKEY] ``` Es werden Ihnen eine Reihe von Fragen gestellt. Beantworten Sie diese, um das automatische Setup abzuschließen. 3. Stellen Sie ein Live-Update bereit Tip Wenn Sie diese Schritte befolgen, sind Sie in kürzester Zeit einsatzbereit. Falls Sie während des Prozesses weitere Unterstützung benötigen, ist unser Support-Team [hier um zu helfen](https://support.capgo.app). Viel Spaß beim Onboarding! [Detaillierter Onboarding-Leitfaden ](/docs/getting-started/onboarding/)Sehen Sie den vollständigen Schritt-für-Schritt-Leitfaden für den CLI-Onboarding-Prozess [Stellen Sie ein Live-Update bereit ](/docs/getting-started/deploy/)Erfahren Sie, wie Sie ein Live-Update für Ihre App bereitstellen ### Manuelles Setup [Section titled “Manuelles Setup”](#manuelles-setup) Falls der Init-Befehl für Sie nicht funktioniert, können Sie eine App manuell hinzufügen. 1. Verbinden Sie die CLI mit Ihrem Konto: ```bash npx @capgo/cli@latest login [APIKEY] ``` 2. Fügen Sie die App mit diesem Befehl zu Ihrem Konto hinzu: ```bash npx @capgo/cli@latest app add [APP_NAME] ``` 3. Installieren Sie das Plugin in Ihrer App: ```bash npm i @capgo/capacitor-updater ``` 4. Konfigurieren Sie das Plugin in Ihrer `capacitor.config` ```json { "plugins": { CapacitorUpdater: { "appId": "Your appID", "autoUpdate": true, "version": "1.0.0" } } } ``` [Siehe alle verfügbaren Optionen](/docs/plugin/settings/). Diese Informationen werden abgeleitet, wenn sie nicht bereitgestellt werden. 5. Rufen Sie die Init-Methode so früh wie möglich in Ihrer App auf: ```typescript import { CapacitorUpdater } from '@capgo/capacitor-updater'; CapacitorUpdater.notifyAppReady(); ``` 6. Stellen Sie ein Live-Update bereit Installation für ältere Capacitor-Versionen Der obige Befehl installiert die neueste Version (v8.x) für Capacitor 8. Für ältere Capacitor-Versionen verwenden Sie den entsprechenden npm-Tag: ```bash # Capacitor 7 npm i @capgo/capacitor-updater@lts-v7 # Capacitor 6 npm i @capgo/capacitor-updater@lts-v6 # Capacitor 5 npm i @capgo/capacitor-updater@lts-v5 ``` Jede Plugin-Hauptversion entspricht der Capacitor-Hauptversion (v8 → Capacitor 8, v7 → Capacitor 7, v6 → Capacitor 6, v5 → Capacitor 5). Minor-Versionen teilen denselben Funktionsumfang über alle Hauptversionen (z.B. 5.34.0, 6.34.0, 7.34.0 und 8.34.0 enthalten dieselben Funktionen). # CI/CD-Integration > Die Integration von Capgo in Ihre CI/CD-Pipeline ermöglicht es Ihnen, den Prozess des Erstellens und Bereitstellens von Updates für Ihre App vollständig zu automatisieren. Durch die Nutzung der Capgo CLI und semantic-release können Sie konsistente, zuverlässige Bereitstellungen sicherstellen und schnelle Iterationen ermöglichen. Die Integration von Capgo in Ihre CI/CD-Pipeline ermöglicht es Ihnen, den Prozess des Erstellens und Bereitstellens von Updates für Ihre App vollständig zu automatisieren. Durch die Nutzung der Capgo CLI und semantic-release können Sie konsistente, zuverlässige Bereitstellungen sicherstellen und schnelle Iterationen ermöglichen. ## Vorteile der CI/CD-Integration [Section titled “Vorteile der CI/CD-Integration”](#vorteile-der-cicd-integration) * **Automatisierung**: Keine manuellen Schritte mehr oder Raum für menschliche Fehler. Ihr gesamter Build-, Test- und Bereitstellungsprozess kann von Anfang bis Ende automatisiert werden. * **Konsistenz**: Jede Bereitstellung folgt den gleichen Schritten und gewährleistet einen vorhersehbaren und wiederholbaren Prozess. Dies ist besonders wertvoll, wenn mehrere Teammitglieder Code beisteuern. * **Schnellere Iterationen**: Mit automatisierten Bereitstellungen können Sie Updates häufiger und mit Zuversicht ausliefern. Kein Warten mehr auf manuelle QA oder Freigabegenehmigungen. ## Capgo CLI [Section titled “Capgo CLI”](#capgo-cli) Die Capgo CLI ist der Schlüssel zur Integration von Capgo in Ihren CI/CD-Workflow. Sie bietet Befehle zum Pushen neuer Bundle-Versionen, Verwalten von Kanälen und mehr. Der wichtigste Befehl für die CI/CD-Integration ist `bundle upload`: ```shell npx @capgo/cli@latest bundle upload --channel Production --apikey YOUR_API_KEY ``` Wenn Sie Verschlüsselung verwenden, sollten Sie diese auf eine der folgenden Arten bereitstellen: **Verwendung eines privaten Schlüsseldateipfads:** ```shell npx @capgo/cli@latest bundle upload --channel Production --apikey YOUR_API_KEY --key-v2 PRIVATE_KEY_PATH ``` **Direktes Verwenden des privaten Schlüsselinhalts (empfohlen für CI/CD):** ```shell npx @capgo/cli@latest bundle upload --channel Production --apikey YOUR_API_KEY --key-data-v2 PRIVATE_KEY_CONTENT ``` **Verwendung von Umgebungsvariablen (Best Practice für CI/CD):** ```shell npx @capgo/cli@latest bundle upload --channel Production --apikey YOUR_API_KEY --key-data-v2 "$CAPGO_PRIVATE_KEY" ``` ### Einrichten von Umgebungsvariablen für Verschlüsselung [Section titled “Einrichten von Umgebungsvariablen für Verschlüsselung”](#einrichten-von-umgebungsvariablen-für-verschlüsselung) Für CI/CD-Umgebungen wird empfohlen, Ihren privaten Schlüssel als Umgebungsvariable statt als Datei zu speichern. So richten Sie es ein: 1. **Holen Sie sich den Inhalt Ihres privaten Schlüssels:** ```shell cat .capgo_key_v2 | pbcopy ``` Dies kopiert den Schlüsselinhalt in Ihre Zwischenablage. 2. **Fügen Sie ihn zu Ihrer CI/CD-Umgebung hinzu:** * **GitHub Actions**: Fügen Sie `CAPGO_PRIVATE_KEY` zu Ihren Repository-Secrets hinzu * **GitLab CI**: Fügen Sie ihn als maskierte Variable in Ihren Projekteinstellungen hinzu * **CircleCI**: Fügen Sie ihn als Umgebungsvariable in Ihren Projekteinstellungen hinzu * **Jenkins**: Fügen Sie ihn als Secret-Text-Credential hinzu 3. **Verwenden Sie ihn in Ihrer Pipeline:** ```yaml - run: npx @capgo/cli@latest bundle upload --channel=production --apikey=${{ secrets.CAPGO_API_KEY }} --key-data-v2 "${{ secrets.CAPGO_PRIVATE_KEY }}" ``` **Hinweis**: Das `--key-data-v2` Flag ermöglicht es Ihnen, den Inhalt des privaten Schlüssels direkt als String zu übergeben, was perfekt für Umgebungsvariablen in CI/CD-Pipelines ist, wo Sie keine temporären Dateien erstellen möchten. Dieser Befehl lädt den aktuellen Web-Build in den angegebenen Kanal hoch. Sie führen dies typischerweise als letzten Schritt in Ihrer CI/CD-Pipeline aus, nachdem Ihr Web-Build erfolgreich abgeschlossen wurde. ## Einrichtung von Capgo in Ihrer CI/CD-Pipeline [Section titled “Einrichtung von Capgo in Ihrer CI/CD-Pipeline”](#einrichtung-von-capgo-in-ihrer-cicd-pipeline) Während die genauen Schritte je nach gewähltem CI/CD-Tool variieren, sieht der allgemeine Prozess zur Integration von Capgo wie folgt aus: 1. **API-Schlüssel generieren**: Melden Sie sich beim Capgo-Dashboard an und erstellen Sie einen neuen API-Schlüssel. Dieser Schlüssel wird zur Authentifizierung der CLI in Ihrer CI/CD-Umgebung verwendet. Halten Sie ihn geheim und committen Sie ihn niemals in Ihr Repository! 2. **Konfigurieren Sie den `bundle upload` Befehl**: Fügen Sie Ihrer CI/CD-Konfiguration einen Schritt hinzu, der den `bundle upload` Befehl mit den entsprechenden Argumenten ausführt: upload.yml ```yaml - run: npx @capgo/cli@latest bundle upload --channel=production --apikey=${{ secrets.CAPGO_API_KEY }} ``` \n Ersetzen Sie `Production` mit dem Kanal, in den Sie deployen möchten, `${{ secrets.CAPGO_API_KEY }}` mit der Umgebungsvariable, die Ihren API-Schlüssel enthält, und fügen Sie `--key-data-v2 "${{ secrets.CAPGO_PRIVATE_KEY }}"` hinzu, wenn Sie Verschlüsselung verwenden. 3. **Fügen Sie den `upload` Schritt nach Ihrem Web-Build hinzu**: Stellen Sie sicher, dass der `upload` Schritt nach erfolgreichem Abschluss Ihres Web-Builds erfolgt. Dies stellt sicher, dass Sie immer Ihren neuesten Code bereitstellen.\n Hier ist ein Beispiel für die Konfiguration für GitHub Actions:\n upload.yml ```yaml name: Deploy to Capgo on: push: branches: [main] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - uses: actions/setup-node@v6 with: node-version: '24' - run: npm ci - run: npm run build - run: npm install -g @capgo/cli - run: npx @capgo/cli@latest bundle upload --channel=production --apikey=${{ secrets.CAPGO_API_KEY }} --key-data-v2 "${{ secrets.CAPGO_PRIVATE_KEY }}" ``` ## Versionsverwaltung mit Semantic-release [Section titled “Versionsverwaltung mit Semantic-release”](#versionsverwaltung-mit-semantic-release) Der empfohlene Weg zur Handhabung der Versionierung mit Capgo ist, die Version in Ihrer `capacitor.config.ts` Datei durch Import aus `package.json` zu setzen: ```ts import pkg from './package.json' const config: CapacitorConfig = { // ... andere Konfigurationen plugins: { CapacitorUpdater: { version: pkg.version, } } } ``` Dieser Ansatz ermöglicht es Ihnen: 1. Semantic-release (oder ein anderes Tool) zu verwenden, um die `package.json` Version zu aktualisieren 2. Ihre App mit der automatisch enthaltenen aktualisierten Version zu bauen 3. Das Bundle mit der korrekten Version hochzuladen Ihr CI/CD-Workflow würde so aussehen: ```yaml - run: npm ci - run: npx semantic-release # Aktualisiert package.json Version - run: npm run build # Baut mit neuer Version aus capacitor.config - run: npx @capgo/cli@latest bundle upload --channel=production --apikey=${{ secrets.CAPGO_API_KEY }} ``` Hier ist eine Beispiel-`.releaserc` Konfigurationsdatei für semantic-release: ```json { "branches": [ "main", { "name": "beta", "prerelease": true } ], "plugins": [ "@semantic-release/commit-analyzer", "@semantic-release/release-notes-generator", "@semantic-release/changelog", [ "@semantic-release/git", { "assets": ["CHANGELOG.md", "package.json"], "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}" } ] ] } ``` Diese Konfiguration führt Folgendes aus: 1. Analysiert Commit-Nachrichten, um die nächste Versionsnummer nach der Conventional Commits-Spezifikation zu bestimmen. 2. Generiert Release Notes basierend auf den Commits seit dem letzten Release. 3. Aktualisiert die `CHANGELOG.md` Datei mit den neuen Release Notes. 4. Aktualisiert die `package.json` Version, die von Ihrer capacitor.config übernommen wird. 5. Committet die aktualisierte `CHANGELOG.md`, `package.json` und alle anderen geänderten Dateien zurück ins Repository. Stellen Sie sicher, dass Sie semantic-release vor dem Build Ihrer App ausführen, damit die aktualisierte Version aus `package.json` durch die capacitor.config in Ihren Build aufgenommen wird. ## Fehlerbehebung [Section titled “Fehlerbehebung”](#fehlerbehebung) Wenn Sie Probleme mit Ihrer Capgo CI/CD-Integration haben, hier einige Dinge, die Sie überprüfen sollten: * **API-Schlüssel**: Stellen Sie sicher, dass Ihr API-Schlüssel gültig ist und die erforderlichen Berechtigungen hat. Wenn Sie eine Umgebungsvariable verwenden, überprüfen Sie, ob sie korrekt gesetzt ist. * **CLI-Version**: Stellen Sie sicher, dass Sie die neueste Version der Capgo CLI verwenden. Ältere Versionen können Kompatibilitätsprobleme haben oder bestimmte Funktionen fehlen. * **Build-Artefakte**: Bestätigen Sie, dass Ihr Web-Build die erwarteten Ausgabedateien generiert. Die Capgo CLI benötigt einen gültigen Web-Build, um ein Bundle zu erstellen. * **Netzwerkverbindung**: Überprüfen Sie, ob Ihre CI/CD-Umgebung Netzwerkzugriff auf die Capgo-Server hat. Firewall- oder Proxy-Probleme können manchmal den `upload` Befehl stören. Wenn Sie immer noch Probleme haben, wenden Sie sich an den Capgo-Support. Sie können bei der Fehlerbehebung für Ihre spezifische Einrichtung helfen. ## Fazit [Section titled “Fazit”](#fazit) Die Integration von Capgo in Ihre CI/CD-Pipeline mit ordnungsgemäßer Versionsverwaltung kann Ihren Entwicklungsworkflow erheblich vereinfachen. Durch die Automatisierung Ihrer Bereitstellungen und Versionierung durch den capacitor.config-Ansatz können Sie Updates schneller und mit mehr Zuversicht ausliefern. Der empfohlene Ansatz, die Version in Ihrer `capacitor.config.ts` Datei zu setzen und semantic-release zur Aktualisierung von `package.json` zu verwenden, bietet einen robusten und zuverlässigen Bereitstellungsprozess, der es Ihnen ermöglicht, sich auf das Entwickeln großartiger Funktionen zu konzentrieren, anstatt sich über manuelle Release-Schritte Gedanken zu machen. Weitere Details zu den Capgo CLI-Befehlen und Optionen finden Sie in der [CLI-Referenz](/docs/cli/overview). Und für einen tieferen Einblick in die semantic-release-Konfiguration, siehe die [semantic-release Dokumentation](https://github.com/semantic-release/semantic-release). Viel Spaß beim Bereitstellen! # Live-Update bereitstellen > Erfahren Sie, wie Sie ein Live-Update für Ihre App mit der Live-Updates-Funktion von Capgo bereitstellen, die Echtzeit-UI- und Logik-Updates ohne erneute Einreichung im App Store ermöglicht. Nutzen Sie die Live-Updates-Funktion von Capgo, um die Benutzeroberfläche und Geschäftslogik Ihrer App aus der Ferne in Echtzeit zu aktualisieren. Übertragen Sie JS-Bundle-Updates direkt an Ihre Nutzer, ohne den App Store zu durchlaufen, um sofort Fehler zu beheben und neue Funktionen bereitzustellen. Diese Anleitung setzt voraus, dass Sie den [Capgo Quickstart](/docs/getting-started/quickstart) abgeschlossen haben und bereits: 1. Das `@capgo/capacitor-updater` SDK in Ihrer Capacitor-App installiert haben 2. Ihre App-ID und den Update-Kanal in `capacitor.config.ts` konfiguriert haben 3. Die `CapacitorUpdater.notifyAppReady()` Methode in Ihren Code eingefügt haben Wenn Sie diese Schritte noch nicht ausgeführt haben, gehen Sie bitte zurück und schließen Sie zuerst den Quickstart ab. [App hinzufügen ](/docs/getting-started/add-an-app/)Fügen Sie eine App zu Ihrem Capgo-Konto hinzu und installieren Sie das Plugin in Ihrer App ## Bundle hochladen [Section titled “Bundle hochladen”](#bundle-hochladen) Mit dem installierten und konfigurierten Capgo SDK können Sie Ihr erstes Live-Update-Bundle hochladen: 1. Web-Assets erstellen: ```shell npm run build ``` 2. Bundle zu Capgo hochladen: * Console ```shell npx @capgo/cli@latest bundle upload --channel=production ``` * Github Actions .github/workflows/build\_and\_deploy.yml ```yml name: Build source code and send to Capgo concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true on: push: branches: - main jobs: deploy_to_capgo: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v6 - uses: actions/setup-node@v6 with: node-version: '24' - name: Install dependencies run: npm install - name: Build run: npm run build - name: Deploy to Capgo run: npx @capgo/cli@latest bundle upload -a ${{ secrets.CAPGO_TOKEN }} --channel ${{ env.CHANNEL }} env: CAPGO_TOKEN: ${{ secrets.CAPGO_TOKEN }} ``` * Gitlab .gitlab-ci.yml ```yml stages: - build build: stage: build image: node:18 cache: - key: files: - package-lock.json paths: - .node_modules/ script: - npm install - npm run build - npx @capgo/cli@latest bundle upload -a $CAPGO_TOKEN --channel $CAPGO_CHANNEL artifacts: paths: - node_modules/ - dist/ only: - master ``` Dies lädt eine neue Bundle-Version in den im Befehl angegebenen Kanal hoch ### Fehlerbehebung beim Hochladen [Section titled “Fehlerbehebung beim Hochladen”](#fehlerbehebung-beim-hochladen) Wenn das Hochladen fehlschlägt, überprüfen Sie: * Ihre App-ID in `capacitor.config.ts` stimmt mit Ihrer App im Capgo-Dashboard überein * Sie führen den Upload-Befehl vom Root-Verzeichnis Ihres Capacitor-Projekts aus * Ihre Web-Assets sind erstellt und aktuell Wenn Sie immer noch Probleme haben, gehen Sie zum Abschnitt [Fehlerbehebung](/docs/getting-started/troubleshooting/) ## Update auf einem Gerät empfangen [Section titled “Update auf einem Gerät empfangen”](#update-auf-einem-gerät-empfangen) Sobald Ihr Bundle hochgeladen ist, können Sie das Live-Update auf einem Gerät testen: 1. Synchronisieren Sie Ihre App mit dem Gerät: ```shell npx cap sync ios ``` 2. Öffnen Sie ein weiteres Terminal und führen Sie den folgenden Befehl aus, um den Update-Status zu überprüfen: ```shell npx @capgo/cli@latest app debug ``` 3. Führen Sie Ihre App lokal aus: ```shell npx cap run ios ``` Oder öffnen Sie das iOS/Android-Projekt in Xcode/Android Studio und führen Sie einen nativen Start durch 4. Lassen Sie die App etwa 30 Sekunden geöffnet, damit das Update im Hintergrund heruntergeladen werden kann 5. Die Logs brauchen einige Sekunden, um sich zu aktualisieren und den Update-Status anzuzeigen 6. Schließen und öffnen Sie die App erneut. Sie sollten Ihr Live-Update angewendet sehen! Schauen Sie im [Capgo Quickstart](/docs/getting-started/quickstart#receiving-a-live-update-on-a-device) nach, um weitere Details zum Testen von Live-Updates zu erhalten ## Nächste Schritte [Section titled “Nächste Schritte”](#nächste-schritte) Herzlichen Glückwunsch zum Bereitstellen Ihres ersten Live-Updates mit Capgo! 🎉 Um mehr zu erfahren, lesen Sie den Rest der [Capgo Live-Updates-Dokumentation](/docs/live-updates). Einige wichtige Themen, die Sie als Nächstes überprüfen sollten: * [Updates mit Kanälen zielgerichtet einsetzen](/docs/live-updates/channels) * [Update-Verhalten anpassen](/docs/live-updates/update-behavior) * [Live-Update-Rollbacks](/docs/live-updates/rollbacks) # CLI Onboarding-Anleitung > Vollständige Schritt-für-Schritt-Anleitung zum Onboarding Ihrer App mit Capgo über die interaktive CLI ## Schnellübersicht [Section titled “Schnellübersicht”](#schnellübersicht) Die Capgo CLI bietet ein interaktives Onboarding, das Live-Updates für Ihre Capacitor-App einrichtet. Sie werden: 1. ✅ Ihre App in Capgo registrieren 2. 🔌 Das Updater-Plugin installieren und konfigurieren 3. 🚀 Ihr erstes Live-Update bereitstellen 4. 📱 Das Update auf Ihrem Gerät testen **Geschätzte Zeit:** 10-20 Minuten (variiert je nach Internetgeschwindigkeit und Build-Zeit) Tip Das Onboarding ist vollständig fortsetzbar - beenden Sie jederzeit und fahren Sie später fort, wo Sie aufgehört haben. ## Onboarding starten [Section titled “Onboarding starten”](#onboarding-starten) Führen Sie den Onboarding-Befehl mit Ihrem API-Schlüssel aus: ```bash npx @capgo/cli@latest init [APIKEY] ``` Sie sehen die Willkommensnachricht: ```plaintext Capgo onboarding 🛫 ``` ## Was während des Onboardings passiert [Section titled “Was während des Onboardings passiert”](#was-während-des-onboardings-passiert) Die CLI führt Sie durch 13 interaktive Schritte: **Setup-Phase (Schritte 1-6):** * Überprüfen Sie Ihre Entwicklungsumgebung (Xcode/Android Studio) * Fügen Sie Ihre App zu Capgo hinzu und erstellen Sie einen Produktionskanal * Installieren Sie das `@capgo/capacitor-updater` Plugin * Fügen Sie den erforderlichen Code in Ihre App ein * Optional: End-to-End-Verschlüsselung aktivieren * Wählen Sie eine Plattform zum Testen (iOS oder Android) **Testphase (Schritte 7-12):** * Erstellen Sie Ihre App und führen Sie sie auf einem Gerät/Simulator aus * Nehmen Sie eine sichtbare Codeänderung vor (automatisch oder manuell) * Laden Sie das aktualisierte Bundle zu Capgo hoch * Sehen Sie das Live-Update in Echtzeit auf Ihrem Gerät erscheinen **Abschluss (Schritt 13):** * Ihre App ist bereit für Live-Updates! 🎉 ## Der 13-Schritte-Onboarding-Prozess [Section titled “Der 13-Schritte-Onboarding-Prozess”](#der-13-schritte-onboarding-prozess) ### Schritt 1: Voraussetzungen prüfen [Section titled “Schritt 1: Voraussetzungen prüfen”](#schritt-1-voraussetzungen-prüfen) Die CLI überprüft Ihre Entwicklungsumgebung, um sicherzustellen, dass Sie die erforderlichen Tools installiert haben. **Was wird überprüft:** * **Xcode** (nur macOS) - für iOS-Entwicklung * **Android SDK** - für Android-Entwicklung **Mögliche Ergebnisse:** ✅ **Beide Umgebungen gefunden:** ```plaintext ✅ Xcode detected - iOS development ready ✅ Android SDK detected - Android development ready ``` ⚠️ **Keine Umgebung gefunden:** ```plaintext ⚠️ Xcode not found ⚠️ Android SDK not found ❌ No development environment detected 📱 To develop mobile apps with Capacitor, you need: • For iOS: Xcode (macOS only) - https://developer.apple.com/xcode/ • For Android: Android Studio - https://developer.android.com/studio ``` Caution Wenn keine Entwicklungsumgebung erkannt wird, werden Sie gefragt, ob Sie fortfahren möchten. Es wird empfohlen, mindestens eine Plattform zu installieren, bevor Sie fortfahren. ### Schritt 2: Ihre App hinzufügen [Section titled “Schritt 2: Ihre App hinzufügen”](#schritt-2-ihre-app-hinzufügen) Die CLI meldet Sie bei Capgo an und fügt Ihre App zu Ihrem Konto hinzu. ```plaintext (spinner) Running: npm @capgo/cli@latest login *** Login Done ✅ ❓ Add {appId} in Capgo? ``` **Wenn Ihre App-ID bereits vergeben ist:** Die CLI schlägt Alternativen vor: ```plaintext ❌ App ID "com.example.app" is already taken 💡 Here are some suggestions: 1. com.example.app2 2. com.example.app3 3. com.example.app.new 4. com.example.app.app ❓ What would you like to do? ``` Sie können einen Vorschlag wählen oder eine benutzerdefinierte App-ID eingeben. Note App-IDs müssen der Reverse-Domain-Notation folgen (z.B. `com.example.myapp`) ### Schritt 3: Produktionskanal erstellen [Section titled “Schritt 3: Produktionskanal erstellen”](#schritt-3-produktionskanal-erstellen) Kanäle ermöglichen es Ihnen, verschiedene Update-Streams für Ihre App zu verwalten. ```plaintext ❓ Create default channel production for {appId} in Capgo? ``` Tip **Keine Sorge!** Dies ist nur für lokale Tests während des Onboardings. Das Erstellen eines “Produktions”-Kanals bedeutet nicht, dass Ihre Updates sofort an Kunden gesendet werden. Sie haben die volle Kontrolle darüber, wann Updates bereitgestellt werden. Wählen Sie **Ja**, es sei denn, Sie haben spezifische Kanalanforderungen. **Wenn Sie Ja wählen:** ```plaintext (spinner) Running: npm @capgo/cli@latest channel add production {appId} --default Channel add Done ✅ (or "Channel already added ✅") ``` Ein Produktionskanal wird erstellt und als Standard festgelegt. Dies ist die empfohlene Option für die meisten Benutzer. **Wenn Sie Nein wählen:** ```plaintext If you change your mind, run it for yourself with: "npm @capgo/cli@latest channel add production {appId} --default" ``` Sie müssen Kanäle später manuell erstellen und konfigurieren. Alternativ können Sie: * Den Kanal in Ihrer `capacitor.config.ts` Datei festlegen * Die JavaScript `setChannel()` Methode verwenden, um den Kanal dynamisch festzulegen * Kanäle später über die Capgo-Webkonsole konfigurieren ### Schritt 4: Updater-Plugin installieren [Section titled “Schritt 4: Updater-Plugin installieren”](#schritt-4-updater-plugin-installieren) Die CLI installiert das `@capgo/capacitor-updater` Plugin, das mit Ihrer Capacitor-Version kompatibel ist. ```plaintext ❓ Automatic Install "@capgo/capacitor-updater" dependency in {appId}? ``` **Versionskompatibilität:** * **Capacitor 5**: Installiert `@capgo/capacitor-updater` v5 * **Capacitor 6**: Installiert `@capgo/capacitor-updater` v6 * **Capacitor 7**: Installiert `@capgo/capacitor-updater` v7 * **Capacitor 8+**: Installiert die neueste Version Caution Capgo unterstützt nur Capacitor v5 und höher. Wenn Sie eine ältere Version verwenden, müssen Sie zuerst aktualisieren. **Sofortige Updates-Option:** Nach der Installation werden Sie gefragt: ```plaintext ❓ Do you want to set instant updates in {appId}? Read more: https://capgo.app/docs/live-updates/update-behavior/#applying-updates-immediately ``` Tip **Was sind sofortige Updates?** Mit aktivierten sofortigen Updates wendet Ihre App Updates sofort an, wenn sie in den Hintergrund verschoben und wieder geöffnet wird. Dies funktioniert nahtlos, da Capgo Updates weltweit in unter 300ms verteilen kann. Ohne diese Option (Standardmodus) werden Updates im Hintergrund heruntergeladen und beim nächsten App-Neustart angewendet. Sofortige Updates sind ideal für schnellere Iteration während der Entwicklung und kritische Fehlerbehebungen in der Produktion. **Wenn Sie Ja wählen:** * Updates werden so konfiguriert, dass sie sofort angewendet werden, wenn die App in den Hintergrund verschoben und wieder geöffnet wird * `directUpdate: 'always'` und `autoSplashscreen: true` werden zu Ihrer Konfiguration hinzugefügt * Ihre `capacitor.config.ts` wird automatisch aktualisiert * **Delta-Updates** werden automatisch aktiviert - dies sendet nur die Dateien, die sich zwischen Updates geändert haben, anstelle des vollständigen Bundles, was Updates viel schneller macht **Wenn Sie Nein wählen:** * Updates verwenden das Standardverhalten (Download im Hintergrund, Anwendung beim nächsten Neustart) * Sie können sofortige Updates später jederzeit aktivieren, indem Sie Ihre `capacitor.config.ts` ändern ### Schritt 5: Integrationscode hinzufügen [Section titled “Schritt 5: Integrationscode hinzufügen”](#schritt-5-integrationscode-hinzufügen) Die CLI fügt automatisch den erforderlichen Code in Ihre Hauptanwendungsdatei ein. ```plaintext ❓ Automatic Add "CapacitorUpdater.notifyAppReady()" code and import in {appId}? ``` **Was hinzugefügt wird:** ```typescript import { CapacitorUpdater } from '@capgo/capacitor-updater' CapacitorUpdater.notifyAppReady() ``` **Projekttypenerkennung:** * **Nuxt.js**: Erstellt `plugins/capacitorUpdater.client.ts` * **Andere Frameworks**: Fügt zu Ihrer Haupteingabedatei hinzu Tip **Wenn die automatische Injektion fehlschlägt**, können Sie den Code manuell zu Ihrer Hauptanwendungsdatei hinzufügen: **Für Nuxt.js:** Erstellen Sie `plugins/capacitorUpdater.client.ts`: ```typescript import { CapacitorUpdater } from '@capgo/capacitor-updater' export default defineNuxtPlugin(() => { CapacitorUpdater.notifyAppReady() }) ``` **Für andere Frameworks:** Fügen Sie zu Ihrer Haupteingabedatei hinzu (z.B. `main.ts`, `index.js`, `App.tsx`): ```typescript import { CapacitorUpdater } from '@capgo/capacitor-updater' CapacitorUpdater.notifyAppReady() ``` Platzieren Sie diesen Code nach Ihren Imports und vor Ihrer App-Initialisierung. Weitere Details finden Sie im [App hinzufügen Leitfaden](/docs/getting-started/add-an-app/). ### Schritt 6: Verschlüsselung einrichten (Optional) [Section titled “Schritt 6: Verschlüsselung einrichten (Optional)”](#schritt-6-verschlüsselung-einrichten-optional) End-to-End-Verschlüsselung fügt eine zusätzliche Sicherheitsebene für Ihre Updates hinzu. ```plaintext 🔐 End-to-end encryption ✅ Use this for: Banking, healthcare, or apps with legal encryption requirements ⚠️ Note: Makes debugging harder - skip if you don't need it ❓ Enable end-to-end encryption for {appId} updates? ``` Note Verschlüsselung ist nur für Capacitor v6 und höher verfügbar. Wenn Sie Verschlüsselung aktivieren, wird die CLI: 1. Verschlüsselungsschlüssel generieren 2. Anbieten, Ihre Capacitor-Konfiguration zu synchronisieren ### Schritt 7: Plattform auswählen [Section titled “Schritt 7: Plattform auswählen”](#schritt-7-plattform-auswählen) Wählen Sie aus, mit welcher Plattform Sie während des Onboardings testen möchten. ```plaintext 📱 Platform selection for onboarding This is just for testing during onboarding - your app will work on all platforms ❓ Which platform do you want to test with during this onboarding? Options: - iOS - Android ``` Tip Dies betrifft nur den Onboarding-Prozess. Ihre endgültige App unterstützt alle Plattformen. ### Schritt 8: Ihr Projekt erstellen [Section titled “Schritt 8: Ihr Projekt erstellen”](#schritt-8-ihr-projekt-erstellen) Die CLI erstellt Ihre App und synchronisiert sie mit Capacitor. ```plaintext ❓ Automatic build {appId} with "npm run build"? ``` **Was passiert:** 1. Erkennt Ihren Projekttyp 2. Führt Ihr Build-Skript aus 3. Führt `npx cap sync {platform}` aus **Wenn das Build-Skript fehlt:** Sie werden gefragt, ob Sie den Build überspringen oder ein Build-Skript zu Ihrer `package.json` hinzufügen möchten. ### Schritt 9: Auf Gerät ausführen [Section titled “Schritt 9: Auf Gerät ausführen”](#schritt-9-auf-gerät-ausführen) Testen Sie die Anfangsversion Ihrer App auf einem Gerät oder Simulator. ```plaintext ❓ Run {appId} on {PLATFORM} device now to test the initial version? ``` Wenn Sie **Ja** wählen: ```plaintext (spinner) Running: npx cap run {platform} (device picker appears) App started ✅ 📱 Your app should now be running on your {platform} device with Capgo integrated 🔄 This is your baseline version - we'll create an update next ``` ### Schritt 10: Teständerung vornehmen [Section titled “Schritt 10: Teständerung vornehmen”](#schritt-10-teständerung-vornehmen) Jetzt ist es Zeit, das Update-System von Capgo zu testen, indem Sie eine sichtbare Änderung vornehmen. ```plaintext 🎯 Now let's test Capgo by making a visible change and deploying an update! ❓ How would you like to test the update? Options: - Auto: Let Capgo CLI make a visible change for you - Manual: I'll make changes myself ``` **Auto-Modus:** Die CLI ändert automatisch Ihre Dateien, um ein sichtbares Test-Banner oder eine Änderung hinzuzufügen. **Manueller Modus:** Sie nehmen Ihre eigenen Änderungen vor (z.B. Text ändern, Farben oder Elemente hinzufügen). **Versionsverwaltung:** ```plaintext ❓ How do you want to handle the version for this update? Options: - Auto: Bump patch version ({currentVersion} → {nextVersion}) - Manual: I'll provide the version number ``` **Mit Änderungen erstellen:** ```plaintext ❓ Build {appId} with changes before uploading? ``` Tip Wenn Sie manuell in einem anderen Terminal erstellen müssen, wählen Sie “Nein” und erstellen Sie selbst, dann fahren Sie fort. ### Schritt 11: Bundle hochladen [Section titled “Schritt 11: Bundle hochladen”](#schritt-11-bundle-hochladen) Laden Sie Ihr aktualisiertes App-Bundle zu Capgo hoch. ```plaintext ❓ Upload the updated {appId} bundle (v{version}) to Capgo? ``` Die CLI führt aus: ```bash npx @capgo/cli@latest bundle upload ``` Tip **Delta-Updates mit Direct Update:** Wenn Sie sofortige Updates (Direct Update) in Schritt 4 aktiviert haben, fragt die CLI automatisch, ob Sie Delta-Updates aktivieren möchten. Delta-Updates senden nur die Dateien, die sich zwischen Versionen geändert haben, anstelle des gesamten Bundles. Da sich normalerweise nur wenige Dateien zwischen Updates ändern, macht dies Downloads viel schneller. Wählen Sie **Ja** für die beste Erfahrung mit sofortigen Updates. **Delta-Updates-Eingabeaufforderung (wenn Direct Update aktiviert ist):** ```plaintext 💡 Direct Update (instant updates) is enabled in your config Delta updates send only changed files instead of the full bundle ❓ Enable delta updates for this upload? (Recommended with Direct Update) ``` Caution Für Monorepos müssen Sie möglicherweise zusätzliche Pfade zu Ihrer `package.json` und `node_modules` angeben. **Erfolg:** ```plaintext ✅ Update v{version} uploaded successfully! 🎉 Your updated bundle is now available on Capgo ``` ### Schritt 12: Update auf Gerät testen [Section titled “Schritt 12: Update auf Gerät testen”](#schritt-12-update-auf-gerät-testen) Zeit, das Update in Aktion zu sehen! ```plaintext 🧪 Time to test the Capgo update system! 📱 Go to your device where the app is running ``` **Für sofortige Updates:** ```plaintext 🔄 IMPORTANT: Background your app (swipe up/press home button) and then reopen it ⏱️ The update should be downloaded and applied automatically ``` **Für Standard-Updates:** ```plaintext 📱 With standard updates, you will need to: 1. Background the app (swipe up/press home button) to start download 2. Wait a few seconds for download to complete 3. Background and foreground again to see the update ``` **Protokolle überwachen:** ```plaintext ❓ Monitor Capgo logs to verify the update worked? ``` Wenn Sie **Ja** wählen, sehen Sie Live-Protokolle von Ihrem Gerät, die den Update-Prozess zeigen. ### Schritt 13: Abschluss [Section titled “Schritt 13: Abschluss”](#schritt-13-abschluss) ```plaintext Welcome onboard ✈️! ``` Herzlichen Glückwunsch! Sie haben erfolgreich Capgo Live-Updates für Ihre App eingerichtet. ## Was Sie erreicht haben [Section titled “Was Sie erreicht haben”](#was-sie-erreicht-haben) Nach Abschluss des Onboardings haben Sie: ✅ App registriert Ihre App ist in Capgo mit einem Produktionskanal registriert ✅ Plugin installiert Das Capacitor Updater Plugin ist installiert und konfiguriert ✅ Code integriert Integrationscode ist zu Ihrer App hinzugefügt ✅ Update getestet Sie haben erfolgreich ein Live-Update bereitgestellt und empfangen ## Täglicher Workflow [Section titled “Täglicher Workflow”](#täglicher-workflow) Für spätere Updates verwenden Sie: ```bash npm run build npx @capgo/cli@latest bundle upload --channel=production ``` Weitere Bereitstellungsoptionen finden Sie unter [Live-Update bereitstellen](/docs/getting-started/deploy/). ## Onboarding fortsetzen [Section titled “Onboarding fortsetzen”](#onboarding-fortsetzen) Wenn Sie den Onboarding-Prozess beenden, können Sie jederzeit fortfahren: ```bash npx @capgo/cli@latest init [APIKEY] ``` Sie sehen: ```plaintext You have already got to the step {stepNumber}/13 in the previous session ❓ Would you like to continue from where you left off? ``` Tip Der Fortschritt wird lokal gespeichert, sodass Sie den Onboarding-Prozess sicher beenden und fortsetzen können. ## Fehlerbehebung [Section titled “Fehlerbehebung”](#fehlerbehebung) ### Keine Entwicklungsumgebung [Section titled “Keine Entwicklungsumgebung”](#keine-entwicklungsumgebung) **Problem:** Weder Xcode noch Android SDK wird erkannt. **Lösung:** * **Für iOS**: Installieren Sie [Xcode](https://developer.apple.com/xcode/) (nur macOS) * **Für Android**: Installieren Sie [Android Studio](https://developer.android.com/studio) ### App-ID bereits vergeben [Section titled “App-ID bereits vergeben”](#app-id-bereits-vergeben) **Problem:** Ihre App-ID ist bereits registriert. **Lösung:** Wählen Sie eine der vorgeschlagenen Alternativen oder geben Sie eine benutzerdefinierte App-ID in Reverse-Domain-Notation ein. ### Build-Skript fehlt [Section titled “Build-Skript fehlt”](#build-skript-fehlt) **Problem:** Kein Build-Skript in `package.json` gefunden. **Lösung:** Fügen Sie ein Build-Skript zu Ihrer `package.json` hinzu: ```json { "scripts": { "build": "your-build-command" } } ``` ### Auto-Injektion fehlgeschlagen [Section titled “Auto-Injektion fehlgeschlagen”](#auto-injektion-fehlgeschlagen) **Problem:** Die CLI kann den Integrationscode nicht automatisch einfügen. **Lösung:** Fügen Sie den Code manuell zu Ihrer Hauptdatei hinzu: ```typescript import { CapacitorUpdater } from '@capgo/capacitor-updater' CapacitorUpdater.notifyAppReady() ``` ### Capacitor-Version zu alt [Section titled “Capacitor-Version zu alt”](#capacitor-version-zu-alt) **Problem:** Ihre Capacitor-Version ist unter v5. **Lösung:** Aktualisieren Sie Capacitor auf v5 oder höher: * [Upgrade auf Capacitor 5](https://capacitorjs.com/docs/updating/5-0) * [Upgrade auf Capacitor 6](https://capacitorjs.com/docs/updating/6-0) * [Upgrade auf Capacitor 7](https://capacitorjs.com/docs/updating/7-0) ## Nächste Schritte [Section titled “Nächste Schritte”](#nächste-schritte) Nachdem Sie das Onboarding abgeschlossen haben, erkunden Sie diese Themen: [ Updates bereitstellen](/docs/getting-started/deploy/) [Erfahren Sie, wie Sie Updates vom Capgo-Dashboard bereitstellen](/docs/getting-started/deploy/) [ Update-Typen](/docs/live-updates/update-types/) [Referenz aller OTA-Update-Typen: Anwendungszeitpunkt, Verzögerungsbedingungen, Versionsblockierung und Bereitstellung](/docs/live-updates/update-types/) [ CI/CD-Integration](/docs/getting-started/cicd-integration/) [Automatisieren Sie Ihre Update-Bereitstellungen mit CI/CD](/docs/getting-started/cicd-integration/) [ Kanäle](/docs/live-updates/channels/) [Verwalten Sie mehrere Update-Streams mit Kanälen](/docs/live-updates/channels/) [ Verschlüsselung](/docs/live-updates/encryption/) [Sichern Sie Ihre Updates mit End-to-End-Verschlüsselung](/docs/live-updates/encryption/) [ Update-Verhalten](/docs/live-updates/update-behavior/) [Passen Sie an, wann und wie Updates angewendet werden (direct, delta, etc.)](/docs/live-updates/update-behavior/) ## Hilfe erhalten [Section titled “Hilfe erhalten”](#hilfe-erhalten) Wenn Sie während des Onboardings auf Probleme stoßen: * Überprüfen Sie den [Fehlerbehebungsleitfaden](/docs/getting-started/troubleshooting/) * Treten Sie der [Discord-Community](https://discord.capgo.app) bei * Überprüfen Sie die [FAQ](/docs/faq/) * Kontaktieren Sie den [Support](/docs/getting-help/) # Überblick > Erste Schritte mit Capgo durch Erlernen der wichtigsten Konzepte und Schritte zur Integration und Bereitstellung von Live-Updates für Ihre App. Das Quickstart-Tutorial führt Sie durch die wichtigsten Konzepte von Capgo! Zu den behandelten Konzepten gehören: 1. Hinzufügen einer App zu Ihrem Capgo-Konto 2. Integration von Capgo in Ihre CI/CD 3. Auslösen des Bundle-Uploads auf Capgo durch Pushen von Commits 4. Konfiguration und Anpassung der Capgo Bundle-Veröffentlichung 5. Einrichten Ihrer App für Live-Updates über Capgo 6. Bereitstellung von Live-Updates für Ihre App von Capgo aus Folgen Sie einfach der Anleitung Schritt für Schritt oder navigieren Sie direkt zur Dokumentation der Komponente, die Sie interessiert. [ Tutorial starten](/docs/getting-started/add-an-app/) [Folgen Sie dem Quickstart-Tutorial und nutzen Sie Capgo in kürzester Zeit!](/docs/getting-started/add-an-app/) [ CLI Onboarding-Leitfaden](/docs/getting-started/onboarding/) [Vollständiger Schritt-für-Schritt-Leitfaden für den interaktiven CLI-Onboarding-Prozess.](/docs/getting-started/onboarding/) [ Updates ausliefern](/docs/getting-started/deploy/) [Liefern Sie Updates für Ihre App über das Capgo-Dashboard aus.](/docs/getting-started/deploy/) [ Updates automatisieren](/docs/getting-started/cicd-integration/) [Integrieren Sie Capgo in Ihre CI/CD und lösen Sie Bundle-Uploads auf Capgo durch Pushen von Commits aus.](/docs/getting-started/cicd-integration/) [ Fehlerbehebung](/docs/getting-started/troubleshooting/) [Häufige Probleme und deren Lösungen.](/docs/getting-started/troubleshooting/) [ Zusammenfassung](/docs/getting-started/wrapping-up/) [Schließen Sie das Tutorial ab und erhalten Sie einen schnellen Überblick über das Gelernte.](/docs/getting-started/wrapping-up/) Tip Die Over-the-Air (OTA) Update-Funktion gilt nur für Änderungen an HTML-, CSS- und JavaScript-Dateien. Wenn Sie Änderungen am nativen Code vornehmen, wie z.B. Updates von Capacitor-Plugins, müssen Sie die Anwendung erneut zur Genehmigung im App Store einreichen. ## Discord Community beitreten [Section titled “Discord Community beitreten”](#discord-community-beitreten) [Treten Sie dem Capgo Discord Server bei!](https://discord.capgo.app) ## Wartung [Section titled “Wartung”](#wartung) | Plugin-Version | Capacitor-Kompatibilität | Gewartet | | ------------------ | ------------------------ | ---------------------------------------------------------------------- | | v7.\*.\* (≥7.25.0) | v7.\*.\* | ✅ Vollständig unterstützt | | v6.\*.\* (≥6.25.0) | v6.\*.\* | ✅ Vollständig unterstützt | | v5.\*.\* (≥5.10.0) | v5.\*.\* | ✅ Vollständig unterstützt | | v5.\*.\* (<5.10.0) | v5.\*.\* | ⚠️ Veraltet | | v4.\*.\* | v4.\*.\* | ❌ Nicht mehr unterstützt | | v3.\*.\* | v3.\*.\* | ❌ Nicht mehr unterstützt | | >= 8 | v4.\*.\* | ⚠️ Veraltet aufgrund von Versionierungsproblemen in unserem CI-Prozess | ## Einhaltung der Store-Richtlinien [Section titled “Einhaltung der Store-Richtlinien”](#einhaltung-der-store-richtlinien) Der Android Google Play und der iOS App Store haben entsprechende Richtlinien mit Regeln, die Sie kennen sollten, bevor Sie die Capacitor-updater-Lösung in Ihre Anwendung integrieren ### Google Play [Section titled “Google Play”](#google-play) Der dritte Absatz zum Thema [Geräte- und Netzwerkmissbrauch](https://supportgooglecom/googleplay/android-developer/answer/9888379/?hl=en) beschreibt, dass die Aktualisierung von Quellcode durch andere Methoden als den Update-Mechanismus von Google Play eingeschränkt ist. Diese Einschränkung gilt jedoch nicht für die Aktualisierung von JavaScript-Bundles > Diese Einschränkung gilt nicht für Code, der in einer virtuellen Maschine ausgeführt wird und eingeschränkten Zugriff auf Android-APIs hat (wie JavaScript in einer Webview oder einem Browser) Das erlaubt Capacitor-updater vollständig, da es nur die JS-Bundles aktualisiert und keinen nativen Code ändert ### App Store [Section titled “App Store”](#app-store) Absatz **332** des [Apple Developer Program License Agreement](https://developer.apple.com/programs/ios/information/) erlaubt seit 2015 vollständig Over-the-Air-Updates von JavaScript und Assets - und in der neuesten Version (20170605) [hier herunterladbar](https://developer.apple.com/terms/) ist diese Regelung sogar noch umfassender: > Interpretierter Code kann in eine Anwendung heruntergeladen werden, solange dieser Code: (a) den primären Zweck der Anwendung nicht durch Funktionen oder Funktionalitäten ändert, die mit dem beabsichtigten und beworbenen Zweck der Anwendung, wie sie im App Store eingereicht wurde, nicht übereinstimmen, (b) keinen Store oder Storefront für anderen Code oder Anwendungen erstellt, und (c) keine Signierung, Sandbox oder andere Sicherheitsfunktionen des Betriebssystems umgeht Capacitor-updater ermöglicht es Ihnen, diese Regeln vollständig einzuhalten, solange das Update, das Sie pushen, nicht wesentlich von der ursprünglichen, vom App Store genehmigten Absicht Ihres Produkts abweicht Um weiterhin die Apple-Richtlinien einzuhalten, empfehlen wir, dass im App Store vertriebene Apps das Szenario ‘Erzwungenes Update’ nicht aktivieren, da in den [App Store Review Guidelines](https://developer.apple.com/app-store/review/guidelines/) steht: > Apps dürfen Benutzer nicht zwingen, die App zu bewerten, die App zu überprüfen, andere Apps herunterzuladen oder ähnliche Aktionen durchzuführen, um auf Funktionen, Inhalte oder die Nutzung der App zugreifen zu können Dies ist kein Problem für das Standardverhalten des Hintergrund-Updates, da es den Benutzer nicht zwingt, die neue Version anzuwenden, bis er die App das nächste Mal schließt, aber Sie sollten sich dieser Rolle bewusst sein, wenn Sie sich entscheiden, sie anzuzeigen ## Open Source [Section titled “Open Source”](#open-source) Das Plugin steht unter der LGPL-3.0 Lizenz und das Backend unter der AGPL-3.0 Lizenz > 💡 LGPL-3.0 bedeutet, wenn jemand den Code des Plugins modifiziert, ist es verpflichtend, ihn als Open Source mit der gleichen Lizenzierung zu veröffentlichen. Wenn Sie den Code ohne Modifikation verwenden, betrifft Sie das nicht. Weitere Details finden Sie im Issue unten, siehe Link 👇 [Lizenzierung? ](https://github.com/Cap-go/capacitor-updater/issues/7) [Probieren Sie GPTS Capgo aus, um Hilfe zu erhalten, anstatt die Dokumentation zu lesen ](https://chatopenaicom/g/g-3dMwHbF2w-capgo-doc-gpt) > Sie können es ohne Bedenken in Ihre App einbinden ## Abschließende Bemerkungen [Section titled “Abschließende Bemerkungen”](#abschließende-bemerkungen) Wenn Sie selbst hosten und dieses Tool nützlich finden, erwägen Sie bitte, meine Arbeit zu unterstützen, indem Sie [GitHub Sponsor](https://github.com/sponsors/riderx/) werden Ich habe mich entschieden, den gesamten Code, den ich hier entwickelt habe, als Open Source zur Verfügung zu stellen, anstatt ihn hinter einer Bezahlschranke zu verstecken. Ich glaube, dass wir durch Öffnung statt Kampf und Verstecken die Welt zu einem besseren Ort machen können Um dies zu ermöglichen, müssen wir alle unseren Teil beitragen, auch Sie 🥹 Wenn Capgo Cloud Ihre Bedürfnisse nicht erfüllt, können Sie einen bootstrapped Maker [hier](https://github.com/sponsors/riderx/) zu Ihren eigenen Bedingungen unterstützen ## Einfache Rechnung [Section titled “Einfache Rechnung”](#einfache-rechnung) Der Preis des Basic-Plans: 14€\*12 = 168€ pro Jahr Während durchschnittliche Entwicklerkosten/Stunde = 60€ Das bedeutet, dass 3 verschwendete Entwicklerstunden beim Self-Hosting es Ihnen ermöglichen, ein ganzes Jahr zu bezahlen. Wenn Sie mehr als 3 Stunden aufwenden, verlieren Sie Geld ^^ # Fehlerbehebung > Lösen Sie häufige Probleme bei der Verwendung von Capgo mit detaillierten Fehlerbehebungsschritten und erweiterten Optionen für Upload und Debugging. Hier sind einige häufige Probleme, auf die Sie bei der Verwendung von Capgo stoßen könnten, und wie Sie diese lösen können 🚀 Benötigen Sie Experten-Hilfe? Stecken Sie bei einem komplexen Problem fest? Unser Expertenteam ist hier, um zu helfen! Erhalten Sie persönlichen Support, Code-Reviews und maßgeschneiderte Lösungen für Ihre spezifischen Bedürfnisse. [Professionellen Support erhalten](/consulting/) ### Upload-Fehler [Section titled “Upload-Fehler”](#upload-fehler) Wenn Ihr Bundle-Upload fehlschlägt, überprüfen Sie: * Ihre App-ID in `capacitor.config.ts` stimmt mit Ihrer App im Capgo-Dashboard überein * Sie führen den Upload-Befehl vom Root-Verzeichnis Ihres Capacitor-Projekts aus * Ihre Web-Assets sind gebaut und aktuell #### Erweiterte Upload-Optionen [Section titled “Erweiterte Upload-Optionen”](#erweiterte-upload-optionen) Die Capgo CLI bietet einige zusätzliche Flags zur Lösung häufiger Upload-Probleme: * `--tus`: Verwendet das [tus resumable upload protocol](https://tusio/) für zuverlässigere Uploads von großen Bundles oder bei schlechten Netzwerkverbindungen. Wenn Ihr Bundle über 10MB groß ist oder Sie eine instabile Verbindung haben, erwägen Sie die Verwendung von `--tus`: ```shell npx @capgo/cli@latest bundle upload --tus ``` * `--package-json` und `--node-modules`: Teilt Capgo mit, wo Ihre root `package.json` und `node_modules` zu finden sind, wenn Ihre App eine nicht-standardmäßige Struktur wie ein Monorepo oder npm Workspace verwendet. Übergeben Sie den Pfad zur root `package.json` und den `--node_modules` Pfad: ```shell npx @capgo/cli@latest bundle upload --package-json=path/to/package.json --node_modules=path/to/node_modules ``` Capgo benötigt diese Informationen, um die Abhängigkeiten Ihrer App korrekt zu bündeln. Sie können diese Flags mit anderen Optionen wie `--channel` nach Bedarf kombinieren. Siehe die [Capgo CLI Dokumentation](/docs/cli/overview/) für vollständige Details zu den verfügbaren Upload-Optionen. Wenn Sie immer noch Probleme mit Uploads haben, wenden Sie sich an den [Capgo Support](https://support.capgo.app) für weitere Unterstützung. ### Updates debuggen [Section titled “Updates debuggen”](#updates-debuggen) Wenn Sie Probleme mit Live-Updates haben, ist der Capgo Debug-Befehl ein hilfreiches Tool zur Fehlerbehebung. So verwenden Sie es: 1. Führen Sie den folgenden Befehl in Ihrem Projektverzeichnis aus: ```shell npx @capgo/cli@latest app debug ``` 2. Starten Sie Ihre App auf einem Gerät oder Emulator und führen Sie die Aktion aus, die ein Update auslösen sollte (z.B. die App nach dem Hochladen eines neuen Bundles neu öffnen) 3. Beobachten Sie die Ausgabe des Debug-Befehls. Er protokolliert Informationen über den Update-Prozess, einschließlich: * Wann die App nach einem Update sucht * Ob ein Update gefunden wurde und welche Version es ist * Download- und Installationsfortschritt für das Update * Alle Fehler, die während des Update-Prozesses auftreten 4. Verwenden Sie die Debug-Logs, um zu identifizieren, wo das Problem auftritt. Zum Beispiel: * Wenn kein Update gefunden wird, überprüfen Sie, ob Ihr Bundle erfolgreich hochgeladen wurde und die App für den richtigen Kanal konfiguriert ist * Wenn das Update heruntergeladen, aber nicht installiert wird, stellen Sie sicher, dass Sie `CapacitorUpdater.notifyAppReady()` aufgerufen haben und die App vollständig geschlossen und neu geöffnet wurde * Wenn Sie eine Fehlermeldung sehen, suchen Sie nach diesem spezifischen Fehler in der Capgo-Dokumentation oder wenden Sie sich an den Support Der Debug-Befehl ist besonders nützlich, um Probleme mit dem Update-Download und Installationsprozess zu identifizieren. Wenn die Logs zeigen, dass die erwartete Update-Version gefunden, aber nicht letztendlich angewendet wurde, konzentrieren Sie Ihre Fehlerbehebung auf die Schritte nach dem Download. ### Debugging mit nativen Logs [Section titled “Debugging mit nativen Logs”](#debugging-mit-nativen-logs) Zusätzlich zum Capgo Debug-Befehl können die nativen Logs auf Android und iOS wertvolle Fehlerbehebungsinformationen liefern, besonders für Probleme auf der nativen Seite des Update-Prozesses. #### Android Logs [Section titled “Android Logs”](#android-logs) So greifen Sie auf die Android-Logs zu: 1. Verbinden Sie Ihr Gerät oder starten Sie Ihren Emulator 2. Öffnen Sie Android Studio und wählen Sie “View > Tool Windows > Logcat” 3. Im Logcat-Fenster filtern Sie die Logs auf den Prozess Ihrer App, indem Sie ihn aus dem Dropdown-Menü oben auswählen 4. Suchen Sie nach Zeilen, die `Capgo` enthalten, um die SDK-Logs zu finden Alternativ können Sie den `adb logcat` Befehl verwenden und nach `Capgo` grep’en, um die Logs zu filtern. Das Capgo SDK protokolliert wichtige Ereignisse während des Update-Prozesses, wie zum Beispiel: * Wann eine Update-Prüfung initiiert wird * Ob ein Update gefunden wurde und welche Version es ist * Wann der Update-Download startet und abgeschlossen ist * Wann die Update-Installation ausgelöst wird * Alle Fehler, die während der nativen Update-Schritte auftreten Häufige Android-spezifische Probleme, die Sie in den Logs sehen könnten, sind: * Netzwerkverbindungsprobleme, die den Update-Download verhindern * Dateiberechtigungsfehler beim Speichern oder Lesen des Update-Bundles * Nicht genügend Speicherplatz für das Update-Bundle * Fehler beim Neustart der App nach der Update-Installation #### iOS Logs [Section titled “iOS Logs”](#ios-logs) So greifen Sie auf die iOS-Logs zu: 1. Verbinden Sie Ihr Gerät oder starten Sie Ihren Simulator 2. Öffnen Sie Xcode und gehen Sie zu “Window > Devices and Simulators” 3. Wählen Sie Ihr Gerät und klicken Sie auf “Open Console” 4. Suchen Sie in der Konsolenausgabe nach Zeilen, die `Capgo` enthalten, um die SDK-Logs zu finden Sie können auch den `log stream` Befehl im Terminal verwenden und nach `Capgo` grep’en, um die Logs zu filtern. Ähnlich wie bei Android protokolliert das Capgo SDK wichtige iOS-seitige Ereignisse: * Update-Prüfung Initiierung und Ergebnis * Download-Start, Fortschritt und Abschluss * Installations-Trigger und Ergebnis * Alle Fehler während des nativen Update-Prozesses iOS-spezifische Probleme, die Sie in den Logs identifizieren könnten, sind: * SSL-Zertifikatsprobleme beim Herunterladen des Updates * App Transport Security blockiert den Update-Download * Unzureichender Speicherplatz für das Update-Bundle * Fehler beim korrekten Extrahieren oder Anwenden des Update-Bundles Auf beiden Plattformen bieten die nativen Logs einen tieferen Einblick in den Update-Prozess mit mehr Details zur nativen Implementierung. Sie sind besonders nützlich für die Identifizierung von Problemen, die außerhalb der Capgo JavaScript-Schicht auftreten. Bei der Fehlerbehebung eines kniffligen Live-Update-Problems ist es eine gute Idee, sowohl die Capgo Debug-Logs als auch die nativen Logs für ein umfassendes Bild der Situation zu erfassen. Die beiden Logs zusammen geben Ihnen die beste Chance, das Problem zu identifizieren und zu lösen. ### Updates werden nicht angewendet [Section titled “Updates werden nicht angewendet”](#updates-werden-nicht-angewendet) Wenn Sie ein Bundle hochgeladen haben, aber die Änderungen auf Ihrem Gerät nicht sehen: * Stellen Sie sicher, dass Sie `CapacitorUpdater.notifyAppReady()` in Ihrem App-Code aufgerufen haben, wie im [Quickstart](/docs/getting-started/quickstart) gezeigt * Prüfen Sie, ob Ihr Gerät mit dem Internet verbunden ist und die Capgo Debug-Logs zeigen, dass das Update heruntergeladen wurde * Versuchen Sie, die App vollständig zu schließen und neu zu öffnen, da Updates nur bei einem Neustart angewendet werden * Suchen Sie in den nativen Logs nach Fehlern, die auf ein Problem beim Anwenden des Updates hinweisen könnten Weitere Details zum Update-Prozess finden Sie im [Live-Updates Deployment Guide](/docs/getting-started/deploy). Wenn Sie immer noch Probleme haben, verwenden Sie den `npx @capgo/cli@latest app debug` Befehl und native Logs, um mehr Einblick in das Geschehen zu bekommen. ## SDK-Installation [Section titled “SDK-Installation”](#sdk-installation) Wenn Sie Probleme bei der Installation des Capgo SDK haben, stellen Sie sicher: * Ihre App verwendet eine unterstützte Version von Capacitor (4.0 oder neuer) * Sie sind den [Quickstart](/docs/getting-started/quickstart) Schritten in der richtigen Reihenfolge gefolgt, einschließlich der Synchronisierung Ihrer App nach der Installation des SDK ## CI/CD-Integration [Section titled “CI/CD-Integration”](#cicd-integration) Bei Problemen mit dem Auslösen von Capgo-Uploads aus Ihrer CI/CD-Pipeline: * Überprüfen Sie, ob Ihr Capgo-Authentifizierungstoken korrekt eingerichtet ist * Stellen Sie sicher, dass Sie den Upload-Befehl nach dem Build Ihrer Web-Assets ausführen * Prüfen Sie, ob der Upload-Befehl den richtigen Kanalnamen für Ihre Zielumgebung verwendet Weitere Fehlerbehebungstipps finden Sie in der [CI/CD-Integration](/docs/getting-started/cicd-integration/) Dokumentation. Sie können auch den `npx @capgo/cli@latest app debug` Befehl verwenden, um zu bestätigen, ob Ihre CI/CD-ausgelösten Updates von der App empfangen werden. # Zusammenfassung > Schließen Sie Ihre Capgo-Reise mit einem prägnanten Überblick über wichtige Konzepte und nächste Schritte ab und stellen Sie eine solide Grundlage für zukünftige Erkundungen und Beherrschung sicher. Nachdem Sie die Schnellstartanleitung abgeschlossen haben, sollten Sie ein grundlegendes Verständnis der Hauptkonzepte von Capgo haben! Die wichtigsten Konzepte, die Sie in dieser Anleitung gelernt haben, sind: 1. Hinzufügen einer App zu Ihrem Capgo-Konto 2. Integration von Capgo in Ihre CI/CD-Pipeline 3. Auslösen von Bundle-Uploads zu Capgo bei neuen Commits 4. Konfigurieren Ihrer App zur Aktivierung von Live-Updates mit dem Capgo SDK 5. Bereitstellen von Live-Updates für Ihre App über das Capgo-Dashboard Aber es gibt noch mehr über Capgo zu lernen! Erkunden Sie die Dokumentation weiter oder schauen Sie sich einige dieser wichtigen Themen an: [ CI/CD-Integration](/docs/getting-started/cicd-integration/) [Sie haben bereits eine CI/CD-Pipeline? Erfahren Sie, wie Sie Capgo in Ihren bestehenden Workflow integrieren können.](/docs/getting-started/cicd-integration/) [ Live-Updates](/docs/live-updates/) [Tauchen Sie tiefer in die Live-Update-Funktionen und Bewährte Methoden von Capgo ein.](/docs/live-updates/) [ FAQ](/docs/faq/) [Finden Sie Antworten auf häufige Fragen zu Capgo.](/docs/faq/) [ Fehlerbehebung](/docs/getting-started/troubleshooting/) [Erhalten Sie Hilfe bei häufigen Problemen, die bei der Verwendung von Capgo auftreten können.](/docs/getting-started/troubleshooting/) # Anleitungen > Ein umfassender Leitfaden für Capgo mit detaillierten Tutorials, hilfreichen Tipps und fortgeschrittenen Techniken zur effektiven Nutzung der Plattform [Wie Versionierung in Capgo funktioniert ](https://capgo.app/blog/how-version-work-in-capgo/)capgo.app [Wie man Major Versionen in Capgo veröffentlicht ](https://capgo.app/blog/how-to-release-major-version-in-capgo/)capgo.app [Wie man spezifische Updates an einen Benutzer oder eine Gruppe sendet ](https://capgo.app/blog/how-to-send-specific-version-to-users/)capgo.app ## CI / CD [Section titled “CI / CD”](#ci--cd) [Automatischer Build und Release mit GitHub Actions ](https://capgo.app/blog/automatic-build-and-release-with-github-actions/)capgo.app [Verwaltung von Entwicklungs- und Produktionsbuilds mit GitHub Actions ](https://capgo.app/blog/automatic-build-and-release-with-github-actions/)capgo.app ## Mitwirken [Section titled “Mitwirken”](#mitwirken) [Beitragen zu Capgo Open Source ](https://github.com/Cap-go/capgo/blob/main/CONTRIBUTING.md)github.com # Übersicht Nutzen Sie Capgos Live Updates-Funktion, um die JavaScript-Bundles Ihrer App remote in Echtzeit zu aktualisieren. Pushen Sie JS-Updates direkt an Ihre Nutzer, ohne den App Store-Überprüfungsprozess zu durchlaufen, um sofort Fehler zu beheben und neue Funktionen bereitzustellen. Note Live Updates sind auf JavaScript-Bundle-Änderungen beschränkt. Wenn Sie nativen Code aktualisieren müssen, wie das Hinzufügen oder Entfernen eines Plugins oder das Ändern der nativen Projektkonfiguration, müssen Sie einen neuen nativen Binary-Build an die App Stores übermitteln. ## Wie Live Updates funktionieren [Section titled “Wie Live Updates funktionieren”](#wie-live-updates-funktionieren) Das Live Update-System von Capgo hat zwei Schlüsselkomponenten: 1. Das Capgo SDK, das Sie in Ihrer App installieren. Das SDK prüft auf verfügbare Updates und lädt diese im Hintergrund herunter. 2. Kanäle, mit denen Sie Updates an bestimmte Benutzergruppen richten können. Sie können Kanäle verwenden, um verschiedene Release-Tracks wie `Production`, `Staging` und `Dev` zu verwalten. Wenn Sie ein neues JS-Bundle zu Capgo hochladen und es einem Kanal zuweisen, erkennt das Capgo SDK in Apps, die für diesen Kanal konfiguriert sind, das Update und lädt es herunter. Beim nächsten Neustart der App wird das neue Bundle geladen. ## Erste Schritte [Section titled “Erste Schritte”](#erste-schritte) Folgen Sie diesen Schritten, um Live Updates zu verwenden: 1. Schließen Sie den [Capgo Quickstart](/docs/getting-started/quickstart) ab, um Ihre App in Capgo einzurichten und das Capgo SDK zu installieren. 2. Rufen Sie in Ihrem App-Code `CapacitorUpdater.notifyAppReady()` auf, nachdem Ihre App initialisiert wurde. Dies teilt dem Capgo SDK mit, dass Ihre App bereit ist, Updates zu empfangen. 3. Erstellen Sie Ihr JS-Bundle und laden Sie es zu Capgo hoch: ```shell npm run build npx @capgo/cli@latest bundle upload --channel=production ``` 4. Öffnen Sie Ihre App und warten Sie, bis das Update heruntergeladen ist. Sie können den Status überprüfen mit: ```shell npx @capgo/cli@latest app debug ``` 5. Sobald das Update heruntergeladen wurde, schließen und öffnen Sie die App erneut, um das neue Bundle zu laden. Weitere Details finden Sie im [Leitfaden zum Bereitstellen von Live Updates](/docs/getting-started/deploy). ## Nächste Schritte [Section titled “Nächste Schritte”](#nächste-schritte) [ Update-Typen](/docs/live-updates/update-types/) [Referenz aller OTA-Update-Typen: Anwendungszeitpunkt, Verzögerungsbedingungen, Versionssperrung und Bereitstellung.](/docs/live-updates/update-types/) [ Kanäle](/docs/live-updates/channels/) [Erfahren Sie, wie Sie Kanäle verwenden, um verschiedene Release-Tracks zu verwalten und Updates an bestimmte Benutzer zu richten](/docs/live-updates/channels/) [ Rollbacks](/docs/live-updates/rollbacks/) [Entdecken Sie, wie Sie zu einer vorherigen JS-Bundle-Version zurückkehren können, wenn ein Update Probleme verursacht](/docs/live-updates/rollbacks/) [ Update-Verhalten](/docs/live-updates/update-behavior/) [Passen Sie an, wie und wann Updates in Ihrer App heruntergeladen und angewendet werden](/docs/live-updates/update-behavior/) [ Schnelle Updates](/docs/live-updates/differentials/) [Erfahren Sie, wie Sie schnelle Updates verwenden können, um den Update-Prozess zu beschleunigen](/docs/live-updates/differentials/) # Breaking Changes > Wie man Breaking Changes mit versionierten Kanälen handhabt Diese Dokumentation erklärt, wie Sie Breaking Changes in Ihrer App mithilfe von versionierten Kanälen handhaben. Dieser Ansatz ermöglicht es Ihnen, verschiedene Versionen Ihrer App zu pflegen und gleichzeitig sicherzustellen, dass Benutzer kompatible Updates erhalten. ## Beispielszenario [Section titled “Beispielszenario”](#beispielszenario) Angenommen, Sie haben: * App-Version 1.2.3 (alte Version) - verwendet Produktionskanal * App-Version 2.0.0 (neue Version mit Breaking Changes) - verwendet v2-Kanal * Live-Update 1.2.4 (kompatibel mit 1.2.3) * Live-Update 2.0.1 (kompatibel mit 2.0.0) ## Strategie: Immer defaultChannel für Major-Versionen verwenden [Section titled “Strategie: Immer defaultChannel für Major-Versionen verwenden”](#strategie-immer-defaultchannel-für-major-versionen-verwenden) **Empfohlener Ansatz:** Legen Sie einen `defaultChannel` für jede Major-Version fest. Dies stellt sicher, dass Sie Updates immer an bestimmte Benutzergruppen pushen können, ohne auf dynamische Kanalzuweisung angewiesen zu sein. ```ts // Version 1.x Releases defaultChannel: 'v1' // Version 2.x Releases defaultChannel: 'v2' // Version 3.x Releases (zukünftig) defaultChannel: 'v3' ``` Tip **Vorteile dieses Ansatzes:** * **Immer Kontrolle** darüber, welche Benutzer Updates erhalten * **Keine dynamische Kanalumschaltung** im App-Code erforderlich * **Klare Trennung** zwischen verschiedenen App-Versionen * **Flexibilität**, Updates an jede spezifische Versionsgruppe zu pushen ## 1. Kanal für neue Version erstellen [Section titled “1. Kanal für neue Version erstellen”](#1-kanal-für-neue-version-erstellen) ```bash # Kanal für Version 2.x erstellen npx @capgo/cli channel create v2 ``` ## 2. Capacitor-Konfiguration für Version 2.0.0 aktualisieren [Section titled “2. Capacitor-Konfiguration für Version 2.0.0 aktualisieren”](#2-capacitor-konfiguration-für-version-200-aktualisieren) Aktualisieren Sie Ihre Capacitor-Konfiguration, bevor Sie Version 2.0.0 für den App Store erstellen: capacitor.config.ts ```ts import { CapacitorConfig } from '@capacitor/cli'; const config: CapacitorConfig = { appId: 'com.example.app', appName: 'Example App', plugins: { CapacitorUpdater: { // ... weitere Optionen defaultChannel: 'v2' // Alle 2.0.0-Benutzer verwenden v2-Kanal } } }; export default config; ``` Note **Für Version 1.x:** Wenn Sie anfangs keinen `defaultChannel` festgelegt haben, sind Version 1.x-Benutzer auf dem `production`-Kanal. Für zukünftige Major-Versionen legen Sie immer einen spezifischen Kanal wie `v3`, `v4` usw. fest. ## 3. Separate Code-Branches verwalten [Section titled “3. Separate Code-Branches verwalten”](#3-separate-code-branches-verwalten) Erstellen Sie separate Git-Branches, um die Kompatibilität zwischen App-Versionen zu gewährleisten: ```bash # Branch für Version 1.x-Updates erstellen und pflegen git checkout -b v1-maintenance git push origin v1-maintenance # Ihr Main-Branch setzt Entwicklung von Version 2.x fort git checkout main ``` **Kritisch:** Pushen Sie niemals JavaScript-Bundles an ältere Apps, die nativen Code/APIs erwarten, die sie nicht haben. Erstellen Sie Updates immer vom entsprechenden Branch: * **v1-maintenance Branch**: Für Updates an 1.x Apps (Produktionskanal) * **main Branch**: Für Updates an 2.x Apps (v2-Kanal) ## 4. Bundles an entsprechende Kanäle hochladen [Section titled “4. Bundles an entsprechende Kanäle hochladen”](#4-bundles-an-entsprechende-kanäle-hochladen) ```bash # Für 1.x Updates: Vom v1-maintenance Branch erstellen git checkout v1-maintenance # Nehmen Sie hier Ihre 1.x-kompatiblen Änderungen vor npx @capgo/cli bundle upload --channel production # Für 2.x Updates: Vom Main-Branch erstellen git checkout main # Nehmen Sie hier Ihre 2.x-Änderungen vor npx @capgo/cli bundle upload --channel v2 ``` ## 5. Selbstzuweisung aktivieren [Section titled “5. Selbstzuweisung aktivieren”](#5-selbstzuweisung-aktivieren) ```bash # Apps erlauben, sich selbst dem v2-Kanal zuzuweisen npx @capgo/cli channel set v2 --self-assign ``` ## 6. Im App Store bereitstellen [Section titled “6. Im App Store bereitstellen”](#6-im-app-store-bereitstellen) Erstellen und stellen Sie Version 2.0.0 im App Store bereit. Alle Benutzer, die diese Version herunterladen (ob neue Benutzer oder bestehende Benutzer, die aktualisieren), werden automatisch den v2-Kanal verwenden, da dies im App-Bundle konfiguriert ist. Note **Keine Code-Änderungen erforderlich!** Da `defaultChannel: 'v2'` mit der App Store-Version gebündelt ist, verwenden alle Benutzer, die Version 2.0.0 herunterladen, automatisch den richtigen Kanal. ## Skalierung auf zukünftige Versionen [Section titled “Skalierung auf zukünftige Versionen”](#skalierung-auf-zukünftige-versionen) Wenn Sie Version 3.0.0 mit weiteren Breaking Changes veröffentlichen: ```bash # Kanal für Version 3.x erstellen npx @capgo/cli channel create v3 ``` ```ts // capacitor.config.ts für Version 3.0.0 const config: CapacitorConfig = { // ... plugins: { CapacitorUpdater: { defaultChannel: 'v3' // Version 3.x-Benutzer } } }; ``` Jetzt können Sie Updates an jede Version pushen: * `production` Kanal → Version 1.x-Benutzer * `v2` Kanal → Version 2.x-Benutzer * `v3` Kanal → Version 3.x-Benutzer ## 7. Bereinigung (Nach Migration) [Section titled “7. Bereinigung (Nach Migration)”](#7-bereinigung-nach-migration) Sobald alle Benutzer auf Version 2.x migriert sind (rechnen Sie mit 3-4 Monaten): 1. Entfernen Sie `defaultChannel` aus Ihrer Capacitor-Konfiguration 2. Löschen Sie den v2-Kanal: ```bash npx @capgo/cli channel delete v2 ``` 3. Löschen Sie den v1-maintenance Branch: ```bash git branch -d v1-maintenance git push origin --delete v1-maintenance ``` Tip Dieser Ansatz stellt sicher, dass Benutzer nur Updates erhalten, die mit ihrer App-Version kompatibel sind Testen Sie Updates in jedem Kanal gründlich vor der Bereitstellung Note Sie können den v2-Kanal in Capgo sicher löschen, auch wenn einige Benutzer noch die Kanalüberschreibung haben. Sie erhalten stattdessen automatisch Updates vom Produktionskanal. ## Version 1.x-Updates pflegen [Section titled “Version 1.x-Updates pflegen”](#version-1x-updates-pflegen) Um Updates zu senden, die mit Version 1.x kompatibel sind: 1. Zum v1-maintenance Branch wechseln: ```bash git checkout v1-maintenance ``` 2. Ihre Änderungen vornehmen und committen: ```bash # 1.x-kompatible Änderungen vornehmen git add . git commit -m "Fix for v1.x" git push origin v1-maintenance ``` 3. Zum Produktionskanal erstellen und hochladen: ```bash npx @capgo/cli bundle upload --channel production ``` Tip Halten Sie Ihren v1-maintenance Branch mit Bugfixes auf dem neuesten Stand, die mit Version 1.x kompatibel sind, aber mergen Sie niemals Breaking Changes vom Main-Branch # Kanäle > Erfahren Sie, wie Sie Live Update-Kanäle in Capgo verwalten und konfigurieren, um nahtlose App-Updates zu ermöglichen, indem Sie spezifische JS-Bundle-Builds an Geräte weiterleiten, die für diese Kanäle konfiguriert sind. Ein Live Update-Kanal verweist auf einen bestimmten JS-Bundle-Build Ihrer App, der mit allen Geräten geteilt wird, die für Updates auf diesen Kanal eingestellt sind. Wenn Sie das [Capgo Live Updates SDK](/docs/getting-started/quickstart/) in Ihrer App installieren, prüft jede native Binary, die für diesen Kanal konfiguriert ist, beim App-Start auf verfügbare Updates. Sie können den Build, auf den ein Kanal verweist, jederzeit ändern und bei Bedarf auch auf vorherige Builds zurücksetzen. ## Wie ein Gerät einen Kanal auswählt (Priorität) [Section titled “Wie ein Gerät einen Kanal auswählt (Priorität)”](#wie-ein-gerät-einen-kanal-auswählt-priorität) Wenn ein Gerät nach einem Update sucht, entscheidet Capgo in dieser strikten Reihenfolge, welcher Kanal verwendet wird (höchste Priorität zuerst): 1. **Erzwungene Gerätezuordnung (Dashboard)** – Ordnen Sie ein bestimmtes Gerät manuell einem Kanal zu. Verwenden Sie dies für dringendes Debugging oder kontrolliertes Testen mit einem einzelnen echten Benutzer. Dies hat immer Vorrang. 2. **Cloud-Override (pro Gerät) über Dashboard oder API** – Wird erstellt, wenn Sie den Kanal des Geräts im Dashboard oder über die API ändern. Verwenden Sie dies für QA-Benutzer, die zwischen Feature-/PR-Kanälen wechseln, oder um ein Benutzerproblem zu reproduzieren. Eine Neuinstallation der Binary löscht dies nicht; das Löschen des Geräteeintrags schon. Sofortiger Kanalwechsel mit setChannel() **Ab Plugin-Version 5.34.0, 6.34.0, 7.34.0 oder 8.0.0** (je nach Ihrer Hauptversion) funktioniert `setChannel()` anders: Es kontaktiert das Backend, um zu **validieren**, dass der Kanal erlaubt ist (prüft, ob Selbstzuweisung für diesen Kanal aktiviert ist), und speichert den Kanal dann **lokal auf dem Gerät** als `defaultChannel`. Das bedeutet, der neue Kanal wird **sofort** für die nächste Update-Prüfung wirksam – kein Warten auf Replikation. Zuvor speicherte `setChannel()` den Kanal-Override in der Backend-Datenbank (wie Dashboard- oder API-Änderungen), und Geräte mussten auf die Datenreplikation warten (bis zu 2 Minuten), bevor der neue Kanal erkannt wurde. Das neue Verhalten liest nur vom Backend (zur Validierung) und speichert lokal, was Kanalwechsel sofort macht. **Hinweis:** Selbst wenn ein Kanal nach dem lokalen Setzen nicht mehr erlaubt ist, validiert das Backend den Kanal weiterhin bei Update-Prüfungen, sodass die Sicherheit gewährleistet bleibt. **Wichtig:** Wenn Kanaländerungen über das Dashboard oder die API vorgenommen werden, gibt es immer noch eine Replikationsverzögerung von bis zu 2 Minuten, bevor alle Edge-Server die Änderung widerspiegeln. Für sofortige Kanalwechsel verwenden Sie `setChannel()` aus Ihrem App-Code – es validiert mit dem Backend und setzt dann den Kanal lokal für sofortige Wirkung. 3. **Capacitor-Konfig `defaultChannel` (Test-Build-Standard)** – Wenn in `capacitor.config.*` vorhanden und kein Force/Override existiert, startet die App auf diesem Kanal (z.B. `beta`, `qa`, `pr-123`). Gedacht für TestFlight-/interne Builds, damit Tester automatisch auf einem Pre-Release-Kanal landen. Produktions-Builds lassen dies normalerweise leer. 4. **Cloud-Standardkanal (Hauptpfad \~99% der Benutzer)** – Wenn Sie im Dashboard einen Standardkanal markieren, werden alle normalen Endbenutzer (kein Force, kein Override, kein Config-defaultChannel) hier angehängt. Ändern Sie ihn, um sofort auszurollen oder zurückzusetzen – keine neue Binary nötig. Wenn Sie plattformspezifische Standards haben (einen nur für iOS, einen nur für Android), landet jedes Gerät auf dem Standard, der seiner Plattform entspricht. Den Cloud-Standard leer zu lassen ist erlaubt; in diesem Fall muss das Gerät über Schritte 1-3 zugeordnet werden, um Updates zu erhalten. Best Practice: * Behandeln Sie 1-3 als Ausnahme-/Test-Layer; wenn Sie einen Cloud-Standard setzen, sollten echte Benutzer darüber fließen. Wenn Sie keinen setzen, seien Sie bewusst darüber, wie Benutzer zugeordnet werden (typischerweise über `defaultChannel` in der Konfig oder gerätespezifische Overrides). * Konfigurieren Sie `defaultChannel` nur in Binaries, die Sie explizit an Tester versenden. Lassen Sie es leer, um die Produktionslogik zentral im Dashboard zu halten. * Verwenden Sie `setChannel()` sparsam in der Produktion – hauptsächlich für QA oder gezielte Diagnosen. Wenn ein Kanal für die Plattform deaktiviert ist (iOS/Android-Schalter), wenn er sonst gewählt würde, überspringt der Auswahlprozess ihn und fährt in der Liste fort. > Zusammenfassung: Force > Override > Config `defaultChannel` > Cloud-Standard. ## Standardkanal-Verhalten [Section titled “Standardkanal-Verhalten”](#standardkanal-verhalten) Das Setzen eines Cloud-Standards ist optional, dient aber normalerweise als Auffangpfad für neue Geräte. Ohne einen erhalten nur Geräte Updates, die über erzwungene Zuordnungen, Overrides oder einen `defaultChannel` in der Capacitor-Konfig zugeordnet sind. Wenn Sie Standards markieren möchten, beachten Sie diese Muster: * **Einzelner Standard (am häufigsten)** – Wenn der Kanal sowohl iOS als auch Android aktiviert hat, wird er zum einzigen Standard; jedes Gerät ohne Overrides wird hier angehängt. * **Plattformspezifische Standards** – Wenn Sie Kanäle nach Plattform aufteilen (z.B. `ios-production` nur mit iOS aktiviert und `android-production` nur mit Android aktiviert), markieren Sie jeden als Standard für seine Plattform. iOS-Geräte gehen zum iOS-Standard, Android-Geräte zum Android-Standard. Denken Sie daran, dass der Cloud-Standard und `defaultChannel` in `capacitor.config.*` beide dieselbe Entscheidungsebene belegen. Wenn Sie einen Cloud-Standard setzen, müssen Sie den Wert nicht in Ihrer Capacitor-Konfig duplizieren – lassen Sie `defaultChannel` für Produktions-Builds leer. Reservieren Sie `defaultChannel` für Binaries, die Sie absichtlich an Tester oder QA versenden, wenn sie auf einem Nicht-Produktionskanal starten sollen, auch wenn der Cloud-Standard anders ist. Sie können Standards jederzeit im Dashboard ändern. Wenn Sie einen Standard wechseln, folgen neue Geräte sofort der neuen Weiterleitung und bestehende Geräte folgen beim nächsten Check-in den normalen Prioritätsregeln. ## Einrichten eines Kanals [Section titled “Einrichten eines Kanals”](#einrichten-eines-kanals) Während des Onboardings erstellen Sie den ersten Kanal (die meisten Teams nennen ihn “Production”), aber nichts ist gesperrt – Sie können jeden Kanal jederzeit umbenennen oder löschen. Um später zusätzliche Kanäle hinzuzufügen: 1. Gehen Sie zum Bereich “Channels” im Capgo Dashboard 2. Klicken Sie auf den Button “New Channel” 3. Geben Sie einen Namen für den Kanal ein und klicken Sie auf “Create” Kanalnamen können frei gewählt werden. Eine gängige Strategie ist es, Kanäle Ihren Entwicklungsphasen zuzuordnen, zum Beispiel: * `Development` - zum Testen von Live-Updates auf lokalen Geräten oder Emulatoren * `QA` - für Ihr QA-Team zur Überprüfung von Updates vor der breiten Veröffentlichung * `Staging` - für finale Tests in einer produktionsähnlichen Umgebung * `Production` - für die Version Ihrer App, die Endbenutzer aus den App Stores erhalten ## Konfigurieren des Kanals in Ihrer App [Section titled “Konfigurieren des Kanals in Ihrer App”](#konfigurieren-des-kanals-in-ihrer-app) Nachdem Sie Ihre Kanäle erstellt haben, müssen Sie Ihre App so konfigurieren, dass sie auf den entsprechenden Kanal hört. In diesem Beispiel verwenden wir den `Development`-Kanal. Öffnen Sie Ihre `capacitor.config.ts` (oder `capacitor.config.json`) Datei. Setzen Sie unter dem `plugins`-Abschnitt optional `defaultChannel` für **Test-Builds** (intern / QA). Für Produktions-Builds bevorzugen Sie, es wegzulassen, damit Geräte den Cloud-Standard verwenden, es sei denn, sie werden explizit überschrieben. ```ts import { CapacitorConfig } from '@capacitor/cli'; const config: CapacitorConfig = { plugins: { CapacitorUpdater: { // Für einen QA/TestFlight-Build – Tester starten automatisch auf dem Development-Kanal. defaultChannel: 'Development', // Produktions-Builds lassen dies normalerweise weg, damit Benutzer sich an den Cloud-Standardkanal anhängen. }, }, }; ``` Bauen Sie anschließend Ihre Web-App und führen Sie `npx cap sync` aus, um die aktualisierte Konfigurationsdatei in Ihre iOS- und Android-Projekte zu kopieren. Wenn Sie diesen Sync-Schritt überspringen, verwenden Ihre nativen Projekte weiterhin den Kanal, für den sie zuvor konfiguriert waren. Caution Kanalauswahlreihenfolge: Force > Override (`setChannel` / Dashboard) > Config `defaultChannel` > Cloud-Standard. Verwenden Sie `defaultChannel` nur in Test-/internen Builds; lassen Sie es für die Produktion weg, damit Benutzer dem Cloud-Standard folgen (wenn gesetzt), anstatt die Weiterleitung in der nativen Konfig zu duplizieren. Sie können ein Gerät später noch erzwingen (pinnen) oder einen Override anwenden – diese überschreiben sofort den Konfig-Wert. > Kanalnamen unterscheiden Groß- und Kleinschreibung. ## Kanaloptionen und Strategien [Section titled “Kanaloptionen und Strategien”](#kanaloptionen-und-strategien) Kanäle haben mehrere Optionen, die steuern, wer Updates erhalten kann und wie Updates geliefert werden. Die wichtigsten sind unten aufgeführt. Sie können diese über die Web-App, die CLI oder die Public API konfigurieren. * Standardkanal: Markieren Sie optional den Kanal oder plattformspezifische Kanäle, an die sich neue Geräte anhängen. Siehe “Standardkanal-Verhalten” für Weiterleitungsszenarien. * Plattformfilter: Aktivieren oder deaktivieren Sie die Zustellung an `iOS`- und/oder `Android`-Geräte pro Kanal. * Auto-Downgrade unter Native deaktivieren: Verhindert das Senden eines Updates, wenn die native App-Version des Geräts neuer ist als das Bundle des Kanals (z.B. Gerät auf 1.2.3, während der Kanal 1.2.2 hat). * Development-Builds erlauben: Ermöglicht Updates für Development-Builds (nützlich zum Testen). * Emulator-Geräte erlauben: Ermöglicht Updates für Emulatoren/Simulatoren (nützlich zum Testen). * Geräte-Selbstzuweisung erlauben: Ermöglicht der App, zur Laufzeit mit `setChannel` zu diesem Kanal zu wechseln. Wenn deaktiviert, schlägt `setChannel` für diesen Kanal fehl. ### Strategien zum Deaktivieren automatischer Updates [Section titled “Strategien zum Deaktivieren automatischer Updates”](#strategien-zum-deaktivieren-automatischer-updates) Verwenden Sie dies, um einzuschränken, welche Arten von Updates der Kanal automatisch liefert. Optionen: * major: Blockiert kanalübergreifende Major-Updates (0.0.0 → 1.0.0). Minor- und Patch-Updates sind weiterhin erlaubt. * minor: Blockiert kanalübergreifende Minor-Updates (z.B. 1.1.0 → 1.2.0) und Majors. Patch-Updates sind weiterhin erlaubt. Hinweis: Blockiert nicht 0.1.0 → 1.1.0. * patch: Sehr strikt. Erlaubt nur steigende Patch-Versionen innerhalb desselben Major und Minor. Beispiele: 0.0.311 → 0.0.314 ✅, 0.1.312 → 0.0.314 ❌, 1.0.312 → 0.0.314 ❌. * metadata: Erfordert eine minimale Update-Version-Metadaten für jedes Bundle. Konfigurieren Sie über CLI mit `--min-update-version` oder `--auto-min-update-version`. Wenn fehlend, wird der Kanal als fehlkonfiguriert markiert und Updates werden abgelehnt, bis es gesetzt ist. * none: Erlaubt alle Updates gemäß semver-Kompatibilität. Erfahren Sie mehr Details und Beispiele unter Strategie zum Deaktivieren von Updates unter /docs/cli/commands/#disable-updates-strategy. Beispiel (CLI): ```bash # Major-Updates auf dem Production-Kanal blockieren npx @capgo/cli@latest channel set production com.example.app \ --disable-auto-update major # Geräten erlauben, sich selbst dem Beta-Kanal zuzuweisen npx @capgo/cli@latest channel set beta com.example.app --self-assign ``` ### setChannel() aus Ihrer App verwenden [Section titled “setChannel() aus Ihrer App verwenden”](#setchannel-aus-ihrer-app-verwenden) Die `setChannel()`-Methode ermöglicht Ihrer App, zur Laufzeit programmatisch den Kanal zu wechseln. Dies ist besonders nützlich für: * QA/Debug-Menüs, in denen Tester zwischen Kanälen wechseln können * Beta-Programm-Opt-in-Flows * Feature-Flag-Implementierungen * A/B-Test-Szenarien ```typescript import { CapacitorUpdater } from '@capgo/capacitor-updater'; // Zum Beta-Kanal wechseln await CapacitorUpdater.setChannel({ channel: 'beta' }); // Optional eine sofortige Update-Prüfung nach dem Wechsel auslösen await CapacitorUpdater.setChannel({ channel: 'beta', triggerAutoUpdate: true }); ``` Wie setChannel() funktioniert (v5.34.0+ / v6.34.0+ / v7.34.0+ / v8.0.0+) Wenn `setChannel()` aufgerufen wird: 1. **Backend-Validierung (nur Lesen)**: Eine Anfrage wird an das Capgo-Backend gesendet, um zu validieren, dass der Kanal erlaubt ist (prüft Selbstzuweisungsberechtigungen) 2. **Lokale Speicheraktualisierung**: Wenn die Validierung erfolgreich ist, wird der Kanal im lokalen Speicher des Geräts als `defaultChannel` gespeichert 3. **Sofortige Wirkung**: Die nächste Update-Prüfung verwendet den neuen Kanal sofort (kein Warten auf Replikation) **Warum das wichtig ist:** In älteren Versionen speicherte `setChannel()` den Kanal-Override in der Backend-Datenbank (wie Dashboard- oder API-Änderungen). Geräte mussten auf die Backend-Replikation warten (bis zu 2 Minuten), bevor die Kanaländerung wirksam wurde. Jetzt liest `setChannel()` nur vom Backend (zur Validierung) und speichert lokal, was Kanalwechsel sofort macht. **Sicherheitshinweis:** Selbst wenn sich die Berechtigungen eines Kanals nach dem lokalen Setzen ändern (z.B. Selbstzuweisung wird deaktiviert), validiert das Backend den Kanal weiterhin bei Update-Prüfungen, um die Sicherheit zu gewährleisten. **Vergleich der Kanaländerungsmethoden:** | Methode | Wirkungszeit | Wo gespeichert | Anwendungsfall | | ------------------------- | ------------ | ----------------- | ------------------------------------------------ | | `setChannel()` vom Plugin | **Sofort** | Nur Gerät (lokal) | Benutzer-initiierter Kanalwechsel in der App | | Dashboard-Geräte-Override | Bis zu 2 Min | Backend-Datenbank | Admin-initiierte Änderungen für bestimmte Geräte | | API-Kanalzuweisung | Bis zu 2 Min | Backend-Datenbank | Automatisierte Backend-Integrationen | Für die beste Benutzererfahrung beim Erstellen von Kanalwechsel-UIs verwenden Sie immer die `setChannel()`-Methode des Plugins. Mindestversionen für lokalen Kanalwechsel: **5.34.0**, **6.34.0**, **7.34.0** oder **8.0.0** (je nach Ihrer Hauptversion). Jede Minor-Versionsnummer entspricht demselben Funktionsumfang über alle Hauptversionen hinweg (z.B. X.34.0 enthält dieselben Funktionen, ob X 5, 6, 7 oder 8 ist). Siehe [Plugin-Installation](/docs/getting-started/add-an-app/) für Versions-Tags. ## Zuweisen eines Bundles zu einem Kanal [Section titled “Zuweisen eines Bundles zu einem Kanal”](#zuweisen-eines-bundles-zu-einem-kanal) Um ein Live-Update bereitzustellen, müssen Sie einen neuen JS-Bundle-Build hochladen und einem Kanal zuweisen. Sie können dies in einem Schritt mit der Capgo CLI tun: ```shell npx @capgo/cli@latest bundle upload --channel=Development ``` Dies lädt Ihre gebauten Web-Assets hoch und setzt den neuen Bundle als aktiven Build für den `Development`-Kanal. Alle Apps, die für diesen Kanal konfiguriert sind, erhalten das Update beim nächsten Überprüfen. Sie können Builds auch über den “Bundles”-Bereich des Capgo Dashboards Kanälen zuweisen. Klicken Sie auf das Menü-Symbol neben einem Build und wählen Sie “Assign to Channel”, um den Kanal für diesen Build auszuwählen. ## Bundle-Versionierung und Kanäle [Section titled “Bundle-Versionierung und Kanäle”](#bundle-versionierung-und-kanäle) Es ist wichtig zu beachten, dass Bundles in Capgo global für Ihre App sind und nicht spezifisch für einzelne Kanäle. Dasselbe Bundle kann mehreren Kanälen zugewiesen werden. Bei der Versionierung Ihrer Bundles empfehlen wir die Verwendung von Semantic Versioning [semver](https://semver.org/) mit Pre-Release-Kennungen für kanalspezifische Builds. Ein Beta-Release könnte beispielsweise als `1.2.3-beta.1` versioniert sein. Dieser Ansatz hat mehrere Vorteile: * Er kommuniziert die Beziehung zwischen Builds klar: `1.2.3-beta.1` ist offensichtlich eine Vorabversion von `1.2.3` * Er ermöglicht die Wiederverwendung von Versionsnummern über Kanäle hinweg und reduziert Verwirrung * Er ermöglicht klare Rollback-Pfade. Wenn Sie von `1.2.3` zurückrollen müssen, wissen Sie, dass `1.2.2` die vorherige stabile Version ist Hier ist ein Beispiel, wie Sie Ihre Bundle-Versionen mit einem typischen Kanal-Setup abstimmen könnten: * `Development`-Kanal: `1.2.3-dev.1`, `1.2.3-dev.2`, etc. * `QA`-Kanal: `1.2.3-qa.1`, `1.2.3-qa.2`, etc. * `Staging`-Kanal: `1.2.3-rc.1`, `1.2.3-rc.2`, etc. * `Production`-Kanal: `1.2.3`, `1.2.4`, etc. Die Verwendung von semver mit Pre-Release-Kennungen ist ein empfohlener Ansatz, aber nicht strikt erforderlich. Der Schlüssel ist, ein Versionierungsschema zu finden, das die Beziehungen zwischen Ihren Builds klar kommuniziert und mit dem Entwicklungsprozess Ihres Teams übereinstimmt. ## Zurücksetzen eines Live-Updates [Section titled “Zurücksetzen eines Live-Updates”](#zurücksetzen-eines-live-updates) Wenn Sie ein Live-Update bereitstellen, das einen Fehler einführt oder aus anderen Gründen zurückgesetzt werden muss, können Sie einfach zu einem vorherigen Build zurückkehren. Vom “Channels”-Bereich des Dashboards aus: 1. Klicken Sie auf den Namen des Kanals, den Sie zurücksetzen möchten 2. Finden Sie den Build, zu dem Sie zurückkehren möchten, und klicken Sie auf das Kronen-Symbol ![Rollback build](/select_bundle.webp) 3. Bestätigen Sie die Aktion Der ausgewählte Build wird sofort wieder zum aktiven Build für diesen Kanal. Apps erhalten die zurückgesetzte Version beim nächsten Update-Check. ## Automatisierung von Deployments [Section titled “Automatisierung von Deployments”](#automatisierung-von-deployments) Für fortgeschrittenere Workflows können Sie Ihre Live-Update-Deployments als Teil Ihrer CI/CD-Pipeline automatisieren. Durch die Integration von Capgo in Ihren Build-Prozess können Sie automatisch neue Bundles hochladen und Kanälen zuweisen, wenn Sie zu bestimmten Branches pushen oder neue Releases erstellen. Schauen Sie sich die [CI/CD Integration](/docs/getting-started/cicd-integration/) Dokumentation an, um mehr über die Automatisierung von Capgo Live-Updates zu erfahren. ## Bereitstellung auf einem Gerät [Section titled “Bereitstellung auf einem Gerät”](#bereitstellung-auf-einem-gerät) Nachdem Sie die Kanäle verstanden haben, können Sie mit der Bereitstellung von Live-Updates auf echten Geräten beginnen. Der grundlegende Prozess ist: 1. Installieren Sie das Capgo SDK in Ihrer App 2. Konfigurieren Sie die App, um auf Ihren gewünschten Kanal zu hören 3. Laden Sie einen Build hoch und weisen Sie ihn diesem Kanal zu 4. Starten Sie die App und warten Sie auf das Update! Eine detailliertere Anleitung finden Sie im [Deploying Live Updates](/docs/getting-started/deploy/) Guide. Viel Spaß beim Aktualisieren! ## Erweiterte Kanalnutzung: Benutzersegmentierung [Section titled “Erweiterte Kanalnutzung: Benutzersegmentierung”](#erweiterte-kanalnutzung-benutzersegmentierung) Kanäle können für mehr als nur Entwicklungsphasen verwendet werden. Sie sind ein leistungsstarkes Werkzeug für die Benutzersegmentierung und ermöglichen Funktionen wie: * Feature-Flags für verschiedene Benutzerstufen * A/B-Tests * Schrittweise Feature-Rollouts * Beta-Test-Programme Erfahren Sie, wie Sie diese erweiterten Anwendungsfälle in unserem Leitfaden implementieren: [How to Segment Users by Plan and Channels for Feature Flags and A/B Testing](/blog/how-to-segment-users-by-plan-and-channels/). # Capgo in China verwenden > Erfahren Sie, wie Sie Capgo Live Updates für den Betrieb in China konfigurieren, indem Sie regionale OST-URLs für optimale Leistung und Zuverlässigkeit verwenden. Wenn Sie Ihre App für Benutzer in China bereitstellen, müssen Sie Capgo für die Verwendung regionaler OST-URLs (Object Storage Technology) konfigurieren, um zuverlässige und schnelle Updates sicherzustellen. ## Warum China-spezifische URLs verwenden? [Section titled “Warum China-spezifische URLs verwenden?”](#warum-china-spezifische-urls-verwenden) Aufgrund der Netzwerkinfrastruktur und Vorschriften in China (der Great Firewall) können direkte Verbindungen zu internationalen Servern langsam oder unzuverlässig sein. Capgo stellt dedizierte OST-URLs mit Daten in Hongkong bereit, um die Latenz zu minimieren und sicherzustellen, dass Ihre Benutzer Updates so schnell und zuverlässig wie möglich erhalten. ## Konfiguration [Section titled “Konfiguration”](#konfiguration) Um Capgo für China zu konfigurieren, müssen Sie drei spezifische URLs in Ihrer Capacitor-Konfigurationsdatei festlegen. Diese URLs verweisen auf die in Hongkong basierende Infrastruktur von Capgo. 1. Öffnen Sie Ihre `capacitor.config.ts`-Datei 2. Fügen Sie die folgende Konfiguration zum `CapacitorUpdater`-Plugin-Abschnitt hinzu: ```typescript import { CapacitorConfig } from '@capacitor/cli'; const config: CapacitorConfig = { plugins: { CapacitorUpdater: { autoUpdate: true, updateUrl: 'https://updater.capgo.com.cn/updates', statsUrl: 'https://updater.capgo.com.cn/stats', channelUrl: 'https://updater.capgo.com.cn/channel_self', }, }, }; export default config; ``` 3. Bauen Sie Ihre App neu, um die Änderungen anzuwenden: ```shell npm run build npx cap sync ``` ## Konfigurationsdetails [Section titled “Konfigurationsdetails”](#konfigurationsdetails) Hier ist, was jede URL macht: * **updateUrl**: `https://updater.capgo.com.cn/updates` - Wird verwendet, um verfügbare Updates für Ihre App zu überprüfen und herunterzuladen * **statsUrl**: `https://updater.capgo.com.cn/stats` - Wird verwendet, um Analysen und Nutzungsstatistiken an Capgo zurückzumelden * **channelUrl**: `https://updater.capgo.com.cn/channel_self` - Wird verwendet, um Kanalkonfiguration abzurufen und zu bestimmen, welche Updates angewendet werden Tip Alle drei URLs müssen zusammen konfiguriert werden, um die volle Funktionalität des Capgo-Updaters in China sicherzustellen. ## Empfohlene Einstellungen für China [Section titled “Empfohlene Einstellungen für China”](#empfohlene-einstellungen-für-china) Aufgrund von Netzwerkleistungseinschränkungen durch die Great Firewall of China haben wir spezifische Empfehlungen für Apps, die in Festlandchina bereitgestellt werden: ### Direkte Updates deaktivieren [Section titled “Direkte Updates deaktivieren”](#direkte-updates-deaktivieren) Wir **empfehlen dringend, `directUpdate` zu deaktivieren** für Apps in China. Die Netzwerkkonnektivität in China ist weniger leistungsfähig als in anderen Regionen, und direkte Updates (die sofort angewendet werden) können zu einer schlechten Benutzererfahrung führen, wenn Downloads unterbrochen oder langsam sind. Verwenden Sie stattdessen das Standard-Update-Verhalten, bei dem Updates im Hintergrund heruntergeladen und angewendet werden, wenn die App in den Hintergrund geht oder neu startet. Dies bietet eine zuverlässigere Erfahrung für Ihre Benutzer. ```typescript const config: CapacitorConfig = { plugins: { CapacitorUpdater: { autoUpdate: true, directUpdate: false, // Empfohlen für China updateUrl: 'https://updater.capgo.com.cn/updates', statsUrl: 'https://updater.capgo.com.cn/stats', channelUrl: 'https://updater.capgo.com.cn/channel_self', }, }, }; ``` Caution Obwohl unsere in Hongkong basierende Infrastruktur hilft, die Latenz zu minimieren und die Zuverlässigkeit zu verbessern, kann die Netzwerkleistung nach Festlandchina immer noch von der Great Firewall beeinträchtigt werden. Das Deaktivieren von `directUpdate` hilft sicherzustellen, dass Updates erfolgreich abgeschlossen werden, ohne die Benutzererfahrung zu beeinträchtigen. ## Vollständiges Konfigurationsbeispiel [Section titled “Vollständiges Konfigurationsbeispiel”](#vollständiges-konfigurationsbeispiel) Hier ist ein vollständiges Beispiel mit empfohlenen Einstellungen für Apps, die in China bereitgestellt werden: ```typescript import { CapacitorConfig } from '@capacitor/cli'; const config: CapacitorConfig = { appId: 'com.example.app', appName: 'My App', webDir: 'dist', plugins: { CapacitorUpdater: { autoUpdate: true, directUpdate: false, // Empfohlen: für bessere Zuverlässigkeit in China deaktivieren updateUrl: 'https://updater.capgo.com.cn/updates', statsUrl: 'https://updater.capgo.com.cn/stats', channelUrl: 'https://updater.capgo.com.cn/channel_self', }, }, }; export default config; ``` ## Ihre Konfiguration testen [Section titled “Ihre Konfiguration testen”](#ihre-konfiguration-testen) Nachdem Sie die China-spezifischen URLs konfiguriert haben, können Sie überprüfen, ob Updates korrekt funktionieren: 1. Ein neues Bundle zu Capgo hochladen: ```shell npx @capgo/cli@latest bundle upload --channel=production ``` 2. Ihre App auf einem Testgerät in China installieren 3. Den Update-Prozess überwachen: ```shell npx @capgo/cli@latest app debug ``` 4. Überprüfen, dass Updates von den China OST-URLs heruntergeladen werden Note Das Update-Verhalten und Timing bleibt gleich wie bei der Standard-Capgo-Konfiguration. Siehe die [Update-Verhalten](/docs/live-updates/update-behavior/)-Dokumentation für Details darüber, wie und wann Updates angewendet werden. ## Multi-Region-Bereitstellung [Section titled “Multi-Region-Bereitstellung”](#multi-region-bereitstellung) Wenn Ihre App Benutzer sowohl innerhalb als auch außerhalb Chinas bedient, können Sie die chinesische Domain-Konfiguration für alle Benutzer weltweit verwenden. Die `updater.capgo.com.cn`-Domain wird dank der Alibaba DNS-Infrastruktur global aufgelöst und ist sowohl in China als auch überall sonst auf der Welt zugänglich. ### Chinesische Domains global verwenden [Section titled “Chinesische Domains global verwenden”](#chinesische-domains-global-verwenden) Die chinesischen Domain-URLs funktionieren nahtlos für Multi-Region-Apps: ```typescript const config: CapacitorConfig = { plugins: { CapacitorUpdater: { autoUpdate: true, directUpdate: false, // Empfohlen für China-Benutzer updateUrl: 'https://updater.capgo.com.cn/updates', statsUrl: 'https://updater.capgo.com.cn/stats', channelUrl: 'https://updater.capgo.com.cn/channel_self', }, }, }; ``` Diese einzelne Konfiguration funktioniert für: * Benutzer in Festlandchina (verwenden in Hongkong basierende Infrastruktur) * Benutzer außerhalb Chinas (greifen auf dieselbe Infrastruktur über Alibaba DNS zu) **Leistungsüberlegungen:** Obwohl die `.cn`-Domain global über Alibaba DNS aufgelöst wird und überall funktioniert, ist sie für Benutzer außerhalb Chinas im Vergleich zur Standard-Domain (`api.capgo.app`), die direkt von Cloudflare aufgelöst wird, wo unser Backend gehostet ist, etwas weniger leistungsfähig. Die DNS-Auflösung ist jedoch schnell, sodass der Leistungsunterschied minimal ist und die Benutzererfahrung nicht wesentlich beeinträchtigt. Tip Die Verwendung der `.cn`-Domain für alle Benutzer vereinfacht Ihre Bereitstellung und stellt ein konsistentes Update-Verhalten in allen Regionen sicher. Sie benötigen keine separaten Builds oder umgebungsbasierten Konfigurationen. Der kleine Leistungsnachteil außerhalb Chinas ist typischerweise die vereinfachte Bereitstellung wert. ### Alternative: Regionsspezifische Konfigurationen [Section titled “Alternative: Regionsspezifische Konfigurationen”](#alternative-regionsspezifische-konfigurationen) Wenn Sie es vorziehen, für jede Region unterschiedlich zu optimieren, können Sie auch in Betracht ziehen: * Separate App-Varianten mit unterschiedlichen Konfigurationen zu erstellen * Umgebungsbasierte Konfiguration zu verwenden, um die URLs dynamisch festzulegen * Verschiedene Release-Kanäle für verschiedene Regionen zu erstellen Wenn Sie Unterstützung bei Multi-Region-Bereitstellungsstrategien benötigen, kontaktieren Sie uns bitte unter oder treten Sie unserer [Discord-Community](https://discord.capgo.app) bei, um Hilfe zu erhalten. ## Fehlerbehebung [Section titled “Fehlerbehebung”](#fehlerbehebung) Wenn Sie Probleme mit Updates in China haben: 1. **Überprüfen Sie Ihre Konfiguration** - Überprüfen Sie doppelt, dass alle drei URLs in Ihrer `capacitor.config.ts` korrekt festgelegt sind 2. **Netzwerkkonnektivität prüfen** - Stellen Sie sicher, dass Ihr Gerät die `updater.capgo.com.cn`-Domain erreichen kann 3. **Protokolle überprüfen** - Verwenden Sie `npx @capgo/cli@latest app debug`, um auf Fehlermeldungen zu prüfen 4. **Updates testen** - Versuchen Sie, ein neues Bundle hochzuladen und den Download-Prozess zu überwachen 5. **Support kontaktieren** - Wenn Probleme bestehen bleiben, wenden Sie sich an uns unter oder treten Sie unserer [Discord-Community](https://discord.capgo.app) bei, um Unterstützung zu erhalten Caution Stellen Sie sicher, dass Sie die `.cn`-Domain (`updater.capgo.com.cn`) und nicht die internationale Standard-Domain verwenden, wenn Sie für China konfigurieren. ## Nächste Schritte [Section titled “Nächste Schritte”](#nächste-schritte) * Erfahren Sie mehr über [Update-Verhalten](/docs/live-updates/update-behavior/), um anzupassen, wann Updates angewendet werden * Erkunden Sie [Kanäle](/docs/live-updates/channels/), um verschiedene Release-Tracks zu verwalten * Überprüfen Sie [Verschlüsselung](/docs/live-updates/encryption/), um Ihre Updates zu sichern # Compliance > Erfahren Sie mehr über Capgos Datenschutzpraktiken, Datenerfassung, Sicherheitskonformität und wie wir die Informationen Ihrer Benutzer während Live-Updates schützen. Capgo ist mit Fokus auf Datenschutz, Sicherheit und Compliance konzipiert. Dieses Dokument erklärt, welche Daten erfasst werden, wie sie verwendet werden und welche Maßnahmen zum Schutz der Privatsphäre Ihrer Benutzer und zur Gewährleistung der regulatorischen Compliance bei der Nutzung des Live-Update-Dienstes von Capgo getroffen werden. ## Datenerfassungs-Übersicht [Section titled “Datenerfassungs-Übersicht”](#datenerfassungs-übersicht) Capgo erfasst minimale Daten, die zur effektiven Bereitstellung des Live-Update-Dienstes erforderlich sind. Die Datenerfassung konzentriert sich auf betriebliche Anforderungen statt auf Benutzer-Tracking oder Analysen. ### Welche Daten werden erfasst [Section titled “Welche Daten werden erfasst”](#welche-daten-werden-erfasst) Capgo erfasst nur die Daten, die zur Bereitstellung der Live-Updates-Funktion erforderlich sind. Wenn Ihre App nach Updates sucht oder neue Bundles herunterlädt, werden folgende Informationen erfasst: * **App-ID**: Ein eindeutiger Identifikator für Ihre App, der verwendet wird, um die App mit dem richtigen Konto zu verknüpfen * **App-Versionscode**: Der Versionscode der App, der verwendet wird, um zu bestimmen, welche Updates mit der App kompatibel sind * **App-Versionsname**: Der Versionsname der App, der für Anzeigezwecke verwendet wird * **Plattform**: Die Plattform (iOS, Android) der App, die verwendet wird, um zu bestimmen, welche Updates mit der App kompatibel sind * **Geräte-ID**: Ein eindeutiger Identifikator für das Gerät, der verwendet wird, um Updates an ein bestimmtes Gerät zu liefern und für Abrechnungszwecke. Dieser Identifikator ist eine zufällige Zeichenfolge, die beim ersten Start der App erstellt wird. **Ab Plugin-Version v5.10.0, v6.25.0 und v7.25.0** bleibt die Geräte-ID jetzt über App-Neuinstallationen hinweg bestehen (sicher gespeichert im Keychain auf iOS und EncryptedSharedPreferences auf Android), um eine bessere Geräteverfolgung zu ermöglichen und gleichzeitig die Compliance mit App Store-Richtlinien aufrechtzuerhalten. Vor diesen Versionen wurde die Geräte-ID bei jeder App-Installation zurückgesetzt * **Bundle-ID**: Der eindeutige Identifikator für das Bundle, das aktuell auf dem Gerät installiert ist * **Kanalname**: Der Name des Kanals, der ausgewählt wurde, um Updates zu erhalten * **OS-Version**: Die Version des Betriebssystems, die verwendet wird, um zu bestimmen, welche Updates mit dem Gerät kompatibel sind * **Plugin-Version**: Die Version des @capgo/capacitor-updater Plugins, das verwendet wird, um Updates an das Gerät zu liefern **Zusätzliche technische Daten:** * Update-Prüfungs-Zeitstempel * Download-Erfolgs-/Fehlerstatus * Bundle-Installationsstatus * Rollback-Ereignisse und Gründe * IP-Adresse (für Geolokalisierung und CDN-Optimierung) Note Sie können die erfassten Daten überprüfen, indem Sie den Quellcode des @capgo/capacitor-updater Plugins inspizieren, der Open Source ist und auf [GitHub](https://github.com/Cap-go/capacitor-updater) verfügbar ist. Capgo erfasst keine personenbezogenen Daten (PII) wie Namen, E-Mail-Adressen, Telefonnummern oder persistente Geräte-Identifikatoren, die verwendet werden können, um einzelne Benutzer über Apps hinweg zu verfolgen. ### Welche Daten werden NICHT erfasst [Section titled “Welche Daten werden NICHT erfasst”](#welche-daten-werden-nicht-erfasst) Capgo erfasst ausdrücklich nicht: * Persönliche Benutzerinformationen oder Zugangsdaten * App-Nutzungsanalysen oder Benutzerverhaltens-Daten * Inhalte aus Ihrer App oder benutzergenerierte Daten * Standortdaten über die allgemeine geografische Region hinaus * Persistente Geräte-Identifikatoren für Tracking * Biometrische oder sensible persönliche Daten ## Datennutzung und -zweck [Section titled “Datennutzung und -zweck”](#datennutzung-und--zweck) Die von Capgo erfassten Daten werden ausschließlich verwendet für: ### Dienstbetrieb [Section titled “Dienstbetrieb”](#dienstbetrieb) * Bestimmen, welche Updates für bestimmte App-Versionen verfügbar sind * Optimierung der Inhaltsbereitstellung durch geografische CDN-Auswahl * Sicherstellen der Kompatibilität zwischen Updates und Gerätefunktionen * Verwaltung von Update-Rollouts und Kanalzuweisungen ### Dienstverbesserung [Section titled “Dienstverbesserung”](#dienstverbesserung) * Überwachung der Update-Erfolgsraten und Identifizierung von Problemen * Optimierung der Download-Leistung und Zuverlässigkeit * Verbesserung des gesamten Update-Liefersystems * Fehlersuche und Behebung von Update-Fehlern ### Sicherheit und Integrität [Section titled “Sicherheit und Integrität”](#sicherheit-und-integrität) * Verhinderung von Missbrauch und Sicherstellung der Dienstverfügbarkeit * Validierung der Authentizität und Integrität von Updates * Schutz vor bösartigen oder beschädigten Updates * Aufrechterhaltung der Dienstsicherheit und Stabilität ## Datenspeicherung und Aufbewahrung [Section titled “Datenspeicherung und Aufbewahrung”](#datenspeicherung-und-aufbewahrung) ### Speicherort [Section titled “Speicherort”](#speicherort) * Update-Bundles und Metadaten werden auf sicherer Cloud-Infrastruktur gespeichert * Daten werden über mehrere geografische Regionen verteilt für bessere Leistung * Alle Datenübertragungen werden mit branchenüblichen Protokollen verschlüsselt (HTTPS/TLS) ### Datenaufbewahrung [Section titled “Datenaufbewahrung”](#datenaufbewahrung) * Update-Prüfungsprotokolle werden für betriebliche Zwecke aufbewahrt (typischerweise 30-90 Tage) * Bundle-Dateien werden aufbewahrt, solange sie aktiven Kanälen zugewiesen sind * Aggregierte, nicht-personenbezogene Metriken können länger für Dienstverbesserungen aufbewahrt werden * Persönliche Daten, falls vorhanden, werden gemäß geltenden Datenschutzgesetzen gelöscht ### Datensicherheit [Section titled “Datensicherheit”](#datensicherheit) * Alle Daten werden während der Übertragung und im Ruhezustand verschlüsselt * Zugriff auf Daten ist nur autorisiertem Personal vorbehalten * Regelmäßige Sicherheitsaudits und Überwachung werden durchgeführt * Branchenübliche Sicherheitspraktiken werden befolgt * **SOC 2-Zertifizierung**: Capgo ist derzeit SOC 2 Typ II zertifiziert und gewährleistet höchste Standards für Sicherheit, Verfügbarkeit und Vertraulichkeit. Sehen Sie sich unseren Compliance-Status unter [trust.capgo.app](https://trust.capgo.app) an * **Kontinuierliche Code-Prüfung**: Jeder Commit wird automatisch von [SonarCloud](https://sonarcloud.io/summary/overall?id=Cap-go_capacitor-updater\&branch=main) für das [Plugin](https://sonarcloud.io/summary/overall?id=Cap-go_capgo\&branch=main) und [Backend](https://sonarcloud.io/summary/overall?id=Cap-go_capgo\&branch=main) geprüft, um Codequalität, Erkennung von Sicherheitslücken und Wartbarkeit sicherzustellen * **Schwachstellen-Scanning**: Zusätzliches Sicherheitsscanning wird von [Snyk](https://snyk.io/test/github/Cap-go/capgo) durchgeführt, um Sicherheitslücken in Abhängigkeiten zu erkennen und zu beheben * **Infrastruktur-Sicherheit**: Unsere Hosting-Infrastruktur wird kontinuierlich überwacht und durch [Hosting-Sicherheitsprüfungen](https://hosting-checker.net/websites/api.capgo.app) verifiziert * **KI-gestützte Code-Überprüfung**: Jede Pull-Request wird von CodeRabbit AI überprüft, um potenzielle Probleme, Sicherheitsbedenken zu erkennen und Codequalitätsstandards aufrechtzuerhalten ## Datenschutzkontrollen [Section titled “Datenschutzkontrollen”](#datenschutzkontrollen) ### Für App-Entwickler [Section titled “Für App-Entwickler”](#für-app-entwickler) Als Capgo-Benutzer haben Sie Kontrolle über: * **Kanalverwaltung**: Steuern Sie, welche Updates an welche Benutzer verteilt werden * **Datenminimierung**: Konfigurieren Sie, welche Geräteinformationen geteilt werden * **Geografische Kontrollen**: Verwalten Sie, wo Ihre Updates verteilt werden * **Aufbewahrungseinstellungen**: Steuern Sie, wie lange Update-Daten aufbewahrt werden ### Für Endbenutzer [Section titled “Für Endbenutzer”](#für-endbenutzer) Ihre App-Benutzer profitieren von: * **Minimale Datenerfassung**: Nur wesentliche Daten für die Update-Bereitstellung werden erfasst * **Kein Tracking**: Kein App-übergreifendes oder persistentes Benutzer-Tracking * **Transparenz**: Diese Datenschutzrichtlinie erklärt genau, welche Daten erfasst werden * **Sicherheit**: Alle Datenübertragungen sind verschlüsselt und sicher ## Compliance und Rechtliches [Section titled “Compliance und Rechtliches”](#compliance-und-rechtliches) ### Datenschutzvorschriften [Section titled “Datenschutzvorschriften”](#datenschutzvorschriften) Capgo ist darauf ausgelegt, wichtige Datenschutzvorschriften einzuhalten, einschließlich: * **GDPR** (Datenschutz-Grundverordnung) * **CCPA** (California Consumer Privacy Act) * **COPPA** (Children’s Online Privacy Protection Act) * Andere anwendbare regionale Datenschutzgesetze ### App Store Compliance [Section titled “App Store Compliance”](#app-store-compliance) Capgo hält sich strikt an App Store-Richtlinien und -Richtlinien: * **Apple App Store**: Entspricht den [App Store Review Guidelines](https://developer.apple.com/app-store/review/guidelines/) Abschnitt 3.3.2 und stellt sicher, dass Live-Updates nur das Verhalten der App auf eine Weise ändern, die mit der eingereichten App übereinstimmt * **Google Play Store**: Befolgt die Anforderungen der [Google Play Developer Policy](https://play.google.com/about/developer-content-policy/) für dynamisches Code-Laden und App-Updates * **Inhaltsbeschränkungen**: Live-Updates können keine Funktionalität einführen, die in der ursprünglichen App-Einreichung nicht vorhanden war oder plattformspezifische Inhaltsrichtlinien verletzt * **Sicherheitsanforderungen**: Alle Updates behalten die gleiche Sicherheitslage und Berechtigungen wie die ursprüngliche App bei ### Ihre Verantwortlichkeiten [Section titled “Ihre Verantwortlichkeiten”](#ihre-verantwortlichkeiten) Als App-Entwickler, der Capgo verwendet, sollten Sie: * Angemessene Datenschutzerklärungen in die Datenschutzrichtlinie Ihrer App aufnehmen * Benutzer über die Verwendung von Live-Update-Diensten informieren * Die Einhaltung geltender Gesetze in Ihrer Gerichtsbarkeit sicherstellen * Geeignete Einwilligungsmechanismen implementieren, falls erforderlich Tip **Keine zusätzliche Datenschutzimplementierung erforderlich**: Capgo ist standardmäßig datenschutzkonform konzipiert. Sie müssen keine zusätzlichen Datenschutzkontrollen oder Datenverarbeitungsmechanismen in Ihrem App-Code implementieren. Der minimale Datenerfassungsansatz und das Privacy-by-Design-Prinzip bedeuten, dass die Integration von Capgo typischerweise keine Änderungen an Ihren bestehenden Datenschutzerklärungen oder Richtlinien erfordert. ## Privacy by Design [Section titled “Privacy by Design”](#privacy-by-design) Capgo folgt Privacy-by-Design-Prinzipien: ### Datenminimierung [Section titled “Datenminimierung”](#datenminimierung) * Nur Daten erfassen, die absolut notwendig für den Dienstbetrieb sind * Erfassung persönlicher oder sensibler Informationen vermeiden * Wo möglich aggregierte und anonymisierte Daten verwenden ### Zweckbindung [Section titled “Zweckbindung”](#zweckbindung) * Erfasste Daten nur für die angegebenen Zwecke verwenden * Daten nicht für unzusammenhängende Aktivitäten umwidmen * Klare Grenzen für die Datennutzung aufrechterhalten ### Transparenz [Section titled “Transparenz”](#transparenz) * Klare Informationen über Datenerfassung und -nutzung bereitstellen * Datenschutzpraktiken leicht zugänglich und verständlich machen * Datenschutzdokumentation regelmäßig aktualisieren ## Kontakt und Fragen [Section titled “Kontakt und Fragen”](#kontakt-und-fragen) Wenn Sie Fragen zu Capgos Datenschutzpraktiken haben oder ein Datenschutzanliegen melden möchten: * Überprüfen Sie unsere vollständige Datenschutzrichtlinie unter [capgo.app/privacy](https://capgo.app/privacy) * Sehen Sie sich unseren Sicherheits- und Compliance-Status unter [capgo.app/trust](https://capgo.app/trust) an * Kontaktieren Sie unser Datenschutzteam über die Support-Kanäle * Melden Sie datenschutzbezogene Probleme über unseren Sicherheitskontakt Tip Denken Sie daran, die Datenschutzrichtlinie Ihrer eigenen App zu aktualisieren, um die Verwendung des Live-Update-Dienstes von Capgo und jegliche Datenerfassung widerzuspiegeln, die als Teil des Update-Prozesses stattfinden kann. ## Bewährte Methoden für Datenschutz [Section titled “Bewährte Methoden für Datenschutz”](#bewährte-methoden-für-datenschutz) Bei der Implementierung von Capgo in Ihrer App: 1. **Seien Sie transparent**: Informieren Sie Benutzer über die Live-Update-Funktionalität 2. **Minimieren Sie Daten**: Aktivieren Sie nur Datenerfassungsfunktionen, die Sie tatsächlich benötigen 3. **Sichere Implementierung**: Befolgen Sie Sicherheits-Bewährte Methoden bei Ihrer Integration 4. **Regelmäßige Überprüfungen**: Überprüfen Sie regelmäßig Ihre Datenschutzpraktiken und aktualisieren Sie Richtlinien 5. **Benutzerkontrolle**: Erwägen Sie, Benutzern Optionen zur Steuerung des Update-Verhaltens anzubieten Durch die Befolgung dieser Praktiken und das Verständnis von Capgos Datenschutzansatz können Sie Ihren Benutzern eine sichere, datenschutzfreundliche Live-Update-Erfahrung bieten. # Custom Storage > Erfahren Sie, wie Sie benutzerdefinierte Speicherlösungen mit Capgo Live Updates verwenden, einschließlich externer URLs, S3-Integration und Bundle-Verschlüsselung für sichere Bereitstellungen. Capgo unterstützt benutzerdefinierte Speicherlösungen für Ihre App-Bundles, sodass Sie Ihre Updates auf Ihrer eigenen Infrastruktur oder Drittspeicherdiensten hosten können. Dies ist besonders nützlich für Organisationen mit spezifischen Sicherheitsanforderungen, Compliance-Anforderungen oder bestehender Speicherinfrastruktur. ## Übersicht [Section titled “Übersicht”](#übersicht) Custom Storage in Capgo funktioniert durch Hochladen Ihres Bundles an einen externen Standort und Bereitstellung der URL für Capgo, um darauf zuzugreifen. Das Capgo SDK lädt dann Updates direkt von Ihrem benutzerdefinierten Speicherort herunter anstelle von Capgos Standard-Cloud-Speicher. Tip Custom Storage ist ideal für: * Organisationen mit strengen Datenresidenzanforderungen * Teams mit bestehender CDN- oder Speicherinfrastruktur * Anwendungen, die zusätzliche Sicherheitsebenen erfordern * Kostenoptimierung für große Bundle-Größen ## Externe URL-Upload [Section titled “Externe URL-Upload”](#externe-url-upload) Der einfachste Weg, benutzerdefinierten Speicher zu verwenden, besteht darin, Ihr Bundle auf eine öffentlich zugängliche URL hochzuladen und diese URL Capgo bereitzustellen. ### Basis Externe URL-Upload [Section titled “Basis Externe URL-Upload”](#basis-externe-url-upload) ```shell npx @capgo/cli@latest bundle upload --external https://your-domain.com/bundles/v1.2.3.zip ``` Dieser Befehl weist Capgo an, auf das Bundle unter der angegebenen URL zu verweisen, anstatt es in Capgos Cloud-Speicher hochzuladen. ### Mit Verschlüsselung [Section titled “Mit Verschlüsselung”](#mit-verschlüsselung) Für sicheren externen Speicher können Sie Ihr Bundle verschlüsseln und die Entschlüsselungsschlüssel bereitstellen: ```shell npx @capgo/cli@latest bundle upload --external https://your-domain.com/bundles/v1.2.3.zip --iv-session-key YOUR_IV_SESSION_KEY ``` ## S3-Integration [Section titled “S3-Integration”](#s3-integration) Capgo bietet integrierte Unterstützung für Amazon S3 und S3-kompatible Speicherdienste. Die CLI kann Ihr Bundle automatisch zu S3 hochladen und Capgo konfigurieren, die S3-URL zu verwenden. ### S3 Upload-Optionen [Section titled “S3 Upload-Optionen”](#s3-upload-optionen) ```shell npx @capgo/cli@latest bundle upload \ --s3-region us-east-1 \ --s3-apikey YOUR_ACCESS_KEY \ --s3-apisecret YOUR_SECRET_KEY \ --s3-bucket-name your-bucket-name ``` ### Vollständige S3-Konfiguration [Section titled “Vollständige S3-Konfiguration”](#vollständige-s3-konfiguration) Für S3-kompatible Dienste oder benutzerdefinierte Endpunkte: ```shell npx @capgo/cli@latest bundle upload \ --s3-region us-east-1 \ --s3-apikey YOUR_ACCESS_KEY \ --s3-apisecret YOUR_SECRET_KEY \ --s3-endpoint https://s3.your-provider.com \ --s3-bucket-name your-bucket-name \ --s3-port 443 \ --no-s3-ssl # Nur wenn Ihr Endpunkt SSL nicht unterstützt ``` ### S3-Konfigurationsparameter [Section titled “S3-Konfigurationsparameter”](#s3-konfigurationsparameter) | Parameter | Beschreibung | Erforderlich | | ------------------ | ---------------------------------- | ------------ | | `--s3-region` | AWS-Region für Ihren S3-Bucket | Ja | | `--s3-apikey` | S3-Zugriffsschlüssel-ID | Ja | | `--s3-apisecret` | S3-Geheimzugriffsschlüssel | Ja | | `--s3-bucket-name` | Name Ihres S3-Buckets | Ja | | `--s3-endpoint` | Benutzerdefinierte S3-Endpunkt-URL | Nein | | `--s3-port` | Port für S3-Endpunkt | Nein | | `--no-s3-ssl` | SSL für S3-Upload deaktivieren | Nein | ## Bundle-Vorbereitung und Verschlüsselung [Section titled “Bundle-Vorbereitung und Verschlüsselung”](#bundle-vorbereitung-und-verschlüsselung) Bei Verwendung von benutzerdefiniertem Speicher, insbesondere mit Verschlüsselung, müssen Sie Ihre Bundles ordnungsgemäß vorbereiten. Dies beinhaltet das Erstellen einer Zip-Datei und optional deren Verschlüsselung. ### Schritt 1: Ein Zip-Bundle erstellen [Section titled “Schritt 1: Ein Zip-Bundle erstellen”](#schritt-1-ein-zip-bundle-erstellen) Erstellen Sie zunächst eine Zip-Datei Ihres App-Bundles: ```shell npx @capgo/cli@latest bundle zip com.example.app --path ./dist ``` Der Zip-Befehl gibt die Prüfsumme der Zip-Datei zurück. Sie können diese Prüfsumme verwenden, um die Zip-Datei bei Bedarf zu verschlüsseln. Verwenden Sie die Option `--json`, um strukturierte Ausgabe einschließlich der Prüfsumme zu erhalten. #### Zip-Befehlsoptionen [Section titled “Zip-Befehlsoptionen”](#zip-befehlsoptionen) ```shell npx @capgo/cli@latest bundle zip [appId] \ --path ./dist \ --bundle 1.2.3 \ --name myapp-v1.2.3 \ --json \ --no-code-check \ --key-v2 \ --package-json ../../package.json,./package.json ``` | Option | Beschreibung | | ----------------- | ----------------------------------------------------------------------- | | `--path` | Pfad zum zu zippenden Ordner (Standard ist webDir aus capacitor.config) | | `--bundle` | Bundle-Versionsnummer zur Benennung der Zip-Datei | | `--name` | Benutzerdefinierter Name für die Zip-Datei | | `--json` | Ergebnisse im JSON-Format ausgeben (enthält Prüfsumme) | | `--no-code-check` | Prüfung auf notifyAppReady()-Aufruf und Index-Datei überspringen | | `--key-v2` | Verschlüsselung v2 verwenden | | `--package-json` | Pfade zu package.json-Dateien für Monorepos (kommagetrennt) | ### Schritt 2: Bundle verschlüsseln (Optional) [Section titled “Schritt 2: Bundle verschlüsseln (Optional)”](#schritt-2-bundle-verschlüsseln-optional) Für erhöhte Sicherheit verschlüsseln Sie Ihr Zip-Bundle vor dem Hochladen: ```shell # Verwendung des lokalen Standardschlüssels npx @capgo/cli@latest bundle encrypt ./myapp.zip CHECKSUM # Verwendung einer benutzerdefinierten Schlüsseldatei npx @capgo/cli@latest bundle encrypt ./myapp.zip CHECKSUM --key ./path/to/.capgo_key_v2 # Verwendung von Schlüsseldaten direkt npx @capgo/cli@latest bundle encrypt ./myapp.zip CHECKSUM --key-data "PRIVATE_KEY_CONTENT" ``` Der Parameter `CHECKSUM` ist erforderlich und sollte die Prüfsumme Ihrer Zip-Datei sein. Sie können die Prüfsumme aus der Ausgabe des Zip-Befehls erhalten (verwenden Sie die Option `--json` für strukturierte Ausgabe). Standardmäßig verwendet der encrypt-Befehl Ihren lokalen privaten Signaturschlüssel. Sie können einen benutzerdefinierten Schlüssel mit den Optionen `--key` oder `--key-data` angeben. Der encrypt-Befehl gibt den für Upload oder Entschlüsselung benötigten `ivSessionKey` zurück. #### Verschlüsselungs-Befehlsoptionen [Section titled “Verschlüsselungs-Befehlsoptionen”](#verschlüsselungs-befehlsoptionen) | Option | Beschreibung | | ------------ | ------------------------------------------------------------------------------------------------------------- | | `zipPath` | Pfad zur zu verschlüsselnden Zip-Datei (erforderlich) | | `checksum` | Prüfsumme der Zip-Datei (erforderlich) - aus Zip-Befehl erhalten | | `--key` | Benutzerdefinierter Pfad für privaten Signaturschlüssel (optional, verwendet lokalen Schlüssel standardmäßig) | | `--key-data` | Private Signaturschlüsseldaten direkt (optional) | | `--json` | Ergebnisse im JSON-Format ausgeben | Caution Der encrypt-Befehl gibt einen `ivSessionKey` aus, den Sie beim Hochladen mit der Option `--iv-session-key` bereitstellen müssen. ## Vollständige Workflow-Beispiele [Section titled “Vollständige Workflow-Beispiele”](#vollständige-workflow-beispiele) ### Beispiel 1: Externe URL mit Verschlüsselung [Section titled “Beispiel 1: Externe URL mit Verschlüsselung”](#beispiel-1-externe-url-mit-verschlüsselung) 1. **Erstellen Sie Ihre App:** ```shell npm run build ``` 2. **Erstellen Sie ein Zip-Bundle:** ```shell npx @capgo/cli@latest bundle zip com.example.app --path ./dist --bundle 1.2.3 ``` Notieren Sie sich die von diesem Befehl zurückgegebene Prüfsumme. 3. **Verschlüsseln Sie das Bundle:** ```shell npx @capgo/cli@latest bundle encrypt ./com.example.app-1.2.3.zip CHECKSUM_FROM_STEP_2 ``` Notieren Sie sich den `ivSessionKey` aus der Ausgabe. 4. **In Ihren Speicher hochladen:** Laden Sie die verschlüsselte Zip-Datei zu Ihrem Hosting-Dienst hoch. 5. **Bei Capgo registrieren:** ```shell npx @capgo/cli@latest bundle upload \ --external https://your-cdn.com/bundles/com.example.app-1.2.3.zip \ --iv-session-key IV_SESSION_KEY_FROM_STEP_3 ``` ### Beispiel 2: Direkter S3-Upload [Section titled “Beispiel 2: Direkter S3-Upload”](#beispiel-2-direkter-s3-upload) 1. **Erstellen Sie Ihre App:** ```shell npm run build ``` 2. **Direkt zu S3 hochladen:** ```shell npx @capgo/cli@latest bundle upload \ --s3-region us-west-2 \ --s3-apikey YOUR_ACCESS_KEY \ --s3-apisecret YOUR_SECRET_KEY \ --s3-bucket-name your-app-bundles \ --channel Production ``` ### Beispiel 3: S3 mit Verschlüsselung [Section titled “Beispiel 3: S3 mit Verschlüsselung”](#beispiel-3-s3-mit-verschlüsselung) 1. **Erstellen und zippen:** ```shell npm run build npx @capgo/cli@latest bundle zip com.example.app --path ./dist --key-v2 ``` 2. **Bundle verschlüsseln:** ```shell npx @capgo/cli@latest bundle encrypt ./com.example.app.zip CHECKSUM ``` 3. **Zu S3 mit Verschlüsselung hochladen:** ```shell npx @capgo/cli@latest bundle upload \ --s3-region us-west-2 \ --s3-apikey YOUR_ACCESS_KEY \ --s3-apisecret YOUR_SECRET_KEY \ --s3-bucket-name your-app-bundles \ --iv-session-key IV_SESSION_KEY_FROM_STEP_2 \ --channel Production ``` ## Sicherheitsüberlegungen [Section titled “Sicherheitsüberlegungen”](#sicherheitsüberlegungen) Bei Verwendung von benutzerdefiniertem Speicher sollten Sie diese Sicherheits-Best-Practices beachten: ### Zugriffskontrolle [Section titled “Zugriffskontrolle”](#zugriffskontrolle) * Stellen Sie sicher, dass Ihre Speicher-URLs für Ihre App-Benutzer zugänglich, aber nicht öffentlich auffindbar sind * Verwenden Sie signierte URLs oder tokenbasierte Authentifizierung, wenn möglich * Implementieren Sie ordnungsgemäße CORS-Header für webbasierte Apps ### Verschlüsselung [Section titled “Verschlüsselung”](#verschlüsselung) * Verschlüsseln Sie immer sensible Bundles mit den Capgo-Verschlüsselungstools * Speichern Sie Verschlüsselungsschlüssel sicher und rotieren Sie sie regelmäßig * Verwenden Sie HTTPS für alle Bundle-URLs (erforderlich für iOS und Android) ### Überwachung [Section titled “Überwachung”](#überwachung) * Überwachen Sie Zugriffsprotokolle, um ungewöhnliche Download-Muster zu erkennen * Richten Sie Benachrichtigungen für fehlgeschlagene Bundle-Downloads ein * Überprüfen Sie regelmäßig Ihre Speicherberechtigungen ## Fehlerbehebung [Section titled “Fehlerbehebung”](#fehlerbehebung) ### Häufige Probleme [Section titled “Häufige Probleme”](#häufige-probleme) **Bundle wird nicht heruntergeladen:** * Überprüfen Sie, ob die URL öffentlich zugänglich ist und HTTPS verwendet (erforderlich für iOS und Android) * Prüfen Sie CORS-Header für Web-Apps * Stellen Sie sicher, dass das Bundle-Format korrekt ist **Verschlüsselungsfehler:** * Überprüfen Sie, ob der `ivSessionKey` mit dem verschlüsselten Bundle übereinstimmt * Prüfen Sie, ob das Bundle mit dem richtigen Schlüssel verschlüsselt wurde * Stellen Sie sicher, dass Verschlüsselung v2 für neue Bundles verwendet wird **S3-Upload-Fehler:** * Überprüfen Sie Ihre S3-Anmeldeinformationen und Berechtigungen * Prüfen Sie Bucket-Richtlinien und CORS-Konfiguration * Stellen Sie sicher, dass die angegebene Region korrekt ist ### Debug-Befehle [Section titled “Debug-Befehle”](#debug-befehle) Bundle-Status überprüfen: ```shell npx @capgo/cli@latest app debug ``` Bundle-Integrität überprüfen: ```shell npx @capgo/cli@latest bundle list ``` ## Nächste Schritte [Section titled “Nächste Schritte”](#nächste-schritte) * Erfahren Sie mehr über [Channels](/docs/live-updates/channels/), um verschiedene Bereitstellungsumgebungen zu verwalten * Erkunden Sie [Update Behavior](/docs/live-updates/update-behavior/), um anzupassen, wie Updates angewendet werden * Richten Sie [CI/CD Integration](/docs/getting-started/cicd-integration/) ein, um Ihren benutzerdefinierten Speicher-Workflow zu automatisieren # Schnelle Aktualisierungen Capgos Live-Update-System kann Updates schneller und effizienter bereitstellen, indem nur die geänderten Dateien und nicht das gesamte JS-Bundle gesendet werden Dies ist besonders vorteilhaft für Benutzer mit langsameren oder getakteten Netzwerkverbindungen, da die Menge der herunterzuladenden Daten minimiert wird Ein zweiter Vorteil ist, wenn die App große Assets enthält, die sich selten ändern, wie Bilder oder Videos - im Vergleich zu gezippten JS-Dateien werden diese nur einmal heruntergeladen ## Wie Differenzielle Updates funktionieren [Section titled “Wie Differenzielle Updates funktionieren”](#wie-differenzielle-updates-funktionieren) Differenzielle Updates in Capgo werden durch das in Ihrer App installierte Capgo-Plugin verarbeitet. Wenn Sie eine neue Version Ihrer App mit der Flag `--partial` hochladen, führt Capgo Folgendes aus: 1. Jede Datei in Ihrem Build wird einzeln hochgeladen 2. Für jede Datei werden Prüfsummen generiert 3. Ein neues JSON-Manifest wird erstellt, das alle Dateien und ihre Prüfsummen auflistet 4. Dieses Manifest wird in die Capgo-Datenbank hochgeladen Wenn ein Gerät, auf dem Ihre App läuft, nach Updates sucht, erhält das Capgo-Plugin das neue Manifest vom Server. Es vergleicht dieses Manifest mit dem aktuellen und identifiziert anhand der Prüfsummen und Dateipfade, welche Dateien sich geändert haben Das Plugin lädt dann nur die geänderten Dateien herunter, anstatt das gesamte JS-Bundle. Es rekonstruiert die neue Version der App, indem es diese heruntergeladenen Dateien mit den unveränderten Dateien kombiniert Manifest Bei differenziellen Updates speichert das Gerät alle heruntergeladenen Dateien in einem gemeinsamen Cache. Capgo wird diesen nie bereinigen, aber das Betriebssystem kann dies jederzeit tun ## Aktivierung von Differenziellen Updates [Section titled “Aktivierung von Differenziellen Updates”](#aktivierung-von-differenziellen-updates) Um differenzielle Updates für Ihre Capgo-App zu aktivieren, verwenden Sie einfach die Flag `--partial` beim Hochladen einer neuen Version: ## Erzwingen von Differenziellen Updates [Section titled “Erzwingen von Differenziellen Updates”](#erzwingen-von-differenziellen-updates) Wenn Sie sicherstellen möchten, dass alle Uploads differenzielle Updates sind und versehentliche vollständige Bundle-Uploads verhindert werden, können Sie die Flag `--partial-only` verwenden: ```shell npx @capgo/cli@latest bundle upload --partial-only ``` Wenn `--partial-only` verwendet wird, lädt Capgo nur einzelne Dateien hoch und generiert ein Manifest. Geräte, die keine partiellen Updates unterstützen, können das Update nicht herunterladen Sie möchten `--partial-only` möglicherweise verwenden, wenn: * Sie immer differenzielle Updates verwenden und nie vollständige Bundle-Uploads zulassen möchten * Sie eine CI/CD-Pipeline einrichten und sicherstellen möchten, dass alle automatisierten Uploads differenziell sind * Ihre App groß ist und die Bandbreite begrenzt ist, sodass Sie Upload-/Download-Größen minimieren müssen Wenn Sie einen vollständigen Bundle-Upload durchführen müssen, während `--partial-only` gesetzt ist, führen Sie den Upload-Befehl einfach ohne `--partial-only` aus. Dies überschreibt die Einstellung für diesen einzelnen Upload und ermöglicht es Ihnen, ein vollständiges Bundle zu pushen, wenn nötig ## Fehlerbehebung [Section titled “Fehlerbehebung”](#fehlerbehebung) Wenn differenzielle Updates nicht zu funktionieren scheinen (d.h. Geräte laden immer das vollständige JS-Bundle herunter, auch bei kleinen Änderungen), überprüfen Sie Folgendes: * Sie verwenden die Flag `--partial` bei jedem Upload einer neuen Version * Bei Verwendung von `--partial-only` stellen Sie sicher, dass Sie die Flag `--partial` nicht versehentlich weggelassen haben * Ihr Gerät läuft mit der neuesten Version des Capgo-Plugins * Ihr Gerät hat eine stabile Netzwerkverbindung und kann die Capgo-Server erreichen Sie können auch die Capgo-Webapp verwenden, um die Details Ihres letzten Uploads zu überprüfen: 1. Gehen Sie zur [Webapp](https://app.capgo.io) 2. Klicken Sie auf Ihre App 3. Klicken Sie auf die Bundles-Nummer in der Statistikleiste 4. Wählen Sie das letzte Bundle 5. Überprüfen Sie das `Partial`-Feld ![bundle type](/bundle_type.webp) Wenn Sie weiterhin Probleme haben, wenden Sie sich bitte an den Capgo-Support für weitere Unterstützung. Sie können die Server-Logs überprüfen, um zu bestätigen, dass Ihre partiellen Uploads korrekt verarbeitet werden und die Geräte die aktualisierten Manifeste erhalten Das war’s! Die Flag `--partial` weist Capgo an, die einzelnen Datei-Uploads und Manifest-Generierung durchzuführen, die für differenzielle Updates benötigt werden Beachten Sie, dass Sie `--partial` jedes Mal verwenden müssen, wenn Sie eine neue Version hochladen, die als differenzielles Update bereitgestellt werden soll. Wenn Sie die Flag weglassen, lädt Capgo das gesamte JS-Bundle als einzelne Datei hoch, und Geräte werden das gesamte Bundle herunterladen, auch wenn sich nur ein kleiner Teil geändert hat # Encryption > Erfahren Sie, wie die Ende-zu-Ende-Verschlüsselung von Capgo Ihre App-Bundles während der Übertragung und Speicherung sichert und Ihren Code und Benutzerdaten schützt. Capgo bietet robuste Ende-zu-Ende-Verschlüsselung für Ihre App-Bundles und stellt sicher, dass Ihr JavaScript-Code und Assets während der Übertragung und Speicherung geschützt sind. Dieses Verschlüsselungssystem ist darauf ausgelegt, Ihnen die vollständige Kontrolle über die Sicherheit Ihrer App zu geben und gleichzeitig den Komfort von Live-Updates beizubehalten. ## Übersicht [Section titled “Übersicht”](#übersicht) Das Verschlüsselungssystem von Capgo verwendet branchenübliche kryptografische Methoden, um Ihre Bundles vor unbefugtem Zugriff zu schützen. Wenn die Verschlüsselung aktiviert ist, werden Ihre Bundles verschlüsselt, bevor sie Ihre Entwicklungsumgebung verlassen, und bleiben verschlüsselt, bis sie von Ihrer App auf dem Gerät des Benutzers entschlüsselt werden. **Echte Ende-zu-Ende-Verschlüsselung**: Im Gegensatz zu anderen OTA-Update-Plattformen, die nur Updates signieren (wobei der Code öffentlich lesbar bleibt), bietet Capgo echte Ende-zu-Ende-Verschlüsselung. Das bedeutet, dass nur Ihre Benutzer Ihre Updates entschlüsseln können - niemand sonst, einschließlich Capgo selbst. Ihr Bundle-Inhalt bleibt während des gesamten Bereitstellungsprozesses vollständig privat und unlesbar. Tip Verschlüsselung ist besonders wichtig für: * Apps, die sensible Daten oder Geschäftslogik verarbeiten * Unternehmensanwendungen mit Compliance-Anforderungen * Apps, die in regulierten Branchen eingesetzt werden * Organisationen mit strengen Sicherheitsrichtlinien ## Wie Verschlüsselung funktioniert [Section titled “Wie Verschlüsselung funktioniert”](#wie-verschlüsselung-funktioniert) Capgo verwendet einen hybriden Verschlüsselungsansatz, der RSA- und AES-Verschlüsselung für optimale Sicherheit und Leistung kombiniert: ![Capgo Encryption Flow](/encryption_flow.webp) ### 1. Schlüsselerzeugung [Section titled “1. Schlüsselerzeugung”](#1-schlüsselerzeugung) * **Privater Schlüssel**: In Ihrer Entwicklungsumgebung generiert und sicher gespeichert (wird für Verschlüsselung verwendet) * **Öffentlicher Schlüssel**: Von Ihrem privaten Schlüssel abgeleitet und in der Capacitor-Konfiguration Ihrer App gespeichert (wird für Entschlüsselung verwendet) * **Session-Schlüssel**: Zufällige AES-Schlüssel, die für jedes Bundle-Upload generiert werden ### 2. Verschlüsselungsprozess [Section titled “2. Verschlüsselungsprozess”](#2-verschlüsselungsprozess) 1. Ein zufälliger AES-Session-Schlüssel wird für jedes Bundle-Upload generiert 2. Ihr Bundle wird mit dem AES-Session-Schlüssel verschlüsselt 3. Die Bundle-Prüfsumme wird berechnet 4. Sowohl der AES-Session-Schlüssel als auch die Prüfsumme werden zusammen mit Ihrem RSA-Privatschlüssel verschlüsselt (wodurch die “Signatur” erstellt wird) 5. Das verschlüsselte Bundle und die verschlüsselte Signatur werden gespeichert Die Prüfsumme wird zusammen mit dem AES-Schlüssel verschlüsselt, um Manipulationen zu verhindern. Da nur Ihr RSA-Privatschlüssel diese Signatur erstellen kann und nur der entsprechende öffentliche Schlüssel sie entschlüsseln kann, wird sichergestellt, dass sowohl der AES-Session-Schlüssel als auch die erwartete Prüfsumme authentisch sind und nicht von einem Angreifer modifiziert wurden. ### 3. Entschlüsselungsprozess [Section titled “3. Entschlüsselungsprozess”](#3-entschlüsselungsprozess) 1. Ihre App lädt das verschlüsselte Bundle und die verschlüsselte Signatur herunter 2. Das Capgo SDK verwendet Ihren RSA-öffentlichen Schlüssel (in der App gespeichert), um die Signatur zu entschlüsseln 3. Dies enthüllt den AES-Session-Schlüssel und die ursprüngliche Prüfsumme 4. Der AES-Session-Schlüssel wird verwendet, um das Bundle zu entschlüsseln 5. Eine Prüfsumme des entschlüsselten Bundles wird berechnet und mit der ursprünglichen Prüfsumme zur Integritätsprüfung verglichen Dieser Prozess stellt sicher, dass selbst wenn ein Angreifer das verschlüsselte Bundle abfängt, er weder den AES-Session-Schlüssel ändern noch eine gefälschte Prüfsumme bereitstellen kann, da er Ihren privaten Schlüssel benötigen würde, um eine gültige Signatur zu erstellen, die der öffentliche Schlüssel entschlüsseln kann. Tip RSA kann große Datenmengen nicht effizient verschlüsseln, daher wird AES für die eigentliche Bundle-Verschlüsselung verwendet, während RSA den AES-Schlüssel sichert und Integritätsprüfung durch Prüfsummen-Signierung bietet. ## Capgo vs. andere Plattformen [Section titled “Capgo vs. andere Plattformen”](#capgo-vs-andere-plattformen) | Merkmal | Capgo | Andere OTA-Plattformen | | ---------------------- | --------------------------------------------------------- | --------------------------------------- | | **Bundle-Inhalt** | Vollständig verschlüsselt (unlesbar) | Öffentlich lesbar | | **Sicherheitsmethode** | Echte Ende-zu-Ende-Verschlüsselung | Nur Code-Signierung | | **Datenschutzniveau** | Zero-Knowledge (selbst Capgo kann Ihren Code nicht lesen) | Plattform kann auf Ihren Code zugreifen | | **Schutz** | Inhalt + Integrität + Authentizität | Nur Integrität + Authentizität | **Warum das wichtig ist:** * **Code-Signierung** überprüft nur, dass Updates nicht manipuliert wurden und aus der richtigen Quelle stammen * **Ende-zu-Ende-Verschlüsselung** stellt sicher, dass Ihr tatsächlicher Code-Inhalt während der Übertragung und Speicherung privat und unlesbar bleibt * Mit Capgos echter Ende-zu-Ende-Verschlüsselung können nur Ihre Benutzer Updates entschlüsseln - niemand sonst, einschließlich Capgo selbst ## Verschlüsselungsmethoden [Section titled “Verschlüsselungsmethoden”](#verschlüsselungsmethoden) Capgo verwendet Verschlüsselung V2 als Standard-Verschlüsselungsmethode: ### Verschlüsselung V2 (Aktueller Standard) [Section titled “Verschlüsselung V2 (Aktueller Standard)”](#verschlüsselung-v2-aktueller-standard) * Verwendet RSA-4096 für erhöhte Sicherheit * AES-256-GCM für authentifizierte Verschlüsselung * Bietet Integritätsprüfung * Bessere Leistung und Sicherheit ### Verschlüsselung V1 (Veraltet) [Section titled “Verschlüsselung V1 (Veraltet)”](#verschlüsselung-v1-veraltet) * Verwendet RSA-2048 für Schlüsselverschlüsselung * AES-256-CBC für Bundle-Verschlüsselung * **In der aktuellen CLI nicht mehr verfügbar** * Legacy-Apps, die V1 verwenden, müssen auf V2 migrieren Danger Verschlüsselung V1 wird in der aktuellen Capgo CLI nicht mehr unterstützt. Wenn Sie V1-Verschlüsselung verwenden, müssen Sie auf V2 migrieren. Siehe den [Migrationsleitfaden](/docs/upgrade/encryption-v1-to-v2/) für detaillierte Anweisungen. ## Verschlüsselung einrichten [Section titled “Verschlüsselung einrichten”](#verschlüsselung-einrichten) ### Schritt 1: Verschlüsselungsschlüssel generieren [Section titled “Schritt 1: Verschlüsselungsschlüssel generieren”](#schritt-1-verschlüsselungsschlüssel-generieren) Generieren Sie zunächst Ihre Verschlüsselungsschlüssel mit der Capgo CLI: ```shell # Neue Verschlüsselungsschlüssel generieren (erstellt Dateien im aktuellen Verzeichnis) npx @capgo/cli@latest key create ``` Dies erstellt: * `.capgo_key_v2`: Ihr privater Schlüssel (halten Sie diesen sicher!) * `.capgo_key_v2.pub`: Ihr öffentlicher Schlüssel (wird von Ihrer App verwendet) Diese Dateien werden im aktuellen Verzeichnis erstellt, in dem Sie den Befehl ausführen. Caution **Wichtige Speicherhinweise:** * **Privater Schlüssel (`.capgo_key_v2`)**: Committen Sie diesen niemals in die Versionskontrolle. Diese Datei sollte sicher aufbewahrt und nur für die Verschlüsselung während Bundle-Uploads verwendet werden. * **Öffentlicher Schlüssel (`.capgo_key_v2.pub`)**: Dies kann sicher in die Versionskontrolle committiert werden, da es eine Sicherung Ihres öffentlichen Schlüssels ist. * **Dateispeicherort**: Schlüssel werden im aktuellen Verzeichnis erstellt, in dem Sie den Befehl `key create` ausführen. * **Öffentlicher Schlüssel in Konfiguration**: Sie müssen `key save` ausführen, um den öffentlichen Schlüssel in Ihrer Capacitor-Konfiguration zu speichern, damit die mobile App ihn verwenden kann. Für die Produktionsnutzung speichern Sie den privaten Schlüssel sicher (Umgebungsvariablen, Schlüsselverwaltungsdienste) und entfernen Sie ihn nach dem Setup aus Ihrem lokalen Projekt. ### Schritt 2: Öffentlichen Schlüssel in Capacitor-Konfiguration speichern (Erforderlich) [Section titled “Schritt 2: Öffentlichen Schlüssel in Capacitor-Konfiguration speichern (Erforderlich)”](#schritt-2-öffentlichen-schlüssel-in-capacitor-konfiguration-speichern-erforderlich) Sie **müssen** Ihren öffentlichen Schlüssel in der Capacitor-Konfiguration speichern, damit Ihre mobile App Bundles entschlüsseln kann: ```shell # Öffentlichen Schlüssel aus Datei in Capacitor-Konfiguration speichern (erforderlich) npx @capgo/cli@latest key save --key ./.capgo_key_v2.pub # Oder öffentliche Schlüsseldaten direkt speichern npx @capgo/cli@latest key save --key-data "$CAPGO_PUBLIC_KEY" ``` ### Schritt 3: Capacitor-Plattform synchronisieren (Erforderlich) [Section titled “Schritt 3: Capacitor-Plattform synchronisieren (Erforderlich)”](#schritt-3-capacitor-plattform-synchronisieren-erforderlich) Nachdem Sie den öffentlichen Schlüssel gespeichert haben, **müssen** Sie die Capacitor-Plattform synchronisieren, um die aktualisierte Konfiguration auf die native Ebene zu kopieren: ```shell # Plattform synchronisieren, um Konfiguration auf native Ebene zu kopieren npx cap sync ``` Caution **Erforderliche Schritte**: 1. Der Befehl `key save` speichert den öffentlichen Schlüssel in Ihrer Capacitor-Konfiguration 2. `npx cap sync` kopiert diese Konfiguration auf die native Ebene, wo die mobile App darauf zugreifen kann 3. Ohne beide Schritte kann Ihre App verschlüsselte Updates nicht entschlüsseln ## Bundles verschlüsseln [Section titled “Bundles verschlüsseln”](#bundles-verschlüsseln) ### Methode 1: Während des Uploads verschlüsseln [Section titled “Methode 1: Während des Uploads verschlüsseln”](#methode-1-während-des-uploads-verschlüsseln) Der einfachste Weg ist die Verschlüsselung während des Upload-Prozesses: ```shell # Upload mit automatischer Verschlüsselung npx @capgo/cli@latest bundle upload --key-v2 # Für externen Speicher müssen Sie zuerst verschlüsseln (siehe Manuelle Verschlüsselungs-Workflow unten) ``` ### Methode 2: Manueller Verschlüsselungs-Workflow [Section titled “Methode 2: Manueller Verschlüsselungs-Workflow”](#methode-2-manueller-verschlüsselungs-workflow) Für mehr Kontrolle können Sie Bundles manuell verschlüsseln: 1. **Ein Zip-Bundle erstellen:** ```shell npx @capgo/cli@latest bundle zip com.example.app --path ./dist --key-v2 ``` 2. **Bundle verschlüsseln:** ```shell npx @capgo/cli@latest bundle encrypt ./com.example.app.zip CHECKSUM_FROM_STEP_1 ``` 3. **In Ihren Speicher hochladen (z.B. S3) und bei Capgo registrieren:** ```shell # Zuerst verschlüsseltes Bundle in Ihren Speicher hochladen (z.B. AWS S3) aws s3 cp ./encrypted-bundle.zip s3://your-bucket/encrypted-bundle.zip # Dann bei Capgo mit externer URL registrieren npx @capgo/cli@latest bundle upload --external https://your-storage.com/encrypted-bundle.zip --iv-session-key IV_SESSION_KEY_FROM_STEP_2 ``` ## Schlüsselverwaltung [Section titled “Schlüsselverwaltung”](#schlüsselverwaltung) ### Schlüssel sicher speichern [Section titled “Schlüssel sicher speichern”](#schlüssel-sicher-speichern) **Optionen für privaten Schlüssel:** 1. **Dateibasiert (lokale Entwicklung):** ```shell # Schlüssel als .capgo_key_v2-Datei im Projektstamm gespeichert npx @capgo/cli@latest bundle upload --key-v2 ``` 2. **Umgebungsvariable (CI/CD):** ```shell # In Umgebungsvariable für CI speichern export CAPGO_PRIVATE_KEY="$(cat .capgo_key_v2)" npx @capgo/cli@latest bundle upload --key-data-v2 "$CAPGO_PRIVATE_KEY" ``` **Öffentliche Schlüssel-Einrichtung (Erforderlich):** ```shell # Öffentlichen Schlüssel in Capacitor-Konfiguration für mobile App speichern npx @capgo/cli@latest key save --key ./.capgo_key_v2.pub ``` **Produktionsumgebung:** * Speichern Sie private Schlüssel in sicheren Schlüsselverwaltungsdiensten (AWS KMS, Azure Key Vault usw.) * Verwenden Sie CI/CD-Geheimnisverwaltung für private Schlüssel * Committen Sie niemals private Schlüssel in die Versionskontrolle **Schlüsselverwendung:** * **Privater Schlüssel**: Wird von CLI für Verschlüsselung während Bundle-Upload verwendet (sicher aufbewahren) * **Öffentlicher Schlüssel**: In App-Konfiguration für Entschlüsselung auf Gerät gespeichert (sicher zu committen) ### Schlüsselrotation [Section titled “Schlüsselrotation”](#schlüsselrotation) Rotieren Sie regelmäßig Ihre Verschlüsselungsschlüssel für erhöhte Sicherheit: 1. **Neue Schlüssel generieren:** ```shell # Navigieren Sie zuerst zum gewünschten Verzeichnis, dann Schlüssel erstellen mkdir ./new-keys && cd ./new-keys npx @capgo/cli@latest key create ``` 2. **Neuen öffentlichen Schlüssel in Capacitor-Konfiguration speichern:** ```shell npx @capgo/cli@latest key save --key ./new-keys/.capgo_key_v2.pub ``` 3. **Aktualisieren Sie Ihre App-Konfiguration** mit dem neuen öffentlichen Schlüssel 4. **Deployen Sie die aktualisierte App**, bevor Sie verschlüsselte Bundles mit dem neuen Schlüssel hochladen ## Sicherheits-Best-Practices [Section titled “Sicherheits-Best-Practices”](#sicherheits-best-practices) ### Schlüsselsicherheit [Section titled “Schlüsselsicherheit”](#schlüsselsicherheit) * **Teilen Sie niemals private Schlüssel** zwischen Umgebungen oder Teammitgliedern * **Verwenden Sie unterschiedliche Schlüssel** für verschiedene Umgebungen (dev, staging, production) * **Rotieren Sie Schlüssel regelmäßig** (empfohlen: alle 6-12 Monate) * **Speichern Sie Schlüssel sicher** mit geeigneten Schlüsselverwaltungssystemen ### Bundle-Sicherheit [Section titled “Bundle-Sicherheit”](#bundle-sicherheit) * **Überprüfen Sie immer** die Bundle-Integrität nach der Entschlüsselung * **Überwachen Sie** auf ungewöhnliche Download-Muster oder Fehler * **Verwenden Sie HTTPS** für alle Bundle-URLs (erforderlich für mobile Apps) * **Implementieren Sie** ordnungsgemäße Fehlerbehandlung für Entschlüsselungsfehler ### Zugriffskontrolle [Section titled “Zugriffskontrolle”](#zugriffskontrolle) * **Beschränken Sie den Zugriff** auf Verschlüsselungsschlüssel nur auf autorisiertes Personal * **Verwenden Sie rollenbasierte Zugriffskontrolle** für Schlüsselverwaltungsoperationen * **Prüfen Sie** regelmäßig Schlüsselnutzung und -zugriff * **Implementieren Sie** ordnungsgemäße Backup- und Wiederherstellungsverfahren ## Fehlerbehebung bei Verschlüsselung [Section titled “Fehlerbehebung bei Verschlüsselung”](#fehlerbehebung-bei-verschlüsselung) ### Häufige Probleme [Section titled “Häufige Probleme”](#häufige-probleme) **Entschlüsselungsfehler:** * Überprüfen Sie, ob der private Schlüssel mit dem für die Verschlüsselung verwendeten öffentlichen Schlüssel übereinstimmt * Prüfen Sie, ob der `ivSessionKey` korrekt ist * Stellen Sie sicher, dass Sie Verschlüsselung V2 verwenden (V1 wird nicht mehr unterstützt) **Schlüsselbezogene Fehler:** * Bestätigen Sie, dass das Format des privaten Schlüssels korrekt ist (PEM-Format) * Überprüfen Sie, ob der Schlüssel während der Speicherung/Übertragung nicht beschädigt wurde * Prüfen Sie, ob der Schlüssel die richtigen Berechtigungen in Ihrer App-Konfiguration hat **Leistungsprobleme:** * Große Bundles können länger zum Ver-/Entschlüsseln benötigen * Erwägen Sie die Verwendung von differenziellen Updates, um Bundle-Größen zu reduzieren * Überwachen Sie die Geräteleistung während der Entschlüsselung ### Debug-Befehle [Section titled “Debug-Befehle”](#debug-befehle) Verschlüsselungsstatus überprüfen: ```shell npx @capgo/cli@latest app debug ``` Verschlüsselungs-/Entschlüsselungs-Workflow testen: ```shell # Vollständigen Workflow testen: zip → encrypt → decrypt → unzip npx @capgo/cli@latest bundle zip com.example.app --key-v2 npx @capgo/cli@latest bundle encrypt ./com.example.app.zip CHECKSUM --json npx @capgo/cli@latest bundle decrypt ./encrypted-bundle.zip IV_SESSION_KEY ``` ## Compliance und Standards [Section titled “Compliance und Standards”](#compliance-und-standards) Die Verschlüsselungsimplementierung von Capgo folgt Industriestandards: * **AES-256**: FIPS 140-2 genehmigter Verschlüsselungsalgorithmus * **RSA-4096**: Starke asymmetrische Verschlüsselung für Schlüsselschutz * **GCM-Modus**: Bietet sowohl Vertraulichkeit als auch Authentizität * **Sichere Zufallszahlen**: Kryptografisch sichere Zufallszahlengenerierung Dies macht Capgo geeignet für Anwendungen, die Compliance benötigen mit: * GDPR (General Data Protection Regulation) * HIPAA (Health Insurance Portability and Accountability Act) * SOC 2 (Service Organization Control 2) * ISO 27001 (Information Security Management) ## Leistungsüberlegungen [Section titled “Leistungsüberlegungen”](#leistungsüberlegungen) ### Verschlüsselungs-Overhead [Section titled “Verschlüsselungs-Overhead”](#verschlüsselungs-overhead) * **Bundle-Größe**: Verschlüsselte Bundles sind etwas größer (\~1-2% Overhead) * **Verarbeitungszeit**: Ver-/Entschlüsselung fügt minimale Latenz hinzu * **Speichernutzung**: Temporärer Anstieg während Ver-/Entschlüsselungsoperationen ### Optimierungstipps [Section titled “Optimierungstipps”](#optimierungstipps) * Verwenden Sie differentielle Updates, um verschlüsselte Datenübertragung zu minimieren * Optimieren Sie Ihre Bundle-Größe durch Konvertierung von Bildern ins WebP-Format * Minimieren Sie JavaScript- und CSS-Dateien vor dem Bundling * Entfernen Sie ungenutzte Abhängigkeiten und Code * Überwachen Sie die Geräteleistung auf älteren/langsameren Geräten ## Nächste Schritte [Section titled “Nächste Schritte”](#nächste-schritte) * Erfahren Sie mehr über [Custom Storage](/docs/live-updates/custom-storage/), um Verschlüsselung mit Ihrer eigenen Infrastruktur zu verwenden * Erkunden Sie [Channels](/docs/live-updates/channels/), um verschlüsselte Bundles über Umgebungen hinweg zu verwalten * Richten Sie [CI/CD Integration](/docs/getting-started/cicd-integration/) ein, um verschlüsselte Bereitstellungen zu automatisieren # Funktionen > Vollständige Referenz aller Capgo Live Update-Funktionen, vom Kern-Update-System bis hin zu fortgeschrittenen Bereitstellungskontrollen, Analytik und Funktionen zur Teamzusammenarbeit. Diese Seite bietet einen umfassenden Überblick über alle in Capgo Live Updates verfügbaren Funktionen. Jede Funktion enthält eine kurze Beschreibung und Links zur detaillierten Dokumentation. ## Kern-Update-System [Section titled “Kern-Update-System”](#kern-update-system) ### Over-the-Air (OTA) Updates [Section titled “Over-the-Air (OTA) Updates”](#over-the-air-ota-updates) Bereitstellung von JavaScript-, HTML-, CSS- und Asset-Updates direkt an die Benutzer ohne Genehmigung durch den App-Store. Updates werden im Hintergrund heruntergeladen und beim nächsten Neustart der App angewendet. **Kernfunktionen:** * Downloads im Hintergrund * Automatische Installation * Keine Unterbrechung durch den Benutzer * Plattformübergreifende Unterstützung (iOS, Android, Electron) [Weitere Informationen zum Verhalten von Updates →](/docs/live-updates/update-behavior/) *** ### Delta Updates (Differenzielle Updates) [Section titled “Delta Updates (Differenzielle Updates)”](#delta-updates-differenzielle-updates) Nur Dateien herunterladen, die sich zwischen Versionen geändert haben, wodurch der Bandbreitenverbrauch um bis zu 95 % reduziert und die Bereitstellung von Updates beschleunigt wird. **Kernfunktionen:** * Automatischer Unterschied-Vergleich auf Dateiebene * Prüfum Basis auf Checksummen * Manifest-Vergleich * Intelligenter Rückgriff auf vollständige Updates bei Bedarf [Weitere Informationen zu Differenz-Updates →](/docs/live-updates/differentials/) *** ### Automatisches Rollback [Section titled “Automatisches Rollback”](#automatisches-rollback) Wenn ein Update nicht lädt oder Abstürze verursacht, setzt das System automatisch auf die zuletzt bekannte funktionierende Version zurück. **Kernfunktionen:** * Crash-Erkennung * Timeout-Erkennung * Automatische Rückkehr * Keine Benutzereingriffe erforderlich [Weitere Informationen zu Rollbacks →](/docs/live-updates/rollbacks/) *** ### Prüfsummen-Validierung & Fallback [Section titled “Prüfsummen-Validierung & Fallback”](#prüfsummen-validierung--fallback) Überprüft die Integrität des Bundles anhand von Checksummen und fällt bei erkanntem Schaden automatisch auf die zuletzt funktionierende Version zurück. **Kernfunktionen:** * Prüfsummen-Validierung beim Download * Erkennung von Beschädigungen * Automatischer Fallback auf das zuletzt funktionierende Bundle * Manuelle Wiederherstellungswerkzeuge verfügbar *** ### Breaking Update Detection [Section titled “Breaking Update Detection”](#breaking-update-detection) Verhindert, dass inkompatible Updates auf Geräten mit älteren nativen Code-Versionen angewendet werden. **Kernfunktionen:** * Prüfung der Kompatibilität nativer Versionen * Validierung von Plugin-Abhängigkeiten * Automatisches Blockieren inkompatibler Updates * Klare Fehlermeldungen [Weitere Informationen zur Versionszielausrichtung →](/docs/live-updates/version-targeting/) *** ## Deployment-Kontrolle [Section titled “Deployment-Kontrolle”](#deployment-kontrolle) ### Kanal-System [Section titled “Kanal-System”](#kanal-system) Organisieren und verwalten Sie Updates über verschiedene Umgebungen und Benutzersegmente hinweg mit flexiblen Kanal-Konfigurationen. **Kernfunktionen:** * Unbegrenzte benutzerdefinierte Kanäle (Produktion, Staging, Beta usw.) * Bündelzuweisungen pro Kanal * Kanalspezifische Zielregeln * Geräte-Selbstzuordnung * Kanalkonflikt pro Gerät [Weitere Informationen zu Kanälen →](/docs/live-updates/channels/) *** ### Geräte-Zielauswahl [Section titled “Geräte-Zielauswahl”](#geräte-zielauswahl) Gezielte Ansprache bestimmter Geräte, Versionen oder Benutzersegmente für gestaffelte Rollouts und kontrollierte Bereitstellungen. **Kernfunktionen:** * Versionsbasierte Zielauswahl * Gerätebezogene Überschreibungen * Plattform-Filterung (iOS, Android) * Benutzerdefinierte Metadaten-Filterung * Emulator- bzw. Entwicklungsbuild-Sperrung *** ### Kanalrichtlinien [Section titled “Kanalrichtlinien”](#kanalrichtlinien) Richten Sie Regeln und Einschränkungen dafür ein, wie Updates auf jedem Kanal ausgeliefert werden. **Kernfunktionen:** * Auto-Updates deaktivieren * Großversions-Updates blockieren * Updates auf Emulatoren deaktivieren * Updates in Entwicklungs-Builds deaktivieren * Plattformabhängige Richtlinien (nur iOS, nur Android) [Weitere Informationen zu Kanalrichtlinien →](/docs/live-updates/channels/#channel-policies) *** ## Entwicklertools [Section titled “Entwicklertools”](#entwicklertools) ### Bundle-Vorschau [Section titled “Bundle-Vorschau”](#bundle-vorschau) Vorschau von Bundles in einer Live-Web-Umgebung vor dem Bereitstellen auf Geräten, zugänglich über das Web-Dashboard. **Standort:** Web Dashboard → App → Bundle → Vorschau-Reiter *** ### Live-Debugging [Section titled “Live-Debugging”](#live-debugging) Echtzeit-Überwachung von Update-Ereignissen für bestimmte Geräte über die CLI, zeigt Prüf-, Download-, Installations- und Fehlerereignisse. **Verwendung:** ```bash npx @capgo/cli app debug [appId] ``` **Zeigt an:** * Update-Prüfungen * Download-Fortschritt * Installationsstatus * Fehlermeldungen * Richtlinienblockaden *** ### Bundle-Manifest-Viewer [Section titled “Bundle-Manifest-Viewer”](#bundle-manifest-viewer) Untersuchen Sie das vollständige Manifest eines Bundles einschließlich Dateiliste, Checksummen und Metadaten. **Standort:** Web Dashboard → App → Bundle → Manifest-Reiter **Zeigt an:** * Dateiliste mit Checksums * Bundle-Metadaten * Native Version-Kompatibilität * Plugin-Abhängigkeiten *** ### Native Plugin-Abhängigkeiten [Section titled “Native Plugin-Abhängigkeiten”](#native-plugin-abhängigkeiten) Zeigen Sie alle nativen Capacitor-Plugins, die in jedem Bundle enthalten sind, um Abhängigkeitsänderungen über Versionen hinweg zu verfolgen. **Standort:** Web Dashboard → App → Bundle → Dependencies Reiter **Zeigt an:** * Plugin-Namen und Versionen * Abhängigkeits-Hinzufügungen/-Entfernungen * Kompatibilitätswarnungen *** ### CLI-Integration [Section titled “CLI-Integration”](#cli-integration) Umfassende Kommandozeilen-Schnittstelle für automatisierte Bereitstellungen und CI/CD-Integration. **Kernbefehle:** * `bundle upload` - Neue Bundles hochladen * `bundle list` - Alle Bundles auflisten * `bundle delete` - Bundles löschen * `bundle cleanup` - Alte Bundles bereinigen * `channel set` - Kanäle konfigurieren * `app debug` - Live-Debugging [Vollständige CLI-Referenz ansehen →](/docs/cli/reference/) *** ### Bundle-Verschlüsselung [Section titled “Bundle-Verschlüsselung”](#bundle-verschlüsselung) End-to-End-Verschlüsselung für Bundles mit AES-256-Verschlüsselung, schützt Ihren Code während der Übertragung und im Ruhezustand. **Kernfunktionen:** * Generierung eines RSA-Schlüsselpaares * AES-256 Bundle-Verschlüsselung * Signaturüberprüfung des Codes * Verwaltung von Verschlüsselungsschlüsseln [Weitere Informationen zur Verschlüsselung →](/docs/live-updates/encryption/) *** ### Bundle-Bereinigung & Aufbewahrung [Section titled “Bundle-Bereinigung & Aufbewahrung”](#bundle-bereinigung--aufbewahrung) Bereinigt automatisch alte Bundles basierend auf Aufbewahrungsrichtlinien, um den Speicherverbrauch zu verwalten. **Kernfunktionen:** * Konfigurierbare Aufbewahrungsanzahl * Automatische Bereinigung über CLI * Geplante Bereinigungsaufträge * Speicherverbrauch-Überwachung **Verwendung:** ```bash npx @capgo/cli bundle cleanup --keep=10 ``` *** ## Analytik & Überwachung [Section titled “Analytik & Überwachung”](#analytik--überwachung) ### Update-Statistiken [Section titled “Update-Statistiken”](#update-statistiken) Verfolgen Sie Akzeptanzraten von Updates, Erfolgsquoten und Bereitstellungsfortschritt in Ihrer Benutzerbasis. **Verfügbare Kennzahlen:** * Erfolgsquote beim Herunterladen * Erfolgsquote bei der Installation * Fehlerquoten nach Typ * Update-Adoption im Zeitverlauf * Versionsverteilung **Standort:** Web Dashboard → App → Statistiken *** ### Geräte-Protokolle [Section titled “Geräte-Protokolle”](#geräte-protokolle) Ereignisprotokolle pro Gerät, die den gesamten Update-Zyklus von Prüfung bis Installation zeigen. **Ereignistypen:** * Update-Prüfungen * Download-Start/Abschluss/Fehler * Installationsstart/Abschluss/Fehler * Rollback-Ereignisse * Richtlinienblockaden **Standort:** * Web Dashboard → App → Device → Logs * Web Dashboard → App → Logs (alle Geräte) [Weitere Informationen zu Protokollen →](/docs/webapp/logs/) *** ### Bundle-Nutzungsanalytik [Section titled “Bundle-Nutzungsanalytik”](#bundle-nutzungsanalytik) Detaillierte Analysen darüber, welche Bundles aktiv sind, Download-Anzahlen und Speicherverbrauch. **Kennzahlen:** * Aktive Installationen pro Bundle * Download-Anzahlen * Speicherverbrauch pro Bundle * Bandbreitennutzung *** ### Kanalstatistiken [Section titled “Kanalstatistiken”](#kanalstatistiken) Verfolgen Sie Leistungs- und Akzeptanzmetriken pro Kanal. **Kennzahlen:** * Geräte pro Kanal * Erfolgsquoten bei Updates pro Kanal * Bereitstellungshistorie * Fehlerquoten nach Kanal **Standort:** Web Dashboard → App → Channel → Statistiken *** ### Bereitstellungshistorie [Section titled “Bereitstellungshistorie”](#bereitstellungshistorie) Vollständige Audit-Trails aller Bundle-Bereitstellungen, Kanalzuweisungen und Konfigurationsänderungen. **Verfolgte Ereignisse:** * Bundle-Uploads * Kanalzuweisungen * Richtlinienänderungen * Geräteüberschreibungen **Standort:** Web Dashboard → App → Channel → History *** ## Sicherheit & Compliance [Section titled “Sicherheit & Compliance”](#sicherheit--compliance) ### End-to-End-Verschlüsselung [Section titled “End-to-End-Verschlüsselung”](#end-to-end-verschlüsselung) Verschlüsselt Bundles im Ruhezustand und während der Übertragung mit der AES-256-Verschlüsselung gemäß Industriestandard. [Weitere Informationen zur Verschlüsselung →](/docs/live-updates/encryption/) *** ### Code-Signierung [Section titled “Code-Signierung”](#code-signierung) Überprüfen Sie die Integrität von Bundles mithilfe kryptografischer Signaturen, um Manipulationen zu verhindern. *** ### SOC 2 Typ II-Compliance [Section titled “SOC 2 Typ II-Compliance”](#soc-2-typ-ii-compliance) Infrastruktur und Prozesse zertifiziert nach SOC 2 Type II Standards für Unternehmenssicherheit. *** ### App-Store-Compliance [Section titled “App-Store-Compliance”](#app-store-compliance) Vollständige Einhaltung der Richtlinien des Apple App Store und des Google Play Store für OTA-Updates. [Weitere Informationen zur Compliance →](/docs/live-updates/compliance/) *** ### 2FA-Verpflichtung (Organisationsebene) [Section titled “2FA-Verpflichtung (Organisationsebene)”](#2fa-verpflichtung-organisationsebene) Erfordern Sie eine Zwei-Faktor-Authentifizierung für alle Organisationsmitglieder, um auf das Dashboard und die API zuzugreifen. **Standort:** Web Dashboard → Organization → Security [Weitere Informationen zur 2FA →](/docs/webapp/2fa-enforcement/) *** ### Durchsetzung verschlüsselter Bundles [Section titled “Durchsetzung verschlüsselter Bundles”](#durchsetzung-verschlüsselter-bundles) Verlangen Sie, dass alle Bundles auf Organisationsebene verschlüsselt sind.**Standort:** Web Dashboard → Organisation → Sicherheit *** ## Teamzusammenarbeit [Section titled “Teamzusammenarbeit”](#teamzusammenarbeit) ### Rollenbasierte Zugriffskontrolle (RBAC) [Section titled “Rollenbasierte Zugriffskontrolle (RBAC)”](#rollenbasierte-zugriffskontrolle-rbac) Genaue Berechtigungen für die Organisations- und App-Ebene der Zugriffskontrolle **Organisationsrollen:** 1. `super_admin` - Voller Zugriff 2. `admin` - Administrativer Zugriff 3. `read` - Nur-Lesezugriff 4. `upload` - Nur-Upload-Zugriff **App-Rollen:** 1. `app_developer` - Voller App-Zugriff 2. `app_uploader` - Nur Upload von Bundles 3. `app_reader` - Nur-Lesezugriff **Standort:** 1. Web Dashboard → Organisation → Mitglieder 2. Web Dashboard → App → Zugriff [Learn more about RBAC →](/docs/webapp/organization-system/#roles-and-permissions) *** ### Audit-Protokolle [Section titled “Audit-Protokolle”](#audit-protokolle) Vollständige Audit-Spur aller Aktivitäten der Organisation und der Apps zur Einhaltung von Vorschriften und Sicherheit. **Protokollierte Ereignisse:** 1. Benutzeraktionen (Anmeldung, Abmeldung, Berechtigungsänderungen) 2. Bundle-Operationen (Upload, Löschen, Zuweisen) 3. Kanal-Operationen (Erstellen, Aktualisieren, Löschen) 4. Organisationsänderungen (Einstellungen, Mitglieder) **Standort:** Web Dashboard → Organisation → Audit Logs *** ### Webhooks [Section titled “Webhooks”](#webhooks) Echtzeit-Benachrichtigungen über Ereignisse in Ihren Apps per HTTP-Webhooks empfangen. **Unterstützte Ereignisse:** 1. `apps` - App erstellt/aktualisiert/gelöscht 2. `app_versions` - Bundle hochgeladen/gelöscht 3. `channels` - Kanal erstellt/aktualisiert/gelöscht 4. `org_users` - Mitglied hinzugefügt/entfernt 5. `orgs` - Organisation aktualisiert **Funktionen:** 1. Custom webhook URLs 2. Ereignis-Filterung 3. Zustellungsprotokolle 4. Wiederholungsmechanismus 5. Testfunktionalität **Standort:** Web Dashboard → Organisation → Webhooks *** ### Mehrbenutzer-Zusammenarbeit [Section titled “Mehrbenutzer-Zusammenarbeit”](#mehrbenutzer-zusammenarbeit) Laden Sie Teammitglieder in Ihre Organisation mit bestimmten Rollen und Berechtigungen ein. **Funktionen:** 1. Einladungen per E-Mail 2. Rollenzuweisung 3. Mitgliederverwaltung 4. Zugriff entziehen **Standort:** Web Dashboard → Organisation → Mitglieder *** ### API-Schlüssel-Verwaltung [Section titled “API-Schlüssel-Verwaltung”](#api-schlüssel-verwaltung) Erstellen, verwalten und widerrufen Sie API-Schlüssel mit optionalen Ablaufdaten und gehashter Speicherung. **Schlüssel-Funktionen:** 1. Pro-App- oder Organisationsschlüssel 2. Optionale Ablaufdaten 3. Gehashte Speicherung (irreversibel) 4. Schlüsselrotation unterstützt **Standort:** Web Dashboard → API Keys [Learn more about API keys →](/docs/public-api/#authentication) *** ### Passwortrichtlinien [Section titled “Passwortrichtlinien”](#passwortrichtlinien) Organisationsweite Passwortrichtlinien zur Durchsetzung von Sicherheitsstandards. **Konfigurierbare Richtlinien:** 1. Mindestlänge 2. Großbuchstaben erforderlich 3. Zahlen erforderlich 4. Sonderzeichen erforderlich **Standort:** Web Dashboard → Organisation → Security *** ## Plattformunterstützung [Section titled “Plattformunterstützung”](#plattformunterstützung) ### Plattformübergreifende Unterstützung [Section titled “Plattformübergreifende Unterstützung”](#plattformübergreifende-unterstützung) Unterstützung für iOS-, Android- und Electron-Apps mit einem einzigen SDK. **Unterstützte Plattformen:** 1. iOS (Capacitor 5, 6, 7, 8) 2. Android (Capacitor 5, 6, 7, 8) 3. Electron (NEU im Jahr 2025) *** ### Langzeitunterstützung [Section titled “Langzeitunterstützung”](#langzeitunterstützung) Fortlaufende Unterstützung älterer Capacitor-Versionen zur Aufrechterhaltung der Kompatibilität mit Legacy-Apps. **Derzeit unterstützt:** 1. Capacitor 8 (neueste) 2. Capacitor 7 3. Capacitor 6 4. Capacitor 5 *** ### Eigene Speicher-Backends [Section titled “Eigene Speicher-Backends”](#eigene-speicher-backends) Verwenden Sie Ihre eigene Speicherinfrastruktur (S3, R2, etc.) statt Capgo’s Standard-Speicher. [Learn more about custom storage →](/docs/live-updates/custom-storage/) *** ### China-Konfiguration [Section titled “China-Konfiguration”](#china-konfiguration) Spezielle Konfiguration für Apps, die in Festlandchina verteilt werden, um lokale Vorschriften einzuhalten. [Learn more about China configuration →](/docs/live-updates/china-configuration/) *** ## Erweiterte Funktionen [Section titled “Erweiterte Funktionen”](#erweiterte-funktionen) ### Benutzerdefiniertes Update-Verhalten [Section titled “Benutzerdefiniertes Update-Verhalten”](#benutzerdefiniertes-update-verhalten) Konfigurieren Sie, wann und wie Updates über das SDK geprüft und angewendet werden. **Konfigurierbare Optionen:** 1. Prüfintervall (`periodCheckDelay` - Mindestwert 600 Sekunden) 2. Direktes Update-Timing (`directUpdate` - atInstall, onLaunch, always) 3. Auto-Update aktivieren/deaktivieren (`autoUpdate`) 4. Netzwerk-Anforderungen (nur Android - über WorkManager) [Learn more about update behavior →](/docs/live-updates/update-behavior/) *** ### Update-Typen [Section titled “Update-Typen”](#update-typen) Verschiedene Update-Typen für unterschiedliche Anwendungsfälle, von Sofort-Updates bis hin zu benutzerkontrollierten Installationen. **Verfügbare Typen:** 1. Hintergrund-Updates (Standard) 2. Sofort-Updates 3. Vom Benutzer bestätigte Updates 4. Bedingte Updates [Learn more about update types →](/docs/live-updates/update-types/) *** ### Kreditsystem [Section titled “Kreditsystem”](#kreditsystem) Nutzungsbasierte Abrechnung mit Guthaben für Bandbreite, Speicher und andere Ressourcen. **Funktionen:** 1. Kreditnutzung verfolgen 2. Nutzungsbenachrichtigungen 3. Aufladen via Stripe 4. Kreditbuchführung **Standort:** Web Dashboard → Organisation → Credits *** ## Erste Schritte [Section titled “Erste Schritte”](#erste-schritte) Bereit, diese Funktionen zu nutzen? Befolgen Sie unseren [Quickstart Guide](/docs/getting-started/quickstart/) um Ihre erste App mit Capgo Live Updates einzurichten. ## Hilfe benötigt? [Section titled “Hilfe benötigt?”](#hilfe-benötigt) * [Join our Discord](https://discord.capgo.app) für Community-Support * [Check the FAQ](/docs/faq/) für häufig gestellte Fragen * [Browse API documentation](/docs/public-api/) für API-Integration * [Contact support](https://capgo.app/consulting/) für Unternehmensunterstützung . # CI/CD Integrationen > Integrieren Sie Capgo Live Updates mit Ihrer bevorzugten CI/CD-Plattform für automatisierte Bereitstellungs-Workflows. Automatisieren Sie Ihren Capgo Live Updates-Bereitstellungsprozess durch Integration mit beliebten CI/CD-Plattformen. Diese Integrationen ermöglichen es Ihnen, App-Updates automatisch bereitzustellen, wenn Sie Code-Änderungen pushen, Feature-Branches zu testen und mehrere Bereitstellungsumgebungen zu verwalten. ## Verfügbare Integrationen [Section titled “Verfügbare Integrationen”](#verfügbare-integrationen) Wählen Sie Ihre CI/CD-Plattform, um mit automatisierten Bereitstellungen zu beginnen: [Azure DevOps ](/docs/live-updates/integrations/azure-devops/)Integrieren Sie mit Azure DevOps Pipelines für automatisierte Builds, Tests und Bereitstellungs-Workflows. [GitLab CI/CD ](/docs/live-updates/integrations/gitlab-ci/)Richten Sie GitLab CI/CD-Pipelines ein, um Ihre App-Updates automatisch mit umfassendem Umgebungsmanagement bereitzustellen. [GitHub Actions ](/docs/live-updates/integrations/github-actions/)Verwenden Sie GitHub Actions für leistungsstarke Automatisierung mit Multi-Channel-Bereitstellungen und Umgebungsschutz. [Bitbucket Pipelines ](/docs/live-updates/integrations/bitbucket-pipeline/)Deployen Sie mit Bitbucket Pipelines unter Verwendung einfacher oder erweiterter Konfigurationen für mehrere Umgebungen. ## Was Sie erhalten [Section titled “Was Sie erhalten”](#was-sie-erhalten) Alle Integrationsleitfäden enthalten: * **Einfache Einrichtung**: Grundkonfiguration zum schnellen Einstieg * **Erweiterte Workflows**: Multi-Umgebungs-Bereitstellungen mit Staging und Production * **Feature-Branch-Tests**: Automatische Bereitstellung von Feature-Branches zu Test-Channels * **Sicherheits-Best-Practices**: Sichere Geheimnisverwaltung und Umgebungsschutz * **Überwachung**: Benachrichtigungen und Protokollierung für Bereitstellungsstatus Tip **Neu bei CI/CD?** Beginnen Sie mit der einfachen Konfiguration für Ihre Plattform und fügen Sie dann schrittweise erweiterte Funktionen wie Multi-Channel-Bereitstellungen und automatisierte Tests hinzu, wenn Ihre Anforderungen wachsen. ## Gemeinsame Funktionen [Section titled “Gemeinsame Funktionen”](#gemeinsame-funktionen) Jede Integration unterstützt: * **Automatisierte Builds**: Bereitstellungen bei Code-Änderungen auslösen * **Multi-Channel-Unterstützung**: Zu verschiedenen Channels bereitstellen (development, staging, production) * **Pull-Request/Merge-Request-Tests**: Änderungen in isolierten Umgebungen testen * **Verschlüsselungs-Unterstützung**: Sichere Bereitstellungen mit Capgos Verschlüsselungsfunktion * **Umgebungsschutz**: Manuelle Genehmigungen und eingeschränkter Zugriff für Production * **Benachrichtigungen**: Slack-, E-Mail- und andere Benachrichtigungsintegrationen ## Voraussetzungen [Section titled “Voraussetzungen”](#voraussetzungen) Bevor Sie eine Integration einrichten, stellen Sie sicher, dass Sie haben: * Ein Capgo-Konto mit einer konfigurierten App * Den Quellcode Ihrer App in einem Git-Repository * Ein Capgo API-Token von [console.capgo.app/apikeys](https://console.capgo.app/apikeys) * Node.js und npm/yarn in Ihrem Projekt konfiguriert ## Verwandte Dokumentation [Section titled “Verwandte Dokumentation”](#verwandte-dokumentation) * [Channels](/docs/live-updates/channels/) - Erfahren Sie, wie Sie verschiedene Bereitstellungsumgebungen verwalten * [Encryption](/docs/live-updates/encryption/) - Sichern Sie Ihre Bereitstellungen mit Ende-zu-Ende-Verschlüsselung * [Update Behavior](/docs/live-updates/update-behavior/) - Passen Sie an, wie Updates auf Ihre Apps angewendet werden Wählen Sie oben Ihre CI/CD-Plattform, um mit der Automatisierung Ihrer Capgo-Bereitstellungen zu beginnen! # Azure DevOps Integration > Erfahren Sie, wie Sie Capgo Live Updates mit Azure DevOps Pipelines für die automatisierte Bereitstellung Ihrer App-Updates integrieren. Integrieren Sie Capgo Live Updates mit Azure DevOps Pipelines, um Ihre App-Updates automatisch bereitzustellen, wenn Sie Code-Änderungen pushen. Dieser Leitfaden behandelt die Einrichtung automatisierter Build-, Test- und Bereitstellungs-Workflows. ## Voraussetzungen [Section titled “Voraussetzungen”](#voraussetzungen) Stellen Sie vor der Einrichtung der Azure DevOps-Integration sicher, dass Sie Folgendes haben: * Eine Azure DevOps-Organisation und ein Projekt * Ein Capgo-Konto mit einer konfigurierten App * Den Quellcode Ihrer App in einem Azure Repos Git-Repository * Node.js und npm/yarn in Ihrem Projekt konfiguriert ## Einrichten der Azure DevOps Pipeline [Section titled “Einrichten der Azure DevOps Pipeline”](#einrichten-der-azure-devops-pipeline) ### Schritt 1: Pipeline-Variablen erstellen [Section titled “Schritt 1: Pipeline-Variablen erstellen”](#schritt-1-pipeline-variablen-erstellen) Richten Sie zunächst die erforderlichen Variablen in Ihrem Azure DevOps-Projekt ein: 1. Navigieren Sie zu Ihrem Azure DevOps-Projekt 2. Gehen Sie zu **Pipelines** → **Library** → **Variable groups** 3. Erstellen Sie eine neue Variablengruppe namens `Capgo-Variables` 4. Fügen Sie die folgenden Variablen hinzu: | Variablenname | Wert | Sicher | | ------------- | ------------------- | ------ | | `CAPGO_TOKEN` | Ihr Capgo API-Token | ✅ Ja | Tip Holen Sie sich Ihr Capgo API-Token von [console.capgo.app/apikeys](https://console.capgo.app/apikeys). Ihre App-ID ist bereits in Ihrer `capacitor.config.ts`-Datei konfiguriert. ## Einfach [Section titled “Einfach”](#einfach) Grundkonfiguration, die bei jedem Push zum main-Branch in die Produktion deployt: ```yaml # Simple Azure DevOps Pipeline for Capgo Live Updates trigger: branches: include: - main variables: - group: Capgo-Variables jobs: - job: BuildAndDeploy displayName: 'Build and Deploy to Capgo' pool: vmImage: 'ubuntu-latest' steps: - task: NodeTool@0 displayName: 'Setup Node.js' inputs: versionSpec: '22.x' - script: | npm ci npm run test npm run build displayName: 'Install, test and build' - script: | npm install -g @capgo/cli npx @capgo/cli bundle upload --apikey $(CAPGO_TOKEN) --channel production displayName: 'Deploy to Capgo' ``` ## Erweitert [Section titled “Erweitert”](#erweitert) ### Feature-Branch-Bereitstellungen [Section titled “Feature-Branch-Bereitstellungen”](#feature-branch-bereitstellungen) Deployen Sie Feature-Branches zu Test-Channels für Review und Testing: ```yaml # Feature branch deployment trigger: branches: include: - feature/* variables: - group: Capgo-Variables jobs: - job: DeployFeature displayName: 'Deploy Feature Branch' pool: vmImage: 'ubuntu-latest' condition: startsWith(variables['Build.SourceBranch'], 'refs/heads/feature/') steps: - task: NodeTool@0 inputs: versionSpec: '22.x' - script: | npm ci npm run test npm run build displayName: 'Install, test and build' - script: | BRANCH_NAME=$(echo "$(Build.SourceBranchName)" | sed 's/[^a-zA-Z0-9-]/-/g') CHANNEL_NAME="feature-$BRANCH_NAME" npm install -g @capgo/cli npx @capgo/cli channel create $CHANNEL_NAME --apikey $(CAPGO_TOKEN) || true npx @capgo/cli bundle upload --apikey $(CAPGO_TOKEN) --channel $CHANNEL_NAME displayName: 'Deploy to Feature Channel' ``` Tip **Testen mit Channels**: Nach der Bereitstellung in einem Feature-Channel können Sie das Update in Ihrer App testen, indem Sie diese für die Verwendung dieses spezifischen Channels konfigurieren. Erfahren Sie mehr über [das Konfigurieren von Channels in Ihrer App](/docs/live-updates/channels/#configuring-the-channel-in-your-app). ### Verschlüsselung verwenden [Section titled “Verschlüsselung verwenden”](#verschlüsselung-verwenden) Wenn Sie [Capgos Verschlüsselungsfunktion](/docs/live-updates/encryption/) nutzen, müssen Sie Ihren privaten Schlüssel sicher in Ihrer CI/CD-Umgebung speichern. Nach dem [Einrichten der Verschlüsselungsschlüssel](/docs/live-updates/encryption/#setting-up-encryption) lokal fügen Sie Ihren privaten Schlüssel zu den Azure DevOps-Variablen hinzu: ```shell # Zeigen Sie den Inhalt Ihres privaten Schlüssels an (kopieren Sie diese Ausgabe) cat .capgo_key_v2 ``` Fügen Sie diesen Inhalt als `CAPGO_PRIVATE_KEY` in Ihrer Azure DevOps-Variablengruppe hinzu (als Secret markieren) und verwenden Sie ihn dann in Pipelines: ```yaml # Deploy with encryption - script: | npm install -g @capgo/cli npx @capgo/cli bundle upload --apikey $(CAPGO_TOKEN) --key-data-v2 "$(CAPGO_PRIVATE_KEY)" --channel production displayName: 'Deploy to Capgo with Encryption' ``` Caution **Sicherheits-Best-Practices:** * Committen Sie niemals die `.capgo_key_v2`-Datei in die Versionskontrolle * Speichern Sie den privaten Schlüssel nur in sicherer CI/CD-Geheimnisverwaltung * Verwenden Sie unterschiedliche Schlüssel für verschiedene Umgebungen ### Multi-Channel-Konfiguration [Section titled “Multi-Channel-Konfiguration”](#multi-channel-konfiguration) Für umfassende Informationen zum Einrichten und Verwalten mehrerer Bereitstellungs-Channels siehe die [Channels-Dokumentation](/docs/live-updates/channels/). Vollständige Konfiguration mit mehreren Umgebungen und Pull-Request-Bereitstellungen: ```yaml # Advanced Azure DevOps Pipeline with Multiple Channels trigger: branches: include: - main - develop pr: branches: include: - main - develop variables: - group: Capgo-Variables stages: # Build stage - stage: Build jobs: - job: BuildApp pool: vmImage: 'ubuntu-latest' steps: - task: NodeTool@0 inputs: versionSpec: '22.x' - script: | npm ci npm run test npm run build displayName: 'Install, test and build' - task: PublishBuildArtifacts@1 inputs: pathToPublish: 'dist' artifactName: 'app-build' # Deploy to development - stage: DeployDev condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/develop')) jobs: - deployment: DeployDevelopment environment: development pool: vmImage: 'ubuntu-latest' strategy: runOnce: deploy: steps: - task: NodeTool@0 inputs: versionSpec: '22.x' - task: DownloadBuildArtifacts@0 inputs: artifactName: 'app-build' downloadPath: '$(Pipeline.Workspace)' - script: | npm install -g @capgo/cli npx @capgo/cli bundle upload --apikey $(CAPGO_TOKEN) --channel development --path $(Pipeline.Workspace)/app-build displayName: 'Deploy to Development' # Deploy PR to test channel - stage: DeployPR condition: and(succeeded(), eq(variables['Build.Reason'], 'PullRequest')) jobs: - job: DeployPRChannel pool: vmImage: 'ubuntu-latest' steps: - task: NodeTool@0 inputs: versionSpec: '22.x' - task: DownloadBuildArtifacts@0 inputs: artifactName: 'app-build' downloadPath: '$(Pipeline.Workspace)' - script: | CHANNEL_NAME="pr-$(System.PullRequest.PullRequestNumber)" npm install -g @capgo/cli npx @capgo/cli channel create $CHANNEL_NAME --apikey $(CAPGO_TOKEN) || true npx @capgo/cli bundle upload --apikey $(CAPGO_TOKEN) --channel $CHANNEL_NAME --path $(Pipeline.Workspace)/app-build displayName: 'Deploy to PR Channel' # Deploy to production - stage: DeployProd condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main')) jobs: - deployment: DeployProduction environment: production pool: vmImage: 'ubuntu-latest' strategy: runOnce: deploy: steps: - task: NodeTool@0 inputs: versionSpec: '22.x' - task: DownloadBuildArtifacts@0 inputs: artifactName: 'app-build' downloadPath: '$(Pipeline.Workspace)' - script: | npm install -g @capgo/cli npx @capgo/cli bundle upload --apikey $(CAPGO_TOKEN) --channel production --path $(Pipeline.Workspace)/app-build displayName: 'Deploy to Production' ``` ### Multi-Umgebungs-Bereitstellung [Section titled “Multi-Umgebungs-Bereitstellung”](#multi-umgebungs-bereitstellung) Für komplexe Szenarien mit mehreren Umgebungen: ```yaml # Extended pipeline with multiple environments parameters: - name: deployEnvironment displayName: 'Deploy Environment' type: string default: 'staging' values: - staging - production variables: - group: Capgo-Variables - name: channelName ${{ if eq(parameters.deployEnvironment, 'production') }}: value: 'production' ${{ else }}: value: 'staging' stages: # Build stage - stage: Build jobs: - job: BuildApp pool: vmImage: 'ubuntu-latest' steps: - task: NodeTool@0 inputs: versionSpec: '22.x' - script: | npm ci npm run test npm run build displayName: 'Install, test and build' - task: PublishBuildArtifacts@1 inputs: pathToPublish: 'dist' artifactName: 'app-build' - stage: DeployStaging displayName: 'Deploy to Staging' dependsOn: Build condition: and(succeeded(), eq('${{ parameters.deployEnvironment }}', 'staging')) jobs: - deployment: DeployStaging displayName: 'Deploy to Staging Channel' pool: vmImage: 'ubuntu-latest' environment: 'staging' strategy: runOnce: deploy: steps: - template: deploy-steps.yml parameters: channel: 'staging' - stage: DeployProduction displayName: 'Deploy to Production' dependsOn: Build condition: and(succeeded(), eq('${{ parameters.deployEnvironment }}', 'production')) jobs: - deployment: DeployProduction displayName: 'Deploy to Production Channel' pool: vmImage: 'ubuntu-latest' environment: 'production' strategy: runOnce: deploy: steps: - template: deploy-steps.yml parameters: channel: 'production' ``` ### Deployment-Vorlage (deploy-steps.yml) [Section titled “Deployment-Vorlage (deploy-steps.yml)”](#deployment-vorlage-deploy-stepsyml) Erstellen Sie eine wiederverwendbare Vorlagendatei `deploy-steps.yml`: deploy-steps.yml ```yaml parameters: - name: channel type: string steps: - task: NodeTool@0 displayName: 'Install Node.js' inputs: versionSpec: '22.x' - task: DownloadBuildArtifacts@0 displayName: 'Download build artifacts' inputs: artifactName: 'app-build' downloadPath: '$(System.ArtifactsDirectory)' - script: | npm install -g @capgo/cli displayName: 'Install Capgo CLI' - script: | npx @capgo/cli bundle upload \ --apikey $(CAPGO_TOKEN) \ --channel ${{ parameters.channel }} \ --path $(System.ArtifactsDirectory)/app-build displayName: 'Upload to Capgo (${{ parameters.channel }})' ``` ### Branch-basierte Bereitstellungsstrategie [Section titled “Branch-basierte Bereitstellungsstrategie”](#branch-basierte-bereitstellungsstrategie) Konfigurieren Sie verschiedene Bereitstellungsstrategien basierend auf Git-Branches: ```yaml trigger: branches: include: - main - develop - feature/* variables: - group: Capgo-Variables - name: targetChannel ${{ if eq(variables['Build.SourceBranch'], 'refs/heads/main') }}: value: 'production' ${{ elseif eq(variables['Build.SourceBranch'], 'refs/heads/develop') }}: value: 'staging' ${{ else }}: value: 'development' stages: - stage: Build jobs: - job: BuildApp pool: vmImage: 'ubuntu-latest' steps: - task: NodeTool@0 inputs: versionSpec: '22.x' - script: | npm ci npm run test npm run build displayName: 'Install, test and build' - task: PublishBuildArtifacts@1 inputs: pathToPublish: 'dist' artifactName: 'app-build' - stage: Deploy displayName: 'Deploy to $(targetChannel)' dependsOn: Build condition: succeeded() jobs: - deployment: DeployJob displayName: 'Deploy to $(targetChannel) Channel' pool: vmImage: 'ubuntu-latest' environment: '$(targetChannel)' strategy: runOnce: deploy: steps: - template: deploy-steps.yml parameters: channel: '$(targetChannel)' ``` ## Sicherheits-Best-Practices [Section titled “Sicherheits-Best-Practices”](#sicherheits-best-practices) ### Sichere Variablenverwaltung [Section titled “Sichere Variablenverwaltung”](#sichere-variablenverwaltung) 1. **Variablengruppen verwenden**: Sensible Daten in Azure DevOps-Variablengruppen speichern 2. **Als Secret markieren**: Immer API-Tokens und Schlüssel als Secret-Variablen markieren 3. **Zugriff einschränken**: Variablengruppen-Zugriff auf bestimmte Pipelines und Benutzer beschränken 4. **Schlüssel rotieren**: Regelmäßig Ihre Capgo API-Tokens rotieren ## Überwachung und Benachrichtigungen [Section titled “Überwachung und Benachrichtigungen”](#überwachung-und-benachrichtigungen) ### Teams-Integration [Section titled “Teams-Integration”](#teams-integration) Fügen Sie Microsoft Teams-Benachrichtigungen zu Ihrer Pipeline hinzu: ```yaml - task: ms-teams-deploy-card@1.4.1 displayName: 'Notify Teams on Success' condition: succeeded() inputs: webhookUri: '$(TEAMS_WEBHOOK_URL)' title: 'Capgo Deployment Successful' text: 'App deployed to $(targetChannel) channel' themeColor: '00FF00' - task: ms-teams-deploy-card@1.4.1 displayName: 'Notify Teams on Failure' condition: failed() inputs: webhookUri: '$(TEAMS_WEBHOOK_URL)' title: 'Capgo Deployment Failed' text: 'Deployment to $(targetChannel) failed' themeColor: 'FF0000' ``` ### E-Mail-Benachrichtigungen [Section titled “E-Mail-Benachrichtigungen”](#e-mail-benachrichtigungen) Konfigurieren Sie E-Mail-Benachrichtigungen für den Bereitstellungsstatus: ```yaml - task: EmailReport@1 displayName: 'Send Email Report' condition: always() inputs: sendMailConditionConfig: 'Always' subject: 'Capgo Deployment Report - $(Build.BuildNumber)' to: 'team@yourcompany.com' body: | Deployment Status: $(Agent.JobStatus) Channel: $(targetChannel) Build: $(Build.BuildNumber) Commit: $(Build.SourceVersion) ``` ## Fehlerbehebung [Section titled “Fehlerbehebung”](#fehlerbehebung) ### Häufige Probleme [Section titled “Häufige Probleme”](#häufige-probleme) **Pipeline schlägt mit “Capgo CLI not found” fehl:** ```yaml # Ensure global installation - script: | npm install -g @capgo/cli which capgo || echo "Capgo CLI not found in PATH" displayName: 'Install and verify Capgo CLI' ``` **Authentifizierungsfehler:** ```yaml # Verify token is correctly set - script: | echo "Token length: ${#CAPGO_TOKEN}" if [ -z "$CAPGO_TOKEN" ]; then echo "CAPGO_TOKEN is not set" exit 1 fi displayName: 'Verify Capgo token' env: CAPGO_TOKEN: $(CAPGO_TOKEN) ``` **Build-Artefakte nicht gefunden:** ```yaml # List available artifacts for debugging - script: | ls -la $(System.ArtifactsDirectory) find $(System.ArtifactsDirectory) -name "*.js" -o -name "*.html" displayName: 'Debug artifacts' ``` ### Debug-Pipeline [Section titled “Debug-Pipeline”](#debug-pipeline) Fügen Sie Debugging-Schritte hinzu, um Probleme zu beheben: ```yaml - script: | echo "Build.SourceBranch: $(Build.SourceBranch)" echo "Build.BuildNumber: $(Build.BuildNumber)" echo "Target Channel: $(targetChannel)" displayName: 'Debug Pipeline Variables' - script: | npx @capgo/cli app debug --apikey $(CAPGO_TOKEN) displayName: 'Debug Capgo App Status' ``` ## Nächste Schritte [Section titled “Nächste Schritte”](#nächste-schritte) * Erfahren Sie mehr über [Channels](/docs/live-updates/channels/), um verschiedene Bereitstellungsumgebungen zu verwalten * Erkunden Sie [Custom Storage](/docs/live-updates/custom-storage/) für erweiterte Bereitstellungsszenarien * Richten Sie [Encryption](/docs/live-updates/encryption/) für sichere Bereitstellungen ein * Konfigurieren Sie [Update Behavior](/docs/live-updates/update-behavior/), um anzupassen, wie Updates angewendet werden Mit der Azure DevOps-Integration können Sie Ihre Capgo-Bereitstellungen automatisieren und konsistente, zuverlässige Updates für Ihre mobilen App-Benutzer sicherstellen. # Bitbucket Pipelines Integration > Erfahren Sie, wie Sie Capgo Live Updates mit Bitbucket Pipelines für die automatisierte Bereitstellung Ihrer App-Updates integrieren. Integrieren Sie Capgo Live Updates mit Bitbucket Pipelines, um Ihre App-Updates automatisch bereitzustellen, wenn Sie Code-Änderungen pushen. Dieser Leitfaden behandelt die Einrichtung automatisierter Build-, Test- und Bereitstellungs-Workflows. ## Voraussetzungen [Section titled “Voraussetzungen”](#voraussetzungen) Stellen Sie vor der Einrichtung der Bitbucket Pipelines-Integration sicher, dass Sie Folgendes haben: * Ein Bitbucket-Konto mit einem Repository * Ein Capgo-Konto mit einer konfigurierten App * Node.js und npm/yarn in Ihrem Projekt konfiguriert ## Einrichten von Bitbucket Pipelines [Section titled “Einrichten von Bitbucket Pipelines”](#einrichten-von-bitbucket-pipelines) ### Schritt 1: Repository-Variablen konfigurieren [Section titled “Schritt 1: Repository-Variablen konfigurieren”](#schritt-1-repository-variablen-konfigurieren) Richten Sie zunächst die erforderlichen Variablen in Ihrem Bitbucket-Repository ein: 1. Navigieren Sie zu Ihrem Bitbucket-Repository 2. Gehen Sie zu **Repository settings** → **Pipelines** → **Repository variables** 3. Fügen Sie die folgenden Variablen hinzu: | Variablenname | Wert | Gesichert | | ------------- | ------------------- | --------- | | `CAPGO_TOKEN` | Ihr Capgo API-Token | ✅ Ja | Tip Holen Sie sich Ihr Capgo API-Token von [console.capgo.app/apikeys](https://console.capgo.app/apikeys). Ihre App-ID ist bereits in Ihrer `capacitor.config.ts`-Datei konfiguriert. ## Einfach [Section titled “Einfach”](#einfach) Grundkonfiguration, die bei jedem Push zum main-Branch in die Produktion deployt: ```yaml # bitbucket-pipelines.yml - Simple Configuration image: node:22 pipelines: branches: main: - step: name: Build and Deploy to Production script: - npm ci - npm run test - npm run build - npm install -g @capgo/cli - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel production artifacts: - dist/** ``` ## Erweitert [Section titled “Erweitert”](#erweitert) ### Feature-Branch-Bereitstellungen [Section titled “Feature-Branch-Bereitstellungen”](#feature-branch-bereitstellungen) Deployen Sie Feature-Branches zu Test-Channels für Review und Testing: ```yaml # Feature branch deployment pipelines: branches: feature/*: - step: name: Deploy Feature Branch script: - npm ci - npm run test - npm run build - BRANCH_NAME=$(echo $BITBUCKET_BRANCH | sed 's/[^a-zA-Z0-9-]/-/g') - CHANNEL_NAME="feature-$BRANCH_NAME" - npm install -g @capgo/cli - npx @capgo/cli channel create $CHANNEL_NAME --apikey $CAPGO_TOKEN || true - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel $CHANNEL_NAME artifacts: - dist/** ``` Tip **Testen mit Channels**: Nach der Bereitstellung in einem Feature-Channel können Sie das Update in Ihrer App testen, indem Sie diese für die Verwendung dieses spezifischen Channels konfigurieren. Erfahren Sie mehr über [das Konfigurieren von Channels in Ihrer App](/docs/live-updates/channels/#configuring-the-channel-in-your-app). ### Verschlüsselung verwenden [Section titled “Verschlüsselung verwenden”](#verschlüsselung-verwenden) Wenn Sie [Capgos Verschlüsselungsfunktion](/docs/live-updates/encryption/) nutzen, müssen Sie Ihren privaten Schlüssel sicher in Ihrer CI/CD-Umgebung speichern. Nach dem [Einrichten der Verschlüsselungsschlüssel](/docs/live-updates/encryption/#setting-up-encryption) lokal fügen Sie Ihren privaten Schlüssel zu den Bitbucket-Variablen hinzu: ```shell # Zeigen Sie den Inhalt Ihres privaten Schlüssels an (kopieren Sie diese Ausgabe) cat .capgo_key_v2 ``` Fügen Sie diesen Inhalt als `CAPGO_PRIVATE_KEY` in Ihren Bitbucket-Repository-Variablen hinzu (als gesichert markieren) und verwenden Sie ihn dann in Pipelines: ```yaml # Deploy with encryption - step: name: Deploy to Capgo with Encryption script: - npm install -g @capgo/cli - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --key-data-v2 "$CAPGO_PRIVATE_KEY" --channel production ``` Caution **Sicherheits-Best-Practices:** * Committen Sie niemals die `.capgo_key_v2`-Datei in die Versionskontrolle * Speichern Sie den privaten Schlüssel nur in sicherer CI/CD-Geheimnisverwaltung * Verwenden Sie unterschiedliche Schlüssel für verschiedene Umgebungen ### Multi-Channel-Konfiguration [Section titled “Multi-Channel-Konfiguration”](#multi-channel-konfiguration) Für umfassende Informationen zum Einrichten und Verwalten mehrerer Bereitstellungs-Channels siehe die [Channels-Dokumentation](/docs/live-updates/channels/). Vollständige Konfiguration mit mehreren Umgebungen und Pull-Request-Bereitstellungen: ```yaml # bitbucket-pipelines.yml - Advanced Multi-Channel Configuration image: node:22 definitions: steps: - step: &build-step name: Build Application script: - npm ci - npm run test - npm run build artifacts: - dist/** - step: &deploy-step name: Deploy to Capgo script: - npm install -g @capgo/cli - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel $CHANNEL_NAME pipelines: branches: main: - step: <<: *build-step - step: <<: *deploy-step name: Deploy to Production deployment: production trigger: manual script: - export CHANNEL_NAME=production - npm install -g @capgo/cli - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel $CHANNEL_NAME develop: - step: <<: *build-step - step: <<: *deploy-step name: Deploy to Development deployment: development script: - export CHANNEL_NAME=development - npm install -g @capgo/cli - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel $CHANNEL_NAME pull-requests: '**': - step: <<: *build-step - step: name: Deploy PR to Test Channel script: - CHANNEL_NAME="pr-$BITBUCKET_PR_ID" - npm install -g @capgo/cli - npx @capgo/cli channel create $CHANNEL_NAME --apikey $CAPGO_TOKEN || true - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel $CHANNEL_NAME artifacts: - dist/** ``` ### Multi-Umgebungs-Pipeline [Section titled “Multi-Umgebungs-Pipeline”](#multi-umgebungs-pipeline) Für komplexe Bereitstellungsszenarien mit Staging- und Production-Umgebungen: ```yaml # Multi-environment pipeline image: node:22 pipelines: branches: main: - step: name: Build script: - npm ci - npm run test - npm run build artifacts: - dist/** - step: name: Deploy to Staging deployment: staging script: - npm install -g @capgo/cli - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel staging - step: name: Deploy to Production deployment: production trigger: manual script: - npm install -g @capgo/cli - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel production develop: - step: name: Build and Deploy to Development script: - npm ci - npm run test - npm run build - npm install -g @capgo/cli - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel development artifacts: - dist/** ``` ### Branch-basierte Bereitstellungsstrategie [Section titled “Branch-basierte Bereitstellungsstrategie”](#branch-basierte-bereitstellungsstrategie) Deployen Sie verschiedene Branches automatisch zu entsprechenden Channels: ```yaml # Dynamic channel deployment image: node:22 definitions: scripts: - script: &determine-channel | if [ "$BITBUCKET_BRANCH" = "main" ]; then export CHANNEL_NAME="production" elif [ "$BITBUCKET_BRANCH" = "develop" ]; then export CHANNEL_NAME="staging" else export CHANNEL_NAME="development" fi echo "Deploying to channel: $CHANNEL_NAME" pipelines: default: - step: name: Build and Deploy script: - npm ci - npm run test - npm run build - *determine-channel - npm install -g @capgo/cli - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel $CHANNEL_NAME artifacts: - dist/** ``` ### Parallele Pipeline-Ausführung [Section titled “Parallele Pipeline-Ausführung”](#parallele-pipeline-ausführung) Optimieren Sie Build-Zeiten mit parallelen Schritten: ```yaml # Parallel execution pipeline image: node:22 pipelines: branches: main: - parallel: - step: name: Run Tests script: - npm ci - npm run test - step: name: Lint Code script: - npm ci - npm run lint - step: name: Build Application script: - npm ci - npm run build artifacts: - dist/** - step: name: Deploy to Production deployment: production script: - npm install -g @capgo/cli - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel production ``` ## Sicherheits-Best-Practices [Section titled “Sicherheits-Best-Practices”](#sicherheits-best-practices) ### Repository-Variablen [Section titled “Repository-Variablen”](#repository-variablen) 1. **Gesicherte Variablen**: Immer API-Tokens als gesichert markieren 2. **Umgebungsvariablen**: Deployment-spezifische Variablen bei Bedarf verwenden 3. **Zugriffskontrolle**: Repository-Zugriff auf autorisierte Teammitglieder beschränken 4. **Token-Rotation**: Regelmäßig Ihre Capgo API-Tokens rotieren ### Deployment-Umgebungen [Section titled “Deployment-Umgebungen”](#deployment-umgebungen) Konfigurieren Sie Deployment-Umgebungen für bessere Sicherheit: ```yaml # Deployment with environment restrictions pipelines: branches: main: - step: name: Deploy to Production deployment: production trigger: manual script: - npm install -g @capgo/cli - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel production ``` ## Überwachung und Benachrichtigungen [Section titled “Überwachung und Benachrichtigungen”](#überwachung-und-benachrichtigungen) ### Slack-Integration [Section titled “Slack-Integration”](#slack-integration) Fügen Sie Slack-Benachrichtigungen zu Ihrer Pipeline hinzu: ```yaml # Pipeline with Slack notifications pipelines: branches: main: - step: name: Build and Deploy script: - npm ci - npm run test - npm run build - npm install -g @capgo/cli - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel production after-script: - | if [ $BITBUCKET_EXIT_CODE -eq 0 ]; then curl -X POST -H 'Content-type: application/json' \ --data '{"text":"✅ Capgo deployment successful for '$BITBUCKET_BRANCH'"}' \ $SLACK_WEBHOOK_URL else curl -X POST -H 'Content-type: application/json' \ --data '{"text":"❌ Capgo deployment failed for '$BITBUCKET_BRANCH'"}' \ $SLACK_WEBHOOK_URL fi ``` ### E-Mail-Benachrichtigungen [Section titled “E-Mail-Benachrichtigungen”](#e-mail-benachrichtigungen) Konfigurieren Sie E-Mail-Benachrichtigungen durch die integrierten Funktionen von Bitbucket oder über externe Services: ```yaml # Email notification step - step: name: Send Notification script: - | curl -X POST \ -H "Content-Type: application/json" \ -d '{ "to": "team@yourcompany.com", "subject": "Capgo Deployment Status", "body": "Deployment of '$BITBUCKET_BRANCH' completed with status: '$BITBUCKET_EXIT_CODE'" }' \ $EMAIL_SERVICE_URL condition: result: [successful, failed] ``` ## Fehlerbehebung [Section titled “Fehlerbehebung”](#fehlerbehebung) ### Häufige Probleme [Section titled “Häufige Probleme”](#häufige-probleme) **Pipeline schlägt mit “Capgo CLI not found” fehl:** ```yaml # Debug CLI installation - step: name: Debug CLI script: - npm install -g @capgo/cli - which capgo || echo "Capgo CLI not found" - npx @capgo/cli --version ``` **Authentifizierungsfehler:** ```yaml # Verify token configuration - step: name: Debug Auth script: - | if [ -z "$CAPGO_TOKEN" ]; then echo "CAPGO_TOKEN is not set" exit 1 fi echo "Token length: ${#CAPGO_TOKEN}" ``` **Build-Artefakte nicht gefunden:** ```yaml # List build outputs - step: name: Debug Build script: - ls -la dist/ - find dist/ -type f -name "*.js" -o -name "*.html" ``` ### Debug-Pipeline [Section titled “Debug-Pipeline”](#debug-pipeline) Fügen Sie Debugging-Informationen hinzu, um Probleme zu beheben: ```yaml # Debug pipeline pipelines: branches: main: - step: name: Debug Information script: - echo "Branch: $BITBUCKET_BRANCH" - echo "Commit: $BITBUCKET_COMMIT" - echo "Build: $BITBUCKET_BUILD_NUMBER" - env | grep BITBUCKET_ | sort - step: name: Build and Deploy script: - npm ci - npm run test - npm run build - npm install -g @capgo/cli - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel production ``` ### Pipeline-Validierung [Section titled “Pipeline-Validierung”](#pipeline-validierung) Aktivieren Sie Pipeline-Validierung, um Konfigurationsfehler zu erkennen: ```yaml # Enable pipeline validation options: docker: true size: 2x pipelines: branches: main: - step: name: Validate Pipeline script: - echo "Pipeline validation successful" - step: name: Build and Deploy script: # ... deployment steps ``` ## Nächste Schritte [Section titled “Nächste Schritte”](#nächste-schritte) * Erfahren Sie mehr über [Channels](/docs/live-updates/channels/), um verschiedene Bereitstellungsumgebungen zu verwalten * Erkunden Sie [Custom Storage](/docs/live-updates/custom-storage/) für erweiterte Bereitstellungsszenarien * Richten Sie [Encryption](/docs/live-updates/encryption/) für sichere Bereitstellungen ein * Konfigurieren Sie [Update Behavior](/docs/live-updates/update-behavior/), um anzupassen, wie Updates angewendet werden Mit der Bitbucket Pipelines-Integration können Sie Ihre Capgo-Bereitstellungen automatisieren und konsistente, zuverlässige Updates für Ihre mobilen App-Benutzer sicherstellen. # GitHub Actions Integration > Erfahren Sie, wie Sie Capgo Live Updates mit GitHub Actions für die automatisierte Bereitstellung Ihrer App-Updates integrieren. Integrieren Sie Capgo Live Updates mit GitHub Actions, um Ihre App-Updates automatisch bereitzustellen, wenn Sie Code-Änderungen pushen. Dieser Leitfaden behandelt die Einrichtung automatisierter Build-, Test- und Bereitstellungs-Workflows mit GitHubs leistungsstarker CI/CD-Plattform. ## Voraussetzungen [Section titled “Voraussetzungen”](#voraussetzungen) Stellen Sie vor der Einrichtung der GitHub Actions-Integration sicher, dass Sie Folgendes haben: * Ein GitHub-Repository mit dem Quellcode Ihrer App * Ein Capgo-Konto mit einer konfigurierten App * Node.js und npm/yarn in Ihrem Projekt konfiguriert * GitHub Actions für Ihr Repository aktiviert ## Einrichten von GitHub Secrets [Section titled “Einrichten von GitHub Secrets”](#einrichten-von-github-secrets) ### Schritt 1: Repository-Secrets konfigurieren [Section titled “Schritt 1: Repository-Secrets konfigurieren”](#schritt-1-repository-secrets-konfigurieren) Richten Sie die erforderlichen Secrets in Ihrem GitHub-Repository ein: 1. Navigieren Sie zu Ihrem GitHub-Repository 2. Gehen Sie zu **Settings** → **Secrets and variables** → **Actions** 3. Klicken Sie auf **New repository secret** und fügen Sie Folgendes hinzu: | Secret-Name | Wert | | ------------- | ------------------- | | `CAPGO_TOKEN` | Ihr Capgo API-Token | Tip Holen Sie sich Ihr Capgo API-Token von [console.capgo.app/apikeys](https://console.capgo.app/apikeys). Ihre App-ID ist bereits in Ihrer `capacitor.config.ts`-Datei konfiguriert. ## Einfache Production-Bereitstellung [Section titled “Einfache Production-Bereitstellung”](#einfache-production-bereitstellung) Beginnen Sie mit dieser Grundkonfiguration, die bei jedem Push zum main-Branch in die Produktion deployt: ```yaml # Simple GitHub Actions Workflow for Capgo Live Updates name: Deploy to Capgo on: push: branches: - main jobs: deploy: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v6 - name: Setup Node.js uses: actions/setup-node@v6 with: node-version: '24' cache: 'npm' - name: Install, test and build run: | npm ci npm run test npm run build - name: Deploy to Capgo run: | npm install -g @capgo/cli npx @capgo/cli bundle upload --channel production env: CAPGO_TOKEN: ${{ secrets.CAPGO_TOKEN }} # For encrypted uploads, add: --key-data-v2 "${{ secrets.CAPGO_PRIVATE_KEY }}" ``` ## Erweiterte Multi-Channel-Konfiguration [Section titled “Erweiterte Multi-Channel-Konfiguration”](#erweiterte-multi-channel-konfiguration) ### Feature-Branch-Bereitstellungen [Section titled “Feature-Branch-Bereitstellungen”](#feature-branch-bereitstellungen) Deployen Sie Feature-Branches zu temporären Channels zum Testen: ```yaml # Feature branch deployment name: Deploy Feature Branch to Capgo on: push: branches: - 'feature/**' jobs: deploy-feature: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - uses: actions/setup-node@v6 with: node-version: '24' cache: 'npm' - run: | npm ci npm run test npm run build - name: Deploy to feature channel run: | CHANNEL_NAME=$(echo "${{ github.ref_name }}" | sed 's/[^a-zA-Z0-9]/-/g' | tr '[:upper:]' '[:lower:]') npm install -g @capgo/cli npx @capgo/cli channel create $CHANNEL_NAME --apikey ${{ secrets.CAPGO_TOKEN }} || true npx @capgo/cli bundle upload --apikey ${{ secrets.CAPGO_TOKEN }} --channel $CHANNEL_NAME ``` ### Verschlüsselung verwenden [Section titled “Verschlüsselung verwenden”](#verschlüsselung-verwenden) Wenn Sie [Capgos Verschlüsselungsfunktion](/docs/live-updates/encryption/) nutzen, müssen Sie Ihren privaten Schlüssel sicher in Ihrer CI/CD-Umgebung speichern. Nach dem [Einrichten der Verschlüsselungsschlüssel](/docs/live-updates/encryption/#setting-up-encryption) lokal fügen Sie Ihren privaten Schlüssel zu den GitHub-Secrets hinzu: ```shell # Zeigen Sie den Inhalt Ihres privaten Schlüssels an (kopieren Sie diese Ausgabe) cat .capgo_key_v2 ``` Fügen Sie diesen Inhalt als `CAPGO_PRIVATE_KEY` in Ihren GitHub-Repository-Secrets hinzu und verwenden Sie ihn dann in Workflows: ```yaml # Deploy with encryption - name: Deploy to Capgo with Encryption run: | npm install -g @capgo/cli npx @capgo/cli bundle upload --apikey ${{ secrets.CAPGO_TOKEN }} --key-data-v2 "${{ secrets.CAPGO_PRIVATE_KEY }}" --channel production ``` Caution **Sicherheits-Best-Practices:** * Committen Sie niemals die `.capgo_key_v2`-Datei in die Versionskontrolle * Speichern Sie den privaten Schlüssel nur in sicherer CI/CD-Geheimnisverwaltung * Verwenden Sie unterschiedliche Schlüssel für verschiedene Umgebungen ### Multi-Channel-Konfiguration [Section titled “Multi-Channel-Konfiguration”](#multi-channel-konfiguration) Für umfassende Informationen zum Einrichten und Verwalten mehrerer Bereitstellungs-Channels siehe die [Channels-Dokumentation](/docs/live-updates/channels/). Vollständiger Workflow mit Development-, Pull-Request- und Production-Bereitstellungen: ```yaml # Complete multi-environment workflow name: Deploy to Capgo on: push: branches: [main, develop] pull_request: branches: [main, develop] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - uses: actions/setup-node@v6 with: node-version: '24' cache: 'npm' - run: | npm ci npm run test npm run build - uses: actions/upload-artifact@v4 with: name: dist path: dist/ deploy-development: if: github.ref == 'refs/heads/develop' needs: build runs-on: ubuntu-latest environment: development steps: - uses: actions/setup-node@v6 with: node-version: '24' - uses: actions/download-artifact@v4 with: name: dist path: dist/ - run: | npm install -g @capgo/cli npx @capgo/cli bundle upload --apikey ${{ secrets.CAPGO_TOKEN }} --channel development deploy-pr: if: github.event_name == 'pull_request' needs: build runs-on: ubuntu-latest steps: - uses: actions/setup-node@v6 with: node-version: '24' - uses: actions/download-artifact@v4 with: name: dist path: dist/ - name: Deploy to PR channel run: | CHANNEL_NAME="pr-${{ github.event.number }}" npm install -g @capgo/cli npx @capgo/cli channel create $CHANNEL_NAME --apikey ${{ secrets.CAPGO_TOKEN }} || true npx @capgo/cli bundle upload --apikey ${{ secrets.CAPGO_TOKEN }} --channel $CHANNEL_NAME - name: Comment PR uses: actions/github-script@v7 with: script: | github.rest.issues.createComment({ issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo, body: `🚀 This PR has been deployed to Capgo channel: \`pr-${{ github.event.number }}\`\n\nTo test this update in your app, configure it to use this channel. [Learn how to configure channels →](/docs/live-updates/channels/#configuring-the-channel-in-your-app)` }) deploy-production: if: github.ref == 'refs/heads/main' needs: build runs-on: ubuntu-latest environment: production steps: - uses: actions/setup-node@v6 with: node-version: '24' - uses: actions/download-artifact@v4 with: name: dist path: dist/ - run: | npm install -g @capgo/cli npx @capgo/cli bundle upload --apikey ${{ secrets.CAPGO_TOKEN }} --channel production ``` Tip **Testen mit Channels**: Nach der Bereitstellung in einem PR- oder Development-Channel können Sie das Update in Ihrer App testen, indem Sie diese für die Verwendung dieses spezifischen Channels konfigurieren. Erfahren Sie mehr über [das Konfigurieren von Channels in Ihrer App](/docs/live-updates/channels/#configuring-the-channel-in-your-app). ### Feature-Channels aufräumen [Section titled “Feature-Channels aufräumen”](#feature-channels-aufräumen) Automatisches Aufräumen von Feature-Channels, wenn Branches gelöscht werden: ```yaml name: Cleanup Feature Channels on: delete: jobs: cleanup: runs-on: ubuntu-latest if: github.event.ref_type == 'branch' && startsWith(github.event.ref, 'feature/') steps: - uses: actions/setup-node@v6 with: node-version: '24' - name: Delete Capgo channel run: | CHANNEL_NAME=$(echo "${{ github.event.ref }}" | sed 's/[^a-zA-Z0-9]/-/g' | tr '[:upper:]' '[:lower:]') npm install -g @capgo/cli npx @capgo/cli channel delete $CHANNEL_NAME --apikey ${{ secrets.CAPGO_TOKEN }} || true ``` ## Sicherheit und Bewährte Methoden [Section titled “Sicherheit und Bewährte Methoden”](#sicherheit-und-bewährte-methoden) ### Umgebungsschutzregeln [Section titled “Umgebungsschutzregeln”](#umgebungsschutzregeln) Richten Sie Umgebungsschutzregeln in GitHub ein: 1. Gehen Sie zu **Settings** → **Environments** in Ihrem Repository 2. Erstellen Sie Umgebungen: `development`, `staging`, `production` 3. Für die Production-Umgebung fügen Sie hinzu: * **Required reviewers**: Fügen Sie Teammitglieder hinzu, die Bereitstellungen genehmigen müssen * **Wait timer**: Fügen Sie eine Verzögerung vor der Bereitstellung hinzu (optional) * **Deployment branches**: Nur auf `main`-Branch beschränken ### Sichere Secrets-Verwaltung [Section titled “Sichere Secrets-Verwaltung”](#sichere-secrets-verwaltung) Verwenden Sie umgebungsspezifische Secrets: ```yaml # Use different secrets per environment deploy-production: environment: production steps: - name: Deploy to Production run: | npx @capgo/cli bundle upload \ --apikey ${{ secrets.CAPGO_PROD_TOKEN }} \ --app ${{ secrets.CAPGO_PROD_APP_ID }} \ --channel production ``` ## Überwachung und Benachrichtigungen [Section titled “Überwachung und Benachrichtigungen”](#überwachung-und-benachrichtigungen) ### Slack-Integration [Section titled “Slack-Integration”](#slack-integration) Fügen Sie Slack-Benachrichtigungen zu Ihrem Workflow hinzu: ```yaml name: Deploy with Notifications jobs: deploy: runs-on: ubuntu-latest steps: # ... deployment steps - name: Notify Slack on Success if: success() uses: 8398a7/action-slack@v3 with: status: success text: '✅ Capgo deployment successful!' fields: repo,message,commit,author,action,eventName,ref,workflow env: SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} - name: Notify Slack on Failure if: failure() uses: 8398a7/action-slack@v3 with: status: failure text: '❌ Capgo deployment failed!' fields: repo,message,commit,author,action,eventName,ref,workflow env: SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} ``` ### Discord-Integration [Section titled “Discord-Integration”](#discord-integration) Senden Sie Benachrichtigungen an Discord: ```yaml - name: Discord notification if: always() uses: Ilshidur/action-discord@master with: args: | Capgo deployment ${{ job.status }}! App: ${{ secrets.CAPGO_APP_ID }} Channel: ${{ github.ref_name }} Commit: ${{ github.sha }} env: DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }} ``` ### E-Mail-Benachrichtigungen [Section titled “E-Mail-Benachrichtigungen”](#e-mail-benachrichtigungen) Konfigurieren Sie E-Mail-Benachrichtigungen: ```yaml - name: Send email notification if: failure() uses: dawidd6/action-send-mail@v3 with: server_address: smtp.gmail.com server_port: 465 username: ${{ secrets.EMAIL_USERNAME }} password: ${{ secrets.EMAIL_PASSWORD }} subject: 'Capgo Deployment Failed - ${{ github.repository }}' to: team@yourcompany.com from: ci-cd@yourcompany.com body: | Deployment failed for ${{ github.repository }} Branch: ${{ github.ref_name }} Commit: ${{ github.sha }} Workflow: ${{ github.workflow }} ``` ## Fehlerbehebung [Section titled “Fehlerbehebung”](#fehlerbehebung) ### Debug-Workflow [Section titled “Debug-Workflow”](#debug-workflow) Fügen Sie Debugging-Schritte hinzu, um Probleme zu beheben: ```yaml - name: Debug environment run: | echo "Node version: $(node --version)" echo "NPM version: $(npm --version)" echo "Working directory: $(pwd)" echo "Files in dist/: $(ls -la dist/ || echo 'No dist directory')" echo "Environment variables:" env | grep -E "(GITHUB_|CAPGO_)" | sort - name: Test Capgo CLI run: | npx @capgo/cli --version npx @capgo/cli app debug --apikey ${{ secrets.CAPGO_TOKEN }} --app ${{ secrets.CAPGO_APP_ID }} ``` ### Häufige Probleme und Lösungen [Section titled “Häufige Probleme und Lösungen”](#häufige-probleme-und-lösungen) **Workflow schlägt mit “CAPGO\_TOKEN not found” fehl:** ```yaml - name: Verify secrets run: | if [ -z "${{ secrets.CAPGO_TOKEN }}" ]; then echo "ERROR: CAPGO_TOKEN secret is not set" exit 1 fi echo "CAPGO_TOKEN is set (length: ${#CAPGO_TOKEN})" env: CAPGO_TOKEN: ${{ secrets.CAPGO_TOKEN }} ``` **Build-Artefakte nicht gefunden:** ```yaml - name: Debug artifacts run: | echo "Checking for build artifacts..." ls -la dist/ || echo "No dist directory found" find . -name "*.js" -o -name "*.html" | head -10 ``` **Netzwerkverbindungsprobleme:** ```yaml - name: Test connectivity run: | ping -c 3 api.capgo.io || echo "Ping failed" curl -I https://api.capgo.io/health || echo "Health check failed" ``` ## Wiederverwendbare Workflows [Section titled “Wiederverwendbare Workflows”](#wiederverwendbare-workflows) Erstellen Sie wiederverwendbare Workflows für Konsistenz über Projekte hinweg: .github/workflows/reusable-capgo-deploy.yml ```yaml name: Reusable Capgo Deploy on: workflow_call: inputs: environment: required: true type: string channel: required: true type: string secrets: CAPGO_TOKEN: required: true CAPGO_APP_ID: required: true jobs: deploy: runs-on: ubuntu-latest environment: ${{ inputs.environment }} steps: - uses: actions/checkout@v6 - name: Setup Node.js uses: actions/setup-node@v6 with: node-version: '24' cache: 'npm' - name: Install and build run: | npm ci npm run build - name: Deploy to Capgo run: | npm install -g @capgo/cli npx @capgo/cli bundle upload \ --apikey ${{ secrets.CAPGO_TOKEN }} \ --app ${{ secrets.CAPGO_APP_ID }} \ --channel ${{ inputs.channel }} ``` Verwenden Sie den wiederverwendbaren Workflow: .github/workflows/deploy.yml ```yaml name: Deploy App on: push: branches: [main, develop] jobs: deploy-dev: if: github.ref == 'refs/heads/develop' uses: ./.github/workflows/reusable-capgo-deploy.yml with: environment: development channel: development secrets: CAPGO_TOKEN: ${{ secrets.CAPGO_TOKEN }} CAPGO_APP_ID: ${{ secrets.CAPGO_APP_ID }} deploy-prod: if: github.ref == 'refs/heads/main' uses: ./.github/workflows/reusable-capgo-deploy.yml with: environment: production channel: production secrets: CAPGO_TOKEN: ${{ secrets.CAPGO_TOKEN }} CAPGO_APP_ID: ${{ secrets.CAPGO_APP_ID }} ``` ## Nächste Schritte [Section titled “Nächste Schritte”](#nächste-schritte) * Erfahren Sie mehr über [Channels](/docs/live-updates/channels/), um verschiedene Bereitstellungsumgebungen zu verwalten * Erkunden Sie [Custom Storage](/docs/live-updates/custom-storage/) für erweiterte Bereitstellungsszenarien * Richten Sie [Encryption](/docs/live-updates/encryption/) für sichere Bereitstellungen ein * Konfigurieren Sie [Update Behavior](/docs/live-updates/update-behavior/), um anzupassen, wie Updates angewendet werden Mit der GitHub Actions-Integration können Sie GitHubs leistungsstarke CI/CD-Plattform nutzen, um ausgefeilte Bereitstellungs-Workflows mit integrierter Sicherheit, Überwachung und Kollaborationsfunktionen für Ihre Capgo Live Updates zu erstellen. # GitLab CI/CD Integration > Erfahren Sie, wie Sie Capgo Live Updates mit GitLab CI/CD für die automatisierte Bereitstellung Ihrer App-Updates integrieren. Integrieren Sie Capgo Live Updates mit GitLab CI/CD, um Ihre App-Updates automatisch bereitzustellen, wenn Sie Code-Änderungen pushen. Dieser Leitfaden behandelt die Einrichtung automatisierter Build-, Test- und Bereitstellungs-Workflows. ## Voraussetzungen [Section titled “Voraussetzungen”](#voraussetzungen) Stellen Sie vor der Einrichtung der GitLab CI/CD-Integration sicher, dass Sie Folgendes haben: * Ein GitLab-Konto mit einem Projekt-Repository * Ein Capgo-Konto mit einer konfigurierten App * Node.js und npm/yarn in Ihrem Projekt konfiguriert ## Einrichten von GitLab CI/CD [Section titled “Einrichten von GitLab CI/CD”](#einrichten-von-gitlab-cicd) ### Schritt 1: Umgebungsvariablen konfigurieren [Section titled “Schritt 1: Umgebungsvariablen konfigurieren”](#schritt-1-umgebungsvariablen-konfigurieren) Richten Sie zunächst die erforderlichen Variablen in Ihrem GitLab-Projekt ein: 1. Navigieren Sie zu Ihrem GitLab-Projekt 2. Gehen Sie zu **Settings** → **CI/CD** → **Variables** 3. Fügen Sie die folgenden Variablen hinzu: | Variablenname | Wert | Protected | Masked | | ------------- | ------------------- | --------- | ------ | | `CAPGO_TOKEN` | Ihr Capgo API-Token | ✅ Ja | ✅ Ja | Tip Holen Sie sich Ihr Capgo API-Token von [console.capgo.app/apikeys](https://console.capgo.app/apikeys). Ihre App-ID ist bereits in Ihrer `capacitor.config.ts`-Datei konfiguriert. ## Einfach [Section titled “Einfach”](#einfach) Grundkonfiguration, die bei jedem Push zum main-Branch in die Produktion deployt: ```yaml # .gitlab-ci.yml - Simple Configuration image: node:22 stages: - build - deploy variables: npm_config_cache: "$CI_PROJECT_DIR/.npm" build: stage: build script: - npm ci - npm run test - npm run build artifacts: paths: - dist/ expire_in: 1 hour only: - main deploy_production: stage: deploy script: - npm install -g @capgo/cli - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel production # For encrypted uploads, add: --key-data-v2 "$CAPGO_PRIVATE_KEY" dependencies: - build only: - main ``` ## Erweitert [Section titled “Erweitert”](#erweitert) ### Feature-Branch-Bereitstellungen [Section titled “Feature-Branch-Bereitstellungen”](#feature-branch-bereitstellungen) Deployen Sie Feature-Branches zu Test-Channels für Review und Testing: ```yaml # Feature branch deployment deploy_feature: stage: deploy script: - npm install -g @capgo/cli - CHANNEL_NAME="feature-$(echo $CI_COMMIT_REF_NAME | sed 's/[^a-zA-Z0-9-]/-/g')" - npx @capgo/cli channel create $CHANNEL_NAME --apikey $CAPGO_TOKEN || true - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel $CHANNEL_NAME dependencies: - build only: - /^feature\/.*$/ environment: name: feature/$CI_COMMIT_REF_NAME url: https://your-app.com/channels/$CHANNEL_NAME ``` Tip **Testen mit Channels**: Nach der Bereitstellung in einem Feature-Channel können Sie das Update in Ihrer App testen, indem Sie diese für die Verwendung dieses spezifischen Channels konfigurieren. Erfahren Sie mehr über [das Konfigurieren von Channels in Ihrer App](/docs/live-updates/channels/#configuring-the-channel-in-your-app). ### Verschlüsselung verwenden [Section titled “Verschlüsselung verwenden”](#verschlüsselung-verwenden) Wenn Sie [Capgos Verschlüsselungsfunktion](/docs/live-updates/encryption/) nutzen, müssen Sie Ihren privaten Schlüssel sicher in Ihrer CI/CD-Umgebung speichern. Nach dem [Einrichten der Verschlüsselungsschlüssel](/docs/live-updates/encryption/#setting-up-encryption) lokal fügen Sie Ihren privaten Schlüssel zu den GitLab-Variablen hinzu: ```shell # Zeigen Sie den Inhalt Ihres privaten Schlüssels an (kopieren Sie diese Ausgabe) cat .capgo_key_v2 ``` Fügen Sie diesen Inhalt als `CAPGO_PRIVATE_KEY` in Ihren GitLab-Projektvariablen hinzu (als protected und masked markieren) und verwenden Sie ihn dann in Pipelines: ```yaml # Deploy with encryption deploy_production: script: - npm install -g @capgo/cli - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --key-data-v2 "$CAPGO_PRIVATE_KEY" --channel production ``` Caution **Sicherheits-Best-Practices:** * Committen Sie niemals die `.capgo_key_v2`-Datei in die Versionskontrolle * Speichern Sie den privaten Schlüssel nur in sicherer CI/CD-Geheimnisverwaltung * Verwenden Sie unterschiedliche Schlüssel für verschiedene Umgebungen ### Multi-Channel-Konfiguration [Section titled “Multi-Channel-Konfiguration”](#multi-channel-konfiguration) Für umfassende Informationen zum Einrichten und Verwalten mehrerer Bereitstellungs-Channels siehe die [Channels-Dokumentation](/docs/live-updates/channels/). Vollständige Konfiguration mit mehreren Umgebungen und Merge-Request-Bereitstellungen: ```yaml # .gitlab-ci.yml - Advanced Multi-Channel Configuration image: node:22 stages: - build - deploy variables: npm_config_cache: "$CI_PROJECT_DIR/.npm" # Build stage build: stage: build script: - npm ci - npm run test - npm run build artifacts: paths: - dist/ expire_in: 24 hours # Deploy to development channel deploy_development: stage: deploy script: - npm install -g @capgo/cli - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel development dependencies: - build only: - develop environment: name: development # Deploy merge requests to test channels deploy_mr: stage: deploy script: - npm install -g @capgo/cli - CHANNEL_NAME="mr-$CI_MERGE_REQUEST_IID" - npx @capgo/cli channel create $CHANNEL_NAME --apikey $CAPGO_TOKEN || true - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel $CHANNEL_NAME dependencies: - build only: - merge_requests environment: name: review/$CI_MERGE_REQUEST_IID url: https://your-app.com/channels/mr-$CI_MERGE_REQUEST_IID on_stop: cleanup_mr # Cleanup MR channels when MR is closed cleanup_mr: stage: deploy script: - npm install -g @capgo/cli - npx @capgo/cli channel delete mr-$CI_MERGE_REQUEST_IID --apikey $CAPGO_TOKEN || true when: manual environment: name: review/$CI_MERGE_REQUEST_IID action: stop only: - merge_requests # Deploy to staging deploy_staging: stage: deploy script: - npm install -g @capgo/cli - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel staging dependencies: - build only: - develop environment: name: staging # Deploy to production deploy_production: stage: deploy script: - npm install -g @capgo/cli - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel production dependencies: - build only: - main environment: name: production ``` ### Multi-Umgebung mit manueller Genehmigung [Section titled “Multi-Umgebung mit manueller Genehmigung”](#multi-umgebung-mit-manueller-genehmigung) Für Production-Bereitstellungen mit manueller Genehmigung: ```yaml deploy_production: stage: deploy script: - npm install -g @capgo/cli - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel production dependencies: - build only: - main when: manual environment: name: production ``` ### Branch-basierte Bereitstellungsstrategie [Section titled “Branch-basierte Bereitstellungsstrategie”](#branch-basierte-bereitstellungsstrategie) Deployen Sie verschiedene Branches automatisch zu entsprechenden Channels: ```yaml # Dynamic channel deployment based on branch deploy: stage: deploy script: - npm install -g @capgo/cli - | if [ "$CI_COMMIT_REF_NAME" = "main" ]; then CHANNEL="production" elif [ "$CI_COMMIT_REF_NAME" = "develop" ]; then CHANNEL="staging" else CHANNEL="development" fi - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel $CHANNEL dependencies: - build environment: name: $CHANNEL ``` ## Sicherheits-Best-Practices [Section titled “Sicherheits-Best-Practices”](#sicherheits-best-practices) ### Protected-Variablen [Section titled “Protected-Variablen”](#protected-variablen) 1. **Sensible Variablen markieren**: Immer API-Tokens als protected und masked markieren 2. **Branch-Schutz**: Protected-Variablen für Production-Bereitstellungen verwenden 3. **Zugriffskontrolle**: Variablenzugriff nur auf Maintainer beschränken 4. **Regelmäßige Rotation**: API-Tokens regelmäßig rotieren ### Sichere Pipeline-Konfiguration [Section titled “Sichere Pipeline-Konfiguration”](#sichere-pipeline-konfiguration) ```yaml # Use protected variables for production deploy_production: stage: deploy script: - npm install -g @capgo/cli - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel production only: refs: - main variables: - $CI_COMMIT_REF_PROTECTED == "true" ``` ## Überwachung und Benachrichtigungen [Section titled “Überwachung und Benachrichtigungen”](#überwachung-und-benachrichtigungen) ### Slack-Integration [Section titled “Slack-Integration”](#slack-integration) Fügen Sie Slack-Benachrichtigungen zu Ihrer Pipeline hinzu: ```yaml notify_success: stage: .post image: alpine:latest before_script: - apk add --no-cache curl script: - | curl -X POST -H 'Content-type: application/json' \ --data '{"text":"✅ Capgo deployment successful for '"$CI_COMMIT_REF_NAME"'"}' \ $SLACK_WEBHOOK_URL when: on_success notify_failure: stage: .post image: alpine:latest before_script: - apk add --no-cache curl script: - | curl -X POST -H 'Content-type: application/json' \ --data '{"text":"❌ Capgo deployment failed for '"$CI_COMMIT_REF_NAME"'"}' \ $SLACK_WEBHOOK_URL when: on_failure ``` ### E-Mail-Benachrichtigungen [Section titled “E-Mail-Benachrichtigungen”](#e-mail-benachrichtigungen) Konfigurieren Sie E-Mail-Benachrichtigungen in Ihren GitLab-Projekteinstellungen oder verwenden Sie die API: ```yaml notify_email: stage: .post script: - | curl --request POST \ --header "PRIVATE-TOKEN: $GITLAB_API_TOKEN" \ --form "to=team@yourcompany.com" \ --form "subject=Capgo Deployment Status" \ --form "body=Deployment of $CI_COMMIT_REF_NAME completed with status: $CI_JOB_STATUS" \ "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/emails" when: always ``` ## Fehlerbehebung [Section titled “Fehlerbehebung”](#fehlerbehebung) ### Häufige Probleme [Section titled “Häufige Probleme”](#häufige-probleme) **Pipeline schlägt mit “Capgo CLI not found” fehl:** ```yaml # Debug CLI installation debug_cli: script: - npm install -g @capgo/cli - which capgo || echo "Capgo CLI not found" - npx @capgo/cli --version ``` **Authentifizierungsfehler:** ```yaml # Verify token configuration debug_auth: script: - | if [ -z "$CAPGO_TOKEN" ]; then echo "CAPGO_TOKEN is not set" exit 1 fi echo "Token length: ${#CAPGO_TOKEN}" ``` **Build-Artefakte nicht gefunden:** ```yaml # List build outputs debug_build: script: - ls -la dist/ - find dist/ -type f -name "*.js" -o -name "*.html" ``` ### Debug-Pipeline [Section titled “Debug-Pipeline”](#debug-pipeline) Fügen Sie Debugging-Informationen hinzu, um Probleme zu beheben: ```yaml debug: stage: build script: - echo "Branch: $CI_COMMIT_REF_NAME" - echo "Commit: $CI_COMMIT_SHA" - echo "Build: $CI_PIPELINE_ID" - env | grep CI_ | sort only: - branches ``` ## Nächste Schritte [Section titled “Nächste Schritte”](#nächste-schritte) * Erfahren Sie mehr über [Channels](/docs/live-updates/channels/), um verschiedene Bereitstellungsumgebungen zu verwalten * Erkunden Sie [Custom Storage](/docs/live-updates/custom-storage/) für erweiterte Bereitstellungsszenarien * Richten Sie [Encryption](/docs/live-updates/encryption/) für sichere Bereitstellungen ein * Konfigurieren Sie [Update Behavior](/docs/live-updates/update-behavior/), um anzupassen, wie Updates angewendet werden Mit der GitLab CI/CD-Integration können Sie Ihre Capgo-Bereitstellungen automatisieren und konsistente, zuverlässige Updates für Ihre mobilen App-Benutzer sicherstellen. # Rollbacks Während die Live-Updates von Capgo es Ihnen ermöglichen, schnell Verbesserungen und Fehlerbehebungen an Ihre Nutzer zu liefern, kann es Situationen geben, in denen Sie zu einer vorherigen Version Ihrer App zurückkehren müssen. Vielleicht hat ein neues Update ein unerwartetes kritisches Problem eingeführt, oder Sie möchten eine bestimmte Änderung rückgängig machen, während Sie an einer Lösung arbeiten. Capgo bietet verschiedene Möglichkeiten, die Builds eines Kanals zu verwalten und die Version Ihrer App zu kontrollieren, die Benutzer erhalten. ## Zurücksetzen auf ein vorheriges Bundle [Section titled “Zurücksetzen auf ein vorheriges Bundle”](#zurücksetzen-auf-ein-vorheriges-bundle) Jedes Mal, wenn Sie einen neuen Build hochladen und einem Kanal zuweisen, behält Capgo einen Verlauf dieser Builds bei. Wenn Sie ein bestimmtes Update rückgängig machen müssen, können Sie einen dieser vorherigen Builds auswählen, um ihn erneut im Kanal bereitzustellen. Um zu einem vorherigen Build zurückzukehren: 1. Melden Sie sich beim [Capgo Dashboard](https://app.capgo.io) an 2. Navigieren Sie zum Bereich “Channels” 3. Klicken Sie auf den Namen des Kanals, den Sie zurücksetzen möchten 4. Suchen Sie den Build, zu dem Sie zurückkehren möchten, in der Build-Historie des Kanals 5. Klicken Sie auf das Kronen-Symbol neben diesem Build, um ihn zum aktiven Build für den Kanal zu machen ![Kanal-Verwaltungsoptionen](/select_bundle.webp) 6. Bestätigen Sie, dass Sie zu diesem Build zurückkehren möchten Note Das Zurücksetzen auf einen vorherigen Build betrifft nur den ausgewählten Kanal. Wenn Sie mehrere Kanäle haben (z.B. Produktion, Staging, etc.), müssen Sie den Rollback-Prozess für jeden betroffenen Kanal wiederholen. Nach dem Zurücksetzen erhalten Geräte, die für den aktualisierten Kanal konfiguriert sind, beim nächsten Update-Check den vorherigen Build. Der zurückgesetzte Build wird als neues Update behandelt, sodass der übliche Update-Ablauf und die Bedingungen gelten. ## Trennen eines Kanals [Section titled “Trennen eines Kanals”](#trennen-eines-kanals) Wenn Sie Updates auf einem Kanal vorübergehend anhalten möchten, während Sie ein Problem untersuchen, können Sie den Kanal von seinem aktuellen Build trennen. Um einen Kanal zu trennen: 1. Navigieren Sie zum Kanal im Capgo Dashboard 2. Klicken Sie auf die Schaltfläche “Unlink” neben dem aktuellen Build 3. Bestätigen Sie, dass Sie den Kanal trennen möchten Sobald ein Kanal getrennt ist, werden keine neuen Updates mehr verteilt. Geräte, die für diesen Kanal konfiguriert sind, bleiben bei ihrem aktuellen Build, bis der Kanal wieder mit einem Build verknüpft wird. Dies ist nützlich, wenn Sie ein Problem mit einem Update identifiziert haben, aber noch nicht sicher sind, zu welchem Build Sie zurückkehren möchten. Das Trennen des Kanals gibt Ihnen Zeit zur Untersuchung, ohne weitere Updates zu verteilen. ## Erzwingen des eingebauten Bundles [Section titled “Erzwingen des eingebauten Bundles”](#erzwingen-des-eingebauten-bundles) In schwerwiegenderen Situationen möchten Sie möglicherweise alle Geräte eines Kanals auf den Web-Build zurücksetzen, der ursprünglich mit dem nativen Binary Ihrer App gepackt wurde. Dies ist als “eingebautes Bundle” bekannt. Um das eingebaute Bundle auf einem Kanal zu erzwingen: 1. Navigieren Sie zum Kanal im Capgo Dashboard 2. Klicken Sie auf die Schaltfläche “Built-in Bundle” 3. Bestätigen Sie, dass Sie das eingebaute Bundle erzwingen möchten Wenn Sie das eingebaute Bundle erzwingen, kehren alle Geräte, die für diesen Kanal konfiguriert sind, bei ihrer nächsten Update-Prüfung zum ursprünglich gepackten Web-Build zurück. Dies geschieht unabhängig davon, welchen Build sie derzeit verwenden. Dies ist eine aggressivere Rollback-Option als das Zurücksetzen auf einen bestimmten vorherigen Build, da es alle Live-Updates verwirft, die seit der letzten Veröffentlichung der App in den App Stores freigegeben wurden. Caution Seien Sie vorsichtig beim Erzwingen des eingebauten Bundles, da es alle Geräte im Kanal betrifft. Stellen Sie sicher, dass Sie die Auswirkungen bedacht und einen Plan für das weitere Vorgehen haben, bevor Sie diese Aktion durchführen. ## Überwachung und Reaktion auf Probleme [Section titled “Überwachung und Reaktion auf Probleme”](#überwachung-und-reaktion-auf-probleme) Um Probleme schnell zu erkennen und die Auswirkungen problematischer Updates zu minimieren, ist es wichtig, einen Plan für die Überwachung Ihrer Releases und die Reaktion auf Probleme zu haben. Einige Strategien umfassen: * Überwachung von Absturzberichten und Benutzer-Feedback unmittelbar nach der Veröffentlichung eines Updates * Verwendung von stufenweisen Rollouts oder einem mehrstufigen Kanalsystem, um Updates an einer kleineren Gruppe zu testen, bevor sie breit veröffentlicht werden * Einen klaren Entscheidungsprozess haben, wann zurückgesetzt, getrennt oder das eingebaute Bundle erzwungen werden soll, und wer dazu berechtigt ist * Gegebenenfalls Kommunikation mit den Benutzern über das Problem und die Lösung Durch die Kombination sorgfältiger Überwachung mit der Möglichkeit, problematische Updates schnell zu verwalten, können Sie eine kontinuierlich verbesserte App-Erfahrung bieten und gleichzeitig Störungen für Ihre Benutzer minimieren. # Aktualisierungsverhalten Wenn Sie ein Update für Ihre Capgo-App veröffentlichen, möchten Sie wahrscheinlich, dass Ihre Nutzer dieses Update so schnell wie möglich erhalten. Allerdings möchten Sie deren Nutzungserfahrung nicht stören, indem Sie sie zwingen, mitten in einer Sitzung auf einen Download zu warten oder die App neu zu starten. Das Update-Verhalten von Capgo wurde entwickelt, um eine Balance zwischen schneller Update-Bereitstellung und minimaler Störung der Nutzer zu finden. ## Standard Update-Ablauf [Section titled “Standard Update-Ablauf”](#standard-update-ablauf) Standardmäßig behandelt Capgo App-Updates wie folgt: 1. Beim App-Start prüft das Capgo-Plugin, ob ein neues Update verfügbar ist 2. Wenn ein Update gefunden wird, wird es im Hintergrund heruntergeladen, während der Nutzer die aktuelle Version der App weiter verwendet 3. Sobald der Download abgeschlossen ist, wartet Capgo darauf, dass der Nutzer die App entweder in den Hintergrund verschiebt oder vollständig beendet 4. Wenn der Nutzer die App das nächste Mal startet, wird die aktualisierte Version ausgeführt Dieser Ablauf stellt sicher, dass Nutzer immer die neueste Version Ihrer App verwenden, ohne jemals durch Update-Benachrichtigungen unterbrochen oder zum Warten auf Downloads gezwungen zu werden. Tip Capgo prüft auch auf Updates, wenn die App aus dem Hintergrund fortgesetzt wird. So erhalten Nutzer Updates auch dann, wenn sie die App nicht vollständig beenden. ## Warum dieser Ansatz? [Section titled “Warum dieser Ansatz?”](#warum-dieser-ansatz) Das Anwenden von Updates bei Hintergrund- oder Beendigungsereignissen hat einige wichtige Vorteile für die Benutzererfahrung: * Nutzer werden nicht durch Update-Aufforderungen unterbrochen oder müssen mitten in einer Sitzung auf Downloads warten * Updates werden nahtlos zwischen den Sitzungen angewendet, sodass das Starten der App immer ein frisches Erlebnis ist * Sie können häufig Updates bereitstellen, ohne sich Sorgen machen zu müssen, aktive Nutzer zu stören Der Hauptnachteil ist, dass wenn ein Nutzer die App in den Hintergrund verschiebt und schnell wieder aufruft, könnte nicht gespeicherter Status verloren gehen, da das Update zwischen diesen Aktionen angewendet wurde. Um dies zu vermeiden, empfehlen wir: * Status häufig zu speichern und ihn beim Fortsetzen der App korrekt wiederherzustellen * Sehr häufige Updates zu vermeiden, die große Teile des App-Status verändern * Die Anpassung des Update-Verhaltens für sensible Abläufe in Erwägung zu ziehen (siehe unten) ## Anpassen wann Updates angewendet werden [Section titled “Anpassen wann Updates angewendet werden”](#anpassen-wann-updates-angewendet-werden) In manchen Fällen möchten Sie möglicherweise mehr Kontrolle darüber haben, wann genau ein Update angewendet wird. Zum Beispiel möchten Sie vielleicht sicherstellen, dass ein Nutzer einen laufenden Prozess abschließt, bevor das Update installiert wird, oder ein App-Update mit einer serverseitigen Änderung koordinieren. Capgo bietet eine `setDelay`-Funktion, mit der Sie Bedingungen festlegen können, die erfüllt sein müssen, bevor ein Update installiert wird: ```typescript import { CapacitorUpdater } from '@capgo/capacitor-updater'; await CapacitorUpdater.setMultiDelay({ delayConditions: [ { kind: 'date', value: '2023-06-01T00:00:00.000Z', }, { kind: 'background', value: '60000', }, ], }); ``` Dieses Beispiel würde die Installation eines Updates verzögern, bis nach dem 1. Juni 2023 UND die App mindestens 60 Sekunden im Hintergrund war. Die verfügbaren Verzögerungsbedingungen sind: * `date`: Warten bis nach einem bestimmten Datum/Uhrzeit, um das Update anzuwenden * `background`: Nach einer Mindestdauer warten, nachdem die App in den Hintergrund verschoben wurde * `nativeVersion`: Warten auf die Installation einer nativen Binary mit einer Mindestversion * `kill`: Warten bis zum nächsten App-Beendigungsereignis Sie können diese Bedingungen kombinieren, um präzise zu steuern, wann ein Update installiert wird. Danger Beachten Sie, dass die `kill`-Bedingung das Update derzeit nach dem ersten Beendigungsereignis auslöst, nicht nach dem nächsten Hintergrundereignis wie die anderen Bedingungen. Diese Inkonsistenz wird in einer zukünftigen Version behoben. ## Sofortiges Anwenden von Updates [Section titled “Sofortiges Anwenden von Updates”](#sofortiges-anwenden-von-updates) Für kritische Updates oder Apps mit sehr einfachem Status möchten Sie möglicherweise ein Update sofort nach dem Download anwenden, ohne auf ein Hintergrund- oder Beendigungsereignis zu warten. Capgo unterstützt dies über die `directUpdate`-Konfigurationsoption. Empfohlen: Delta Updates mit Direct Update verwenden Bei Verwendung von `directUpdate` **empfehlen wir dringend**, [Delta Updates](/docs/live-updates/differentials/) zu aktivieren, um Download-Zeiten zu minimieren und die Benutzererfahrung zu verbessern. Delta Updates laden nur geänderte Dateien statt des gesamten Bundles herunter, was besonders wichtig ist, wenn Updates sofort angewendet werden, während Benutzer Ihre App aktiv nutzen. **Warum dies für Direct Updates wichtig ist:** * **Schnellere Updates**: Kleinere Downloads bedeuten, dass Updates schnell abgeschlossen werden und die Zeit reduziert wird, in der Benutzer Ladebildschirme sehen * **Besseres mobiles Erlebnis**: Benutzer mit mobilen Netzwerken oder langsameren Verbindungen haben keine langen Wartezeiten * **Geringere Bandbreitennutzung**: Es werden nur geänderte Dateien heruntergeladen, was Daten für Sie und Ihre Benutzer spart Um Delta Updates zu aktivieren, verwenden Sie einfach das `--partial`-Flag beim Hochladen von Bundles: ```shell npx @capgo/cli@latest bundle upload --partial ``` Erfahren Sie mehr in der [Delta Updates Dokumentation](/docs/live-updates/differentials/). `directUpdate` wird in Ihrer `capacitor.config.ts`-Datei gesetzt, nicht im JavaScript-Code: ```typescript import { CapacitorConfig } from '@capacitor/cli'; const config: CapacitorConfig = { plugins: { CapacitorUpdater: { autoUpdate: true, directUpdate: true, keepUrlPathAfterReload: true, }, }, }; export default config; ``` Mit aktiviertem `directUpdate` wird Capgo ein Update sofort anwenden, sobald der Download abgeschlossen ist, auch wenn der Nutzer die App aktiv verwendet. Beachten Sie, dass `directUpdate` als native Konfiguration zusätzliche Behandlung in Ihrem JavaScript-Code erfordert. Bei Verwendung von `directUpdate` müssen Sie auf das `appReady`-Event hören und den Splash-Screen Ihrer App entsprechend ausblenden: ```js import { CapacitorUpdater } from '@capgo/capacitor-updater'; import { SplashScreen } from '@capacitor/splash-screen'; CapacitorUpdater.addListener('appReady', () => { // Splash Screen ausblenden SplashScreen.hide(); }); CapacitorUpdater.notifyAppReady(); ``` Das `appReady`-Event wird ausgelöst, sobald die App die Initialisierung abgeschlossen und alle ausstehenden Updates angewendet hat. Ab diesem Zeitpunkt ist es sicher, die Benutzeroberfläche Ihrer App anzuzeigen, da sichergestellt ist, dass der Nutzer die neueste Version sieht. Zusätzlich zur Behandlung des `appReady`-Events empfehlen wir, die Konfigurationsoption `keepUrlPathAfterReload` auf `true` zu setzen, wenn Sie `directUpdate` verwenden. Dies bewahrt den aktuellen URL-Pfad, wenn die App aufgrund eines Updates neu geladen wird, und hilft dabei, den Standort des Nutzers in der App beizubehalten und Desorientierung zu reduzieren. Wenn Sie das `appReady`-Event nicht behandeln und `keepUrlPathAfterReload` nicht setzen, wenn Sie `directUpdate` verwenden, könnte der Nutzer kurzzeitig eine veraltete Version der App sehen, zur Ausgangsroute zurückgeführt werden oder ein Flackern sehen, während das Update angewendet wird. Die Verwendung von `directUpdate` kann nützlich sein, um kritische Fehlerbehebungen oder Sicherheitspatches bereitzustellen, bringt aber einige Kompromisse mit sich: * Der Nutzer könnte ein kurzes Flackern oder einen Ladezustand sehen, während das Update angewendet wird, wenn Sie das `appReady`-Event nicht richtig behandeln * Wenn das Update den App-Status oder die Benutzeroberfläche ändert, könnte der Nutzer eine störende Änderung mitten in einer Sitzung sehen * Der Standort des Nutzers in der App könnte verloren gehen, wenn `keepUrlPathAfterReload` nicht gesetzt ist, was möglicherweise desorientierend wirkt * Sie müssen sorgfältig das Speichern und Wiederherstellen des Status handhaben, um einen reibungslosen Übergang zu gewährleisten Wenn Sie `directUpdate` aktivieren, empfehlen wir: * Behandlung des `appReady`-Events zur Kontrolle, wann die Benutzeroberfläche Ihrer App angezeigt wird * Setzen von `keepUrlPathAfterReload` auf `true`, um den Standort des Nutzers in der App zu bewahren * Speichern und Wiederherstellen des App-Status nach Bedarf, um Nutzerffortschritt nicht zu verlieren * Gründliches Testen des Update-Verhaltens Ihrer App, um sicherzustellen, dass es keine störenden Übergänge, verlorenen Status oder desorientierenden Standortänderungen gibt In den meisten Fällen bietet das Standard-Update-Verhalten die beste Balance zwischen schneller Update-Bereitstellung und minimaler Störung. Aber für Apps mit spezifischen Anforderungen bietet Capgo die Flexibilität, anzupassen, wann und wie Updates angewendet werden. # Update-Typen > Eine umfassende Referenz aller OTA-Update-Typen, die Capgo bereitstellt: Anwendungszeitpunkt, Verzögerungsbedingungen, Versionssperrung und Bereitstellungsmethoden. Capgo unterstützt verschiedene Arten von Over-the-Air (OTA) Updates. Diese Seite listet und erklärt alle, damit Sie die richtige Kombination für Ihre App wählen können. ## Anwendungszeitpunkt [Section titled “Anwendungszeitpunkt”](#anwendungszeitpunkt) Steuert **wann** ein Update angewendet wird, nachdem es heruntergeladen wurde. | Typ | Beschreibung | Anwendungsfall | | ----------------------------- | --------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ | | **Standard** | Download im Hintergrund, Anwendung wenn Benutzer die App in den Hintergrund verschiebt oder beendet | Die meisten Apps; minimale Unterbrechung | | **directUpdate: `atInstall`** | Sofortige Anwendung nur bei Neuinstallation oder App-Store-Update | Neue Benutzer erhalten die neueste Version; bestehende Benutzer nutzen den Standard-Ablauf | | **directUpdate: `onLaunch`** | Sofortige Anwendung bei Installation, Store-Update oder nach App-Beendigung | Balance zwischen Aktualität und Sitzungsstabilität | | **directUpdate: `always`** | Sofortige Anwendung wann immer ein Update heruntergeladen wird (auch beim Fortsetzen) | Kritische Fixes, Apps mit einfachem Zustand | Konfiguration in `capacitor.config.ts`: ```typescript plugins: { CapacitorUpdater: { directUpdate: false, // default // or: 'atInstall' | 'onLaunch' | 'always' } } ``` Tip Für vollständige Details und Splashscreen-Handhabung siehe [Update-Verhalten](/docs/live-updates/update-behavior/). ## Verzögerungsbedingungen [Section titled “Verzögerungsbedingungen”](#verzögerungsbedingungen) Bedingungen, die erfüllt sein müssen, **bevor** ein Update installiert wird. Verwenden Sie `setMultiDelay`, um sie zu kombinieren (alle Bedingungen müssen erfüllt sein). | Bedingung | Beschreibung | Beispiel | | ----------------- | ------------------------------------------------------------------------------------- | -------------------------------------------------------------- | | **date** | Warten bis nach einem bestimmten Datum/Zeitpunkt | Koordination mit serverseitiger Veröffentlichung | | **background** | Warten auf eine Mindestdauer (ms) nachdem die App in den Hintergrund verschoben wurde | Vermeidung der Anwendung bei schnellen App-Wechseln | | **nativeVersion** | Erfordert eine minimale native Binärversion | Blockierung von Updates bei inkompatibler nativer Code-Version | | **kill** | Warten bis zum nächsten App-Beendigungs-Ereignis | Anwendung nur bei vollständigem Neustart | ```typescript import { CapacitorUpdater } from '@capgo/capacitor-updater'; await CapacitorUpdater.setMultiDelay({ delayConditions: [ { kind: 'date', value: '2023-06-01T00:00:00.000Z' }, { kind: 'background', value: '60000' }, ], }); ``` Danger Die `kill`-Bedingung wird nach dem ersten Beendigungs-Ereignis ausgelöst, nicht beim nächsten Hintergrund-Ereignis wie die anderen. Dies wird in einer zukünftigen Version behoben. ## Versionssperrung (Channel-Richtlinie) [Section titled “Versionssperrung (Channel-Richtlinie)”](#versionssperrung-channel-richtlinie) Steuert, welche **Semver-Updates** ein Channel automatisch bereitstellt. Wird über `--disable-auto-update` bei Channels festgelegt. | Strategie | Blockiert | Erlaubt | Anwendungsfall | | ------------ | ---------------------------------- | ------------------------------------------------ | ----------------------------------------------------------- | | **none** | Nichts | Alle Updates | Standard; vollständiges Auto-Update | | **major** | 0.0.0 → 1.0.0 | Gleiche Major-Version (z.B. 1.x → 1.y) | Verhinderung von Breaking Changes bei alter nativer Version | | **minor** | 0.0.0 → 1.1.0, 1.1.0 → 1.2.0 | Gleiche Minor-Version (z.B. 1.2.x → 1.2.y) | Strengere Kontrolle innerhalb der Major-Version | | **patch** | Jede Änderung außer Patch-Erhöhung | Nur 0.0.311 → 0.0.314 | Sehr streng; nur Patch-Updates | | **metadata** | Updates ohne `min_update_version` | Updates mit expliziten Kompatibilitäts-Metadaten | Benutzerdefinierte Kompatibilitätsregeln pro Bundle | ```bash npx @capgo/cli channel set production --disable-auto-update major ``` Caution `patch` und `metadata` erfordern sorgfältige Einrichtung. Siehe [CLI-Befehle](/docs/cli/commands/#disable-updates-strategy) und [Versions-Targeting](/docs/live-updates/version-targeting/) für Details. ## Bereitstellungstypen [Section titled “Bereitstellungstypen”](#bereitstellungstypen) Wie das **Bundle auf das Gerät übertragen** wird. | Typ | Beschreibung | Wann verwenden | | ------------------------ | -------------------------------------------- | ------------------------------------------------------------------------ | | **Vollständiges Bundle** | Das gesamte JS-Bundle wird heruntergeladen | Erste Installation, große Änderungen oder wenn Delta nicht verfügbar ist | | **Delta (Manifest)** | Nur geänderte Dateien werden heruntergeladen | Die meisten Updates; schneller und bandbreitenfreundlich | ```bash # Full bundle (default) npx @capgo/cli bundle upload --channel production # Delta updates npx @capgo/cli bundle upload --channel production --delta ``` Tip Bei Verwendung von `directUpdate` aktivieren Sie [Delta-Updates](/docs/live-updates/differentials/), um die Download-Zeit zu minimieren und die UX zu verbessern. ## Schnellreferenz [Section titled “Schnellreferenz”](#schnellreferenz) | Kategorie | Typen | | --------------------------- | --------------------------------------------- | | **Anwendungszeitpunkt** | Standard, `atInstall`, `onLaunch`, `always` | | **Verzögerungsbedingungen** | `date`, `background`, `nativeVersion`, `kill` | | **Versionssperrung** | `none`, `major`, `minor`, `patch`, `metadata` | | **Bereitstellung** | Vollständiges Bundle, Delta (Manifest) | ## Verwandte Themen [Section titled “Verwandte Themen”](#verwandte-themen) * [Update-Verhalten](/docs/live-updates/update-behavior/) — Konfiguration von Anwendungszeitpunkt und Verzögerungen * [Versions-Targeting](/docs/live-updates/version-targeting/) — Channel-basiertes Versions-Routing * [Delta (Manifest) Updates](/docs/live-updates/differentials/) — Aktivierung von Teil-Downloads * [Channels](/docs/live-updates/channels/) — Channel-Konfiguration und Priorität # Version Targeting > Stellen Sie automatisch kompatible Updates für Benutzer basierend auf ihrer nativen App-Version bereit Diese Anleitung erklärt, wie Sie automatisch das neueste kompatible Bundle für Benutzer basierend auf ihrer nativen App-Version bereitstellen, **ähnlich wie bei Ionic AppFlow**. Dies gewährleistet vereinfachte Update-Verwaltung und schnellere Rollouts bei gleichzeitiger Vermeidung von Kompatibilitätsproblemen. Migration von Ionic AppFlow? Falls Sie von Ionic AppFlow migrieren, ist diese Anleitung besonders wichtig für Sie. AppFlow hat Updates automatisch nativen Versionen zugeordnet, und Capgo bietet die gleiche Möglichkeit mit noch mehr Kontrolle und Flexibilität. Siehe den [AppFlow-Migrationsleitfaden](/docs/upgrade/from-appflow-to-capgo) für schrittweise Migrationsanweisungen. ## Übersicht [Section titled “Übersicht”](#übersicht) Das Version-Targeting-System von Capgo ermöglicht es Ihnen zu: * **Automatisch kompatible Updates** für Benutzer basierend auf ihrer nativen App-Version bereitzustellen * **Brechende Änderungen** daran zu hindern, inkompatible App-Versionen zu erreichen * **Mehrere App-Versionen** gleichzeitig zu verwalten ohne komplexe Logik * **Updates nahtlos** für spezifische Benutzergruppen bereitzustellen ### Warum Version Targeting wichtig ist (besonders für AppFlow-Benutzer) [Section titled “Warum Version Targeting wichtig ist (besonders für AppFlow-Benutzer)”](#warum-version-targeting-wichtig-ist-besonders-für-appflow-benutzer) Falls Sie mit **Ionic AppFlow** vertraut sind, wissen Sie, wie wichtig es ist, sicherzustellen, dass Benutzer nur kompatible Updates erhalten. AppFlow hat Live-Update-Bundles automatisch nativen App-Versionen zugeordnet, um zu verhindern, dass inkompatibles JavaScript an ältere native Codes geliefert wird. **Capgo bietet die gleichen Sicherheitsgarantien**, mit zusätzlichen Funktionen: * Granularere Kontrolle über das Versions-Matching * Mehrere Strategien (Kanäle, Semver, native Einschränkungen) * Bessere Sichtbarkeit der Versionsverteilung * API- und CLI-Kontrolle neben Dashboard-Verwaltung Dieser Ansatz ist besonders nützlich, wenn: * Sie Benutzer auf verschiedenen Hauptversionen Ihrer App haben (z. B. v1.x, v2.x, v3.x) * Sie Abwärtskompatibilität aufrechterhalten müssen, während Sie brechende Änderungen einführen * Sie verhindern möchten, dass neuere Bundles älteren nativen Code beschädigen * Sie Benutzer graduell von einer Version zu einer anderen migrieren * **Sie von AppFlow migrieren** und die gleiche Update-Sicherheit beibehalten möchten ## Wie es funktioniert [Section titled “Wie es funktioniert”](#wie-es-funktioniert) Capgo verwendet einen mehrschichtigen Ansatz, um Benutzer mit kompatiblen Updates zu vergleichen: 1. **Native Versionsbeschränkungen**: Verhindern Sie, dass Bundles an inkompatible native Versionen geliefert werden 2. **Kanalbasiertes Routing**: Leiten Sie verschiedene App-Versionen an verschiedene Update-Kanäle weiter 3. **Semantische Versionierungskontrollen**: Blockieren Sie Updates automatisch über Major-/Minor-/Patch-Grenzen 4. **Geräteebene-Overrides**: Zielbenutzer spezifische Geräte oder Benutzergruppen an ### Version-Matching-Fluss [Section titled “Version-Matching-Fluss”](#version-matching-fluss) ```mermaid graph TD A[User Opens App] --> B{Check Device Override} B -->|Override Set| C[Use Override Channel] B -->|No Override| D{Check defaultChannel in App} D -->|Has defaultChannel| E[Use App's defaultChannel] D -->|No defaultChannel| F[Use Cloud Default Channel] C --> G{Check Version Constraints} E --> G F --> G G -->|Compatible| H[Deliver Update] G -->|Incompatible| I[Skip Update] ``` ## Strategie 1: Kanalbasiertes Versions-Routing [Section titled “Strategie 1: Kanalbasiertes Versions-Routing”](#strategie-1-kanalbasiertes-versions-routing) Dies ist der **empfohlene Ansatz** zum Verwalten von brechenden Änderungen und Hauptversions-Updates. Es ähnelt AppFlow’s Liefermodell. ### Beispielszenario [Section titled “Beispielszenario”](#beispielszenario) * **App v1.x** (100.000 Benutzer) → `production` Kanal * **App v2.x** (50.000 Benutzer mit brechenden Änderungen) → `v2` Kanal * **App v3.x** (10.000 Beta-Benutzer) → `v3` Kanal ### Implementierung [Section titled “Implementierung”](#implementierung) #### Schritt 1: Kanäle für jede Hauptversion konfigurieren [Section titled “Schritt 1: Kanäle für jede Hauptversion konfigurieren”](#schritt-1-kanäle-für-jede-hauptversion-konfigurieren) ```typescript // capacitor.config.ts für Version 1.x Builds import { CapacitorConfig } from '@capacitor/cli'; const config: CapacitorConfig = { appId: 'com.example.app', appName: 'Example App', plugins: { CapacitorUpdater: { autoUpdate: true, defaultChannel: 'production', // oder weglassen für Standard } } }; export default config; ``` ```typescript // capacitor.config.ts für Version 2.x Builds const config: CapacitorConfig = { appId: 'com.example.app', appName: 'Example App', plugins: { CapacitorUpdater: { autoUpdate: true, defaultChannel: 'v2', // Leitet v2-Benutzer automatisch weiter } } }; ``` ```typescript // capacitor.config.ts für Version 3.x Builds const config: CapacitorConfig = { appId: 'com.example.app', appName: 'Example App', plugins: { CapacitorUpdater: { autoUpdate: true, defaultChannel: 'v3', // Leitet v3-Benutzer automatisch weiter } } }; ``` #### Schritt 2: Kanäle erstellen [Section titled “Schritt 2: Kanäle erstellen”](#schritt-2-kanäle-erstellen) ```bash # Erstellen Sie Kanäle für jede Hauptversion npx @capgo/cli channel create production npx @capgo/cli channel create v2 npx @capgo/cli channel create v3 # Aktivieren Sie die Selbstzuweisung, damit Apps Kanäle wechseln können npx @capgo/cli channel set production --self-assign npx @capgo/cli channel set v2 --self-assign npx @capgo/cli channel set v3 --self-assign ``` #### Schritt 3: Versionsspezifische Bundles hochladen [Section titled “Schritt 3: Versionsspezifische Bundles hochladen”](#schritt-3-versionsspezifische-bundles-hochladen) ```bash # Für v1.x Benutzer (von v1-maintenance Branch) git checkout v1-maintenance npm run build npx @capgo/cli bundle upload --channel production # Für v2.x Benutzer (von v2-maintenance oder main Branch) git checkout main npm run build npx @capgo/cli bundle upload --channel v2 # Für v3.x Benutzer (von beta/v3 Branch) git checkout beta npm run build npx @capgo/cli bundle upload --channel v3 ``` Automatisches Routing Wenn Benutzer die App öffnen, stellen sie automatisch eine Verbindung zu ihrem designierten Kanal basierend auf dem `defaultChannel` in ihrem installierten App-Bundle her. Keine JavaScript-Codeänderungen erforderlich! ### Vorteile [Section titled “Vorteile”](#vorteile) * **Null Code-Änderungen** - Kanalrouting erfolgt automatisch * **Klare Trennung** - Jede Version hat ihre eigene Update-Pipeline * **Flexible Targeting** - Pushen Sie Updates an spezifische Versionsgruppen * **Sichere Rollouts** - Brechende Änderungen erreichen nie inkompatible Versionen ## Strategie 2: Semantische Versionierungskontrollen [Section titled “Strategie 2: Semantische Versionierungskontrollen”](#strategie-2-semantische-versionierungskontrollen) Verwenden Sie die integrierten semantischen Versionierungskontrollen von Capgo, um Updates über Versionsgrenzen hinweg zu verhindern. ### Deaktivieren Sie Auto-Update über Hauptversionen hinweg [Section titled “Deaktivieren Sie Auto-Update über Hauptversionen hinweg”](#deaktivieren-sie-auto-update-über-hauptversionen-hinweg) ```bash # Erstellen Sie einen Kanal, der Update über Hauptversionen blockiert npx @capgo/cli channel create stable --disable-auto-update major ``` Diese Konfiguration bedeutet: * Benutzer mit App-Version **1.2.3** erhalten Updates bis zu **1.9.9** * Benutzer erhalten **NICHT** Version **2.0.0** automatisch * Verhindert, dass brechende Änderungen älteren nativen Code erreichen ### Granulare Steuerungsoptionen [Section titled “Granulare Steuerungsoptionen”](#granulare-steuerungsoptionen) ```bash # Blockieren Sie Update über Minor-Versionen (1.2.x erhält 1.3.0 nicht) npx @capgo/cli channel set stable --disable-auto-update minor # Blockieren Sie Patch-Updates (1.2.3 erhält 1.2.4 nicht) npx @capgo/cli channel set stable --disable-auto-update patch # Erlauben Sie alle Updates npx @capgo/cli channel set stable --disable-auto-update none ``` Semantische Versionierung erforderlich Diese Strategie funktioniert nur, wenn Sie semantische Versionierung (Semver) für Ihre App-Versionen befolgen. Stellen Sie sicher, dass Ihre Versionsnummern das `MAJOR.MINOR.PATCH` Format befolgen. ## Strategie 3: Native Versionsbeschränkungen [Section titled “Strategie 3: Native Versionsbeschränkungen”](#strategie-3-native-versionsbeschränkungen) Geben Sie die Mindestanforderungen für native Versionen für Bundles an, um die Lieferung an inkompatible Geräte zu verhindern. ### Verwenden der nativeVersion Verzögerungsbedingung [Section titled “Verwenden der nativeVersion Verzögerungsbedingung”](#verwenden-der-nativeversion-verzögerungsbedingung) Beim Hochladen eines Bundles können Sie eine Mindest-Native-Version angeben: ```bash # Dieses Bundle erfordert native Version 2.0.0 oder höher npx @capgo/cli bundle upload \ --channel production \ --native-version "2.0.0" ``` Wie es funktioniert Geräte mit nativer Version 1.x erhalten dieses Bundle NICHT. Nur Geräte mit 2.0.0+ erhalten es. Dies ist perfekt für Updates, die neue native APIs oder Plugins erfordern. ### Anwendungsfälle [Section titled “Anwendungsfälle”](#anwendungsfälle) 1. **Neues Native Plugin erforderlich** ```bash # Bundle benötigt Camera-Plugin in v2.0.0 hinzugefügt npx @capgo/cli bundle upload --native-version "2.0.0" ``` 2. **Breaking Native API-Änderungen** ```bash # Bundle verwendet neue Capacitor 6 APIs npx @capgo/cli bundle upload --native-version "3.0.0" ``` 3. **Graduelle Migration** ```bash # Test Bundle nur auf neuester nativer Version npx @capgo/cli bundle upload \ --channel beta \ --native-version "2.5.0" ``` ## Strategie 4: Prävention von Auto-Downgrade [Section titled “Strategie 4: Prävention von Auto-Downgrade”](#strategie-4-prävention-von-auto-downgrade) Verhindern Sie, dass Benutzer Bundles erhalten, die älter als ihre aktuelle native Version sind. ### In Kanaleinstellungen aktivieren [Section titled “In Kanaleinstellungen aktivieren”](#in-kanaleinstellungen-aktivieren) Im Capgo-Dashboard: 1. Gehen Sie zu **Kanäle** → Wählen Sie Ihren Kanal 2. Aktivieren Sie **“Deaktivieren Sie automatisches Downgrade unter native”** 3. Speichern Sie die Änderungen Oder über CLI: ```bash npx @capgo/cli channel set production --disable-downgrade ``` ### Beispiel [Section titled “Beispiel”](#beispiel) * Geräteversion des Benutzers: Native Version **1.2.5** * Kanal Bundle: Version **1.2.3** * **Ergebnis**: Update wird blockiert (wäre ein Downgrade) Dies ist nützlich, wenn: * Benutzer eine neuere Version manuell aus dem App-Store installiert haben * Sie sicherstellen müssen, dass Benutzer immer die neuesten Sicherheits-Patches haben * Sie Regressionsfehler verhindern möchten ## Strategie 5: Geräteebene-Targeting [Section titled “Strategie 5: Geräteebene-Targeting”](#strategie-5-geräteebene-targeting) Überschreiben Sie die Kanalzuweisung für spezifische Geräte oder Benutzergruppen. ### Erzwinge spezifische Version zum Testen [Section titled “Erzwinge spezifische Version zum Testen”](#erzwinge-spezifische-version-zum-testen) ```typescript import { CapacitorUpdater } from '@capgo/capacitor-updater' // Erzwinge Beta-Tester zur Verwendung des v3-Kanals async function assignBetaTesters() { const deviceId = await CapacitorUpdater.getDeviceId() // Überprüfen Sie, ob Benutzer Beta-Tester ist if (isBetaTester(userId)) { await CapacitorUpdater.setChannel({ channel: 'v3' }) } } ``` ### Dashboard-Geräte-Override [Section titled “Dashboard-Geräte-Override”](#dashboard-geräte-override) Im Capgo-Dashboard: 1. Gehen Sie zu **Geräte** → Finden Sie Gerät 2. Klicken Sie auf **Set Channel** oder **Set Version** 3. Überschreiben Sie mit spezifischem Kanal oder Bundle-Version 4. Gerät erhält Updates von der überschriebenen Quelle Testing Updates Verwenden Sie Geräte-Overrides, um Updates auf Ihrem eigenen Gerät zu testen, bevor Sie sie für alle Benutzer einführen. ## Kompletter AppFlow-ähnlicher Workflow [Section titled “Kompletter AppFlow-ähnlicher Workflow”](#kompletter-appflow-ähnlicher-workflow) Hier ist ein vollständiges Beispiel, das alle Strategien kombiniert: ### 1. Initiales Setup (App v1.0.0) [Section titled “1. Initiales Setup (App v1.0.0)”](#1-initiales-setup-app-v100) ```bash # Erstellen Sie Production-Kanal mit Semver-Kontrollen npx @capgo/cli channel create production \ --disable-auto-update major \ --disable-downgrade ``` capacitor.config.ts ```typescript const config: CapacitorConfig = { plugins: { CapacitorUpdater: { autoUpdate: true, defaultChannel: 'production', } } }; ``` ### 2. Brechende Änderung freigeben (App v2.0.0) [Section titled “2. Brechende Änderung freigeben (App v2.0.0)”](#2-brechende-änderung-freigeben-app-v200) ```bash # Erstellen Sie v2 Kanal für neue Version npx @capgo/cli channel create v2 \ --disable-auto-update major \ --disable-downgrade \ --self-assign # Erstellen Sie Git-Branch für v1 Wartung git checkout -b v1-maintenance git push origin v1-maintenance ``` ```typescript // capacitor.config.ts für v2.0.0 const config: CapacitorConfig = { plugins: { CapacitorUpdater: { autoUpdate: true, defaultChannel: 'v2', // Neue Benutzer erhalten v2 Kanal } } }; ``` ### 3. Updates für beide Versionen pushen [Section titled “3. Updates für beide Versionen pushen”](#3-updates-für-beide-versionen-pushen) ```bash # Update v1.x Benutzer (Fehlerbehebung) git checkout v1-maintenance # Vornehmen von Änderungen npx @capgo/cli bundle upload \ --channel production \ --native-version "1.0.0" # Update v2.x Benutzer (neues Feature) git checkout main # Vornehmen von Änderungen npx @capgo/cli bundle upload \ --channel v2 \ --native-version "2.0.0" ``` ### 4. Versionsverteilung überwachen [Section titled “4. Versionsverteilung überwachen”](#4-versionsverteilung-überwachen) Verwenden Sie das Capgo-Dashboard zum Verfolgen von: * Wie viele Benutzer auf v1 vs v2 sind * Bundle-Adoptionsraten pro Version * Fehler oder Abstürze pro Version ### 5. Alte Version abschaffen [Section titled “5. Alte Version abschaffen”](#5-alte-version-abschaffen) Sobald v1-Nutzung unter Schwellenwert fällt: ```bash # Stopfen Sie das Hochladen zum Production-Kanal # Optional: Löschen Sie v1 Maintenance-Branch git branch -d v1-maintenance # Verschieben Sie alle verbleibenden Benutzer zum Standard # (Sie müssen über App-Store aktualisieren) ``` ## Kanal-Priorität [Section titled “Kanal-Priorität”](#kanal-priorität) Wenn mehrere Kanalkonfigurationen vorhanden sind, verwendet Capgo diese Prioritätsreihenfolge: 1. **Geräte-Override** (Dashboard oder API) - Höchste Priorität 2. **Cloud Override** via `setChannel()` Aufruf 3. **defaultChannel** in capacitor.config.ts 4. **Standard Kanal** (Cloud-Einstellung) - Niedrigste Priorität Prioritäts-Beispiel Wenn die App eines Benutzers `defaultChannel: 'v2'` hat, aber Sie ihr Gerät im Dashboard zu `'beta'` überschreiben, erhalten sie Updates vom `'beta'` Kanal. ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) ### 1. Setzen Sie immer defaultChannel für Hauptversionen [Section titled “1. Setzen Sie immer defaultChannel für Hauptversionen”](#1-setzen-sie-immer-defaultchannel-für-hauptversionen) ```typescript // ✅ Gut: Jede Hauptversion hat expliziten Kanal // v1.x → production // v2.x → v2 // v3.x → v3 // ❌ Schlecht: Verlassen Sie sich auf dynamischen Kanalwechsel // Alle Versionen → production, Manueller Wechsel ``` ### 2. Verwenden Sie semantische Versionierung [Section titled “2. Verwenden Sie semantische Versionierung”](#2-verwenden-sie-semantische-versionierung) ```bash # ✅ Gut 1.0.0 → 1.0.1 → 1.1.0 → 2.0.0 # ❌ Schlecht 1.0 → 1.1 → 2 → 2.5 ``` ### 3. Behalten Sie separate Branches bei [Section titled “3. Behalten Sie separate Branches bei”](#3-behalten-sie-separate-branches-bei) ```bash # ✅ Gut: Separate Branches pro Hauptversion main (v3.x) v2-maintenance (v2.x) v1-maintenance (v1.x) # ❌ Schlecht: Einzelner Branch für alle Versionen ``` ### 4. Testen Sie vor dem Rollout [Section titled “4. Testen Sie vor dem Rollout”](#4-testen-sie-vor-dem-rollout) ```bash # Testen Sie zuerst auf Beta-Kanal npx @capgo/cli bundle upload --channel beta # Überwachen Sie Probleme, dann fördern Sie zur Production npx @capgo/cli bundle upload --channel production ``` ### 5. Versionsverteilung überwachen [Section titled “5. Versionsverteilung überwachen”](#5-versionsverteilung-überwachen) Überprüfen Sie regelmäßig Ihr Dashboard: * Aktualisieren Benutzer auf neuere native Versionen? * Erhalten alte Versionen noch hohen Verkehr? * Sollten Sie alte Kanäle abschaffen? ## Vergleich mit Ionic AppFlow [Section titled “Vergleich mit Ionic AppFlow”](#vergleich-mit-ionic-appflow) Für Teams, die von **Ionic AppFlow** migrieren, ist hier ein Vergleich des Version-Targetings von Capgo: | Feature | Ionic AppFlow | Capgo | | --------------------------------- | ------------------------------------------- | --------------------------------------------------------- | | **Versionsbasiertes Routing** | Automatisch basierend auf nativer Version | Automatisch via `defaultChannel` + mehrere Strategien | | **Semantische Versionierung** | Grundlegende Unterstützung | Erweitert mit `--disable-auto-update` (major/minor/patch) | | **Native Versionsbeschränkungen** | Manuelle Konfiguration im AppFlow-Dashboard | Eingebautes `--native-version` Flag in CLI | | **Kanal-Management** | Web UI + CLI | Web UI + CLI + API | | **Geräte-Overrides** | Begrenzte Geräteebene-Kontrolle | Vollständige Kontrolle via Dashboard/API | | **Auto-Downgrade-Prävention** | Ja | Ja via `--disable-downgrade` | | **Multi-Versions-Wartung** | Manuelle Branch-/Kanal-Verwaltung | Automatisiert mit Kanal-Priorität | | **Self-Hosting** | Nein | Ja (vollständige Kontrolle) | | **Versions-Analytik** | Grundlegend | Detailliert pro-Version Metriken | AppFlow-Parität und darüber hinaus Capgo bietet **alle Version-Targeting-Fähigkeiten**, die AppFlow angeboten hat, plus zusätzliche Kontrollmechanismen. Wenn Sie sich auf AppFlow’s automatisches Versions-Matching verlassen haben, werden Sie feststellen, dass Capgo gleich sicher ist mit mehr Flexibilität. ## Fehlerbehebung [Section titled “Fehlerbehebung”](#fehlerbehebung) ### Benutzer erhalten Updates nicht [Section titled “Benutzer erhalten Updates nicht”](#benutzer-erhalten-updates-nicht) Überprüfen Sie Folgendes: 1. **Kanal-Zuordnung**: Überprüfen Sie, dass Gerät auf korrektem Kanal ist ```typescript const channel = await CapacitorUpdater.getChannel() console.log('Current channel:', channel) ``` 2. **Versions-Einschränkungen**: Überprüfen Sie, ob Bundle native Versionsanforderungen hat * Dashboard → Bundles → Überprüfen Sie “Native Version” Spalte 3. **Semver-Einstellungen**: Überprüfen Sie die `disable-auto-update` Einstellung des Kanals ```bash npx @capgo/cli channel list ``` 4. **Geräte-Override**: Überprüfen Sie, ob Gerät manuellen Override hat * Dashboard → Geräte → Suchen Sie nach Gerät → Überprüfen Sie Kanal/Version ### Bundle wird an falsche Version geliefert [Section titled “Bundle wird an falsche Version geliefert”](#bundle-wird-an-falsche-version-geliefert) 1. **Überprüfen Sie defaultChannel**: Stellen Sie sicher, korrekter Kanal in `capacitor.config.ts` 2. **Überprüfen Sie Bundle-Upload**: Verifizieren Sie, dass Bundle zum beabsichtigten Kanal hochgeladen wurde 3. **Überprüfen Sie Native-Version**: Bestätigen Sie, dass `--native-version` Flag korrekt verwendet wurde ### Brechende Änderungen beeinflussen alte Versionen [Section titled “Brechende Änderungen beeinflussen alte Versionen”](#brechende-änderungen-beeinflussen-alte-versionen) 1. **Sofort-Fix**: Überschreiben Sie betroffene Geräte zum sicheren Bundle * Dashboard → Geräte → Mehrfach auswählen → Set Version 2. **Langfristig-Fix**: Erstellen Sie versionierte Kanäle und behalten Sie separate Branches 3. **Prävention**: Testen Sie immer Updates auf repräsentativen Geräten vor dem Rollout ## Migration von Ionic AppFlow [Section titled “Migration von Ionic AppFlow”](#migration-von-ionic-appflow) Falls Sie von **Ionic AppFlow** migrieren, funktioniert Version-Targeting in Capgo sehr ähnlich, mit verbesserter Flexibilität: ### Konzept-Mapping [Section titled “Konzept-Mapping”](#konzept-mapping) | AppFlow Concept | Capgo Equivalent | Notes | | ------------------------------ | ---------------------------------------------- | ---------------------------------- | | **Deploy Channel** | Capgo Channel | Gleiches Konzept, leistungsreicher | | **Native Version Lock** | `--native-version` flag | Granularere Kontrolle | | **Channel Priority** | Channel Priorität (override → cloud → default) | Transparentere Priorität | | **Deployment Target** | Channel + Semver-Kontrollen | Mehrere Strategien verfügbar | | **Production Channel** | `production` Kanal (oder beliebiger Name) | Flexible Benennung | | **Git-based deployment** | CLI Bundle-Upload von Branch | Gleicher Workflow | | **Automatic version matching** | `defaultChannel` + Versions-Einschränkungen | Erweitert mit mehreren Strategien | ### Schlüsseldifferenzen für AppFlow-Benutzer [Section titled “Schlüsseldifferenzen für AppFlow-Benutzer”](#schlüsseldifferenzen-für-appflow-benutzer) 1. **Mehr Kontrolle**: Capgo gibt Ihnen mehrere Strategien (Kanäle, Semver, native Version), die kombiniert werden können 2. **Bessere Sichtbarkeit**: Dashboard zeigt Versionsverteilung und Kompatibilitätsprobleme 3. **API-Zugriff**: Vollständige programmatische Kontrolle über Version-Targeting 4. **Self-Hosting**: Option, Ihren eigenen Update-Server mit gleicher Versionslogik zu betreiben ### Migrations-Schritte [Section titled “Migrations-Schritte”](#migrations-schritte) 1. **Ordnen Sie Ihre AppFlow-Kanäle** Capgo-Kanälen zu (üblicherweise 1:1) 2. **Setzen Sie `defaultChannel`** in `capacitor.config.ts` für jede Hauptversion 3. **Konfigurieren Sie Semver-Regeln**, wenn Sie automatisches Blockieren auf Versionsgrenzen wünschen 4. **Laden Sie versionsspezifische Bundles** mit `--native-version` Flag hoch 5. **Überwachen Sie Versionsverteilung** im Capgo-Dashboard Kompletter Migrations-Leitfaden Für vollständige Migrationsanweisungen einschließlich SDK-Ersatz und API-Zuordnung, siehe den [AppFlow zu Capgo Migrations-Leitfaden](/docs/upgrade/from-appflow-to-capgo). ## Erweiterte Muster [Section titled “Erweiterte Muster”](#erweiterte-muster) ### Gradueller Rollout nach Version [Section titled “Gradueller Rollout nach Version”](#gradueller-rollout-nach-version) ```typescript // Migrieren Sie v1-Benutzer graduell zu v2 async function migrateUsers() { const deviceId = await CapacitorUpdater.getDeviceId() const rolloutPercentage = 10 // Beginnen Sie mit 10% // Hash Device-ID zum Abrufen deterministischen Prozentsatz const hash = hashCode(deviceId) % 100 if (hash < rolloutPercentage) { // Benutzer ist in Rollout-Gruppe - Migrieren Sie zu v2 await CapacitorUpdater.setChannel({ channel: 'v2' }) } } ``` ### Feature-Flags nach Version [Section titled “Feature-Flags nach Version”](#feature-flags-nach-version) ```typescript // Aktivieren Sie Features basierend auf nativer Version async function checkFeatureAvailability() { const info = await CapacitorUpdater.getDeviceId() const nativeVersion = info.nativeVersion if (compareVersions(nativeVersion, '2.0.0') >= 0) { // Aktivieren Sie Features, die v2.0.0+ erfordern enableNewCameraFeature() } } ``` ### A/B-Tests über Versionen hinweg [Section titled “A/B-Tests über Versionen hinweg”](#ab-tests-über-versionen-hinweg) ```typescript // Führen Sie A/B-Tests innerhalb gleicher nativer Version aus async function assignABTest() { const nativeVersion = await getNativeVersion() if (nativeVersion.startsWith('2.')) { // Nur A/B-Test auf v2-Benutzern const variant = Math.random() < 0.5 ? 'v2-test-a' : 'v2-test-b' await CapacitorUpdater.setChannel({ channel: variant }) } } ``` ## Zusammenfassung [Section titled “Zusammenfassung”](#zusammenfassung) Capgo bietet mehrere Strategien für versionsspezifische Update-Lieferung: 1. **Kanalbasiertes Routing**: Automatische Versionsaufteilung via `defaultChannel` 2. **Semantische Versionierung**: Verhindern Sie Updates über Major-/Minor-/Patch-Grenzen 3. **Native Versionsbeschränkungen**: Erfordern Sie Mindest-Native-Version für Bundles 4. **Auto-Downgrade-Prävention**: Liefern Sie nie ältere Bundles an neuere native Versionen 5. **Geräte-Overrides**: Manuelle Kontrolle zum Testen und Targeting Durch das Kombinieren dieser Strategien können Sie AppFlow-ähnliche automatische Update-Lieferung mit noch mehr Flexibilität und Kontrolle erreichen. Wählen Sie den Ansatz, der am besten zu Ihrem App’s Versionierung und Deployment-Workflow passt. Für mehr Details zu spezifischen Funktionen: * [Leitfaden zu brechenden Änderungen](/docs/live-updates/breaking-changes) - Detaillierte Kanal-Versionierungs-Strategie * [Kanal-Verwaltung](/docs/live-updates/channels) - Vollständige Kanal-Konfigurationsreferenz * [Update-Verhalten](/docs/live-updates/update-behavior) - Native Versions-Verzögerungen und Bedingungen # Funktionen und Einstellungen > Alle verfügbaren Methoden und Einstellungen des Plugins # Updater Plugin Konfiguration [Section titled “Updater Plugin Konfiguration”](#updater-plugin-konfiguration) Siehe Github [Readme](https://github.com/Cap-go/capacitor-updater) für weitere Informationen CapacitorUpdater kann mit diesen Optionen konfiguriert werden: | Eigenschaft | Typ | Beschreibung | Standard | Seit | | ---------------------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------- | ------- | | **`appReadyTimeout`** | `number` | Konfiguriert die Anzahl der Millisekunden, die das native Plugin warten soll, bevor ein Update als ‘fehlgeschlagen’ gilt. Nur verfügbar für Android und iOS | `10000 // (10 Sekunden)` | | | **`responseTimeout`** | `number` | Konfiguriert die Anzahl der Millisekunden, die das native Plugin warten soll, bevor API-Timeout eintritt. Nur verfügbar für Android und iOS | `20 // (20 Sekunden)` | | | **`autoDeleteFailed`** | `boolean` | Konfiguriert, ob das Plugin fehlgeschlagene Bundles automatisch löschen soll. Nur verfügbar für Android und iOS | `true` | | | **`autoDeletePrevious`** | `boolean` | Konfiguriert, ob das Plugin vorherige Bundles nach einem erfolgreichen Update automatisch löschen soll. Nur verfügbar für Android und iOS | `true` | | | **`autoUpdate`** | `boolean` | Konfiguriert, ob das Plugin Auto-Update über einen Update-Server verwenden soll. Nur verfügbar für Android und iOS | `true` | | | **`resetWhenUpdate`** | `boolean` | Löscht automatisch zuvor heruntergeladene Bundles, wenn ein neueres natives App-Bundle auf dem Gerät installiert wird. Nur verfügbar für Android und iOS | `true` | | | **`updateUrl`** | `string` | Konfiguriert die URL / den Endpunkt, an den Update-Prüfungen gesendet werden. Nur verfügbar für Android und iOS | `https://plugin.capgo.app/updates` | | | **`channelUrl`** | `string` | Konfiguriert die URL / den Endpunkt für Kanal-Operationen. Nur verfügbar für Android und iOS | `https://plugin.capgo.app/channel_self` | | | **`statsUrl`** | `string` | Konfiguriert die URL / den Endpunkt, an den Update-Statistiken gesendet werden. Nur verfügbar für Android und iOS. Auf "" setzen, um Statistik-Reporting zu deaktivieren | `https://plugin.capgo.app/stats` | | | **`privateKey`** | `string` | Konfiguriert den privaten Schlüssel für Ende-zu-Ende Live-Update-Verschlüsselung. Nur verfügbar für Android und iOS. Veraltet in Version 6.2.0, wird in Version 7.0.0 entfernt | `undefined` | | | **`publicKey`** | `string` | Konfiguriert den öffentlichen Schlüssel für Ende-zu-Ende Live-Update-Verschlüsselung Version 2. Nur verfügbar für Android und iOS | `undefined` | 6.2.0 | | **`version`** | `string` | Konfiguriert die aktuelle Version der App. Wird für die erste Update-Anfrage verwendet. Wenn nicht gesetzt, holt das Plugin die Version aus dem nativen Code. Nur verfügbar für Android und iOS | `undefined` | 4.17.48 | | **`directUpdate`** | `boolean` | Lässt das Plugin Updates direkt installieren, wenn die App gerade aktualisiert/installiert wurde. Nur für autoUpdate-Modus. Nur verfügbar für Android und iOS | `undefined` | 5.1.0 | | **`periodCheckDelay`** | `number` | Konfiguriert die Verzögerungsperiode für periodische Update-Prüfungen in Sekunden. Nur verfügbar für Android und iOS. Kann nicht weniger als 600 Sekunden (10 Minuten) sein | `600 // (10 Minuten)` | | | **`localS3`** | `boolean` | Konfiguriert die CLI zur Verwendung eines lokalen Servers für Tests oder selbst-gehosteten Update-Server | `undefined` | 4.17.48 | | **`localHost`** | `string` | Konfiguriert die CLI zur Verwendung eines lokalen Servers für Tests oder selbst-gehosteten Update-Server | `undefined` | 4.17.48 | | **`localWebHost`** | `string` | Konfiguriert die CLI zur Verwendung eines lokalen Servers für Tests oder selbst-gehosteten Update-Server | `undefined` | 4.17.48 | | **`localSupa`** | `string` | Konfiguriert die CLI zur Verwendung eines lokalen Servers für Tests oder selbst-gehosteten Update-Server | `undefined` | 4.17.48 | | **`localSupaAnon`** | `string` | Konfiguriert die CLI zur Verwendung eines lokalen Servers für Tests | `undefined` | 4.17.48 | | **`localApi`** | `string` | Konfiguriert die CLI zur Verwendung einer lokalen API für Tests | `undefined` | 6.3.3 | | **`localApiFiles`** | `string` | Konfiguriert die CLI zur Verwendung einer lokalen Datei-API für Tests | `undefined` | 6.3.3 | | **`allowModifyUrl`** | `boolean` | Erlaubt dem Plugin, updateUrl, statsUrl und channelUrl dynamisch von der JavaScript-Seite zu ändern | `false` | 5.4.0 | | **`defaultChannel`** | `string` | Setzt den Standard-Kanal für die App in der Konfiguration | `undefined` | 4.17.48 | | **`appId`** | `string` | App-ID in der Konfiguration für die App konfigurieren | `undefined` | 600 | | **`keepUrlPathAfterReload`** | `boolean` | Plugin so konfigurieren, dass der URL-Pfad nach einem Reload erhalten bleibt WARNUNG: Wenn ein Reload ausgelöst wird, wird ‘windowhistory’ gelöscht | `false` | 680 | ## Beispiele [Section titled “Beispiele”](#beispiele) In `capacitorconfigjson`: ```json { "plugins": { "CapacitorUpdater": { "appReadyTimeout": 1000 // (1 Sekunde), "responseTimeout": 10 // (10 Sekunden), "autoDeleteFailed": false, "autoDeletePrevious": false, "autoUpdate": false, "resetWhenUpdate": false, "updateUrl": https://examplecom/api/auto_update, "channelUrl": https://examplecom/api/channel, "statsUrl": https://examplecom/api/stats, "privateKey": undefined, "publicKey": undefined, "version": undefined, "directUpdate": undefined, "periodCheckDelay": undefined, "localS3": undefined, "localHost": undefined, "localWebHost": undefined, "localSupa": undefined, "localSupaAnon": undefined, "localApi": undefined, "localApiFiles": undefined, "allowModifyUrl": undefined, "defaultChannel": undefined, "appId": undefined, "keepUrlPathAfterReload": undefined } } } ``` In `capacitorconfigts`: ```ts import { CapacitorConfig } from '@capacitor/cli'; const config: CapacitorConfig = { plugins: { CapacitorUpdater: { appReadyTimeout: 1000 // (1 Sekunde), responseTimeout: 10 // (10 Sekunden), autoDeleteFailed: false, autoDeletePrevious: false, autoUpdate: false, resetWhenUpdate: false, updateUrl: https://examplecom/api/auto_update, channelUrl: https://examplecom/api/channel, statsUrl: https://examplecom/api/stats, privateKey: undefined, publicKey: undefined, version: undefined, directUpdate: undefined, periodCheckDelay: undefined, localS3: undefined, localHost: undefined, localWebHost: undefined, localSupa: undefined, localSupaAnon: undefined, localApi: undefined, localApiFiles: undefined, allowModifyUrl: undefined, defaultChannel: undefined, appId: undefined, keepUrlPathAfterReload: undefined, }, }, }; export default config; ``` * [`notifyAppReady()`](#notifyappready) * [`setUpdateUrl()`](#setupdateurl) * [`setStatsUrl()`](#setstatsurl) * [`setChannelUrl()`](#setchannelurl) * [`download()`](#download) * [`next()`](#next) * [`set()`](#set) * [`delete()`](#delete) * [`list()`](#list) * [`reset()`](#reset) * [`current()`](#current) * [`reload()`](#reload) * [`setMultiDelay()`](#setmultidelay) * [`cancelDelay()`](#canceldelay) * [`getLatest()`](#getlatest) * [`setChannel()`](#setchannel) * [`unsetChannel()`](#unsetchannel) * [`getChannel()`](#getchannel) * [`setCustomId()`](#setcustomid) * [`getBuiltinVersion()`](#getbuiltinversion) * [`getDeviceId()`](#getdeviceid) * [`getPluginVersion()`](#getpluginversion) * [`isAutoUpdateEnabled()`](#isautoupdateenabled) * [`removeAllListeners()`](#removealllisteners) * [`addListener('download', )`](#addlistenerdownload-) * [`addListener('noNeedUpdate', )`](#addlistenernoneedupdate-) * [`addListener('updateAvailable', )`](#addlistenerupdateavailable-) * [`addListener('downloadComplete', )`](#addlistenerdownloadcomplete-) * [`addListener('majorAvailable', )`](#addlistenermajoravailable-) * [`addListener('updateFailed', )`](#addlistenerupdatefailed-) * [`addListener('downloadFailed', )`](#addlistenerdownloadfailed-) * [`addListener('appReloaded', )`](#addlistenerappreloaded-) * [`addListener('appReady', )`](#addlistenerappready-) * [`isAutoUpdateAvailable()`](#isautoupdateavailable) * [`getNextBundle()`](#getnextbundle) * [Schnittstellen](#interfaces) * [Typalias](#type-aliases) # Methoden [Section titled “Methoden”](#methoden) ## notifyAppReady() [Section titled “notifyAppReady()”](#notifyappready) ```typescript notifyAppReady() => Promise ``` Benachrichtigt Capacitor Updater, dass das aktuelle Bundle funktioniert (ein Rollback erfolgt, wenn diese Methode nicht bei jedem App-Start aufgerufen wird) Standardmäßig sollte diese Methode innerhalb der ersten 10 Sekunden nach dem App-Start aufgerufen werden, andernfalls erfolgt ein Rollback Dieses Verhalten kann mit {@link appReadyTimeout} geändert werden **Returns:** `Promise` *** ## setUpdateUrl() [Section titled “setUpdateUrl()”](#setupdateurl) ```typescript setUpdateUrl(options: UpdateUrl) => Promise ``` Legt die updateUrl für die App fest, diese wird zum Prüfen auf Updates verwendet | Param | Type | Description | | ------------- | ----------- | ------------------------------------------------------ | | **`options`** | `UpdateUrl` | enthält die URL, die für Update-Prüfungen genutzt wird | **Seit:** 540 *** ## setStatsUrl() [Section titled “setStatsUrl()”](#setstatsurl) ```typescript setStatsUrl(options: StatsUrl) => Promise ``` Legt die statsUrl für die App fest, diese wird zum Senden von Statistiken verwendet. Wenn ein leerer String übergeben wird, wird die Erfassung von Statistiken deaktiviert | Param | Type | Description | | ------------- | ---------- | ------------------------------------------------------------------ | | **`options`** | `StatsUrl` | enthält die URL, die für das Senden von Statistiken verwendet wird | **Seit:** 540 *** ## setChannelUrl() [Section titled “setChannelUrl()”](#setchannelurl) ```typescript setChannelUrl(options: ChannelUrl) => Promise ``` Legt die channelUrl für die App fest, diese wird zum Setzen des Kanals verwendet | Param | Type | Description | | ------------- | ------------ | ------------------------------------------------------------- | | **`options`** | `ChannelUrl` | enthält die URL, die für das Setzen des Kanals verwendet wird | **Seit:** 540 *** ## download() [Section titled “download()”](#download) ```typescript download(options: DownloadOptions) => Promise ``` Lädt ein neues Bundle von der angegebenen URL herunter, es sollte eine ZIP-Datei sein, mit Dateien innerhalb oder mit einer eindeutigen ID innerhalb mit all Ihren Dateien | Param | Type | Description | | ------------- | ----------------- | --------------------------------------------------------------------------------------------- | | **`options`** | `DownloadOptions` | Die {@link [DownloadOptions](#downloadoptions)} für das Herunterladen eines neuen Bundle-ZIPs | **Returns:** `Promise` *** ## next() [Section titled “next()”](#next) ```typescript next(options: BundleId) => Promise ``` Legt das nächste Bundle fest, das verwendet werden soll, wenn die App neu geladen wird| Param | Type | Beschreibung | | ------------- | --------------------------------------------- | ---------------------------------------------------------------------------------------------- | | **`options`** | `BundleId` | Enthält die ID des nächsten Bundles, das beim nächsten App-Start gesetzt wird {@link [BundleInfoid](#bundleinfo)} | **Returns:** `Promise` *** ## set() [Section titled “set()”](#set) ```typescript set(options: BundleId) => Promise ``` Setzt das aktuelle Bundle und lädt die App sofort neu | Param | Type | Beschreibung | | ------------- | ---------- | ------------------------------------------------------------------------ | | **`options`** | `BundleId` | Ein {@link [BundleId](#bundleid)} Objekt, das die neue Bundle-ID enthält | *** ## delete() [Section titled “delete()”](#delete) ```typescript delete(options: BundleId) => Promise ``` Löscht das angegebene Bundle aus dem nativen App-Speicher. Verwenden Sie {@link list} um die gespeicherten Bundle-IDs zu erhalten | Param | Type | Beschreibung | | ------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | | **`options`** | `BundleId` | Ein {@link [BundleId](#bundleid)} Objekt, das die ID des zu löschenden Bundles enthält (Hinweis: dies ist die Bundle-ID, NICHT der Versionsname) | *** ## list() [Section titled “list()”](#list) ```typescript list(options?: ListOptions | undefined) => Promise ``` Alle lokal heruntergeladenen Bundles in Ihrer App abrufen | Param | Type | Beschreibung | | ------------- | ------------- | --------------------------------------------------------------------- | | **`options`** | `ListOptions` | Die {@link [ListOptions](#listoptions)} für das Auflisten von Bundles | **Returns:** `Promise` *** ## reset() [Section titled “reset()”](#reset) ```typescript reset(options?: ResetOptions | undefined) => Promise ``` Setzt die App auf das ‘builtin’ Bundle (das an den Apple App Store / Google Play Store gesendet wurde) oder das zuletzt erfolgreich geladene Bundle zurück | Param | Type | Beschreibung | | ------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **`options`** | `ResetOptions` | Enthält {@link [ResetOptionstoLastSuccessful](#resetoptions)}, `true` setzt auf das builtin Bundle zurück und `false` setzt auf das zuletzt erfolgreich geladene Bundle zurück | *** ## current() [Section titled “current()”](#current) ```typescript current() => Promise ``` Ruft das aktuelle Bundle ab, wenn keines gesetzt ist, wird ‘builtin’ zurückgegeben. currentNative ist das ursprünglich auf dem Gerät installierte Bundle **Returns:** `Promise` *** ## reload() [Section titled “reload()”](#reload) ```typescript reload() => Promise ``` Die Ansicht neu laden *** ## setMultiDelay() [Section titled “setMultiDelay()”](#setmultidelay) ```typescript setMultiDelay(options: MultiDelayConditions) => Promise ``` Setzt ein {@link [DelayCondition](#delaycondition)} Array mit Bedingungen, die das Plugin verwendet, um das Update zu verzögern Nachdem alle Bedingungen erfüllt sind, wird der Update-Prozess wie gewohnt neu gestartet, sodass das Update nach dem Hintergrundsetzen oder Beenden der App installiert wird Für die Art ‘date’ sollte der Wert ein iso8601-Datums-String sein Für die Art ‘background’ sollte der Wert eine Zahl in Millisekunden sein Für die Art ‘nativeVersion’ sollte der Wert die Versionsnummer sein Für die Art ‘kill’ wird der Wert nicht verwendet Die Funktion hat inkonsistentes Verhalten: Die Option kill löst das Update nach dem ersten Beenden aus und nicht nach dem nächsten Hintergrundsetzen wie andere Optionen. Dies wird in einem zukünftigen Major Release behoben | Param | Type | Beschreibung | | ------------- | ---------------------- | --------------------------------------------------------------------------------------- | | **`options`** | `MultiDelayConditions` | Enthält das {@link [MultiDelayConditions](#multidelayconditions)} Array von Bedingungen | **Seit:** 430 *** ## cancelDelay() [Section titled “cancelDelay()”](#canceldelay) ```typescript cancelDelay() => Promise ``` Bricht eine {@link [DelayCondition](#delaycondition)} ab, um ein Update sofort zu verarbeiten **Seit:** 400 *** ## getLatest() [Section titled “getLatest()”](#getlatest) ```typescript getLatest(options?: GetLatestOptions | undefined) => Promise ``` Neuestes verfügbares Bundle von der Update-URL abrufen | Param | Type | | ------------- | ------------------ | | **`options`** | `GetLatestOptions` | **Returns:** `Promise` **Seit:** 400 *** ## setChannel() [Section titled “setChannel()”](#setchannel) ```typescript setChannel(options: SetChannelOptions) => Promise ``` Setzt den Kanal für dieses Gerät. Der Kanal muss Selbstzuweisung erlauben, damit dies funktioniert Verwenden Sie diese Methode nicht, um den Kanal beim Start zu setzen, wenn `autoUpdate` in der {@link PluginsConfig} aktiviert ist Diese Methode dient dazu, den Kanal zu setzen, nachdem die App bereit ist Diese Methode sendet eine Anfrage an den Capgo-Backend, um die Geräte-ID mit dem Kanal zu verknüpfen. Capgo kann dies je nach den Einstellungen Ihres Kanals akzeptieren oder ablehnen | Param | Type | Beschreibung | | ------------- | ------------------- | ----------------------------------------------------------------------- | | **`options`** | `SetChannelOptions` | Ist die {@link [SetChannelOptions](#setchanneloptions)} Kanal zu setzen | **Returns:** `Promise` **Seit:** 470 *** ## unsetChannel() [Section titled “unsetChannel()”](#unsetchannel) ```typescript unsetChannel(options: UnsetChannelOptions) => Promise ``` Hebt die Einstellung des Kanals für dieses Gerät aufDas Gerät kehrt dann zum Standardkanal zurück | Param | Type | | ------------- | --------------------- | | **`options`** | `UnsetChannelOptions` | **Seit:** 470 *** ## getChannel() [Section titled “getChannel()”](#getchannel) ```typescript getChannel() => Promise ``` Rufe den Kanal für dieses Gerät ab **Returns:** `Promise` **Seit:** 480 *** ## setCustomId() [Section titled “setCustomId()”](#setcustomid) ```typescript setCustomId(options: SetCustomIdOptions) => Promise ``` Lege eine benutzerdefinierte ID für dieses Gerät fest | Param | Type | Description | | ------------- | -------------------- | ----------------------------------------------------------------------------- | | **`options`** | `SetCustomIdOptions` | ist die {@link [SetCustomIdOptions](#setcustomidoptions)} customId zum Setzen | **Seit:** 490 *** ## getBuiltinVersion() [Section titled “getBuiltinVersion()”](#getbuiltinversion) ```typescript getBuiltinVersion() => Promise ``` Rufe die native App-Version oder die in der Konfiguration festgelegte Builtin-Version ab **Returns:** `Promise` **Seit:** 520 *** ## getDeviceId() [Section titled “getDeviceId()”](#getdeviceid) ```typescript getDeviceId() => Promise ``` Rufe eindeutige ID ab, die zur Identifizierung des Geräts verwendet wird (wird an den Auto-Update-Server gesendet) **Returns:** `Promise` *** ## getPluginVersion() [Section titled “getPluginVersion()”](#getpluginversion) ```typescript getPluginVersion() => Promise ``` Rufe die native Capacitor Updater Plugin-Version ab (wird an den Auto-Update-Server gesendet) **Returns:** `Promise` *** ## isAutoUpdateEnabled() [Section titled “isAutoUpdateEnabled()”](#isautoupdateenabled) ```typescript isAutoUpdateEnabled() => Promise ``` Rufe den Status der Auto-Update-Konfiguration ab **Returns:** `Promise` *** ## removeAllListeners() [Section titled “removeAllListeners()”](#removealllisteners) ```typescript removeAllListeners() => Promise ``` Entferne alle Listener für dieses Plugin **Seit:** 100 *** ## addListener(‘download’, ) [Section titled “addListener(‘download’, )”](#addlistenerdownload) ```typescript addListener(eventName: 'download', listenerFunc: (state: DownloadEvent) => void) => Promise ``` Höre auf Bundle-Download-Events in der App. Wird ausgelöst, wenn ein Download startet, während des Downloads und wenn er abgeschlossen ist | Param | Type | | ------------------ | -------------------------------- | | **`eventName`** | `’download’` | | **`listenerFunc`** | `(state: DownloadEvent) => void` | **Returns:** `Promise` **Seit:** 2011 *** ## addListener(‘noNeedUpdate’, ) [Section titled “addListener(‘noNeedUpdate’, )”](#addlistenernoneedupdate) ```typescript addListener(eventName: 'noNeedUpdate', listenerFunc: (state: NoNeedEvent) => void) => Promise ``` Höre auf Events, wenn kein Update benötigt wird. Nützlich, wenn bei jedem App-Start eine Überprüfung erzwungen werden soll | Param | Type | | ------------------ | ------------------------------ | | **`eventName`** | `’noNeedUpdate’` | | **`listenerFunc`** | `(state: NoNeedEvent) => void` | **Returns:** `Promise` **Seit:** 400 *** ## addListener(‘updateAvailable’, ) [Section titled “addListener(‘updateAvailable’, )”](#addlistenerupdateavailable) ```typescript addListener(eventName: 'updateAvailable', listenerFunc: (state: UpdateAvailableEvent) => void) => Promise ``` Höre auf verfügbare Update-Events. Nützlich, wenn bei jedem App-Start eine Überprüfung erzwungen werden soll | Param | Type | | ------------------ | --------------------------------------- | | **`eventName`** | `’updateAvailable’` | | **`listenerFunc`** | `(state: UpdateAvailableEvent) => void` | **Returns:** `Promise` **Seit:** 400 *** ## addListener(‘downloadComplete’, ) [Section titled “addListener(‘downloadComplete’, )”](#addlistenerdownloadcomplete) ```typescript addListener(eventName: 'downloadComplete', listenerFunc: (state: DownloadCompleteEvent) => void) => Promise ``` Höre auf downloadComplete Events | Param | Type | | ------------------ | ---------------------------------------- | | **`eventName`** | `’downloadComplete’` | | **`listenerFunc`** | `(state: DownloadCompleteEvent) => void` | **Returns:** `Promise` **Seit:** 400 *** ## addListener(‘majorAvailable’, ) [Section titled “addListener(‘majorAvailable’, )”](#addlistenermajoravailable) ```typescript addListener(eventName: 'majorAvailable', listenerFunc: (state: MajorAvailableEvent) => void) => Promise ``` Höre auf Major-Update-Events in der App. Informiert dich, wenn ein Major-Update durch die Einstellung disableAutoUpdateBreaking blockiert wird | Param | Type | | ------------------ | -------------------------------------- | | **`eventName`** | `’majorAvailable’` | | **`listenerFunc`** | `(state: MajorAvailableEvent) => void` | **Returns:** `Promise` **Seit:** 230 *** ## addListener(‘updateFailed’, ) [Section titled “addListener(‘updateFailed’, )”](#addlistenerupdatefailed) ```typescript addListener(eventName: 'updateFailed', listenerFunc: (state: UpdateFailedEvent) => void) => Promise ``` Höre auf Update-Fehlschlag-Events in der App. Informiert dich, wenn ein Update beim nächsten App-Start nicht installiert werden konnte | Param | Type | | ------------------ | ------------------------------------ | | **`eventName`** | `’updateFailed’` | | **`listenerFunc`** | `(state: UpdateFailedEvent) => void` | **Returns:** `Promise` **Seit:** 230 *** ## addListener(‘downloadFailed’, [Section titled “addListener(‘downloadFailed’,”](#addlistenerdownloadfailed) ```typescript addListener(eventName: 'downloadFailed', listenerFunc: (state: DownloadFailedEvent) => void) => Promise ``` Lauschen Sie auf das Download-Fehlerereignis in der App, das Sie darüber informiert, wenn ein Bundle-Download fehlgeschlagen ist | Param | Typ | | ------------------ | -------------------------------------- | | **`eventName`** | `’downloadFailed’` | | **`listenerFunc`** | `(state: DownloadFailedEvent) => void` | **Gibt zurück:** `Promise` **Seit:** 400 *** ## addListener(‘appReloaded’, ) [Section titled “addListener(‘appReloaded’, )”](#addlistenerappreloaded) ```typescript addListener(eventName: 'appReloaded', listenerFunc: () => void) => Promise ``` Lauschen Sie auf das Neuladen-Ereignis in der App, das Sie darüber informiert, wenn ein Neuladen stattgefunden hat | Param | Typ | | ------------------ | --------------- | | **`eventName`** | `’appReloaded’` | | **`listenerFunc`** | `() => void` | **Gibt zurück:** `Promise` **Seit:** 430 *** ## addListener(‘appReady’, ) [Section titled “addListener(‘appReady’, )”](#addlistenerappready) ```typescript addListener(eventName: 'appReady', listenerFunc: (state: AppReadyEvent) => void) => Promise ``` Lauschen Sie auf das App-Bereit-Ereignis in der App, das Sie darüber informiert, wenn die App einsatzbereit ist | Param | Typ | | ------------------ | -------------------------------- | | **`eventName`** | `’appReady’` | | **`listenerFunc`** | `(state: AppReadyEvent) => void` | **Gibt zurück:** `Promise` **Seit:** 510 *** ## isAutoUpdateAvailable() [Section titled “isAutoUpdateAvailable()”](#isautoupdateavailable) ```typescript isAutoUpdateAvailable() => Promise ``` Prüfen Sie, ob automatische Aktualisierung verfügbar ist (nicht durch serverUrl deaktiviert) **Gibt zurück:** `Promise` *** ## getNextBundle() [Section titled “getNextBundle()”](#getnextbundle) ```typescript getNextBundle() => Promise ``` Ruft das nächste Bundle ab, das beim Neuladen der App verwendet wird Gibt null zurück, wenn kein nächstes Bundle festgelegt ist **Gibt zurück:** `Promise` **Seit:** 680 *** ## Interfaces [Section titled “Interfaces”](#interfaces) ### AppReadyResult [Section titled “AppReadyResult”](#appreadyresult) | Eigenschaft | Typ | | ------------ | ------------ | | **`bundle`** | `BundleInfo` | ### BundleInfo [Section titled “BundleInfo”](#bundleinfo) | Eigenschaft | Typ | | ---------------- | -------------- | | **`id`** | `string` | | **`version`** | `string` | | **`downloaded`** | `string` | | **`checksum`** | `string` | | **`status`** | `BundleStatus` | ### UpdateUrl [Section titled “UpdateUrl”](#updateurl) | Eigenschaft | Typ | | ----------- | -------- | | **`url`** | `string` | ### StatsUrl [Section titled “StatsUrl”](#statsurl) | Eigenschaft | Typ | | ----------- | -------- | | **`url`** | `string` | ### ChannelUrl [Section titled “ChannelUrl”](#channelurl) | Eigenschaft | Typ | | ----------- | -------- | | **`url`** | `string` | ### DownloadOptions [Section titled “DownloadOptions”](#downloadoptions) | Eigenschaft | Typ | Beschreibung | Standard | Seit | | ---------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | ---- | | **`url`** | `string` | Die URL der Bundle-ZIP-Datei (z.B. distzip), die heruntergeladen werden soll (Dies kann eine beliebige URL sein, z.B. Amazon S3, ein GitHub-Tag oder ein anderer Ort, an dem Sie Ihr Bundle gehostet haben) | | | | **`version`** | `string` | Der Versions-Code/Name dieses Bundles/dieser Version | | | | **`sessionKey`** | `string` | Der Sitzungsschlüssel für das Update | `undefined` | 400 | | **`checksum`** | `string` | Die Prüfsumme für das Update | `undefined` | 400 | ### BundleId [Section titled “BundleId”](#bundleid) | Eigenschaft | Typ | | ----------- | -------- | | **`id`** | `string` | ### BundleListResult [Section titled “BundleListResult”](#bundlelistresult) | Eigenschaft | Typ | | ------------- | -------------- | | **`bundles`** | `BundleInfo[]` | ### ListOptions [Section titled “ListOptions”](#listoptions) | Eigenschaft | Typ | Beschreibung | Standard | Seit | | ----------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | ---- | | **`raw`** | `boolean` | Ob die rohe Bundle-Liste oder das Manifest zurückgegeben werden soll. Wenn true, wird versucht, die interne Datenbank anstelle von Dateien auf der Festplatte zu lesen | `false` | 6140 | ### ResetOptions [Section titled “ResetOptions”](#resetoptions) | Eigenschaft | Typ | | ---------------------- | --------- | | **`toLastSuccessful`** | `boolean` | ### CurrentBundleResult [Section titled “CurrentBundleResult”](#currentbundleresult) | Eigenschaft | Typ | | ------------ | ------------ | | **`bundle`** | `BundleInfo` | | **`native`** | `string` | ### MultiDelayConditions [Section titled “MultiDelayConditions”](#multidelayconditions) | Eigenschaft | Typ | | --------------------- | ------------------ | | **`delayConditions`** | `DelayCondition[]` | ### DelayCondition [Section titled “DelayCondition”](#delaycondition) | Eigenschaft | Typ | Beschreibung | | ----------- | ---------------- | --------------------------------------------------- | | **`kind`** | `DelayUntilNext` | Verzögerungsbedingungen in setMultiDelay einrichten | | **`value`** | `string` | | ### LatestVersion [Section titled “LatestVersion”](#latestversion) | Eigenschaft | Typ | Beschreibung | Seit | | ---------------- | ----------------- | ------------------------------ | ---- | | **`version`** | `string` | Ergebnis der getLatest-Methode | 400 | | **`checksum`** | `string` | | 6 | | **`major`** | `boolean` | | | | **`message`** | `string` | | | | **`sessionKey`** | `string` | | | | **`error`** | `string` | | | | **`old`** | `string` | | | | **`url`** | `string` | | | | **`manifest`** | `ManifestEntry[]` | | 61 | ### ManifestEntry [Section titled “ManifestEntry”](#manifestentry) | Eigenschaft | Typ | | ------------------ | ---------------- | | **`file_name`** | `string \| null` | | **`file_hash`** | `string \| null` | | **`download_url`** | `string \| null` | ### GetLatestOptions [Section titled “GetLatestOptions”](#getlatestoptions) | Eigenschaft | Typ | Beschreibung | Standard | Seit | | ------------- | -------- | --------------------------------------------------------------------------------------------------------------------------- | ----------- | ---- | | **`channel`** | `string` | Der Kanal, für den die neueste Version abgerufen werden soll. Der Kanal muss ‘self\_assign’ für die Funktionalität erlauben | `undefined` | 680 | ### ChannelRes [Section titled “ChannelRes”](#channelres) | Eigenschaft | Typ | Beschreibung | Seit | | ------------- | -------- | --------------------------- | ---- | | **`status`** | `string` | Aktueller Status des Kanals | 470 | | **`error`** | `string` | | | | **`message`** | `string` | | | ### SetChannelOptions [Section titled “SetChannelOptions”](#setchanneloptions) | Eigenschaft | Typ | | ----------------------- | --------- | | **`channel`** | `string` | | **`triggerAutoUpdate`** | `boolean` | ### UnsetChannelOptions [Section titled “UnsetChannelOptions”](#unsetchanneloptions) | Eigenschaft | Typ | | ----------------------- | --------- | | **`triggerAutoUpdate`** | `boolean` | ### GetChannelRes [Section titled “GetChannelRes”](#getchannelres) | Eigenschaft | Typ | Beschreibung | Seit | | -------------- | --------- | --------------------------- | ---- | | **`channel`** | `string` | Aktueller Status des Kanals | 480 | | **`error`** | `string` | | | | **`message`** | `string` | | | | **`status`** | `string` | | | | **`allowSet`** | `boolean` | | | ### SetCustomIdOptions [Section titled “SetCustomIdOptions”](#setcustomidoptions) | Eigenschaft | Typ | | -------------- | -------- | | **`customId`** | `string` | ### BuiltinVersion [Section titled “BuiltinVersion”](#builtinversion) | Eigenschaft | Typ | | ------------- | -------- | | **`version`** | `string` | ### DeviceId [Section titled “DeviceId”](#deviceid) | Eigenschaft | Typ | | -------------- | -------- | | **`deviceId`** | `string` | ### PluginVersion [Section titled “PluginVersion”](#pluginversion) | Eigenschaft | Typ | | ------------- | -------- | | **`version`** | `string` | ### AutoUpdateEnabled [Section titled “AutoUpdateEnabled”](#autoupdateenabled) | Eigenschaft | Typ | | ------------- | --------- | | **`enabled`** | `boolean` | ### PluginListenerHandle [Section titled “PluginListenerHandle”](#pluginlistenerhandle) | Eigenschaft | Typ | | ------------ | --------------------- | | **`remove`** | `() => Promise` | ### DownloadEvent [Section titled “DownloadEvent”](#downloadevent) | Eigenschaft | Typ | Beschreibung | Seit | | ------------- | ------------ | --------------------------------------------- | ---- | | **`percent`** | `number` | Aktueller Download-Status, zwischen 0 und 100 | 400 | | **`bundle`** | `BundleInfo` | | | ### NoNeedEvent [Section titled “NoNeedEvent”](#noneedevent) | Eigenschaft | Typ | Beschreibung | Seit | | ------------ | ------------ | --------------------------------------------- | ---- | | **`bundle`** | `BundleInfo` | Aktueller Download-Status, zwischen 0 und 100 | 400 | ### UpdateAvailableEvent [Section titled “UpdateAvailableEvent”](#updateavailableevent) | Eigenschaft | Typ | Beschreibung | Seit | | ------------ | ------------ | --------------------------------------------- | ---- | | **`bundle`** | `BundleInfo` | Aktueller Download-Status, zwischen 0 und 100 | 400 | ### DownloadCompleteEvent [Section titled “DownloadCompleteEvent”](#downloadcompleteevent) | Eigenschaft | Typ | Beschreibung | Seit | | ------------ | ------------ | --------------------------------------------- | ---- | | **`bundle`** | `BundleInfo` | Wird ausgelöst, wenn ein Update verfügbar ist | 400 | ### MajorAvailableEvent [Section titled “MajorAvailableEvent”](#majoravailableevent) | Eigenschaft | Typ | Beschreibung | Seit | | ------------- | -------- | --------------------------------------------------- | ---- | | **`version`** | `string` | Wird ausgelöst, wenn ein Major-Update verfügbar ist | 400 | ### UpdateFailedEvent [Section titled “UpdateFailedEvent”](#updatefailedevent) | Eigenschaft | Typ | Beschreibung | Seit | | ------------ | ------------ | -------------------------------------------------- | ---- | | **`bundle`** | `BundleInfo` | Wird ausgelöst, wenn ein Update fehlgeschlagen ist | 400 | ### DownloadFailedEvent [Section titled “DownloadFailedEvent”](#downloadfailedevent) | Eigenschaft | Typ | Beschreibung | Seit | | ------------- | -------- | --------------------------------------------- | ---- | | **`version`** | `string` | Wird ausgelöst, wenn ein Download fehlschlägt | 400 | ### AppReadyEvent [Section titled “AppReadyEvent”](#appreadyevent) | Eigenschaft | Typ | Beschreibung | Seit | | ------------ | ------------ | --------------------------------------- | ---- | | **`bundle`** | `BundleInfo` | Wird ausgelöst, wenn die App bereit ist | 520 | | **`status`** | `string` | | | ### AutoUpdateAvailable [Section titled “AutoUpdateAvailable”](#autoupdateavailable) | Eigenschaft | Typ | | --------------- | --------- | | **`available`** | `boolean` | ## Type Aliases [Section titled “Type Aliases”](#type-aliases) ### BundleStatus [Section titled “BundleStatus”](#bundlestatus) `‘success’ | ‘error’ | ‘pending’ | ‘downloading’` ### DelayUntilNext [Section titled “DelayUntilNext”](#delayuntilnext) `‘background’ | ‘kill’ | ‘nativeVersion’ | ‘date’` # Capacitor Plugins von Capgo > Entdecken Sie unsere umfassende Sammlung von Capacitor-Plugins, um die nativen Funktionen Ihrer App mit leistungsstarken Features zu erweitern. Willkommen bei der Capgo Capacitor Plugins-Sammlung! Wir pflegen eine Suite hochwertiger, gut dokumentierter Plugins, um Ihnen zu helfen, erstaunliche native Erlebnisse in Ihren Capacitor-Apps zu erstellen. ## Capgo Cloud - Live-Updates [Section titled “Capgo Cloud - Live-Updates”](#capgo-cloud---live-updates) [Capacitor Updater ](/docs/plugins/updater/)Das Kern-Plugin, das Capgo Cloud antreibt - liefern Sie sofortige Updates an Ihre Capacitor-Apps ohne Verzögerungen im App Store. Pushen Sie Fixes und Features direkt an Ihre Benutzer. Das Updater-Plugin ist die Grundlage von Capgo Cloud und ermöglicht Ihnen: * Aktualisierungen sofort ohne App Store Reviews bereitstellen * JavaScript, HTML, CSS und Assets Ihrer App aktualisieren * Spezifische Benutzersegmente mit Kanälen ansprechen * Update-Erfolg mit integrierten Analysen überwachen * Updates mit Verschlüsselung und Code-Signierung sichern ## Ausgewählte Plugins [Section titled “Ausgewählte Plugins”](#ausgewählte-plugins) [Social Login ](/docs/plugins/social-login/)Nahtlose Authentifizierung mit Google, Apple und Facebook. Ein Plugin für alle großen Social-Anbieter. [Native Purchases ](/docs/plugins/capacitor-native-purchases/)Implementieren Sie In-App-Käufe und Abonnements mit einer einheitlichen API für iOS und Android. [Camera Preview ](/docs/plugins/capacitor-camera-preview/)Zeigen Sie Echtzeit-Kamera-Feed in Ihrer App mit voller Kontrolle über Aufnahme und Einstellungen an. [Data Storage SQLite ](/docs/plugins/data-storage-sqlite/)Schneller und sicherer Key-Value-Speicher, angetrieben von SQLite mit optionaler Verschlüsselung. ## Geräte- und System-Plugins [Section titled “Geräte- und System-Plugins”](#geräte--und-system-plugins) [Native Market ](/docs/plugins/capacitor-native-market/)Verlinken Sie zu App Stores für Bewertungen, Reviews und App-Updates. [Native Biometric ](/docs/plugins/native-biometric/)Zugriff auf biometrische Authentifizierungs-APIs für sicheren App-Zugang. [Shake ](/docs/plugins/shake/)Erkennen Sie Schüttelgesten für interaktive Features und Debug-Menüs. [Navigation Bar ](/docs/plugins/navigation-bar/)Passen Sie die Farbe der Android-Navigationsleiste an Ihr App-Theme an. [Home Indicator ](/docs/plugins/capacitor-home-indicator/)Steuern Sie die Sichtbarkeit des iOS-Home-Indikators für immersive Erlebnisse. [NFC ](/docs/plugins/nfc/)Lesen und schreiben Sie NFC-Tags mit nativer Unterstützung für iOS und Android. [Barometer ](/docs/plugins/barometer/)Greifen Sie auf das Gerätebarometer für atmosphärische Druck- und Höhenmessungen zu. [Accelerometer ](/docs/plugins/accelerometer/)Lesen Sie das Gerätebeschleunigungsmesser für Bewegungserkennung und Orientierungsverfolgung. ## Medien- und Kamera-Plugins [Section titled “Medien- und Kamera-Plugins”](#medien--und-kamera-plugins) [Screen Recorder ](/docs/plugins/screen-recorder/)Nehmen Sie Ihren Gerätebildschirm für Tutorials, Demos oder Benutzerfeedback auf. [IVS Player ](/docs/plugins/capacitor-ivs-player/)Amazon IVS-Integration für ultra-niedrige Latenz Live-Streaming. [Native Audio ](/docs/plugins/capacitor-native-audio/)Hochleistungs-Native-Audio-Engine für Spiele und Apps. [Mute ](/docs/plugins/mute/)Erkennen Sie, ob der Gerätestummschalter aktiviert oder deaktiviert ist. [Video Player ](/docs/plugins/video-player/)Native Videowiedergabe mit Untertiteln, Vollbild und umfassenden Steuerelementen. [YouTube Player ](/docs/plugins/youtube-player/)Betten Sie YouTube-Videos mit voller Player-API-Steuerung und Ereignisbehandlung ein. [FFmpeg ](/docs/plugins/ffmpeg/)Videokodierung und -verarbeitung mit FFmpeg für Komprimierung und Konvertierung. [Volume Buttons ](/docs/plugins/volume-buttons/)Hören Sie auf Hardware-Lautstärketasten-Drücke für benutzerdefinierte Steuerelemente. [Audio Recorder ](/docs/plugins/audio-recorder/)Nehmen Sie Audio auf iOS, Android und Web mit einfachen Steuerelementen auf. ## Utility-Plugins [Section titled “Utility-Plugins”](#utility-plugins) [Uploader ](/docs/plugins/uploader/)Hintergrund-Datei-Upload mit Fortschrittsverfolgung und fortsetzbaren Uploads. [Flash ](/docs/plugins/flash/)Steuern Sie die Geräte-Taschenlampe/Torch für Utility- und Kamera-Features. [Native Geocoder ](/docs/plugins/nativegeocoder/)Vorwärts- und Rückwärts-Geocodierung mit nativen Plattform-APIs. [InAppBrowser ](/docs/plugins/inappbrowser/)Öffnen Sie Webinhalte in einem anpassbaren In-App-Browser. [Crisp ](/docs/plugins/crisp/)Integrieren Sie Crisp-Chat-Support direkt in Ihre mobile App. [Live Reload ](/docs/plugins/live-reload/)Verbinden Sie sich mit Ihrem Dev-Server für sofortiges Hot-Reloading während der Entwicklung. [Contacts ](/docs/plugins/contacts/)Greifen Sie auf Gerätekontakte mit Lese- und Schreibfunktionen zu und verwalten Sie sie. ## KI und erweiterte Medien [Section titled “KI und erweiterte Medien”](#ki-und-erweiterte-medien) [LLM ](/docs/plugins/llm/)Führen Sie Large Language Models lokal mit Apple Intelligence-Unterstützung aus. [StreamCall ](/docs/plugins/streamcall/)Aktivieren Sie hochwertige Video-Streaming- und Anruffunktionen. [JW Player ](/docs/plugins/jw-player/)Professioneller Videoplayer mit erweiterten Streaming-Funktionen. [Ricoh 360 Camera ](/docs/plugins/ricoh360-camera/)Steuern und erfassen Sie von Ricoh 360-Grad-Kameras. ## Standort und Hintergrunddienste [Section titled “Standort und Hintergrunddienste”](#standort-und-hintergrunddienste) [Background Geolocation ](/docs/plugins/background-geolocation/)Verfolgen Sie den Gerätestandort im Hintergrund mit Batterieoptimierung. [Alarm ](/docs/plugins/alarm/)Planen Sie native Alarme und Benachrichtigungen auch wenn die App geschlossen ist. [iBeacon ](/docs/plugins/ibeacon/)Näherungserkennung und Beacon-Regionsüberwachung für standortbasierte Features. ## Kommunikation und Analysen [Section titled “Kommunikation und Analysen”](#kommunikation-und-analysen) [Twilio Voice ](/docs/plugins/twilio-voice/)Tätigen und empfangen Sie Telefonanrufe mit Twilio Voice API. [GTM ](/docs/plugins/gtm/)Google Tag Manager-Integration für Analysen und Tracking. [AppInsights ](/docs/plugins/appinsights/)Anwendungseinblicke und Analysen mit dem Apptopia AppInsights SDK. [WeChat ](/docs/plugins/wechat/)WeChat SDK-Integration für Authentifizierung, Teilen, Zahlungen und Mini-Programme. ## Sicherheit und System [Section titled “Sicherheit und System”](#sicherheit-und-system) [Is Root ](/docs/plugins/is-root/)Erkennen Sie, ob das Gerät gerootet oder jailbroken ist, um die Sicherheit zu gewährleisten. [Persistent Account ](/docs/plugins/persistent-account/)Behalten Sie Benutzerkonten über App-Neuinstallationen hinweg bei. [Autofill Save Password ](/docs/plugins/autofill-save-password/)Aktivieren Sie Passwort-Autofill und Speicherfunktionalität. ## Android-spezifische Features [Section titled “Android-spezifische Features”](#android-spezifische-features) [Android Kiosk ](/docs/plugins/android-kiosk/)Sperren Sie Android-Geräte in den Kiosk-Modus mit Launcher-Funktionalität und Hardware-Tastensteuerung. [Android Usage Stats ](/docs/plugins/android-usagestatsmanager/)Greifen Sie auf Android-App-Nutzungsstatistiken und Bildschirmzeitdaten zu. [Android Inline Install ](/docs/plugins/android-inline-install/)Aktivieren Sie Inline-App-Installation auf Android-Geräten. [Age Signals ](/docs/plugins/age-signals/)Greifen Sie auf Google Play Age Signals zu, um überwachte Konten und verifizierte Benutzer zu erkennen. ## Download und Navigation [Section titled “Download und Navigation”](#download-und-navigation) [Downloader ](/docs/plugins/downloader/)Hintergrund-Datei-Downloads mit Fortschrittsverfolgung. [Launch Navigator ](/docs/plugins/launch-navigator/)Starten Sie Navigations-Apps mit Adressen und Koordinaten. ## Warum Capgo Plugins wählen? [Section titled “Warum Capgo Plugins wählen?”](#warum-capgo-plugins-wählen) **Gut gepflegt**: Regelmäßige Updates und Kompatibilität mit den neuesten Capacitor-Versionen **Umfassende Dokumentation**: Detaillierte Dokumentation mit Beispielen für jedes Plugin **Einfache Integration**: Einfache APIs, die den Bewährte Methoden von Capacitor folgen **TypeScript-Unterstützung**: Vollständige TypeScript-Definitionen für bessere Entwicklungserfahrung ## Erste Schritte [Section titled “Erste Schritte”](#erste-schritte) Jedes Plugin folgt einem ähnlichen Installationsmuster: 1. **Installieren Sie das Plugin** mit Ihrem bevorzugten Paketmanager 2. **Synchronisieren Sie Ihr Projekt** mit `npx cap sync` 3. **Konfigurieren Sie plattformspezifische Einstellungen**, falls erforderlich 4. **Beginnen Sie mit der Verwendung des Plugins** in Ihrem Code Besuchen Sie die Dokumentation jedes Plugins für detaillierte Installationsanweisungen und API-Referenzen. ## Benötigen Sie Hilfe? [Section titled “Benötigen Sie Hilfe?”](#benötigen-sie-hilfe) * Sehen Sie sich die individuelle Plugin-Dokumentation an * Treten Sie unserer [Discord-Community](https://discord.capgo.app) bei * Melden Sie Probleme im GitHub-Repository des Plugins * Kontaktieren Sie den Support für Unternehmensanfragen ## Benötigen Sie ein individuelles Plugin? [Section titled “Benötigen Sie ein individuelles Plugin?”](#benötigen-sie-ein-individuelles-plugin) Können Sie die genaue Funktionalität, die Sie benötigen, nicht finden? Wir können helfen! [Individuelle Plugin-Entwicklung ](/consulting/)Unser Team ist spezialisiert auf die Entwicklung individueller Capacitor-Plugins, die auf Ihre spezifischen Anforderungen zugeschnitten sind. Von komplexen nativen Integrationen bis hin zu einzigartigen Gerätefunktionen - wir haben alles für Sie. Egal ob Sie benötigen: * Ein völlig neues Plugin von Grund auf erstellt * Modifikationen an bestehenden Plugins * Expertenberatung zur nativen Entwicklung * Plattformspezifische Implementierungen [Nehmen Sie Kontakt mit unserem Team auf](/consulting/), um Ihre individuellen Plugin-Bedürfnisse zu besprechen. ## Mitwirken [Section titled “Mitwirken”](#mitwirken) Wir begrüßen Beiträge! Jedes Plugin ist Open Source und auf GitHub verfügbar. Fühlen Sie sich frei: * Fehler zu melden und Features anzufordern * Pull Requests einzureichen * Dokumentation zu verbessern * Ihre Anwendungsfälle zu teilen *** **Entwickelt mit Herz vom Capgo-Team** # @capgo/capacitor-accelerometer > Zugriff auf Gerätebewegung und Beschleunigungsdaten von Beschleunigungssensoren 3-Achsen-Beschleunigung Messung von Beschleunigungskräften in X-, Y- und Z-Achsen Bewegungserkennung Erkennung von Geräteschütteln, Neigungen und Bewegungen Echtzeit-Updates Kontinuierliche Überwachung von Beschleunigungsdaten Umfassende Dokumentation Sehen Sie sich die [Dokumentation](/docs/plugins/accelerometer/getting-started/) an, um das Plugin in nur wenigen Minuten zu meistern. # Erste Schritte mit Accelerometer > Erfahren Sie, wie Sie Beschleunigungssensor-Erfassung in Ihre Capacitor-App integrieren Dieser Leitfaden führt Sie durch die Integration des Capacitor Accelerometer Plugins in Ihre Anwendung. ## Installation [Section titled “Installation”](#installation) Installieren Sie das Plugin mit npm: ```bash npm install @capgo/capacitor-accelerometer npx cap sync ``` ## Plattformkonfiguration [Section titled “Plattformkonfiguration”](#plattformkonfiguration) ### iOS [Section titled “iOS”](#ios) Keine zusätzliche Konfiguration erforderlich. Der Beschleunigungssensor ist immer verfügbar. ### Android [Section titled “Android”](#android) Keine zusätzliche Konfiguration erforderlich. Der Beschleunigungssensor ist immer verfügbar. ### Web [Section titled “Web”](#web) Das Plugin verwendet die DeviceMotion API. Erfordert HTTPS in der Produktion. ## Grundlegende Verwendung [Section titled “Grundlegende Verwendung”](#grundlegende-verwendung) ### Plugin importieren [Section titled “Plugin importieren”](#plugin-importieren) ```typescript import { Accelerometer } from '@capgo/capacitor-accelerometer'; ``` ### Überwachung starten [Section titled “Überwachung starten”](#überwachung-starten) ```typescript const startAccelerometer = async () => { await Accelerometer.start({ interval: 100 // Update-Intervall in Millisekunden }); console.log('Accelerometer started'); }; ``` ### Beschleunigungsereignisse abhören [Section titled “Beschleunigungsereignisse abhören”](#beschleunigungsereignisse-abhören) ```typescript Accelerometer.addListener('accelerationChange', (data) => { console.log('X:', data.x); console.log('Y:', data.y); console.log('Z:', data.z); console.log('Timestamp:', data.timestamp); }); ``` ### Aktuelle Messung abrufen [Section titled “Aktuelle Messung abrufen”](#aktuelle-messung-abrufen) ```typescript const getCurrentAcceleration = async () => { const reading = await Accelerometer.getCurrentAcceleration(); console.log('Current acceleration:', reading); }; ``` ### Überwachung stoppen [Section titled “Überwachung stoppen”](#überwachung-stoppen) ```typescript const stopAccelerometer = async () => { await Accelerometer.stop(); console.log('Accelerometer stopped'); }; ``` ## Vollständiges Beispiel [Section titled “Vollständiges Beispiel”](#vollständiges-beispiel) Hier ist ein vollständiges Beispiel mit Schüttel-Erkennung: ```typescript import { Accelerometer } from '@capgo/capacitor-accelerometer'; class AccelerometerService { private listener: any; private lastX = 0; private lastY = 0; private lastZ = 0; private shakeThreshold = 15; async initialize() { await Accelerometer.start({ interval: 100 }); this.listener = Accelerometer.addListener('accelerationChange', (data) => { this.handleAcceleration(data); }); } handleAcceleration(data: any) { // Delta berechnen const deltaX = Math.abs(data.x - this.lastX); const deltaY = Math.abs(data.y - this.lastY); const deltaZ = Math.abs(data.z - this.lastZ); // Auf Schütteln prüfen if (deltaX > this.shakeThreshold || deltaY > this.shakeThreshold || deltaZ > this.shakeThreshold) { this.onShake(); } // Letzte Werte aktualisieren this.lastX = data.x; this.lastY = data.y; this.lastZ = data.z; // UI aktualisieren this.updateDisplay(data); } onShake() { console.log('Device shaken!'); // Schüttel-Aktion auslösen } updateDisplay(data: any) { console.log(`X: ${data.x.toFixed(2)} m/s²`); console.log(`Y: ${data.y.toFixed(2)} m/s²`); console.log(`Z: ${data.z.toFixed(2)} m/s²`); // Magnitude berechnen const magnitude = Math.sqrt( data.x * data.x + data.y * data.y + data.z * data.z ); console.log(`Magnitude: ${magnitude.toFixed(2)} m/s²`); } async cleanup() { if (this.listener) { this.listener.remove(); } await Accelerometer.stop(); } } // Verwendung const accelService = new AccelerometerService(); accelService.initialize(); // Aufräumen wenn fertig // accelService.cleanup(); ``` ## Messwerte verstehen [Section titled “Messwerte verstehen”](#messwerte-verstehen) ### Beschleunigungsachsen [Section titled “Beschleunigungsachsen”](#beschleunigungsachsen) * **X-Achse**: Links (-) bis Rechts (+) * **Y-Achse**: Unten (-) bis Oben (+) * **Z-Achse**: Hinten (-) bis Vorne (+) ### Schwerkraft [Section titled “Schwerkraft”](#schwerkraft) * Gerät in Ruhe zeigt \~9,8 m/s² auf einer Achse (Schwerkraft) * Bewegtes Gerät zeigt Beschleunigung zusätzlich zur Schwerkraft ### Einheiten [Section titled “Einheiten”](#einheiten) * Gemessen in Meter pro Quadratsekunde (m/s²) * Schwerkraft = 9,8 m/s² ## Häufige Anwendungsfälle [Section titled “Häufige Anwendungsfälle”](#häufige-anwendungsfälle) ### Schüttel-Erkennung [Section titled “Schüttel-Erkennung”](#schüttel-erkennung) ```typescript class ShakeDetector { private lastUpdate = 0; private lastX = 0; private lastY = 0; private lastZ = 0; detectShake(x: number, y: number, z: number): boolean { const currentTime = Date.now(); if (currentTime - this.lastUpdate > 100) { const deltaX = Math.abs(x - this.lastX); const deltaY = Math.abs(y - this.lastY); const deltaZ = Math.abs(z - this.lastZ); this.lastUpdate = currentTime; this.lastX = x; this.lastY = y; this.lastZ = z; return deltaX + deltaY + deltaZ > 15; } return false; } } ``` ### Neigungs-Erkennung [Section titled “Neigungs-Erkennung”](#neigungs-erkennung) ```typescript class TiltDetector { getTiltAngles(x: number, y: number, z: number) { const roll = Math.atan2(y, z) * (180 / Math.PI); const pitch = Math.atan2(-x, Math.sqrt(y * y + z * z)) * (180 / Math.PI); return { roll, pitch }; } isDeviceFlat(z: number): boolean { return Math.abs(z - 9.8) < 1.0; } isDeviceUpright(y: number): boolean { return Math.abs(y - 9.8) < 2.0; } } ``` ### Schrittzähler [Section titled “Schrittzähler”](#schrittzähler) ```typescript class StepCounter { private steps = 0; private lastMagnitude = 0; private threshold = 11; processAcceleration(x: number, y: number, z: number) { const magnitude = Math.sqrt(x * x + y * y + z * z); if (magnitude > this.threshold && this.lastMagnitude < this.threshold) { this.steps++; console.log('Steps:', this.steps); } this.lastMagnitude = magnitude; } } ``` ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) 1. **Geeignete Intervalle wählen**: Balance zwischen Reaktionsfähigkeit und Akkulaufzeit * Gaming: 16-50ms * Fitness: 100-200ms * Allgemein: 200-500ms 2. **Listener entfernen**: Immer aufräumen, wenn fertig 3. **Rauschen filtern**: Gleitende Durchschnitte für glattere Daten verwenden 4. **Akku beachten**: Hochfrequente Abfragen entladen den Akku 5. **Auf echten Geräten testen**: Simulatoren liefern keine genauen Daten ## Leistungstipps [Section titled “Leistungstipps”](#leistungstipps) ### Debouncing [Section titled “Debouncing”](#debouncing) ```typescript class AccelerometerDebouncer { private timeout: any; debounce(callback: Function, delay: number) { return (...args: any[]) => { clearTimeout(this.timeout); this.timeout = setTimeout(() => callback(...args), delay); }; } } ``` ### Datenglättung [Section titled “Datenglättung”](#datenglättung) ```typescript class AccelerometerFilter { private alpha = 0.8; private filteredX = 0; private filteredY = 0; private filteredZ = 0; filter(x: number, y: number, z: number) { this.filteredX = this.alpha * x + (1 - this.alpha) * this.filteredX; this.filteredY = this.alpha * y + (1 - this.alpha) * this.filteredY; this.filteredZ = this.alpha * z + (1 - this.alpha) * this.filteredZ; return { x: this.filteredX, y: this.filteredY, z: this.filteredZ }; } } ``` ## Nächste Schritte [Section titled “Nächste Schritte”](#nächste-schritte) * Erkunden Sie die [API Reference](https://github.com/Cap-go/capacitor-accelerometer#api) für vollständige Methodendokumentation * Sehen Sie sich die [Beispiel-App](https://github.com/Cap-go/capacitor-accelerometer/tree/main/example) für erweiterte Nutzung an * Siehe das [Tutorial](/plugins/capacitor-accelerometer) für vollständige Implementierungsbeispiele # @capgo/capacitor-admob > Monetarisieren Sie Ihre Capacitor-App mit Google AdMob-Bannern, Interstitials und Belohnungsanzeigen, die vollständig über JavaScript verwaltet werden. Das AdMob-Plugin von Capgo bietet eine erstklassige Brücke zum Google Mobile Ads SDK, damit Sie native Erlebnisse monetarisieren können, ohne die Einfachheit von Capacitor zu opfern. Flexible Anzeigenformate Schalten Sie Banner-, Interstitial-, Belohnungs- und App-Open-Anzeigen mit einer einheitlichen API. Anforderungskonfiguration Optimieren Sie Anzeigenanforderungen mit Flags für kinderfreundliche Inhalte, Inhaltsbewertungen und zusätzlichen Netzwerkparametern. Lebenszyklus-Verwaltung Erstellen, laden, anzeigen, ausblenden und zerstören Sie Anzeigen nach Bedarf und verfolgen Sie den Ladezustand. Datenschutzgerecht Verarbeiten Sie ATT-Eingabeaufforderungen und überwachen Sie den Status der Tracking-Autorisierung, um konform zu bleiben. Verwenden Sie den Leitfaden für die ersten Schritte, um Ihre AdMob App-IDs zu konfigurieren, die Einwilligung einzurichten und das Anzeigeninventar direkt aus Ihrem Capacitor-Projekt zu verwalten. # Erste Schritte > Installieren und konfigurieren Sie das Capgo AdMob Plugin, um Anzeigen in Ihrer Capacitor-Anwendung zu schalten. 1. **Plugin installieren** * npm ```sh npm i @capgo/capacitor-admob ``` * pnpm ```sh pnpm add @capgo/capacitor-admob ``` * yarn ```sh yarn add @capgo/capacitor-admob ``` * bun ```sh bun add @capgo/capacitor-admob ``` 2. **Native Projekte synchronisieren** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## SDK initialisieren [Section titled “SDK initialisieren”](#sdk-initialisieren) ```typescript import { AdMob, MaxAdContentRating } from '@capgo/capacitor-admob'; // Starten Sie das Mobile Ads SDK einmal beim Anwendungsstart await AdMob.start(); // Optional: Globale Anforderungseinstellungen konfigurieren await AdMob.configure({ appMuted: false, appVolume: 1, }); await AdMob.configRequest({ maxAdContentRating: MaxAdContentRating.T, tagForChildDirectedTreatment: false, tagForUnderAgeOfConsent: false, }); ``` ## Banner-Anzeige anzeigen [Section titled “Banner-Anzeige anzeigen”](#banner-anzeige-anzeigen) ```typescript import { BannerAd } from '@capgo/capacitor-admob'; const banner = new BannerAd({ adUnitId: 'ca-app-pub-xxxxxxxxxxxxxxxx/banner', position: 'bottom', }); await banner.show(); ``` ## Interstitial- oder Belohnungsanzeigen [Section titled “Interstitial- oder Belohnungsanzeigen”](#interstitial--oder-belohnungsanzeigen) ```typescript import { InterstitialAd, RewardedAd } from '@capgo/capacitor-admob'; const interstitial = new InterstitialAd({ adUnitId: 'ca-app-pub-xxxxxxxxxxxxxxxx/interstitial', }); await interstitial.load(); await interstitial.show(); const rewarded = new RewardedAd({ adUnitId: 'ca-app-pub-xxxxxxxxxxxxxxxx/rewarded', }); await rewarded.load(); await rewarded.show(); ``` ## Auf Anzeigenereignisse lauschen [Section titled “Auf Anzeigenereignisse lauschen”](#auf-anzeigenereignisse-lauschen) ```typescript import { AdMob } from '@capgo/capacitor-admob'; const handle = await AdMob.addListener('adImpression', (event) => { console.log('Ad impression', event); }); // Später beim Aufräumen await handle.remove(); ``` ## Plattformhinweise [Section titled “Plattformhinweise”](#plattformhinweise) * **iOS**: Fügen Sie Ihre AdMob App-ID zu `Info.plist` unter dem Schlüssel `GADApplicationIdentifier` hinzu und fügen Sie alle SKAdNetwork-IDs hinzu, auf die Sie angewiesen sind. * **Android**: Deklarieren Sie Ihre AdMob App-ID in `AndroidManifest.xml`, indem Sie `com.google.android.gms.ads.APPLICATION_ID` innerhalb des ``-Tags hinzufügen. * **Einwilligung & Datenschutz**: Verwenden Sie `requestTrackingAuthorization()` auf iOS 14+ und die `configRequest()` Flags für kinderfreundliche Inhalte, um die regionalen Datenschutzbestimmungen einzuhalten. # @capgo/capacitor-android-age-signals > Greifen Sie auf Google Play Age Signals zu, um überwachte Konten zu erkennen, Benutzeralter zu überprüfen und Kinderschutzbestimmungen in Ihrer Android-App einzuhalten. Überwachte Konten Erkennen Sie, wann Benutzer überwachte Google-Konten haben, die von Erziehungsberechtigten verwaltet werden Altersüberprüfung Identifizieren Sie verifizierte Benutzer, die 18+ sind, mit der Altersüberprüfung von Google Compliance Halten Sie Kinderschutzbestimmungen wie COPPA und GDPR ein Elterngenehmigungen Verfolgen Sie ausstehende Genehmigungen und den Zustimmungsstatus der Erziehungsberechtigten Nur Android Speziell für Android mit Google Play Services entwickelt Einfache API Einzelner Methodenaufruf zum Abrufen aller Alterssignale # Erste Schritte > Erfahren Sie, wie Sie das Age Signals Plugin installieren und verwenden, um überwachte Konten und verifizierte Benutzer in Ihrer Android-App zu erkennen. Caution Dieses Plugin ist **nur für Android** und erfordert Google Play Services. Es funktioniert nicht auf iOS, Web oder Android-Geräten ohne Google Play Store. 1. **Paket installieren** * npm ```sh npm i @capgo/capacitor-android-age-signals ``` * pnpm ```sh pnpm add @capgo/capacitor-android-age-signals ``` * yarn ```sh yarn add @capgo/capacitor-android-age-signals ``` * bun ```sh bun add @capgo/capacitor-android-age-signals ``` 2. **Mit Android-Projekt synchronisieren** * npm ```sh npx cap sync android ``` * pnpm ```sh pnpm cap sync android ``` * yarn ```sh yarn cap sync android ``` * bun ```sh bunx cap sync android ``` ## Voraussetzungen [Section titled “Voraussetzungen”](#voraussetzungen) * Android-Gerät mit Google Play Services * Mindestens Android API Level 21 (Android 5.0) * Google Play Store auf dem Gerät installiert ## Verwendung [Section titled “Verwendung”](#verwendung) ### Alterssignale prüfen [Section titled “Alterssignale prüfen”](#alterssignale-prüfen) ```typescript import { AgeSignals, UserStatus } from '@capgo/capacitor-android-age-signals'; try { const result = await AgeSignals.checkAgeSignals(); console.log('Benutzerstatus:', result.userStatus); switch (result.userStatus) { case UserStatus.Verified: console.log('Benutzer ist 18+ und von Google verifiziert'); // Zugriff auf altersbeschränkte Inhalte erlauben break; case UserStatus.Supervised: console.log(`Überwachter Benutzer im Alter von ${result.ageLower}-${result.ageUpper}`); // Altersgerechte Einschränkungen anwenden break; case UserStatus.SupervisedApprovalPending: console.log('Warten auf Genehmigung des Erziehungsberechtigten'); console.log('Ausstehend seit:', result.mostRecentApprovalDate); // Benutzer informieren, auf Genehmigung des Erziehungsberechtigten zu warten break; case UserStatus.SupervisedApprovalDenied: console.log('Erziehungsberechtigter hat Zugriff verweigert'); console.log('Letzte Genehmigung:', result.mostRecentApprovalDate); // Zugriff blockieren oder alternative Inhalte anzeigen break; case UserStatus.Unknown: console.log('Benutzerstatus unbekannt - zur Verifizierung im Play Store auffordern'); // Benutzer zur Altersverifizierung in Google Play anleiten break; case UserStatus.Empty: console.log('Kein Alterssignal verfügbar'); // Als nicht verifizierter Benutzer behandeln break; } } catch (error) { console.error('Fehler beim Prüfen der Alterssignale:', error); } ``` ### Überwachte Benutzer behandeln [Section titled “Überwachte Benutzer behandeln”](#überwachte-benutzer-behandeln) ```typescript async function handleSupervisedUser() { const result = await AgeSignals.checkAgeSignals(); if (result.userStatus === UserStatus.Supervised) { const age = result.ageLower; if (age < 13) { // COPPA-Einschränkungen anwenden console.log('Benutzer ist unter 13 - COPPA gilt'); disableDataCollection(); disableSocialFeatures(); requireParentalConsent(); } else if (age < 18) { // Einschränkungen für Teenager anwenden console.log('Benutzer ist 13-17 - Einschränkungen für Teenager gelten'); enableModeratedSocialFeatures(); restrictAds(); } // Installations-ID für Widerrufbenachrichtigungen verfolgen console.log('Installations-ID:', result.installId); saveInstallId(result.installId); } } ``` ### Alterssperre implementieren [Section titled “Alterssperre implementieren”](#alterssperre-implementieren) ```typescript async function ageGate() { const result = await AgeSignals.checkAgeSignals(); // Verifizierte 18+ Benutzer erlauben if (result.userStatus === UserStatus.Verified) { return true; } // Alter überwachter Benutzer prüfen if (result.userStatus === UserStatus.Supervised) { return result.ageUpper >= 18; } // Benutzer mit ausstehenden oder abgelehnten Genehmigungen blockieren if ( result.userStatus === UserStatus.SupervisedApprovalPending || result.userStatus === UserStatus.SupervisedApprovalDenied ) { return false; } // Für unbekannt/leer, Fallback-Altersverifizierung implementieren return await showAgeVerificationDialog(); } ``` ### Genehmigungsstatus überwachen [Section titled “Genehmigungsstatus überwachen”](#genehmigungsstatus-überwachen) ```typescript async function checkApprovalStatus() { const result = await AgeSignals.checkAgeSignals(); if (result.userStatus === UserStatus.SupervisedApprovalPending) { console.log('Genehmigung des Erziehungsberechtigten ausstehend'); console.log('Altersbereich:', result.ageLower, '-', result.ageUpper); console.log('Letzte Genehmigung:', result.mostRecentApprovalDate); // Nachricht an Benutzer anzeigen showMessage( 'Ihr Erziehungsberechtigter muss diese App genehmigen. ' + 'Wir haben ihn am ' + result.mostRecentApprovalDate + ' benachrichtigt' ); } else if (result.userStatus === UserStatus.SupervisedApprovalDenied) { console.log('Erziehungsberechtigter hat Genehmigung verweigert'); console.log('Zuletzt genehmigt am:', result.mostRecentApprovalDate); // Blockierte Nachricht anzeigen showMessage( 'Ihr Erziehungsberechtigter hat diese App nicht genehmigt. ' + 'Kontaktieren Sie ihn für weitere Informationen.' ); } } ``` ## API-Referenz [Section titled “API-Referenz”](#api-referenz) ### checkAgeSignals() [Section titled “checkAgeSignals()”](#checkagesignals) Fordern Sie die aktuellen Play Age Signals für den aktiven Benutzer an. ```typescript const result = await AgeSignals.checkAgeSignals(); ``` **Rückgabe:** ```typescript interface CheckAgeSignalsResult { userStatus: UserStatus; ageLower?: number; ageUpper?: number; mostRecentApprovalDate?: string; installId?: string; } ``` ### UserStatus Enum [Section titled “UserStatus Enum”](#userstatus-enum) ```typescript enum UserStatus { Verified = 'VERIFIED', // 18+ verifiziert Supervised = 'SUPERVISED', // Überwachtes Konto SupervisedApprovalPending = 'SUPERVISED_APPROVAL_PENDING', SupervisedApprovalDenied = 'SUPERVISED_APPROVAL_DENIED', Unknown = 'UNKNOWN', // Unbekannter Status Empty = 'EMPTY' // Kein Signal } ``` ## Ergebnisfelder [Section titled “Ergebnisfelder”](#ergebnisfelder) ### userStatus [Section titled “userStatus”](#userstatus) Der Verifizierungsstatus des Benutzers, wie von Google Play gemeldet. * **Verified**: Benutzer ist über 18 und von Google altersverifiziert * **Supervised**: Benutzer hat ein überwachtes Google-Konto * **SupervisedApprovalPending**: Ausstehende Genehmigung des Erziehungsberechtigten für Änderungen * **SupervisedApprovalDenied**: Erziehungsberechtigter hat App-Zugriff verweigert * **Unknown**: Benutzer sollte Status im Play Store verifizieren * **Empty**: Alle anderen Benutzer (Standardzustand) ### ageLower [Section titled “ageLower”](#agelower) Inklusive Untergrenze der Altersspanne des überwachten Benutzers. Nur vorhanden, wenn `userStatus` ist: * `SUPERVISED` * `SUPERVISED_APPROVAL_PENDING` * `SUPERVISED_APPROVAL_DENIED` ### ageUpper [Section titled “ageUpper”](#ageupper) Inklusive Obergrenze der Altersspanne des überwachten Benutzers. Nur vorhanden, wenn: * `userStatus` einer der überwachten Status ist * Das Alter des Benutzers als unter 18 gemeldet wird ### mostRecentApprovalDate [Section titled “mostRecentApprovalDate”](#mostrecentapprovaldate) Datumsstring für die letzte bedeutende Änderung, die eine Genehmigung des Erziehungsberechtigten erhalten hat. Nur vorhanden, wenn `userStatus` ist: * `SUPERVISED_APPROVAL_PENDING` * `SUPERVISED_APPROVAL_DENIED` Format: ISO 8601 Datumsstring ### installId [Section titled “installId”](#installid) Kennung, die überwachten Installationen in Google Play zugewiesen wird. Wird für Widerrufbenachrichtigungen verwendet, wenn der Erziehungsberechtigte die App-Genehmigung widerruft. Nur vorhanden, wenn `userStatus` ist: * `SUPERVISED` * `SUPERVISED_APPROVAL_PENDING` * `SUPERVISED_APPROVAL_DENIED` ## Anwendungsfälle [Section titled “Anwendungsfälle”](#anwendungsfälle) ### 1. COPPA-Compliance [Section titled “1. COPPA-Compliance”](#1-coppa-compliance) ```typescript async function applyCoppaRestrictions() { const result = await AgeSignals.checkAgeSignals(); if (result.userStatus === UserStatus.Supervised && result.ageLower < 13) { // Datenerfassung deaktivieren disableAnalytics(); disableAdvertising(); // Social-Funktionen deaktivieren hideChatFeatures(); disableUserProfiles(); // Überprüfbare elterliche Zustimmung erforderlich await requestParentalConsent(result.installId); } } ``` ### 2. Altersgerechte Inhalte [Section titled “2. Altersgerechte Inhalte”](#2-altersgerechte-inhalte) ```typescript async function filterContent() { const result = await AgeSignals.checkAgeSignals(); let contentRating; if (result.userStatus === UserStatus.Verified) { contentRating = 'MATURE'; } else if (result.userStatus === UserStatus.Supervised) { if (result.ageUpper < 13) { contentRating = 'EVERYONE'; } else if (result.ageUpper < 18) { contentRating = 'TEEN'; } else { contentRating = 'MATURE'; } } else { contentRating = 'TEEN'; // Standard-Sicherheitsbewertung } loadContentForRating(contentRating); } ``` ### 3. Erziehungsberechtigten-Dashboard [Section titled “3. Erziehungsberechtigten-Dashboard”](#3-erziehungsberechtigten-dashboard) ```typescript async function getGuardianInfo() { const result = await AgeSignals.checkAgeSignals(); if ( result.userStatus === UserStatus.Supervised || result.userStatus === UserStatus.SupervisedApprovalPending || result.userStatus === UserStatus.SupervisedApprovalDenied ) { return { isSupervised: true, ageRange: `${result.ageLower}-${result.ageUpper}`, approvalStatus: result.userStatus, lastApprovalDate: result.mostRecentApprovalDate, installId: result.installId, }; } return { isSupervised: false }; } ``` ## Vollständiges Beispiel [Section titled “Vollständiges Beispiel”](#vollständiges-beispiel) ```typescript import { AgeSignals, UserStatus } from '@capgo/capacitor-android-age-signals'; export class AgeVerificationService { async verifyAge(): Promise<{ allowed: boolean; reason: string; restrictions: string[]; }> { try { const result = await AgeSignals.checkAgeSignals(); switch (result.userStatus) { case UserStatus.Verified: return { allowed: true, reason: 'Benutzer ist verifiziert 18+', restrictions: [], }; case UserStatus.Supervised: return this.handleSupervised(result); case UserStatus.SupervisedApprovalPending: return { allowed: false, reason: 'Warten auf Genehmigung des Erziehungsberechtigten', restrictions: ['Genehmigung des Erziehungsberechtigten erforderlich'], }; case UserStatus.SupervisedApprovalDenied: return { allowed: false, reason: 'Erziehungsberechtigter hat Zugriff verweigert', restrictions: ['Zugriff vom Erziehungsberechtigten verweigert'], }; case UserStatus.Unknown: return { allowed: false, reason: 'Altersverifizierung erforderlich', restrictions: ['Alter im Google Play verifizieren'], }; case UserStatus.Empty: default: return { allowed: false, reason: 'Kein Alterssignal verfügbar', restrictions: ['Altersverifizierung erforderlich'], }; } } catch (error) { console.error('Altersverifizierung fehlgeschlagen:', error); return { allowed: false, reason: 'Verifizierungsfehler', restrictions: ['Später erneut versuchen'], }; } } private handleSupervised(result: any) { const age = result.ageLower; const restrictions: string[] = []; if (age < 13) { restrictions.push('Keine Datenerfassung (COPPA)'); restrictions.push('Keine Social-Funktionen'); restrictions.push('Elterliche Zustimmung erforderlich'); return { allowed: false, reason: `Benutzer ist unter 13 (${result.ageLower}-${result.ageUpper})`, restrictions, }; } else if (age < 18) { restrictions.push('Nur altersgerechte Inhalte'); restrictions.push('Moderierte Social-Funktionen'); return { allowed: true, reason: `Teenager-Benutzer (${result.ageLower}-${result.ageUpper})`, restrictions, }; } else { return { allowed: true, reason: `Erwachsener überwachter Benutzer (${result.ageLower}+)`, restrictions: [], }; } } async saveInstallId(installId: string) { // Installations-ID für Widerrufbehandlung speichern localStorage.setItem('ageSignalsInstallId', installId); } async checkRevocation() { const result = await AgeSignals.checkAgeSignals(); const storedId = localStorage.getItem('ageSignalsInstallId'); if (result.installId && storedId && result.installId !== storedId) { // Installations-ID hat sich geändert - wahrscheinlich widerrufen und neu installiert console.log('App wurde widerrufen und neu installiert'); return true; } return false; } } ``` ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) 1. **Beim App-Start prüfen**: Alterssignale beim Start der App überprüfen 2. **Ergebnisse zwischenspeichern**: Ergebnisse zwischenspeichern, aber regelmäßig aktualisieren 3. **Alle Zustände behandeln**: Logik für alle UserStatus-Werte implementieren 4. **Ablehnungen respektieren**: Keinen Zugriff erlauben, wenn Erziehungsberechtigter Genehmigung verweigert 5. **Installations-ID speichern**: Installations-ID für Widerrufserkennung verfolgen 6. **Fallback-Logik**: Fallback-Altersverifizierung für Unknown/Empty-Zustände haben 7. **Datenschutz zuerst**: Keine unnötigen Daten von überwachten Benutzern sammeln ## Compliance-Richtlinien [Section titled “Compliance-Richtlinien”](#compliance-richtlinien) ### COPPA (Children’s Online Privacy Protection Act) [Section titled “COPPA (Children’s Online Privacy Protection Act)”](#coppa-childrens-online-privacy-protection-act) Für Benutzer unter 13: * Überprüfbare elterliche Zustimmung einholen * Datenerfassung begrenzen * Verhaltensbasierte Werbung deaktivieren * Elternkontrollen bereitstellen ### GDPR (General Data Protection Regulation) [Section titled “GDPR (General Data Protection Regulation)”](#gdpr-general-data-protection-regulation) Für überwachte Benutzer: * Daten rechtmäßig verarbeiten * Zustimmungsmechanismen für Erziehungsberechtigte bereitstellen * Datenzugriff und -löschung ermöglichen * Datenschutz durch Design implementieren ## Plattformhinweise [Section titled “Plattformhinweise”](#plattformhinweise) ### Android [Section titled “Android”](#android) * Erfordert Google Play Services * Mindestens API Level 21 (Android 5.0+) * Funktioniert nur auf Geräten mit Play Store * Signale sind möglicherweise nicht in allen Regionen verfügbar * Ergebnisse können sich ändern, wenn Erziehungsberechtigter Einstellungen ändert ### iOS / Web [Section titled “iOS / Web”](#ios--web) * **Nicht unterstützt** - Dies ist ein Android-exklusives Plugin * Wirft einen Fehler, wenn auf nicht unterstützten Plattformen aufgerufen ## Fehlerbehebung [Section titled “Fehlerbehebung”](#fehlerbehebung) ### Kein Signal zurückgegeben (Empty Status) [Section titled “Kein Signal zurückgegeben (Empty Status)”](#kein-signal-zurückgegeben-empty-status) Dies ist normal für: * Benutzer außerhalb unterstützter Regionen * Geräte ohne Google Play Services * Benutzer, die Family Link nicht eingerichtet haben * Neue Konten ohne Verifizierung ### Unknown Status [Section titled “Unknown Status”](#unknown-status) Benutzer sollte: 1. Google Play Store öffnen 2. Zu Einstellungen → Familie gehen 3. Altersverifizierungsprozess abschließen ### Berechtigungsprobleme [Section titled “Berechtigungsprobleme”](#berechtigungsprobleme) Sicherstellen: * Google Play Services ist aktualisiert * App hat korrekten Paketnamen in der Play Console * Test auf echtem Gerät (nicht Emulator ohne Play Services) ## Ressourcen [Section titled “Ressourcen”](#ressourcen) * [Google Play Age Signals Dokumentation](https://developers.google.com/android/reference/com/google/android/gms/agesignals) * [Family Link Entwicklerhandbuch](https://developers.google.com/families) * [COPPA Compliance-Leitfaden](https://www.ftc.gov/business-guidance/resources/complying-coppa-frequently-asked-questions) # @capgo/capacitor-alarm > Verwalten Sie native Alarme auf iOS und Android direkt aus Ihrer Capacitor-App. Native Integration Verwendet natives AlarmKit auf iOS und AlarmClock-Intents auf Android Einfache API Benutzerfreundliche Methoden zum Erstellen und Verwalten von Alarmen Berechtigungsverwaltung Integrierte Unterstützung für die Anforderung notwendiger Berechtigungen Umfassende Dokumentation Schauen Sie sich die [Dokumentation](/docs/plugins/alarm/getting-started/) an, um das Plugin in nur wenigen Minuten zu meistern. # Erste Schritte > Erfahren Sie, wie Sie das Capacitor Alarm Plugin installieren und verwenden, um native Alarme auf iOS und Android zu verwalten. ## Installation [Section titled “Installation”](#installation) * npm ```bash npm install @capgo/capacitor-alarm npx cap sync ``` * yarn ```bash yarn add @capgo/capacitor-alarm npx cap sync ``` * pnpm ```bash pnpm add @capgo/capacitor-alarm npx cap sync ``` * bun ```bash bun add @capgo/capacitor-alarm npx cap sync ``` ## Anforderungen [Section titled “Anforderungen”](#anforderungen) * **iOS**: Nur iOS 26+. Dieses Plugin basiert auf `AlarmKit`-APIs und meldet auf früheren Versionen oder wenn das Framework nicht verfügbar ist “nicht unterstützt”. * **Android**: Verwendet `AlarmClock`-Intents; das Verhalten hängt von der Standard-Uhr-App und den OEM-Richtlinien ab. Hinweis: Dieses Plugin stellt nur native Alarmaktionen (erstellen/öffnen) bereit. Es implementiert keine benutzerdefinierte In-App-Alarmplanung/CRUD. ## API [Section titled “API”](#api) ### createAlarm(…) [Section titled “createAlarm(…)”](#createalarm) ```typescript createAlarm(options: NativeAlarmCreateOptions) => Promise ``` Erstellen Sie einen nativen OS-Alarm mit der Plattform-Uhr-App. Auf Android verwendet dies den Alarm Clock Intent; auf iOS verwendet dies AlarmKit, falls verfügbar (iOS 26+). | Parameter | Typ | | ------------- | -------------------------- | | **`options`** | `NativeAlarmCreateOptions` | **Rückgabe:** `Promise` ### openAlarms() [Section titled “openAlarms()”](#openalarms) ```typescript openAlarms() => Promise ``` Öffnen Sie die native Alarmlisten-UI der Plattform, falls verfügbar. **Rückgabe:** `Promise` ### getOSInfo() [Section titled “getOSInfo()”](#getosinfo) ```typescript getOSInfo() => Promise ``` Informationen über das Betriebssystem und die Funktionen abrufen. **Rückgabe:** `Promise` ### requestPermissions(…) [Section titled “requestPermissions(…)”](#requestpermissions) ```typescript requestPermissions(options?: { exactAlarm?: boolean | undefined; } | undefined) => Promise ``` Relevante Berechtigungen für die Alarmnutzung auf der Plattform anfordern. Auf Android kann dies zu den Einstellungen für exakte Alarme führen. | Parameter | Typ | | ------------- | --------------------------- | | **`options`** | `{ exactAlarm?: boolean; }` | **Rückgabe:** `Promise` ## Schnittstellen [Section titled “Schnittstellen”](#schnittstellen) ### NativeActionResult [Section titled “NativeActionResult”](#nativeactionresult) | Eigenschaft | Typ | | ------------- | --------- | | **`success`** | `boolean` | | **`message`** | `string` | ### NativeAlarmCreateOptions [Section titled “NativeAlarmCreateOptions”](#nativealarmcreateoptions) Optionen zum Erstellen eines nativen OS-Alarms über die Plattform-Uhr-App. | Eigenschaft | Typ | Beschreibung | | ------------- | --------- | -------------------------------------------- | | **`hour`** | `number` | Stunde des Tages im 24-Stunden-Format (0-23) | | **`minute`** | `number` | Minute der Stunde (0-59) | | **`label`** | `string` | Optionale Beschriftung für den Alarm | | **`skipUi`** | `boolean` | Nur Android: UI überspringen, wenn möglich | | **`vibrate`** | `boolean` | Nur Android: Alarm auf Vibration setzen | ### OSInfo [Section titled “OSInfo”](#osinfo) Zurückgegebene Informationen über das aktuelle Betriebssystem und die Funktionen. | Eigenschaft | Typ | Beschreibung | | ------------------------------------ | --------- | --------------------------------------------------------- | | **`platform`** | `string` | ’ios’ \| ‘android’ \| ‘web’ | | **`version`** | `string` | Versionsstring des Betriebssystems | | **`supportsNativeAlarms`** | `boolean` | Ob die Plattform eine native Alarm-App-Integration bietet | | **`supportsScheduledNotifications`** | `boolean` | Ob das Planen lokaler Benachrichtigungen unterstützt wird | | **`canScheduleExactAlarms`** | `boolean` | Nur Android: Ob exakte Alarme erlaubt sind | ### PermissionResult [Section titled “PermissionResult”](#permissionresult) Ergebnis einer Berechtigungsanforderung. | Eigenschaft | Typ | Beschreibung | | ------------- | ------------------------- | --------------------------------------------- | | **`granted`** | `boolean` | Gesamtgenehmigung für angeforderten Bereich | | **`details`** | `Record` | Optionale Details nach Berechtigungsschlüssel | # @capgo/capacitor-android-inline-install > Ermöglichen Sie nahtlose In-App-Installation und Updates für Android-Apps mit Inline-Installationsfunktionen und benutzerfreundlichen Abläufen. ## Übersicht [Section titled “Übersicht”](#übersicht) Das Capacitor Android Inline Install Plugin ermöglicht das Auslösen von Googles Play Inline Install Overlay für Android-Anwendungen. Dieses Plugin bietet eine nahtlose In-App-Installationserfahrung mit Googles Premium Growth Tools, sodass Benutzer Apps installieren können, ohne Ihre Anwendung zu verlassen. Google Play Overlay Natives Google Play Install Overlay auslösen Nahtlose Erfahrung Apps installieren, ohne Ihre App zu verlassen Fallback-Unterstützung Automatischer Fallback zur vollständigen Play Store-Seite Kampagnen-Tracking Unterstützung für Referrer-Tracking und Analytics ## Dokumentation [Section titled “Dokumentation”](#dokumentation) Schauen Sie sich die [vollständige Dokumentation](/docs/plugins/android-inline-install/getting-started/) für detaillierte Implementierungsanleitungen und erweiterte Integrationsmuster an. # Erste Schritte > Installations- und Verwendungsanleitung für @capgo/capacitor-android-inline-install ## Installation [Section titled “Installation”](#installation) * npm ```bash npm install @capgo/capacitor-android-inline-install npx cap sync ``` * yarn ```bash yarn add @capgo/capacitor-android-inline-install npx cap sync ``` * pnpm ```bash pnpm add @capgo/capacitor-android-inline-install npx cap sync ``` * bun ```bash bun add @capgo/capacitor-android-inline-install npx cap sync ``` ## Verwendungsbeispiel [Section titled “Verwendungsbeispiel”](#verwendungsbeispiel) ```typescript import { AndroidInlineInstall } from '@capgo/capacitor-android-inline-install'; // Einfache Inline-Installation await AndroidInlineInstall.startInlineInstall({ id: 'com.example.targetapp' }); // Erweiterte Installation mit Tracking await AndroidInlineInstall.startInlineInstall({ id: 'com.example.targetapp', referrer: 'campaign=my-campaign&source=app', overlay: true, fallback: true }); // Installationsablauf behandeln try { await AndroidInlineInstall.startInlineInstall({ id: 'com.spotify.music', referrer: 'utm_source=myapp&utm_campaign=music_promotion' }); console.log('Install-Overlay erfolgreich ausgelöst'); } catch (error) { console.error('Installation fehlgeschlagen:', error); } ``` ## Kern-API-Methoden [Section titled “Kern-API-Methoden”](#kern-api-methoden) ### Installationsverwaltung [Section titled “Installationsverwaltung”](#installationsverwaltung) * `startInlineInstall(options)` - Google Play Inline Install Overlay für angegebene App auslösen ## Konfigurationsoptionen [Section titled “Konfigurationsoptionen”](#konfigurationsoptionen) ```typescript interface InlineInstallOptions { id: string; // Ziel-App-Paketname (erforderlich) referrer?: string; // Tracking-Kampagnenstring (optional) callerId?: string; // Aufrufer-App-Paketname (Standard: aktuelle App) overlay?: boolean; // Play-Overlay aktivieren/deaktivieren (Standard: true) fallback?: boolean; // Vollständige Store-Seite verwenden, wenn Overlay fehlschlägt (Standard: true) } ``` ## Google Play Anforderungen [Section titled “Google Play Anforderungen”](#google-play-anforderungen) ### Berechtigung für Premium Growth Tools [Section titled “Berechtigung für Premium Growth Tools”](#berechtigung-für-premium-growth-tools) Ihre App muss für Googles Premium Growth Tools qualifiziert sein, um Inline-Installation zu verwenden: * Apps mit signifikantem Benutzerengagement * Gute Play Store-Bewertungen und Rezensionen * Einhaltung der Google Play-Richtlinien ### Anforderungen an die Ziel-App [Section titled “Anforderungen an die Ziel-App”](#anforderungen-an-die-ziel-app) * Ziel-App muss im Google Play Store verfügbar sein * Ziel-App muss Inline-Installation unterstützen * Benutzer muss im Google Play Store angemeldet sein ## Verhalten und Fallbacks [Section titled “Verhalten und Fallbacks”](#verhalten-und-fallbacks) ### Overlay-Modus (Standard) [Section titled “Overlay-Modus (Standard)”](#overlay-modus-standard) 1. Versucht, Google Play Overlay innerhalb Ihrer App zu öffnen 2. Wenn Overlay nicht verfügbar ist, Fallback zur vollständigen Play Store-Seite 3. Wenn Play Store nicht verfügbar ist, Fehler anzeigen ### Vollständiger Store-Modus [Section titled “Vollständiger Store-Modus”](#vollständiger-store-modus) * Öffnet direkt die vollständige Google Play Store-Seite * Umgeht Overlay-Versuch vollständig ## Plattformunterstützung [Section titled “Plattformunterstützung”](#plattformunterstützung) * **Android**: Volle Unterstützung mit Google Play Services * **iOS**: Nicht unterstützt (Android-spezifische Funktion) * **Web**: Nicht unterstützt (native Android-Funktion) ## Implementierungsdetails [Section titled “Implementierungsdetails”](#implementierungsdetails) Das Plugin verwendet Android-Intents zur Kommunikation mit dem Google Play Store: * Intent-Aktion für Inline Install Overlay * Fallback zu Standard-Play Store Intent * Automatische Behandlung der Play Services-Verfügbarkeit ## Anwendungsfälle [Section titled “Anwendungsfälle”](#anwendungsfälle) * **App-Entdeckung**: Verwandte Apps in Ihrem Ökosystem bewerben * **Cross-Promotion**: Installationen für Partner-Anwendungen fördern * **Funktionsfreischaltung**: Zusätzliche Module oder Erweiterungen installieren * **Begleit-Apps**: Unterstützende Anwendungen nahtlos installieren ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) * Immer Fallback-Optionen für Benutzer ohne Play Services bereitstellen * Mit verschiedenen Gerätekonfigurationen und Play Store-Versionen testen * Referrer-Tracking verwenden, um Konversionsraten zu messen * Installationsfehler angemessen behandeln * Benutzerwahl respektieren, wenn sie die Installation ablehnen ## Einschränkungen [Section titled “Einschränkungen”](#einschränkungen) * Nur Android-Funktionalität * Erfordert Google Play Services * Funktioniert nur mit Apps, die für Premium Growth Tools qualifiziert sind * Unterliegt den Google Play Store-Richtlinien und der Verfügbarkeit # @capgo/capacitor-android-kiosk > Sperren Sie Ihr Android-Gerät in den Kiosk-Modus mit voller Kontrolle über Hardware-Tasten und Launcher-Funktionalität für Ihre Capacitor-Apps. Kiosk-Modus System-UI ausblenden und in immersiven Vollbildmodus wechseln Launcher-Integration Legen Sie Ihre App als Geräte-Launcher/Home-App fest Hardware-Tastensteuerung Hardware-Tasten blockieren oder erlauben (Zurück, Home, Kürzlich, Lautstärke, Power) Statuserkennung Prüfen Sie, ob der Kiosk-Modus aktiv ist oder die App als Launcher festgelegt ist Android 6.0+ Unterstützt Android API 23 bis Android 15 (API 35) Umfassende Dokumentation Schauen Sie sich die [Dokumentation](/docs/plugins/android-kiosk/getting-started/) an, um den Kiosk-Modus in Minuten zu integrieren. ## Plattformunterstützung [Section titled “Plattformunterstützung”](#plattformunterstützung) Dieses Plugin ist **nur für Android**. Für die iOS-Kiosk-Modus-Funktionalität verwenden Sie bitte die integrierte Funktion [Geführter Zugriff](https://support.apple.com/en-us/HT202612) des Geräts. # Erste Schritte > Erfahren Sie, wie Sie das Android Kiosk Plugin installieren und verwenden, um Ihr Android-Gerät in den Kiosk-Modus zu sperren. 1. **Paket installieren** * npm ```sh npm i @capgo/capacitor-android-kiosk ``` * pnpm ```sh pnpm add @capgo/capacitor-android-kiosk ``` * yarn ```sh yarn add @capgo/capacitor-android-kiosk ``` * bun ```sh bun add @capgo/capacitor-android-kiosk ``` 2. **Mit nativen Projekten synchronisieren** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Plattformunterstützung [Section titled “Plattformunterstützung”](#plattformunterstützung) Dieses Plugin ist **nur für Android**. Für die iOS-Kiosk-Modus-Funktionalität verwenden Sie bitte die integrierte Funktion [Geführter Zugriff](https://support.apple.com/en-us/HT202612) des Geräts. ## Funktionen [Section titled “Funktionen”](#funktionen) * **Kiosk-Modus**: System-UI ausblenden und in immersiven Vollbildmodus wechseln * **Launcher-Integration**: Legen Sie Ihre App als Geräte-Launcher/Home-App fest * **Hardware-Tastensteuerung**: Blockieren oder erlauben Sie spezifische Hardware-Tasten * **Statuserkennung**: Prüfen Sie, ob der Kiosk-Modus aktiv ist oder die App als Launcher festgelegt ist * **Android 6.0+**: Unterstützt Android API 23 bis Android 15 (API 35) ## Grundlegende Verwendung [Section titled “Grundlegende Verwendung”](#grundlegende-verwendung) ### Kiosk-Modus aktivieren und beenden [Section titled “Kiosk-Modus aktivieren und beenden”](#kiosk-modus-aktivieren-und-beenden) ```typescript import { CapacitorAndroidKiosk } from '@capgo/capacitor-android-kiosk'; // Kiosk-Modus aktivieren await CapacitorAndroidKiosk.enterKioskMode(); // Kiosk-Modus beenden await CapacitorAndroidKiosk.exitKioskMode(); // Prüfen, ob im Kiosk-Modus const { isInKioskMode } = await CapacitorAndroidKiosk.isInKioskMode(); console.log('Kiosk-Modus aktiv:', isInKioskMode); ``` ### Launcher-Funktionalität [Section titled “Launcher-Funktionalität”](#launcher-funktionalität) Für die vollständige Kiosk-Modus-Funktionalität müssen Sie Ihre App als Geräte-Launcher festlegen: ```typescript // Home-Bildschirm-Einstellungen öffnen, damit der Benutzer Ihre App als Launcher auswählen kann await CapacitorAndroidKiosk.setAsLauncher(); // Prüfen, ob die App als Launcher festgelegt ist const { isLauncher } = await CapacitorAndroidKiosk.isSetAsLauncher(); console.log('App ist Launcher:', isLauncher); ``` ### Hardware-Tastensteuerung [Section titled “Hardware-Tastensteuerung”](#hardware-tastensteuerung) Steuern Sie, welche Hardware-Tasten im Kiosk-Modus funktionieren dürfen: ```typescript // Nur Lautstärketasten erlauben await CapacitorAndroidKiosk.setAllowedKeys({ volumeUp: true, volumeDown: true, back: false, home: false, recent: false }); // Alle Tasten blockieren (Standard) await CapacitorAndroidKiosk.setAllowedKeys({}); ``` ## Vollständiges Beispiel [Section titled “Vollständiges Beispiel”](#vollständiges-beispiel) ```typescript async function setupKioskMode() { try { // Prüfen, ob bereits als Launcher festgelegt const { isLauncher } = await CapacitorAndroidKiosk.isSetAsLauncher(); if (!isLauncher) { // Benutzer auffordern, als Launcher festzulegen await CapacitorAndroidKiosk.setAsLauncher(); alert('Bitte wählen Sie diese App als Ihre Home-App aus'); return; } // Erlaubte Tasten konfigurieren await CapacitorAndroidKiosk.setAllowedKeys({ volumeUp: true, volumeDown: true, back: false, home: false, recent: false, power: false }); // Kiosk-Modus aktivieren await CapacitorAndroidKiosk.enterKioskMode(); console.log('Kiosk-Modus aktiviert'); } catch (error) { console.error('Fehler beim Einrichten des Kiosk-Modus:', error); } } ``` ## API-Referenz [Section titled “API-Referenz”](#api-referenz) ### isInKioskMode() [Section titled “isInKioskMode()”](#isinkioskmode) Prüft, ob die App derzeit im Kiosk-Modus läuft. ```typescript const { isInKioskMode } = await CapacitorAndroidKiosk.isInKioskMode(); ``` **Rückgabe:** * `isInKioskMode` (boolean): Ob der Kiosk-Modus derzeit aktiv ist *** ### isSetAsLauncher() [Section titled “isSetAsLauncher()”](#issetaslauncher) Prüft, ob die App als Geräte-Launcher (Home-App) festgelegt ist. ```typescript const { isLauncher } = await CapacitorAndroidKiosk.isSetAsLauncher(); ``` **Rückgabe:** * `isLauncher` (boolean): Ob die App als Geräte-Launcher festgelegt ist *** ### enterKioskMode() [Section titled “enterKioskMode()”](#enterkioskmode) Aktiviert den Kiosk-Modus, blendet die System-UI aus und blockiert Hardware-Tasten. Die App muss als Geräte-Launcher festgelegt sein, damit dies effektiv funktioniert. ```typescript await CapacitorAndroidKiosk.enterKioskMode(); ``` *** ### exitKioskMode() [Section titled “exitKioskMode()”](#exitkioskmode) Beendet den Kiosk-Modus und stellt die normale System-UI und Hardware-Tastenfunktionalität wieder her. ```typescript await CapacitorAndroidKiosk.exitKioskMode(); ``` *** ### setAsLauncher() [Section titled “setAsLauncher()”](#setaslauncher) Öffnet die Home-Bildschirm-Einstellungen des Geräts, damit der Benutzer diese App als Launcher festlegen kann. Dies ist für die vollständige Kiosk-Modus-Funktionalität erforderlich. ```typescript await CapacitorAndroidKiosk.setAsLauncher(); ``` *** ### setAllowedKeys(options) [Section titled “setAllowedKeys(options)”](#setallowedkeysoptions) Legt fest, welche Hardware-Tasten im Kiosk-Modus funktionieren dürfen. Standardmäßig sind alle Hardware-Tasten im Kiosk-Modus blockiert. ```typescript await CapacitorAndroidKiosk.setAllowedKeys({ volumeUp: true, volumeDown: true, back: false, home: false, recent: false, power: false, camera: false, menu: false }); ``` **Parameter:** * `volumeUp` (boolean, optional): Lautstärke-Plus-Taste erlauben (Standard: false) * `volumeDown` (boolean, optional): Lautstärke-Minus-Taste erlauben (Standard: false) * `back` (boolean, optional): Zurück-Taste erlauben (Standard: false) * `home` (boolean, optional): Home-Taste erlauben (Standard: false) * `recent` (boolean, optional): Kürzlich-verwendete-Apps-Taste erlauben (Standard: false) * `power` (boolean, optional): Power-Taste erlauben (Standard: false) * `camera` (boolean, optional): Kamera-Taste erlauben, falls vorhanden (Standard: false) * `menu` (boolean, optional): Menü-Taste erlauben, falls vorhanden (Standard: false) *** ### getPluginVersion() [Section titled “getPluginVersion()”](#getpluginversion) Plugin-Version des nativen Capacitor-Plugins abrufen. ```typescript const { version } = await CapacitorAndroidKiosk.getPluginVersion(); console.log('Plugin-Version:', version); ``` **Rückgabe:** * `version` (string): Die Plugin-Versionsnummer ## Android-Konfiguration [Section titled “Android-Konfiguration”](#android-konfiguration) ### 1. MainActivity-Einrichtung [Section titled “1. MainActivity-Einrichtung”](#1-mainactivity-einrichtung) Um die vollständige Hardware-Tastenblockierung zu aktivieren, müssen Sie `dispatchKeyEvent` in Ihrer `MainActivity.java` überschreiben: ```java import android.view.KeyEvent; import ee.forgr.plugin.android_kiosk.CapacitorAndroidKioskPlugin; public class MainActivity extends BridgeActivity { @Override public boolean dispatchKeyEvent(KeyEvent event) { // Kiosk-Plugin abrufen CapacitorAndroidKioskPlugin kioskPlugin = (CapacitorAndroidKioskPlugin) this.getBridge().getPlugin("CapacitorAndroidKiosk").getInstance(); if (kioskPlugin != null && kioskPlugin.shouldBlockKey(event.getKeyCode())) { return true; // Taste blockieren } return super.dispatchKeyEvent(event); } @Override public void onBackPressed() { // super.onBackPressed() nicht aufrufen, um Zurück-Taste zu deaktivieren // Oder handleOnBackPressed des Plugins aufrufen } } ``` ### 2. AndroidManifest.xml [Section titled “2. AndroidManifest.xml”](#2-androidmanifestxml) Launcher-Intent-Filter hinzufügen, um Ihre App als Launcher auswählbar zu machen: ```xml ``` ## Wichtige Hinweise [Section titled “Wichtige Hinweise”](#wichtige-hinweise) 1. **Launcher-Anforderung**: Für die vollständige Kiosk-Modus-Funktionalität (Home-Taste blockieren, Task-Wechsel verhindern) muss Ihre App als Geräte-Launcher festgelegt sein. 2. **Testen**: Beim Testen können Sie den Kiosk-Modus programmatisch beenden oder eine andere App als Launcher festlegen. 3. **Android-Versionen**: Das Plugin verwendet moderne Android-APIs für Android 11+ und fällt für die Kompatibilität mit Android 6.0+ auf ältere Methoden zurück. 4. **Sicherheit**: Dieses Plugin ist für legitime Kiosk-Anwendungen konzipiert. Stellen Sie sicher, dass Sie Benutzern eine Möglichkeit bieten, den Kiosk-Modus zu beenden. 5. **Akku**: Der Kiosk-Modus hält den Bildschirm an. Erwägen Sie, Ihre eigene Bildschirm-Timeout- oder Helligkeitsverwaltung zu implementieren. ## iOS-Alternative [Section titled “iOS-Alternative”](#ios-alternative) Für iOS-Geräte verwenden Sie die integrierte Funktion [Geführter Zugriff](https://support.apple.com/en-us/HT202612): 1. Gehen Sie zu Einstellungen > Bedienungshilfen > Geführter Zugriff 2. Aktivieren Sie Geführter Zugriff 3. Legen Sie einen Code fest 4. Öffnen Sie Ihre App 5. Dreimal auf die Seitentaste klicken 6. Einstellungen anpassen und Geführten Zugriff starten ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) 1. **Launcher-Status zuerst prüfen** ```typescript const { isLauncher } = await CapacitorAndroidKiosk.isSetAsLauncher(); if (!isLauncher) { // Benutzer zuerst auffordern, als Launcher festzulegen await CapacitorAndroidKiosk.setAsLauncher(); } ``` 2. **Ausstiegsmechanismus bereitstellen** ```typescript // Spezifische Tastenkombination zum Beenden erlauben // Oder geheime Geste/Muster implementieren async function exitKioskWithConfirmation() { const confirmed = confirm('Kiosk-Modus beenden?'); if (confirmed) { await CapacitorAndroidKiosk.exitKioskMode(); } } ``` 3. **App-Lebenszyklus behandeln** ```typescript // Kiosk-Modus erneut aktivieren, wenn App fortgesetzt wird window.addEventListener('resume', async () => { const { isInKioskMode } = await CapacitorAndroidKiosk.isInKioskMode(); if (!isInKioskMode) { await CapacitorAndroidKiosk.enterKioskMode(); } }); ``` 4. **Fehlerbehandlung** ```typescript try { await CapacitorAndroidKiosk.enterKioskMode(); } catch (error) { console.error('Fehler beim Aktivieren des Kiosk-Modus:', error); // Benutzer benachrichtigen und Alternative bereitstellen } ``` # @capgo/capacitor-android-usagestatsmanager > Zugriff auf Android-Nutzungsstatistiken zur Überwachung von App-Nutzung, Bildschirmzeit und Benutzerverhalten mit Daten auf Systemebene. ## Überblick [Section titled “Überblick”](#überblick) Das Capacitor Android Usage Stats Manager Plugin stellt das UsageStatsManager SDK von Android für Capacitor-Anwendungen bereit und ermöglicht den Zugriff auf detaillierte App-Nutzungsstatistiken und Gerätenutzungsdaten. Dieses Plugin ermöglicht es Entwicklern, App-Nutzungsmuster, Bildschirmzeit und Benutzerverhalten auf Android-Geräten zu verfolgen. Nutzungsstatistiken Zugriff auf Android UsageStatsManager SDK-Daten 📱 App-Überwachung Verfolgen Sie individuelle App-Nutzungszeit und -Häufigkeit 🕐 Berechtigungsverwaltung Nahtlose Verwaltung von Nutzungsstatistik-Berechtigungen 🛡️ Paketinformationen Abfrage installierter Paketdetails und Metadaten 📦 ## Installation [Section titled “Installation”](#installation) ```bash npm install @capgo/capacitor-android-usagestatsmanager npx cap sync ``` ## Erforderliche Berechtigungen [Section titled “Erforderliche Berechtigungen”](#erforderliche-berechtigungen) Fügen Sie diese Berechtigungen zu Ihrer `android/app/src/main/AndroidManifest.xml` hinzu: ```xml ``` ## Kern-API-Methoden [Section titled “Kern-API-Methoden”](#kern-api-methoden) ### Nutzungsstatistiken [Section titled “Nutzungsstatistiken”](#nutzungsstatistiken) * `queryAndAggregateUsageStats(options)` - Detaillierte Nutzungsstatistiken für installierte Apps abrufen * `isUsageStatsPermissionGranted()` - Prüfen, ob die Berechtigung für Nutzungsstatistiken erteilt wurde * `openUsageStatsSettings()` - Systemeinstellungen für Nutzungsstatistik-Berechtigung öffnen ### Paketinformationen [Section titled “Paketinformationen”](#paketinformationen) * `queryAllPackages()` - Informationen über alle installierten Pakete abrufen ## Verwendungsbeispiel [Section titled “Verwendungsbeispiel”](#verwendungsbeispiel) ```typescript import { AndroidUsageStatsManager } from '@capgo/capacitor-android-usagestatsmanager'; // Prüfen, ob die Berechtigung erteilt wurde const permissionResult = await AndroidUsageStatsManager.isUsageStatsPermissionGranted(); if (!permissionResult.granted) { // Einstellungen öffnen, um die Berechtigung zu erteilen await AndroidUsageStatsManager.openUsageStatsSettings(); return; } // Nutzungsstatistiken abfragen const statsOptions = { intervalType: 0, // INTERVAL_DAILY startTime: Date.now() - (7 * 24 * 60 * 60 * 1000), // vor 7 Tagen endTime: Date.now() }; const usageStats = await AndroidUsageStatsManager.queryAndAggregateUsageStats(statsOptions); console.log('Nutzungsstatistiken:', usageStats); // Alle installierten Pakete abrufen const packages = await AndroidUsageStatsManager.queryAllPackages(); console.log('Installierte Pakete:', packages); ``` ## Berechtigungsverwaltung [Section titled “Berechtigungsverwaltung”](#berechtigungsverwaltung) Das Plugin erfordert spezielle Berechtigungen, die nicht über normale Laufzeit-Berechtigungsanforderungen erteilt werden können: 1. **PACKAGE\_USAGE\_STATS**: Ermöglicht den Zugriff auf Nutzungsstatistiken 2. **QUERY\_ALL\_PACKAGES**: Erforderlich für Paketinformationen (Android 11+) Benutzer müssen diese Berechtigungen manuell über die Systemeinstellungen erteilen. Verwenden Sie `openUsageStatsSettings()`, um Benutzer zur entsprechenden Einstellungsseite zu leiten. ## Datentypen [Section titled “Datentypen”](#datentypen) ### Nutzungsstatistiken [Section titled “Nutzungsstatistiken”](#nutzungsstatistiken-1) * App-Nutzungszeit und -Häufigkeit * Erste und letzte Verwendung * Gesamtzeit im Vordergrund * Anzahl der Starts ### Paketinformationen [Section titled “Paketinformationen”](#paketinformationen-1) * Paketname und Version * Installationszeit * App-Bezeichnungen und Icons * System- vs. Benutzer-Apps ## Anwendungsfälle [Section titled “Anwendungsfälle”](#anwendungsfälle) * **Digital Wellbeing Apps**: Bildschirmzeit und App-Nutzung überwachen * **Kindersicherung**: Gerätenutzung von Kindern verfolgen * **Produktivitäts-Apps**: Arbeitsmuster und Fokuszeiten analysieren * **Analytics**: Benutzerverhalten und App-Engagement verstehen ## Android-Kompatibilität [Section titled “Android-Kompatibilität”](#android-kompatibilität) * **Minimale Android-Version**: API Level 21 (Android 5.0) * **Erweiterte Funktionen**: API Level 29+ (Android 10+) für erweiterte Statistiken * **Paketabfragen**: API Level 30+ (Android 11+) erfordert QUERY\_ALL\_PACKAGES ## Sicherheitsüberlegungen [Section titled “Sicherheitsüberlegungen”](#sicherheitsüberlegungen) * Berechtigung für Nutzungsstatistiken ist sensibel und erfordert Benutzerzustimmung * Berücksichtigen Sie die Privatsphäre der Benutzer beim Sammeln von Nutzungsdaten * Implementieren Sie ordnungsgemäße Datenverarbeitungs- und Speicherpraktiken * Befolgen Sie die Google Play-Richtlinien für die Erfassung von Nutzungsdaten ## Dokumentation [Section titled “Dokumentation”](#dokumentation) Siehe die [vollständige Dokumentation](/docs/plugins/android-usagestatsmanager/getting-started/) für detaillierte Implementierungsanleitungen und erweiterte Nutzungsmuster. # Erste Schritte > Erfahren Sie, wie Sie das Android Usage Stats Manager Plugin installieren und verwenden, um auf App-Nutzungsstatistiken und Gerätenutzungsdaten zuzugreifen. ## Installation [Section titled “Installation”](#installation) * npm ```bash npm install @capgo/capacitor-android-usagestatsmanager npx cap sync ``` * yarn ```bash yarn add @capgo/capacitor-android-usagestatsmanager npx cap sync ``` * pnpm ```bash pnpm add @capgo/capacitor-android-usagestatsmanager npx cap sync ``` * bun ```bash bun add @capgo/capacitor-android-usagestatsmanager npx cap sync ``` ## Plattform-Konfiguration [Section titled “Plattform-Konfiguration”](#plattform-konfiguration) ### Android [Section titled “Android”](#android) Fügen Sie die erforderlichen Berechtigungen zu Ihrer `android/app/src/main/AndroidManifest.xml` hinzu: ```xml ``` ## Verwendungsbeispiel [Section titled “Verwendungsbeispiel”](#verwendungsbeispiel) ```typescript import { AndroidUsageStatsManager } from '@capgo/capacitor-android-usagestatsmanager'; // Prüfen, ob die Berechtigung erteilt wurde const permissionResult = await AndroidUsageStatsManager.isUsageStatsPermissionGranted(); if (!permissionResult.granted) { // Einstellungen öffnen, um die Berechtigung zu erteilen await AndroidUsageStatsManager.openUsageStatsSettings(); return; } // Nutzungsstatistiken für die letzten 7 Tage abfragen const statsOptions = { intervalType: 0, // INTERVAL_DAILY startTime: Date.now() - (7 * 24 * 60 * 60 * 1000), // vor 7 Tagen endTime: Date.now() }; const usageStats = await AndroidUsageStatsManager.queryAndAggregateUsageStats(statsOptions); console.log('Nutzungsstatistiken:', usageStats); // Alle installierten Pakete abrufen const packages = await AndroidUsageStatsManager.queryAllPackages(); console.log('Installierte Pakete:', packages); ``` ## API-Referenz [Section titled “API-Referenz”](#api-referenz) ### isUsageStatsPermissionGranted() [Section titled “isUsageStatsPermissionGranted()”](#isusagestatspermissiongranted) ```typescript isUsageStatsPermissionGranted() => Promise<{ granted: boolean }> ``` Prüft, ob die PACKAGE\_USAGE\_STATS-Berechtigung erteilt wurde. **Rückgabewert:** `Promise<{ granted: boolean }>` ### openUsageStatsSettings() [Section titled “openUsageStatsSettings()”](#openusagestatssettings) ```typescript openUsageStatsSettings() => Promise ``` Öffnet die Systemeinstellungsseite zum Erteilen der Berechtigung für Nutzungsstatistiken. ### queryAndAggregateUsageStats(options) [Section titled “queryAndAggregateUsageStats(options)”](#queryandaggregateusagestatsoptions) ```typescript queryAndAggregateUsageStats(options: UsageStatsOptions) => Promise ``` Ruft detaillierte Nutzungsstatistiken für installierte Apps ab. | Parameter | Typ | | ------------- | ------------------- | | **`options`** | `UsageStatsOptions` | **Rückgabewert:** `Promise` ### queryAllPackages() [Section titled “queryAllPackages()”](#queryallpackages) ```typescript queryAllPackages() => Promise ``` Ruft Informationen über alle auf dem Gerät installierten Pakete ab. **Rückgabewert:** `Promise` ## Schnittstellen [Section titled “Schnittstellen”](#schnittstellen) ### UsageStatsOptions [Section titled “UsageStatsOptions”](#usagestatsoptions) | Eigenschaft | Typ | Beschreibung | | ------------------ | -------- | ---------------------------------------------------------- | | **`intervalType`** | `number` | Intervalltyp (0=TÄGLICH, 1=WÖCHENTLICH, 2=MONATLICH, etc.) | | **`startTime`** | `number` | Startzeit in Millisekunden | | **`endTime`** | `number` | Endzeit in Millisekunden | ### UsageStatsResult [Section titled “UsageStatsResult”](#usagestatsresult) Enthält Nutzungsstatistikdaten für jede App: * App-Nutzungszeit und -Häufigkeit * Erste und letzte Verwendung * Gesamtzeit im Vordergrund * Anzahl der Starts ### PackagesResult [Section titled “PackagesResult”](#packagesresult) Enthält Informationen über installierte Pakete: * Paketname und Version * Installationszeit * App-Bezeichnungen und Icons * System- vs. Benutzer-Apps ## Berechtigungsverwaltung [Section titled “Berechtigungsverwaltung”](#berechtigungsverwaltung) Das Plugin erfordert spezielle Berechtigungen, die nicht über normale Laufzeit-Berechtigungsanforderungen erteilt werden können: 1. **PACKAGE\_USAGE\_STATS**: Ermöglicht den Zugriff auf Nutzungsstatistiken 2. **QUERY\_ALL\_PACKAGES**: Erforderlich für Paketinformationen (Android 11+) Benutzer müssen diese Berechtigungen manuell über die Systemeinstellungen erteilen. Verwenden Sie `openUsageStatsSettings()`, um Benutzer zur entsprechenden Einstellungsseite zu leiten. ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) * Prüfen Sie immer den Berechtigungsstatus, bevor Sie Nutzungsstatistiken abfragen * Gehen Sie mit Berechtigungsablehnungen benutzerfreundlich um * Berücksichtigen Sie die Privatsphäre der Benutzer beim Sammeln von Nutzungsdaten * Implementieren Sie ordnungsgemäße Datenverarbeitungs- und Speicherpraktiken * Befolgen Sie die Google Play-Richtlinien für die Erfassung von Nutzungsdaten ## Anwendungsfälle [Section titled “Anwendungsfälle”](#anwendungsfälle) * **Digital Wellbeing Apps**: Bildschirmzeit und App-Nutzung überwachen * **Kindersicherung**: Gerätenutzung von Kindern verfolgen * **Produktivitäts-Apps**: Arbeitsmuster und Fokuszeiten analysieren * **Analytics**: Benutzerverhalten und App-Engagement verstehen # @capgo/capacitor-appinsights > Verfolgen Sie App-Nutzung, Benutzerverhalten und Leistungsmetriken mit AppInsights-Integration für Ihre Capacitor-Apps. Analytics-Tracking Verfolgen Sie Benutzerverhalten und App-Nutzung mit AppInsights Benutzeridentifikation Legen Sie Benutzer-IDs fest und verwalten Sie diese für personalisiertes Tracking SDK-Zustandsverwaltung Überwachen Sie SDK-Initialisierung und Berechtigungsstatus Umfassende Dokumentation Schauen Sie sich die [Dokumentation](/docs/plugins/appinsights/getting-started/) an, um Analytics in Minuten zu integrieren. # Erste Schritte > Erfahren Sie, wie Sie das AppInsights Plugin für Anwendungsanalysen in Ihrer Capacitor-App installieren und verwenden. 1. **Paket installieren** * npm ```sh npm i @capgo/capacitor-appinsights ``` * pnpm ```sh pnpm add @capgo/capacitor-appinsights ``` * yarn ```sh yarn add @capgo/capacitor-appinsights ``` * bun ```sh bun add @capgo/capacitor-appinsights ``` 2. **Mit nativen Projekten synchronisieren** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Verwendung [Section titled “Verwendung”](#verwendung) Importieren Sie das Plugin und initialisieren Sie es mit Ihren AppInsights-Anmeldedaten: ```typescript import { CapacitorAppinsights } from '@capgo/capacitor-appinsights'; // SDK initialisieren const initializeAnalytics = async () => { await CapacitorAppinsights.init({ partnerId: 'your-partner-id', partnerKey: 'your-partner-key' }); }; // Benutzer-ID für Tracking festlegen const setUser = async (userId: string) => { await CapacitorAppinsights.setUserId({ userId }); }; // SDK-Zustand abrufen const checkSDKState = async () => { const state = await CapacitorAppinsights.getState(); console.log('SDK-Zustand:', state); }; // Plugin-Version abrufen const checkVersion = async () => { const { version } = await CapacitorAppinsights.getPluginVersion(); console.log('Plugin-Version:', version); }; ``` ## API-Referenz [Section titled “API-Referenz”](#api-referenz) ### init(options) [Section titled “init(options)”](#initoptions) Initialisieren Sie das AppInsights SDK mit Ihren Anmeldedaten. ```typescript await CapacitorAppinsights.init({ partnerId: 'your-partner-id', partnerKey: 'your-partner-key' }); ``` **Parameter:** * `partnerId` (string): Ihre AppInsights Partner-ID * `partnerKey` (string): Ihr AppInsights Partner-Schlüssel ### setUserId(options) [Section titled “setUserId(options)”](#setuseridoptions) Legen Sie die Benutzer-ID nach der Initialisierung fest oder aktualisieren Sie diese. ```typescript await CapacitorAppinsights.setUserId({ userId: 'user-123' }); ``` **Parameter:** * `userId` (string): Die Benutzer-ID, die für das Tracking festgelegt werden soll ### getState() [Section titled “getState()”](#getstate) Rufen Sie den aktuellen Zustand des SDK ab. ```typescript const state = await CapacitorAppinsights.getState(); console.log(state); // { // initCompleted: true, // jobScheduled: true, // permissionAcquired: true // } ``` **Rückgabe:** * `initCompleted` (boolean): Ob die SDK-Initialisierung abgeschlossen ist * `jobScheduled` (boolean): Ob der Datenerfassungsauftrag geplant ist * `permissionAcquired` (boolean): Ob die erforderlichen Berechtigungen erworben wurden ### getPluginVersion() [Section titled “getPluginVersion()”](#getpluginversion) Rufen Sie die native Capacitor-Plugin-Version ab. ```typescript const { version } = await CapacitorAppinsights.getPluginVersion(); console.log('Version:', version); ``` ## Vollständiges Beispiel [Section titled “Vollständiges Beispiel”](#vollständiges-beispiel) ```typescript import { CapacitorAppinsights } from '@capgo/capacitor-appinsights'; export class AnalyticsService { private initialized = false; async initialize(partnerId: string, partnerKey: string) { try { await CapacitorAppinsights.init({ partnerId, partnerKey }); const state = await CapacitorAppinsights.getState(); this.initialized = state.initCompleted; console.log('AppInsights initialisiert:', state); } catch (error) { console.error('Fehler beim Initialisieren von AppInsights:', error); } } async identifyUser(userId: string) { if (!this.initialized) { throw new Error('AppInsights nicht initialisiert'); } await CapacitorAppinsights.setUserId({ userId }); console.log('Benutzer identifiziert:', userId); } async getSDKStatus() { const state = await CapacitorAppinsights.getState(); return { ready: state.initCompleted && state.permissionAcquired, tracking: state.jobScheduled, state }; } } ``` ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) 1. **Früh initialisieren** Initialisieren Sie das SDK, sobald Ihre App startet, um genaues Tracking zu gewährleisten: ```typescript // Bei der App-Initialisierung await CapacitorAppinsights.init({ partnerId: process.env.APPINSIGHTS_PARTNER_ID, partnerKey: process.env.APPINSIGHTS_PARTNER_KEY }); ``` 2. **Benutzer-ID beim Login festlegen** Identifizieren Sie Benutzer nach der Authentifizierung: ```typescript async function onUserLogin(user) { await CapacitorAppinsights.setUserId({ userId: user.id }); } ``` 3. **SDK-Zustand prüfen** Überprüfen Sie, ob das SDK vor kritischen Operationen bereit ist: ```typescript const state = await CapacitorAppinsights.getState(); if (!state.initCompleted) { console.warn('AppInsights nicht bereit'); } ``` 4. **Fehler angemessen behandeln** ```typescript try { await CapacitorAppinsights.init(config); } catch (error) { console.error('Analytics-Initialisierung fehlgeschlagen:', error); // App-Betrieb auch bei Analytics-Fehler fortsetzen } ``` ## Plattformhinweise [Section titled “Plattformhinweise”](#plattformhinweise) ### iOS [Section titled “iOS”](#ios) * Erfordert iOS 10.0+ * Stellen Sie sicher, dass Sie die erforderlichen Berechtigungen in Info.plist für das Tracking haben ### Android [Section titled “Android”](#android) * Erfordert Android 5.0 (API 21)+ * Kann je nach Tracking-Funktionen Nutzungsstatistik-Berechtigung erfordern ### Web [Section titled “Web”](#web) * Auf der Web-Plattform nicht unterstützt # @capgo/capacitor-audio-recorder > Hochwertige Audioaufnahmen für Ihre Capacitor-Anwendungen Plattformübergreifend Audio auf iOS, Android und Web mit einer einheitlichen API aufnehmen 🌐 Mehrere Formate Unterstützung für AAC-, MP3- und WAV-Kodierung 🎵 Konfigurierbare Qualität Steuern Sie Abtastrate, Bitrate und Kanäle für optimale Qualität 🎚️ Umfassende Dokumentation Lesen Sie die [Dokumentation](/docs/plugins/audio-recorder/getting-started/), um das Plugin in wenigen Minuten zu beherrschen. # Erste Schritte mit Audio Recorder > Erfahren Sie, wie Sie Audioaufnahmen in Ihre Capacitor App integrieren Diese Anleitung führt Sie durch die Integration des Capacitor Audio Recorder Plugins in Ihre Anwendung. ## Installation [Section titled “Installation”](#installation) Installieren Sie das Plugin mit npm: ```bash npm install @capgo/capacitor-audio-recorder npx cap sync ``` ## iOS-Konfiguration [Section titled “iOS-Konfiguration”](#ios-konfiguration) Fügen Sie Folgendes zu Ihrer `Info.plist` hinzu: ```xml NSMicrophoneUsageDescription Diese App benötigt Zugriff auf das Mikrofon, um Audio aufzunehmen ``` ## Android-Konfiguration [Section titled “Android-Konfiguration”](#android-konfiguration) Fügen Sie die folgenden Berechtigungen zu Ihrer `AndroidManifest.xml` hinzu: ```xml ``` ## Web-Konfiguration [Section titled “Web-Konfiguration”](#web-konfiguration) Das Plugin verwendet die MediaRecorder API. Erfordert HTTPS in der Produktion. ## Grundlegende Verwendung [Section titled “Grundlegende Verwendung”](#grundlegende-verwendung) ### Plugin importieren [Section titled “Plugin importieren”](#plugin-importieren) ```typescript import { AudioRecorder } from '@capgo/capacitor-audio-recorder'; ``` ### Berechtigungen anfordern [Section titled “Berechtigungen anfordern”](#berechtigungen-anfordern) ```typescript const requestPermission = async () => { const permission = await AudioRecorder.requestPermissions(); console.log('Berechtigungsstatus:', permission.recordAudio); }; ``` ### Aufnahme starten [Section titled “Aufnahme starten”](#aufnahme-starten) ```typescript const startRecording = async () => { await AudioRecorder.startRecording(); console.log('Aufnahme gestartet'); }; ``` ### Aufnahme stoppen [Section titled “Aufnahme stoppen”](#aufnahme-stoppen) ```typescript const stopRecording = async () => { const result = await AudioRecorder.stopRecording(); console.log('Aufnahmepfad:', result.filePath); console.log('Dauer:', result.duration); }; ``` ### Aufnahme pausieren/fortsetzen [Section titled “Aufnahme pausieren/fortsetzen”](#aufnahme-pausierenfortsetzen) ```typescript const pauseRecording = async () => { await AudioRecorder.pauseRecording(); console.log('Aufnahme pausiert'); }; const resumeRecording = async () => { await AudioRecorder.resumeRecording(); console.log('Aufnahme fortgesetzt'); }; ``` ### Status abrufen [Section titled “Status abrufen”](#status-abrufen) ```typescript const getStatus = async () => { const status = await AudioRecorder.getStatus(); console.log('Nimmt auf:', status.isRecording); console.log('Dauer:', status.currentTime); }; ``` ## Vollständiges Beispiel [Section titled “Vollständiges Beispiel”](#vollständiges-beispiel) Hier ist eine vollständige Implementierung eines Sprachrekorders: ```typescript import { AudioRecorder } from '@capgo/capacitor-audio-recorder'; class VoiceRecorder { private isRecording = false; private isPaused = false; private recordingPath: string | null = null; async initialize() { const permission = await AudioRecorder.checkPermissions(); if (permission.recordAudio !== 'granted') { const requested = await AudioRecorder.requestPermissions(); if (requested.recordAudio !== 'granted') { throw new Error('Mikrofonberechtigung verweigert'); } } } async startRecording() { try { await AudioRecorder.startRecording(); this.isRecording = true; this.isPaused = false; console.log('Aufnahme gestartet'); } catch (error) { console.error('Aufnahme konnte nicht gestartet werden:', error); throw error; } } async pauseRecording() { if (!this.isRecording || this.isPaused) { return; } try { await AudioRecorder.pauseRecording(); this.isPaused = true; console.log('Aufnahme pausiert'); } catch (error) { console.error('Aufnahme konnte nicht pausiert werden:', error); throw error; } } async resumeRecording() { if (!this.isRecording || !this.isPaused) { return; } try { await AudioRecorder.resumeRecording(); this.isPaused = false; console.log('Aufnahme fortgesetzt'); } catch (error) { console.error('Aufnahme konnte nicht fortgesetzt werden:', error); throw error; } } async stopRecording() { if (!this.isRecording) { return null; } try { const result = await AudioRecorder.stopRecording(); this.isRecording = false; this.isPaused = false; this.recordingPath = result.filePath; console.log('Aufnahme gestoppt'); console.log('Dateipfad:', result.filePath); console.log('Dauer:', result.duration, 'Sekunden'); return result; } catch (error) { console.error('Aufnahme konnte nicht gestoppt werden:', error); throw error; } } async getCurrentStatus() { const status = await AudioRecorder.getStatus(); return { isRecording: status.isRecording, duration: status.currentTime, isPaused: this.isPaused }; } formatDuration(seconds: number): string { const mins = Math.floor(seconds / 60); const secs = Math.floor(seconds % 60); return `${mins}:${secs.toString().padStart(2, '0')}`; } getRecordingPath(): string | null { return this.recordingPath; } } // Verwendung const recorder = new VoiceRecorder(); // Initialisieren await recorder.initialize(); // Aufnahme starten await recorder.startRecording(); // Pausieren await recorder.pauseRecording(); // Fortsetzen await recorder.resumeRecording(); // Stoppen und Ergebnis abrufen const result = await recorder.stopRecording(); console.log('Aufnahme gespeichert:', result?.filePath); ``` ## Erweiterte Konfiguration [Section titled “Erweiterte Konfiguration”](#erweiterte-konfiguration) ### Audioqualität konfigurieren [Section titled “Audioqualität konfigurieren”](#audioqualität-konfigurieren) ```typescript const startHighQualityRecording = async () => { await AudioRecorder.startRecording({ format: 'aac', // aac, mp3 oder wav sampleRate: 44100, // 44100 Hz (CD-Qualität) channels: 2, // Stereo bitRate: 320000 // 320 kbps }); }; ``` ### Für Sprache konfigurieren [Section titled “Für Sprache konfigurieren”](#für-sprache-konfigurieren) ```typescript const startVoiceRecording = async () => { await AudioRecorder.startRecording({ format: 'aac', sampleRate: 16000, // 16 kHz (für Sprache optimiert) channels: 1, // Mono bitRate: 64000 // 64 kbps }); }; ``` ## UI-Integrationsbeispiel [Section titled “UI-Integrationsbeispiel”](#ui-integrationsbeispiel) ```typescript class AudioRecorderUI { private recorder: VoiceRecorder; private updateInterval: any; constructor() { this.recorder = new VoiceRecorder(); } async init() { await this.recorder.initialize(); this.setupEventListeners(); } setupEventListeners() { const recordButton = document.getElementById('record-btn'); const pauseButton = document.getElementById('pause-btn'); const stopButton = document.getElementById('stop-btn'); recordButton?.addEventListener('click', () => this.handleRecord()); pauseButton?.addEventListener('click', () => this.handlePause()); stopButton?.addEventListener('click', () => this.handleStop()); } async handleRecord() { await this.recorder.startRecording(); this.startDurationUpdate(); this.updateUI('recording'); } async handlePause() { const status = await this.recorder.getCurrentStatus(); if (status.isPaused) { await this.recorder.resumeRecording(); this.updateUI('recording'); } else { await this.recorder.pauseRecording(); this.updateUI('paused'); } } async handleStop() { const result = await this.recorder.stopRecording(); this.stopDurationUpdate(); this.updateUI('stopped'); if (result) { this.showRecordingResult(result); } } startDurationUpdate() { this.updateInterval = setInterval(async () => { const status = await this.recorder.getCurrentStatus(); this.updateDurationDisplay(status.duration); }, 100); } stopDurationUpdate() { if (this.updateInterval) { clearInterval(this.updateInterval); } } updateDurationDisplay(duration: number) { const display = document.getElementById('duration'); if (display) { display.textContent = this.recorder.formatDuration(duration); } } updateUI(state: 'recording' | 'paused' | 'stopped') { // Button-Zustände, Farben usw. aktualisieren console.log('UI-Status:', state); } showRecordingResult(result: any) { console.log('Aufnahme abgeschlossen:', result); // Wiedergabe-UI, Speicheroptionen usw. anzeigen } } // UI initialisieren const ui = new AudioRecorderUI(); ui.init(); ``` ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) 1. **Berechtigungen frühzeitig anfordern**: Prüfen Sie Berechtigungen, bevor Sie die Aufnahme-UI anzeigen 2. **Unterbrechungen behandeln**: Telefonanrufe, Alarme können die Aufnahme unterbrechen 3. **Speicher verwalten**: Alte Aufnahmen löschen, um Speicherplatz zu sparen 4. **Feedback geben**: Aufnahmestatus, Dauer und Wellenform anzeigen 5. **Auf Geräten testen**: Simulatoren/Emulatoren haben eingeschränkte Audio-Unterstützung ## Häufige Probleme [Section titled “Häufige Probleme”](#häufige-probleme) ### Berechtigung verweigert [Section titled “Berechtigung verweigert”](#berechtigung-verweigert) ```typescript const handlePermissionDenied = async () => { const permission = await AudioRecorder.checkPermissions(); if (permission.recordAudio === 'denied') { alert('Mikrofonberechtigung ist erforderlich, um Audio aufzunehmen. Bitte aktivieren Sie sie in den Einstellungen.'); } }; ``` ### Aufnahme unterbrochen [Section titled “Aufnahme unterbrochen”](#aufnahme-unterbrochen) ```typescript // App im Hintergrund behandeln document.addEventListener('pause', async () => { const status = await recorder.getCurrentStatus(); if (status.isRecording) { await recorder.pauseRecording(); // Benutzer benachrichtigen } }); ``` ### Speicherverwaltung [Section titled “Speicherverwaltung”](#speicherverwaltung) ```typescript const cleanupOldRecordings = async () => { // Bereinigungslogik implementieren // Aufnahmen löschen, die älter als X Tage sind // Oder nur die letzten N Aufnahmen behalten }; ``` ## Nächste Schritte [Section titled “Nächste Schritte”](#nächste-schritte) * Erkunden Sie die [API-Referenz](https://github.com/Cap-go/capacitor-audio-recorder#api) für vollständige Methodendokumentation * Sehen Sie sich die [Beispiel-App](https://github.com/Cap-go/capacitor-audio-recorder/tree/main/example) für erweiterte Verwendung an * Siehe das [Tutorial](/plugins/capacitor-audio-recorder) für vollständige Implementierungsbeispiele # @capgo/capacitor-plugin-audiosession > Hören Sie auf Audio-Routen-Änderungen, reagieren Sie auf Unterbrechungen und leiten Sie die Wiedergabe zum Lautsprecher um, ohne Capacitor zu verlassen. Das AudioSession Plugin von Capgo gibt Ihnen eine feinkörnige Kontrolle über die iOS AVAudioSession-Ebene, sodass Sie Kopfhörer, Bluetooth-Geräte und unerwartete Unterbrechungen elegant handhaben können. Routen-Awareness Erkennen Sie, wenn Benutzer Headsets, AirPods, Bluetooth-Lautsprecher und mehr einstecken oder herausziehen. Lautsprecher-Überschreibung Erzwingen Sie die Wiedergabe über den eingebauten Lautsprecher, wenn Sie Freisprechton benötigen. Unterbrechungsbehandlung Reagieren Sie auf Unterbrechungsereignisse wie Telefonanrufe oder Siri, um Audio sicher zu pausieren und fortzusetzen. Typsicherheit Starke TypeScript-Definitionen erleichtern den Aufbau robuster Audio-Erlebnisse. Dieses Plugin zielt auf iOS ab, wo AVAudioSession-Kontrolle erforderlich ist. Auf anderen Plattformen werden die Aufrufe ohne Auswirkung aufgelöst, sodass Sie einen gemeinsamen Code-Pfad beibehalten können. # Erste Schritte > Konfigurieren Sie das AudioSession Plugin, um auf iOS Audio-Route-Änderungen und Unterbrechungen zu reagieren. > ℹ️ **Plattformunterstützung:** Die Audio-Session-API ist auf iOS verfügbar. Aufrufe auf Android und im Web werden ohne Auswirkung aufgelöst, sodass Sie gemeinsame Logik beibehalten können. 1. **Paket installieren** * npm ```sh npm i @capgo/capacitor-plugin-audiosession ``` * pnpm ```sh pnpm add @capgo/capacitor-plugin-audiosession ``` * yarn ```sh yarn add @capgo/capacitor-plugin-audiosession ``` * bun ```sh bun add @capgo/capacitor-plugin-audiosession ``` 2. **iOS-Plattform synchronisieren** * npm ```sh npx cap sync ios ``` * pnpm ```sh pnpm cap sync ios ``` * yarn ```sh yarn cap sync ios ``` * bun ```sh bunx cap sync ios ``` ## Verfügbare Ausgänge überprüfen [Section titled “Verfügbare Ausgänge überprüfen”](#verfügbare-ausgänge-überprüfen) ```typescript import { AudioSession, AudioSessionPorts } from '@capgo/capacitor-plugin-audiosession'; const outputs = await AudioSession.currentOutputs(); if (outputs.includes(AudioSessionPorts.BLUETOOTH_A2DP)) { console.log('Bluetooth-Lautsprecher verbunden'); } ``` ## Auf Lautsprecher-Modus umschalten [Section titled “Auf Lautsprecher-Modus umschalten”](#auf-lautsprecher-modus-umschalten) ```typescript import { AudioSession, OutputOverrideType } from '@capgo/capacitor-plugin-audiosession'; await AudioSession.overrideOutput(OutputOverrideType.speaker); // System-Routing wiederherstellen, wenn Sie fertig sind await AudioSession.overrideOutput(OutputOverrideType.default); ``` ## Auf Routen- und Unterbrechungsereignisse hören [Section titled “Auf Routen- und Unterbrechungsereignisse hören”](#auf-routen--und-unterbrechungsereignisse-hören) ```typescript import { AudioSession, RouteChangeReasons, InterruptionTypes } from '@capgo/capacitor-plugin-audiosession'; const routeListener = await AudioSession.addListener('routeChanged', (reason) => { if (reason === RouteChangeReasons.NEW_DEVICE_AVAILABLE) { console.log('Benutzer hat eine neue Audio-Route verbunden'); } }); const interruptionListener = await AudioSession.addListener('interruption', (type) => { if (type === InterruptionTypes.BEGAN) { // Wiedergabe während der Unterbrechung pausieren } else { // Wiedergabe fortsetzen } }); // Später während der Bereinigung await routeListener.remove(); await interruptionListener.remove(); ``` ## Berechtigungen [Section titled “Berechtigungen”](#berechtigungen) * **iOS**: Fügen Sie alle erforderlichen Hintergrund-Audio-Modi (z.B. `audio`, `voice`) in `ios/App/App/Info.plist` hinzu, wenn Ihre App Audio im Hintergrund abspielt. * **Andere Plattformen**: Keine zusätzliche Einrichtung erforderlich; Methoden werden mit leeren Ergebnissen aufgelöst. # @capgo/capacitor-autofill-save-password > Aktivieren Sie Passwort-Autofill und Anmeldedatenverwaltung mit Systemintegration für nahtlose Authentifizierungserlebnisse. ## Überblick [Section titled “Überblick”](#überblick) Das Capacitor Autofill Save Password Plugin bietet Passwort-Speicher- und Autofill-Funktionalität für Capacitor-Anwendungen. Dieses Plugin integriert sich mit der Anmeldedatenverwaltung auf Systemebene, um nahtlose Authentifizierungserlebnisse mit sicherer Passwortspeicherung und -abruf zu bieten. Passwort-Speicherung Anmeldedaten sicher im System-Keychain speichern 🔐 Autofill-Integration Passwort-Autofill-Unterstützung auf Systemebene 🗝️ Plattformübergreifend iOS-Unterstützung mit Android-Entwicklung in Arbeit 📱 Domain-Zuordnung Associated Domains für nahtlose Authentifizierung ❤️ ## Installation [Section titled “Installation”](#installation) ```bash npm install @capgo/capacitor-autofill-save-password npx cap sync ``` ## Plattformunterstützung [Section titled “Plattformunterstützung”](#plattformunterstützung) * **iOS**: Vollständige Unterstützung (funktioniert mit iOS 18.3 und älteren Versionen) * **Android**: In Arbeit ## iOS-Konfiguration [Section titled “iOS-Konfiguration”](#ios-konfiguration) ### 1. Associated Domains Einrichtung [Section titled “1. Associated Domains Einrichtung”](#1-associated-domains-einrichtung) Konfigurieren Sie Associated Domains in Ihrem Apple Developer Account und fügen Sie sie zu Ihrer `App.entitlements` Datei hinzu: ```xml com.apple.developer.associated-domains webcredentials:yourdomain.com webcredentials:www.yourdomain.com ``` ### 2. Web-Anmeldedaten-Konfiguration [Section titled “2. Web-Anmeldedaten-Konfiguration”](#2-web-anmeldedaten-konfiguration) Fügen Sie eine `.well-known/apple-app-site-association` Datei zu Ihrer Website hinzu: ```json { "webcredentials": { "apps": [ "TEAMID.com.yourcompany.yourapp" ] } } ``` ## Kern-API-Methoden [Section titled “Kern-API-Methoden”](#kern-api-methoden) ### Passwortverwaltung [Section titled “Passwortverwaltung”](#passwortverwaltung) * `promptDialog(options)` - Passwort im System-Keychain speichern * `readPassword()` - Gespeichertes Passwort aus Keychain lesen ## Verwendungsbeispiel [Section titled “Verwendungsbeispiel”](#verwendungsbeispiel) ```typescript import { SavePassword } from '@capgo/capacitor-autofill-save-password'; // Passwort nach erfolgreicher Anmeldung speichern async function login(username: string, password: string) { try { // Führen Sie hier Ihre Anmeldelogik aus const loginSuccess = await performLogin(username, password); if (loginSuccess) { // Benutzer zum Speichern des Passworts auffordern await SavePassword.promptDialog({ username: username, password: password, url: 'https://yourdomain.com' // nur iOS }); console.log('Passwort erfolgreich gespeichert'); } } catch (error) { console.error('Passwort konnte nicht gespeichert werden:', error); } } // Gespeichertes Passwort für Autofill lesen async function loadSavedCredentials() { try { const credentials = await SavePassword.readPassword(); if (credentials.username && credentials.password) { console.log('Gespeicherte Anmeldedaten gefunden'); // Anmeldeformular vorab ausfüllen return { username: credentials.username, password: credentials.password }; } } catch (error) { console.error('Gespeichertes Passwort konnte nicht gelesen werden:', error); } return null; } ``` ## Android-Konfiguration (In Arbeit) [Section titled “Android-Konfiguration (In Arbeit)”](#android-konfiguration-in-arbeit) Für zukünftige Android-Unterstützung wird die folgende Konfiguration erforderlich sein: ### 1. Google Services Plugin [Section titled “1. Google Services Plugin”](#1-google-services-plugin) Fügen Sie zu `android/app/build.gradle` hinzu: ```kotlin apply plugin: 'com.google.gms.google-services' ``` ### 2. Domain-Konfiguration [Section titled “2. Domain-Konfiguration”](#2-domain-konfiguration) Fügen Sie zu `android/app/src/main/res/values/strings.xml` hinzu: ```xml [{ "relation": ["delegate_permission/common.handle_all_urls"], "target": { "namespace": "web", "site": "https://yourdomain.com" } }] ``` ### 3. Google Services JSON [Section titled “3. Google Services JSON”](#3-google-services-json) Fügen Sie `google-services.json` zu Ihrem Android-Projekt hinzu. ## API-Referenz [Section titled “API-Referenz”](#api-referenz) ### promptDialog(options) [Section titled “promptDialog(options)”](#promptdialogoptions) Speichert Benutzeranmeldedaten im System-Keychain. ```typescript interface SavePasswordOptions { username: string; password: string; url?: string; // Nur iOS - zugehörige Domain-URL } ``` ### readPassword() [Section titled “readPassword()”](#readpassword) Ruft gespeicherte Anmeldedaten aus dem System-Keychain ab. ```typescript interface SavedCredentials { username: string; password: string; } ``` ## Integration mit dem Anmeldeablauf [Section titled “Integration mit dem Anmeldeablauf”](#integration-mit-dem-anmeldeablauf) ```typescript import { SavePassword } from '@capgo/capacitor-autofill-save-password'; class AuthService { async login(username: string, password: string) { try { // Mit Ihrem Backend authentifizieren const response = await fetch('/api/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username, password }) }); if (response.ok) { // Anmeldedaten zum Speichern anbieten await this.offerToSavePassword(username, password); return true; } } catch (error) { console.error('Anmeldung fehlgeschlagen:', error); } return false; } private async offerToSavePassword(username: string, password: string) { try { await SavePassword.promptDialog({ username, password, url: 'https://yourdomain.com' }); } catch (error) { // Benutzer hat abgelehnt zu speichern oder Fehler aufgetreten console.log('Passwort nicht gespeichert:', error); } } async loadSavedCredentials() { try { return await SavePassword.readPassword(); } catch (error) { console.log('Keine gespeicherten Anmeldedaten gefunden'); return null; } } } ``` ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) * Fordern Sie nur zum Speichern von Passwörtern nach erfolgreicher Authentifizierung auf * Behandeln Sie Fälle, in denen Benutzer das Speichern von Passwörtern ablehnen, angemessen * Implementieren Sie ordnungsgemäße Fehlerbehandlung für Keychain-Zugriffsfehler * Verwenden Sie Associated Domains für nahtloses Web-App-Anmeldedaten-Sharing * Testen Sie die Autofill-Funktionalität über verschiedene iOS-Versionen hinweg ## Sicherheitsüberlegungen [Section titled “Sicherheitsüberlegungen”](#sicherheitsüberlegungen) * Anmeldedaten werden im System-Keychain mit entsprechenden Sicherheitsflags gespeichert * Associated Domains stellen sicher, dass Anmeldedaten nur für autorisierte Apps zugänglich sind * Befolgen Sie die Plattform-Sicherheitsrichtlinien für die Anmeldedatenverwaltung * Erwägen Sie die Implementierung zusätzlicher Sicherheitsmaßnahmen für sensible Anwendungen ## Einschränkungen [Section titled “Einschränkungen”](#einschränkungen) * Android-Unterstützung ist derzeit in Entwicklung * iOS erfordert ordnungsgemäße Associated Domains-Konfiguration * Autofill-Verhalten kann über verschiedene iOS-Versionen variieren * Erfordert Benutzerzustimmung zum Speichern und Zugriff auf Anmeldedaten ## Dokumentation [Section titled “Dokumentation”](#dokumentation) Siehe die [vollständige Dokumentation](/docs/plugins/autofill-save-password/getting-started/) für detaillierte Implementierungsanleitungen und erweiterte Integrationsmuster. # Erste Schritte > Erfahren Sie, wie Sie das Autofill Save Password Plugin für nahtlose Passwortverwaltung in Ihrer Capacitor App installieren und konfigurieren. ## Installation [Section titled “Installation”](#installation) * npm ```bash npm install @capgo/capacitor-autofill-save-password npx cap sync ``` * yarn ```bash yarn add @capgo/capacitor-autofill-save-password npx cap sync ``` * pnpm ```bash pnpm add @capgo/capacitor-autofill-save-password npx cap sync ``` * bun ```bash bun add @capgo/capacitor-autofill-save-password npx cap sync ``` ## Plattformunterstützung [Section titled “Plattformunterstützung”](#plattformunterstützung) * **iOS**: Vollständige Unterstützung (iOS 18.3 und ältere Versionen) * **Android**: In Arbeit ## Plattform-Konfiguration [Section titled “Plattform-Konfiguration”](#plattform-konfiguration) ### iOS [Section titled “iOS”](#ios) #### 1. Associated Domains Einrichtung [Section titled “1. Associated Domains Einrichtung”](#1-associated-domains-einrichtung) Konfigurieren Sie Associated Domains in Ihrem Apple Developer Account und fügen Sie sie zu Ihrer `App.entitlements` Datei hinzu: ```xml com.apple.developer.associated-domains webcredentials:yourdomain.com webcredentials:www.yourdomain.com ``` #### 2. Web-Anmeldedaten-Konfiguration [Section titled “2. Web-Anmeldedaten-Konfiguration”](#2-web-anmeldedaten-konfiguration) Fügen Sie eine `.well-known/apple-app-site-association` Datei zu Ihrer Website hinzu: ```json { "webcredentials": { "apps": [ "TEAMID.com.yourcompany.yourapp" ] } } ``` ### Android (Zukünftige Unterstützung) [Section titled “Android (Zukünftige Unterstützung)”](#android-zukünftige-unterstützung) #### 1. Google Services Plugin [Section titled “1. Google Services Plugin”](#1-google-services-plugin) Fügen Sie zu `android/app/build.gradle` hinzu: ```kotlin apply plugin: 'com.google.gms.google-services' ``` #### 2. Domain-Konfiguration [Section titled “2. Domain-Konfiguration”](#2-domain-konfiguration) Fügen Sie zu `android/app/src/main/res/values/strings.xml` hinzu: ```xml [{ "relation": ["delegate_permission/common.handle_all_urls"], "target": { "namespace": "web", "site": "https://yourdomain.com" } }] ``` ## Verwendungsbeispiel [Section titled “Verwendungsbeispiel”](#verwendungsbeispiel) ```typescript import { SavePassword } from '@capgo/capacitor-autofill-save-password'; // Passwort nach erfolgreicher Anmeldung speichern async function login(username: string, password: string) { try { // Führen Sie hier Ihre Anmeldelogik aus const loginSuccess = await performLogin(username, password); if (loginSuccess) { // Benutzer zum Speichern des Passworts auffordern await SavePassword.promptDialog({ username: username, password: password, url: 'https://yourdomain.com' // nur iOS }); console.log('Passwort erfolgreich gespeichert'); } } catch (error) { console.error('Passwort konnte nicht gespeichert werden:', error); } } // Gespeichertes Passwort für Autofill lesen async function loadSavedCredentials() { try { const credentials = await SavePassword.readPassword(); if (credentials.username && credentials.password) { console.log('Gespeicherte Anmeldedaten gefunden'); // Anmeldeformular vorab ausfüllen return { username: credentials.username, password: credentials.password }; } } catch (error) { console.error('Gespeichertes Passwort konnte nicht gelesen werden:', error); } return null; } ``` ## API-Referenz [Section titled “API-Referenz”](#api-referenz) ### promptDialog(options) [Section titled “promptDialog(options)”](#promptdialogoptions) ```typescript promptDialog(options: SavePasswordOptions) => Promise ``` Speichert Benutzeranmeldedaten im System-Keychain. | Parameter | Typ | | ------------- | --------------------- | | **`options`** | `SavePasswordOptions` | ### readPassword() [Section titled “readPassword()”](#readpassword) ```typescript readPassword() => Promise ``` Ruft gespeicherte Anmeldedaten aus dem System-Keychain ab. **Rückgabewert:** `Promise` ## Schnittstellen [Section titled “Schnittstellen”](#schnittstellen) ### SavePasswordOptions [Section titled “SavePasswordOptions”](#savepasswordoptions) | Eigenschaft | Typ | Beschreibung | | -------------- | -------- | ------------------------------------------ | | **`username`** | `string` | Der zu speichernde Benutzername | | **`password`** | `string` | Das zu speichernde Passwort | | **`url`** | `string` | Nur iOS - zugehörige Domain-URL (optional) | ### SavedCredentials [Section titled “SavedCredentials”](#savedcredentials) | Eigenschaft | Typ | Beschreibung | | -------------- | -------- | ----------------------------- | | **`username`** | `string` | Der gespeicherte Benutzername | | **`password`** | `string` | Das gespeicherte Passwort | ## Integration mit dem Anmeldeablauf [Section titled “Integration mit dem Anmeldeablauf”](#integration-mit-dem-anmeldeablauf) ```typescript import { SavePassword } from '@capgo/capacitor-autofill-save-password'; class AuthService { async login(username: string, password: string) { try { // Mit Ihrem Backend authentifizieren const response = await fetch('/api/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username, password }) }); if (response.ok) { // Anmeldedaten zum Speichern anbieten await this.offerToSavePassword(username, password); return true; } } catch (error) { console.error('Anmeldung fehlgeschlagen:', error); } return false; } private async offerToSavePassword(username: string, password: string) { try { await SavePassword.promptDialog({ username, password, url: 'https://yourdomain.com' }); } catch (error) { // Benutzer hat abgelehnt zu speichern oder Fehler aufgetreten console.log('Passwort nicht gespeichert:', error); } } async loadSavedCredentials() { try { return await SavePassword.readPassword(); } catch (error) { console.log('Keine gespeicherten Anmeldedaten gefunden'); return null; } } } ``` ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) * Fordern Sie nur zum Speichern von Passwörtern nach erfolgreicher Authentifizierung auf * Behandeln Sie Fälle, in denen Benutzer das Speichern von Passwörtern ablehnen, angemessen * Implementieren Sie ordnungsgemäße Fehlerbehandlung für Keychain-Zugriffsfehler * Verwenden Sie Associated Domains für nahtloses Web-App-Anmeldedaten-Sharing * Testen Sie die Autofill-Funktionalität über verschiedene iOS-Versionen hinweg ## Sicherheitsüberlegungen [Section titled “Sicherheitsüberlegungen”](#sicherheitsüberlegungen) * Anmeldedaten werden im System-Keychain mit entsprechenden Sicherheitsflags gespeichert * Associated Domains stellen sicher, dass Anmeldedaten nur für autorisierte Apps zugänglich sind * Befolgen Sie die Plattform-Sicherheitsrichtlinien für die Anmeldedatenverwaltung * Erwägen Sie die Implementierung zusätzlicher Sicherheitsmaßnahmen für sensible Anwendungen # Background Geolocation > Erhalten Sie genaue Standortaktualisierungen, auch wenn die App im Hintergrund läuft. Genaue Verfolgung Priorisiert Genauigkeit für Anwendungsfälle wie Wandern und Navigation 🎯 Hintergrund-Updates Erhält weiterhin Standortaktualisierungen, auch wenn die App im Hintergrund ist 🏃 Akkueffizient Optimiert, um Genauigkeit und Akkuverbrauch auszugleichen 🔋 Erste Schritte Lesen Sie die [Erste Schritte Anleitung](/docs/plugins/background-geolocation/getting-started/), um das Plugin zu installieren und zu konfigurieren. ## Plugin-Vergleich [Section titled “Plugin-Vergleich”](#plugin-vergleich) Ein kurzer Vergleich zwischen den drei Haupt-Hintergrund-Geolokalisierungs-Plugins, die häufig in Capacitor Apps verwendet werden. | Plugin | Genauigkeit | Hintergrund | HTTP Upload | Preisgestaltung | | --------------------------------------------------------- | ----------- | ----------- | -------------------------------------------- | --------------- | | `@capacitor-community/background-geolocation` (Community) | Nicht genau | Ja | Nein | Kostenlos | | `@capgo/background-geolocation` (dieses Plugin) | Genau | Ja | Nein | Kostenlos | | Transistorsoft (Original) | Genau | Ja | Ja — integrierter HTTP-Uploader zu Ihrer API | Kostenpflichtig | **Hinweise:** * Das Community-Plugin ist leichtgewichtig und funktioniert weiterhin im Hintergrund, ist aber bekanntermaßen weniger genau als die unten aufgeführten Optionen. * Dieses Capgo-Plugin ist optimiert, um genaue Standortfixierungen und zuverlässigen Hintergrundbetrieb ohne kostenpflichtige Lizenz zu bieten. * Transistorsoft bietet eine vollständige kommerzielle Lösung, die auch integrierte HTTP-Upload-Funktionen zum Senden von Standortdaten an Ihr Backend umfasst. # Erste Schritte > Erfahren Sie, wie Sie das Background Geolocation Plugin installieren und verwenden, um den Gerätestandort im Hintergrund zu verfolgen. ## Installation [Section titled “Installation”](#installation) Dieses Plugin unterstützt Capacitor v7: | Capacitor | Plugin | | --------- | ------ | | v7 | v7 | * npm ```bash npm install @capgo/background-geolocation npx cap update ``` * yarn ```bash yarn add @capgo/background-geolocation npx cap update ``` * pnpm ```bash pnpm add @capgo/background-geolocation npx cap update ``` * bun ```bash bun add @capgo/background-geolocation npx cap update ``` ## Plattform-Einrichtung [Section titled “Plattform-Einrichtung”](#plattform-einrichtung) ### iOS [Section titled “iOS”](#ios) Fügen Sie die folgenden Schlüssel zu `Info.plist.` hinzu: ```xml ... NSLocationWhenInUseUsageDescription Wir müssen Ihren Standort verfolgen NSLocationAlwaysAndWhenInUseUsageDescription Wir müssen Ihren Standort verfolgen, während Ihr Gerät gesperrt ist. UIBackgroundModes location ... ``` ### Android [Section titled “Android”](#android) Setzen Sie die Option `android.useLegacyBridge` in Ihrer Capacitor-Konfiguration auf `true`. Dies verhindert, dass Standortaktualisierungen nach 5 Minuten im Hintergrund anhalten. Siehe und . Ab Android 13+ benötigt die App die Laufzeitberechtigung `POST_NOTIFICATIONS`, um die dauerhafte Benachrichtigung anzuzeigen, die den Benutzer darüber informiert, dass sein Standort im Hintergrund verwendet wird. Diese Laufzeitberechtigung wird nach Erteilung der Standortberechtigung angefordert. Wenn Ihre App Standortaktualisierungen in Echtzeit an einen Server weiterleitet, beachten Sie, dass Android nach 5 Minuten im Hintergrund HTTP-Anfragen drosselt, die von der WebView initiiert werden. Die Lösung besteht darin, ein natives HTTP-Plugin wie [CapacitorHttp](https://capacitorjs.com/docs/apis/http) zu verwenden. Siehe . Android-spezifische Konfiguration kann in `strings.xml` vorgenommen werden: ```xml Background Tracking drawable/ic_tracking yellow ``` ## Verwendung [Section titled “Verwendung”](#verwendung) ```javascript import { BackgroundGeolocation } from "@capgo/background-geolocation"; BackgroundGeolocation.start( { backgroundMessage: "Abbrechen, um Akkuverbrauch zu verhindern.", backgroundTitle: "Verfolge Sie.", requestPermissions: true, stale: false, distanceFilter: 50 }, (location, error) => { if (error) { if (error.code === "NOT_AUTHORIZED") { if (window.confirm( "Diese App benötigt Ihren Standort, " + "hat aber keine Berechtigung.\n\n" + "Einstellungen jetzt öffnen?" )) { // Es kann nützlich sein, den Benutzer zu den Einstellungen seines Geräts // zu leiten, wenn Standortberechtigungen verweigert wurden. Das // Plugin bietet die 'openSettings' Methode, um genau // dies zu tun. BackgroundGeolocation.openSettings(); } } return console.error(error); } return console.log(location); } ).then(() => { // Wenn Standortaktualisierungen nicht mehr benötigt werden, sollte das Plugin durch Aufruf gestoppt werden BackgroundGeolocation.stop(); }); // Setzen Sie eine geplante Route, um einen Benachrichtigungston zu erhalten, wenn ein neuer Standort ankommt und nicht auf der Route liegt: BackgroundGeolocation.setPlannedRoute({soundFile: "assets/myFile.mp3", route: [[1,2], [3,4]], distance: 30 }); // Wenn Sie nur den aktuellen Standort wollen, versuchen Sie etwas wie dies. Je länger // das Timeout, desto genauer wird die Schätzung sein. Ich würde nicht unter ca. 100ms gehen. function guessLocation(callback, timeout) { let last_location; BackgroundGeolocation.start( { requestPermissions: false, stale: true }, (location) => { last_location = location || undefined; } ).then(() => { setTimeout(() => { callback(last_location); BackgroundGeolocation.stop(); }, timeout); }); } ``` ## API [Section titled “API”](#api) ### start(…) [Section titled “start(…)”](#start) ```typescript start(options: StartOptions, callback: (position?: Location | undefined, error?: CallbackError | undefined) => void) => Promise ``` Um mit dem Lauschen auf Änderungen am Standort des Geräts zu beginnen, rufen Sie diese Methode auf. Ein Promise wird zurückgegeben, um anzuzeigen, dass der Aufruf abgeschlossen wurde. Der Callback wird jedes Mal aufgerufen, wenn ein neuer Standort verfügbar ist, oder wenn bei Aufruf dieser Methode ein Fehler aufgetreten ist. Verlassen Sie sich nicht auf Promise-Ablehnung dafür. | Parameter | Typ | Beschreibung | | -------------- | ------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------- | | **`options`** | `StartOptions` | Die Konfigurationsoptionen | | **`callback`** | `(position?: Location, error?: CallbackError) => void` | Die Callback-Funktion, die aufgerufen wird, wenn ein neuer Standort verfügbar ist oder ein Fehler auftritt | ### stop() [Section titled “stop()”](#stop) ```typescript stop() => Promise ``` Stoppt Standortaktualisierungen. ### openSettings() [Section titled “openSettings()”](#opensettings) ```typescript openSettings() => Promise ``` Öffnet die Standorteinstellungsseite des Geräts. Nützlich, um Benutzer zu leiten, Standortdienste zu aktivieren oder Berechtigungen anzupassen. ### setPlannedRoute(…) [Section titled “setPlannedRoute(…)”](#setplannedroute) ```typescript setPlannedRoute(options: SetPlannedRouteOptions) => Promise ``` Spielt eine Audiodatei ab, wenn der Benutzer von der geplanten Route abweicht. Dies sollte verwendet werden, um einen Ton abzuspielen (auch im Hintergrund, nur für native). | Parameter | Typ | Beschreibung | | ------------- | ------------------------ | ------------------------------------------------------------- | | **`options`** | `SetPlannedRouteOptions` | Die Optionen zum Festlegen der geplanten Route und Audiodatei | ## Schnittstellen [Section titled “Schnittstellen”](#schnittstellen) ### StartOptions [Section titled “StartOptions”](#startoptions) Die Optionen zum Konfigurieren von Standortaktualisierungen. | Eigenschaft | Typ | Beschreibung | Standard | | ------------------------ | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------- | | **`backgroundMessage`** | `string` | Wenn die Option “backgroundMessage” definiert ist, stellt das Plugin Standortaktualisierungen bereit, egal ob die App im Hintergrund oder Vordergrund ist. Wenn es nicht definiert ist, werden Standortaktualisierungen nur im Vordergrund garantiert. Dies gilt für beide Plattformen. Auf Android muss eine Benachrichtigung angezeigt werden, um Standortaktualisierungen im Hintergrund weiter zu erhalten. Diese Option gibt den Text dieser Benachrichtigung an. | | | **`backgroundTitle`** | `string` | Der Titel der oben genannten Benachrichtigung. | `"Using your location"` | | **`requestPermissions`** | `boolean` | Ob Berechtigungen automatisch vom Benutzer angefordert werden sollen, wenn sie nicht bereits erteilt wurden. | `true` | | **`stale`** | `boolean` | Wenn “true”, können veraltete Standorte geliefert werden, während das Gerät eine GPS-Fixierung erhält. Sie sind dafür verantwortlich, die “time” Eigenschaft zu überprüfen. Wenn “false”, sind Standorte garantiert aktuell. | `false` | | **`distanceFilter`** | `number` | Die Entfernung in Metern, die sich das Gerät bewegen muss, bevor eine neue Standortaktualisierung ausgelöst wird. Dies wird verwendet, um kleine Bewegungen herauszufiltern und die Anzahl der Aktualisierungen zu reduzieren. | `0` | ### Location [Section titled “Location”](#location) Repräsentiert einen geografischen Standort mit verschiedenen Attributen. Enthält alle standardmäßigen Standorteigenschaften, die von GPS/Netzwerkanbietern zurückgegeben werden. | Eigenschaft | Typ | Beschreibung | | ---------------------- | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **`latitude`** | `number` | Breitengrad in Grad. Bereich: -90.0 bis +90.0 | | **`longitude`** | `number` | Längengrad in Grad. Bereich: -180.0 bis +180.0 | | **`accuracy`** | `number` | Radius der horizontalen Unsicherheit in Metern, mit 68% Konfidenz. Niedrigere Werte zeigen einen genaueren Standort an. | | **`altitude`** | `number \| null` | Meter über dem Meeresspiegel (oder null, wenn nicht verfügbar). | | **`altitudeAccuracy`** | `number \| null` | Vertikale Unsicherheit in Metern, mit 68% Konfidenz (oder null, wenn nicht verfügbar). | | **`simulated`** | `boolean` | `true`, wenn der Standort durch Software simuliert wurde, anstatt durch GPS. Nützlich zum Erkennen von Mock-Standorten in der Entwicklung oder beim Testen. | | **`bearing`** | `number \| null` | Abweichung vom wahren Norden in Grad (oder null, wenn nicht verfügbar). Bereich: 0.0 bis 360.0 | | **`speed`** | `number \| null` | Geschwindigkeit in Metern pro Sekunde (oder null, wenn nicht verfügbar). | | **`time`** | `number \| null` | Zeit, zu der der Standort produziert wurde, in Millisekunden seit der Unix-Epoche. Verwenden Sie dies, um zu prüfen, ob ein Standort veraltet ist, wenn stale: true verwendet wird. | ### CallbackError [Section titled “CallbackError”](#callbackerror) Fehlerobjekt, das möglicherweise an den Standort-Start-Callback übergeben wird. Erweitert den Standard-Error mit optionalen Fehlercodes. | Eigenschaft | Typ | Beschreibung | | ----------- | -------- | --------------------------------------------------------- | | **`code`** | `string` | Optionaler Fehlercode für spezifischere Fehlerbehandlung. | ### SetPlannedRouteOptions [Section titled “SetPlannedRouteOptions”](#setplannedrouteoptions) | Eigenschaft | Typ | Beschreibung | Standard | | --------------- | -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | | **`soundFile`** | `string` | Der Name der abzuspielenden Audiodatei. Muss ein gültiger relativer Pfad im öffentlichen Ordner der App sein, um sowohl für Web- als auch für native Plattformen zu funktionieren. Es ist nicht erforderlich, den öffentlichen Ordner im Pfad anzugeben. | | | **`route`** | `[number, number][]` | Die geplante Route als Array von Längen- und Breitengradpaaren. Jedes Paar repräsentiert einen Punkt auf der Route. Dies wird verwendet, um eine Route zu definieren, der der Benutzer folgen kann. Die Route wird verwendet, um einen Ton abzuspielen, wenn der Benutzer davon abweicht. | | | **`distance`** | `number` | Die Entfernung in Metern, die der Benutzer von der geplanten Route abweichen muss, um den Ton auszulösen. Dies wird verwendet, um zu bestimmen, wie weit der Benutzer von der Route entfernt sein kann, bevor der Ton abgespielt wird. Wenn nicht angegeben, wird ein Standardwert von 50 Metern verwendet. | `50` | # @capgo/capacitor-barometer > Zugriff auf barometrischen Druck und Höhendaten von Gerätesensoren für Wetter- und Fitness-Anwendungen. Drucküberwachung Echtzeit-Messungen des atmosphärischen Drucks Höhenberechnung Relative Höhe aus Druckänderungen ableiten Event-Listener Auf Druckänderungsereignisse abonnieren Plattformübergreifend Funktioniert auf iOS- und Android-Geräten mit Barometer-Sensoren Erste Schritte Schauen Sie sich die [Erste Schritte Anleitung](/docs/plugins/barometer/getting-started/) an, um das Plugin zu installieren und zu konfigurieren. # Erste Schritte > Erfahren Sie, wie Sie die Barometer-Messung in Ihre Capacitor App integrieren Diese Anleitung führt Sie durch die Integration des Capacitor Barometer Plugins in Ihre Anwendung. ## Installation [Section titled “Installation”](#installation) Installieren Sie das Plugin mit npm: ```bash npm install @capgo/capacitor-barometer npx cap sync ``` ## iOS-Konfiguration [Section titled “iOS-Konfiguration”](#ios-konfiguration) Fügen Sie Folgendes zu Ihrer `Info.plist` hinzu: ```xml NSMotionUsageDescription Diese App nutzt das Barometer, um den atmosphärischen Druck zu messen ``` ## Android-Konfiguration [Section titled “Android-Konfiguration”](#android-konfiguration) Keine zusätzliche Konfiguration erforderlich. Das Plugin fordert automatisch die notwendigen Berechtigungen an. ## Grundlegende Verwendung [Section titled “Grundlegende Verwendung”](#grundlegende-verwendung) ### Plugin importieren [Section titled “Plugin importieren”](#plugin-importieren) ```typescript import { Barometer } from '@capgo/capacitor-barometer'; ``` ### Sensorverfügbarkeit prüfen [Section titled “Sensorverfügbarkeit prüfen”](#sensorverfügbarkeit-prüfen) ```typescript const checkBarometer = async () => { const { available } = await Barometer.isAvailable(); console.log('Barometer verfügbar:', available); }; ``` ### Drucküberwachung starten [Section titled “Drucküberwachung starten”](#drucküberwachung-starten) ```typescript const startMonitoring = async () => { await Barometer.start({ interval: 1000 // Aktualisierungsintervall in Millisekunden }); console.log('Barometer-Überwachung gestartet'); }; ``` ### Auf Druckereignisse lauschen [Section titled “Auf Druckereignisse lauschen”](#auf-druckereignisse-lauschen) ```typescript Barometer.addListener('pressureChange', (data) => { console.log('Druck (hPa):', data.pressure); console.log('Relative Höhe (m):', data.relativeAltitude); console.log('Zeitstempel:', data.timestamp); }); ``` ### Aktuelle Messung abrufen [Section titled “Aktuelle Messung abrufen”](#aktuelle-messung-abrufen) ```typescript const getCurrentPressure = async () => { const reading = await Barometer.getCurrentReading(); console.log('Aktueller Druck:', reading.pressure, 'hPa'); console.log('Relative Höhe:', reading.relativeAltitude, 'm'); }; ``` ### Überwachung stoppen [Section titled “Überwachung stoppen”](#überwachung-stoppen) ```typescript const stopMonitoring = async () => { await Barometer.stop(); console.log('Barometer-Überwachung gestoppt'); }; ``` ## Vollständiges Beispiel [Section titled “Vollständiges Beispiel”](#vollständiges-beispiel) Hier ist ein vollständiges Beispiel zur Barometer-Integration: ```typescript import { Barometer } from '@capgo/capacitor-barometer'; class BarometerService { private listener: any; async initialize() { // Verfügbarkeit prüfen const { available } = await Barometer.isAvailable(); if (!available) { console.error('Barometer auf diesem Gerät nicht verfügbar'); return; } // Überwachung starten await Barometer.start({ interval: 1000 }); // Listener hinzufügen this.listener = Barometer.addListener('pressureChange', (data) => { this.handlePressureChange(data); }); } handlePressureChange(data: any) { console.log('Druck:', data.pressure, 'hPa'); console.log('Höhe:', data.relativeAltitude, 'm'); // UI aktualisieren oder Daten verarbeiten this.updateDisplay(data); } updateDisplay(data: any) { // hPa in andere Einheiten umrechnen const inHg = (data.pressure * 0.02953).toFixed(2); const mmHg = (data.pressure * 0.750062).toFixed(1); console.log(`Druck: ${data.pressure.toFixed(1)} hPa`); console.log(` ${inHg} inHg`); console.log(` ${mmHg} mmHg`); console.log(`Höhe: ${data.relativeAltitude.toFixed(1)} m`); } async cleanup() { // Listener entfernen if (this.listener) { this.listener.remove(); } // Überwachung stoppen await Barometer.stop(); } } // Verwendung const barometerService = new BarometerService(); barometerService.initialize(); // Aufräumen wenn fertig // barometerService.cleanup(); ``` ## Messwerte verstehen [Section titled “Messwerte verstehen”](#messwerte-verstehen) ### Atmosphärischer Druck [Section titled “Atmosphärischer Druck”](#atmosphärischer-druck) * Gemessen in Hektopascal (hPa) oder Millibar (mb) * Standard-Meeresspiegel-Druck: 1013,25 hPa * Höhere Höhe = niedrigerer Druck * Wettersysteme beeinflussen Druckmessungen ### Relative Höhe [Section titled “Relative Höhe”](#relative-höhe) * Berechnet aus Druckänderungen * Relativ zum Startbezugspunkt * Genauigkeit: ±10-30 Meter * Funktioniert am besten für kurzfristige Verfolgung ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) 1. **Verfügbarkeit prüfen**: Immer Sensorverfügbarkeit vor Verwendung überprüfen 2. **Angemessene Intervalle**: Nicht zu häufig abfragen (1000ms ist gut) 3. **Listener entfernen**: Listener aufräumen, wenn nicht benötigt 4. **Kalibrierung**: Bekannte Höhe für genaue Messungen verwenden 5. **Akkuauswirkung**: Akkuverbrauch bei kontinuierlicher Messung überwachen ## Häufige Probleme [Section titled “Häufige Probleme”](#häufige-probleme) ### Sensor nicht verfügbar [Section titled “Sensor nicht verfügbar”](#sensor-nicht-verfügbar) ```typescript try { const { available } = await Barometer.isAvailable(); if (!available) { // Auf GPS-Höhe oder manuelle Eingabe zurückfallen showAlternativeMethod(); } } catch (error) { console.error('Barometer-Prüfung fehlgeschlagen:', error); } ``` ### Verrauschte Daten [Section titled “Verrauschte Daten”](#verrauschte-daten) ```typescript class PressureFilter { private readings: number[] = []; private maxReadings = 5; addReading(pressure: number): number { this.readings.push(pressure); if (this.readings.length > this.maxReadings) { this.readings.shift(); } // Gleitenden Durchschnitt zurückgeben const sum = this.readings.reduce((a, b) => a + b, 0); return sum / this.readings.length; } } ``` ## Nächste Schritte [Section titled “Nächste Schritte”](#nächste-schritte) * Erkunden Sie die [API-Referenz](https://github.com/Cap-go/capacitor-barometer#api) für die vollständige Methodendokumentation * Schauen Sie sich die [Beispiel-App](https://github.com/Cap-go/capacitor-barometer/tree/main/example) für erweiterte Verwendung an * Sehen Sie das [Tutorial](/plugins/capacitor-barometer) für vollständige Implementierungsbeispiele # @capgo/camera-preview > Zeigen Sie Kameravorschau in Ihrer App mit voller Kontrolle über Kameraeinstellungen an, nehmen Sie Fotos und Videos direkt aus der Vorschau auf. Echtzeit-Vorschau Zeigen Sie Live-Kamera-Feed direkt in Ihrer App-UI an Volle Kontrolle Steuern Sie Blitz, Zoom, Fokus und wechseln Sie zwischen Kameras Aufnahmefähigkeiten Machen Sie Fotos und nehmen Sie Videos aus der Vorschau auf Umfassende Dokumentation Schauen Sie sich die [Dokumentation](/docs/plugins/camera-preview/getting-started/) an, um das Plugin in nur wenigen Minuten zu meistern. # Erste Schritte > Erfahren Sie, wie Sie das Camera Preview Plugin installieren und verwenden, um Echtzeit-Kamera-Feeds anzuzeigen und Fotos/Videos in Ihrer Capacitor App aufzunehmen. 1. **Paket installieren** * npm ```sh npm i @capgo/camera-preview ``` * pnpm ```sh pnpm add @capgo/camera-preview ``` * yarn ```sh yarn add @capgo/camera-preview ``` * bun ```sh bun add @capgo/camera-preview ``` 2. **Mit nativen Projekten synchronisieren** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` 3. **Berechtigungen konfigurieren** ### iOS [Section titled “iOS”](#ios) Fügen Sie die Kamera-Nutzungsbeschreibung zu Ihrer `Info.plist` hinzu: ```xml NSCameraUsageDescription Um Fotos und Videos aufzunehmen NSMicrophoneUsageDescription Um Audio mit Videos aufzunehmen NSPhotoLibraryUsageDescription Um Fotos und Videos zu speichern ``` ### Android [Section titled “Android”](#android) Fügen Sie Kamera-Berechtigungen zu Ihrer `AndroidManifest.xml` hinzu: ```xml ``` ## Verwendung [Section titled “Verwendung”](#verwendung) Importieren Sie das Plugin und verwenden Sie seine Methoden zur Steuerung der Kamera: ```typescript import { CameraPreview } from '@capgo/camera-preview'; import { CameraPreviewOptions, CameraPreviewPictureOptions } from '@capgo/camera-preview'; // Kameravorschau starten const startCamera = async () => { const cameraPreviewOptions: CameraPreviewOptions = { position: 'rear', height: 1920, width: 1080, quality: 50, toBack: false, paddingBottom: 0, rotateWhenOrientationChanged: true, x: 0, y: 0 }; await CameraPreview.start(cameraPreviewOptions); }; // Kameravorschau stoppen const stopCamera = async () => { await CameraPreview.stop(); }; // Foto aufnehmen const takePicture = async () => { const pictureOptions: CameraPreviewPictureOptions = { quality: 90, width: 1920, height: 1080 }; const result = await CameraPreview.capture(pictureOptions); const base64PictureData = result.value; // Base64-Bilddaten verwenden console.log(base64PictureData); }; // Kamera wechseln const flipCamera = async () => { await CameraPreview.flip(); }; // Blitz steuern const setFlashMode = async (mode: 'on' | 'off' | 'auto' | 'torch') => { await CameraPreview.setFlashMode({ flashMode: mode }); }; // Videoaufnahme starten const startRecording = async () => { await CameraPreview.startRecordVideo({ storeToFile: true, fileName: 'myvideo' }); }; // Aufnahme stoppen const stopRecording = async () => { const result = await CameraPreview.stopRecordVideo(); console.log('Videopfad:', result.videoFilePath); }; ``` ## API-Referenz [Section titled “API-Referenz”](#api-referenz) ### start(options) [Section titled “start(options)”](#startoptions) Startet die Kameravorschau. ```typescript interface CameraPreviewOptions { position?: 'rear' | 'front'; height?: number; width?: number; x?: number; y?: number; toBack?: boolean; paddingBottom?: number; rotateWhenOrientationChanged?: boolean; storeToFile?: boolean; disableExifHeaderStripping?: boolean; enableHighResolution?: boolean; disableAudio?: boolean; lockAndroidOrientation?: boolean; enableOpacity?: boolean; enableZoom?: boolean; } ``` ### stop() [Section titled “stop()”](#stop) Stoppt die Kameravorschau. ### capture(options) [Section titled “capture(options)”](#captureoptions) Macht ein Foto und gibt es als Base64-codierten String zurück. ```typescript interface CameraPreviewPictureOptions { height?: number; width?: number; quality?: number; } ``` ### flip() [Section titled “flip()”](#flip) Wechselt zwischen Front- und Rückkamera. ### setFlashMode(options) [Section titled “setFlashMode(options)”](#setflashmodeoptions) Setzt den Blitzmodus. ```typescript interface CameraPreviewFlashMode { flashMode: 'off' | 'on' | 'auto' | 'torch'; } ``` ### startRecordVideo(options) [Section titled “startRecordVideo(options)”](#startrecordvideooptions) Startet die Videoaufnahme. ```typescript interface CameraPreviewVideoOptions { storeToFile?: boolean; fileName?: string; width?: number; height?: number; quality?: number; withFlash?: boolean; } ``` ### stopRecordVideo() [Section titled “stopRecordVideo()”](#stoprecordvideo) Stoppt die Videoaufnahme und gibt den Videopfad zurück. ## Erweiterte Funktionen [Section titled “Erweiterte Funktionen”](#erweiterte-funktionen) ### Fokussteuerung [Section titled “Fokussteuerung”](#fokussteuerung) ```typescript // Fokus setzen await CameraPreview.setFocusCoordinate({ x: 100, y: 100 }); // Unterstützte Fokusmodi abrufen const focusModes = await CameraPreview.getFocusModes(); // Fokusmodus setzen await CameraPreview.setFocusMode({ focusMode: 'continuous-picture' }); ``` ### Zoomsteuerung [Section titled “Zoomsteuerung”](#zoomsteuerung) ```typescript // Maximalen Zoom abrufen const maxZoom = await CameraPreview.getMaxZoom(); // Zoom setzen await CameraPreview.setZoom({ zoom: 2.0 }); // Aktuellen Zoom abrufen const currentZoom = await CameraPreview.getZoom(); ``` ### Belichtungssteuerung [Section titled “Belichtungssteuerung”](#belichtungssteuerung) ```typescript // Belichtungskorrekturbereich abrufen const range = await CameraPreview.getExposureCompensationRange(); // Belichtungskorrektur setzen await CameraPreview.setExposureCompensation({ exposureCompensation: 0.5 }); ``` ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) 1. **Berechtigungen zuerst anfordern** ```typescript import { Camera } from '@capacitor/camera'; const requestPermissions = async () => { const permissions = await Camera.requestPermissions(); if (permissions.camera === 'granted') { // Kameravorschau starten } }; ``` 2. **Orientierungsänderungen handhaben** Setzen Sie `rotateWhenOrientationChanged: true`, um die Vorschau automatisch anzupassen. 3. **Leistung optimieren** * Verwenden Sie angemessene Auflösungseinstellungen * Stoppen Sie die Vorschau, wenn sie nicht verwendet wird * Deaktivieren Sie Funktionen, die Sie nicht benötigen 4. **Fehlerbehandlung** ```typescript try { await CameraPreview.start(options); } catch (error) { console.error('Kamera konnte nicht gestartet werden:', error); } ``` ## Plattform-Hinweise [Section titled “Plattform-Hinweise”](#plattform-hinweise) ### iOS [Section titled “iOS”](#ios-1) * Erfordert iOS 10.0+ * Verwendet AVFoundation Framework * Hardware-Beschleunigung unterstützt ### Android [Section titled “Android”](#android-1) * Erfordert Android 5.0 (API 21)+ * Verwendet Camera2 API * Unterstützt verschiedene Kamerafunktionen basierend auf Gerätefähigkeiten # Capacitor+ > Capacitor+ ist ein automatisierter Fork von Capacitor, der Community-PRs schneller merged, mit sicherheitsgeprüften Releases. Tip **Warum Capacitor+?** Großartige PRs liegen monatelang ungemergt im offiziellen Capacitor-Repository. Capacitor+ merged diese Community-Beiträge aktiv, damit Sie nicht warten müssen. Community-PRs schneller gemergt Bug-Fixes und Features stecken in Upstream fest? Wir mergen sie, damit Sie sofort profitieren können. Täglich automatisch synchronisiert Jede Änderung vom offiziellen Capacitor wird automatisch abgerufen, getestet und verifiziert. Sicherheitsgeprüft Jedes Release wird von KI auf Sicherheitslücken, Breaking Changes und Stabilitätsrisiken analysiert. Drop-in-Ersatz Gleiche API wie offizielles Capacitor - ändern Sie einfach den Paket-Scope und fertig. ## Philosophie [Section titled “Philosophie”](#philosophie) Das Ionic-Team wartet Capacitor mit ihren eigenen Prioritäten und Release-Zeitplänen. Community-Beiträge können Monate oder sogar Jahre warten, bis sie gemergt werden. Capacitor+ verfolgt einen anderen Ansatz: 1. **PRs von Forks mergen** - Wertvolle PRs, die in der Upstream-Warteschlange feststecken, werden aktiv gemergt 2. **Kontinuierliche Synchronisierung** - Jede Upstream-Änderung wird automatisch abgerufen und getestet 3. **Schnelle Releases** - Änderungen werden auf npm veröffentlicht, sobald sie CI bestehen 4. **Community-First** - Ihre Beiträge zählen und werden priorisiert 5. **Transparenz** - Alle Automatisierung ist Open Source und sichtbar ## Pakete [Section titled “Pakete”](#pakete) | Paket | npm | | ------------------------- | --------------------------------------------------------------------------------------------------------------------- | | `@capacitor-plus/core` | [![npm](https://img.shields.io/npm/v/@capacitor-plus/core)](https://www.npmjs.com/package/@capacitor-plus/core) | | `@capacitor-plus/cli` | [![npm](https://img.shields.io/npm/v/@capacitor-plus/cli)](https://www.npmjs.com/package/@capacitor-plus/cli) | | `@capacitor-plus/android` | [![npm](https://img.shields.io/npm/v/@capacitor-plus/android)](https://www.npmjs.com/package/@capacitor-plus/android) | | `@capacitor-plus/ios` | [![npm](https://img.shields.io/npm/v/@capacitor-plus/ios)](https://www.npmjs.com/package/@capacitor-plus/ios) | # Erste Schritte > Erfahren Sie, wie Sie Capacitor+ als Drop-in-Ersatz für offizielle Capacitor-Pakete installieren. ## Installation für neue Projekte [Section titled “Installation für neue Projekte”](#installation-für-neue-projekte) 1. **Kernpakete installieren** ```bash npm install @capacitor-plus/core @capacitor-plus/cli ``` 2. **Plattformpakete hinzufügen** ```bash npm install @capacitor-plus/android # für Android npm install @capacitor-plus/ios # für iOS ``` 3. **Capacitor initialisieren** * npm ```sh npx cap init ``` * pnpm ```sh pnpm cap init ``` * yarn ```sh yarn cap init ``` * bun ```sh bunx cap init ``` 4. **Plattformen hinzufügen** * npm ```sh npx cap add android ``` * pnpm ```sh pnpm cap add android ``` * yarn ```sh yarn cap add android ``` * bun ```sh bunx cap add android ``` - npm ```sh npx cap add ios ``` - pnpm ```sh pnpm cap add ios ``` - yarn ```sh yarn cap add ios ``` - bun ```sh bunx cap add ios ``` ## Migration von offiziellem Capacitor [Section titled “Migration von offiziellem Capacitor”](#migration-von-offiziellem-capacitor) Wenn Sie ein bestehendes Capacitor-Projekt haben, ist die Migration zu Capacitor+ einfach: 1. **Offizielle Pakete entfernen** ```bash npm uninstall @capacitor/core @capacitor/cli @capacitor/android @capacitor/ios ``` 2. **Capacitor+ Pakete installieren** ```bash npm install @capacitor-plus/core @capacitor-plus/cli npm install @capacitor-plus/android # wenn Android verwendet wird npm install @capacitor-plus/ios # wenn iOS verwendet wird ``` 3. **Projekt synchronisieren** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` Note Keine Code-Änderungen erforderlich! Capacitor+ hat die gleiche API wie offizielles Capacitor. Ihre Imports und Ihr Code bleiben genau gleich. ## Verwendung [Section titled “Verwendung”](#verwendung) Da Capacitor+ API-kompatibel ist, funktioniert Ihr bestehender Code ohne Änderungen: ```typescript import { Capacitor } from '@capacitor/core'; import { registerPlugin } from '@capacitor/core'; // Plattform prüfen const platform = Capacitor.getPlatform(); console.log('Läuft auf:', platform); // Prüfen ob nativ if (Capacitor.isNativePlatform()) { console.log('Läuft auf nativer Plattform'); } // Eigenes Plugin registrieren const MyPlugin = registerPlugin('MyPlugin'); ``` ### Mit offiziellen Capacitor Plugins [Section titled “Mit offiziellen Capacitor Plugins”](#mit-offiziellen-capacitor-plugins) Alle offiziellen Capacitor Plugins funktionieren nahtlos: ```typescript import { Camera, CameraResultType } from '@capacitor/camera'; import { Geolocation } from '@capacitor/geolocation'; import { Storage } from '@capacitor/preferences'; // Kamera const photo = await Camera.getPhoto({ quality: 90, resultType: CameraResultType.Uri }); // Geolokalisierung const position = await Geolocation.getCurrentPosition(); // Speicher await Storage.set({ key: 'name', value: 'John' }); ``` ### Mit Capgo Plugins [Section titled “Mit Capgo Plugins”](#mit-capgo-plugins) Capgo Plugins funktionieren perfekt mit Capacitor+: ```typescript import { CapacitorUpdater } from '@capgo/capacitor-updater'; import { ScreenOrientation } from '@capgo/capacitor-screen-orientation'; import { CapacitorFlash } from '@capgo/capacitor-flash'; // Live-Updates await CapacitorUpdater.notifyAppReady(); // Bildschirmorientierung await ScreenOrientation.lock({ orientation: 'portrait' }); // Taschenlampe await CapacitorFlash.toggle(); ``` ## Wie die Synchronisierung funktioniert [Section titled “Wie die Synchronisierung funktioniert”](#wie-die-synchronisierung-funktioniert) ```plaintext ┌─────────────────────┐ ┌──────────────────┐ ┌──────────────────┐ ┌─────────────────┐ │ ionic-team/ │ │ CI/CD │ │ Claude Code │ │ npm publish │ │ capacitor │────▶│ Pipeline │────▶│ Sicherheitsprüfung │────▶│ @capacitor-plus│ │ (upstream) │ │ (täglich sync) │ │ (AI-Analyse) │ │ Pakete │ └─────────────────────┘ └──────────────────┘ └──────────────────┘ └─────────────────┘ ``` 1. **Tägliche Synchronisierung**: GitHub Actions holen die neuesten Änderungen von `ionic-team/capacitor` 2. **PR-Erstellung**: Änderungen werden als Pull Requests für den `plus` Branch vorgeschlagen 3. **CI-Validierung**: Vollständige Test-Suite läuft (Lint, Unit-Tests, iOS-Build, Android-Build) 4. **Sicherheitsprüfung**: KI-gestützte Analyse prüft auf Schwachstellen und Breaking Changes 5. **Auto-Merge**: Nur wenn CI besteht UND Sicherheitsprüfung genehmigt 6. **Auto-Publish**: Neue Version wird auf npm unter `@capacitor-plus/*` veröffentlicht ## Details zur Sicherheitsprüfung [Section titled “Details zur Sicherheitsprüfung”](#details-zur-sicherheitsprüfung) Jede Upstream-Synchronisierung wird analysiert auf: | Prüfung | Was erkannt wird | | -------------------- | -------------------------------------------------------------------------------- | | **Sicherheit** | Command Injection, XSS, Path Traversal, hardcodierte Secrets | | **Breaking Changes** | Entfernte/umbenannte APIs, geänderte Signaturen, Konfigurationsänderungen | | **Stabilität** | Null-Dereferenzierungen, unbehandelte Exceptions, Race Conditions, Speicherlecks | | **Datensicherheit** | Datenverlust-Szenarien, Datenschutzverletzungen, unsichere Speicherung | | **Code-Integrität** | Verschleierter Code, verdächtige Netzwerkaufrufe, Backdoors | Caution Wenn Probleme erkannt werden, wird der PR zur manuellen Überprüfung markiert und wird NICHT automatisch gemergt. Dies stellt sicher, dass Sie immer stabile, sichere Releases erhalten. ## Ihren PR einreichen [Section titled “Ihren PR einreichen”](#ihren-pr-einreichen) Haben Sie einen PR, der im offiziellen Capacitor-Repo feststeckt? Lassen Sie ihn in Capacitor+ mergen: 1. **Öffnen Sie ein Issue** im [Capacitor+ Repo](https://github.com/Cap-go/capacitor-plus/issues) mit Link zu Ihrem Upstream-PR 2. **Oder reichen Sie direkt** als PR für den `plus` Branch ein 3. Das Team wird überprüfen, CI ausführen und mergen, wenn es besteht Auf diese Weise können Sie und andere sofort von Ihrer Arbeit profitieren, ohne auf den Upstream-Release-Zyklus zu warten. ## FAQ [Section titled “FAQ”](#faq) ### Ist das produktionsreif? [Section titled “Ist das produktionsreif?”](#ist-das-produktionsreif) Ja. Capacitor+ wird in Produktions-Apps verwendet. Jedes Release besteht die gleiche Test-Suite wie offizielles Capacitor, plus zusätzliche Sicherheitsanalyse. ### Werden meine offiziellen Plugins noch funktionieren? [Section titled “Werden meine offiziellen Plugins noch funktionieren?”](#werden-meine-offiziellen-plugins-noch-funktionieren) Ja. Alle `@capacitor/*` Plugins funktionieren out of the box mit Capacitor+. ### Was passiert, wenn Upstream einen Breaking Change released? [Section titled “Was passiert, wenn Upstream einen Breaking Change released?”](#was-passiert-wenn-upstream-einen-breaking-change-released) Die KI-Sicherheitsprüfung markiert Breaking Changes zur manuellen Überprüfung. Sie sehen die Änderungen dokumentiert, bevor sie gemergt werden. ### Wie melde ich Probleme? [Section titled “Wie melde ich Probleme?”](#wie-melde-ich-probleme) Melden Sie Probleme im [Capacitor+ GitHub Repo](https://github.com/Cap-go/capacitor-plus/issues). Für Probleme, die auch offizielles Capacitor betreffen, helfen wir bei der Koordination mit Upstream. ### Kann ich beitragen? [Section titled “Kann ich beitragen?”](#kann-ich-beitragen) Absolut! PRs sind willkommen. Sie können Fixes direkt einreichen oder anfordern, dass spezifische Upstream-PRs gemergt werden. # @capgo/capacitor-compass > Greifen Sie auf den Kompass-Sensor des Geräts zu, um Richtungsdaten mit Echtzeit-Updates auf iOS und Android zu erhalten. Echtzeit-Richtung Erhalten Sie kontinuierliche Kompass-Richtungsupdates in Grad (0-360) Cross-Plattform Funktioniert auf iOS- und Android-Geräten mit nativem Sensor-Zugriff Berechtigungsverwaltung Integrierte Berechtigungsverwaltung für Ortungsdienste (iOS) Ereignisgesteuert Abonnieren Sie Richtungsänderungen mit effizienten Event-Listenern # Erste Schritte > Erfahren Sie, wie Sie das Compass Plugin installieren und verwenden, um die Gerätekompassrichtung in Ihrer Capacitor App auszulesen. 1. **Paket installieren** * npm ```sh npm i @capgo/capacitor-compass ``` * pnpm ```sh pnpm add @capgo/capacitor-compass ``` * yarn ```sh yarn add @capgo/capacitor-compass ``` * bun ```sh bun add @capgo/capacitor-compass ``` 2. **Mit nativen Projekten synchronisieren** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## iOS-Einrichtung [Section titled “iOS-Einrichtung”](#ios-einrichtung) Unter iOS erfordert der Kompasszugriff Standortberechtigung. Fügen Sie Folgendes zu Ihrer `Info.plist` hinzu: ```xml NSLocationWhenInUseUsageDescription Wir benötigen Standortberechtigung, um auf den Kompass zuzugreifen ``` ## Android-Einrichtung [Section titled “Android-Einrichtung”](#android-einrichtung) Keine zusätzliche Einrichtung erforderlich. Das Plugin verwendet die Magnetometer- und Beschleunigungssensoren des Geräts. ## Verwendung [Section titled “Verwendung”](#verwendung) Importieren Sie das Plugin und verwenden Sie seine Methoden zum Auslesen der Kompassrichtung: ```typescript import { CapgoCompass } from '@capgo/capacitor-compass'; // Aktuelle Richtung einmalig abrufen const getCurrentHeading = async () => { const { value } = await CapgoCompass.getCurrentHeading(); console.log('Aktuelle Richtung:', value, 'Grad'); }; // Kontinuierliche Richtungsaktualisierungen starten const startCompass = async () => { // Lauschen auf Updates starten await CapgoCompass.startListening(); // Listener für Richtungsänderungen hinzufügen const handle = await CapgoCompass.addListener('headingChange', (event) => { console.log('Richtung:', event.value, 'Grad'); }); // Später zum Stoppen: // await CapgoCompass.stopListening(); // await handle.remove(); }; // Berechtigungen prüfen const checkPermission = async () => { const status = await CapgoCompass.checkPermissions(); console.log('Berechtigungsstatus:', status.compass); }; // Berechtigungen anfordern const requestPermission = async () => { const status = await CapgoCompass.requestPermissions(); if (status.compass === 'granted') { console.log('Kompasszugriff gewährt'); } }; ``` ## API-Referenz [Section titled “API-Referenz”](#api-referenz) ### getCurrentHeading() [Section titled “getCurrentHeading()”](#getcurrentheading) Ruft die aktuelle Kompassrichtung in Grad ab. ```typescript const result = await CapgoCompass.getCurrentHeading(); // Gibt zurück: { value: number } - Richtung in Grad (0-360) ``` ### startListening() [Section titled “startListening()”](#startlistening) Startet das Lauschen auf Kompassrichtungsänderungen. Muss aufgerufen werden, bevor Richtungsereignisse ausgegeben werden. ```typescript await CapgoCompass.startListening(); ``` ### stopListening() [Section titled “stopListening()”](#stoplistening) Stoppt das Lauschen auf Kompassrichtungsänderungen. ```typescript await CapgoCompass.stopListening(); ``` ### addListener(‘headingChange’, callback) [Section titled “addListener(‘headingChange’, callback)”](#addlistenerheadingchange-callback) Fügt einen Listener für Richtungsänderungsereignisse hinzu. ```typescript const handle = await CapgoCompass.addListener('headingChange', (event) => { console.log('Richtung:', event.value); }); // Listener entfernen wenn fertig await handle.remove(); ``` ### removeAllListeners() [Section titled “removeAllListeners()”](#removealllisteners) Entfernt alle registrierten Listener. ```typescript await CapgoCompass.removeAllListeners(); ``` ### checkPermissions() [Section titled “checkPermissions()”](#checkpermissions) Prüft den aktuellen Berechtigungsstatus. ```typescript const status = await CapgoCompass.checkPermissions(); // Gibt zurück: { compass: 'prompt' | 'granted' | 'denied' } ``` ### requestPermissions() [Section titled “requestPermissions()”](#requestpermissions) Fordert Berechtigung zum Zugriff auf Kompassdaten an. ```typescript const status = await CapgoCompass.requestPermissions(); ``` ### getPluginVersion() [Section titled “getPluginVersion()”](#getpluginversion) Ruft die native Plugin-Version ab. ```typescript const { version } = await CapgoCompass.getPluginVersion(); ``` ## Vollständiges Beispiel [Section titled “Vollständiges Beispiel”](#vollständiges-beispiel) ```typescript import { CapgoCompass } from '@capgo/capacitor-compass'; export class CompassService { private listenerHandle: any = null; async init() { // Berechtigungen prüfen und anfordern const status = await CapgoCompass.checkPermissions(); if (status.compass !== 'granted') { const result = await CapgoCompass.requestPermissions(); if (result.compass !== 'granted') { throw new Error('Kompassberechtigung verweigert'); } } } async startTracking(onHeadingChange: (heading: number) => void) { // Lauschen auf Updates starten await CapgoCompass.startListening(); // Event-Listener hinzufügen this.listenerHandle = await CapgoCompass.addListener( 'headingChange', (event) => { onHeadingChange(event.value); } ); } async stopTracking() { if (this.listenerHandle) { await this.listenerHandle.remove(); this.listenerHandle = null; } await CapgoCompass.stopListening(); } async getHeading(): Promise { const { value } = await CapgoCompass.getCurrentHeading(); return value; } getCardinalDirection(heading: number): string { const directions = ['N', 'NO', 'O', 'SO', 'S', 'SW', 'W', 'NW']; const index = Math.round(heading / 45) % 8; return directions[index]; } } ``` ## Plattform-Hinweise [Section titled “Plattform-Hinweise”](#plattform-hinweise) ### iOS [Section titled “iOS”](#ios) * Erfordert iOS 10.0+ * Verwendet Core Location für Richtungsdaten * Erfordert Standortberechtigung (NSLocationWhenInUseUsageDescription) * Richtungsaktualisierungen sind kontinuierlich, wenn Lauschen aktiv ist ### Android [Section titled “Android”](#android) * Erfordert Android 6.0 (API 23)+ * Verwendet Beschleunigungs- und Magnetometersensoren * Keine speziellen Berechtigungen für Kompasssensoren erforderlich * Richtung wird aus Sensorfusion berechnet ### Web [Section titled “Web”](#web) * Nicht auf Web-Plattform unterstützt * Methoden werfen Fehler, wenn aufgerufen # @capgo/capacitor-contacts > Greifen Sie auf Gerätekontakte zu, durchsuchen und verwalten Sie sie in Ihrer Capacitor-App mit vollständigen CRUD-Operationen und Berechtigungsverwaltung. Kontakte lesen Zugriff auf Gerätekontaktliste mit vollständigen Details Suchfunktionalität Finden Sie Kontakte nach Name, E-Mail oder Telefon Erstellen & Aktualisieren Fügen Sie neue Kontakte hinzu oder ändern Sie bestehende Kontakte löschen Entfernen Sie Kontakte sicher vom Gerät Berechtigungsverwaltung Behandeln Sie Kontaktberechtigungen ordnungsgemäß mit Privacy Manifest-Konformität Erste Schritte Schauen Sie sich die [Anleitung für erste Schritte](/docs/plugins/contacts/getting-started/) an, um das Plugin zu installieren und zu konfigurieren. # Erste Schritte mit Contacts > Erfahren Sie, wie Sie die Kontaktverwaltung in Ihre Capacitor-App integrieren Diese Anleitung führt Sie durch die Integration des Capacitor Contacts Plugins in Ihre Anwendung. ## Installation [Section titled “Installation”](#installation) Installieren Sie das Plugin mit npm: ```bash npm install @capgo/capacitor-contacts npx cap sync ``` ## iOS Konfiguration [Section titled “iOS Konfiguration”](#ios-konfiguration) Fügen Sie Folgendes zu Ihrer `Info.plist` hinzu: ```xml NSContactsUsageDescription Diese App benötigt Zugriff auf Kontakte, damit Sie Empfänger auswählen können ``` ## Android Konfiguration [Section titled “Android Konfiguration”](#android-konfiguration) Fügen Sie die folgenden Berechtigungen zu Ihrer `AndroidManifest.xml` hinzu: ```xml ``` ## Grundlegende Verwendung [Section titled “Grundlegende Verwendung”](#grundlegende-verwendung) ### Plugin importieren [Section titled “Plugin importieren”](#plugin-importieren) ```typescript import { Contacts } from '@capgo/capacitor-contacts'; ``` ### Berechtigungen anfordern [Section titled “Berechtigungen anfordern”](#berechtigungen-anfordern) ```typescript const requestPermissions = async () => { const permission = await Contacts.requestPermissions(); console.log('Berechtigungsstatus:', permission.contacts); }; ``` ### Alle Kontakte abrufen [Section titled “Alle Kontakte abrufen”](#alle-kontakte-abrufen) ```typescript const getAllContacts = async () => { const result = await Contacts.getContacts({ projection: { name: true, phones: true, emails: true, image: true } }); console.log('Kontakte:', result.contacts); }; ``` ### Kontakte suchen [Section titled “Kontakte suchen”](#kontakte-suchen) ```typescript const searchContacts = async (query: string) => { const result = await Contacts.getContacts({ projection: { name: true, phones: true, emails: true }, query: query }); console.log('Suchergebnisse:', result.contacts); }; ``` ### Kontakt erstellen [Section titled “Kontakt erstellen”](#kontakt-erstellen) ```typescript const createContact = async () => { const newContact = { name: { given: 'John', family: 'Doe' }, phones: [{ type: 'mobile', number: '+1234567890' }], emails: [{ type: 'work', address: 'john.doe@example.com' }] }; const result = await Contacts.createContact(newContact); console.log('Kontakt erstellt:', result); }; ``` ### Kontakt aktualisieren [Section titled “Kontakt aktualisieren”](#kontakt-aktualisieren) ```typescript const updateContact = async (contactId: string) => { const updates = { contactId: contactId, name: { given: 'Jane', family: 'Doe' } }; await Contacts.updateContact(updates); console.log('Kontakt aktualisiert'); }; ``` ### Kontakt löschen [Section titled “Kontakt löschen”](#kontakt-löschen) ```typescript const deleteContact = async (contactId: string) => { await Contacts.deleteContact({ contactId }); console.log('Kontakt gelöscht'); }; ``` ## Vollständiges Beispiel [Section titled “Vollständiges Beispiel”](#vollständiges-beispiel) Hier ist ein vollständiges Beispiel mit einem Kontakt-Service: ```typescript import { Contacts } from '@capgo/capacitor-contacts'; interface Contact { contactId: string; name: { display?: string; given?: string; family?: string; }; phones?: Array<{ type: string; number: string }>; emails?: Array<{ type: string; address: string }>; image?: { base64String: string }; } class ContactsService { async checkPermissions(): Promise { const permission = await Contacts.checkPermissions(); if (permission.contacts === 'granted') { return true; } const requested = await Contacts.requestPermissions(); return requested.contacts === 'granted'; } async getAllContacts(): Promise { const hasPermission = await this.checkPermissions(); if (!hasPermission) { throw new Error('Kontaktberechtigung verweigert'); } const result = await Contacts.getContacts({ projection: { name: true, phones: true, emails: true, image: true } }); return result.contacts; } async searchContacts(query: string): Promise { if (!query || query.length < 2) { return []; } const result = await Contacts.getContacts({ projection: { name: true, phones: true, emails: true }, query: query }); return result.contacts; } async getContactById(contactId: string): Promise { const result = await Contacts.getContacts({ projection: { name: true, phones: true, emails: true, image: true, organization: true, birthday: true, note: true, urls: true, postalAddresses: true }, contactId: contactId }); return result.contacts.length > 0 ? result.contacts[0] : null; } async createContact(contact: Partial): Promise { const hasPermission = await this.checkPermissions(); if (!hasPermission) { throw new Error('Kontaktberechtigung verweigert'); } const result = await Contacts.createContact(contact); return result.contactId; } async updateContact(contactId: string, updates: Partial): Promise { await Contacts.updateContact({ contactId, ...updates }); } async deleteContact(contactId: string): Promise { await Contacts.deleteContact({ contactId }); } formatPhoneNumber(phone: string): string { // Nicht-numerische Zeichen entfernen const cleaned = phone.replace(/\D/g, ''); // Als (XXX) XXX-XXXX formatieren if (cleaned.length === 10) { return `(${cleaned.slice(0, 3)}) ${cleaned.slice(3, 6)}-${cleaned.slice(6)}`; } return phone; } getContactInitials(contact: Contact): string { const given = contact.name?.given || ''; const family = contact.name?.family || ''; if (given && family) { return `${given[0]}${family[0]}`.toUpperCase(); } const display = contact.name?.display || ''; const parts = display.split(' '); if (parts.length >= 2) { return `${parts[0][0]}${parts[1][0]}`.toUpperCase(); } return display.slice(0, 2).toUpperCase(); } } // Verwendung const contactsService = new ContactsService(); // Alle Kontakte abrufen const contacts = await contactsService.getAllContacts(); console.log('Kontakte:', contacts); // Kontakte suchen const results = await contactsService.searchContacts('john'); console.log('Suchergebnisse:', results); // Kontakt erstellen const newContactId = await contactsService.createContact({ name: { given: 'Jane', family: 'Smith' }, phones: [{ type: 'mobile', number: '+1234567890' }] }); // Kontakt aktualisieren await contactsService.updateContact(newContactId, { emails: [{ type: 'work', address: 'jane@example.com' }] }); ``` ## Projektion verstehen [Section titled “Projektion verstehen”](#projektion-verstehen) Der Parameter `projection` steuert, welche Felder abgerufen werden: ```typescript const result = await Contacts.getContacts({ projection: { name: true, // Kontaktname phones: true, // Telefonnummern emails: true, // E-Mail-Adressen image: true, // Kontaktfoto organization: true, // Firma/Organisation birthday: true, // Geburtsdatum note: true, // Notizen urls: true, // Webseiten postalAddresses: true // Postanschriften } }); ``` **Tipp**: Fordern Sie nur die Felder an, die Sie tatsächlich benötigen, um die Leistung zu verbessern. ## Kontaktauswahl-UI [Section titled “Kontaktauswahl-UI”](#kontaktauswahl-ui) Viele Apps bevorzugen die native Kontaktauswahl: ```typescript const pickContact = async () => { try { const result = await Contacts.pickContact({ projection: { name: true, phones: true, emails: true } }); if (result.contacts.length > 0) { const contact = result.contacts[0]; console.log('Ausgewählter Kontakt:', contact); } } catch (error) { console.error('Kontaktauswahl abgebrochen oder fehlgeschlagen:', error); } }; ``` ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) 1. **Minimale Berechtigungen anfordern**: Fordern Sie Kontaktberechtigung nur bei Bedarf an 2. **Projektion verwenden**: Rufen Sie nur die Felder ab, die Sie tatsächlich verwenden 3. **Ablehnungen behandeln**: Bieten Sie eine Alternative, wenn Berechtigungen verweigert werden 4. **Weise zwischenspeichern**: Kontakte können sich ändern, speichern Sie nicht zu lange zwischen 5. **Privatsphäre respektieren**: Seien Sie transparent über die Kontaktnutzung 6. **Asynchrone Operationen**: Alle Kontaktoperationen sind asynchron ## Häufige Probleme [Section titled “Häufige Probleme”](#häufige-probleme) ### Berechtigung verweigert [Section titled “Berechtigung verweigert”](#berechtigung-verweigert) ```typescript const handlePermissionDenied = async () => { const permission = await Contacts.checkPermissions(); if (permission.contacts === 'denied') { // Dialog anzeigen, der erklärt, warum die Berechtigung benötigt wird showPermissionDialog(); // Benutzer zu den Einstellungen leiten // Auf iOS: Einstellungen > [App] > Kontakte // Auf Android: Einstellungen > Apps > [App] > Berechtigungen } }; ``` ### Große Kontaktlisten [Section titled “Große Kontaktlisten”](#große-kontaktlisten) ```typescript const loadContactsInBatches = async () => { // Zuerst Anzahl abrufen (leichtgewichtig) const projection = { name: true }; // Minimale Projektion // Paginierung implementieren, falls nötig const allContacts = await Contacts.getContacts({ projection }); // In Blöcken verarbeiten const chunkSize = 100; for (let i = 0; i < allContacts.contacts.length; i += chunkSize) { const chunk = allContacts.contacts.slice(i, i + chunkSize); await processContactChunk(chunk); } }; ``` ## Nächste Schritte [Section titled “Nächste Schritte”](#nächste-schritte) * Erkunden Sie die [API-Referenz](https://github.com/Cap-go/capacitor-contacts#api) für vollständige Methodendokumentation * Schauen Sie sich die [Beispiel-App](https://github.com/Cap-go/capacitor-contacts/tree/main/example) für erweiterte Verwendung an * Sehen Sie sich das [Tutorial](/plugins/capacitor-contacts) für vollständige Implementierungsbeispiele an # @capgo/capacitor-crisp > Integrieren Sie das native Crisp Chat SDK in Ihre Capacitor-App für nahtlosen In-App-Kundensupport und Echtzeit-Messaging. Native Performance Native SDK-Integration für reibungslose Chat-Erfahrung Echtzeit-Messaging Live-Chat mit Kunden direkt in Ihrer App Umfangreiche Funktionen Unterstützung für Benutzerdaten, Ereignisse und benutzerdefiniertes Styling Umfassende Dokumentation Schauen Sie sich die [Dokumentation](/docs/plugins/crisp/getting-started/) an, um das Plugin in nur wenigen Minuten zu beherrschen. # Erste Schritte > Erfahren Sie, wie Sie das Crisp Chat SDK Plugin für In-App-Kundensupport in Ihrer Capacitor-App installieren und verwenden. 1. **Paket installieren** * npm ```sh npm i @capgo/capacitor-crisp ``` * pnpm ```sh pnpm add @capgo/capacitor-crisp ``` * yarn ```sh yarn add @capgo/capacitor-crisp ``` * bun ```sh bun add @capgo/capacitor-crisp ``` 2. **Mit nativen Projekten synchronisieren** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` 3. **Ihre Crisp Website-ID abrufen** * Melden Sie sich bei [crisp.chat](https://crisp.chat) an * Erstellen Sie eine Website/ein Projekt * Finden Sie Ihre Website-ID unter Einstellungen > Setup-Anleitung ## Grundlegende Verwendung [Section titled “Grundlegende Verwendung”](#grundlegende-verwendung) Importieren Sie das Plugin und konfigurieren Sie es mit Ihrer Website-ID: ```typescript import { CapacitorCrisp } from '@capgo/capacitor-crisp'; // Crisp mit Ihrer Website-ID konfigurieren const configureCrisp = async () => { await CapacitorCrisp.configure({ websiteID: 'YOUR_WEBSITE_ID' }); }; // Den Chat öffnen const openChat = async () => { await CapacitorCrisp.openMessenger(); }; // Benutzerinformationen festlegen const setUserInfo = async () => { await CapacitorCrisp.setUser({ email: 'user@example.com', nickname: 'John Doe', phone: '+1234567890', avatar: 'https://example.com/avatar.jpg' }); }; // Benutzersitzung zurücksetzen (Abmelden) const logout = async () => { await CapacitorCrisp.resetChatSession(); }; ``` ## API-Referenz [Section titled “API-Referenz”](#api-referenz) ### configure(options) [Section titled “configure(options)”](#configureoptions) Konfigurieren Sie Crisp mit Ihrer Website-ID und optionalen Einstellungen. ```typescript interface ConfigureOptions { websiteID: string; locale?: string; // z.B., 'en', 'fr', 'es' tokenID?: string; // Für authentifizierte Sitzungen } await CapacitorCrisp.configure({ websiteID: 'YOUR_WEBSITE_ID', locale: 'de' }); ``` ### openMessenger() [Section titled “openMessenger()”](#openmessenger) Öffnet die Crisp-Chat-Oberfläche. ```typescript await CapacitorCrisp.openMessenger(); ``` ### setUser(options) [Section titled “setUser(options)”](#setuseroptions) Setzt Benutzerinformationen für die Chat-Sitzung. ```typescript interface UserOptions { email?: string; nickname?: string; phone?: string; avatar?: string; } await CapacitorCrisp.setUser({ email: 'user@example.com', nickname: 'John Doe' }); ``` ### setUserCompany(options) [Section titled “setUserCompany(options)”](#setusercompanyoptions) Setzt Firmeninformationen für Geschäftskunden. ```typescript interface CompanyOptions { name: string; url?: string; description?: string; employment?: { title?: string; role?: string; }; geolocation?: { city?: string; country?: string; }; } await CapacitorCrisp.setUserCompany({ name: 'Acme Corp', url: 'https://acme.com', employment: { title: 'CEO', role: 'Leadership' } }); ``` ### pushEvent(options) [Section titled “pushEvent(options)”](#pusheventoptions) Senden Sie benutzerdefinierte Ereignisse, um Benutzeraktionen zu verfolgen. ```typescript interface EventOptions { name: string; color?: 'red' | 'orange' | 'yellow' | 'green' | 'blue' | 'purple' | 'pink' | 'brown' | 'grey' | 'black'; data?: { [key: string]: any }; } await CapacitorCrisp.pushEvent({ name: 'checkout_completed', color: 'green', data: { price: 99.99, currency: 'USD' } }); ``` ### setSessionSegment(segment) [Section titled “setSessionSegment(segment)”](#setsessionsegmentsegment) Setzen Sie ein Segment, um die Chat-Sitzung zu kategorisieren. ```typescript await CapacitorCrisp.setSessionSegment('premium_customer'); ``` ### resetChatSession() [Section titled “resetChatSession()”](#resetchatsession) Setzen Sie die aktuelle Chat-Sitzung zurück (nützlich für Abmeldung). ```typescript await CapacitorCrisp.resetChatSession(); ``` ## Erweiterte Funktionen [Section titled “Erweiterte Funktionen”](#erweiterte-funktionen) ### Benutzerdefinierte Benutzerdaten [Section titled “Benutzerdefinierte Benutzerdaten”](#benutzerdefinierte-benutzerdaten) ```typescript // Mehrere benutzerdefinierte Datenfelder festlegen await CapacitorCrisp.setSessionData({ key: 'plan', value: 'premium' }); await CapacitorCrisp.setSessionData({ key: 'signup_date', value: '2024-01-15' }); ``` ### Nachrichtenvorlage [Section titled “Nachrichtenvorlage”](#nachrichtenvorlage) Setzen Sie eine vorgefüllte Nachricht in das Chat-Eingabefeld: ```typescript await CapacitorCrisp.setMessageText( "Hallo, ich benötige Hilfe bei meiner Bestellung #12345" ); ``` ### Chat-Verfügbarkeit [Section titled “Chat-Verfügbarkeit”](#chat-verfügbarkeit) Steuern Sie, wann das Chat-Widget verfügbar ist: ```typescript // Chat vorübergehend ausblenden await CapacitorCrisp.setTokenID('user_token_12345'); ``` ## Vollständiges Beispiel [Section titled “Vollständiges Beispiel”](#vollständiges-beispiel) ```typescript import { CapacitorCrisp } from '@capgo/capacitor-crisp'; export class ChatService { async initialize() { // Crisp konfigurieren await CapacitorCrisp.configure({ websiteID: 'YOUR_WEBSITE_ID', locale: 'de' }); } async loginUser(user: any) { // Benutzerinformationen festlegen await CapacitorCrisp.setUser({ email: user.email, nickname: user.name, phone: user.phone, avatar: user.avatarUrl }); // Benutzerdefinierte Daten festlegen await CapacitorCrisp.setSessionData({ key: 'user_id', value: user.id }); await CapacitorCrisp.setSessionData({ key: 'account_type', value: user.accountType }); // Segment festlegen if (user.isPremium) { await CapacitorCrisp.setSessionSegment('premium'); } // Login-Ereignis verfolgen await CapacitorCrisp.pushEvent({ name: 'user_login', color: 'blue', data: { method: 'email' } }); } async openSupport(context?: string) { if (context) { await CapacitorCrisp.setMessageText( `Ich benötige Hilfe bei: ${context}` ); } await CapacitorCrisp.openMessenger(); } async logout() { await CapacitorCrisp.resetChatSession(); } } ``` ## Styling und Anpassung [Section titled “Styling und Anpassung”](#styling-und-anpassung) Crisp passt sich automatisch dem Theme Ihrer App an, aber Sie können es über das Crisp-Dashboard weiter anpassen: 1. Gehen Sie zu Ihrem Crisp-Dashboard 2. Navigieren Sie zu Einstellungen > Website-Einstellungen > Chatbox & E-Mail-Einstellungen 3. Passen Sie Farben, Position und Verhalten an ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) 1. **Früh initialisieren** Konfigurieren Sie Crisp während der App-Initialisierung für sofortige Verfügbarkeit: ```typescript import { App } from '@capacitor/app'; App.addListener('appStateChange', async ({ isActive }) => { if (isActive) { await CapacitorCrisp.configure({ websiteID: 'YOUR_WEBSITE_ID' }); } }); ``` 2. **Benutzerkontext festlegen** Geben Sie immer Benutzerinformationen an, wenn verfügbar, für besseren Support: ```typescript if (user.isAuthenticated) { await CapacitorCrisp.setUser({ email: user.email, nickname: user.name }); } ``` 3. **Wichtige Ereignisse verfolgen** Verwenden Sie Ereignisse, um Support-Agenten Kontext zu bieten: ```typescript await CapacitorCrisp.pushEvent({ name: 'error_occurred', color: 'red', data: { error: error.message, screen: 'checkout' } }); ``` 4. **Abmeldung ordnungsgemäß behandeln** Setzen Sie die Sitzung immer zurück, wenn Benutzer sich abmelden: ```typescript async logout() { await CapacitorCrisp.resetChatSession(); // Ihre andere Abmeldelogik } ``` ## Plattform-Hinweise [Section titled “Plattform-Hinweise”](#plattform-hinweise) ### iOS [Section titled “iOS”](#ios) * Erfordert iOS 10.0+ * Verwendet natives Crisp iOS SDK * Unterstützt Push-Benachrichtigungen (im Crisp-Dashboard konfigurieren) ### Android [Section titled “Android”](#android) * Erfordert Android 5.0 (API 21)+ * Verwendet natives Crisp Android SDK * Material Design-konform ### Web [Section titled “Web”](#web) * Verwendet als Fallback Crisp JavaScript SDK * Vollständige Feature-Parität mit nativen Plattformen # @capgo/capacitor-data-storage-sqlite > Schneller und zuverlässiger SQLite-basierter Schlüssel-Wert-Speicher für Ihre Capacitor-Apps mit Verschlüsselungsunterstützung. SQLite-betrieben Schneller und zuverlässiger Datenspeicher mit SQLite Verschlüsselungsunterstützung Optionale Verschlüsselung für sensible Daten Schlüssel-Wert-Einfachheit Einfache Schlüssel-Wert-API mit leistungsstarken Funktionen Umfassende Dokumentation Schauen Sie sich die [Dokumentation](/docs/plugins/data-storage-sqlite/getting-started/) an, um das Plugin in nur wenigen Minuten zu beherrschen. # Erste Schritte > Erfahren Sie, wie Sie das Capacitor Data Storage SQLite Plugin für schnellen Schlüssel-Wert-Speicher mit optionaler Verschlüsselung installieren und konfigurieren. 1. **Paket installieren** * npm ```sh npm i @capgo/capacitor-data-storage-sqlite ``` * pnpm ```sh pnpm add @capgo/capacitor-data-storage-sqlite ``` * yarn ```sh yarn add @capgo/capacitor-data-storage-sqlite ``` * bun ```sh bun add @capgo/capacitor-data-storage-sqlite ``` 2. **Mit nativen Projekten synchronisieren** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` 3. **Plugin konfigurieren** **Grundlegendes Speicherbeispiel:** ```typescript import { CapacitorDataStorageSqlite } from '@capgo/capacitor-data-storage-sqlite'; // Eine Speicherdatenbank öffnen await CapacitorDataStorageSqlite.openStore({ database: 'myapp_storage' }); // Daten speichern await CapacitorDataStorageSqlite.set({ key: 'user_preferences', value: JSON.stringify({ theme: 'dark' }) }); ``` **Verschlüsseltes Speicherbeispiel:** ```typescript // Verschlüsselten Speicher öffnen await CapacitorDataStorageSqlite.openStore({ database: 'secure_storage', encrypted: true, mode: 'encryption' }); // Sensible Daten speichern await CapacitorDataStorageSqlite.set({ key: 'api_token', value: 'secret_token_value' }); ``` * iOS Keine zusätzliche Einrichtung für iOS erforderlich. * Android Keine zusätzliche Einrichtung für Android erforderlich. 4. **Grundlegende Operationen** ```typescript import { CapacitorDataStorageSqlite } from '@capgo/capacitor-data-storage-sqlite'; // Einen Wert setzen await CapacitorDataStorageSqlite.set({ key: 'username', value: 'john_doe' }); // Einen Wert abrufen const { value } = await CapacitorDataStorageSqlite.get({ key: 'username' }); console.log('Benutzername:', value); // "john_doe" // Einen Wert entfernen await CapacitorDataStorageSqlite.remove({ key: 'username' }); // Alle Daten löschen await CapacitorDataStorageSqlite.clear(); // Prüfen, ob Schlüssel existiert const { result } = await CapacitorDataStorageSqlite.iskey({ key: 'username' }); console.log('Schlüssel existiert:', result); // true oder false // Alle Schlüssel abrufen const { keys } = await CapacitorDataStorageSqlite.keys(); console.log('Alle Schlüssel:', keys); // Alle Werte abrufen const { values } = await CapacitorDataStorageSqlite.values(); console.log('Alle Werte:', values); ``` 5. **Erweiterte Verwendung** ```typescript import { CapacitorDataStorageSqlite } from '@capgo/capacitor-data-storage-sqlite'; export class StorageService { private dbName = 'app_storage'; private isEncrypted = false; async initialize(encrypted = false) { this.isEncrypted = encrypted; // Speicher mit Optionen öffnen await CapacitorDataStorageSqlite.openStore({ database: this.dbName, encrypted: encrypted, mode: encrypted ? 'encryption' : 'no-encryption', version: 1 }); } // Generische Speichermethoden async setObject(key: string, data: T): Promise { const value = JSON.stringify(data); await CapacitorDataStorageSqlite.set({ key, value }); } async getObject(key: string): Promise { try { const { value } = await CapacitorDataStorageSqlite.get({ key }); return value ? JSON.parse(value) : null; } catch (error) { console.error('Fehler beim Abrufen des Objekts:', error); return null; } } // Batch-Operationen async setMultiple(items: Record): Promise { for (const [key, value] of Object.entries(items)) { await CapacitorDataStorageSqlite.set({ key, value: typeof value === 'string' ? value : JSON.stringify(value) }); } } async getMultiple(keys: string[]): Promise> { const results: Record = {}; for (const key of keys) { try { const { value } = await CapacitorDataStorageSqlite.get({ key }); results[key] = value; } catch (error) { results[key] = null; } } return results; } // Tabellenverwaltung async getTables(): Promise { const { tables } = await CapacitorDataStorageSqlite.tables(); return tables; } async deleteTable(table: string): Promise { await CapacitorDataStorageSqlite.deleteTable({ table }); } // Import/Export-Funktionalität async exportToJson(): Promise { const { keys } = await CapacitorDataStorageSqlite.keys(); const { values } = await CapacitorDataStorageSqlite.values(); return keys.map((key, index) => ({ key, value: values[index] })); } async importFromJson(data: Array<{ key: string; value: string }>): Promise { // Vorhandene Daten löschen await CapacitorDataStorageSqlite.clear(); // Neue Daten importieren for (const item of data) { await CapacitorDataStorageSqlite.set({ key: item.key, value: item.value }); } } // Filtern und Suchen async keysStartingWith(prefix: string): Promise { const { keys } = await CapacitorDataStorageSqlite.keys(); return keys.filter(key => key.startsWith(prefix)); } async filterByPrefix(prefix: string): Promise> { const { keys } = await CapacitorDataStorageSqlite.keys(); const { values } = await CapacitorDataStorageSqlite.values(); const filtered: Array<{ key: string; value: string }> = []; keys.forEach((key, index) => { if (key.startsWith(prefix)) { filtered.push({ key, value: values[index] }); } }); return filtered; } // Datenbank schließen, wenn fertig async close(): Promise { await CapacitorDataStorageSqlite.closeStore({ database: this.dbName }); } } // Verwendungsbeispiel const storage = new StorageService(); await storage.initialize(true); // Verschlüsselung verwenden // Benutzerdaten speichern await storage.setObject('user_profile', { id: 123, name: 'John Doe', email: 'john@example.com' }); // Benutzerdaten abrufen const profile = await storage.getObject('user_profile'); console.log('Benutzerprofil:', profile); ``` ## API-Referenz [Section titled “API-Referenz”](#api-referenz) ### Methoden [Section titled “Methoden”](#methoden) #### `openStore(options: OpenStoreOptions)` [Section titled “openStore(options: OpenStoreOptions)”](#openstoreoptions-openstoreoptions) Eine Speicherdatenbank öffnen. **Parameter:** * `options.database`: string - Datenbankname * `options.encrypted`: boolean - Verschlüsselung aktivieren * `options.mode`: string - ‘encryption’ oder ‘no-encryption’ * `options.version`: number - Datenbankversion #### `closeStore(options: CloseStoreOptions)` [Section titled “closeStore(options: CloseStoreOptions)”](#closestoreoptions-closestoreoptions) Die Speicherdatenbank schließen. #### `set(options: SetOptions)` [Section titled “set(options: SetOptions)”](#setoptions-setoptions) Ein Schlüssel-Wert-Paar speichern. **Parameter:** * `options.key`: string - Speicherschlüssel * `options.value`: string - Zu speichernder Wert #### `get(options: GetOptions)` [Section titled “get(options: GetOptions)”](#getoptions-getoptions) Einen Wert nach Schlüssel abrufen. **Rückgabe:** `Promise<{ value: string }>` #### `remove(options: RemoveOptions)` [Section titled “remove(options: RemoveOptions)”](#removeoptions-removeoptions) Ein Schlüssel-Wert-Paar entfernen. #### `clear()` [Section titled “clear()”](#clear) Alle Daten aus dem Speicher löschen. #### `iskey(options: IskeyOptions)` [Section titled “iskey(options: IskeyOptions)”](#iskeyoptions-iskeyoptions) Prüfen, ob ein Schlüssel existiert. **Rückgabe:** `Promise<{ result: boolean }>` #### `keys()` [Section titled “keys()”](#keys) Alle Speicherschlüssel abrufen. **Rückgabe:** `Promise<{ keys: string[] }>` #### `values()` [Section titled “values()”](#values) Alle Speicherwerte abrufen. **Rückgabe:** `Promise<{ values: string[] }>` #### `tables()` [Section titled “tables()”](#tables) Alle Tabellennamen abrufen. **Rückgabe:** `Promise<{ tables: string[] }>` #### `deleteTable(options: DeleteTableOptions)` [Section titled “deleteTable(options: DeleteTableOptions)”](#deletetableoptions-deletetableoptions) Eine bestimmte Tabelle löschen. ### Schnittstellen [Section titled “Schnittstellen”](#schnittstellen) ```typescript interface OpenStoreOptions { database: string; encrypted?: boolean; mode?: string; version?: number; } interface SetOptions { key: string; value: string; } interface GetOptions { key: string; } interface RemoveOptions { key: string; } ``` ## Plattform-Hinweise [Section titled “Plattform-Hinweise”](#plattform-hinweise) ### iOS [Section titled “iOS”](#ios) * Verwendet SQLite3 mit optionalem SQLCipher für Verschlüsselung * Daten bleiben über App-Updates hinweg erhalten * Unterstützt iOS 11.0+ ### Android [Section titled “Android”](#android) * Verwendet SQLite mit optionalem SQLCipher * Daten bleiben über App-Updates hinweg erhalten * Unterstützt Android 5.0 (API 21)+ ## Häufige Anwendungsfälle [Section titled “Häufige Anwendungsfälle”](#häufige-anwendungsfälle) 1. **Benutzereinstellungen**: App-Einstellungen und Präferenzen speichern 2. **Cache-Verwaltung**: API-Antworten und Daten zwischenspeichern 3. **Offline-Speicher**: Daten für Offline-Zugriff speichern 4. **Sitzungsverwaltung**: Benutzersitzungen sicher verwalten 5. **Token-Speicher**: Authentifizierungs-Token sicher speichern ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) 1. **Verschlüsselung für sensible Daten verwenden** ```typescript // Für sensible Daten wie Token await openStore({ database: 'secure_db', encrypted: true, mode: 'encryption' }); ``` 2. **Schlüssel mit Präfixen organisieren** ```typescript // Präfixe zur Organisation verwenden await set({ key: 'user:123:profile', value: userData }); await set({ key: 'cache:api:users', value: apiData }); ``` 3. **Große Daten vorsichtig behandeln** ```typescript // Für große Objekte Kompression in Betracht ziehen const compressed = compress(largeData); await set({ key: 'large_data', value: compressed }); ``` 4. **Regelmäßige Bereinigung** ```typescript // Abgelaufene Cache-Einträge entfernen const keys = await keys(); for (const key of keys.keys) { if (key.startsWith('cache:') && isExpired(key)) { await remove({ key }); } } ``` ## Fehlerbehebung [Section titled “Fehlerbehebung”](#fehlerbehebung) **Datenbank wird nicht geöffnet:** * Prüfen Sie, ob der Datenbankname gültig ist (alphanumerisch, Unterstriche) * Stellen Sie sicher, dass keine Sonderzeichen im Datenbanknamen enthalten sind * Überprüfen Sie, ob der Verschlüsselungsmodus mit der vorhandenen Datenbank übereinstimmt **Daten werden nicht gespeichert:** * Stellen Sie sicher, dass `openStore` vor Operationen aufgerufen wird * Überprüfen Sie auf Fehler in der Konsole * Überprüfen Sie, ob Schlüsselnamen Strings sind **Leistungsprobleme:** * Vermeiden Sie das Speichern sehr großer Werte * Verwenden Sie Batch-Operationen, wenn möglich * Erwägen Sie die Verwendung mehrerer Datenbanken für verschiedene Datentypen # @capgo/capacitor-document-scanner > Starten Sie eine ausgefeilte Dokumentenscan-Erfahrung mit automatischem Zuschneiden, mehrseitigem Erfassen und konfigurierbaren Ausgabeformaten. Das Document Scanner Plugin bietet Ihrer Capacitor-App einen nativen Scan-Workflow in Qualität, der auf iOS- und Android-Geräten funktioniert. Automatische Erkennung Erkennen Sie Seitenkanten, schneiden Sie automatisch zu und lassen Sie Benutzer vor dem Speichern anpassen. Mehrseitiges Erfassen Erfassen Sie eine oder mehrere Seiten in einer einzigen Scan-Sitzung mit anpassbaren Limits. Benutzerdefinierte Ausgabe Geben Sie entweder Dateipfade oder Base64-Strings mit der von Ihnen gewählten Bildqualität zurück. Einheitliche API Eine TypeScript-Schnittstelle umfasst sowohl iOS- als auch Android-Implementierungen. Verwenden Sie die Anleitung für erste Schritte, um Kameraberechtigungen zu aktivieren, Qualitätseinstellungen zu konfigurieren und gescannte Dateien in Ihren Speicher-Workflow zu exportieren. # Erste Schritte > Fügen Sie natives Dokumentenscannen in Qualität mit Kantenerkennung und anpassbaren Ausgabeformaten hinzu. 1. **Plugin installieren** * npm ```sh npm i @capgo/capacitor-document-scanner ``` * pnpm ```sh pnpm add @capgo/capacitor-document-scanner ``` * yarn ```sh yarn add @capgo/capacitor-document-scanner ``` * bun ```sh bun add @capgo/capacitor-document-scanner ``` 2. **Native Plattformen synchronisieren** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Einen Scan auslösen [Section titled “Einen Scan auslösen”](#einen-scan-auslösen) ```typescript import { DocumentScanner, ResponseType, ScanDocumentResponseStatus, } from '@capgo/capacitor-document-scanner'; const result = await DocumentScanner.scanDocument({ croppedImageQuality: 90, letUserAdjustCrop: true, maxNumDocuments: 10, responseType: ResponseType.ImageFilePath, }); if (result.status === ScanDocumentResponseStatus.Success) { console.log('Gescannte Dateien:', result.scannedImages); } else { console.log('Scan vom Benutzer abgebrochen'); } ``` ## Base64-Daten zurückgeben [Section titled “Base64-Daten zurückgeben”](#base64-daten-zurückgeben) ```typescript const result = await DocumentScanner.scanDocument({ responseType: ResponseType.Base64, }); const [firstPage] = result.scannedImages ?? []; if (firstPage) { const dataUrl = `data:image/jpeg;base64,${firstPage}`; // Vorschau anzeigen oder auf Server hochladen } ``` ## Plattformanforderungen [Section titled “Plattformanforderungen”](#plattformanforderungen) * **iOS**: Fügen Sie `NSCameraUsageDescription` zu `ios/App/App/Info.plist` hinzu und erklären Sie, wie Sie die Kamera verwenden. * **Android**: Bestätigen Sie, dass die `CAMERA`-Berechtigung deklariert ist (Capacitor fügt sie automatisch hinzu) und passen Sie `croppedImageQuality`, `letUserAdjustCrop` und `maxNumDocuments` an Ihre Benutzeroberfläche an. * **Speicher**: Wenn Sie `ResponseType.ImageFilePath` verwenden, verschieben oder kopieren Sie die gescannten Dateien an Ihren gewünschten Speicherort, bevor die App-Sitzung endet. # @capgo/capacitor-downloader > Laden Sie Dateien mit Hintergrundunterstützung, Fortschrittsverfolgung, Pausieren/Fortsetzen-Funktionen und effizienter Download-Verwaltung herunter. ## Übersicht [Section titled “Übersicht”](#übersicht) Das Capacitor Downloader Plugin bietet leistungsstarke Datei-Download-Funktionen mit Unterstützung für Hintergrund-Downloads, Fortschrittsverfolgung und umfassende Download-Verwaltung. Dieses Plugin ermöglicht robustes Herunterladen von Dateien mit Pausieren/Fortsetzen-Funktionalität und detaillierter Fortschrittsüberwachung. > **In Entwicklung**: Dieses Plugin befindet sich derzeit in der Entwicklung und ist noch nicht für den Produktionseinsatz bereit. Hintergrund-Downloads Downloads fortsetzen, wenn die App im Hintergrund ist Fortschrittsverfolgung Echtzeit-Download-Fortschritt und Statusüberwachung Download-Steuerung Downloads dynamisch pausieren, fortsetzen und stoppen Netzwerkoptionen Netzwerkpräferenzen und Prioritäten konfigurieren ## Entwicklungsstatus [Section titled “Entwicklungsstatus”](#entwicklungsstatus) Dieses Plugin ist inspiriert von react-native-background-downloader und befindet sich derzeit in der Entwicklung. Funktionen können sich im Laufe der Entwicklung ändern. # Erste Schritte > Installations- und Verwendungsanleitung für @capgo/capacitor-downloader ## Installation [Section titled “Installation”](#installation) * npm ```bash npm install @capgo/capacitor-downloader npx cap sync ``` * yarn ```bash yarn add @capgo/capacitor-downloader npx cap sync ``` * pnpm ```bash pnpm add @capgo/capacitor-downloader npx cap sync ``` * bun ```bash bun add @capgo/capacitor-downloader npx cap sync ``` ## Verwendungsbeispiel [Section titled “Verwendungsbeispiel”](#verwendungsbeispiel) ```typescript import { CapacitorDownloader } from '@capgo/capacitor-downloader'; // Einen Download starten const downloadId = 'my-download-001'; await CapacitorDownloader.download({ id: downloadId, url: 'https://example.com/large-file.zip', destination: '/downloads/large-file.zip', headers: { 'Authorization': 'Bearer token123' }, network: 'wifi-only', priority: 'high' }); // Auf Fortschritts-Updates hören CapacitorDownloader.addListener('downloadProgress', (data) => { console.log(`Download ${data.id}: ${data.progress}% abgeschlossen`); console.log(`Heruntergeladen: ${data.bytesDownloaded}/${data.totalBytes} Bytes`); }); // Abschluss behandeln CapacitorDownloader.addListener('downloadCompleted', (data) => { console.log(`Download abgeschlossen: ${data.id}`); console.log(`Datei gespeichert in: ${data.path}`); }); // Fehler behandeln CapacitorDownloader.addListener('downloadFailed', (error) => { console.error(`Download fehlgeschlagen: ${error.id}`, error.message); }); // Einen Download pausieren await CapacitorDownloader.pause(downloadId); // Den Download fortsetzen await CapacitorDownloader.resume(downloadId); // Download-Status prüfen const status = await CapacitorDownloader.checkStatus(downloadId); console.log('Download-Status:', status); // Den Download stoppen await CapacitorDownloader.stop(downloadId); ``` ## Kern-API-Methoden [Section titled “Kern-API-Methoden”](#kern-api-methoden) ### Download-Verwaltung [Section titled “Download-Verwaltung”](#download-verwaltung) * `download(options)` - Neuen Datei-Download starten * `pause(id)` - Laufenden Download pausieren * `resume(id)` - Pausierten Download fortsetzen * `stop(id)` - Download stoppen und abbrechen * `checkStatus(id)` - Aktuellen Download-Status abrufen ### Dateioperationen [Section titled “Dateioperationen”](#dateioperationen) * `getFileInfo(path)` - Dateiinformationen und Metadaten abrufen ## Download-Konfiguration [Section titled “Download-Konfiguration”](#download-konfiguration) ```typescript interface DownloadOptions { id: string; // Eindeutige Download-Kennung url: string; // Download-Quell-URL destination: string; // Lokaler Speicherpfad headers?: Record; // Benutzerdefinierte HTTP-Header network?: 'cellular' | 'wifi-only'; // Netzwerkbeschränkungen priority?: 'high' | 'normal' | 'low'; // Download-Priorität } ``` ## Event-Listener [Section titled “Event-Listener”](#event-listener) Das Plugin bietet umfassende Ereignisbehandlung: * `downloadProgress` - Echtzeit-Download-Fortschritt verfolgen * `downloadCompleted` - Erfolgreichen Download-Abschluss behandeln * `downloadFailed` - Download-Fehler und -Ausfälle behandeln ## Netzwerkkonfiguration [Section titled “Netzwerkkonfiguration”](#netzwerkkonfiguration) ### Nur-WiFi-Downloads [Section titled “Nur-WiFi-Downloads”](#nur-wifi-downloads) ```typescript await CapacitorDownloader.download({ id: 'large-file', url: 'https://example.com/video.mp4', destination: '/downloads/video.mp4', network: 'wifi-only' // Beschränkt auf WiFi-Netzwerke }); ``` ### Prioritätsverwaltung [Section titled “Prioritätsverwaltung”](#prioritätsverwaltung) ```typescript // Download mit hoher Priorität await CapacitorDownloader.download({ id: 'urgent-update', url: 'https://example.com/update.zip', destination: '/downloads/update.zip', priority: 'high' }); ``` ## Download-Zustände [Section titled “Download-Zustände”](#download-zustände) Downloads können verschiedene Zustände haben: * **Pending**: In Warteschlange für Download * **Running**: Wird gerade heruntergeladen * **Paused**: Vorübergehend gestoppt * **Completed**: Erfolgreich abgeschlossen * **Failed**: Fehler aufgetreten * **Stopped**: Manuell abgebrochen ## Dateiinformationen [Section titled “Dateiinformationen”](#dateiinformationen) ```typescript // Dateidetails abrufen const fileInfo = await CapacitorDownloader.getFileInfo('/downloads/my-file.pdf'); console.log('Dateigröße:', fileInfo.size); console.log('Zuletzt geändert:', fileInfo.lastModified); console.log('MIME-Typ:', fileInfo.mimeType); ``` ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) * Verwenden Sie eindeutige Download-IDs, um Konflikte zu vermeiden * Implementieren Sie ordnungsgemäße Fehlerbehandlung für Netzwerkausfälle * Berücksichtigen Sie Speicherplatz vor dem Starten großer Downloads * Verwenden Sie den Nur-WiFi-Modus für große Dateien, um mobile Daten zu schonen * Bereinigen Sie abgeschlossene Downloads, um Speicher zu verwalten # @capgo/capacitor-env > Liefern Sie mandantenspezifische Einstellungen ohne neue Builds, indem Sie sichere Konfigurationswerte direkt von Capacitor lesen. Mit Capgo Env können Sie Secrets und Build-spezifische Konfigurationen in `capacitor.config.*` speichern und zur Laufzeit durch eine einfache API auflösen. Laufzeitkonfiguration Lesen Sie Werte aus nativen Konfigurationsdateien, ohne sie in Ihrem JavaScript-Bundle zu versenden. Build-spezifische Overrides Erstellen Sie mehrere App-Varianten oder Mandanten, indem Sie unterschiedliche Konfigurationsdateien bereitstellen. Typsicherer Zugriff Rufen Sie Schlüssel mit TypeScript-Unterstützung ab und zentralisieren Sie Fallback-Logik. Standardmäßig sicher Halten Sie API-Schlüssel und Secrets aus Git heraus, während Sie sie dennoch mit nativen Binärdateien versenden. Folgen Sie dem Leitfaden für Erste Schritte, um Schlüssel in Ihrer Capacitor-Konfiguration zu deklarieren und diese sicher auf allen Plattformen abzurufen. # Erste Schritte > Konfigurieren Sie Build-spezifische Umgebungswerte und rufen Sie sie zur Laufzeit mit dem Env-Plugin ab. 1. **Paket installieren** * npm ```sh npm i @capgo/capacitor-env ``` * pnpm ```sh pnpm add @capgo/capacitor-env ``` * yarn ```sh yarn add @capgo/capacitor-env ``` * bun ```sh bun add @capgo/capacitor-env ``` 2. **Native Plattformen synchronisieren** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Konfigurationswerte deklarieren [Section titled “Konfigurationswerte deklarieren”](#konfigurationswerte-deklarieren) Fügen Sie Schlüssel zur Capacitor-Konfiguration hinzu, damit sie in Ihre nativen Builds eingebettet werden. Sie können mehrere Konfigurationsvarianten (`capacitor.config.prod.ts`, `capacitor.config.dev.ts`, etc.) erstellen, um Werte pro Umgebung auszutauschen. capacitor.config.ts ```ts import { CapacitorConfig } from '@capacitor/cli'; const config: CapacitorConfig = { appId: 'com.example.app', appName: 'Example', webDir: 'dist', plugins: { Env: { API_URL: 'https://api.example.com', PUBLIC_KEY: 'pk_live_123', }, }, }; export default config; ``` Auf nativen Plattformen werden die Werte in den generierten Konfigurationsdateien gespeichert (`ios/App/App/capacitor.config.json` und `android/app/src/main/assets/capacitor.config.json`). Aktualisieren Sie diese Dateien pro Variante, wenn Sie mandantenspezifische Werte benötigen. ## Werte im Code lesen [Section titled “Werte im Code lesen”](#werte-im-code-lesen) ```typescript import { Env } from '@capgo/capacitor-env'; const apiUrl = await Env.getKey({ key: 'API_URL' }).then((result) => result.value); if (!apiUrl) { throw new Error('Fehlende API_URL-Konfiguration'); } ``` ## Fallbacks bereitstellen [Section titled “Fallbacks bereitstellen”](#fallbacks-bereitstellen) ```typescript const loadConfig = async () => { const { value: endpoint } = await Env.getKey({ key: 'API_URL' }); return endpoint || 'https://staging.example.com'; }; ``` ## Tipps [Section titled “Tipps”](#tipps) * Verwenden Sie unterschiedliche `capacitor.config`-Dateien pro Umgebung und verweisen Sie die CLI mit `npx cap run ios --configuration=prod` auf die richtige. * Kombinieren Sie mit Capgo Updater-Kanälen, um mandantenspezifische Werte zu liefern, ohne neue Binärdateien zu veröffentlichen. * Halten Sie Geheimnisse aus der Versionskontrolle heraus, indem Sie sie während Ihres CI-Builds vor `npx cap sync` ersetzen. # @capgo/capacitor-fast-sql > Ultra-schnelles natives SQLite mit benutzerdefiniertem Protokoll für Synchronisierungssysteme und große Datensätze, mit bis zu 25-fach höherer Leistung. Benutzerdefiniertes HTTP-Protokoll Umgeht die Capacitor-Bridge für bis zu 25-fach höhere Leistung ⚡ Vollständige SQLite-Unterstützung Vollständige SQL-Unterstützung mit Transaktionen und Batch-Operationen 📊 Synchronisierungsfreundlich Entwickelt für lokale Synchronisierungssysteme wie CRDTs und operative Transformationen 🔄 Plattformübergreifend iOS, Android und Web mit sql.js + IndexedDB 🌐 Transaktionsunterstützung ACID-Transaktionen mit konfigurierbaren Isolationsstufen 🔒 Binärdaten Native Unterstützung für Uint8Array/BLOB-Speicherung 📁 Erste Schritte Schauen Sie sich die [Anleitung für erste Schritte](/docs/plugins/fast-sql/getting-started/) an, um das Plugin zu installieren und zu konfigurieren. # Erste Schritte mit Fast SQL > Installieren und konfigurieren Sie das Fast SQL Plugin für leistungsstarken nativen SQLite-Datenbankzugriff Diese Anleitung hilft Ihnen bei der Installation und Konfiguration des **@capgo/capacitor-fast-sql** Plugins für leistungsstarken SQLite-Datenbankzugriff. ## Warum Fast SQL? [Section titled “Warum Fast SQL?”](#warum-fast-sql) Traditionelle SQLite-Plugins für Capacitor verwenden die Standard-JavaScript-Bridge zur Kommunikation zwischen Ihrem Web-Code und nativem Code. Während dies für kleine Operationen gut funktioniert, wird die Bridge zu einem erheblichen Engpass, wenn große Datenmengen übertragen werden müssen. Jedes Datenstück muss in JSON serialisiert, über die Bridge gesendet und auf der anderen Seite wieder deserialisiert werden. Dieser Serialisierungs-Overhead macht Operationen mit Tausenden von Zeilen oder großen Binärdaten unglaublich langsam. **Fast SQL löst dieses Problem**, indem es einen lokalen HTTP-Server auf dem Gerät einrichtet, der direkt mit der nativen SQLite-Datenbank kommuniziert. Dieses benutzerdefinierte Protokoll umgeht Capacitors Bridge vollständig, eliminiert den Serialisierungs-Overhead und ermöglicht: * **Bis zu 25x schnellere Leistung** für Batch-Operationen und große Datensätze * **Effizienten binären Datentransfer** ohne base64-Kodierung * **Streaming großer Ergebnisse** ohne Speicherprobleme * **Optimale Leistung für Sync-Systeme** wie CRDTs und Operational Transforms Dies macht Fast SQL ideal für Local-First-Anwendungen, Offline-Sync-Systeme und Szenarien, in denen Sie IndexedDB durch eine zuverlässigere und leistungsstärkere Lösung ersetzen müssen. ## Installation [Section titled “Installation”](#installation) ```bash npm install @capgo/capacitor-fast-sql npx cap sync ``` ## Plattform-Konfiguration [Section titled “Plattform-Konfiguration”](#plattform-konfiguration) ### iOS-Konfiguration [Section titled “iOS-Konfiguration”](#ios-konfiguration) Fügen Sie Folgendes zu Ihrer `Info.plist` hinzu, um lokale Netzwerkverbindungen zu erlauben: ios/App/App/Info.plist ```xml NSAppTransportSecurity NSAllowsLocalNetworking ``` ### Android-Konfiguration [Section titled “Android-Konfiguration”](#android-konfiguration) Falls erforderlich, fügen Sie dies zu Ihrer `AndroidManifest.xml` hinzu: android/app/src/main/AndroidManifest.xml ```xml ... ``` ### Web-Konfiguration [Section titled “Web-Konfiguration”](#web-konfiguration) Für die Unterstützung der Web-Plattform installieren Sie sql.js: ```bash npm install sql.js ``` ## Grundlegende Verwendung [Section titled “Grundlegende Verwendung”](#grundlegende-verwendung) ### Verbindung zu einer Datenbank herstellen [Section titled “Verbindung zu einer Datenbank herstellen”](#verbindung-zu-einer-datenbank-herstellen) ```typescript import { FastSQL } from '@capgo/capacitor-fast-sql'; // Verbindung zur Datenbank herstellen (erstellt Datei, falls sie nicht existiert) const db = await FastSQL.connect({ database: 'myapp' }); console.log('Mit Datenbank auf Port verbunden:', db.port); ``` ### Tabellen erstellen [Section titled “Tabellen erstellen”](#tabellen-erstellen) ```typescript // Eine Tabelle erstellen await db.execute(` CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, email TEXT UNIQUE, created_at INTEGER DEFAULT (strftime('%s', 'now')) ) `); ``` ### Daten einfügen [Section titled “Daten einfügen”](#daten-einfügen) ```typescript // Einzelne Zeile einfügen const result = await db.run( 'INSERT INTO users (name, email) VALUES (?, ?)', ['John Doe', 'john@example.com'] ); console.log('Eingefügte Zeilen-ID:', result.insertId); console.log('Betroffene Zeilen:', result.rowsAffected); ``` ### Daten abfragen [Section titled “Daten abfragen”](#daten-abfragen) ```typescript // Abfrage mit Parametern const users = await db.query( 'SELECT * FROM users WHERE name LIKE ?', ['John%'] ); console.log('Gefundene Benutzer:', users); // users ist ein Array von Objekten: // [{ id: 1, name: 'John Doe', email: 'john@example.com', created_at: 1234567890 }] ``` ### Daten aktualisieren [Section titled “Daten aktualisieren”](#daten-aktualisieren) ```typescript const result = await db.run( 'UPDATE users SET email = ? WHERE id = ?', ['newemail@example.com', 1] ); console.log('Aktualisierte Zeilen:', result.rowsAffected); ``` ### Daten löschen [Section titled “Daten löschen”](#daten-löschen) ```typescript const result = await db.run( 'DELETE FROM users WHERE id = ?', [1] ); console.log('Gelöschte Zeilen:', result.rowsAffected); ``` ## Transaktionen [Section titled “Transaktionen”](#transaktionen) Transaktionen gewährleisten Atomarität - entweder werden alle Operationen erfolgreich ausgeführt oder alle werden zurückgesetzt: ```typescript try { await db.transaction(async (tx) => { // Alle Operationen in diesem Block sind Teil der Transaktion await tx.run('INSERT INTO accounts (name, balance) VALUES (?, ?)', ['Alice', 1000]); await tx.run('INSERT INTO accounts (name, balance) VALUES (?, ?)', ['Bob', 500]); // Geld überweisen await tx.run('UPDATE accounts SET balance = balance - 100 WHERE name = ?', ['Alice']); await tx.run('UPDATE accounts SET balance = balance + 100 WHERE name = ?', ['Bob']); }); console.log('Transaktion erfolgreich abgeschlossen!'); } catch (error) { console.error('Transaktion zurückgesetzt:', error); // Alle Änderungen werden bei einem Fehler automatisch zurückgesetzt } ``` ### Transaktions-Isolationsstufen [Section titled “Transaktions-Isolationsstufen”](#transaktions-isolationsstufen) ```typescript import { FastSQL, IsolationLevel } from '@capgo/capacitor-fast-sql'; await db.transaction(async (tx) => { // Ihre Operationen }, IsolationLevel.Serializable); ``` Verfügbare Isolationsstufen: * `ReadUncommitted` - Niedrigste Isolation, höchste Leistung * `ReadCommitted` - Verhindert Dirty Reads * `RepeatableRead` - Verhindert nicht wiederholbare Lesevorgänge * `Serializable` - Höchste Isolation, verhindert alle Anomalien ## Batch-Operationen [Section titled “Batch-Operationen”](#batch-operationen) Führen Sie mehrere Anweisungen effizient aus: ```typescript const results = await db.executeBatch([ { statement: 'INSERT INTO logs (message, level) VALUES (?, ?)', params: ['App started', 'INFO'] }, { statement: 'INSERT INTO logs (message, level) VALUES (?, ?)', params: ['User logged in', 'INFO'] }, { statement: 'INSERT INTO logs (message, level) VALUES (?, ?)', params: ['Error occurred', 'ERROR'] }, ]); console.log('Ausgeführt', results.length, 'Anweisungen'); ``` ## Binärdaten (BLOBs) [Section titled “Binärdaten (BLOBs)”](#binärdaten-blobs) Speichern und abrufen Sie Binärdaten mit Uint8Array: ```typescript // Binärdaten speichern const imageData = new Uint8Array([0xFF, 0xD8, 0xFF, 0xE0, /* ... */]); await db.run( 'INSERT INTO images (name, data) VALUES (?, ?)', ['photo.jpg', imageData] ); // Binärdaten abrufen const rows = await db.query('SELECT data FROM images WHERE name = ?', ['photo.jpg']); const retrievedData = rows[0].data; // Uint8Array ``` ## Verschlüsselung (iOS/Android) [Section titled “Verschlüsselung (iOS/Android)”](#verschlüsselung-iosandroid) Aktivieren Sie SQLCipher-Verschlüsselung für sichere Speicherung: ```typescript const db = await FastSQL.connect({ database: 'secure_db', encrypted: true, encryptionKey: 'your-secure-encryption-key' }); ``` Caution * Verschlüsselung ist nur auf iOS und Android verfügbar * Speichern Sie Verschlüsselungsschlüssel sicher (verwenden Sie Keychain/Keystore) * Verschlüsselte Datenbanken können nicht ohne den richtigen Schlüssel geöffnet werden * Die Web-Plattform unterstützt keine Verschlüsselung ## Nur-Lese-Modus [Section titled “Nur-Lese-Modus”](#nur-lese-modus) Öffnen Sie Datenbanken im Nur-Lese-Modus, um Änderungen zu verhindern: ```typescript const db = await FastSQL.connect({ database: 'myapp', readOnly: true }); ``` ## Verbindungen schließen [Section titled “Verbindungen schließen”](#verbindungen-schließen) Schließen Sie Datenbankverbindungen immer, wenn Sie fertig sind: ```typescript await FastSQL.disconnect('myapp'); ``` ## Direktes HTTP-Protokoll [Section titled “Direktes HTTP-Protokoll”](#direktes-http-protokoll) Für maximale Leistung bei großen Datensätzen verwenden Sie das HTTP-Protokoll direkt: ```typescript const { port, token } = await FastSQL.getServerInfo({ database: 'myapp' }); // Direkte HTTP-Anfragen an localhost:port stellen // Token im Authorization-Header einschließen const response = await fetch(`http://localhost:${port}/execute`, { method: 'POST', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ statement: 'SELECT * FROM users', params: [] }) }); const result = await response.json(); ``` ## Fehlerbehandlung [Section titled “Fehlerbehandlung”](#fehlerbehandlung) ```typescript import { FastSQL } from '@capgo/capacitor-fast-sql'; try { const db = await FastSQL.connect({ database: 'myapp' }); await db.run('INSERT INTO users (name, email) VALUES (?, ?)', ['John', 'john@example.com']); } catch (error) { if (error.message.includes('UNIQUE constraint failed')) { console.error('E-Mail existiert bereits'); } else if (error.message.includes('no such table')) { console.error('Tabelle existiert nicht'); } else { console.error('Datenbankfehler:', error); } } ``` ## Gängige SQL-Muster [Section titled “Gängige SQL-Muster”](#gängige-sql-muster) ### Prüfen, ob Tabelle existiert [Section titled “Prüfen, ob Tabelle existiert”](#prüfen-ob-tabelle-existiert) ```typescript const result = await db.query( "SELECT name FROM sqlite_master WHERE type='table' AND name=?", ['users'] ); const tableExists = result.length > 0; ``` ### Tabellenschema abrufen [Section titled “Tabellenschema abrufen”](#tabellenschema-abrufen) ```typescript const schema = await db.query('PRAGMA table_info(users)'); console.log('Spalten:', schema); ``` ### Zeilen zählen [Section titled “Zeilen zählen”](#zeilen-zählen) ```typescript const result = await db.query('SELECT COUNT(*) as count FROM users'); const count = result[0].count; ``` ### Paginierung [Section titled “Paginierung”](#paginierung) ```typescript const pageSize = 20; const page = 1; const offset = (page - 1) * pageSize; const users = await db.query( 'SELECT * FROM users ORDER BY created_at DESC LIMIT ? OFFSET ?', [pageSize, offset] ); ``` ## Leistungstipps [Section titled “Leistungstipps”](#leistungstipps) 1. **Verwenden Sie Transaktionen** für mehrere Operationen - deutlich schneller als einzelne Commits 2. **Verwenden Sie Batch-Operationen** für Masseneinfügungen - effizienter als Schleifen 3. **Erstellen Sie Indizes** für häufig abgefragte Spalten 4. **Verwenden Sie vorbereitete Anweisungen** mit Parametern (?) - verhindert SQL-Injection und verbessert Leistung 5. **Verwenden Sie HTTP-Protokoll direkt** für sehr große Ergebnismengen 6. **Schließen Sie Verbindungen** wenn nicht in Verwendung, um Ressourcen freizugeben ## Nächste Schritte [Section titled “Nächste Schritte”](#nächste-schritte) Schauen Sie sich das [vollständige Tutorial](/plugins/capacitor-fast-sql) an für fortgeschrittene Muster einschließlich: * Datenbank-Service-Architektur * Migrationssysteme * Sync-Engines * Komplexe Abfragen und Joins * Leistungsoptimierung # @capgo/capacitor-ffmpeg > Codieren Sie Videos mit benutzerdefinierter Auflösung und Bitrate neu, indem Sie die leistungsstarke FFmpeg-Bibliothek in Ihren Capacitor-Apps verwenden. Video-Neucodierung Videos mit FFmpeg konvertieren und komprimieren Benutzerdefinierte Auflösung Videos auf beliebige Breite und Höhe skalieren Bitrate-Steuerung Videoqualität und Dateigröße kontrollieren Umfassende Dokumentation Schauen Sie sich die [Dokumentation](/docs/plugins/ffmpeg/getting-started/) an, um mit der Videoverarbeitung zu beginnen. # Erste Schritte > Erfahren Sie, wie Sie das FFmpeg-Plugin für die Videoverarbeitung in Ihrer Capacitor-App installieren und verwenden. 1. **Paket installieren** * npm ```sh npm i @capgo/capacitor-ffmpeg ``` * pnpm ```sh pnpm add @capgo/capacitor-ffmpeg ``` * yarn ```sh yarn add @capgo/capacitor-ffmpeg ``` * bun ```sh bun add @capgo/capacitor-ffmpeg ``` 2. **Mit nativen Projekten synchronisieren** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Verwendung [Section titled “Verwendung”](#verwendung) Importieren Sie das Plugin und verwenden Sie es zur Neucodierung von Videos: ```typescript import { CapacitorFFmpeg } from '@capgo/capacitor-ffmpeg'; // Video mit benutzerdefinierten Einstellungen neu codieren const processVideo = async () => { await CapacitorFFmpeg.reencodeVideo({ inputPath: '/pfad/zur/eingabe/video.mp4', outputPath: '/pfad/zur/ausgabe/video.mp4', width: 1280, height: 720, bitrate: 2000000 // Optional: 2 Mbps }); }; // Plugin-Version abrufen const checkVersion = async () => { const { version } = await CapacitorFFmpeg.getPluginVersion(); console.log('FFmpeg-Plugin-Version:', version); }; ``` ## API-Referenz [Section titled “API-Referenz”](#api-referenz) ### reencodeVideo(options) [Section titled “reencodeVideo(options)”](#reencodevideooptions) Codiert eine Videodatei mit angegebenen Abmessungen und Bitrate neu. ```typescript await CapacitorFFmpeg.reencodeVideo({ inputPath: '/pfad/zur/eingabe.mp4', outputPath: '/pfad/zur/ausgabe.mp4', width: 1920, height: 1080, bitrate: 5000000 // Optional: 5 Mbps }); ``` **Parameter:** * `inputPath` (string): Vollständiger Pfad zur Eingabevideodatei * `outputPath` (string): Vollständiger Pfad, wo das Ausgabevideo gespeichert wird * `width` (number): Zielbreite in Pixeln * `height` (number): Zielhöhe in Pixeln * `bitrate` (number, optional): Ziel-Bitrate in Bits pro Sekunde ### getPluginVersion() [Section titled “getPluginVersion()”](#getpluginversion) Ruft die native Capacitor-Plugin-Version ab. ```typescript const { version } = await CapacitorFFmpeg.getPluginVersion(); ``` ## Vollständiges Beispiel [Section titled “Vollständiges Beispiel”](#vollständiges-beispiel) ```typescript import { CapacitorFFmpeg } from '@capgo/capacitor-ffmpeg'; import { Filesystem, Directory } from '@capacitor/filesystem'; export class VideoProcessor { /** * Video komprimieren, um Dateigröße zu reduzieren */ async compressVideo(inputPath: string, quality: 'low' | 'medium' | 'high') { const qualitySettings = { low: { width: 640, height: 360, bitrate: 500000 }, medium: { width: 1280, height: 720, bitrate: 2000000 }, high: { width: 1920, height: 1080, bitrate: 5000000 } }; const settings = qualitySettings[quality]; const outputPath = inputPath.replace('.mp4', `_${quality}.mp4`); try { await CapacitorFFmpeg.reencodeVideo({ inputPath, outputPath, width: settings.width, height: settings.height, bitrate: settings.bitrate }); console.log(`Video auf ${quality}-Qualität komprimiert:`, outputPath); return outputPath; } catch (error) { console.error('Videokomprimierung fehlgeschlagen:', error); throw error; } } /** * Video auf bestimmte Abmessungen skalieren */ async resizeVideo( inputPath: string, width: number, height: number ): Promise { const outputPath = inputPath.replace('.mp4', '_resized.mp4'); await CapacitorFFmpeg.reencodeVideo({ inputPath, outputPath, width, height }); return outputPath; } /** * Miniaturansicht-Qualität eines Videos erstellen */ async createThumbnailVideo(inputPath: string): Promise { return this.compressVideo(inputPath, 'low'); } /** * Mehrere Videos stapelweise verarbeiten */ async processMultipleVideos( videoPaths: string[], width: number, height: number, bitrate?: number ): Promise { const outputPaths: string[] = []; for (const inputPath of videoPaths) { const outputPath = inputPath.replace('.mp4', '_processed.mp4'); try { await CapacitorFFmpeg.reencodeVideo({ inputPath, outputPath, width, height, bitrate }); outputPaths.push(outputPath); } catch (error) { console.error(`Verarbeitung von ${inputPath} fehlgeschlagen:`, error); } } return outputPaths; } } ``` ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) 1. **Geeignete Bitraten verwenden** Wählen Sie die Bitrate basierend auf Auflösung und Anwendungsfall: ```typescript // Mobiles Teilen (niedrige Bandbreite) const lowQuality = { width: 640, height: 360, bitrate: 500000 }; // Standardqualität const standardQuality = { width: 1280, height: 720, bitrate: 2000000 }; // Hohe Qualität const highQuality = { width: 1920, height: 1080, bitrate: 5000000 }; ``` 2. **Seitenverhältnis beibehalten** Berechnen Sie Abmessungen, um das Seitenverhältnis zu erhalten: ```typescript function calculateDimensions(originalWidth: number, originalHeight: number, targetWidth: number) { const aspectRatio = originalWidth / originalHeight; return { width: targetWidth, height: Math.round(targetWidth / aspectRatio) }; } ``` 3. **Dateipfade korrekt handhaben** Verwenden Sie Capacitor Filesystem für plattformübergreifende Pfadbehandlung: ```typescript import { Filesystem, Directory } from '@capacitor/filesystem'; const inputPath = await Filesystem.getUri({ directory: Directory.Documents, path: 'input.mp4' }); ``` 4. **Fortschritt für Benutzer anzeigen** Videoverarbeitung kann langsam sein - informieren Sie Benutzer: ```typescript async function processWithProgress(inputPath: string) { // Ladeindikator anzeigen showLoading('Video wird verarbeitet...'); try { await CapacitorFFmpeg.reencodeVideo({ inputPath, outputPath: '/pfad/zur/ausgabe.mp4', width: 1280, height: 720 }); showSuccess('Video erfolgreich verarbeitet!'); } catch (error) { showError('Videoverarbeitung fehlgeschlagen'); } finally { hideLoading(); } } ``` 5. **Temporäre Dateien aufräumen** Entfernen Sie Zwischendateien, um Speicherplatz zu sparen: ```typescript async function processAndCleanup(inputPath: string) { const outputPath = inputPath.replace('.mp4', '_output.mp4'); await CapacitorFFmpeg.reencodeVideo({ inputPath, outputPath, width: 1280, height: 720 }); // Original entfernen, wenn nicht mehr benötigt await Filesystem.deleteFile({ path: inputPath }); return outputPath; } ``` ## Plattformhinweise [Section titled “Plattformhinweise”](#plattformhinweise) ### iOS [Section titled “iOS”](#ios) * Erfordert iOS 11.0+ * Große Videoverarbeitung kann Hintergrund-Task-Berechtigungen erfordern * Verwendet natives iOS VideoToolbox für Hardwarebeschleunigung ### Android [Section titled “Android”](#android) * Erfordert Android 5.0 (API 21)+ * Hardwarebeschleunigung variiert je nach Gerät * Kann WRITE\_EXTERNAL\_STORAGE-Berechtigung für Dateizugriff erfordern ### Web [Section titled “Web”](#web) * Nicht auf Web-Plattform unterstützt ## Leistungstipps [Section titled “Leistungstipps”](#leistungstipps) 1. **Niedrigere Auflösung für schnellere Verarbeitung**: Kleinere Abmessungen = schnellere Codierung 2. **Hardwarebeschleunigung verwenden**: Lassen Sie die native Plattform die Codierung optimieren 3. **Im Hintergrund verarbeiten**: Blockieren Sie nicht die UI während der Videoverarbeitung 4. **Speicherverbrauch überwachen**: Große Videos können erheblichen Speicher verbrauchen 5. **Auf echten Geräten testen**: Emulatoren spiegeln möglicherweise nicht die tatsächliche Leistung wider # @capgo/capacitor-file > Vollständige Dateisystem-Operationen mit Lesen, Schreiben, Kopieren, Verschieben und Verzeichnisverwaltung für iOS und Android. Vollständige Dateioperationen Dateien einfach lesen, schreiben, anhängen, kopieren, verschieben und löschen Verzeichnisverwaltung Verzeichnisse mit rekursiver Unterstützung erstellen, auflisten und entfernen Mehrere Kodierungen Unterstützung für UTF-8, ASCII, UTF-16 und binäre (base64) Daten Plattformübergreifend Funktioniert auf iOS und Android mit konsistenter API über alle Plattformen hinweg # @capgo/capacitor-file-compressor > Komprimieren Sie Bilder effizient mit Unterstützung für PNG-, JPEG- und WebP-Formate auf iOS, Android und Web. Mehrere Formate Unterstützung für JPEG- und WebP-Komprimierung plattformübergreifend 🖼️ Qualitätskontrolle Einstellbare Komprimierungsqualität von 0.0 bis 1.0 für perfekte Balance ⚙️ Intelligente Größenänderung Automatische Beibehaltung des Seitenverhältnisses während der Größenänderung 📐 Kein Backend Alle Komprimierung erfolgt auf dem Gerät - kein Server erforderlich 🚀 Plattformübergreifend Konsistente API über iOS-, Android- und Web-Plattformen 📱 Umfassende Dokumentation Schauen Sie sich die [Dokumentation](/docs/plugins/file-compressor/getting-started/) an, um das Plugin in nur wenigen Minuten zu meistern. # Erste Schritte > Erfahren Sie, wie Sie das File Compressor-Plugin installieren und verwenden, um Bilder in Ihrer Capacitor-App zu komprimieren. 1. **Paket installieren** * npm ```sh npm i @capgo/capacitor-file-compressor ``` * pnpm ```sh pnpm add @capgo/capacitor-file-compressor ``` * yarn ```sh yarn add @capgo/capacitor-file-compressor ``` * bun ```sh bun add @capgo/capacitor-file-compressor ``` 2. **Mit nativen Projekten synchronisieren** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Plattformunterstützung [Section titled “Plattformunterstützung”](#plattformunterstützung) | Plattform | Unterstützte Formate | Hinweise | | --------- | -------------------- | ------------------------------------- | | iOS | JPEG | Nur JPEG-Komprimierung unterstützt | | Android | JPEG, WebP | Beide Formate vollständig unterstützt | | Web | JPEG, WebP | Canvas-API-basierte Komprimierung | Hinweis: EXIF-Metadaten werden während der Komprimierung auf allen Plattformen entfernt. ## Verwendung [Section titled “Verwendung”](#verwendung) Importieren Sie das Plugin und komprimieren Sie Bilder: ```typescript import { FileCompressor } from '@capgo/capacitor-file-compressor'; // Bild komprimieren const compressImage = async () => { const result = await FileCompressor.compressImage({ source: 'file:///pfad/zum/bild.jpg', quality: 0.8, width: 1920, height: 1080 }); console.log('Komprimierter Bildpfad:', result.path); console.log('Originalgröße:', result.originalSize); console.log('Komprimierte Größe:', result.size); }; ``` ## API-Referenz [Section titled “API-Referenz”](#api-referenz) ### compressImage(options) [Section titled “compressImage(options)”](#compressimageoptions) Komprimiert eine Bilddatei mit angegebenen Abmessungen und Qualitätseinstellungen. ```typescript interface CompressImageOptions { source: string; // Pfad zur Bilddatei quality?: number; // 0.0 bis 1.0 (Standard: 0.8) width?: number; // Zielbreite in Pixeln height?: number; // Zielhöhe in Pixeln format?: 'jpeg' | '.webp'; // Ausgabeformat } interface CompressImageResult { path: string; // Pfad zum komprimierten Bild size: number; // Komprimierte Dateigröße in Bytes originalSize: number; // Original-Dateigröße in Bytes } const result = await FileCompressor.compressImage(options); ``` **Wichtige Hinweise:** * EXIF-Metadaten werden während der Komprimierung auf allen Plattformen entfernt * Seitenverhältnis wird automatisch beibehalten, wenn nur eine Dimension angegeben wird * Komprimierte Dateien werden auf nativen Plattformen in temporären Verzeichnissen gespeichert ### getPluginVersion() [Section titled “getPluginVersion()”](#getpluginversion) Ruft die native Plugin-Version ab. ```typescript const { version } = await FileCompressor.getPluginVersion(); console.log('Plugin-Version:', version); ``` ## Vollständiges Beispiel [Section titled “Vollständiges Beispiel”](#vollständiges-beispiel) ```typescript import { FileCompressor } from '@capgo/capacitor-file-compressor'; import { Camera } from '@capacitor/camera'; export class ImageCompressionService { async captureAndCompress() { try { // Foto aufnehmen const photo = await Camera.getPhoto({ quality: 100, allowEditing: false, resultType: 'uri' }); if (!photo.path) { throw new Error('Kein Bildpfad'); } // Foto komprimieren const compressed = await FileCompressor.compressImage({ source: photo.path, quality: 0.7, width: 1920, height: 1080, format: 'jpeg' }); console.log(`Komprimierungsverhältnis: ${ ((1 - compressed.size / compressed.originalSize) * 100).toFixed(1) }%`); return compressed.path; } catch (error) { console.error('Komprimierung fehlgeschlagen:', error); throw error; } } async batchCompress(imagePaths: string[]) { const results = []; for (const path of imagePaths) { try { const result = await FileCompressor.compressImage({ source: path, quality: 0.8, width: 1280 }); results.push(result); } catch (error) { console.error(`Komprimierung von ${path} fehlgeschlagen:`, error); } } return results; } } ``` ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) 1. **Qualitätseinstellungen**: Beginnen Sie mit Qualität 0.8 für eine gute Balance zwischen Dateigröße und Bildqualität 2. **Größenänderungsabmessungen**: Geben Sie nur Abmessungen an, wenn nötig - Seitenverhältnis wird automatisch beibehalten 3. **Formatauswahl**: Verwenden Sie JPEG für Fotos und WebP für bessere Komprimierung (nur Android/Web) 4. **Fehlerbehandlung**: Umschließen Sie Komprimierungsaufrufe immer mit try-catch-Blöcken 5. **Aufräumen**: Denken Sie daran, temporäre Dateien nach Gebrauch zu bereinigen ## Fehlerbehebung [Section titled “Fehlerbehebung”](#fehlerbehebung) ### Häufige Probleme [Section titled “Häufige Probleme”](#häufige-probleme) **Bild wird nicht komprimiert**: Stellen Sie sicher, dass der Quellpfad gültig und zugänglich ist **Nicht genügend Speicher**: Reduzieren Sie die Zielabmessungen oder komprimieren Sie Bilder einzeln **Format nicht unterstützt**: Überprüfen Sie die Plattformunterstützungstabelle oben # Erste Schritte > Erfahren Sie, wie Sie das File-Plugin für Dateisystemoperationen in Ihrer Capacitor-App installieren und verwenden. 1. **Paket installieren** * npm ```sh npm i @capgo/capacitor-file ``` * pnpm ```sh pnpm add @capgo/capacitor-file ``` * yarn ```sh yarn add @capgo/capacitor-file ``` * bun ```sh bun add @capgo/capacitor-file ``` 2. **Mit nativen Projekten synchronisieren** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Verwendung [Section titled “Verwendung”](#verwendung) Importieren Sie das Plugin und verwenden Sie seine Methoden für Dateioperationen: ```typescript import { CapacitorFile, Directory, Encoding } from '@capgo/capacitor-file'; // Textdatei schreiben const writeFile = async () => { const result = await CapacitorFile.writeFile({ path: 'meine-datei.txt', directory: Directory.Documents, data: 'Hallo, Welt!', encoding: Encoding.UTF8, recursive: true, // Elternverzeichnisse erstellen, falls erforderlich }); console.log('Datei geschrieben in:', result.uri); }; // Textdatei lesen const readFile = async () => { const result = await CapacitorFile.readFile({ path: 'meine-datei.txt', directory: Directory.Documents, encoding: Encoding.UTF8, }); console.log('Dateiinhalt:', result.data); }; // Prüfen, ob Datei existiert const checkExists = async () => { const result = await CapacitorFile.exists({ path: 'meine-datei.txt', directory: Directory.Documents, }); console.log('Datei existiert:', result.exists); }; // Datei löschen const deleteFile = async () => { await CapacitorFile.deleteFile({ path: 'meine-datei.txt', directory: Directory.Documents, }); }; ``` ## Verzeichnisspeicherorte [Section titled “Verzeichnisspeicherorte”](#verzeichnisspeicherorte) Das Plugin stellt mehrere Verzeichniskonstanten bereit: ```typescript import { Directory } from '@capgo/capacitor-file'; // Verfügbare Verzeichnisse Directory.Documents // Benutzersichtbare Dokumente (gesichert) Directory.Data // Private App-Datenspeicherung Directory.Library // App-Support-Dateien (iOS) / files (Android) Directory.Cache // Temporärer Cache (kann vom OS gelöscht werden) Directory.External // Externer Speicher (nur Android) Directory.Application // Schreibgeschütztes App-Bundle ``` ## Kodierungsoptionen [Section titled “Kodierungsoptionen”](#kodierungsoptionen) ```typescript import { Encoding } from '@capgo/capacitor-file'; Encoding.UTF8 // UTF-8-Textkodierung Encoding.ASCII // ASCII-Textkodierung Encoding.UTF16 // UTF-16-Textkodierung // Kodierung weglassen für Binärdaten (gibt base64 zurück) ``` ## API-Referenz [Section titled “API-Referenz”](#api-referenz) ### writeFile(options) [Section titled “writeFile(options)”](#writefileoptions) Daten in eine Datei schreiben. ```typescript const result = await CapacitorFile.writeFile({ path: 'ordner/datei.txt', directory: Directory.Documents, data: 'Dateiinhalt', encoding: Encoding.UTF8, recursive: true, // Verzeichnisse erstellen, falls erforderlich append: false, // Vorhandene Datei überschreiben position: 0, // Byte-Position für zufällige Zugriffschreibvorgänge }); // Gibt zurück: { uri: string } ``` ### readFile(options) [Section titled “readFile(options)”](#readfileoptions) Inhalt einer Datei lesen. ```typescript const result = await CapacitorFile.readFile({ path: 'datei.txt', directory: Directory.Documents, encoding: Encoding.UTF8, offset: 0, // Lesen ab Byte-Offset beginnen length: 100, // Nur diese Anzahl von Bytes lesen }); // Gibt zurück: { data: string } ``` ### appendFile(options) [Section titled “appendFile(options)”](#appendfileoptions) Daten an eine Datei anhängen. ```typescript await CapacitorFile.appendFile({ path: 'datei.txt', directory: Directory.Documents, data: '\nNeue Zeile', encoding: Encoding.UTF8, }); ``` ### deleteFile(options) [Section titled “deleteFile(options)”](#deletefileoptions) Datei löschen. ```typescript await CapacitorFile.deleteFile({ path: 'datei.txt', directory: Directory.Documents, }); ``` ### mkdir(options) [Section titled “mkdir(options)”](#mkdiroptions) Verzeichnis erstellen. ```typescript await CapacitorFile.mkdir({ path: 'mein-ordner/unterordner', directory: Directory.Documents, recursive: true, // Elternverzeichnisse erstellen }); ``` ### rmdir(options) [Section titled “rmdir(options)”](#rmdiroptions) Verzeichnis löschen. ```typescript await CapacitorFile.rmdir({ path: 'mein-ordner', directory: Directory.Documents, recursive: true, // Inhalte rekursiv löschen }); ``` ### readdir(options) [Section titled “readdir(options)”](#readdiroptions) Verzeichnisinhalt auflisten. ```typescript const result = await CapacitorFile.readdir({ path: '', // Leer für Wurzel des Verzeichnisses directory: Directory.Documents, }); // Gibt zurück: { entries: Entry[] } // Jeder Eintrag hat: { name, isFile, isDirectory, fullPath, nativeURL } ``` ### stat(options) [Section titled “stat(options)”](#statoptions) Datei- oder Verzeichnismetadaten abrufen. ```typescript const result = await CapacitorFile.stat({ path: 'datei.txt', directory: Directory.Documents, }); // Gibt zurück: { type, size, mtime, ctime, uri } ``` ### exists(options) [Section titled “exists(options)”](#existsoptions) Prüfen, ob eine Datei oder ein Verzeichnis existiert. ```typescript const result = await CapacitorFile.exists({ path: 'datei.txt', directory: Directory.Documents, }); // Gibt zurück: { exists: boolean, type?: 'file' | 'directory' } ``` ### copy(options) [Section titled “copy(options)”](#copyoptions) Datei oder Verzeichnis kopieren. ```typescript const result = await CapacitorFile.copy({ from: 'quelle.txt', to: 'ziel.txt', directory: Directory.Documents, toDirectory: Directory.Documents, }); // Gibt zurück: { uri: string } ``` ### rename(options) / move(options) [Section titled “rename(options) / move(options)”](#renameoptions--moveoptions) Datei oder Verzeichnis umbenennen oder verschieben. ```typescript await CapacitorFile.rename({ from: 'alter-name.txt', to: 'neuer-name.txt', directory: Directory.Documents, toDirectory: Directory.Documents, }); ``` ### truncate(options) [Section titled “truncate(options)”](#truncateoptions) Datei auf bestimmte Größe kürzen. ```typescript await CapacitorFile.truncate({ path: 'datei.txt', directory: Directory.Documents, size: 100, // Auf 100 Bytes kürzen }); ``` ### getUri(options) [Section titled “getUri(options)”](#geturioptions) Native URI für eine Datei abrufen. ```typescript const result = await CapacitorFile.getUri({ path: 'datei.txt', directory: Directory.Documents, }); // Gibt zurück: { uri: string } ``` ### getDirectories() [Section titled “getDirectories()”](#getdirectories) Alle verfügbaren Verzeichnispfade abrufen. ```typescript const dirs = await CapacitorFile.getDirectories(); // Gibt Pfade zurück für: documents, data, cache, external, etc. ``` ### getFreeDiskSpace() [Section titled “getFreeDiskSpace()”](#getfreediskspace) Verfügbaren Speicherplatz abrufen. ```typescript const result = await CapacitorFile.getFreeDiskSpace(); console.log('Freier Speicherplatz:', result.free, 'bytes'); ``` ### checkPermissions() / requestPermissions() [Section titled “checkPermissions() / requestPermissions()”](#checkpermissions--requestpermissions) Speicherberechtigungen handhaben (Android). ```typescript const status = await CapacitorFile.checkPermissions(); if (status.publicStorage !== 'granted') { await CapacitorFile.requestPermissions(); } ``` ## Vollständiges Beispiel [Section titled “Vollständiges Beispiel”](#vollständiges-beispiel) ```typescript import { CapacitorFile, Directory, Encoding } from '@capgo/capacitor-file'; export class FileService { async saveJson(filename: string, data: object): Promise { await CapacitorFile.writeFile({ path: filename, directory: Directory.Documents, data: JSON.stringify(data, null, 2), encoding: Encoding.UTF8, recursive: true, }); } async loadJson(filename: string): Promise { try { const { exists } = await CapacitorFile.exists({ path: filename, directory: Directory.Documents, }); if (!exists) return null; const result = await CapacitorFile.readFile({ path: filename, directory: Directory.Documents, encoding: Encoding.UTF8, }); return JSON.parse(result.data) as T; } catch (error) { console.error('JSON konnte nicht geladen werden:', error); return null; } } async listFiles(folder: string = ''): Promise { const result = await CapacitorFile.readdir({ path: folder, directory: Directory.Documents, }); return result.entries .filter(entry => entry.isFile) .map(entry => entry.name); } async deleteAll(folder: string): Promise { const { exists } = await CapacitorFile.exists({ path: folder, directory: Directory.Documents, }); if (exists) { await CapacitorFile.rmdir({ path: folder, directory: Directory.Documents, recursive: true, }); } } async copyToBackup(filename: string): Promise { const timestamp = Date.now(); const backupName = `backup/${timestamp}-${filename}`; const result = await CapacitorFile.copy({ from: filename, to: backupName, directory: Directory.Documents, toDirectory: Directory.Documents, }); return result.uri; } } ``` ## Plattformhinweise [Section titled “Plattformhinweise”](#plattformhinweise) ### iOS [Section titled “iOS”](#ios) * Erfordert iOS 13.0+ * Documents-Verzeichnis ist in der Dateien-App sichtbar * Library-Verzeichnis ist für App-Support-Dateien * Cache kann gelöscht werden, wenn der Gerätespeicher knapp wird ### Android [Section titled “Android”](#android) * Erfordert Android 6.0 (API 23)+ * Externer Speicher erfordert Laufzeitberechtigungen auf älteren Android-Versionen * Documents-Verzeichnis wird auf das files-Verzeichnis der App gemappt * Verwenden Sie External-Verzeichnis für gemeinsamen Speicherzugriff ### Web [Section titled “Web”](#web) * Nicht auf Web-Plattform unterstützt # @capgo/capacitor-flash > Schalten Sie die Taschenlampe/Blitz Ihres Geräts programmatisch ein und aus mit diesem leichtgewichtigen Capacitor-Plugin. Einfache API Taschenlampe ein/aus mit nur einem Methodenaufruf 🚀 Plattformübergreifend Funktioniert auf iOS-, Android- und Web-Plattformen 📱 Leichtgewichtig Minimaler Footprint ohne Abhängigkeiten ⚡ Umfassende Dokumentation Schauen Sie sich die [Dokumentation](/docs/plugins/flash/getting-started/) an, um das Plugin in nur wenigen Minuten zu meistern. # Erste Schritte > Erfahren Sie, wie Sie das Flash-Plugin installieren und verwenden, um die Taschenlampe Ihres Geräts in Ihrer Capacitor-App zu steuern. 1. **Paket installieren** * npm ```sh npm i @capgo/capacitor-flash ``` * pnpm ```sh pnpm add @capgo/capacitor-flash ``` * yarn ```sh yarn add @capgo/capacitor-flash ``` * bun ```sh bun add @capgo/capacitor-flash ``` 2. **Mit nativen Projekten synchronisieren** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Verwendung [Section titled “Verwendung”](#verwendung) Importieren Sie das Plugin und verwenden Sie seine Methoden zur Steuerung der Taschenlampe: ```typescript import { CapacitorFlash } from '@capgo/capacitor-flash'; // Verfügbarkeit der Taschenlampe prüfen const checkFlashlight = async () => { const { value } = await CapacitorFlash.isAvailable(); console.log('Taschenlampe verfügbar:', value); }; // Taschenlampe einschalten const turnOn = async () => { await CapacitorFlash.switchOn(); }; // Taschenlampe ausschalten const turnOff = async () => { await CapacitorFlash.switchOff(); }; // Prüfen, ob Taschenlampe eingeschaltet ist const checkStatus = async () => { const { value } = await CapacitorFlash.isSwitchedOn(); console.log('Taschenlampe ist an:', value); }; // Taschenlampe umschalten const toggle = async () => { await CapacitorFlash.toggle(); }; ``` ## API-Referenz [Section titled “API-Referenz”](#api-referenz) ### isAvailable() [Section titled “isAvailable()”](#isavailable) Prüft, ob eine Taschenlampe auf dem Gerät verfügbar ist. ```typescript const result = await CapacitorFlash.isAvailable(); // Gibt zurück: { value: boolean } ``` ### switchOn(options?) [Section titled “switchOn(options?)”](#switchonoptions) Schaltet die Taschenlampe ein. ```typescript interface SwitchOnOptions { intensity?: number; // 0.0 bis 1.0 (nur iOS) } await CapacitorFlash.switchOn({ intensity: 0.5 }); ``` ### switchOff() [Section titled “switchOff()”](#switchoff) Schaltet die Taschenlampe aus. ```typescript await CapacitorFlash.switchOff(); ``` ### isSwitchedOn() [Section titled “isSwitchedOn()”](#isswitchedon) Prüft, ob die Taschenlampe derzeit eingeschaltet ist. ```typescript const result = await CapacitorFlash.isSwitchedOn(); // Gibt zurück: { value: boolean } ``` ### toggle() [Section titled “toggle()”](#toggle) Schaltet die Taschenlampe ein/aus. ```typescript await CapacitorFlash.toggle(); ``` ## Vollständiges Beispiel [Section titled “Vollständiges Beispiel”](#vollständiges-beispiel) ```typescript import { CapacitorFlash } from '@capgo/capacitor-flash'; export class FlashlightService { private isOn = false; async init() { const { value } = await CapacitorFlash.isAvailable(); if (!value) { throw new Error('Taschenlampe auf diesem Gerät nicht verfügbar'); } } async toggle() { if (this.isOn) { await CapacitorFlash.switchOff(); } else { await CapacitorFlash.switchOn(); } this.isOn = !this.isOn; } async turnOn(intensity?: number) { await CapacitorFlash.switchOn({ intensity }); this.isOn = true; } async turnOff() { await CapacitorFlash.switchOff(); this.isOn = false; } async strobe(intervalMs: number = 100, duration: number = 3000) { const endTime = Date.now() + duration; while (Date.now() < endTime) { await CapacitorFlash.toggle(); await new Promise(resolve => setTimeout(resolve, intervalMs)); } // Sicherstellen, dass die Taschenlampe nach dem Stroboskop ausgeschaltet ist await CapacitorFlash.switchOff(); this.isOn = false; } } ``` ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) 1. **Verfügbarkeit zuerst prüfen** Überprüfen Sie immer die Verfügbarkeit der Taschenlampe, bevor Sie sie verwenden: ```typescript const { value } = await CapacitorFlash.isAvailable(); if (!value) { // Alternative UI anzeigen oder Taschenlampenfunktionen deaktivieren } ``` 2. **Fehler elegant behandeln** ```typescript try { await CapacitorFlash.switchOn(); } catch (error) { console.error('Taschenlampe konnte nicht eingeschaltet werden:', error); } ``` 3. **Ausschalten, wenn nicht benötigt** Schalten Sie die Taschenlampe immer aus, wenn Ihre App in den Hintergrund geht oder wenn die Funktion nicht mehr benötigt wird, um Batterie zu sparen. 4. **Schnelles Umschalten vermeiden** Implementieren Sie Debouncing, um schnelles Ein-/Ausschalten zu verhindern, das die LED beschädigen oder die Batterie entleeren könnte. ## Plattformhinweise [Section titled “Plattformhinweise”](#plattformhinweise) ### iOS [Section titled “iOS”](#ios) * Erfordert iOS 10.0+ * Verwendet `AVCaptureDevice` Taschenlampen-Modus * Unterstützt Intensitätssteuerung (0.0 bis 1.0) ### Android [Section titled “Android”](#android) * Erfordert Android 6.0 (API 23)+ * Verwendet Camera2 API * Intensitätssteuerung nicht unterstützt (verwendet Standardhelligkeit) ### Web [Section titled “Web”](#web) * Unterstützt auf Browsern mit MediaDevices API und Taschenlampenfähigkeit * Verwendet den Kamera-Stream, um auf die Taschenlampe des Geräts zuzugreifen * Erfordert Benutzerberechtigung für Kamerazugriff * Funktioniert am besten auf mobilen Browsern (Chrome auf Android) * Desktop-Browser geben normalerweise `isAvailable: false` zurück (keine Taschenlampenhardware) * Intensitätssteuerung nicht auf Web unterstützt # @capgo/capacitor-gtm > Integrieren Sie Google Tag Manager in Ihre mobilen Anwendungen mit dem offiziellen SDK für umfassendes Analytics-Tracking. Offizielles SDK Verwendet offizielles Google Tag Manager SDK für iOS und Android 🏆 Ereignisverfolgung Ereignisse mit benutzerdefinierten Parametern an dataLayer senden 📊 Benutzereigenschaften Benutzereigenschaften für Segmentierung festlegen und verwalten 👤 Container-Verwaltung GTM-Container einfach initialisieren und verwalten 📦 DataLayer-Zugriff Werte aus der Container-Konfiguration abrufen 🔍 Erste Schritte Schauen Sie sich die [Anleitung für erste Schritte](/docs/plugins/gtm/getting-started/) an, um das Plugin zu installieren und zu konfigurieren. # Erste Schritte > Erfahren Sie, wie Sie Google Tag Manager mit dem offiziellen SDK in Ihre Capacitor-App installieren und integrieren. ## Installation [Section titled “Installation”](#installation) * npm ```bash npm install @capgo/capacitor-gtm npx cap sync ``` * yarn ```bash yarn add @capgo/capacitor-gtm npx cap sync ``` * pnpm ```bash pnpm add @capgo/capacitor-gtm npx cap sync ``` * bun ```bash bun add @capgo/capacitor-gtm npx cap sync ``` ## Plattformkonfiguration [Section titled “Plattformkonfiguration”](#plattformkonfiguration) ### iOS [Section titled “iOS”](#ios) 1. Laden Sie Ihre GTM-Container-Konfigurationsdatei von der Google Tag Manager-Konsole herunter 2. Fügen Sie die Container-JSON-Datei zu Ihrem iOS-Projekt in Xcode hinzu 3. Die Datei sollte etwa `GTM-XXXXXX.json` heißen ### Android [Section titled “Android”](#android) 1. Laden Sie Ihre GTM-Container-Konfigurationsdatei von der Google Tag Manager-Konsole herunter 2. Fügen Sie die Container-JSON-Datei zu `android/app/src/main/assets/` hinzu 3. Die Datei sollte etwa `GTM-XXXXXX.json` heißen ## Verwendungsbeispiel [Section titled “Verwendungsbeispiel”](#verwendungsbeispiel) ```typescript import { CapacitorGTM } from '@capgo/capacitor-gtm'; // GTM mit Ihrer Container-ID initialisieren await CapacitorGTM.init({ containerId: 'GTM-XXXXXX' }); // Ereignisse an dataLayer senden await CapacitorGTM.pushEvent({ event: 'screen_view', parameters: { screen_name: 'Home', screen_class: 'HomeScreen' } }); // Benutzerdefinierte Ereignisse mit Parametern senden await CapacitorGTM.pushEvent({ event: 'purchase', parameters: { transaction_id: 'T12345', value: 25.00, currency: 'USD', items: [ { item_id: 'SKU_12345', item_name: 'Produktname', quantity: 1, price: 25.00 } ] } }); // Benutzereigenschaften festlegen await CapacitorGTM.setUserProperty({ key: 'user_type', value: 'premium' }); // Werte aus dataLayer abrufen const result = await CapacitorGTM.getVariable({ key: 'config_value' }); console.log('Konfigurationswert:', result.value); ``` ## API-Referenz [Section titled “API-Referenz”](#api-referenz) ### init(options) [Section titled “init(options)”](#initoptions) ```typescript init(options: { containerId: string }) => Promise ``` Initialisiert Google Tag Manager mit Ihrer Container-ID. | Param | Typ | | ------------- | ------------------------- | | **`options`** | `{ containerId: string }` | ### pushEvent(options) [Section titled “pushEvent(options)”](#pusheventoptions) ```typescript pushEvent(options: GTMEvent) => Promise ``` Sendet ein Ereignis mit optionalen Parametern an den dataLayer. | Param | Typ | | ------------- | ---------- | | **`options`** | `GTMEvent` | ### setUserProperty(options) [Section titled “setUserProperty(options)”](#setuserpropertyoptions) ```typescript setUserProperty(options: { key: string; value: string }) => Promise ``` Legt eine Benutzereigenschaft für Segmentierung und Targeting fest. | Param | Typ | | ------------- | -------------------------------- | | **`options`** | `{ key: string; value: string }` | ### getVariable(options) [Section titled “getVariable(options)”](#getvariableoptions) ```typescript getVariable(options: { key: string }) => Promise<{ value: any }> ``` Ruft einen Wert aus der GTM-Container-Konfiguration ab. | Param | Typ | | ------------- | ----------------- | | **`options`** | `{ key: string }` | **Gibt zurück:** `Promise<{ value: any }>` ## Schnittstellen [Section titled “Schnittstellen”](#schnittstellen) ### GTMEvent [Section titled “GTMEvent”](#gtmevent) | Prop | Typ | Beschreibung | | ---------------- | --------------------- | -------------------------------------- | | **`event`** | `string` | Der Ereignisname | | **`parameters`** | `Record` | Optionale Ereignisparameter (optional) | ## Häufige Ereignisbeispiele [Section titled “Häufige Ereignisbeispiele”](#häufige-ereignisbeispiele) ### Bildschirmansichten [Section titled “Bildschirmansichten”](#bildschirmansichten) ```typescript await CapacitorGTM.pushEvent({ event: 'screen_view', parameters: { screen_name: 'Produktdetails', screen_class: 'ProductScreen' } }); ``` ### Benutzeraktionen [Section titled “Benutzeraktionen”](#benutzeraktionen) ```typescript await CapacitorGTM.pushEvent({ event: 'button_click', parameters: { button_name: 'add_to_cart', product_id: 'SKU_12345' } }); ``` ### E-Commerce-Ereignisse [Section titled “E-Commerce-Ereignisse”](#e-commerce-ereignisse) ```typescript await CapacitorGTM.pushEvent({ event: 'begin_checkout', parameters: { currency: 'USD', value: 75.00, items: [ { item_id: 'SKU_12345', item_name: 'Produkt 1', quantity: 2, price: 25.00 }, { item_id: 'SKU_67890', item_name: 'Produkt 2', quantity: 1, price: 25.00 } ] } }); ``` ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) * Initialisieren Sie GTM früh im Lebenszyklus Ihrer App * Verwenden Sie konsistente Ereignisbenennungskonventionen * Fügen Sie relevante Parameter zu jedem Ereignis hinzu * Testen Sie Ihre GTM-Konfiguration im Vorschaumodus * Überwachen Sie Daten in der Google Tag Manager-Konsole * Verwenden Sie Benutzereigenschaften für sinnvolle Segmentierung ## Debugging [Section titled “Debugging”](#debugging) Aktivieren Sie den Debug-Modus in der Google Tag Manager-Konsole, um Ihre Implementierung zu testen: 1. Öffnen Sie die GTM-Konsole 2. Gehen Sie in den Vorschaumodus 3. Verbinden Sie sich mit Ihrer App über die Container-ID 4. Überprüfen Sie, ob Ereignisse korrekt verfolgt werden ## Anwendungsfälle [Section titled “Anwendungsfälle”](#anwendungsfälle) * Benutzerinteraktionen und -verhalten verfolgen * App-Leistungsmetriken überwachen * E-Commerce-Daten sammeln * Drittanbieter-Tags ohne Codeänderungen verwalten * A/B-Tests und Personalisierung implementieren * Mit Google Analytics und anderen Diensten integrieren # @capgo/capacitor-health > Lesen und schreiben Sie Fitnessmetriken auf iOS und Android, während Sie einen konsistenten TypeScript-Vertrag beibehalten. Das Capgo Health-Plugin vereinheitlicht den Zugriff auf HealthKit und Health Connect, sodass Sie Aktivität, biometrische Metriken und Körpermessungen mit einer Capacitor-Integration verfolgen können. Plattformübergreifende Gesundheitsdaten Unterstützen Sie Apple HealthKit und Android Health Connect sofort einsatzbereit. Granulare Berechtigungen Fordern Sie separate Lese-/Schreibbereiche an und reagieren Sie auf Autorisierungsänderungen. Konsistente Einheiten Arbeiten Sie mit normalisierten Datentypen wie `steps`, `heartRate` und `weight`. Echtzeit-Schreibvorgänge Speichern Sie Proben zurück im Gesundheitsspeicher des Benutzers, wenn er Aktivitäten protokolliert. Folgen Sie der Anleitung für erste Schritte, um Plattformfähigkeiten zu aktivieren, Zustimmung einzuholen und mit dem Synchronisieren von Metriken zu beginnen. # Erste Schritte > Aktivieren Sie HealthKit- und Health Connect-Zugriff mit dem Capgo Health-Plugin. 1. **Plugin installieren** * npm ```sh npm i @capgo/capacitor-health ``` * pnpm ```sh pnpm add @capgo/capacitor-health ``` * yarn ```sh yarn add @capgo/capacitor-health ``` * bun ```sh bun add @capgo/capacitor-health ``` 2. **Native Projekte synchronisieren** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## iOS-Konfiguration [Section titled “iOS-Konfiguration”](#ios-konfiguration) * Öffnen Sie `ios/App/App.xcworkspace` in Xcode und aktivieren Sie die **HealthKit**-Fähigkeit. * Geben Sie Nutzungsbeschreibungen in `Info.plist` an: ```xml NSHealthShareUsageDescription Diese App liest Ihre Gesundheitsmetriken, um Ihr Erlebnis zu personalisieren. NSHealthUpdateUsageDescription Diese App schreibt Gesundheitsdaten, die Sie explizit aufzeichnen. ``` ## Android-Konfiguration [Section titled “Android-Konfiguration”](#android-konfiguration) Gesundheitsdaten werden von [Health Connect](https://developer.android.com/health-and-fitness/guides/health-connect) bereitgestellt. Stellen Sie sicher, dass: * `minSdkVersion` **26 oder höher** ist (Capacitor 5 erfüllt dies bereits). * Benutzer Health Connect installiert haben (Android 14+ enthält es; frühere Versionen erfordern einen Play Store-Download). * Sie Benutzer durch die Laufzeitberechtigungs-UI führen, wenn Sie um Zugriff bitten. ## Verfügbarkeit prüfen und Zugriff anfordern [Section titled “Verfügbarkeit prüfen und Zugriff anfordern”](#verfügbarkeit-prüfen-und-zugriff-anfordern) ```typescript import { Health } from '@capgo/capacitor-health'; const { available, reason } = await Health.isAvailable(); if (!available) { console.warn('Health-APIs nicht verfügbar:', reason); return; } await Health.requestAuthorization({ read: ['steps', 'heartRate', 'weight'], write: ['weight'], }); ``` ## Proben lesen [Section titled “Proben lesen”](#proben-lesen) ```typescript const { samples } = await Health.readSamples({ dataType: 'steps', startDate: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString(), endDate: new Date().toISOString(), limit: 200, }); samples.forEach((sample) => { console.log(sample.value, sample.startDate, sample.endDate); }); ``` ## Proben schreiben [Section titled “Proben schreiben”](#proben-schreiben) ```typescript await Health.saveSample({ dataType: 'weight', value: 74.6, startDate: new Date().toISOString(), }); ``` ## Autorisierungsverwaltung [Section titled “Autorisierungsverwaltung”](#autorisierungsverwaltung) * Verwenden Sie `checkAuthorization()`, um aktuelle Lese-/Schreibbereiche zu überprüfen und Ihre UI anzupassen. * Behandeln Sie Ablehnungen elegant, indem Sie erklären, warum die Daten das Erlebnis verbessern, und bieten Sie eine Wiederholung an. * Respektieren Sie immer die Privatsphäre des Benutzers und fordern Sie nur die minimalen Bereiche an, die Ihre Funktion benötigt. # @capgo/home-indicator > Blenden Sie den Home-Indicator auf iOS-Geräten aus oder ein, um wirklich immersive Vollbilderlebnisse in Ihrer App zu schaffen. iOS Home-Indicator-Steuerung Vollständige Kontrolle über die Home-Indicator-Sichtbarkeit 📱 Immersive Erlebnisse Schaffen Sie Vollbilderlebnisse ohne Ablenkung 🎮 Auto-Hide-Unterstützung Konfigurieren Sie Auto-Hide-Verhalten für nahtlose UX ✨ Umfassende Dokumentation Lesen Sie die [Dokumentation](/docs/plugins/home-indicator/getting-started/), um das Plugin in wenigen Minuten zu beherrschen. # Erste Schritte > Erfahren Sie, wie Sie das Capacitor Home Indicator-Plugin installieren und konfigurieren, um den Home-Indikator auf iOS-Geräten zu steuern. 1. **Paket installieren** * npm ```sh npm i @capgo/home-indicator ``` * pnpm ```sh pnpm add @capgo/home-indicator ``` * yarn ```sh yarn add @capgo/home-indicator ``` * bun ```sh bun add @capgo/home-indicator ``` 2. **Mit nativen Projekten synchronisieren** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` 3. **Plugin konfigurieren** **Home-Indikator ausblenden:** ```typescript import { HomeIndicator } from '@capgo/home-indicator'; // Home-Indikator ausblenden await HomeIndicator.hide(); ``` **Home-Indikator anzeigen:** ```typescript // Home-Indikator anzeigen await HomeIndicator.show(); // Sichtbarkeitsstatus prüfen const { hidden } = await HomeIndicator.isHidden(); console.log('Ist ausgeblendet:', hidden); ``` * iOS Keine zusätzliche Einrichtung erforderlich. Das Plugin funktioniert nur auf iOS-Geräten mit Home-Indikator (iPhone X und neuer). * Android Dieses Plugin hat keine Auswirkungen auf Android-Geräte, da sie keinen iOS-ähnlichen Home-Indikator haben. 4. **Auto-Hide-Konfiguration** ```typescript import { HomeIndicator } from '@capgo/home-indicator'; // Auto-Hide-Verhalten konfigurieren await HomeIndicator.setAutoHidden({ autoHidden: true // Auto-Hide aktivieren }); // Der Home-Indikator wird nach einigen Sekunden automatisch ausgeblendet // und erscheint wieder, wenn der Benutzer den Bildschirm berührt ``` 5. **Erweiterte Verwendung** ```typescript import { HomeIndicator } from '@capgo/home-indicator'; import { App } from '@capacitor/app'; export class ImmersiveMode { private isImmersive = false; async enterImmersiveMode() { this.isImmersive = true; // Home-Indikator ausblenden await HomeIndicator.hide(); // Auto-Hide für bessere UX aktivieren await HomeIndicator.setAutoHidden({ autoHidden: true }); // Statusleiste für vollständige Immersion ausblenden // StatusBar.hide(); // Bei Verwendung von @capacitor/status-bar } async exitImmersiveMode() { this.isImmersive = false; // Home-Indikator anzeigen await HomeIndicator.show(); // Auto-Hide deaktivieren await HomeIndicator.setAutoHidden({ autoHidden: false }); // Statusleiste anzeigen // StatusBar.show(); // Bei Verwendung von @capacitor/status-bar } async toggleImmersiveMode() { const { hidden } = await HomeIndicator.isHidden(); if (hidden) { await this.exitImmersiveMode(); } else { await this.enterImmersiveMode(); } } setupAppLifecycle() { // App-Statusänderungen behandeln App.addListener('appStateChange', async ({ isActive }) => { if (!isActive && this.isImmersive) { // App ging in den Hintergrund, möglicherweise Indikator anzeigen await HomeIndicator.show(); } else if (isActive && this.isImmersive) { // App kam in den Vordergrund, immersiven Modus wiederherstellen await HomeIndicator.hide(); } }); } } ``` ## API-Referenz [Section titled “API-Referenz”](#api-referenz) ### Methoden [Section titled “Methoden”](#methoden) #### `hide()` [Section titled “hide()”](#hide) Blendet den Home-Indikator aus. **Gibt zurück:** `Promise` #### `show()` [Section titled “show()”](#show) Zeigt den Home-Indikator an. **Gibt zurück:** `Promise` #### `isHidden()` [Section titled “isHidden()”](#ishidden) Prüft, ob der Home-Indikator derzeit ausgeblendet ist. **Gibt zurück:** `Promise<{ hidden: boolean }>` #### `setAutoHidden(options: { autoHidden: boolean })` [Section titled “setAutoHidden(options: { autoHidden: boolean })”](#setautohiddenoptions--autohidden-boolean) Konfiguriert das Auto-Hide-Verhalten für den Home-Indikator. **Parameter:** * `options.autoHidden`: boolean - Auto-Hide aktivieren oder deaktivieren **Gibt zurück:** `Promise` ### Schnittstellen [Section titled “Schnittstellen”](#schnittstellen) ```typescript interface AutoHiddenOptions { autoHidden: boolean; } interface HiddenResult { hidden: boolean; } ``` ## Plattformhinweise [Section titled “Plattformhinweise”](#plattformhinweise) ### iOS [Section titled “iOS”](#ios) * Funktioniert nur auf Geräten mit Home-Indikator (iPhone X und neuer) * Erfordert iOS 11.0 oder neuer * Auto-Hide macht den Indikator durchscheinend und blendet ihn nach Inaktivität aus * Der Indikator erscheint wieder, wenn Benutzer von unten wischen ### Android [Section titled “Android”](#android) * Dieses Plugin hat keine Auswirkungen auf Android * Android verwendet unterschiedliche Navigationsgesten/-tasten ## Häufige Anwendungsfälle [Section titled “Häufige Anwendungsfälle”](#häufige-anwendungsfälle) 1. **Spiele**: Vollbild-Gaming ohne Ablenkungen 2. **Videoplayer**: Immersive Videowiedergabe 3. **Fotobetrachter**: Vollbild-Fotogalerien 4. **Präsentationen**: Ablenkungsfreie Präsentationen 5. **Kiosk-Apps**: Öffentliche Anzeigeanwendungen ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) 1. **Benutzerkontrolle**: Bieten Sie immer eine Möglichkeit, den immersiven Modus zu verlassen ```typescript // Geste oder Button zum Umschalten des immersiven Modus hinzufügen const toggleButton = document.getElementById('toggle-immersive'); toggleButton.addEventListener('click', () => { immersiveMode.toggleImmersiveMode(); }); ``` 2. **Auto-Hide für Spiele**: Verwenden Sie Auto-Hide für besseres Spielerlebnis ```typescript // Für Spiele bietet Auto-Hide die beste Balance await HomeIndicator.setAutoHidden({ autoHidden: true }); ``` 3. **Systemgesten respektieren**: Beeinträchtigen Sie nicht die Systemnavigation 4. **Status speichern**: Merken Sie sich die Präferenz des Benutzers für den immersiven Modus ## Fehlerbehebung [Section titled “Fehlerbehebung”](#fehlerbehebung) **Home-Indikator wird nicht ausgeblendet:** * Überprüfen Sie, ob das Gerät einen Home-Indikator hat (iPhone X+) * Prüfen Sie, ob die iOS-Version 11.0 oder höher ist * Stellen Sie sicher, dass die App den Fokus hat **Auto-Hide funktioniert nicht:** * Auto-Hide erfordert Benutzerinteraktion zur Aktivierung * Der Indikator wird durchscheinend, nicht vollständig ausgeblendet **Indikator erscheint unerwartet wieder:** * Dies ist normales iOS-Verhalten für Systemgesten * Verwenden Sie Auto-Hide für weniger aufdringliches Verhalten # @capgo/capacitor-ibeacon > Erkennen und überwachen Sie Bluetooth-Beacons für Näherungs-basierte Funktionen, Innennavigation und standortbewusste Erlebnisse. Beacon-Überwachung Überwachen Sie Beacon-Bereiche und erhalten Sie Benachrichtigungen beim Eintritt oder Austritt 🎯 Beacon-Ranging Erkennen Sie in der Nähe befindliche Beacons und messen Sie deren Abstand in Echtzeit 📡 Hintergrund-Erkennung Überwachen Sie Beacons auch wenn die App im Hintergrund läuft 🔔 Beacon-Übertragung Verwandeln Sie Ihr Gerät in einen iBeacon-Transmitter (nur iOS) 📱 Plattformübergreifend Funktioniert auf iOS und Android mit nativen Beacon-Bibliotheken 📱 Umfassende Dokumentation Lesen Sie die [Dokumentation](/docs/plugins/ibeacon/getting-started/), um das Plugin in wenigen Minuten zu beherrschen. # Erste Schritte > Erfahren Sie, wie Sie das iBeacon-Plugin installieren und verwenden, um Bluetooth-Beacons in Ihrer Capacitor-App zu erkennen und zu überwachen. 1. **Paket installieren** * npm ```sh npm i @capgo/capacitor-ibeacon ``` * pnpm ```sh pnpm add @capgo/capacitor-ibeacon ``` * yarn ```sh yarn add @capgo/capacitor-ibeacon ``` * bun ```sh bun add @capgo/capacitor-ibeacon ``` 2. **Mit nativen Projekten synchronisieren** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Konfiguration [Section titled “Konfiguration”](#konfiguration) ### iOS Konfiguration [Section titled “iOS Konfiguration”](#ios-konfiguration) Fügen Sie Folgendes zu Ihrem `Info.plist` hinzu: ```xml NSLocationWhenInUseUsageDescription This app needs location access to detect nearby beacons NSLocationAlwaysAndWhenInUseUsageDescription This app needs location access to monitor beacons in the background NSBluetoothAlwaysUsageDescription This app uses Bluetooth to detect nearby beacons UIBackgroundModes location ``` ### Android Konfiguration [Section titled “Android Konfiguration”](#android-konfiguration) Fügen Sie Folgendes zu Ihrem `AndroidManifest.xml` hinzu: ```xml ``` **Wichtig**: Für Android müssen Sie die Bibliothek [AltBeacon](https://altbeacon.github.io/android-beacon-library/) in Ihr Projekt integrieren, damit die Beacon-Erkennung funktioniert. Fügen Sie `build.gradle` Ihrer App hinzu: ```kotlin dependencies { implementation 'org.altbeacon:android-beacon-library:2.20+' } ``` ## Nutzung [Section titled “Nutzung”](#nutzung) ### Grundeinrichtung [Section titled “Grundeinrichtung”](#grundeinrichtung) ```typescript import { CapacitorIbeacon } from '@capgo/capacitor-ibeacon'; // Define your beacon region const beaconRegion = { identifier: 'MyStore', uuid: 'B9407F30-F5F8-466E-AFF9-25556B57FE6D', major: 1, // Optional minor: 2 // Optional }; ``` ### Berechtigungen anfordern [Section titled “Berechtigungen anfordern”](#berechtigungen-anfordern) ```typescript // Request "When In Use" permission const requestPermission = async () => { const { status } = await CapacitorIbeacon.requestWhenInUseAuthorization(); console.log('Permission status:', status); // status: 'not_determined' | 'restricted' | 'denied' | 'authorized_always' | 'authorized_when_in_use' }; // Request "Always" permission (for background monitoring) const requestAlwaysPermission = async () => { const { status } = await CapacitorIbeacon.requestAlwaysAuthorization(); console.log('Always permission status:', status); }; // Check current authorization status const checkPermission = async () => { const { status } = await CapacitorIbeacon.getAuthorizationStatus(); console.log('Current status:', status); }; ``` ### Überprüfen Sie die Gerätefunktionen [Section titled “Überprüfen Sie die Gerätefunktionen”](#überprüfen-sie-die-gerätefunktionen) ```typescript // Check if Bluetooth is enabled const checkBluetooth = async () => { const { enabled } = await CapacitorIbeacon.isBluetoothEnabled(); if (!enabled) { console.log('Please enable Bluetooth'); } }; // Check if ranging is available const checkRanging = async () => { const { available } = await CapacitorIbeacon.isRangingAvailable(); console.log('Ranging available:', available); }; ``` ### Beacon-Regionen überwachen [Section titled “Beacon-Regionen überwachen”](#beacon-regionen-überwachen) Die Überwachung erkennt, wenn Sie eine Beacon-Region betreten oder verlassen. Dies funktioniert im Hintergrund. ```typescript // Start monitoring const startMonitoring = async () => { await CapacitorIbeacon.startMonitoringForRegion({ identifier: 'MyStore', uuid: 'B9407F30-F5F8-466E-AFF9-25556B57FE6D', major: 1, minor: 2 }); console.log('Started monitoring for beacons'); }; // Stop monitoring const stopMonitoring = async () => { await CapacitorIbeacon.stopMonitoringForRegion({ identifier: 'MyStore', uuid: 'B9407F30-F5F8-466E-AFF9-25556B57FE6D' }); }; ``` ### Reichweitenbaken [Section titled “Reichweitenbaken”](#reichweitenbaken) Ranging bietet kontinuierliche Updates zu nahegelegenen Beacons und deren Entfernungen. Dazu muss die App im Vordergrund sein. ```typescript // Start ranging const startRanging = async () => { await CapacitorIbeacon.startRangingBeaconsInRegion({ identifier: 'MyStore', uuid: 'B9407F30-F5F8-466E-AFF9-25556B57FE6D' }); console.log('Started ranging beacons'); }; // Stop ranging const stopRanging = async () => { await CapacitorIbeacon.stopRangingBeaconsInRegion({ identifier: 'MyStore', uuid: 'B9407F30-F5F8-466E-AFF9-25556B57FE6D' }); }; ``` ### Ereignisse anhören [Section titled “Ereignisse anhören”](#ereignisse-anhören) ```typescript import { PluginListenerHandle } from '@capacitor/core'; // Listen for ranging events (continuous distance updates) const rangingListener: PluginListenerHandle = await CapacitorIbeacon.addListener( 'didRangeBeacons', (data) => { console.log('Beacons detected:', data.beacons); data.beacons.forEach(beacon => { console.log(`UUID: ${beacon.uuid}`); console.log(`Major: ${beacon.major}, Minor: ${beacon.minor}`); console.log(`Distance: ${beacon.accuracy}m`); console.log(`Proximity: ${beacon.proximity}`); // immediate, near, far, unknown console.log(`RSSI: ${beacon.rssi}`); }); } ); // Listen for region enter events const enterListener: PluginListenerHandle = await CapacitorIbeacon.addListener( 'didEnterRegion', (data) => { console.log('Entered region:', data.region.identifier); // Show welcome notification or trigger action } ); // Listen for region exit events const exitListener: PluginListenerHandle = await CapacitorIbeacon.addListener( 'didExitRegion', (data) => { console.log('Exited region:', data.region.identifier); // Trigger goodbye action } ); // Listen for region state changes const stateListener: PluginListenerHandle = await CapacitorIbeacon.addListener( 'didDetermineStateForRegion', (data) => { console.log(`Region ${data.region.identifier}: ${data.state}`); // state: 'inside' | 'outside' | 'unknown' } ); // Clean up listeners when done const cleanup = () => { rangingListener.remove(); enterListener.remove(); exitListener.remove(); stateListener.remove(); }; ``` ### Als iBeacon bewerben (nur iOS) [Section titled “Als iBeacon bewerben (nur iOS)”](#als-ibeacon-bewerben-nur-ios) Verwandeln Sie Ihr Gerät in einen iBeacon-Sender. ```typescript // Start advertising const startAdvertising = async () => { await CapacitorIbeacon.startAdvertising({ uuid: 'B9407F30-F5F8-466E-AFF9-25556B57FE6D', major: 1, minor: 2, identifier: 'MyBeacon', measuredPower: -59 // Optional: calibrated power at 1 meter }); console.log('Started advertising as iBeacon'); }; // Stop advertising const stopAdvertising = async () => { await CapacitorIbeacon.stopAdvertising(); }; ``` ## Vollständiges Beispiel [Section titled “Vollständiges Beispiel”](#vollständiges-beispiel) ```typescript import { CapacitorIbeacon } from '@capgo/capacitor-ibeacon'; import { PluginListenerHandle } from '@capacitor/core'; export class BeaconService { private listeners: PluginListenerHandle[] = []; async init() { // Request permissions const { status } = await CapacitorIbeacon.requestWhenInUseAuthorization(); if (status !== 'authorized_when_in_use' && status !== 'authorized_always') { throw new Error('Location permission denied'); } // Check Bluetooth const { enabled } = await CapacitorIbeacon.isBluetoothEnabled(); if (!enabled) { throw new Error('Bluetooth is not enabled'); } // Set up event listeners this.setupListeners(); } private setupListeners() { this.listeners.push( await CapacitorIbeacon.addListener('didEnterRegion', (data) => { console.log('Welcome! Entered:', data.region.identifier); this.onEnterRegion(data.region); }) ); this.listeners.push( await CapacitorIbeacon.addListener('didExitRegion', (data) => { console.log('Goodbye! Left:', data.region.identifier); this.onExitRegion(data.region); }) ); this.listeners.push( await CapacitorIbeacon.addListener('didRangeBeacons', (data) => { this.onRangeBeacons(data.beacons); }) ); } async startMonitoring(uuid: string, identifier: string, major?: number, minor?: number) { await CapacitorIbeacon.startMonitoringForRegion({ identifier, uuid, major, minor }); } async startRanging(uuid: string, identifier: string) { await CapacitorIbeacon.startRangingBeaconsInRegion({ identifier, uuid }); } private onEnterRegion(region: any) { // Handle region entry (e.g., show notification, trigger content) console.log('Entered beacon region:', region); } private onExitRegion(region: any) { // Handle region exit console.log('Left beacon region:', region); } private onRangeBeacons(beacons: any[]) { // Process beacon distances const nearestBeacon = beacons.reduce((nearest, beacon) => { return beacon.accuracy < nearest.accuracy ? beacon : nearest; }, beacons[0]); if (nearestBeacon) { console.log('Nearest beacon:', nearestBeacon); this.handleProximity(nearestBeacon); } } private handleProximity(beacon: any) { switch (beacon.proximity) { case 'immediate': // < 0.5m console.log('Very close to beacon'); break; case 'near': // 0.5m - 3m console.log('Near beacon'); break; case 'far': // > 3m console.log('Far from beacon'); break; case 'unknown': console.log('Distance unknown'); break; } } cleanup() { this.listeners.forEach(listener => listener.remove()); this.listeners = []; } } ``` ## API Referenz [Section titled “API Referenz”](#api-referenz) ### startMonitoringForRegion(Optionen) [Section titled “startMonitoringForRegion(Optionen)”](#startmonitoringforregionoptionen) Beginnen Sie mit der Überwachung einer Beacon-Region. Löst Ereignisse beim Betreten/Verlassen aus. ```typescript interface BeaconRegion { identifier: string; uuid: string; major?: number; minor?: number; notifyEntryStateOnDisplay?: boolean; } await CapacitorIbeacon.startMonitoringForRegion(options); ``` ### stopMonitoringForRegion(Optionen) [Section titled “stopMonitoringForRegion(Optionen)”](#stopmonitoringforregionoptionen) Beenden Sie die Überwachung einer Beacon-Region. ```typescript await CapacitorIbeacon.stopMonitoringForRegion(options); ``` ### startRangingBeaconsInRegion(Optionen) [Section titled “startRangingBeaconsInRegion(Optionen)”](#startrangingbeaconsinregionoptionen) Starten Sie Messbaken in einer Region, um kontinuierliche Entfernungsaktualisierungen zu erhalten. ```typescript await CapacitorIbeacon.startRangingBeaconsInRegion(options); ``` ### stopRangingBeaconsInRegion(Optionen) [Section titled “stopRangingBeaconsInRegion(Optionen)”](#stoprangingbeaconsinregionoptionen) Stoppen Sie Entfernungsbaken in einer Region. ```typescript await CapacitorIbeacon.stopRangingBeaconsInRegion(options); ``` ### startAdvertising(Optionen) [Section titled “startAdvertising(Optionen)”](#startadvertisingoptionen) Beginnen Sie mit der Werbung für das Gerät als iBeacon (nur iOS). ```typescript interface BeaconAdvertisingOptions { uuid: string; major: number; minor: number; identifier: string; measuredPower?: number; // Calibrated power at 1 meter } await CapacitorIbeacon.startAdvertising(options); ``` ### stopAdvertising() [Section titled “stopAdvertising()”](#stopadvertising) Hören Sie auf, das Gerät als iBeacon zu bewerben. ```typescript await CapacitorIbeacon.stopAdvertising(); ``` ### requestWhenInUseAuthorization() [Section titled “requestWhenInUseAuthorization()”](#requestwheninuseauthorization) Fordern Sie die Standortautorisierung „Bei Verwendung“ an. ```typescript const result = await CapacitorIbeacon.requestWhenInUseAuthorization(); // Returns: { status: string } ``` ### requestAlwaysAuthorization() [Section titled “requestAlwaysAuthorization()”](#requestalwaysauthorization) Fordern Sie die Standortberechtigung „Immer“ an (erforderlich für die Hintergrundüberwachung). ```typescript const result = await CapacitorIbeacon.requestAlwaysAuthorization(); // Returns: { status: string } ``` ### getAuthorizationStatus() [Section titled “getAuthorizationStatus()”](#getauthorizationstatus) Erhalten Sie den aktuellen Status der Standortautorisierung. ```typescript const result = await CapacitorIbeacon.getAuthorizationStatus(); // Returns: { status: 'not_determined' | 'restricted' | 'denied' | 'authorized_always' | 'authorized_when_in_use' } ``` ### isBluetoothEnabled() [Section titled “isBluetoothEnabled()”](#isbluetoothenabled) Überprüfen Sie, ob Bluetooth aktiviert ist. ```typescript const result = await CapacitorIbeacon.isBluetoothEnabled(); // Returns: { enabled: boolean } ``` ### isRangingAvailable() [Section titled “isRangingAvailable()”](#israngingavailable) Überprüfen Sie, ob die Bereichswahl auf dem Gerät verfügbar ist. ```typescript const result = await CapacitorIbeacon.isRangingAvailable(); // Returns: { available: boolean } ``` ### enableARMAFilter(Optionen) [Section titled “enableARMAFilter(Optionen)”](#enablearmafilteroptionen) Aktivieren Sie die ARMA-Filterung für Entfernungsberechnungen (nur Android). ```typescript await CapacitorIbeacon.enableARMAFilter({ enabled: true }); ``` ## Ereignisse [Section titled “Ereignisse”](#ereignisse) ### didRangeBeacons [Section titled “didRangeBeacons”](#didrangebeacons) Wird ausgelöst, wenn während der Entfernungsmessung Beacons erkannt werden. ```typescript interface RangingEvent { region: BeaconRegion; beacons: Beacon[]; } interface Beacon { uuid: string; major: number; minor: number; rssi: number; // Signal strength proximity: 'immediate' | 'near' | 'far' | 'unknown'; accuracy: number; // Distance in meters } ``` ### didEnterRegion [Section titled “didEnterRegion”](#didenterregion) Wird beim Betreten einer überwachten Beacon-Region ausgelöst. ```typescript interface RegionEvent { region: BeaconRegion; } ``` ### didExitRegion [Section titled “didExitRegion”](#didexitregion) Wird beim Verlassen einer überwachten Beacon-Region ausgelöst. ```typescript interface RegionEvent { region: BeaconRegion; } ``` ### didDetermineStateForRegion [Section titled “didDetermineStateForRegion”](#diddeterminestateforregion) Wird ausgelöst, wenn der Regionsstatus ermittelt wird. ```typescript interface StateEvent { region: BeaconRegion; state: 'inside' | 'outside' | 'unknown'; } ``` ## Näherungswerte [Section titled “Näherungswerte”](#näherungswerte) * **sofort**: Sehr nah (< 0.5 meters) * **near**: Relatively close (0.5 - 3 meters) * **far**: Further away (> 3 Meter) * **unbekannt**: Entfernung kann nicht bestimmt werden ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) 1. **Entsprechende Berechtigungen anfordern** * Verwenden Sie „Bei Verwendung“ für Vordergrundfunktionen * Fordern Sie „Immer“ nur an, wenn Sie eine Hintergrundüberwachung benötigen * Erklären Sie deutlich, warum Sie einen Standortzugriff benötigen2. **Bluetooth-Status verwalten** ```typescript const { enabled } = await CapacitorIbeacon.isBluetoothEnabled(); if (!enabled) { // Prompt user to enable Bluetooth } ``` 2. **Batterieoptimierung** * Verwenden Sie nach Möglichkeit eine Überwachung anstelle einer Entfernungsmessung (batterieeffizienter). * Stoppen Sie die Bereichswahl, wenn sie nicht aktiv benötigt wird * Erwägen Sie die Verwendung größerer Haupt-/Nebenbereiche, um die Verarbeitung zu reduzieren 3. **Fehlerbehandlung** ```typescript try { await CapacitorIbeacon.startMonitoringForRegion(region); } catch (error) { console.error('Failed to start monitoring:', error); } ``` 4. **Räumen Sie die Zuhörer auf** Entfernen Sie immer Ereignis-Listener, wenn die Bereitstellung einer Komponente aufgehoben wird, um Speicherlecks zu verhindern. ## Plattformhinweise [Section titled “Plattformhinweise”](#plattformhinweise) ### iOS [Section titled “iOS”](#ios) * Erfordert iOS 10.0+ * Verwendet das native CoreLocation-Framework * Unterstützt Hintergrundüberwachung mit der Berechtigung „Immer“. * Kann mit CoreBluetooth als iBeacon werben * Für die Entfernungsmessung muss sich die App im Vordergrund befinden ### Android [Section titled “Android”](#android) * Erfordert Android 6.0 (API 23)+ * Verwendet die AltBeacon-Bibliothek * Erfordert Standortberechtigungen, obwohl Beacons Bluetooth verwenden – Hintergrundüberwachung erfordert ACCESS\_BACKGROUND\_LOCATION (Android 10+) * Kann nicht als iBeacon werben (Hardwareeinschränkung auf den meisten Geräten) ### Web [Section titled “Web”](#web) – Wird auf der Webplattform nicht unterstützt ## Häufige Anwendungsfälle [Section titled “Häufige Anwendungsfälle”](#häufige-anwendungsfälle) 1. **Proximity-Marketing**: Lösen Sie Benachrichtigungen oder Inhalte aus, wenn sich Benutzer Ihrem Geschäft nähern 2. **Indoor-Navigation**: Führen Sie Benutzer mithilfe von Beacon-Wegpunkten durch Gebäude 3. **Anwesenheitsverfolgung**: Automatisches Einchecken, wenn Benutzer einen Standort betreten 4. **Asset Tracking**: Überwachen Sie Geräte- oder Bestandsbewegungen 5. **Museumsführungen**: Stellen Sie kontextbezogene Informationen bereit, wenn Besucher sich den Ausstellungen nähern 6. **Smart Home**: Automatisierungen basierend auf der Raumpräsenz auslösen ## Fehlerbehebung [Section titled “Fehlerbehebung”](#fehlerbehebung) ### Beacons nicht erkannt [Section titled “Beacons nicht erkannt”](#beacons-nicht-erkannt) * Stellen Sie sicher, dass Bluetooth aktiviert ist * Überprüfen Sie, ob Standortberechtigungen erteilt wurden * Überprüfen Sie, ob die Beacon-UUID genau übereinstimmt (Groß- und Kleinschreibung beachten). * Bestätigen Sie, dass die Bake mit Strom versorgt wird und sendet * Versuchen Sie es zunächst ohne Haupt-/Nebenfilter ### Hintergrundüberwachung funktioniert nicht [Section titled “Hintergrundüberwachung funktioniert nicht”](#hintergrundüberwachung-funktioniert-nicht) * Stellen Sie sicher, dass die Standortberechtigung „Immer“ erteilt ist – `location` zu UIBackgroundModes hinzufügen (iOS) * ACCESS\_BACKGROUND\_LOCATION anfordern (Android 10+) * Hinweis: iOS kann Hintergrundrückrufe verzögern, um Batterie zu sparen ### Entfernungsmessungen ungenau [Section titled “Entfernungsmessungen ungenau”](#entfernungsmessungen-ungenau) * Beacon RSSI variiert je nach Umgebung (Wände, Störungen) * Verwenden Sie mehrere Beacons zur Triangulation * Kalibrieren Sie die gemessene Leistung 1 Meter von der Bake entfernt – Aktivieren Sie die ARMA-Filterung auf Android für glattere Werte # @capgo/inappbrowser > Öffnen Sie Webinhalte in Ihrer App mit einem vollständig ausgestatteten In-App-Browser, der URL-Änderungsereignisse und Navigationssteuerelemente unterstützt. URL-Änderungsereignisse Verfolgen Sie die Navigation mit URL-Änderungsereignis-Listenern Vollständige Navigationssteuerung Zurück-, Vorwärts-, Neuladen- und Schließen-Steuerelemente Anpassbare Benutzeroberfläche Toolbar-Farbe, Schaltflächen und Anzeigeoptionen Umfassende Dokumentation Schauen Sie sich die [Dokumentation](/docs/plugins/inappbrowser/getting-started/) an, um das Plugin in nur wenigen Minuten zu meistern. # Erste Schritte > Erfahren Sie, wie Sie das InAppBrowser-Plugin mit URL-Änderungsereignissen für eingebettete Webinhalte in Ihrer Capacitor-App installieren und verwenden. 1. **Installieren Sie das Paket** * npm ```sh npm i @capgo/inappbrowser ``` * pnpm ```sh pnpm add @capgo/inappbrowser ``` * yarn ```sh yarn add @capgo/inappbrowser ``` * bun ```sh bun add @capgo/inappbrowser ``` 2. **Mit nativen Projekten synchronisieren** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Verwendung [Section titled “Verwendung”](#verwendung) Importieren Sie das Plugin und verwenden Sie seine zwei Haupteinstiegspunkte: ```typescript import { InAppBrowser, ToolBarType, BackgroundColor } from '@capgo/inappbrowser'; // 1) Einfacher Custom Tab / SFSafariViewController const openExternal = async () => { await InAppBrowser.open({ url: 'https://capgo.app', isPresentAfterPageLoad: true, preventDeeplink: false, }); }; // 2) Vollständige WebView mit Navigation, Headern, Teilen, Messaging usw. const openWebView = async () => { await InAppBrowser.openWebView({ url: 'https://capgo.app', title: 'Capgo', toolbarType: ToolBarType.NAVIGATION, backgroundColor: BackgroundColor.BLACK, activeNativeNavigationForWebview: true, showReloadButton: true, shareSubject: 'Schauen Sie sich diese Seite an', shareDisclaimer: { title: 'Haftungsausschluss', message: 'Sie sind dabei, Inhalte zu teilen', confirmBtn: 'Fortfahren', cancelBtn: 'Abbrechen', }, }); }; // Nachrichtenaustausch zwischen App und geöffneter WebView const setupListeners = async () => { await InAppBrowser.addListener('urlChangeEvent', (event) => { console.log('URL geändert zu:', event.url); }); await InAppBrowser.addListener('messageFromWebview', (event) => { console.log('Nachricht vom Web:', event.detail); }); await InAppBrowser.addListener('closeEvent', () => { console.log('WebView geschlossen'); }); }; // Daten an die WebView senden const sendData = async () => { await InAppBrowser.postMessage({ detail: { action: 'refresh-profile' } }); }; // Hilfsfunktionen zum Schließen und Neuladen const closeBrowser = () => InAppBrowser.close(); const reloadPage = () => InAppBrowser.reload(); ``` ## API-Referenz [Section titled “API-Referenz”](#api-referenz) ### open(options: OpenOptions) [Section titled “open(options: OpenOptions)”](#openoptions-openoptions) Öffnet eine URL in einem Custom Tab / SFSafariViewController. ```typescript interface OpenOptions { /** Ziel-URL zum Laden */ url: string; /** Nach Abschluss des Seitenladens anzeigen */ isPresentAfterPageLoad?: boolean; /** Verhindern, dass Deep Links externe Apps öffnen */ preventDeeplink?: boolean; } await InAppBrowser.open({ url: 'https://example.com', preventDeeplink: true }); ``` ### openWebView(options: OpenWebViewOptions) [Section titled “openWebView(options: OpenWebViewOptions)”](#openwebviewoptions-openwebviewoptions) Lädt eine vollständig ausgestattete WebView mit Navigations-UI, Headern, Anmeldeinformationen, Skripting und Messaging. ```typescript interface OpenWebViewOptions { url: string; headers?: Record; credentials?: { username: string; password: string }; materialPicker?: boolean; shareDisclaimer?: { title: string; message: string; confirmBtn: string; cancelBtn: string; }; toolbarType?: ToolBarType; shareSubject?: string; title?: string; backgroundColor?: BackgroundColor; activeNativeNavigationForWebview?: boolean; disableGoBackOnNativeApplication?: boolean; isPresentAfterPageLoad?: boolean; isInspectable?: boolean; isAnimated?: boolean; showReloadButton?: boolean; closeModal?: boolean; closeModalTitle?: string; closeModalDescription?: string; closeModalOk?: string; closeModalCancel?: string; visibleTitle?: boolean; toolbarColor?: string; toolbarTextColor?: string; showArrow?: boolean; ignoreUntrustedSSLError?: boolean; preShowScript?: string; preShowScriptInjectionTime?: 'documentStart' | 'pageLoad'; proxyRequests?: string; buttonNearDone?: { ios: { iconType: 'sf-symbol' | 'asset'; icon: string }; android: { iconType: 'asset' | 'vector'; icon: string; width?: number; height?: number }; }; textZoom?: number; preventDeeplink?: boolean; authorizedAppLinks?: string[]; enabledSafeBottomMargin?: boolean; useTopInset?: boolean; enableGooglePaySupport?: boolean; blockedHosts?: string[]; width?: number; height?: number; x?: number; y?: number; } await InAppBrowser.openWebView({ url: 'https://new-page.com', toolbarType: ToolBarType.NAVIGATION, showReloadButton: true, }); ``` `ToolBarType` Werte: `activity` (Schließen + Teilen), `compact` (nur Schließen), `navigation` (Zurück/Vorwärts + Schließen), `blank` (keine Toolbar). `BackgroundColor` Werte: `white` oder `black`. ### close(options?) [Section titled “close(options?)”](#closeoptions) Schließt die WebView/den Custom Tab. **Optionen:** * `isAnimated?: boolean` - Ob die Schließaktion animiert werden soll ### reload() [Section titled “reload()”](#reload) Lädt die aktuelle WebView-Seite neu. ### goBack() [Section titled “goBack()”](#goback) Geht in der WebView-Historie zurück und gibt `{ canGoBack: boolean }` zurück. ### `setUrl({ url: string })` [Section titled “setUrl({ url: string })”](#seturl-url-string) Ersetzt die aktuelle WebView-URL. ### `executeScript({ code: string })` [Section titled “executeScript({ code: string })”](#executescript-code-string) Injiziert JavaScript in die WebView. ### `postMessage({ detail: Record })` [Section titled “postMessage({ detail: Record\ })”](#postmessage-detail-recordstring-any) Sendet Daten von der nativen App an die WebView (empfangen in JS über `window.addEventListener('messageFromNative', ...)`). ### `getCookies({ url, includeHttpOnly? })` [Section titled “getCookies({ url, includeHttpOnly? })”](#getcookies-url-includehttponly) Gibt Cookies für die URL zurück. ### `clearCookies({ url })` / clearAllCookies() / clearCache() [Section titled “clearCookies({ url }) / clearAllCookies() / clearCache()”](#clearcookies-url---clearallcookies--clearcache) Hilfsfunktionen zur Verwaltung von Cookies und Cache. ### updateDimensions(options: DimensionOptions) [Section titled “updateDimensions(options: DimensionOptions)”](#updatedimensionsoptions-dimensionoptions) Ändert WebView-Größe/-Position zur Laufzeit (`width`, `height`, `x`, `y`). ### removeAllListeners() [Section titled “removeAllListeners()”](#removealllisteners) Entfernt alle Listener für das Plugin. ## Ereignisse [Section titled “Ereignisse”](#ereignisse) ### urlChangeEvent [Section titled “urlChangeEvent”](#urlchangeevent) Wird ausgelöst, wenn sich die URL im Browser ändert. ```typescript interface UrlChangeEvent { url: string; } InAppBrowser.addListener('urlChangeEvent', (event) => { console.log('Neue URL:', event.url); }); ``` ### messageFromWebview [Section titled “messageFromWebview”](#messagefromwebview) Wird ausgelöst, wenn `window.mobileApp.postMessage(...)` innerhalb der WebView aufgerufen wird. ```typescript InAppBrowser.addListener('messageFromWebview', (event) => { console.log('Payload vom Web:', event.detail); }); ``` ### closeEvent [Section titled “closeEvent”](#closeevent) Wird ausgelöst, wenn der Browser geschlossen wird. ```typescript InAppBrowser.addListener('closeEvent', () => { console.log('Browser geschlossen'); }); ``` ### buttonNearDoneClick [Section titled “buttonNearDoneClick”](#buttonneardoneclick) Wird ausgelöst, wenn die benutzerdefinierte Schaltfläche, die mit `buttonNearDone` hinzugefügt wurde, gedrückt wird. ```typescript InAppBrowser.addListener('buttonNearDoneClick', (event) => { console.log('Schaltfläche neben Fertig getippt', event); }); ``` ### confirmBtnClicked [Section titled “confirmBtnClicked”](#confirmbtnclicked) Wird ausgelöst, wenn ein Bestätigungsdialog (Haftungsausschluss oder Schließen-Modal) akzeptiert wird. ```typescript InAppBrowser.addListener('confirmBtnClicked', (event) => { console.log('Bestätigung akzeptiert, aktuelle URL:', event.url); }); ``` ### browserPageLoaded / pageLoadError [Section titled “browserPageLoaded / pageLoadError”](#browserpageloaded--pageloaderror) Lebenszyklusereignisse für erfolgreichen oder fehlgeschlagenen WebView-Ladevorgang. ```typescript InAppBrowser.addListener('browserPageLoaded', () => console.log('Seite geladen')); InAppBrowser.addListener('pageLoadError', () => console.log('Seite konnte nicht geladen werden')); ``` ## Erweiterte Verwendung [Section titled “Erweiterte Verwendung”](#erweiterte-verwendung) ### OAuth-Flow-Implementierung [Section titled “OAuth-Flow-Implementierung”](#oauth-flow-implementierung) ```typescript import { InAppBrowser } from '@capgo/inappbrowser'; export class OAuthService { private listeners: any[] = []; async authenticate(authUrl: string, redirectUri: string) { return new Promise((resolve, reject) => { // Auf URL-Änderungen hören const urlListener = InAppBrowser.addListener('urlChangeEvent', (event) => { if (event.url.startsWith(redirectUri)) { // OAuth-Code/Token aus URL extrahieren const url = new URL(event.url); const code = url.searchParams.get('code'); if (code) { InAppBrowser.close(); resolve(code); } else { const error = url.searchParams.get('error'); reject(new Error(error || 'OAuth fehlgeschlagen')); } } }); this.listeners.push(urlListener); // OAuth-Anbieter öffnen InAppBrowser.open({ url: authUrl, preventDeeplink: true, }); }); } cleanup() { this.listeners.forEach(listener => listener.remove()); this.listeners = []; } } ``` ### Benutzerdefinierte Browser-UI [Section titled “Benutzerdefinierte Browser-UI”](#benutzerdefinierte-browser-ui) ```typescript const openCustomBrowser = async () => { await InAppBrowser.open({ url: 'https://example.com', isPresentAfterPageLoad: true, preventDeeplink: false, }); }; ``` ### Umgang mit externen Links [Section titled “Umgang mit externen Links”](#umgang-mit-externen-links) ```typescript import { InAppBrowser } from '@capgo/inappbrowser'; export class LinkHandler { async openExternalLink(url: string) { // Prüfen, ob URL im Browser geöffnet werden soll if (this.shouldOpenInBrowser(url)) { await InAppBrowser.open({ url, preventDeeplink: true, }); } else { // Intern behandeln window.location.href = url; } } private shouldOpenInBrowser(url: string): boolean { // Externe Domains const externalDomains = ['youtube.com', 'twitter.com', 'facebook.com']; const urlDomain = new URL(url).hostname; return externalDomains.some(domain => urlDomain.includes(domain)); } } ``` ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) 1. **Listener immer entfernen** ```typescript const listener = await InAppBrowser.addListener('urlChangeEvent', handler); // Wenn fertig listener.remove(); ``` 2. **Browser-Zustände handhaben** ```typescript let browserOpen = false; const launch = async () => { browserOpen = true; await InAppBrowser.openWebView({ url: 'https://example.com' }); }; InAppBrowser.addListener('closeEvent', () => { browserOpen = false; }); ``` 3. **URLs vor dem Öffnen validieren** ```typescript const isValidUrl = (url: string): boolean => { try { new URL(url); return true; } catch { return false; } }; if (isValidUrl(url)) { await InAppBrowser.open({ url }); } ``` 4. **Für Plattform anpassen** ```typescript import { Capacitor } from '@capacitor/core'; const options = { url: 'https://example.com', preventDeeplink: Capacitor.getPlatform() === 'ios', }; ``` ## Plattform-Hinweise [Section titled “Plattform-Hinweise”](#plattform-hinweise) ### iOS [Section titled “iOS”](#ios) * Verwendet `SFSafariViewController` * Unterstützt iOS 11.0+ * Respektiert Safe Area Insets * Unterstützt benutzerdefinierte Präsentationsstile ### Android [Section titled “Android”](#android) * Verwendet Chrome Custom Tabs * Fällt auf WebView zurück, wenn Chrome nicht verfügbar ist * Unterstützt Android 5.0 (API 21)+ * Toolbar-Anpassung über `toolbarType`, `toolbarColor`, `buttonNearDone` usw. unterstützt ### Web [Section titled “Web”](#web) * Öffnet in neuem Browser-Tab/-Fenster * Eingeschränkte Anpassungsoptionen * Keine URL-Änderungsereignisse # @capgo/capacitor-is-root > Erkennen Sie gerootete Android-Geräte und jailbroken iOS-Geräte, um die App-Sicherheit zu erhöhen und sensible Daten zu schützen. ## Übersicht [Section titled “Übersicht”](#übersicht) Das Capacitor Is Root-Plugin bietet umfassende Root- und Jailbreak-Erkennung für Android-Geräte und Emulator-Erkennung. Dieses Plugin hilft, die App-Sicherheit zu erhöhen, indem kompromittierte Geräte und emulierte Umgebungen identifiziert werden, die Sicherheitsrisiken darstellen können. Root-Erkennung Erweiterte Android-Root-Erkennung mit mehreren Methoden Emulator-Erkennung Emulierte Umgebungen und Test-Frameworks identifizieren Sicherheitsvalidierung Mehrere Erkennungstechniken für erhöhte Genauigkeit Android-fokussiert Spezialisierte Erkennung für Android-Sicherheitsbewertung ## Installation [Section titled “Installation”](#installation) ```bash npm install @capgo/capacitor-is-root npx cap sync ``` ## Kern-API-Methoden [Section titled “Kern-API-Methoden”](#kern-api-methoden) ### Root-Erkennung [Section titled “Root-Erkennung”](#root-erkennung) * `isRooted()` - Führt umfassende Root-Erkennung mit Standardmethoden durch * `isRootedWithBusyBox()` - Erweiterte Erkennung einschließlich BusyBox-Prüfungen * `detectRootManagementApps()` - Identifiziert installierte Root-Management-Anwendungen * `checkForSuBinary()` - Prüft auf `su`-Binärdatei-Vorhandensein in Systempfaden ### Emulator-Erkennung [Section titled “Emulator-Erkennung”](#emulator-erkennung) * `isRunningOnEmulator()` - Erkennt gängige Android-Emulator-Fingerabdrücke ## Erkennungstechniken [Section titled “Erkennungstechniken”](#erkennungstechniken) Das Plugin verwendet mehrere Erkennungsmethoden: ### Root-Erkennung [Section titled “Root-Erkennung”](#root-erkennung-1) * Prüfung auf Root-Management-Anwendungen (SuperSU, Magisk usw.) * Scannen auf verdächtige Systemeigenschaften * Identifizierung von Test-Build-Tags und Debug-Flags * Validierung gefährlicher Binärspeicherorte * Untersuchung von Systempfadberechtigungen ### Emulator-Erkennung [Section titled “Emulator-Erkennung”](#emulator-erkennung-1) * Hardware-Fingerabdruck-Analyse * Build-Eigenschaftsinspektion * Emulator-spezifische Merkmale * Virtuelle Umgebungsindikatoren ## Verwendungsbeispiel [Section titled “Verwendungsbeispiel”](#verwendungsbeispiel) ```typescript import { IsRoot } from '@capgo/capacitor-is-root'; // Grundlegende Root-Erkennung const rootResult = await IsRoot.isRooted(); if (rootResult.isRooted) { console.log('Gerät ist gerootet'); // Gerootetes Gerät angemessen behandeln } // Erweiterte Root-Erkennung mit BusyBox const extendedResult = await IsRoot.isRootedWithBusyBox(); if (extendedResult.isRooted) { console.log('Gerät ist gerootet (erweiterte Prüfung)'); } // Auf Emulator prüfen const emulatorResult = await IsRoot.isRunningOnEmulator(); if (emulatorResult.isEmulator) { console.log('Läuft auf Emulator'); } // Root-Management-Apps erkennen const rootAppsResult = await IsRoot.detectRootManagementApps(); if (rootAppsResult.hasRootApps) { console.log('Root-Management-Apps erkannt'); } ``` ## Sicherheitsüberlegungen [Section titled “Sicherheitsüberlegungen”](#sicherheitsüberlegungen) * Verwenden Sie mehrere Erkennungsmethoden für höhere Genauigkeit * Implementieren Sie eine angemessene Degradierung für erkannte Umgebungen * Berücksichtigen Sie die Privatsphäre der Benutzer bei der Implementierung von Sicherheitsmaßnahmen * Regelmäßige Updates empfohlen, da sich Erkennungsmethoden weiterentwickeln ## Dokumentation [Section titled “Dokumentation”](#dokumentation) Schauen Sie sich die [vollständige Dokumentation](/docs/plugins/is-root/getting-started/) für detaillierte Implementierungsanleitungen und erweiterte Sicherheitsmuster an. # Erste Schritte > Erfahren Sie, wie Sie das Is Root-Plugin installieren und verwenden, um gerootete Android-Geräte und Emulatoren für erweiterte App-Sicherheit zu erkennen. ## Installation [Section titled “Installation”](#installation) * npm ```bash npm install @capgo/capacitor-is-root npx cap sync ``` * yarn ```bash yarn add @capgo/capacitor-is-root npx cap sync ``` * pnpm ```bash pnpm add @capgo/capacitor-is-root npx cap sync ``` * bun ```bash bun add @capgo/capacitor-is-root npx cap sync ``` ## Plattformunterstützung [Section titled “Plattformunterstützung”](#plattformunterstützung) * **Android**: Vollständige Unterstützung für Root- und Emulator-Erkennung * **iOS**: Keine Konfiguration erforderlich (Plugin ist auf Android fokussiert) ## Verwendungsbeispiel [Section titled “Verwendungsbeispiel”](#verwendungsbeispiel) ```typescript import { IsRoot } from '@capgo/capacitor-is-root'; // Grundlegende Root-Erkennung const rootResult = await IsRoot.isRooted(); if (rootResult.isRooted) { console.log('Gerät ist gerootet'); // Gerootetes Gerät angemessen behandeln // Beispiel: Warnung anzeigen, Funktionalität einschränken oder Zugriff blockieren } // Erweiterte Root-Erkennung mit BusyBox const extendedResult = await IsRoot.isRootedWithBusyBox(); if (extendedResult.isRooted) { console.log('Gerät ist gerootet (erweiterte Prüfung)'); } // Auf Emulator prüfen const emulatorResult = await IsRoot.isRunningOnEmulator(); if (emulatorResult.isEmulator) { console.log('Läuft auf Emulator'); // Emulator-Umgebung behandeln } // Root-Management-Apps erkennen const rootAppsResult = await IsRoot.detectRootManagementApps(); if (rootAppsResult.hasRootApps) { console.log('Root-Management-Apps erkannt'); } // Auf su-Binärdatei prüfen const suResult = await IsRoot.checkForSuBinary(); if (suResult.hasSu) { console.log('SU-Binärdatei auf Gerät gefunden'); } ``` ## API-Referenz [Section titled “API-Referenz”](#api-referenz) ### isRooted() [Section titled “isRooted()”](#isrooted) ```typescript isRooted() => Promise<{ isRooted: boolean }> ``` Führt eine umfassende Root-Erkennung mit Standardmethoden durch. **Rückgabe:** `Promise<{ isRooted: boolean }>` ### isRootedWithBusyBox() [Section titled “isRootedWithBusyBox()”](#isrootedwithbusybox) ```typescript isRootedWithBusyBox() => Promise<{ isRooted: boolean }> ``` Erweiterte Root-Erkennung einschließlich BusyBox-Prüfungen. **Rückgabe:** `Promise<{ isRooted: boolean }>` ### detectRootManagementApps() [Section titled “detectRootManagementApps()”](#detectrootmanagementapps) ```typescript detectRootManagementApps() => Promise<{ hasRootApps: boolean }> ``` Identifiziert installierte Root-Management-Anwendungen (SuperSU, Magisk usw.). **Rückgabe:** `Promise<{ hasRootApps: boolean }>` ### checkForSuBinary() [Section titled “checkForSuBinary()”](#checkforsubinary) ```typescript checkForSuBinary() => Promise<{ hasSu: boolean }> ``` Prüft auf das Vorhandensein der `su`-Binärdatei in Systempfaden. **Rückgabe:** `Promise<{ hasSu: boolean }>` ### isRunningOnEmulator() [Section titled “isRunningOnEmulator()”](#isrunningonemulator) ```typescript isRunningOnEmulator() => Promise<{ isEmulator: boolean }> ``` Erkennt gängige Android-Emulator-Fingerabdrücke. **Rückgabe:** `Promise<{ isEmulator: boolean }>` ## Umfassende Sicherheitsprüfung [Section titled “Umfassende Sicherheitsprüfung”](#umfassende-sicherheitsprüfung) ```typescript import { IsRoot } from '@capgo/capacitor-is-root'; async function performSecurityCheck() { const checks = { rooted: false, emulator: false, rootApps: false, suBinary: false }; try { // Alle Erkennungsmethoden ausführen const [rootResult, emulatorResult, rootAppsResult, suResult] = await Promise.all([ IsRoot.isRootedWithBusyBox(), IsRoot.isRunningOnEmulator(), IsRoot.detectRootManagementApps(), IsRoot.checkForSuBinary() ]); checks.rooted = rootResult.isRooted; checks.emulator = emulatorResult.isEmulator; checks.rootApps = rootAppsResult.hasRootApps; checks.suBinary = suResult.hasSu; // Sicherheitsstufe bestimmen const securityIssues = Object.values(checks).filter(v => v).length; if (securityIssues > 0) { console.warn(`Gerät hat ${securityIssues} Sicherheitsbedenken`, checks); return { secure: false, issues: checks }; } return { secure: true, issues: checks }; } catch (error) { console.error('Sicherheitsprüfung fehlgeschlagen:', error); throw error; } } // In Ihrer App verwenden const securityStatus = await performSecurityCheck(); if (!securityStatus.secure) { // Unsicheres Gerät behandeln showSecurityWarning(securityStatus.issues); } ``` ## Erkennungstechniken [Section titled “Erkennungstechniken”](#erkennungstechniken) ### Root-Erkennung [Section titled “Root-Erkennung”](#root-erkennung) Das Plugin verwendet mehrere Erkennungsmethoden: * Prüfung auf Root-Management-Anwendungen (SuperSU, Magisk, KingRoot usw.) * Scannen auf verdächtige Systemeigenschaften * Identifizierung von Test-Build-Tags und Debug-Flags * Validierung gefährlicher Binärspeicherorte * Untersuchung von Systempfadberechtigungen * Erkennung bekannter Root-Verschleierungs-Apps ### Emulator-Erkennung [Section titled “Emulator-Erkennung”](#emulator-erkennung) * Hardware-Fingerabdruck-Analyse * Build-Eigenschaftsinspektion * Emulator-spezifische Merkmale * Virtuelle Umgebungsindikatoren ## Umgang mit Sicherheitsproblemen [Section titled “Umgang mit Sicherheitsproblemen”](#umgang-mit-sicherheitsproblemen) ```typescript import { IsRoot } from '@capgo/capacitor-is-root'; async function handleDeviceSecurity() { const rootResult = await IsRoot.isRooted(); if (rootResult.isRooted) { // Option 1: Warnung anzeigen und fortfahren showWarning('Ihr Gerät scheint gerootet zu sein. Einige Funktionen können eingeschränkt sein.'); // Option 2: Funktionalität einschränken disableSensitiveFeatures(); // Option 3: Zugriff auf App blockieren showBlockedScreen('Diese App kann aus Sicherheitsgründen nicht auf gerooteten Geräten ausgeführt werden.'); return false; } return true; } function showWarning(message: string) { // Benutzerfreundlichen Warndialog anzeigen alert(message); } function disableSensitiveFeatures() { // Zahlungsabwicklung, Zugriff auf sensible Daten usw. deaktivieren console.log('Sensible Funktionen aufgrund von gerootettem Gerät deaktiviert'); } function showBlockedScreen(message: string) { // Sperrbildschirm anzeigen und App beenden alert(message); } ``` ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) * Verwenden Sie mehrere Erkennungsmethoden für höhere Genauigkeit * Implementieren Sie eine angemessene Degradierung anstatt den Zugriff vollständig zu blockieren * Bieten Sie klare Benutzerkommunikation über Sicherheitsbedenken * Berücksichtigen Sie die Benutzererfahrung bei der Implementierung von Sicherheitsmaßnahmen * Halten Sie das Plugin aktuell, da sich Erkennungsmethoden weiterentwickeln * Testen Sie auf gerooteten und nicht gerooteten Geräten * Behandeln Sie Erkennungsfehler elegant ## Sicherheitsüberlegungen [Section titled “Sicherheitsüberlegungen”](#sicherheitsüberlegungen) * Keine Erkennungsmethode ist 100% narrensicher * Fortgeschrittene Benutzer können Erkennungsmechanismen umgehen * Verwenden Sie in Kombination mit serverseitigen Sicherheitsmaßnahmen * Berücksichtigen Sie die Privatsphäre der Benutzer bei der Implementierung von Sicherheitsprüfungen * Befolgen Sie Plattformrichtlinien für Sicherheitsimplementierungen * Regelmäßige Updates empfohlen, da sich Root-Verschleierungstechniken weiterentwickeln ## Anwendungsfälle [Section titled “Anwendungsfälle”](#anwendungsfälle) * **Bank- und Finanz-Apps**: Zugriff auf kompromittierte Geräte verhindern * **DRM-geschützte Inhalte**: Urheberrechtlich geschütztes Material schützen * **Unternehmens-Apps**: BYOD-Sicherheitsrichtlinien durchsetzen * **Zahlungsabwicklung**: Sichere Transaktionsumgebung gewährleisten * **Sensible Daten-Apps**: Vertrauliche Informationen schützen # @capgo/ivs-player > Integrieren Sie Amazon IVS Player für ultra-niedrige Latenz Live-Streaming mit interaktiven Features in Ihren Capacitor-Apps. Low-Latency-Streaming Stream mit Sub-Sekunden-Latenz unter Verwendung von Amazon IVS 📡 Interaktive Features Unterstützung für zeitgesteuerte Metadaten und Zuschauer-Interaktionen 💬 Qualitätsanpassung Automatische Qualitätsumschaltung basierend auf Netzwerkbedingungen 📶 Umfassende Dokumentation Schauen Sie sich die [Dokumentation](/docs/plugins/ivs-player/getting-started/) an, um das Plugin in nur wenigen Minuten zu meistern. # Erste Schritte > Erfahren Sie, wie Sie das Capacitor IVS Player Plugin installieren und konfigurieren, um Amazon IVS Low-Latency-Streaming in Ihrer App zu integrieren. 1. **Installieren Sie das Paket** * npm ```sh npm i @capgo/ivs-player ``` * pnpm ```sh pnpm add @capgo/ivs-player ``` * yarn ```sh yarn add @capgo/ivs-player ``` * bun ```sh bun add @capgo/ivs-player ``` 2. **Mit nativen Projekten synchronisieren** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` 3. **Konfigurieren Sie das Plugin** **Grundlegende Player-Einrichtung:** ```typescript import { IvsPlayer } from '@capgo/ivs-player'; // Player erstellen const { playerId } = await IvsPlayer.create({ url: 'https://your-ivs-playback-url.m3u8', autoplay: true }); // Wiedergabe starten await IvsPlayer.play({ playerId }); ``` **Player-Steuerung:** ```typescript // Wiedergabe pausieren await IvsPlayer.pause({ playerId }); // Lautstärke einstellen await IvsPlayer.setVolume({ playerId, volume: 0.5 // 0.0 bis 1.0 }); // Zu Position springen await IvsPlayer.seekTo({ playerId, position: 30 // Sekunden }); ``` * iOS Fügen Sie zu Ihrer `Info.plist` hinzu: ```xml NSAppTransportSecurity NSAllowsArbitraryLoads ``` * Android Fügen Sie zu Ihrer `AndroidManifest.xml` hinzu: ```xml ``` 4. **Ereignisbehandlung** ```typescript import { IvsPlayer } from '@capgo/ivs-player'; // Player-Ereignisse abhören IvsPlayer.addListener('onState', (event) => { console.log('Player-Status:', event.state); // Status: idle, buffering, ready, playing, ended }); IvsPlayer.addListener('onError', (event) => { console.error('Player-Fehler:', event.error); }); IvsPlayer.addListener('onDuration', (event) => { console.log('Video-Dauer:', event.duration); }); IvsPlayer.addListener('onProgress', (event) => { console.log('Aktuelle Position:', event.position); }); // Zeitgesteuerte Metadaten abhören IvsPlayer.addListener('onMetadata', (event) => { console.log('Zeitgesteuerte Metadaten:', event.metadata); }); ``` 5. **Erweiterte Verwendung** ```typescript import { IvsPlayer } from '@capgo/ivs-player'; export class StreamPlayer { private playerId: string | null = null; private listeners: any[] = []; async initialize(streamUrl: string) { // Player mit benutzerdefinierter Konfiguration erstellen const result = await IvsPlayer.create({ url: streamUrl, autoplay: false, muted: true, // Stummgeschaltet starten für Autoplay-Richtlinien controls: true }); this.playerId = result.playerId; this.setupEventListeners(); } private setupEventListeners() { // Statusänderungen const stateListener = IvsPlayer.addListener('onState', (event) => { this.handleStateChange(event.state); }); this.listeners.push(stateListener); // Qualitätsänderungen const qualityListener = IvsPlayer.addListener('onQuality', (event) => { console.log(`Qualität geändert zu: ${event.quality.name}`); }); this.listeners.push(qualityListener); // Puffer-Updates const bufferListener = IvsPlayer.addListener('onBuffer', (event) => { console.log(`Puffer: ${event.percentage}%`); }); this.listeners.push(bufferListener); } private handleStateChange(state: string) { switch (state) { case 'playing': console.log('Stream wird abgespielt'); break; case 'buffering': console.log('Stream puffert'); break; case 'ended': console.log('Stream beendet'); break; } } async play() { if (this.playerId) { await IvsPlayer.play({ playerId: this.playerId }); } } async pause() { if (this.playerId) { await IvsPlayer.pause({ playerId: this.playerId }); } } async setQuality(qualityName: string) { if (this.playerId) { await IvsPlayer.setQuality({ playerId: this.playerId, quality: qualityName }); } } async destroy() { // Listener entfernen this.listeners.forEach(listener => listener.remove()); // Player zerstören if (this.playerId) { await IvsPlayer.destroy({ playerId: this.playerId }); } } } ``` ## API-Referenz [Section titled “API-Referenz”](#api-referenz) ### Methoden [Section titled “Methoden”](#methoden) #### `create(options: CreateOptions)` [Section titled “create(options: CreateOptions)”](#createoptions-createoptions) Erstellen Sie eine neue IVS Player-Instanz. **Parameter:** * `options`: Player-Konfiguration * `url`: string - IVS Wiedergabe-URL * `autoplay`: boolean - Wiedergabe automatisch starten * `muted`: boolean - Stummgeschaltet starten * `controls`: boolean - Native Steuerelemente anzeigen **Rückgabe:** `Promise<{ playerId: string }>` #### `play(options: PlayerOptions)` [Section titled “play(options: PlayerOptions)”](#playoptions-playeroptions) Wiedergabe starten. #### `pause(options: PlayerOptions)` [Section titled “pause(options: PlayerOptions)”](#pauseoptions-playeroptions) Wiedergabe pausieren. #### `stop(options: PlayerOptions)` [Section titled “stop(options: PlayerOptions)”](#stopoptions-playeroptions) Wiedergabe stoppen und Position zurücksetzen. #### `seekTo(options: SeekOptions)` [Section titled “seekTo(options: SeekOptions)”](#seektooptions-seekoptions) Zu einer bestimmten Position springen. #### `setVolume(options: VolumeOptions)` [Section titled “setVolume(options: VolumeOptions)”](#setvolumeoptions-volumeoptions) Player-Lautstärke einstellen (0.0 bis 1.0). #### `setQuality(options: QualityOptions)` [Section titled “setQuality(options: QualityOptions)”](#setqualityoptions-qualityoptions) Wiedergabequalität einstellen. #### `destroy(options: PlayerOptions)` [Section titled “destroy(options: PlayerOptions)”](#destroyoptions-playeroptions) Player-Instanz zerstören. ### Ereignisse [Section titled “Ereignisse”](#ereignisse) * `onState`: Player-Statusänderungen * `onError`: Wiedergabefehler * `onDuration`: Video-Dauer verfügbar * `onProgress`: Wiedergabefortschritt-Updates * `onQuality`: Qualitätsänderungen * `onMetadata`: Zeitgesteuerte Metadaten-Ereignisse * `onBuffer`: Pufferstatus-Updates ### Schnittstellen [Section titled “Schnittstellen”](#schnittstellen) ```typescript interface CreateOptions { url: string; autoplay?: boolean; muted?: boolean; controls?: boolean; } interface PlayerOptions { playerId: string; } interface SeekOptions extends PlayerOptions { position: number; // Sekunden } interface VolumeOptions extends PlayerOptions { volume: number; // 0.0 bis 1.0 } interface QualityOptions extends PlayerOptions { quality: string; // Qualitätsname } ``` ## Plattform-Hinweise [Section titled “Plattform-Hinweise”](#plattform-hinweise) ### iOS [Section titled “iOS”](#ios) * Erfordert iOS 12.0 oder höher * Verwendet nativen AVPlayer mit IVS-Erweiterungen * Unterstützt Bild-in-Bild auf iPads ### Android [Section titled “Android”](#android) * Erfordert Android 5.0 (API-Level 21) oder höher * Verwendet Amazon IVS Player SDK * Hardware-Beschleunigung empfohlen ## Häufige Anwendungsfälle [Section titled “Häufige Anwendungsfälle”](#häufige-anwendungsfälle) 1. **Live-Streaming**: Echtzeit-Events, Sport, Gaming 2. **Interaktives Streaming**: Zuschauer-Umfragen, Q\&A-Sessions 3. **E-Commerce**: Live-Shopping mit Produkthighlights 4. **Bildung**: Live-Kurse mit zeitgesteuerten Inhalten 5. **Unterhaltung**: Konzerte, Shows mit Publikumsinteraktion ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) 1. **Netzwerk-Behandlung**: Verbindungsqualität überwachen ```typescript IvsPlayer.addListener('onError', (event) => { if (event.error.includes('network')) { // Netzwerkfehler behandeln showRetryButton(); } }); ``` 2. **Ressourcenverwaltung**: Player immer zerstören, wenn fertig 3. **Autoplay-Richtlinien**: Stummgeschaltet starten für zuverlässiges Autoplay 4. **Qualitätsauswahl**: IVS automatische Qualitätsumschaltung handhaben lassen ## Fehlerbehebung [Section titled “Fehlerbehebung”](#fehlerbehebung) **Stream wird nicht abgespielt:** * Überprüfen Sie, ob die IVS-Wiedergabe-URL gültig ist * Netzwerkberechtigungen prüfen * Stellen Sie sicher, dass der Stream live ist **Hohe Latenz:** * IVS ist für niedrige Latenz optimiert, überprüfen Sie Ihre Encoder-Einstellungen * Stellen Sie sicher, dass Sie die IVS-spezifische Wiedergabe-URL verwenden **Qualitätsprobleme:** * Automatische Qualitätsumschaltung zulassen * Netzwerkbandbreiten-Anforderungen prüfen # @capgo/capacitor-jw-player > Spielen Sie Videos von JW Player mit Vollbildschnittstelle, Playlists und vollständiger Wiedergabesteuerung in Ihrer Capacitor-App ab. Vollbild-Player Immer Vollbild-Videoplayer-Oberfläche 📺 Playlist-Unterstützung Unterstützt sowohl einzelne Videos als auch vollständige Playlists 📋 Wiedergabesteuerung Vollständige Kontrolle über Wiedergabe, Pause, Spulen, Geschwindigkeit 🎮 Audio & Untertitel Audiospur-Auswahl und Untertitel-Unterstützung 🔊 Ereignissystem Ereignis-Listener für Player-Statusänderungen 📡 Loslegen Schauen Sie sich den [Leitfaden für den Einstieg](/docs/plugins/jw-player/getting-started/) an, um das Plugin zu installieren und zu konfigurieren. # Erste Schritte > Erfahren Sie, wie Sie JW Player installieren und in Ihre Capacitor-App integrieren, um professionelle Video-Streaming-Funktionen zu erhalten. ## Installation [Section titled “Installation”](#installation) * npm ```bash npm install @capgo/capacitor-jw-player npx cap sync ``` * yarn ```bash yarn add @capgo/capacitor-jw-player npx cap sync ``` * pnpm ```bash pnpm add @capgo/capacitor-jw-player npx cap sync ``` * bun ```bash bun add @capgo/capacitor-jw-player npx cap sync ``` ## Voraussetzungen [Section titled “Voraussetzungen”](#voraussetzungen) Sie benötigen ein JW Player-Konto und einen Lizenzschlüssel, um dieses Plugin zu verwenden. Registrieren Sie sich bei [JW Player](https://www.jwplayer.com/), wenn Sie noch keines haben. ## Plattform-Konfiguration [Section titled “Plattform-Konfiguration”](#plattform-konfiguration) ### iOS [Section titled “iOS”](#ios) Fügen Sie Ihren JW Player Lizenzschlüssel zu Ihrer `Info.plist` hinzu: ```xml JWPlayerLicenseKey YOUR_LICENSE_KEY ``` ### Android [Section titled “Android”](#android) Fügen Sie Ihren JW Player Lizenzschlüssel zu `android/app/src/main/res/values/strings.xml` hinzu: ```xml YOUR_LICENSE_KEY ``` ## Verwendungsbeispiel [Section titled “Verwendungsbeispiel”](#verwendungsbeispiel) ```typescript import { JWPlayer } from '@capgo/capacitor-jw-player'; // Einzelnes Video abspielen await JWPlayer.playVideo({ mediaUrl: 'https://example.com/video.mp4', title: 'Mein Video', description: 'Video-Beschreibung', poster: 'https://example.com/poster.jpg' }); // Playlist abspielen await JWPlayer.playPlaylist({ playlistUrl: 'https://example.com/playlist.json' }); // Player-Ereignisse abhören JWPlayer.addListener('playerReady', () => { console.log('Player ist bereit'); }); JWPlayer.addListener('play', (data) => { console.log('Video-Wiedergabe gestartet'); }); JWPlayer.addListener('pause', (data) => { console.log('Video pausiert'); }); JWPlayer.addListener('complete', (data) => { console.log('Video-Wiedergabe abgeschlossen'); }); JWPlayer.addListener('error', (error) => { console.error('Player-Fehler:', error); }); ``` ## API-Referenz [Section titled “API-Referenz”](#api-referenz) ### playVideo(options) [Section titled “playVideo(options)”](#playvideooptions) ```typescript playVideo(options: VideoOptions) => Promise ``` Spielt ein einzelnes Video im Vollbildmodus ab. | Parameter | Typ | | ------------- | -------------- | | **`options`** | `VideoOptions` | ### playPlaylist(options) [Section titled “playPlaylist(options)”](#playplaylistoptions) ```typescript playPlaylist(options: PlaylistOptions) => Promise ``` Spielt eine Playlist im Vollbildmodus ab. | Parameter | Typ | | ------------- | ----------------- | | **`options`** | `PlaylistOptions` | ### pause() [Section titled “pause()”](#pause) ```typescript pause() => Promise ``` Pausiert das aktuelle Video. ### play() [Section titled “play()”](#play) ```typescript play() => Promise ``` Setzt die Video-Wiedergabe fort. ### seek(options) [Section titled “seek(options)”](#seekoptions) ```typescript seek(options: { position: number }) => Promise ``` Springt zu einer bestimmten Position im Video. | Parameter | Typ | | ------------- | ---------------------- | | **`options`** | `{ position: number }` | ### setPlaybackSpeed(options) [Section titled “setPlaybackSpeed(options)”](#setplaybackspeedoptions) ```typescript setPlaybackSpeed(options: { speed: number }) => Promise ``` Setzt die Wiedergabegeschwindigkeit. | Parameter | Typ | | ------------- | ------------------- | | **`options`** | `{ speed: number }` | ### setVolume(options) [Section titled “setVolume(options)”](#setvolumeoptions) ```typescript setVolume(options: { volume: number }) => Promise ``` Setzt die Audiolautstärke (0.0 bis 1.0). | Parameter | Typ | | ------------- | -------------------- | | **`options`** | `{ volume: number }` | ### selectAudioTrack(options) [Section titled “selectAudioTrack(options)”](#selectaudiotrackoptions) ```typescript selectAudioTrack(options: { trackIndex: number }) => Promise ``` Wählt eine Audiospur nach Index aus. | Parameter | Typ | | ------------- | ------------------------ | | **`options`** | `{ trackIndex: number }` | ### selectCaptionTrack(options) [Section titled “selectCaptionTrack(options)”](#selectcaptiontrackoptions) ```typescript selectCaptionTrack(options: { trackIndex: number }) => Promise ``` Wählt eine Untertitelspur nach Index aus. | Parameter | Typ | | ------------- | ------------------------ | | **`options`** | `{ trackIndex: number }` | ### stop() [Section titled “stop()”](#stop) ```typescript stop() => Promise ``` Stoppt die Wiedergabe und schließt den Player. ## Schnittstellen [Section titled “Schnittstellen”](#schnittstellen) ### VideoOptions [Section titled “VideoOptions”](#videooptions) | Eigenschaft | Typ | Beschreibung | | ----------------- | --------- | --------------------------------------------------------- | | **`mediaUrl`** | `string` | URL der Videodatei | | **`title`** | `string` | Video-Titel (optional) | | **`description`** | `string` | Video-Beschreibung (optional) | | **`poster`** | `string` | URL des Posters/Thumbnails (optional) | | **`autoStart`** | `boolean` | Wiedergabe automatisch starten (optional, Standard: true) | ### PlaylistOptions [Section titled “PlaylistOptions”](#playlistoptions) | Eigenschaft | Typ | Beschreibung | | ----------------- | --------- | ----------------------------------------- | | **`playlistUrl`** | `string` | URL der JW Playlist JSON | | **`autoStart`** | `boolean` | Wiedergabe automatisch starten (optional) | ## Event-Listener [Section titled “Event-Listener”](#event-listener) ### Verfügbare Ereignisse [Section titled “Verfügbare Ereignisse”](#verfügbare-ereignisse) * `playerReady` - Player ist initialisiert und bereit * `play` - Video-Wiedergabe gestartet * `pause` - Video-Wiedergabe pausiert * `complete` - Video-Wiedergabe abgeschlossen * `error` - Player hat einen Fehler festgestellt * `seek` - Benutzer ist zu einer anderen Position gesprungen * `time` - Wiedergabezeit aktualisiert * `bufferChange` - Pufferstatus geändert ### Event-Beispiel [Section titled “Event-Beispiel”](#event-beispiel) ```typescript // Zeit-Updates abhören JWPlayer.addListener('time', (data) => { console.log('Aktuelle Zeit:', data.position); console.log('Dauer:', data.duration); }); // Pufferänderungen abhören JWPlayer.addListener('bufferChange', (data) => { console.log('Puffert:', data.buffering); }); // Listener entfernen, wenn fertig const listener = await JWPlayer.addListener('play', (data) => { console.log('Spielt ab'); }); // Später... listener.remove(); ``` ## Erweiterte Verwendung [Section titled “Erweiterte Verwendung”](#erweiterte-verwendung) ### Abspielen mit Untertiteln [Section titled “Abspielen mit Untertiteln”](#abspielen-mit-untertiteln) ```typescript await JWPlayer.playVideo({ mediaUrl: 'https://example.com/video.mp4', title: 'Video mit Untertiteln', poster: 'https://example.com/poster.jpg', tracks: [ { file: 'https://example.com/captions-en.vtt', label: 'English', kind: 'captions' }, { file: 'https://example.com/captions-es.vtt', label: 'Spanish', kind: 'captions' } ] }); ``` ### Benutzerdefinierte Wiedergabesteuerung [Section titled “Benutzerdefinierte Wiedergabesteuerung”](#benutzerdefinierte-wiedergabesteuerung) ```typescript import { JWPlayer } from '@capgo/capacitor-jw-player'; // Wiedergabe starten await JWPlayer.play(); // Nach 10 Sekunden pausieren setTimeout(() => { JWPlayer.pause(); }, 10000); // Zu 30 Sekunden springen await JWPlayer.seek({ position: 30 }); // Wiedergabegeschwindigkeit auf 1.5x setzen await JWPlayer.setPlaybackSpeed({ speed: 1.5 }); // Lautstärke auf 50% setzen await JWPlayer.setVolume({ volume: 0.5 }); ``` ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) * Stellen Sie immer ein Posterbild für bessere Benutzererfahrung bereit * Behandeln Sie Player-Fehler elegant mit passenden Fehlermeldungen * Entfernen Sie Event-Listener, wenn Komponente demontiert wird * Testen Sie Video-Wiedergabe auf iOS- und Android-Geräten * Verwenden Sie geeignete Videoformate (MP4 empfohlen) * Implementieren Sie Ladezustände, während Video initialisiert wird * Berücksichtigen Sie Netzwerkbedingungen beim Streaming ## Fehlerbehebung [Section titled “Fehlerbehebung”](#fehlerbehebung) ### Video wird nicht abgespielt [Section titled “Video wird nicht abgespielt”](#video-wird-nicht-abgespielt) * Überprüfen Sie, ob Ihr JW Player Lizenzschlüssel korrekt ist * Prüfen Sie, ob die Video-URL zugänglich und gültig ist * Stellen Sie sicher, dass korrekte CORS-Header vorhanden sind, wenn Sie einen benutzerdefinierten Server verwenden * Testen Sie mit verschiedenen Videoformaten ### Vollbild-Probleme [Section titled “Vollbild-Probleme”](#vollbild-probleme) * Das Plugin spielt immer im Vollbildmodus ab * Stellen Sie sicher, dass korrekte Berechtigungen für Vollbild in Ihrer App vorhanden sind ### Performance [Section titled “Performance”](#performance) * Verwenden Sie geeignete Video-Qualität für Zielgeräte * Erwägen Sie adaptives Streaming für bessere Performance * Implementieren Sie geeignete Puffer-Indikatoren # @capgo/capacitor-launch-navigator > Starten Sie Navigations-Apps mit Turn-by-Turn-Anweisungen, unterstützen Sie mehrere Kartenanbieter und benutzerdefinierte Routing-Optionen. ## Übersicht [Section titled “Übersicht”](#übersicht) Das Capacitor Launch Navigator-Plugin ermöglicht das Starten nativer Navigations-Apps auf iOS- und Android-Geräten mit koordinatenbasierter Navigation. Dieses Plugin bietet nahtlose Integration mit beliebten Kartenanwendungen und unterstützt mehrere Transportmodi für umfassende Navigationslösungen. Multi-App-Unterstützung Google Maps, Apple Maps, Waze, Citymapper und mehr 🗺️ Transportmodi Fahr-, Geh-, Öffentliche Verkehrsmittel- und Fahrradrichtungen 🧭 App-Erkennung Verfügbarkeit prüfen und installierte Navigations-Apps auflisten 🔍 Koordinatenbasiert Navigieren mit Breiten-/Längengrad-Koordinaten 📍 ## Installation [Section titled “Installation”](#installation) ```bash npm install @capgo/capacitor-launch-navigator npx cap sync ``` ## iOS-Konfiguration [Section titled “iOS-Konfiguration”](#ios-konfiguration) Fügen Sie URL-Schemata zu Ihrer `Info.plist` hinzu, um installierte Navigations-Apps zu erkennen: ```xml LSApplicationQueriesSchemes googlemaps waze citymapper ``` ## Kern-API-Methoden [Section titled “Kern-API-Methoden”](#kern-api-methoden) ### Navigation [Section titled “Navigation”](#navigation) * `navigate(options)` - Navigation zu angegebenen Koordinaten starten * `isAppAvailable(options)` - Prüfen, ob eine bestimmte Navigations-App installiert ist * `getAvailableApps()` - Alle verfügbaren Navigations-Apps auf dem Gerät auflisten * `getSupportedApps()` - Alle unterstützten Apps für die aktuelle Plattform abrufen ## Verwendungsbeispiel [Section titled “Verwendungsbeispiel”](#verwendungsbeispiel) ```typescript import { LaunchNavigator, TransportMode } from '@capgo/capacitor-launch-navigator'; // Grundlegende Navigation zu Koordinaten await LaunchNavigator.navigate({ destination: [37.7749, -122.4194], // San Francisco Koordinaten }); // Erweiterte Navigation mit Optionen await LaunchNavigator.navigate({ destination: [37.7749, -122.4194], options: { start: [37.7849, -122.4094], // Optionaler Startpunkt transportMode: TransportMode.DRIVING, app: 'google_maps', // Bevorzugte Navigations-App appDisplayName: 'Google Maps' } }); // Prüfen, ob bestimmte App verfügbar ist const result = await LaunchNavigator.isAppAvailable({ app: 'google_maps' }); if (result.available) { console.log('Google Maps ist installiert'); } else { console.log('Google Maps ist nicht verfügbar'); } // Alle verfügbaren Navigations-Apps abrufen const availableApps = await LaunchNavigator.getAvailableApps(); console.log('Verfügbare Navigations-Apps:', availableApps); // Alle unterstützten Apps für die Plattform abrufen const supportedApps = await LaunchNavigator.getSupportedApps(); console.log('Unterstützte Apps:', supportedApps); ``` ## Navigationsoptionen [Section titled “Navigationsoptionen”](#navigationsoptionen) ```typescript interface NavigationOptions { start?: [number, number]; // Startkoordinaten [lat, lng] transportMode?: TransportMode; // Transportmethode app?: string; // Bevorzugte Navigations-App appDisplayName?: string; // Anzeigename für App launchMode?: LaunchMode; // Wie die App gestartet werden soll } ``` ## Transportmodi [Section titled “Transportmodi”](#transportmodi) ```typescript enum TransportMode { DRIVING = 'driving', WALKING = 'walking', TRANSIT = 'transit', CYCLING = 'cycling' } ``` ## Unterstützte Navigations-Apps [Section titled “Unterstützte Navigations-Apps”](#unterstützte-navigations-apps) ### iOS [Section titled “iOS”](#ios) * Apple Maps (integriert) * Google Maps * Waze * Citymapper * Transit * Moovit * Uber * Lyft * Und viele mehr… ### Android [Section titled “Android”](#android) * Google Maps (integriert) * Waze * Citymapper * HERE WeGo * Sygic * MapQuest * Moovit * Und viele mehr… ## Koordinaten-Anforderungen [Section titled “Koordinaten-Anforderungen”](#koordinaten-anforderungen) > ⚠️ **Wichtig**: Dieses Plugin akzeptiert nur Breiten-/Längengrad-Koordinaten für die Navigation. Verwenden Sie [@capgo/capacitor-nativegeocoder](https://github.com/Cap-go/capacitor-nativegeocoder), um Adressen in Koordinaten umzuwandeln. ```typescript // Beispiel mit Geocoding import { NativeGeocoder } from '@capgo/capacitor-nativegeocoder'; // Adresse in Koordinaten umwandeln const geocodeResult = await NativeGeocoder.forwardGeocode({ address: '1600 Amphitheatre Parkway, Mountain View, CA' }); if (geocodeResult.results.length > 0) { const coords = geocodeResult.results[0]; // Navigation mit geokodierten Koordinaten starten await LaunchNavigator.navigate({ destination: [coords.latitude, coords.longitude] }); } ``` ## App-Erkennung und -Auswahl [Section titled “App-Erkennung und -Auswahl”](#app-erkennung-und--auswahl) ```typescript // Mehrere Apps prüfen und die erste verfügbare verwenden const appsToCheck = ['google_maps', 'waze', 'apple_maps']; let selectedApp = null; for (const app of appsToCheck) { const result = await LaunchNavigator.isAppAvailable({ app }); if (result.available) { selectedApp = app; break; } } if (selectedApp) { await LaunchNavigator.navigate({ destination: [37.7749, -122.4194], options: { app: selectedApp } }); } else { console.log('Keine unterstützten Navigations-Apps gefunden'); } ``` ## Fehlerbehandlung [Section titled “Fehlerbehandlung”](#fehlerbehandlung) ```typescript try { await LaunchNavigator.navigate({ destination: [37.7749, -122.4194], options: { app: 'google_maps', transportMode: TransportMode.DRIVING } }); } catch (error) { console.error('Navigation fehlgeschlagen:', error); // Fehler behandeln - App nicht verfügbar, ungültige Koordinaten, etc. } ``` ## Anwendungsfälle [Section titled “Anwendungsfälle”](#anwendungsfälle) * **Standortbasierte Dienste**: Benutzer zu Sehenswürdigkeiten navigieren * **Liefer-Apps**: Fahrer zu Lieferorten führen * **Event-Apps**: Teilnehmer zu Veranstaltungsorten leiten * **Immobilien-Apps**: Zu Immobilienstandorten navigieren * **Reise-Apps**: Touristen zu Attraktionen führen ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) * Prüfen Sie immer die App-Verfügbarkeit, bevor Sie die Navigation versuchen * Bieten Sie Fallback-Optionen an, wenn bevorzugte Apps nicht verfügbar sind * Verwenden Sie aussagekräftige App-Anzeigenamen für die Benutzerauswahl * Behandeln Sie Fehler mit benutzerfreundlichen Meldungen * Berücksichtigen Sie Benutzerpräferenzen für Standard-Navigations-Apps ## Dokumentation [Section titled “Dokumentation”](#dokumentation) Schauen Sie sich die [vollständige Dokumentation](/docs/plugins/launch-navigator/getting-started/) für detaillierte Implementierungsanleitungen und erweiterte Navigationsmuster an. # Loslegen > Erfahren Sie, wie Sie das Launch Navigator-Plugin installieren und verwenden, um native Navigations-Apps auf iOS und Android zu integrieren. ## Installation [Section titled “Installation”](#installation) * npm ```bash npm install @capgo/capacitor-launch-navigator npx cap sync ``` * yarn ```bash yarn add @capgo/capacitor-launch-navigator npx cap sync ``` * pnpm ```bash pnpm add @capgo/capacitor-launch-navigator npx cap sync ``` * bun ```bash bun add @capgo/capacitor-launch-navigator npx cap sync ``` ## Plattform-Konfiguration [Section titled “Plattform-Konfiguration”](#plattform-konfiguration) ### iOS [Section titled “iOS”](#ios) Fügen Sie URL-Schemata zu Ihrer `Info.plist` hinzu, um installierte Navigations-Apps zu erkennen: ```xml LSApplicationQueriesSchemes googlemaps waze citymapper transit moovit uber lyft ``` ### Android [Section titled “Android”](#android) Keine zusätzliche Konfiguration erforderlich. Das Plugin erkennt installierte Navigations-Apps automatisch. ## Verwendungsbeispiel [Section titled “Verwendungsbeispiel”](#verwendungsbeispiel) ```typescript import { LaunchNavigator, TransportMode } from '@capgo/capacitor-launch-navigator'; // Grundlegende Navigation zu Koordinaten await LaunchNavigator.navigate({ destination: [37.7749, -122.4194], // San Francisco Koordinaten }); // Erweiterte Navigation mit Optionen await LaunchNavigator.navigate({ destination: [37.7749, -122.4194], options: { start: [37.7849, -122.4094], // Optionaler Startpunkt transportMode: TransportMode.DRIVING, app: 'google_maps', // Bevorzugte Navigations-App appDisplayName: 'Google Maps' } }); // Prüfen, ob bestimmte App verfügbar ist const result = await LaunchNavigator.isAppAvailable({ app: 'google_maps' }); if (result.available) { console.log('Google Maps ist installiert'); } else { console.log('Google Maps ist nicht verfügbar'); } // Alle verfügbaren Navigations-Apps abrufen const availableApps = await LaunchNavigator.getAvailableApps(); console.log('Verfügbare Navigations-Apps:', availableApps); // Alle unterstützten Apps für die Plattform abrufen const supportedApps = await LaunchNavigator.getSupportedApps(); console.log('Unterstützte Apps:', supportedApps); ``` ## API-Referenz [Section titled “API-Referenz”](#api-referenz) ### navigate(options) [Section titled “navigate(options)”](#navigateoptions) ```typescript navigate(options: NavigateOptions) => Promise ``` Startet die Navigation zu angegebenen Koordinaten. | Param | Typ | | ------------- | ----------------- | | **`options`** | `NavigateOptions` | ### isAppAvailable(options) [Section titled “isAppAvailable(options)”](#isappavailableoptions) ```typescript isAppAvailable(options: { app: string }) => Promise<{ available: boolean }> ``` Prüft, ob eine bestimmte Navigations-App installiert ist. | Param | Typ | | ------------- | ----------------- | | **`options`** | `{ app: string }` | **Rückgabe:** `Promise<{ available: boolean }>` ### getAvailableApps() [Section titled “getAvailableApps()”](#getavailableapps) ```typescript getAvailableApps() => Promise<{ apps: string[] }> ``` Ruft die Liste aller verfügbaren Navigations-Apps auf dem Gerät ab. **Rückgabe:** `Promise<{ apps: string[] }>` ### getSupportedApps() [Section titled “getSupportedApps()”](#getsupportedapps) ```typescript getSupportedApps() => Promise<{ apps: string[] }> ``` Ruft die Liste aller unterstützten Apps für die aktuelle Plattform ab. **Rückgabe:** `Promise<{ apps: string[] }>` ## Schnittstellen [Section titled “Schnittstellen”](#schnittstellen) ### NavigateOptions [Section titled “NavigateOptions”](#navigateoptions-1) | Eigenschaft | Typ | Beschreibung | | ----------------- | ------------------- | ------------------------------------------ | | **`destination`** | `[number, number]` | Zielkoordinaten \[lat, lng] | | **`options`** | `NavigationOptions` | Zusätzliche Navigationsoptionen (optional) | ### NavigationOptions [Section titled “NavigationOptions”](#navigationoptions) | Eigenschaft | Typ | Beschreibung | | -------------------- | ------------------ | --------------------------------------- | | **`start`** | `[number, number]` | Startkoordinaten \[lat, lng] (optional) | | **`transportMode`** | `TransportMode` | Transportmethode (optional) | | **`app`** | `string` | Bevorzugte Navigations-App (optional) | | **`appDisplayName`** | `string` | Anzeigename für App (optional) | ### TransportMode [Section titled “TransportMode”](#transportmode) ```typescript enum TransportMode { DRIVING = 'driving', WALKING = 'walking', TRANSIT = 'transit', CYCLING = 'cycling' } ``` ## Koordinaten-Anforderung [Section titled “Koordinaten-Anforderung”](#koordinaten-anforderung) > **Wichtig**: Dieses Plugin akzeptiert nur Breiten-/Längengrad-Koordinaten für die Navigation. Verwenden Sie [@capgo/capacitor-nativegeocoder](https://github.com/Cap-go/capacitor-nativegeocoder), um Adressen in Koordinaten umzuwandeln. ```typescript import { NativeGeocoder } from '@capgo/capacitor-nativegeocoder'; import { LaunchNavigator } from '@capgo/capacitor-launch-navigator'; // Adresse in Koordinaten umwandeln const geocodeResult = await NativeGeocoder.forwardGeocode({ address: '1600 Amphitheatre Parkway, Mountain View, CA' }); if (geocodeResult.results.length > 0) { const coords = geocodeResult.results[0]; // Navigation mit geokodierten Koordinaten starten await LaunchNavigator.navigate({ destination: [coords.latitude, coords.longitude] }); } ``` ## Unterstützte Navigations-Apps [Section titled “Unterstützte Navigations-Apps”](#unterstützte-navigations-apps) ### iOS [Section titled “iOS”](#ios-1) * Apple Maps (integriert) * Google Maps * Waze * Citymapper * Transit * Moovit * Uber * Lyft * Und viele mehr… ### Android [Section titled “Android”](#android-1) * Google Maps (integriert) * Waze * Citymapper * HERE WeGo * Sygic * MapQuest * Moovit * Und viele mehr… ## Erweiterte Beispiele [Section titled “Erweiterte Beispiele”](#erweiterte-beispiele) ### Mehrere Apps prüfen und die erste verfügbare verwenden [Section titled “Mehrere Apps prüfen und die erste verfügbare verwenden”](#mehrere-apps-prüfen-und-die-erste-verfügbare-verwenden) ```typescript const appsToCheck = ['google_maps', 'waze', 'apple_maps']; let selectedApp = null; for (const app of appsToCheck) { const result = await LaunchNavigator.isAppAvailable({ app }); if (result.available) { selectedApp = app; break; } } if (selectedApp) { await LaunchNavigator.navigate({ destination: [37.7749, -122.4194], options: { app: selectedApp } }); } else { console.log('Keine unterstützten Navigations-Apps gefunden'); } ``` ### Benutzerauswahl der Navigations-App [Section titled “Benutzerauswahl der Navigations-App”](#benutzerauswahl-der-navigations-app) ```typescript // Verfügbare Apps abrufen const { apps } = await LaunchNavigator.getAvailableApps(); if (apps.length === 0) { alert('Keine Navigations-Apps verfügbar'); return; } // App-Auswahl dem Benutzer zeigen (Pseudo-Code) const selectedApp = await showAppSelectionDialog(apps); // Mit ausgewählter App navigieren await LaunchNavigator.navigate({ destination: [37.7749, -122.4194], options: { app: selectedApp } }); ``` ### Navigation mit verschiedenen Transportmodi [Section titled “Navigation mit verschiedenen Transportmodi”](#navigation-mit-verschiedenen-transportmodi) ```typescript // Fahranweisungen await LaunchNavigator.navigate({ destination: [37.7749, -122.4194], options: { transportMode: TransportMode.DRIVING } }); // Gehrichtungen await LaunchNavigator.navigate({ destination: [37.7749, -122.4194], options: { transportMode: TransportMode.WALKING } }); // Öffentliche Verkehrsmittel await LaunchNavigator.navigate({ destination: [37.7749, -122.4194], options: { transportMode: TransportMode.TRANSIT } }); ``` ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) * Prüfen Sie immer die App-Verfügbarkeit, bevor Sie die Navigation versuchen * Bieten Sie Fallback-Optionen an, wenn bevorzugte Apps nicht verfügbar sind * Verwenden Sie aussagekräftige App-Anzeigenamen für die Benutzerauswahl * Behandeln Sie Fehler mit benutzerfreundlichen Meldungen * Berücksichtigen Sie Benutzerpräferenzen für Standard-Navigations-Apps * Testen Sie auf iOS- und Android-Geräten * Implementieren Sie eine ordnungsgemäße Fehlerbehandlung für ungültige Koordinaten ## Fehlerbehandlung [Section titled “Fehlerbehandlung”](#fehlerbehandlung) ```typescript try { await LaunchNavigator.navigate({ destination: [37.7749, -122.4194], options: { app: 'google_maps', transportMode: TransportMode.DRIVING } }); } catch (error) { console.error('Navigation fehlgeschlagen:', error); // Fehler behandeln - App nicht verfügbar, ungültige Koordinaten, etc. alert('Navigation konnte nicht gestartet werden. Bitte überprüfen Sie Ihre Koordinaten und versuchen Sie es erneut.'); } ``` ## Anwendungsfälle [Section titled “Anwendungsfälle”](#anwendungsfälle) * **Standortbasierte Dienste**: Benutzer zu Sehenswürdigkeiten navigieren * **Liefer-Apps**: Fahrer zu Lieferorten führen * **Event-Apps**: Teilnehmer zu Veranstaltungsorten leiten * **Immobilien-Apps**: Zu Immobilienstandorten navigieren * **Reise-Apps**: Touristen zu Attraktionen führen * **Service-Apps**: Außendienstmitarbeiter zu Einsatzorten leiten # @capgo/capacitor-live-reload > Verbinden Sie Ihre Capacitor-App mit Ihrem lokalen Entwicklungsserver für sofortiges Hot Reloading und schnellere Iteration während der Entwicklung. Hot Module Replacement Sofortige Updates ohne Neuerstellung Ihrer App Remote-Dev-Server Verbindung zu Vite oder jedem kompatiblen Dev-Server WebSocket-Verbindung Echtzeit-Kommunikation für Live-Updates Umfassende Dokumentation Schauen Sie sich die [Dokumentation](/docs/plugins/live-reload/getting-started/) an, um Live Reload einzurichten. # Erste Schritte > Erfahren Sie, wie Sie Live Reload für schnellere Entwicklung mit Ihrer Capacitor-App einrichten. 1. **Installieren Sie das Paket** * npm ```sh npm i @capgo/capacitor-live-reload ``` * pnpm ```sh pnpm add @capgo/capacitor-live-reload ``` * yarn ```sh yarn add @capgo/capacitor-live-reload ``` * bun ```sh bun add @capgo/capacitor-live-reload ``` 2. **Mit nativen Projekten synchronisieren** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Einrichtung [Section titled “Einrichtung”](#einrichtung) Konfigurieren Sie Ihren Vite Dev-Server für die Arbeit mit Live Reload. Sie müssen: 1. Den eingebauten HMR-Client deaktivieren 2. Reload-Events über einen dedizierten WebSocket-Endpunkt weiterleiten ## Verwendung [Section titled “Verwendung”](#verwendung) ```typescript import { LiveReload } from '@capgo/capacitor-live-reload'; // Dev-Server konfigurieren await LiveReload.configureServer({ url: 'http://localhost:5173', websocketPath: '/capgo-livereload', autoReconnect: true, reconnectInterval: 2000 }); // Zum Dev-Server verbinden await LiveReload.connect(); // Reload-Ereignisse abhören LiveReload.addListener('reloadEvent', (event) => { console.log('Reload-Ereignis:', event); if (event.type === 'full-reload') { console.log('Vollständiges Neuladen der Seite ausgelöst'); } else if (event.type === 'file-update') { console.log('Datei aktualisiert:', event.file); } }); // Verbindungsstatus-Änderungen abhören LiveReload.addListener('statusChange', (status) => { console.log('Verbindungsstatus:', status.connected); console.log('Server-URL:', status.url); }); ``` ## API-Referenz [Section titled “API-Referenz”](#api-referenz) ### configureServer(options) [Section titled “configureServer(options)”](#configureserveroptions) Speichert Remote-Dev-Server-Einstellungen für nachfolgende Verbindungen. ```typescript const status = await LiveReload.configureServer({ url: 'http://192.168.1.100:5173', websocketPath: '/capgo-livereload', headers: { 'Authorization': 'Bearer token' }, autoReconnect: true, reconnectInterval: 2000 }); ``` **Parameter:** * `url` (string): Basis-URL für den Dev-Server (z.B. `http://dev.local:5173`) * `websocketPath` (string, optional): WebSocket-Pfad-Überschreibung (Standard: `/ws`) * `headers` (Record\, optional): Zusätzliche Header für WebSocket-Verbindung * `autoReconnect` (boolean, optional): Automatisch bei Trennung erneut verbinden (Standard: `true`) * `reconnectInterval` (number, optional): Verzögerung zwischen Verbindungsversuchen in ms (Standard: `2000`) **Rückgabe:** `LiveReloadStatus` mit Verbindungsinformationen ### connect() [Section titled “connect()”](#connect) Stellt eine WebSocket-Verbindung her, wenn noch keine aktiv ist. ```typescript const status = await LiveReload.connect(); console.log('Verbunden:', status.connected); ``` **Rückgabe:** Aktueller Verbindungsstatus ### disconnect() [Section titled “disconnect()”](#disconnect) Schließt die aktuelle WebSocket-Verbindung und deaktiviert automatische Wiederverbindung. ```typescript await LiveReload.disconnect(); ``` ### getStatus() [Section titled “getStatus()”](#getstatus) Ruft den aktuellen Verbindungsstatus ab. ```typescript const status = await LiveReload.getStatus(); console.log('Verbunden:', status.connected); console.log('URL:', status.url); ``` ### reload() [Section titled “reload()”](#reload) Löst manuell ein vollständiges Neuladen der Capacitor WebView aus. ```typescript await LiveReload.reload(); ``` ### reloadFile(options) [Section titled “reloadFile(options)”](#reloadfileoptions) Lädt eine einzelne Datei/Modul neu (fällt auf vollständiges Neuladen zurück, wenn nicht unterstützt). ```typescript await LiveReload.reloadFile({ path: '/src/components/MyComponent.tsx', hash: 'abc123' }); ``` ### addListener(‘reloadEvent’, callback) [Section titled “addListener(‘reloadEvent’, callback)”](#addlistenerreloadevent-callback) Hört auf eingehende Reload-Ereignisse vom Server. ```typescript const handle = await LiveReload.addListener('reloadEvent', (event) => { switch (event.type) { case 'full-reload': console.log('Vollständiges Neuladen angefordert'); break; case 'file-update': console.log('Datei aktualisiert:', event.file?.path); break; case 'error': console.error('Fehler:', event.message); break; } }); // Listener entfernen, wenn fertig await handle.remove(); ``` ### addListener(‘statusChange’, callback) [Section titled “addListener(‘statusChange’, callback)”](#addlistenerstatuschange-callback) Hört auf Socket-Statusänderungen. ```typescript await LiveReload.addListener('statusChange', (status) => { console.log(status.connected ? 'Verbunden' : 'Getrennt'); }); ``` ### removeAllListeners() [Section titled “removeAllListeners()”](#removealllisteners) Entfernt alle registrierten Listener. ```typescript await LiveReload.removeAllListeners(); ``` ## Vollständiges Beispiel [Section titled “Vollständiges Beispiel”](#vollständiges-beispiel) ```typescript import { LiveReload } from '@capgo/capacitor-live-reload'; export class DevServer { private connected = false; async initialize() { // Nur in der Entwicklung aktivieren if (process.env.NODE_ENV !== 'development') { return; } try { // Server konfigurieren await LiveReload.configureServer({ url: 'http://192.168.1.100:5173', websocketPath: '/capgo-livereload', autoReconnect: true, reconnectInterval: 3000 }); // Listener vor dem Verbinden einrichten await LiveReload.addListener('reloadEvent', this.handleReloadEvent.bind(this)); await LiveReload.addListener('statusChange', this.handleStatusChange.bind(this)); // Verbinden await LiveReload.connect(); } catch (error) { console.error('Live Reload konnte nicht initialisiert werden:', error); } } private handleReloadEvent(event: any) { console.log('Reload-Ereignis empfangen:', event.type); switch (event.type) { case 'full-reload': this.performFullReload(); break; case 'file-update': this.handleFileUpdate(event.file); break; case 'error': console.error('Server-Fehler:', event.message); break; case 'connected': console.log('Server verbunden'); break; case 'disconnected': console.log('Server getrennt'); break; } } private handleStatusChange(status: any) { this.connected = status.connected; console.log(`Live Reload ${status.connected ? 'verbunden' : 'getrennt'}`); } private performFullReload() { console.log('Vollständiges Neuladen der Seite wird durchgeführt...'); window.location.reload(); } private handleFileUpdate(file: any) { console.log('Datei aktualisiert:', file?.path); // HMR wird dies in den meisten Fällen automatisch behandeln } async disconnect() { await LiveReload.disconnect(); await LiveReload.removeAllListeners(); this.connected = false; } isConnected(): boolean { return this.connected; } } ``` ## Vite-Konfigurationsbeispiel [Section titled “Vite-Konfigurationsbeispiel”](#vite-konfigurationsbeispiel) Konfigurieren Sie Ihren Vite-Server für die Arbeit mit dem Live Reload Plugin: vite.config.ts ```typescript import { defineConfig } from 'vite'; export default defineConfig({ server: { host: '0.0.0.0', // Verbindungen vom Netzwerk erlauben port: 5173, hmr: { // Benutzerdefinierter WebSocket-Pfad für Live Reload path: '/capgo-livereload', } } }); ``` ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) 1. **Nur in der Entwicklung verwenden** ```typescript if (import.meta.env.DEV) { await LiveReload.configureServer({ url: 'http://localhost:5173', websocketPath: '/capgo-livereload' }); await LiveReload.connect(); } ``` 2. **Verwenden Sie Ihre lokale IP für mobiles Testen** ```typescript const devServerUrl = process.env.VITE_DEV_SERVER_URL || 'http://192.168.1.100:5173'; await LiveReload.configureServer({ url: devServerUrl }); ``` 3. **Behandeln Sie Verbindungsfehler elegant** ```typescript try { await LiveReload.connect(); } catch (error) { console.warn('Konnte nicht zum Dev-Server verbinden, verwende Produktions-Build'); } ``` 4. **Beim App-Beenden aufräumen** ```typescript window.addEventListener('beforeunload', async () => { await LiveReload.disconnect(); }); ``` 5. **Verbindungsstatus in UI anzeigen** ```typescript LiveReload.addListener('statusChange', (status) => { // Indikator in Dev-Builds anzeigen updateDevIndicator(status.connected); }); ``` ## Plattform-Hinweise [Section titled “Plattform-Hinweise”](#plattform-hinweise) ### iOS [Section titled “iOS”](#ios) * Funktioniert mit iOS 11.0+ * Stellen Sie sicher, dass der Dev-Server vom Netzwerk Ihres Geräts aus erreichbar ist * Möglicherweise müssen Sie die Firewall konfigurieren, um Verbindungen zuzulassen ### Android [Section titled “Android”](#android) * Funktioniert mit Android 5.0 (API 21)+ * Verwenden Sie `adb reverse` für localhost-Verbindungen: ```bash adb reverse tcp:5173 tcp:5173 ``` ### Web [Section titled “Web”](#web) * Vollständige Unterstützung für Web-Plattform * Direkte WebSocket-Verbindung zum Vite Dev-Server ## Fehlerbehebung [Section titled “Fehlerbehebung”](#fehlerbehebung) **Verbindung schlägt fehl:** * Überprüfen Sie, ob der Dev-Server läuft * Prüfen Sie, ob Gerät und Computer im selben Netzwerk sind * Stellen Sie sicher, dass die Firewall Verbindungen auf dem Port erlaubt * Verwenden Sie die IP-Adresse statt localhost für mobile Geräte **Langsames Neuladen:** * Netzwerkgeschwindigkeit prüfen * Wiederverbindungsintervall reduzieren * Vite-Build-Konfiguration optimieren **WebSocket-Fehler:** * Überprüfen Sie, ob websocketPath mit der Vite-Konfiguration übereinstimmt * Auf Port-Konflikte prüfen * Stellen Sie sicher, dass Header korrekt sind, wenn Authentifizierung verwendet wird # @capgo/capacitor-llm > Führen Sie LLM-Modelle direkt auf dem Gerät mit nativer Apple Intelligence-Integration und Hardware-Beschleunigung aus. On-Device-Datenschutz Führen Sie LLM-Modelle direkt auf dem Gerät für Datenschutz und Offline-Funktionen aus 🤖 Apple Intelligence Native Apple Intelligence-Integration auf iOS 26.0+ 🍎 Mehrere Formate Unterstützung für .task- und .litertlm-Modellformate 📦 Hardware-Beschleunigung Schnelle Inferenz mit nativer Hardware-Beschleunigung ⚡ Streaming-Antworten Echtzeit-Streaming-Antworten mit Event-Listenern 🔄 Erste Schritte Schauen Sie sich den [Leitfaden für den Einstieg](/docs/plugins/llm/getting-started/) an, um das Plugin zu installieren und zu konfigurieren. # Erste Schritte > Erfahren Sie, wie Sie das Capacitor LLM Plugin installieren und verwenden, um KI-Modelle lokal auf iOS und Android auszuführen. ## Installation [Section titled “Installation”](#installation) * npm ```bash npm install @capgo/capacitor-llm npx cap sync ``` * yarn ```bash yarn add @capgo/capacitor-llm npx cap sync ``` * pnpm ```bash pnpm add @capgo/capacitor-llm npx cap sync ``` * bun ```bash bun add @capgo/capacitor-llm npx cap sync ``` ## Plattform-Konfiguration [Section titled “Plattform-Konfiguration”](#plattform-konfiguration) ### iOS-Konfiguration [Section titled “iOS-Konfiguration”](#ios-konfiguration) * **iOS 26.0+**: Verwendet standardmäßig Apple Intelligence (kein Modell erforderlich) - **Empfohlen** * **iOS < 26.0**: Erfordert benutzerdefinierte MediaPipe-Modelle (experimentell, kann Kompatibilitätsprobleme haben) Für benutzerdefinierte Modelle auf älteren iOS-Versionen platzieren Sie Modelldateien über Xcode’s “Copy Bundle Resources” in Ihrem iOS-App-Bundle. ### Android-Konfiguration [Section titled “Android-Konfiguration”](#android-konfiguration) Platzieren Sie Modelldateien in Ihrem Android-Assets-Ordner: ```plaintext android/app/src/main/assets/ ``` Sie benötigen **beide** Dateien für Android: * `.task`-Datei (Hauptmodell) * `.litertlm`-Datei (Begleitdatei) Download von [Kaggle Gemma-Modelle](https://www.kaggle.com/models/google/gemma) → “LiteRT (formerly TFLite)“-Tab ## Empfohlene Modelle [Section titled “Empfohlene Modelle”](#empfohlene-modelle) ### Für Android (Gemma-3-Modelle) [Section titled “Für Android (Gemma-3-Modelle)”](#für-android-gemma-3-modelle) * **Gemma 3 270M** - Kleinstes, effizientestes für Mobilgeräte (\~240-400MB) - **Empfohlen** * **Gemma 3 1B** - Größeres Textgenerierungsmodell (\~892MB-1.5GB) Download von [Kaggle Gemma-Modelle](https://www.kaggle.com/models/google/gemma) → Klicken Sie auf “LiteRT (formerly TFLite)“-Tab ### Für iOS [Section titled “Für iOS”](#für-ios) * **Apple Intelligence** (iOS 26.0+) - Integriert, kein Download erforderlich - **Empfohlen** * **Gemma-2 2B** (experimentell) - Kann Kompatibilitätsprobleme mit `.task`-Format haben Für benutzerdefinierte iOS-Modelle laden Sie von [Hugging Face MediaPipe-Modelle](https://huggingface.co/collections/google/mediapipe-668392ead2d6768e82fb3b87) herunter ## Verwendung [Section titled “Verwendung”](#verwendung) Plugin importieren und initialisieren: ```typescript import { CapgoLLM } from '@capgo/capacitor-llm'; import { Capacitor } from '@capacitor/core'; // Überprüfen, ob LLM bereit ist const { readiness } = await CapgoLLM.getReadiness(); console.log('LLM-Bereitschaft:', readiness); // Modell basierend auf Plattform festlegen const platform = Capacitor.getPlatform(); if (platform === 'ios') { // iOS: Apple Intelligence verwenden (Standard) await CapgoLLM.setModel({ path: 'Apple Intelligence' }); } else { // Android: MediaPipe-Modell verwenden await CapgoLLM.setModel({ path: '/android_asset/gemma-3-270m-it-int8.task' }); } // Chat-Sitzung erstellen const { id: chatId } = await CapgoLLM.createChat(); // KI-Antworten abhören CapgoLLM.addListener('textFromAi', (event) => { console.log('KI-Antwort:', event.text); }); // Auf Fertigstellung hören CapgoLLM.addListener('aiFinished', (event) => { console.log('KI hat Antwort abgeschlossen'); }); // Nachricht senden await CapgoLLM.sendMessage({ chatId, message: 'Hallo! Wie geht es dir heute?' }); ``` ## Erweiterte Funktionen [Section titled “Erweiterte Funktionen”](#erweiterte-funktionen) ### Modelle herunterladen [Section titled “Modelle herunterladen”](#modelle-herunterladen) ```typescript // Modell von URL herunterladen await CapgoLLM.downloadModel({ url: 'https://example.com/model.task', filename: 'model.task' }); // Für Android beide .task- und .litertlm-Dateien herunterladen await CapgoLLM.downloadModel({ url: 'https://example.com/gemma-3-270m-it-int8.task', companionUrl: 'https://example.com/gemma-3-270m-it-int8.litertlm', filename: 'gemma-3-270m-it-int8.task' }); // Download-Fortschritt abhören CapgoLLM.addListener('downloadProgress', (event) => { console.log(`Download-Fortschritt: ${event.progress}%`); console.log(`Heruntergeladen: ${event.downloadedBytes} / ${event.totalBytes}`); }); ``` ### Modellverwaltung [Section titled “Modellverwaltung”](#modellverwaltung) ```typescript // Spezifisches Modell mit Konfiguration festlegen await CapgoLLM.setModel({ path: '/android_asset/gemma-3-270m-it-int8.task', maxTokens: 2048, topk: 40, temperature: 0.8 }); // Bereitschaft prüfen const { readiness } = await CapgoLLM.getReadiness(); if (readiness === 'ready') { // Modell ist geladen und bereit } // Auf Bereitschaftsänderungen hören CapgoLLM.addListener('readinessChange', (event) => { console.log('Bereitschaft geändert:', event.readiness); }); ``` ## API-Methoden [Section titled “API-Methoden”](#api-methoden) ### createChat() [Section titled “createChat()”](#createchat) Erstellt eine neue Chat-Sitzung. ```typescript const { id: chatId } = await CapgoLLM.createChat(); ``` **Rückgabe:** `Promise<{ id: string; instructions?: string }>` ### sendMessage(…) [Section titled “sendMessage(…)”](#sendmessage) Sendet eine Nachricht an das LLM. ```typescript await CapgoLLM.sendMessage({ chatId: 'chat-id', message: 'Wie ist das Wetter?' }); ``` | Parameter | Typ | Beschreibung | | ------------- | -------- | --------------------- | | **`chatId`** | `string` | Chat-Sitzungs-ID | | **`message`** | `string` | Zu sendende Nachricht | ### getReadiness() [Section titled “getReadiness()”](#getreadiness) Prüft, ob das LLM einsatzbereit ist. ```typescript const { readiness } = await CapgoLLM.getReadiness(); ``` **Rückgabe:** `Promise<{ readiness: string }>` Mögliche Werte: * `ready` - Modell ist geladen und bereit * `loading` - Modell wird geladen * `not_ready` - Modell noch nicht geladen * `error` - Fehler beim Laden des Modells ### setModel(…) [Section titled “setModel(…)”](#setmodel) Setzt die Modellkonfiguration. ```typescript // iOS: Apple Intelligence verwenden (empfohlen) await CapgoLLM.setModel({ path: 'Apple Intelligence' }); // iOS: Benutzerdefiniertes MediaPipe-Modell verwenden (experimentell) await CapgoLLM.setModel({ path: 'Gemma2-2B-IT_multi-prefill-seq_q8_ekv1280', modelType: 'task', maxTokens: 1280 }); // Android: MediaPipe-Modell verwenden await CapgoLLM.setModel({ path: '/android_asset/gemma-3-270m-it-int8.task', maxTokens: 2048, topk: 40, temperature: 0.8 }); ``` | Parameter | Typ | Beschreibung | | ----------------- | -------- | --------------------------------------------------------- | | **`path`** | `string` | Modellpfad oder “Apple Intelligence” für iOS-System | | **`modelType`** | `string` | Optional: Modelldateityp (z.B. “task”, “bin”) | | **`maxTokens`** | `number` | Optional: Maximale Token, die das Modell verarbeitet | | **`topk`** | `number` | Optional: Anzahl der betrachteten Token bei jedem Schritt | | **`temperature`** | `number` | Optional: Zufälligkeit bei der Generierung (0.0-1.0) | | **`randomSeed`** | `number` | Optional: Zufallsseed für Generierung | ### downloadModel(…) [Section titled “downloadModel(…)”](#downloadmodel) Lädt ein Modell von einer URL herunter und speichert es im Gerätespeicher. ```typescript await CapgoLLM.downloadModel({ url: 'https://example.com/gemma-3-270m-it-int8.task', companionUrl: 'https://example.com/gemma-3-270m-it-int8.litertlm', filename: 'gemma-3-270m-it-int8.task' }); ``` | Parameter | Typ | Beschreibung | | ------------------ | -------- | ------------------------------------------ | | **`url`** | `string` | URL zum Herunterladen | | **`companionUrl`** | `string` | Optional: URL für Begleitdatei (.litertlm) | | **`filename`** | `string` | Optional: Dateiname zum Speichern | **Rückgabe:** `Promise<{ path: string; companionPath?: string }>` ## Ereignisse [Section titled “Ereignisse”](#ereignisse) ### textFromAi [Section titled “textFromAi”](#textfromai) Wird ausgelöst, wenn KI Text generiert (Streaming-Antwort). ```typescript CapgoLLM.addListener('textFromAi', (event) => { console.log('KI-Text:', event.text); console.log('Chat-ID:', event.chatId); console.log('Ist Chunk:', event.isChunk); }); ``` **Ereignisdaten:** * `text` (string) - Inkrementeller Textabschnitt von KI * `chatId` (string) - Chat-Sitzungs-ID * `isChunk` (boolean) - Ob dies ein vollständiger Chunk oder partielle Streaming-Daten ist ### aiFinished [Section titled “aiFinished”](#aifinished) Wird ausgelöst, wenn KI die Antwort abgeschlossen hat. ```typescript CapgoLLM.addListener('aiFinished', (event) => { console.log('Abgeschlossen für Chat:', event.chatId); }); ``` **Ereignisdaten:** * `chatId` (string) - Chat-Sitzungs-ID ### downloadProgress [Section titled “downloadProgress”](#downloadprogress) Wird während des Modell-Downloads ausgelöst, um Fortschritt zu melden. ```typescript CapgoLLM.addListener('downloadProgress', (event) => { console.log('Fortschritt:', event.progress, '%'); console.log('Heruntergeladen:', event.downloadedBytes, '/', event.totalBytes); }); ``` **Ereignisdaten:** * `progress` (number) - Prozentsatz des abgeschlossenen Downloads (0-100) * `downloadedBytes` (number) - Bisher heruntergeladene Bytes * `totalBytes` (number) - Gesamte zu ladende Bytes ### readinessChange [Section titled “readinessChange”](#readinesschange) Wird ausgelöst, wenn sich der Bereitschaftsstatus des LLM ändert. ```typescript CapgoLLM.addListener('readinessChange', (event) => { console.log('Bereitschaft geändert zu:', event.readiness); }); ``` **Ereignisdaten:** * `readiness` (string) - Der neue Bereitschaftsstatus ## Vollständiges Beispiel [Section titled “Vollständiges Beispiel”](#vollständiges-beispiel) ```typescript import { CapgoLLM } from '@capgo/capacitor-llm'; import { Capacitor } from '@capacitor/core'; class AIService { private chatId: string | null = null; private messageBuffer: string = ''; async initialize() { // Modell basierend auf Plattform einrichten const platform = Capacitor.getPlatform(); if (platform === 'ios') { // iOS: Apple Intelligence verwenden (empfohlen) await CapgoLLM.setModel({ path: 'Apple Intelligence' }); } else { // Android: MediaPipe-Modell verwenden await CapgoLLM.setModel({ path: '/android_asset/gemma-3-270m-it-int8.task', maxTokens: 2048, topk: 40, temperature: 0.8 }); } // Warten, bis Modell bereit ist let isReady = false; while (!isReady) { const { readiness } = await CapgoLLM.getReadiness(); if (readiness === 'ready') { isReady = true; } else if (readiness === 'error') { throw new Error('Fehler beim Laden des Modells'); } await new Promise(resolve => setTimeout(resolve, 500)); } // Chat-Sitzung erstellen const { id } = await CapgoLLM.createChat(); this.chatId = id; // Event-Listener einrichten this.setupListeners(); } private setupListeners() { CapgoLLM.addListener('textFromAi', (event) => { if (event.chatId === this.chatId) { this.messageBuffer += event.text; this.onTextReceived(event.text); } }); CapgoLLM.addListener('aiFinished', (event) => { if (event.chatId === this.chatId) { this.onMessageComplete(this.messageBuffer); this.messageBuffer = ''; } }); } async sendMessage(message: string) { if (!this.chatId) { throw new Error('Chat nicht initialisiert'); } await CapgoLLM.sendMessage({ chatId: this.chatId, message }); } onTextReceived(text: string) { // UI mit Streaming-Text aktualisieren console.log('Empfangen:', text); } onMessageComplete(fullMessage: string) { // Vollständige Nachricht verarbeiten console.log('Vollständige Nachricht:', fullMessage); } } // Verwendung const ai = new AIService(); await ai.initialize(); await ai.sendMessage('Erzähl mir von KI'); ``` ## Plattform-Unterstützung [Section titled “Plattform-Unterstützung”](#plattform-unterstützung) | Plattform | Unterstützt | Anforderungen | | --------- | ----------- | ---------------------------------------- | | iOS | ✅ | iOS 13.0+ (26.0+ für Apple Intelligence) | | Android | ✅ | API 24+ | | Web | ❌ | Nicht unterstützt | ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) 1. **Modellauswahl**: Wählen Sie Modelle basierend auf Gerätefähigkeiten * Verwenden Sie 270M für die meisten Mobilgeräte * Verwenden Sie 1B für High-End-Geräte mit mehr RAM * Testen Sie Leistung auf Zielgeräten 2. **Speicherverwaltung**: Chat-Sitzungen löschen, wenn fertig ```typescript // Neuen Chat für neue Konversationen erstellen const { id } = await CapacitorLLM.createChat(); ``` 3. **Fehlerbehandlung**: Bereitschaft immer vor Verwendung prüfen ```typescript const { readiness } = await CapacitorLLM.getReadiness(); if (readiness !== 'ready') { // Nicht-bereiten Zustand behandeln } ``` 4. **Streaming-UI**: UI inkrementell mit Streaming-Text aktualisieren * Text anzeigen, wie er über `onAiText` eintrifft * Mit `onAiCompletion` als vollständig markieren 5. **Modell-Download**: Modelle während App-Setup herunterladen, nicht bei erster Verwendung ```typescript // Während App-Initialisierung await CapacitorLLM.downloadModel({ url: 'https://your-cdn.com/model.task', filename: 'model.task' }); ``` ## Fehlerbehebung [Section titled “Fehlerbehebung”](#fehlerbehebung) ### Modell lädt nicht [Section titled “Modell lädt nicht”](#modell-lädt-nicht) * Modelldatei am richtigen Ort überprüfen * Modellformat mit Plattform abgleichen (.gguf für iOS, .task für Android) * Ausreichenden Gerätespeicher sicherstellen ### Schlechte Leistung [Section titled “Schlechte Leistung”](#schlechte-leistung) * Kleineres Modell versuchen (270M statt 1B) * Andere Apps schließen, um Speicher freizugeben * Auf echtem Gerät testen, nicht im Simulator ### Keine Antworten [Section titled “Keine Antworten”](#keine-antworten) * Bereitschaftsstatus auf ‘ready’ prüfen * Event-Listener vor dem Senden von Nachrichten einrichten * Konsole auf Fehler überprüfen ## Ressourcen [Section titled “Ressourcen”](#ressourcen) * [GitHub-Repository](https://github.com/Cap-go/capacitor-llm) * [Gemma-Modelle](https://ai.google.dev/gemma) * [Apple Intelligence](https://developer.apple.com/machine-learning/) * [MediaPipe](https://developers.google.com/mediapipe) # @capgo/capacitor-media-session > Veröffentlichen Sie Track-Metadaten, reagieren Sie auf Play/Pause-Ereignisse und synchronisieren Sie den Wiedergabestatus mit nativen Mediensteuerungen. Das Media Session Plugin verbindet Capacitor mit der Media Session API, damit sich Ihre Audio- und Video-Player überall nativ anfühlen. Umfangreiche Metadaten Stellen Sie Album-Cover, Künstler und Titel für Sperrbildschirm- und Benachrichtigungssteuerungen bereit. Wiedergabestatus-Synchronisation Halten Sie Play/Pause-Tasten und Fortschrittsanzeigen mit Ihrem Player synchron. Action-Callbacks Behandeln Sie Headset-Tasten und OS-Medienaktionen aus Ihrem Capacitor-Code. Positionsmeldung Übertragen Sie Dauer, Position und Rate, damit Scrubbing flüssig funktioniert. Gehen Sie zum Leitfaden für den Einstieg, um Metadaten-Updates in Ihren Player-Lebenszyklus einzubinden. # Erste Schritte > Synchronisieren Sie Ihre Player-UI mit nativen Mediensteuerungen mithilfe des Media Session Plugins. 1. **Installieren Sie das Plugin** * npm ```sh npm i @capgo/capacitor-media-session ``` * pnpm ```sh pnpm add @capgo/capacitor-media-session ``` * yarn ```sh yarn add @capgo/capacitor-media-session ``` * bun ```sh bun add @capgo/capacitor-media-session ``` 2. **Plattformen synchronisieren** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` > ℹ️ Die Media Session API ist auf Android (Chrome-basierte Browser, WebView) und vielen Desktop-Browsern verfügbar. Auf nicht unterstützten Plattformen werden die Aufrufe ohne Wirkung aufgelöst, passen Sie Ihre UI entsprechend an. ## Metadaten veröffentlichen [Section titled “Metadaten veröffentlichen”](#metadaten-veröffentlichen) ```typescript import { MediaSession } from '@capgo/capacitor-media-session'; await MediaSession.setMetadata({ title: 'Wöchentlicher Tech-Podcast', artist: 'Capgo Studio', album: 'Staffel 2', artwork: [ { src: 'https://cdn.example.com/covers/s02e05-512.png', sizes: '512x512', type: 'image/.png' }, ], }); ``` ## Wiedergabestatus aktualisieren [Section titled “Wiedergabestatus aktualisieren”](#wiedergabestatus-aktualisieren) ```typescript await MediaSession.setPlaybackState({ playbackState: 'playing' }); // Später pausieren, wenn nötig await MediaSession.setPlaybackState({ playbackState: 'paused' }); ``` ## Medienaktionen handhaben [Section titled “Medienaktionen handhaben”](#medienaktionen-handhaben) ```typescript const pauseHandler = async () => { player.pause(); await MediaSession.setPlaybackState({ playbackState: 'paused' }); }; await MediaSession.setActionHandler({ action: 'pause' }, pauseHandler); await MediaSession.setActionHandler({ action: 'play' }, async () => { await player.play(); await MediaSession.setPlaybackState({ playbackState: 'playing' }); }); // Handler entfernen, wenn Player entsorgt wird await MediaSession.setActionHandler({ action: 'pause' }, null); ``` ## Position synchron halten [Section titled “Position synchron halten”](#position-synchron-halten) ```typescript const updatePosition = async () => { await MediaSession.setPositionState({ duration: player.duration, position: player.currentTime, playbackRate: player.playbackRate, }); }; player.ontimeupdate = updatePosition; player.onratechange = updatePosition; ``` ## Empfehlungen [Section titled “Empfehlungen”](#empfehlungen) * Aktualisieren Sie die Metadaten, wenn sich der Track ändert, damit Benachrichtigungen und Smart Speaker auf dem neuesten Stand bleiben. * Drosseln Sie Positionsupdates (z.B. alle 250 ms), um die native Ebene nicht zu überlasten. * Entfernen Sie immer Action-Handler beim Abbau einer Player-Instanz, um versehentliche Lecks zu verhindern. # @capgo/capacitor-mute > Überprüfen Sie, ob ein Gerät stummgeschaltet oder im Lautlos-Modus ist. Erkennt den Stummschalter auf iOS und den Lautstärkestatus auf Android. Lautlos-Modus-Erkennung Erkennen Sie, ob sich das Gerät im Lautlos-/Stummschaltungsmodus befindet 🔇 Plattformübergreifend Funktioniert sowohl auf iOS (Stummschalter) als auch auf Android (Lautstärke) 📱 Einfache API Einfach zu verwenden mit unkomplizierten Methoden 🎯 Umfassende Dokumentation Schauen Sie sich die [Dokumentation](/docs/plugins/mute/getting-started/) an, um das Plugin in nur wenigen Minuten zu meistern. # Erste Schritte > Erfahren Sie, wie Sie das Mute-Plugin installieren und verwenden, um den Stummschaltungsstatus in Ihrer Capacitor-App zu erkennen. 1. **Installieren Sie das Paket** * npm ```sh npm i @capgo/capacitor-mute ``` * pnpm ```sh pnpm add @capgo/capacitor-mute ``` * yarn ```sh yarn add @capgo/capacitor-mute ``` * bun ```sh bun add @capgo/capacitor-mute ``` 2. **Mit nativen Projekten synchronisieren** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Verwendung [Section titled “Verwendung”](#verwendung) Importieren Sie das Plugin und verwenden Sie seine Methoden, um den Stummschaltungsstatus zu überprüfen: ```typescript import { CapacitorMute } from '@capgo/capacitor-mute'; // Prüfen, ob Gerät stummgeschaltet ist const checkMuteState = async () => { const { value } = await CapacitorMute.isMuted(); if (value) { console.log('Gerät ist stummgeschaltet/lautlos'); // App-Verhalten für Lautlos-Modus anpassen } else { console.log('Geräteton ist eingeschaltet'); } }; ``` ## API-Referenz [Section titled “API-Referenz”](#api-referenz) ### isMuted() [Section titled “isMuted()”](#ismuted) Prüft, ob das Gerät derzeit stummgeschaltet/im Lautlos-Modus ist. ```typescript const result = await CapacitorMute.isMuted(); // Rückgabe: { value: boolean } ``` ## Vollständiges Beispiel [Section titled “Vollständiges Beispiel”](#vollständiges-beispiel) ```typescript import { CapacitorMute } from '@capgo/capacitor-mute'; export class SoundManager { private isMuted = false; async initialize() { // Anfänglichen Stummschaltungsstatus abrufen await this.checkMuteState(); } async checkMuteState() { try { const { value } = await CapacitorMute.isMuted(); this.isMuted = value; this.updateAppBehavior(); } catch (error) { console.error('Fehler beim Prüfen des Stummschaltungsstatus:', error); } } private updateAppBehavior() { if (this.isMuted) { // Gerät ist stummgeschaltet - App-Verhalten anpassen this.disableSoundEffects(); this.showVisualNotifications(); } else { // Geräteton ist eingeschaltet this.enableSoundEffects(); this.useAudioNotifications(); } } private disableSoundEffects() { // In-App-Sounds deaktivieren console.log('Soundeffekte werden deaktiviert'); } private enableSoundEffects() { // In-App-Sounds aktivieren console.log('Soundeffekte werden aktiviert'); } private showVisualNotifications() { // Visuelles Feedback anstelle von Audio verwenden console.log('Visuelle Benachrichtigungen werden verwendet'); } private useAudioNotifications() { // Audio-Benachrichtigungen verwenden console.log('Audio-Benachrichtigungen werden verwendet'); } } // Verwendung const soundManager = new SoundManager(); await soundManager.initialize(); // Regelmäßig abfragen, um Änderungen zu überprüfen setInterval(() => { soundManager.checkMuteState(); }, 5000); // Alle 5 Sekunden prüfen ``` ## Plattformverhalten [Section titled “Plattformverhalten”](#plattformverhalten) ### iOS [Section titled “iOS”](#ios) * Erkennt den Status des physischen Stummschalters * Keine Echtzeit-Änderungsereignisse (Abfrage erforderlich) * Funktioniert auf iPhone und iPad mit Stummschalter ### Android [Section titled “Android”](#android) * Prüft, ob der Klingeltonmodus auf lautlos oder vibrieren eingestellt ist * Keine Änderungsereignisse (Abfrage erforderlich) * Basierend auf AudioManager-Klingeltonmodus ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) 1. **Auf Änderungen abfragen** Da das Plugin keine Echtzeit-Ereignisse bereitstellt, fragen Sie regelmäßig ab, wenn Sie Änderungen verfolgen müssen: ```typescript // Stummschaltungsstatus regelmäßig prüfen setInterval(async () => { const { value } = await CapacitorMute.isMuted(); if (value !== previousMuteState) { handleMuteStateChange(value); } }, 5000); ``` 2. **Benutzerpräferenzen respektieren** Respektieren Sie immer den Stummschaltungsstatus und passen Sie das Audio-Verhalten Ihrer App entsprechend an. 3. **Visuelle Alternativen bereitstellen** ```typescript const { value: isMuted } = await CapacitorMute.isMuted(); if (isMuted) { // Visuelle Benachrichtigungen anzeigen showToast('Neue Nachricht empfangen'); } else { // Tonbenachrichtigung abspielen playNotificationSound(); } ``` ## Anwendungsfälle [Section titled “Anwendungsfälle”](#anwendungsfälle) 1. **Spiel-Sound-Management** ```typescript const shouldPlaySound = async () => { const { value: isMuted } = await CapacitorMute.isMuted(); return !isMuted && userPreferences.soundEnabled; }; ``` 2. **Benachrichtigungsbehandlung** ```typescript const sendNotification = async (message: string) => { const { value: isMuted } = await CapacitorMute.isMuted(); if (isMuted) { // Vibration oder visuelle Benachrichtigung verwenden await Haptics.vibrate(); showBanner(message); } else { // Benachrichtigungston abspielen await playSound('notification.mp3'); } }; ``` 3. **Videoplayer** ```typescript const initVideoPlayer = async () => { const { value: isMuted } = await CapacitorMute.isMuted(); videoPlayer.setMuted(isMuted); if (isMuted) { showSubtitles(true); showMuteIndicator(); } }; ``` ## Fehlerbehebung [Section titled “Fehlerbehebung”](#fehlerbehebung) 1. **Gibt auf Android immer false zurück** * Prüfen Sie, ob der Geräteklingeltonmodus tatsächlich auf lautlos eingestellt ist * Einige Android-Geräte können unterschiedliches Verhalten aufweisen 2. **Keine Änderungsereignisse verfügbar** * Das Plugin bietet keine Echtzeit-Änderungsereignisse * Implementieren Sie Abfragen, wenn Sie Änderungen erkennen müssen 3. **Funktioniert nicht im Simulator** * iOS-Simulator hat keinen Stummschalter * Testen Sie auf echten Geräten für genaue Ergebnisse # @capgo/capacitor-mux-player > Streamen Sie hochwertige Mux-Videos im Vollbildmodus mit DRM, Analytics und einheitlichen Steuerelementen auf iOS, Android und Web. Bieten Sie Premium-Wiedergabe, indem Sie die offiziellen Mux-nativen SDKs in eine einfache Capacitor-Schnittstelle einbinden. Native SDK-Erfahrung Verwenden Sie denselben Wiedergabe-Stack wie die Mux Mobile SDKs mit adaptiver Bitrate und DRM. Signierte Wiedergabe Stellen Sie Wiedergabe- und DRM-Token für sichere Bereitstellung geschützter Streams bereit. Einheitliche API Starten Sie die Wiedergabe, schließen Sie den Player und hören Sie auf Ereignisse mit einer TypeScript-API. Analytics-bereit Übergeben Sie Umgebungsschlüssel und Playernamen, um Mux Data-Telemetrie automatisch zu speisen. Der Leitfaden für die ersten Schritte führt durch das Einrichten der nativen SDKs, die Handhabung von Token und die Reaktion auf Player-Ereignisse. # Erste Schritte mit Mux Player > Integrieren Sie das native Mux Player SDK in Ihre Capacitor-Anwendung 1. **Plugin installieren** * npm ```sh npm i @capgo/capacitor-mux-player ``` * pnpm ```sh pnpm add @capgo/capacitor-mux-player ``` * yarn ```sh yarn add @capgo/capacitor-mux-player ``` * bun ```sh bun add @capgo/capacitor-mux-player ``` 2. **Native Projekte synchronisieren** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## iOS-Vorbereitung [Section titled “iOS-Vorbereitung”](#ios-vorbereitung) 1. Öffnen Sie den Xcode-Workspace unter `ios/App/`. 2. Fügen Sie das Swift-Package `https://github.com/muxinc/mux-player-swift` zu Ihrem App-Ziel hinzu, damit das `MuxPlayerSwift`-Modul verfügbar ist. 3. Stellen Sie sicher, dass das Deployment Target **iOS 15+** ist und erstellen Sie neu. ## Android-Vorbereitung [Section titled “Android-Vorbereitung”](#android-vorbereitung) Das Gradle-Modul wird mit den korrekten Repositories und Abhängigkeiten ausgeliefert. Wenn Sie einen benutzerdefinierten unternehmensinternen Proxy verwenden, erlauben Sie Anfragen an `https://muxinc.jfrog.io/artifactory/default-maven-release-local`. ## Player starten [Section titled “Player starten”](#player-starten) ```typescript import { MuxPlayer } from '@capgo/capacitor-mux-player'; await MuxPlayer.play({ playbackId: 'your-playback-id', environmentKey: 'your-mux-data-key', title: 'Launch Announcement', subtitle: 'Filmed live at Capgo HQ', poster: 'https://stream.example.com/poster.jpg', }); ``` ## Auf Player-Ereignisse abhören [Section titled “Auf Player-Ereignisse abhören”](#auf-player-ereignisse-abhören) ```typescript const readyHandle = await MuxPlayer.addListener('ready', ({ playerName }) => { console.log('Mux player ready', playerName); }); const errorHandle = await MuxPlayer.addListener('error', ({ message }) => { console.error('Mux player error:', message); }); // Aufräumen nach dem Schließen des Players const dismissPlayer = async () => { await MuxPlayer.dismiss(); await readyHandle.remove(); await errorHandle.remove(); }; ``` ## Token-basierte Wiedergabe [Section titled “Token-basierte Wiedergabe”](#token-basierte-wiedergabe) ```typescript await MuxPlayer.play({ playbackId: 'signed-playback-id', playbackToken: signedPlaybackToken, drmToken: signedDrmToken, // Optional, wenn Sie DRM-Richtlinien aktiviert haben autoPlay: true, startTime: 120, // beginnen Sie bei 2 Minuten }); ``` ## Auf Player-Lebenszyklus reagieren [Section titled “Auf Player-Lebenszyklus reagieren”](#auf-player-lebenszyklus-reagieren) ```typescript const { active } = await MuxPlayer.isActive(); if (active) { console.log('Player aktuell sichtbar'); } // Entfernen Sie alle Listener-Registrierungen beim Entfernen await MuxPlayer.removeAllListeners(); ``` ## Tipps [Section titled “Tipps”](#tipps) * Generieren Sie signierte Playback- und DRM-Tokens auf Ihrem Backend mit Mux-Signaturschlüsseln. * Übergeben Sie einen `playerName`, um die Analytik bei mehreren Playern in Ihrer App zu trennen. * Kombinieren Sie mit `enableSmartCache`, um die Offline-Stabilität auf unterstützten Plattformen zu verbessern. # @capgo/native-audio > Spielen Sie Audio mit nativer Leistung, niedriger Latenz und gleichzeitiger Wiedergabe ab. Perfekt für Spiele, Soundeffekte und Hintergrundmusik. Niedrige Latenz Native Audio-APIs für sofortige Wiedergabe 🚀 Gleichzeitige Wiedergabe Spielen Sie mehrere Sounds gleichzeitig ab 🎵 Hintergrundaudio Setzen Sie die Wiedergabe fort, wenn die App im Hintergrund ist 🎧 Umfassende Dokumentation Schauen Sie sich die [Dokumentation](/docs/plugins/native-audio/getting-started/) an, um das Plugin in nur wenigen Minuten zu beherrschen. # Erste Schritte > Erfahren Sie, wie Sie das Native Audio-Plugin für hochleistungsfähige Audio-Wiedergabe in Ihrer Capacitor-App installieren und verwenden. 1. **Installieren Sie das Paket** * npm ```sh npm i @capgo/native-audio ``` * pnpm ```sh pnpm add @capgo/native-audio ``` * yarn ```sh yarn add @capgo/native-audio ``` * bun ```sh bun add @capgo/native-audio ``` 2. **Synchronisieren Sie mit nativen Projekten** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` 3. **Fügen Sie Audiodateien hinzu** Platzieren Sie Ihre Audiodateien in den entsprechenden Plattformordnern: * **iOS**: `ios/App/App/sounds/` * **Android**: `android/app/src/main/res/raw/` ## Verwendung [Section titled “Verwendung”](#verwendung) Importieren Sie das Plugin und laden Sie Audiodateien vor, bevor Sie sie abspielen: ```typescript import { NativeAudio } from '@capgo/native-audio'; // Audiodateien vorladen const preloadAudio = async () => { // Einfaches Vorladen für kurze Sounds await NativeAudio.preload({ assetId: 'click', assetPath: 'sounds/click.mp3', audioChannelNum: 1, isUrl: false }); // Komplexes Vorladen für Musik/längere Audiodateien await NativeAudio.preloadComplex({ assetId: 'background-music', assetPath: 'sounds/background.mp3', audioChannelNum: 1, volume: 0.5, delay: 0, isUrl: false }); }; // Audio abspielen const playSound = async () => { await NativeAudio.play({ assetId: 'click' }); }; // Mit Optionen abspielen const playMusic = async () => { await NativeAudio.play({ assetId: 'background-music', time: 0 // Von Anfang an starten }); }; // Audio in Schleife abspielen const loopMusic = async () => { await NativeAudio.loop({ assetId: 'background-music' }); }; // Audio stoppen const stopMusic = async () => { await NativeAudio.stop({ assetId: 'background-music' }); }; // Entladen wenn fertig const cleanup = async () => { await NativeAudio.unload({ assetId: 'click' }); await NativeAudio.unload({ assetId: 'background-music' }); }; ``` ## API-Referenz [Section titled “API-Referenz”](#api-referenz) ### preload(options) [Section titled “preload(options)”](#preloadoptions) Lädt eine Audiodatei für einfache Wiedergabe vor (am besten für kurze Sounds). ```typescript interface PreloadOptions { assetId: string; assetPath: string; audioChannelNum?: number; isUrl?: boolean; } await NativeAudio.preload({ assetId: 'sound-effect', assetPath: 'sounds/effect.mp3', audioChannelNum: 1, isUrl: false }); ``` ### preloadComplex(options) [Section titled “preloadComplex(options)”](#preloadcomplexoptions) Lädt Audio mit erweiterten Optionen vor (am besten für Musik/Hintergrundaudio). ```typescript interface PreloadComplexOptions { assetId: string; assetPath: string; volume?: number; // 0.0 bis 1.0 audioChannelNum?: number; delay?: number; isUrl?: boolean; fadeDuration?: number; } await NativeAudio.preloadComplex({ assetId: 'theme-song', assetPath: 'sounds/theme.mp3', volume: 0.7, audioChannelNum: 2, isUrl: false }); ``` ### play(options) [Section titled “play(options)”](#playoptions) Spielt eine vorgeladene Audiodatei ab. ```typescript interface PlayOptions { assetId: string; time?: number; // Startzeit in Sekunden } await NativeAudio.play({ assetId: 'sound-effect', time: 0 }); ``` ### loop(options) [Section titled “loop(options)”](#loopoptions) Spielt eine vorgeladene Audiodatei in Schleife ab. ```typescript await NativeAudio.loop({ assetId: 'background-music' }); ``` ### stop(options) [Section titled “stop(options)”](#stopoptions) Stoppt die Wiedergabe einer Audiodatei. ```typescript await NativeAudio.stop({ assetId: 'background-music' }); ``` ### pause(options) [Section titled “pause(options)”](#pauseoptions) Pausiert die Audio-Wiedergabe. ```typescript await NativeAudio.pause({ assetId: 'background-music' }); ``` ### resume(options) [Section titled “resume(options)”](#resumeoptions) Setzt pausiertes Audio fort. ```typescript await NativeAudio.resume({ assetId: 'background-music' }); ``` ### setVolume(options) [Section titled “setVolume(options)”](#setvolumeoptions) Setzt die Lautstärke für ein Audio-Asset. ```typescript interface SetVolumeOptions { assetId: string; volume: number; // 0.0 bis 1.0 } await NativeAudio.setVolume({ assetId: 'background-music', volume: 0.3 }); ``` ### getDuration(options) [Section titled “getDuration(options)”](#getdurationoptions) Ermittelt die Dauer einer Audiodatei in Sekunden. ```typescript const { duration } = await NativeAudio.getDuration({ assetId: 'background-music' }); console.log(`Dauer: ${duration} Sekunden`); ``` ### getCurrentTime(options) [Section titled “getCurrentTime(options)”](#getcurrenttimeoptions) Ermittelt die aktuelle Wiedergabezeit in Sekunden. ```typescript const { currentTime } = await NativeAudio.getCurrentTime({ assetId: 'background-music' }); console.log(`Aktuelle Zeit: ${currentTime} Sekunden`); ``` ### isPlaying(options) [Section titled “isPlaying(options)”](#isplayingoptions) Überprüft, ob Audio gerade abgespielt wird. ```typescript const { isPlaying } = await NativeAudio.isPlaying({ assetId: 'background-music' }); console.log(`Wird abgespielt: ${isPlaying}`); ``` ### unload(options) [Section titled “unload(options)”](#unloadoptions) Entlädt eine Audiodatei aus dem Speicher. ```typescript await NativeAudio.unload({ assetId: 'sound-effect' }); ``` ## Erweiterte Verwendung [Section titled “Erweiterte Verwendung”](#erweiterte-verwendung) ### Sound Manager-Klasse [Section titled “Sound Manager-Klasse”](#sound-manager-klasse) ```typescript import { NativeAudio } from '@capgo/native-audio'; export class SoundManager { private sounds: Map = new Map(); private volume = 1.0; async init() { // Alle Sounds vorladen await this.preloadSound('click', 'sounds/click.mp3'); await this.preloadSound('success', 'sounds/success.mp3'); await this.preloadSound('error', 'sounds/error.mp3'); // Musik vorladen await this.preloadMusic('background', 'sounds/background.mp3', 0.5); } private async preloadSound(id: string, path: string) { try { await NativeAudio.preload({ assetId: id, assetPath: path, audioChannelNum: 1, isUrl: false }); this.sounds.set(id, true); } catch (error) { console.error(`Fehler beim Vorladen von ${id}:`, error); } } private async preloadMusic(id: string, path: string, volume: number) { try { await NativeAudio.preloadComplex({ assetId: id, assetPath: path, volume, audioChannelNum: 2, isUrl: false }); this.sounds.set(id, true); } catch (error) { console.error(`Fehler beim Vorladen von ${id}:`, error); } } async playSound(id: string) { if (!this.sounds.has(id)) { console.warn(`Sound ${id} nicht vorgeladen`); return; } try { await NativeAudio.play({ assetId: id }); } catch (error) { console.error(`Fehler beim Abspielen von ${id}:`, error); } } async playMusic(id: string) { if (!this.sounds.has(id)) return; try { await NativeAudio.loop({ assetId: id }); } catch (error) { console.error(`Fehler beim Abspielen von Musik ${id}:`, error); } } async stopMusic(id: string) { try { await NativeAudio.stop({ assetId: id }); } catch (error) { console.error(`Fehler beim Stoppen von ${id}:`, error); } } async setMasterVolume(volume: number) { this.volume = Math.max(0, Math.min(1, volume)); // Alle geladenen Sounds aktualisieren for (const [id] of this.sounds) { await NativeAudio.setVolume({ assetId: id, volume: this.volume }); } } async cleanup() { for (const [id] of this.sounds) { await NativeAudio.unload({ assetId: id }); } this.sounds.clear(); } } ``` ### Laden von URLs [Section titled “Laden von URLs”](#laden-von-urls) ```typescript // Audio von URL laden await NativeAudio.preloadComplex({ assetId: 'remote-audio', assetPath: 'https://example.com/audio.mp3', isUrl: true, volume: 0.8 }); ``` ### Ein-/Ausblenden-Effekte [Section titled “Ein-/Ausblenden-Effekte”](#ein-ausblenden-effekte) ```typescript const fadeIn = async (assetId: string, duration: number) => { const steps = 20; const stepDuration = duration / steps; await NativeAudio.setVolume({ assetId, volume: 0 }); await NativeAudio.play({ assetId }); for (let i = 1; i <= steps; i++) { await new Promise(resolve => setTimeout(resolve, stepDuration)); await NativeAudio.setVolume({ assetId, volume: i / steps }); } }; const fadeOut = async (assetId: string, duration: number) => { const steps = 20; const stepDuration = duration / steps; for (let i = steps; i >= 0; i--) { await NativeAudio.setVolume({ assetId, volume: i / steps }); await new Promise(resolve => setTimeout(resolve, stepDuration)); } await NativeAudio.stop({ assetId }); }; ``` ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) 1. **Während der App-Initialisierung vorladen** ```typescript import { App } from '@capacitor/app'; App.addListener('appStateChange', async ({ isActive }) => { if (isActive) { await soundManager.init(); } }); ``` 2. **Fehler elegant behandeln** ```typescript try { await NativeAudio.play({ assetId: 'sound' }); } catch (error) { console.log('Audio-Wiedergabe fehlgeschlagen, wird ohne Ton fortgesetzt'); } ``` 3. **Nicht verwendetes Audio entladen** ```typescript // Sounds entladen beim Verlassen eines Bildschirms ionViewWillLeave() { this.unloadScreenSounds(); } ``` 4. **Geeignete Vorlade-Methoden verwenden** * `preload()` für kurze Soundeffekte (< 5 Sekunden) * `preloadComplex()` für Musik und längere Audiodateien ## Plattform-Hinweise [Section titled “Plattform-Hinweise”](#plattform-hinweise) ### iOS [Section titled “iOS”](#ios) * Unterstützt AAC, MP3, WAV und andere Core Audio-Formate * Verwendet AVAudioPlayer für komplexes Audio * Verwendet System Sound Services für einfaches Audio * Unterstützt Hintergrundaudio mit entsprechender Konfiguration ### Android [Section titled “Android”](#android) * Unterstützt MP3, OGG, WAV-Formate * Verwendet SoundPool für einfaches Audio * Verwendet MediaPlayer für komplexes Audio * Benötigt möglicherweise WAKE\_LOCK-Berechtigung für Hintergrundwiedergabe ### Dateiplatzierung [Section titled “Dateiplatzierung”](#dateiplatzierung) #### iOS [Section titled “iOS”](#ios-1) Platzieren Sie Dateien in `ios/App/App/sounds/` oder erstellen Sie eine Ordnerreferenz in Xcode. #### Android [Section titled “Android”](#android-1) Platzieren Sie Dateien in `android/app/src/main/res/raw/`. Hinweis: Dateinamen müssen in Kleinbuchstaben und ohne Sonderzeichen sein. ## Häufige Probleme [Section titled “Häufige Probleme”](#häufige-probleme) 1. **Audio wird nicht abgespielt** * Stellen Sie sicher, dass Dateien in den richtigen Verzeichnissen sind * Überprüfen Sie die Kompatibilität des Dateiformats * Stellen Sie sicher, dass assetId genau übereinstimmt 2. **Verzögerung bei der Wiedergabe** * Verwenden Sie `preload()` für Soundeffekte * Vorladen, bevor Sie abspielen müssen 3. **Speicherprobleme** * Audiodateien entladen, wenn sie nicht benötigt werden * Nicht zu viele große Dateien vorladen 4. **Hintergrundwiedergabe** * Hintergrundaudio-Fähigkeit auf iOS konfigurieren * Audio-Fokus auf Android behandeln # @capgo/capacitor-native-biometric > Greifen Sie auf native biometrische Authentifizierungs-APIs für Android und iOS zu und bieten Sie sichere und bequeme Benutzerauthentifizierung. Mehrere Biometrie-Verfahren Unterstützt Fingerabdruck, Face ID, Touch ID und andere biometrische Sensoren 🚀 Sichere Authentifizierung Bietet sichere biometrische Authentifizierung für sensible Vorgänge 🔒 Fallback-Optionen Beinhaltet Gerätepasscode-Fallback, wenn Biometrie nicht verfügbar ist 😊 Umfassende Dokumentation Schauen Sie sich die [Dokumentation](/docs/plugins/native-biometric/getting-started/) an, um das Plugin in nur wenigen Minuten zu beherrschen. # Erste Schritte > Erfahren Sie, wie Sie das Native Biometric-Plugin für sichere biometrische Authentifizierung in Ihrer Capacitor-App installieren und verwenden. 1. **Installieren Sie das Paket** * npm ```sh npm i @capgo/capacitor-native-biometric ``` * pnpm ```sh pnpm add @capgo/capacitor-native-biometric ``` * yarn ```sh yarn add @capgo/capacitor-native-biometric ``` * bun ```sh bun add @capgo/capacitor-native-biometric ``` 2. **Synchronisieren Sie mit nativen Projekten** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` 3. **Berechtigungen konfigurieren** ### iOS [Section titled “iOS”](#ios) Fügen Sie die Face ID-Nutzungsbeschreibung zu Ihrer `Info.plist` hinzu: ```xml NSFaceIDUsageDescription For secure authentication ``` ### Android [Section titled “Android”](#android) Fügen Sie die biometrische Berechtigung zu Ihrer `AndroidManifest.xml` hinzu: ```xml ``` ## Verwendung [Section titled “Verwendung”](#verwendung) Importieren Sie das Plugin und verwenden Sie seine Methoden für biometrische Authentifizierung: ```typescript import { NativeBiometric } from '@capgo/capacitor-native-biometric'; // Überprüfen, ob biometrische Authentifizierung verfügbar ist const checkBiometric = async () => { const result = await NativeBiometric.isAvailable(); if (result.isAvailable) { console.log(`Biometrischer Typ: ${result.biometryType}`); } }; // Biometrische Verifizierung durchführen const verify = async () => { const verified = await NativeBiometric.verifyIdentity({ reason: "Für sicheren Zugriff auf Ihr Konto", title: "Authentifizierung", subtitle: "Verifizieren Sie Ihre Identität", description: "Legen Sie Ihren Finger auf den Sensor" }) .then(() => true) .catch(() => false); if (verified) { console.log("Authentifizierung erfolgreich"); } }; // Anmeldedaten sicher speichern const saveCredentials = async () => { await NativeBiometric.setCredentials({ username: "user@example.com", password: "securepassword", server: "https://api.example.com" }); }; // Gespeicherte Anmeldedaten abrufen const getCredentials = async () => { const credentials = await NativeBiometric.getCredentials({ server: "https://api.example.com" }); console.log(credentials.username); console.log(credentials.password); }; // Gespeicherte Anmeldedaten löschen const deleteCredentials = async () => { await NativeBiometric.deleteCredentials({ server: "https://api.example.com" }); }; ``` ## API-Referenz [Section titled “API-Referenz”](#api-referenz) ### isAvailable() [Section titled “isAvailable()”](#isavailable) Überprüft, ob biometrische Authentifizierung auf dem Gerät verfügbar ist. ```typescript interface AvailableResult { isAvailable: boolean; biometryType?: BiometryType; errorCode?: number; } enum BiometryType { NONE = 0, TOUCH_ID = 1, FACE_ID = 2, FINGERPRINT = 3, FACE_AUTHENTICATION = 4, IRIS_AUTHENTICATION = 5 } ``` ### verifyIdentity(options) [Section titled “verifyIdentity(options)”](#verifyidentityoptions) Führt biometrische Authentifizierung durch. ```typescript interface BiometricOptions { reason?: string; title?: string; subtitle?: string; description?: string; fallbackTitle?: string; useFallback?: boolean; maxAttempts?: number; } ``` ### setCredentials(options) [Section titled “setCredentials(options)”](#setcredentialsoptions) Speichert Anmeldedaten sicher mit biometrischem Schutz. ```typescript interface Credentials { username: string; password: string; server: string; } ``` ### getCredentials(options) [Section titled “getCredentials(options)”](#getcredentialsoptions) Ruft gespeicherte Anmeldedaten nach biometrischer Authentifizierung ab. ```typescript interface GetCredentialOptions { server: string; } ``` ### deleteCredentials(options) [Section titled “deleteCredentials(options)”](#deletecredentialsoptions) Löscht gespeicherte Anmeldedaten. ```typescript interface DeleteCredentialOptions { server: string; } ``` ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) 1. **Immer zuerst Verfügbarkeit prüfen** ```typescript const result = await NativeBiometric.isAvailable(); if (!result.isAvailable) { // Auf Passwort-Authentifizierung zurückgreifen } ``` 2. **Klare Gründe angeben** Erklären Sie immer, warum Sie biometrische Authentifizierung benötigen, um Vertrauen aufzubauen. 3. **Fehler elegant behandeln** ```typescript try { await NativeBiometric.verifyIdentity({ reason: "Zugriff auf Ihre sicheren Daten" }); } catch (error) { // Abbruch oder Fehler behandeln console.log("Authentifizierung fehlgeschlagen oder abgebrochen"); } ``` 4. **Geeignete Fallbacks verwenden** Aktivieren Sie Passcode-Fallback für Geräte, bei denen die Biometrie fehlschlagen kann. ## Plattform-Unterschiede [Section titled “Plattform-Unterschiede”](#plattform-unterschiede) ### iOS [Section titled “iOS”](#ios-1) * Unterstützt Touch ID und Face ID * Erfordert `NSFaceIDUsageDescription` für Face ID * Fallback zum Gerätepasscode verfügbar ### Android [Section titled “Android”](#android-1) * Unterstützt Fingerabdruck-, Gesichts- und Iris-Erkennung * Erfordert `USE_BIOMETRIC`-Berechtigung * Biometric Prompt API für Android 9+ # @capgo/native-market > Ein Capacitor-Plugin zur Weiterleitung von Benutzern zu App Stores (Google Play oder Apple App Store) aus Ihrer mobilen Anwendung. Plattformübergreifend Funktioniert nahtlos auf Android- und iOS-Plattformen 🚀 Einfache Integration Einfache API zur Weiterleitung von Benutzern, um Ihre App in ihren jeweiligen App Stores zu bewerten oder anzuzeigen 💨 TypeScript-Unterstützung Volle TypeScript-Unterstützung für bessere Entwicklererfahrung 😊 Umfassende Dokumentation Schauen Sie sich die [Dokumentation](/docs/plugins/native-market/getting-started/) an, um das Plugin in nur wenigen Minuten zu beherrschen. # Erste Schritte > Erfahren Sie, wie Sie das Native Market-Plugin installieren und verwenden, um Benutzer aus Ihrer Capacitor-App zum Google Play Store oder Apple App Store weiterzuleiten. 1. **Installieren Sie das Paket** * npm ```sh npm i @capgo/native-market ``` * pnpm ```sh pnpm add @capgo/native-market ``` * yarn ```sh yarn add @capgo/native-market ``` * bun ```sh bun add @capgo/native-market ``` 2. **Synchronisieren Sie mit nativen Projekten** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Verwendung [Section titled “Verwendung”](#verwendung) Importieren Sie das Plugin und verwenden Sie seine Methoden, um Benutzer zu App Stores weiterzuleiten: ```typescript import { NativeMarket } from '@capgo/native-market'; // App Store-Eintrag öffnen const openAppStore = async () => { await NativeMarket.openStoreListing({ appId: 'com.example.app' // Ihre App-Bundle-ID }); }; // App-Bewertung anfordern const requestReview = async () => { await NativeMarket.requestReview(); }; // App Store-Suche öffnen const searchInStore = async () => { await NativeMarket.search({ terms: 'fitness app' // Suchbegriffe }); }; ``` ## API-Referenz [Section titled “API-Referenz”](#api-referenz) ### openStoreListing(options) [Section titled “openStoreListing(options)”](#openstorelistingoptions) Öffnet den App Store-Eintrag für die angegebene App. ```typescript interface OpenStoreListingOptions { appId: string; // Bundle-ID auf iOS, Paketname auf Android } ``` ### requestReview() [Section titled “requestReview()”](#requestreview) Fordert eine In-App-Bewertung vom Benutzer an. Auf iOS 10.3+ wird der Bewertungsdialog angezeigt, ohne die App zu verlassen. ### search(options) [Section titled “search(options)”](#searchoptions) Öffnet den App Store mit Suchergebnissen. ```typescript interface SearchOptions { terms: string; // Zu verwendende Suchbegriffe } ``` ## Plattform-Hinweise [Section titled “Plattform-Hinweise”](#plattform-hinweise) ### iOS [Section titled “iOS”](#ios) * Verwendet `SKStoreReviewController` für In-App-Bewertungen auf iOS 10.3+ * Fällt auf das Öffnen des App Store für ältere Versionen zurück ### Android [Section titled “Android”](#android) * Öffnet Google Play Store * Verwendet In-App-Review-API, wenn verfügbar ## Beispiel [Section titled “Beispiel”](#beispiel) ```typescript import { NativeMarket } from '@capgo/native-market'; import { Capacitor } from '@capacitor/core'; export class AppService { async rateApp() { try { // Versuchen Sie zuerst In-App-Bewertung await NativeMarket.requestReview(); } catch (error) { // Fallback zum Öffnen des Store-Eintrags const platform = Capacitor.getPlatform(); const appId = platform === 'ios' ? 'id123456789' // Ihre iOS-App-ID : 'com.example.app'; // Ihr Android-Paketname await NativeMarket.openStoreListing({ appId }); } } } ``` # @capgo/native-purchases > Vereinfachen Sie In-App-Käufe und Abonnements mit einer einheitlichen API, die nahtlos auf iOS- und Android-Plattformen funktioniert. Einheitliche API Einzelne API für iOS- und Android-Käufe 💳 Abonnementverwaltung Verwalten Sie Abonnements mit automatischer Verlängerung 🔄 Quittungsvalidierung Integrierte Quittungsvalidierung für Sicherheit 🔒 Erste Schritte Sehen Sie sich den [Leitfaden für die ersten Schritte](/docs/plugins/native-purchases/getting-started/) an, um das Plugin zu installieren und zu konfigurieren. ## Plattform-Setup-Anleitungen [Section titled “Plattform-Setup-Anleitungen”](#plattform-setup-anleitungen) Android-Setup Vollständige Anleitungen zum Einrichten von In-App-Käufen auf Android: * [Sandbox-Tests konfigurieren](/docs/plugins/native-purchases/android-sandbox-testing/) * [Abonnements erstellen](/docs/plugins/native-purchases/android-create-subscription/) * [Einführungsangebote](/docs/plugins/native-purchases/android-introductory-offer/) iOS-Setup Vollständige Anleitungen zum Einrichten von In-App-Käufen auf iOS: * [Sandbox-Tests konfigurieren](/docs/plugins/native-purchases/ios-sandbox-testing/) * [Abonnementgruppen](/docs/plugins/native-purchases/ios-subscription-group/) * [Abonnements erstellen](/docs/plugins/native-purchases/ios-create-subscription/) * [Einführungsangebote](/docs/plugins/native-purchases/ios-introductory-offer/) # Android Auto-Renewable Subscription erstellen > Schritt-für-Schritt-Anleitung zur Erstellung von automatisch verlängernden Abonnements in der Google Play Console für das native-purchases-Plugin. Automatisch verlängernde Abonnements bieten fortlaufenden Zugang zu Inhalten, Diensten oder Premium-Funktionen in Ihrer App. Dieser Leitfaden hilft Ihnen, Abonnements in der Google Play Console zu erstellen und zu konfigurieren. ## Überblick [Section titled “Überblick”](#überblick) Abonnements werden am Ende jeder Abrechnungsperiode automatisch verlängert, bis der Benutzer kündigt. Sie sind ideal für: * Zugang zu Premium-Inhalten * Werbefreie Erfahrungen * Cloud-Speicher * Fortlaufende Dienste ## Ein Abonnement erstellen [Section titled “Ein Abonnement erstellen”](#ein-abonnement-erstellen) 1. **Zu Abonnements navigieren** Wählen Sie in der Google Play Console Ihre App aus und wählen Sie **Monetarisieren > Abonnements** aus dem linken Menü. Klicken Sie auf die Schaltfläche **Abonnement erstellen**, um zu beginnen. ![Zu Abonnements navigieren](/native-purchases/android/create-subscription/navigate-to-subscriptions.webp) 2. **Grundlegende Informationen eingeben** Geben Sie einen Abonnementnamen und eine Produkt-ID an. Die Produkt-ID ist für die Konfiguration in Ihrer App erforderlich und kann später nicht geändert werden. ![Abonnementdetails eingeben](/native-purchases/android/create-subscription/enter-subscription-details.webp) 3. **Basisplan erstellen** Google Play erfordert genau einen Basisplan pro Abonnement. Das native-purchases-Plugin unterstützt nur einen Basisplan, um die Kompatibilität mit iOS zu gewährleisten. Klicken Sie auf **Basisplan hinzufügen**, um fortzufahren. ![Basisplan erstellen](/native-purchases/android/create-subscription/create-base-plan.webp) 4. **Basisplan-Details konfigurieren** Eingeben: * **Basisplan-ID**: Eindeutige Kennung für diesen Plan * **Abrechnungszeitraum**: Wie oft Benutzer belastet werden (wöchentlich, monatlich, jährlich usw.) * **Nachfrist**: Zeitfenster, während dessen Google das Abonnement aufrechterhält, während die Zahlung vor der Kündigung wiederholt wird ![Basisplan konfigurieren](/native-purchases/android/create-subscription/configure-base-plan-details.webp) 5. **Preise einrichten** Greifen Sie auf den Preisbereich zu und wählen Sie alle Länder/Regionen aus, in denen Sie das Abonnement anbieten möchten. ![Regionen auswählen](/native-purchases/android/create-subscription/select-pricing-regions.webp) 6. **Preis konfigurieren** Legen Sie Ihren Basispreis in Ihrer Hauptwährung fest. Google Play konvertiert diesen automatisch in lokale Währungen. ![Preis festlegen](/native-purchases/android/create-subscription/set-base-price.webp) 7. **Regionale Preise überprüfen** Überprüfen Sie die automatisch konvertierten Preise für jedes Land. Sie können einzelne Preise bei Bedarf anpassen. ![Preise überprüfen](/native-purchases/android/create-subscription/review-regional-pricing.webp) 8. **Konfiguration speichern** Speichern Sie Ihre Preiskonfiguration. ![Preise speichern](/native-purchases/android/create-subscription/save-pricing-configuration.webp) 9. **Abonnement aktivieren** Klicken Sie auf die Schaltfläche **Aktivieren**, um Ihr Abonnementprodukt live und zum Kauf verfügbar zu machen. ![Abonnement aktivieren](/native-purchases/android/create-subscription/activate-subscription.webp) ## Wichtige Überlegungen [Section titled “Wichtige Überlegungen”](#wichtige-überlegungen) ### Basisplan-Einschränkung [Section titled “Basisplan-Einschränkung”](#basisplan-einschränkung) Das native-purchases-Plugin erfordert genau einen Basisplan pro Abonnement, um Konsistenz mit der iOS-Abonnementverwaltung zu gewährleisten. Mehrere Basispläne werden nicht unterstützt. ### Nachfrist [Section titled “Nachfrist”](#nachfrist) Die Nachfrist ermöglicht es Google Play, fehlgeschlagene Zahlungen zu wiederholen, während der Zugang des Benutzers zum Abonnement aufrechterhalten wird. Übliche Nachfristen sind: * 3 Tage für monatliche Abonnements * 7 Tage für längere Abonnements ### Abonnementstatus [Section titled “Abonnementstatus”](#abonnementstatus) Nach der Erstellung befindet sich Ihr Abonnement im Status “Entwurf”, bis es aktiviert wird. Sie können Entwurfs-Abonnements im Sandbox-Modus testen. ## Verwendung in Ihrer App [Section titled “Verwendung in Ihrer App”](#verwendung-in-ihrer-app) Nach der Erstellung verweisen Sie in Ihrer App auf das Abonnement mit der Produkt-ID: ```typescript import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases'; // Abonnementinformationen laden const { products } = await NativePurchases.getProducts({ productIdentifiers: ['com.example.premium.monthly'], productType: PURCHASE_TYPE.SUBS, }); const product = products[0]; console.log(`${product.title} — ${product.priceString}`); // Kaufen (planIdentifier = Basisplan-ID aus der Google Play Console) const transaction = await NativePurchases.purchaseProduct({ productIdentifier: 'com.example.premium.monthly', planIdentifier: 'monthly-plan', // ERFORDERLICH auf Android, wird auf iOS ignoriert productType: PURCHASE_TYPE.SUBS, }); console.log('Transaktions-ID', transaction.transactionId); // Später Kaufstatus überprüfen const { purchases } = await NativePurchases.getPurchases({ productType: PURCHASE_TYPE.SUBS, }); const subscription = purchases.find( (purchase) => purchase.productIdentifier === 'com.example.premium.monthly', ); if (subscription && subscription.purchaseState === 'PURCHASED' && subscription.isAcknowledged) { console.log('Abonnement lokal aktiv'); // Für Ablauf/Kündigung validieren Sie purchaseToken über Ihr Backend } ``` ## Nächste Schritte [Section titled “Nächste Schritte”](#nächste-schritte) * [Einführungsangebot erstellen](/docs/plugins/native-purchases/android-introductory-offer/) um neue Abonnenten zu gewinnen * [Sandbox-Tests konfigurieren](/docs/plugins/native-purchases/android-sandbox-testing/) um Ihre Abonnements zu testen * Backend-Beleg-Validierung für Sicherheit einrichten ## Fehlerbehebung [Section titled “Fehlerbehebung”](#fehlerbehebung) **Abonnement erscheint nicht in der App:** * Überprüfen Sie, ob die Produkt-ID genau übereinstimmt * Stellen Sie sicher, dass das Abonnement aktiviert ist * Überprüfen Sie, ob Ihre App den korrekten Paketnamen hat * Warten Sie 2-3 Stunden nach der Aktivierung, bis Änderungen übernommen werden **Basisplan-Fehler:** * Stellen Sie sicher, dass Sie genau einen Basisplan haben * Überprüfen Sie, ob alle erforderlichen Felder ausgefüllt sind * Prüfen Sie, ob der Abrechnungszeitraum gültig ist **Preisprobleme:** * Bestätigen Sie, dass mindestens ein Land ausgewählt ist * Überprüfen Sie, ob der Basispreis über dem zulässigen Minimum liegt * Prüfen Sie, ob die Währungsumrechnungskurse akzeptabel sind # Android-Abonnement-Einführungsangebot erstellen > Erfahren Sie, wie Sie Einführungsangebote für automatisch verlängernde Abonnements auf Android erstellen, um neue Abonnenten zu gewinnen. Einführungsangebote ermöglichen es Ihnen, berechtigten Benutzern entweder eine kostenlose Testversion oder einen ermäßigten Einführungspreis anzubieten. Nach Ablauf der Einführungsphase werden Abonnements automatisch zum Standardpreis verlängert, sofern sie nicht gekündigt werden. ## Überblick [Section titled “Überblick”](#überblick) Einführungsangebote sind ein mächtiges Werkzeug, um: * Eintrittsbarrieren für neue Abonnenten zu reduzieren * Konversionsraten zu erhöhen * Benutzern zu ermöglichen, Ihre Premium-Funktionen risikofrei zu testen * Langfristige Abonnentenbeziehungen aufzubauen ## Berechtigung [Section titled “Berechtigung”](#berechtigung) Benutzer können ein Einführungsangebot erhalten, wenn sie das Abonnement zuvor noch nicht gekauft oder ein Einführungsangebot dafür erhalten haben. Google Play verwaltet die Berechtigung automatisch. ## Voraussetzungen [Section titled “Voraussetzungen”](#voraussetzungen) Sie müssen zuerst [ein automatisch verlängerndes Abonnement erstellen](/docs/plugins/native-purchases/android-create-subscription/), bevor Sie ein Einführungsangebot hinzufügen. ## Ein Einführungsangebot erstellen [Section titled “Ein Einführungsangebot erstellen”](#ein-einführungsangebot-erstellen) 1. **Auf Angebotskonfiguration zugreifen** Navigieren Sie zu Ihrem Abonnement in der Google Play Console und wählen Sie die Schaltfläche **Angebot hinzufügen**. ![Schaltfläche Angebot hinzufügen](/native-purchases/android/introductory-offer/add-offer-button.webp) 2. **Basisplan auswählen** Ein Modal wird angezeigt, in dem Sie aufgefordert werden, Ihren Basisplan auszuwählen. Normalerweise haben Sie nur einen Basisplan. Klicken Sie auf **Angebot hinzufügen**, um fortzufahren. ![Basisplan auswählen](/native-purchases/android/introductory-offer/select-base-plan.webp) 3. **Angebotsdetails konfigurieren** Geben Sie folgende Informationen ein: **Angebots-ID**: Eine eindeutige Kennung für dieses Angebot **Berechtigung**: Wählen Sie, wer dieses Angebot erhalten kann * **Neue Kunden**: Nur Benutzer, die noch nie abonniert haben * **Bestehende Kunden**: Benutzer, die zuvor abonniert haben * **Vom Entwickler bestimmt**: Benutzerdefinierte Berechtigungslogik (wird von native-purchases nicht unterstützt) Caution Das native-purchases-Plugin unterstützt nicht die Option “Vom Entwickler bestimmt”. Verwenden Sie stattdessen “Neue Kunden” oder “Bestehende Kunden”. ![Angebot konfigurieren](/native-purchases/android/introductory-offer/configure-offer-details.webp) 4. **Phasen hinzufügen** Klicken Sie am Ende der Seite auf **Phase hinzufügen**, um Ihre Angebotsstruktur zu definieren. Sie können bis zu zwei Phasen hinzufügen, was Kombinationen wie die folgenden ermöglicht: * Nur kostenlose Testversion * Nur ermäßigter Preis * Kostenlose Testversion gefolgt von ermäßigter wiederkehrender Zahlung 5. **Phasentyp auswählen** Wählen Sie aus drei Phasentypen: **Kostenlose Testversion** * Kostenloser Zugang für eine festgelegte Dauer * Beispiel: 7 Tage kostenlos, dann 9,99 €/Monat **Einmalzahlung** * Einmalig ermäßigter Preis für einen bestimmten Zeitraum * Beispiel: 1,99 € für 2 Monate, dann 9,99 €/Monat **Ermäßigte wiederkehrende Zahlung** * Reduzierter Preis pro Abrechnungszyklus für mehrere Zyklen * Beispiel: 4,99 €/Monat für 3 Monate, dann 9,99 €/Monat 6. **Phasendauer konfigurieren** Legen Sie fest, wie lange die Einführungsphase dauert: * Tage, Wochen oder Monate * Anzahl der Abrechnungszyklen 7. **Fertigstellen und aktivieren** Klicken Sie auf **Anwenden** und dann auf **Speichern**, um das Angebot zu aktivieren. Die Schaltfläche **Aktivieren** wird verfügbar, sobald gespeichert wurde. ## Beispiele für Angebotsphasen [Section titled “Beispiele für Angebotsphasen”](#beispiele-für-angebotsphasen) ### Beispiel 1: Einfache kostenlose Testversion [Section titled “Beispiel 1: Einfache kostenlose Testversion”](#beispiel-1-einfache-kostenlose-testversion) * Phase 1: 7 Tage kostenlos * Dann: 9,99 €/Monat Standardpreis ### Beispiel 2: Ermäßigte Einführung [Section titled “Beispiel 2: Ermäßigte Einführung”](#beispiel-2-ermäßigte-einführung) * Phase 1: 1,99 € für den ersten Monat * Dann: 9,99 €/Monat Standardpreis ### Beispiel 3: Erweiterte Testversion + Rabatt [Section titled “Beispiel 3: Erweiterte Testversion + Rabatt”](#beispiel-3-erweiterte-testversion--rabatt) * Phase 1: 14 Tage kostenlos * Phase 2: 4,99 €/Monat für 2 Monate * Dann: 9,99 €/Monat Standardpreis ## Verwendung in Ihrer App [Section titled “Verwendung in Ihrer App”](#verwendung-in-ihrer-app) Das native-purchases-Plugin verwaltet automatisch die Berechtigung für Einführungsangebote und deren Darstellung: ```typescript import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases'; // Produkte abrufen (enthält Metadaten zum Einführungsangebot) const { products } = await NativePurchases.getProducts({ productIdentifiers: ['com.example.premium.monthly'], productType: PURCHASE_TYPE.SUBS, }); const product = products[0]; if (product.introductoryPrice) { console.log(`Einführungspreis: ${product.introductoryPriceString}`); console.log(`Regulärer Preis: ${product.priceString}`); console.log( `Angebotsdauer: ${product.introductoryPrice.subscriptionPeriod?.numberOfUnits} ${product.introductoryPrice.subscriptionPeriod?.unit}`, ); } else { console.log('Kein Einführungsangebot für dieses Produkt konfiguriert'); } // Kaufen (Google Play wendet Einführungspreis automatisch an, wenn der Benutzer berechtigt ist) const transaction = await NativePurchases.purchaseProduct({ productIdentifier: 'com.example.premium.monthly', planIdentifier: 'monthly-plan', // Basisplan-ID aus der Google Play Console productType: PURCHASE_TYPE.SUBS, }); console.log('Einführungsangebot-Transaktion', transaction.transactionId); ``` ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) ### Angebotsdauer [Section titled “Angebotsdauer”](#angebotsdauer) * **Kostenlose Testversionen**: 3-14 Tage sind optimal für die meisten Apps * **Ermäßigte Zeiträume**: 1-3 Monate funktionieren gut für den Gewohnheitsaufbau * **Preisrabatt**: 50-70% Rabatt auf den regulären Preis treibt Konversionen an ### Marketing [Section titled “Marketing”](#marketing) * Zeigen Sie das Einführungsangebot und den regulären Preis klar an * Zeigen Sie, was nach der Einführungsphase passiert * Machen Sie die Kündigung einfach und transparent * Erinnern Sie Benutzer, bevor die Einführungsphase endet ### A/B-Tests [Section titled “A/B-Tests”](#ab-tests) Testen Sie verschiedene Angebotsstrukturen: * Länge der kostenlosen Testversion * Rabattprozentsatz * Rabattdauer * Einzelphase vs. Mehrphasen ## Wichtige Hinweise [Section titled “Wichtige Hinweise”](#wichtige-hinweise) * Nur ein Einführungsangebot kann gleichzeitig pro Abonnement aktiv sein * Benutzer können ein Einführungsangebot nur einmal pro Abonnement in Anspruch nehmen * Einführungsangebote gelten nicht für Abonnement-Upgrades/-Downgrades * Änderungen an Einführungsangeboten wirken sich nicht auf bestehende Abonnenten aus ## Fehlerbehebung [Section titled “Fehlerbehebung”](#fehlerbehebung) **Einführungsangebot wird nicht angezeigt:** * Überprüfen Sie, ob das Angebot in der Play Console aktiviert ist * Prüfen Sie die Benutzerberechtigung (hat möglicherweise das Angebot bereits verwendet) * Stellen Sie sicher, dass die App die neuesten Produktinformationen verwendet **Falsche Benutzer erhalten das Angebot:** * Überprüfen Sie die Berechtigungseinstellungen (neue vs. bestehende Kunden) * Prüfen Sie, ob der Benutzer zuvor auf einem anderen Gerät abonniert hat * Überprüfen Sie den Play Store-Kontoverlauf **Angebot wird beim Kauf nicht angewendet:** * Bestätigen Sie, dass die Produkt-ID genau übereinstimmt * Prüfen Sie, ob das Angebot noch aktiv und nicht abgelaufen ist * Überprüfen Sie die Datumsbereichseinstellungen für das Angebot ## Nächste Schritte [Section titled “Nächste Schritte”](#nächste-schritte) * [Sandbox-Tests konfigurieren](/docs/plugins/native-purchases/android-sandbox-testing/) um Ihre Angebote zu testen * Konversionsraten in der Play Console-Analyse überwachen * Erwägen Sie, mehrere Abonnementstufen mit unterschiedlichen Angeboten zu erstellen # Android Play Store Review-Richtlinien für IAP > Vollständiger Leitfaden zum Bestehen der Google Play-Überprüfung mit In-App-Käufen und Abonnements, einschließlich Compliance-Anforderungen und Bewährte Methoden. Die Genehmigung Ihrer Android-App im Google Play erfordert die Einhaltung der Google-Richtlinien, insbesondere für Apps mit In-App-Käufen und Abonnements. Dieser Leitfaden behandelt alles, was Sie für eine erfolgreiche Überprüfung benötigen. ## Google Play Billing-Anforderungen [Section titled “Google Play Billing-Anforderungen”](#google-play-billing-anforderungen) ### Obligatorisches Abrechnungssystem [Section titled “Obligatorisches Abrechnungssystem”](#obligatorisches-abrechnungssystem) Für digitale Güter und Dienstleistungen **müssen** Sie das Abrechnungssystem von Google Play verwenden: **Digitale Güter (Play Billing erforderlich):** * Abonnements für Premium-Funktionen * In-App-Währung oder Credits * Digitale Inhalte (E-Books, Musik, Videos) * Spiel-Upgrades und Power-ups * App-Freischaltungen und Premium-Stufen **Physische Güter (Play Billing nicht erlaubt):** * Physische Waren * Reale Dienstleistungen * Einmalige Spenden an gemeinnützige Organisationen :::caution 2025-Anforderung Neue Apps müssen die `monetization.subscriptions`-APIs für die Verwaltung von Abonnementkatalogen verwenden. Legacy-Billing-APIs sind veraltet. ::: ### Implementierung mit Native Purchases [Section titled “Implementierung mit Native Purchases”](#implementierung-mit-native-purchases) ```typescript import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases'; // Sicherstellen, dass Billing auf dem Gerät verfügbar ist const { isBillingSupported } = await NativePurchases.isBillingSupported(); if (!isBillingSupported) throw new Error('Google Play Billing nicht verfügbar'); // Abonnementprodukte abrufen (Store-Daten erforderlich—niemals Preise fest codieren) const { products } = await NativePurchases.getProducts({ productIdentifiers: ['premium_monthly', 'premium_yearly'], productType: PURCHASE_TYPE.SUBS, }); // Plan-IDs sind die Basisplan-IDs, die Sie in der Google Play Console erstellen const transaction = await NativePurchases.purchaseProduct({ productIdentifier: 'premium_monthly', planIdentifier: 'monthly-plan', // ERFORDERLICH auf Android, wird auf iOS ignoriert productType: PURCHASE_TYPE.SUBS, }); console.log('Purchase-Token für Servervalidierung:', transaction.purchaseToken); ``` ## Transparenz- und Offenlegungsanforderungen [Section titled “Transparenz- und Offenlegungsanforderungen”](#transparenz--und-offenlegungsanforderungen) ### Vorabpreisoffenlegung [Section titled “Vorabpreisoffenlegung”](#vorabpreisoffenlegung) Google Play fordert eine klare Offenlegung aller Kosten vor dem Kauf: **Erforderliche Elemente:** * Genauer Preis in der lokalen Währung des Benutzers * Abrechnungshäufigkeit (monatlich, jährlich usw.) * Was im Abonnement enthalten ist * Gesamtkosten für Einführungsangebote * Wann Belastungen erfolgen ![UI-Design Bewährte Methoden](/native-purchases/review-guides/ui-design-dos-donts.webp) **Beispiel für konforme UI:** ```typescript function SubscriptionCard({ product }) { return (

{product.title}

{/* Einführungsangebot anzeigen, falls verfügbar */} {product.introductoryPrice && (

{product.introductoryPriceString}

für {product.introductoryPricePeriod}

)} {/* Regulärer Preis */}

{product.priceString}

pro {product.subscriptionPeriod}

{/* Klare Beschreibung */}

{product.description}

{/* Verlängerungsbedingungen */}

Wird automatisch verlängert. Jederzeit in Google Play kündbar.

); } ``` ### Offenlegung der automatischen Verlängerung [Section titled “Offenlegung der automatischen Verlängerung”](#offenlegung-der-automatischen-verlängerung) Vor einer automatischen Verlängerung des Abonnements verlangt Google: * Klare Benachrichtigung, dass eine Verlängerung erfolgt * Erinnerung an den Preis * Einfacher Zugang zur Kündigung Tip Das native-purchases-Plugin arbeitet mit Google Play zusammen, um automatische Verlängerungsbenachrichtigungen automatisch zu verwalten. Stellen Sie sicher, dass Ihre Abonnementprodukte in der Google Play Console richtig konfiguriert sind. ### Preiskonsistenz über Plattformen hinweg [Section titled “Preiskonsistenz über Plattformen hinweg”](#preiskonsistenz-über-plattformen-hinweg) **Wichtige Regel:** Die Preise müssen auf allen Plattformen, auf denen Ihre App verfügbar ist, konsistent sein. **Beispielverstoß:** * iOS: 9,99 €/Monat * Android: 7,99 €/Monat * Web: 11,99 €/Monat **Warum es wichtig ist:** Benutzer können Screenshots von Preisunterschieden machen und diese an Google melden, was zu Richtlinienverstößen führt. ## Datenschutzrichtlinien-Anforderungen [Section titled “Datenschutzrichtlinien-Anforderungen”](#datenschutzrichtlinien-anforderungen) ### Obligatorische Datenschutzrichtlinie [Section titled “Obligatorische Datenschutzrichtlinie”](#obligatorische-datenschutzrichtlinie) Wenn Ihre App In-App-Käufe enthält, müssen Sie: 1. **Link im Play Store-Eintrag** * Datenschutzrichtlinien-URL in der Play Console hinzufügen * Muss öffentlich zugänglich sein * Muss in derselben Sprache wie Ihre App sein 2. **Link in der App** * Datenschutzrichtlinie in App-Einstellungen anzeigen * Vor dem Sammeln von Benutzerdaten anzeigen * Leicht auffindbar machen **Beispielimplementierung:** ```typescript function SettingsScreen() { const openPrivacyPolicy = () => { window.open('https://yourapp.com/privacy', '_blank'); }; const openTerms = () => { window.open('https://yourapp.com/terms', '_blank'); }; return (

Einstellungen

); } ``` ### Datensicherheitsbereich [Section titled “Datensicherheitsbereich”](#datensicherheitsbereich) Google Play erfordert detaillierte Offenlegung im Datensicherheitsbereich: **Für IAP-Apps deklarieren:** * Erhebung der Kaufhistorie * E-Mail-Adressen (für Belege) * Geräte-IDs (zur Betrugsprävention) * Zahlungsinformationsverarbeitung * Erhebung von Analysedaten Der Datensicherheitsbereich ist rechtlich verbindlich. Ungenaue Erklärungen können zur Entfernung der App führen. ## Häufige Ablehnungsgründe [Section titled “Häufige Ablehnungsgründe”](#häufige-ablehnungsgründe) ### 1. Fehlende oder falsche Billing-Implementierung [Section titled “1. Fehlende oder falsche Billing-Implementierung”](#1-fehlende-oder-falsche-billing-implementierung) **Warum es fehlschlägt:** * Nicht Google Play Billing für digitale Güter verwenden * Veraltete Billing-APIs verwenden * Benutzerdefinierte Zahlungslösungen für Abonnements implementieren **Prävention:** ```typescript // ✅ Korrekt: Native-purchases verwenden (verwendet Google Play Billing) await NativePurchases.purchaseProduct({ productIdentifier: 'premium_monthly' }); // ❌ Falsch: Benutzerdefinierter Zahlungsanbieter für Abonnements // await CustomPayment.charge(user, 9.99); ``` ### 2. Unklare Preise oder versteckte Kosten [Section titled “2. Unklare Preise oder versteckte Kosten”](#2-unklare-preise-oder-versteckte-kosten) **Warum es fehlschlägt:** * Preis wird erst nach Klick auf Kaufen angezeigt * Zusätzliche Gebühren nicht im Voraus offengelegt * Vage Abonnementbedingungen **Prävention:** ```typescript function PurchaseScreen({ product }) { return (
{/* ALLE Kosten im Voraus anzeigen */}

Premium-Abonnement

{product.priceString}/Monat

Steuern können je nach Standort anfallen

Enthält:

  • Werbefreies Erlebnis
  • Unbegrenzter Cloud-Speicher
  • Prioritätssupport

Das Abonnement wird automatisch verlängert, sofern nicht mindestens 24 Stunden vor Ende der aktuellen Periode gekündigt wird.

Verwalten oder kündigen Sie in Google Play-Abonnements.

); } ``` ### 3. Täuschende Abonnementmuster [Section titled “3. Täuschende Abonnementmuster”](#3-täuschende-abonnementmuster) **Warum es fehlschlägt:** * Vorauswahl von Premium-Optionen * Verstecken günstigerer Alternativen * Kündigung schwierig machen * Falsche Dringlichkeit (“Nur noch 3 Plätze frei!”) ![Beschreibungs-Bewährte Methoden](/native-purchases/review-guides/description-guidelines-1.webp) ![Marketing-Richtlinien](/native-purchases/review-guides/description-guidelines-2.webp) **Prävention:** * Alle Abonnementstufen gleichwertig anzeigen * Kündigung klar und zugänglich machen * Countdown-Timer oder falsche Knappheit vermeiden * Keine Dark Patterns verwenden, um teure Optionen zu pushen ### 4. Unvollständige Tests [Section titled “4. Unvollständige Tests”](#4-unvollständige-tests) **Warum es fehlschlägt:** * App stürzt beim Kaufen ab * Produkte werden nicht geladen * Kaufbestätigung wird nicht angezeigt * Premium-Funktionen werden nach Kauf nicht freigeschaltet **Prävention:** ```typescript import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases'; // Umfassende Tests vor Einreichung async function testPurchaseFlow() { try { // 1. Produktladen testen const { products } = await NativePurchases.getProducts({ productIdentifiers: ['premium_monthly', 'premium_yearly'], productType: PURCHASE_TYPE.SUBS, }); console.log('✓ Produkte geladen:', products.length); // 2. Kaufablauf testen const transaction = await NativePurchases.purchaseProduct({ productIdentifier: 'premium_monthly', planIdentifier: 'monthly-plan', productType: PURCHASE_TYPE.SUBS, }); console.log('✓ Kauf abgeschlossen', transaction.transactionId); // 3. Berechtigungen überprüfen const { purchases } = await NativePurchases.getPurchases({ productType: PURCHASE_TYPE.SUBS, }); if ( purchases.some( (purchase) => purchase.productIdentifier === 'premium_monthly' && ['PURCHASED', '1'].includes(purchase.purchaseState ?? '') && purchase.isAcknowledged, ) ) { console.log('✓ Premium-Funktionen freigeschaltet'); } // 4. Wiederherstellung testen await NativePurchases.restorePurchases(); console.log('✓ Wiederherstellung funktioniert'); } catch (error) { console.error('✗ Test fehlgeschlagen:', error); } } ``` ### 5. Datenschutzrichtlinienverstöße [Section titled “5. Datenschutzrichtlinienverstöße”](#5-datenschutzrichtlinienverstöße) **Warum es fehlschlägt:** * Kein Datenschutzrichtlinien-Link in der App * Datenschutzrichtlinie nicht zugänglich * Datenerhebung nicht offengelegt * Datensicherheitsbereich ungenau **Prävention:** * Datenschutzrichtlinie zum Play Store-Eintrag hinzufügen * Link in App-Einstellungen einbeziehen * Datensicherheitsbereich genau ausfüllen * Richtlinie aktualisieren, wenn neue Datenerhebung hinzugefügt wird ## Alternative Abrechnungssysteme (2025-Update) [Section titled “Alternative Abrechnungssysteme (2025-Update)”](#alternative-abrechnungssysteme-2025-update) ### Regionale Compliance [Section titled “Regionale Compliance”](#regionale-compliance) Google erlaubt jetzt alternative Abrechnungssysteme in bestimmten Regionen: **Berechtigte Regionen:** * Europäischer Wirtschaftsraum (EWR) * Südkorea * Indien (demnächst verfügbar) **Anforderungen bei Verwendung alternativer Abrechnung:** * Google Play Billing muss weiterhin als Option angeboten werden * Klare Kommunikation mit Benutzern über die Wahl * Einhaltung lokaler Vorschriften * Servicegebühr fällt weiterhin an (reduziert) Note Für die meisten Entwickler ist es einfacher, nur bei Google Play Billing zu bleiben. Das native-purchases-Plugin handhabt dies automatisch. ## Abonnementverwaltung [Section titled “Abonnementverwaltung”](#abonnementverwaltung) ### Einfache Kündigung [Section titled “Einfache Kündigung”](#einfache-kündigung) Benutzer müssen in der Lage sein: * Aktive Abonnements einfach anzuzeigen * Ohne Kontaktaufnahme mit dem Support zu kündigen * Zu verstehen, wann die Kündigung wirksam wird **Implementierung:** ```typescript import { NativePurchases } from '@capgo/native-purchases'; function ManageSubscriptionButton() { const openManagement = async () => { try { // Öffnet Google Play-Abonnementverwaltung await NativePurchases.showManageSubscriptions(); } catch (error) { // Fallback zu direkter URL const playStoreUrl = 'https://play.google.com/store/account/subscriptions'; window.open(playStoreUrl, '_blank'); } }; return ( ); } ``` ### Kündigungsfrist [Section titled “Kündigungsfrist”](#kündigungsfrist) **Erforderliche Offenlegung:** * Wann wird die Kündigung wirksam? * Behalten Benutzer Zugang bis zum Periodenende? * Sind teilweise Rückerstattungen verfügbar? ```typescript function CancellationInfo() { return (

Kündigungsrichtlinie

  • Jederzeit in Google Play kündbar
  • Zugang bleibt bis Ende der Abrechnungsperiode bestehen
  • Keine Rückerstattungen für Teilperioden
  • Jederzeit neu abonnieren, um Zugang wiederzuerlangen
); } ``` ## Checkliste vor Einreichung [Section titled “Checkliste vor Einreichung”](#checkliste-vor-einreichung) ![Checkliste vor Einreichung](/native-purchases/review-guides/pre-submission-checklist.webp) 1. **Billing-Implementierung überprüfen** * Google Play Billing verwenden (über native-purchases) * Alle Abonnementprodukte in Play Console erstellt * Produkte sind aktiviert und veröffentlicht * Preise für alle Zielländer festgelegt 2. **Kaufabläufe testen** * Lizenztestkonto erstellen * Jede Abonnementstufe testen * Überprüfen, ob Produkte korrekt geladen werden * Kaufabschluss testen * Überprüfen, ob Premium-Funktionen freigeschaltet werden * Abonnement-Wiederherstellung testen * Auf mehreren Geräten testen 3. **Alle Texte überprüfen** * Preise vor Kauf klar angezeigt * Alle Gebühren im Voraus offengelegt * Abonnementbedingungen sind klar * Kündigungsprozess erklärt * Keine irreführenden Behauptungen 4. **Datenschutz-Compliance** * Datenschutzrichtlinie in Play Console verlinkt * Datenschutzrichtlinie in App zugänglich * Datensicherheitsbereich genau ausgefüllt * Berechtigungen gerechtfertigt und dokumentiert 5. **Inhaltsbewertung** * Fragebogen zur Inhaltsbewertung ausfüllen * Sicherstellen, dass Bewertung dem tatsächlichen Inhalt entspricht * In-App-Käufe im Fragebogen deklarieren 6. **Store-Eintrag vorbereiten** * App-Beschreibung genau * Screenshots zeigen aktuelle Version * Feature-Grafik erfüllt Anforderungen * Alle erforderlichen Assets hochgeladen ## Überprüfungszeitplan [Section titled “Überprüfungszeitplan”](#überprüfungszeitplan) **Erstüberprüfung:** Durchschnittlich 7 Tage (kann schneller sein) **Updates:** Normalerweise schneller als erste Einreichung **Richtlinienverstöße:** Sofortige Suspendierung möglich **Einsprüche:** 7-14 Tage für Überprüfung :::tip Laufende Überprüfungen Anders als bei Apple überprüft Google Apps kontinuierlich. Ihre App kann jederzeit während der Überprüfungsphase live gehen, nicht zu einer festen Zeit. ::: ## Tests vor Einreichung [Section titled “Tests vor Einreichung”](#tests-vor-einreichung) ### Lizenztests [Section titled “Lizenztests”](#lizenztests) 1. **Testkonto hinzufügen:** * Zur Play Console gehen * Einrichtung > Lizenztests * Gmail-Konto zum Testen hinzufügen 2. **In Sandbox testen:** ```typescript import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases'; // Käufe mit Lizenztestkonto testen async function testInSandbox() { const { isBillingSupported } = await NativePurchases.isBillingSupported(); if (!isBillingSupported) { console.error('Billing in dieser Umgebung nicht unterstützt'); return; } // Produkte abrufen (gibt Testpreise zurück, wenn ein Lizenztester verwendet wird) const { products } = await NativePurchases.getProducts({ productIdentifiers: ['premium_monthly'], productType: PURCHASE_TYPE.SUBS, }); console.log('Testprodukte:', products); // Testkauf tätigen (keine Belastung) const transaction = await NativePurchases.purchaseProduct({ productIdentifier: 'premium_monthly', planIdentifier: 'monthly-plan', productType: PURCHASE_TYPE.SUBS, }); console.log('Testkauf abgeschlossen:', transaction.transactionId); } ``` 3. **Test-Banner überprüfen:** * Beim Kauf mit Testkonto * Sollte “Testkauf”-Benachrichtigung sehen * Keine echten Belastungen erfolgen ### Interner Test-Track [Section titled “Interner Test-Track”](#interner-test-track) Vor Produktionsfreigabe: 1. Internen Test-Track in Play Console erstellen 2. APK/AAB hochladen 3. Tester-E-Mail-Adressen hinzufügen 4. Tester laden aus Play Store herunter (Test-Track) 5. Überprüfen, ob Kaufabläufe durchgängig funktionieren ## Bewährte Methoden für Native Purchases [Section titled “Bewährte Methoden für Native Purchases”](#bewährte-methoden-für-native-purchases) ### Alle Kaufzustände behandeln [Section titled “Alle Kaufzustände behandeln”](#alle-kaufzustände-behandeln) ```typescript import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases'; async function handlePurchase(productId: string, planIdentifier?: string) { try { setLoading(true); const transaction = await NativePurchases.purchaseProduct({ productIdentifier: productId, planIdentifier, productType: planIdentifier ? PURCHASE_TYPE.SUBS : PURCHASE_TYPE.INAPP, }); console.log('Purchase-Token:', transaction.purchaseToken ?? transaction.receipt); // Erfolg - Berechtigungen aus Store prüfen const { purchases } = await NativePurchases.getPurchases({ productType: planIdentifier ? PURCHASE_TYPE.SUBS : PURCHASE_TYPE.INAPP, }); const isOwned = purchases.some( (purchase) => purchase.productIdentifier === productId && (purchase.purchaseState === 'PURCHASED' || purchase.purchaseState === '1') && purchase.isAcknowledged, ); if (isOwned) { unlockPremiumFeatures(); showSuccess('Premium aktiviert!'); } } catch (error: any) { // Spezifische Fehlerfälle behandeln switch (error.code) { case 'USER_CANCELLED': // Benutzer hat abgebrochen - kein Fehler erforderlich console.log('Kauf abgebrochen'); break; case 'ITEM_ALREADY_OWNED': // Sie besitzen es bereits - stattdessen wiederherstellen showInfo('Sie besitzen dies bereits! Wird wiederhergestellt...'); await NativePurchases.restorePurchases(); break; case 'ITEM_UNAVAILABLE': showError('Dieses Abonnement ist derzeit nicht verfügbar. Bitte versuchen Sie es später erneut.'); break; case 'NETWORK_ERROR': showError('Netzwerkfehler. Bitte überprüfen Sie Ihre Verbindung und versuchen Sie es erneut.'); break; default: showError('Kauf fehlgeschlagen. Bitte versuchen Sie es erneut.'); console.error('Kauffehler:', error); } } finally { setLoading(false); } } ``` ### Käufe wiederherstellen implementieren [Section titled “Käufe wiederherstellen implementieren”](#käufe-wiederherstellen-implementieren) ```typescript import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases'; function RestorePurchasesButton() { const [loading, setLoading] = useState(false); const handleRestore = async () => { setLoading(true); try { await NativePurchases.restorePurchases(); const { purchases } = await NativePurchases.getPurchases({ productType: PURCHASE_TYPE.SUBS, }); const hasSubscription = purchases.some( (purchase) => purchase.productType === 'subs' && purchase.isAcknowledged, ); if (hasSubscription) { unlockPremiumFeatures(); showSuccess('Abonnements wiederhergestellt!'); return; } // Einmalige Freischaltungen prüfen, falls erforderlich const { purchases: iaps } = await NativePurchases.getPurchases({ productType: PURCHASE_TYPE.INAPP, }); const hasInApp = iaps.some((purchase) => purchase.productIdentifier === 'premium_unlock'); if (hasInApp) { unlockPremiumFeatures(); showSuccess('Frühere Käufe wiederhergestellt!'); return; } showInfo('Keine früheren Käufe gefunden.'); } catch (error) { showError('Wiederherstellen der Käufe fehlgeschlagen. Bitte versuchen Sie es erneut.'); } finally { setLoading(false); } }; return ( ); } ``` ### Abonnementstatus überprüfen [Section titled “Abonnementstatus überprüfen”](#abonnementstatus-überprüfen) ```typescript import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases'; async function checkSubscriptionStatus() { try { const { purchases } = await NativePurchases.getPurchases({ productType: PURCHASE_TYPE.SUBS, }); const subscription = purchases.find( (purchase) => purchase.productIdentifier === 'premium_monthly' && (purchase.purchaseState === 'PURCHASED' || purchase.purchaseState === '1') && purchase.isAcknowledged, ); if (!subscription) { showPaywall(); return; } console.log('Abonnement aktiv:', { productId: subscription.productIdentifier, expiresAt: subscription.expirationDate, willRenew: subscription.willCancel === false, purchaseToken: subscription.purchaseToken, }); unlockPremiumFeatures(); } catch (error) { console.error('Fehler beim Überprüfen des Abonnements:', error); } } ``` ## Wenn Ihre App abgelehnt wird [Section titled “Wenn Ihre App abgelehnt wird”](#wenn-ihre-app-abgelehnt-wird) ### Häufige Richtlinienverstöße [Section titled “Häufige Richtlinienverstöße”](#häufige-richtlinienverstöße) **Zahlungsrichtlinie:** * Nicht Google Play Billing verwenden * Irreführende Abonnementbedingungen * Versteckte Kosten **Benutzerdaten-Richtlinie:** * Fehlende Datenschutzrichtlinie * Ungenaue Datensicherheitserklärungen * Übermäßige Berechtigungen ### Lösungsschritte [Section titled “Lösungsschritte”](#lösungsschritte) 1. **Verstoßmitteilung überprüfen** * Lesen Sie die spezifische zitierte Richtlinie * Verstehen Sie, was Google markiert hat * Prüfen Sie Beispiele, die sie bereitgestellt haben 2. **Problem beheben** * Grundursache beheben, nicht nur Symptome * Nach der Behebung gründlich testen * Alle vorgenommenen Änderungen dokumentieren 3. **Einspruch einreichen (falls zutreffend)** ![Klärung und Einspruchsprozess](/native-purchases/review-guides/clarification-process.webp) ```plaintext Betreff: Einspruch gegen Richtlinienverstoß - [App-Name] Sehr geehrtes Google Play Review-Team, Ich habe eine Benachrichtigung erhalten, dass meine App gegen [Richtlinie X.Y] verstößt. Ich habe die folgenden Änderungen vorgenommen, um die Anforderungen zu erfüllen: 1. [Spezifische vorgenommene Änderung] 2. [Spezifische vorgenommene Änderung] 3. [Spezifische vorgenommene Änderung] Die aktualisierte Version [Versionsnummer] behebt alle angesprochenen Bedenken. Testkonto zur Überprüfung: E-Mail: test@example.com Passwort: TestPass123 Vielen Dank für Ihre Berücksichtigung. ``` ![Anforderung von Dokumentationsbeispiel](/native-purchases/review-guides/request-documents.webp) 4. **Erneut einreichen oder aktualisieren** * Behobene Version hochladen * Zur Überprüfung erneut einreichen * Status in Play Console überwachen ## Zusätzliche Ressourcen [Section titled “Zusätzliche Ressourcen”](#zusätzliche-ressourcen) * [Google Play-Entwicklerrichtlinien-Center](https://play.google.com/about/developer-content-policy/) * [Google Play Billing-Dokumentation](https://developer.android.com/google/play/billing) * [Abonnement-Bewährte Methoden](https://developer.android.com/google/play/billing/subscriptions) * [Play Console-Hilfe](https://support.google.com/googleplay/android-developer/) ## Benötigen Sie Expertenrat? [Section titled “Benötigen Sie Expertenrat?”](#benötigen-sie-expertenrat) Die Navigation durch die Play Store-Überprüfung kann komplex sein, insbesondere mit den neuen 2025-Testanforderungen. Wenn Sie persönliche Unterstützung benötigen: **[Buchen Sie ein Beratungsgespräch mit unserem Team](https://cal.com/team/capgo/capacitor-consulting-services)** für Hilfe bei: * Vollständiger Play Store-Überprüfungsvorbereitung * Einrichtung von Test-Tracks und Tester-Rekrutierung * IAP-Implementierungsüberprüfung * Datensicherheit und Datenschutz-Compliance * Ablehnungs-Fehlerbehebung und Einsprüche * Vollständiger App-Einreichungsprozess Unsere Experten haben Hunderte von Apps durch erfolgreiche Play Store-Einreichungen geführt und können Ihnen helfen, die 2025-Anforderungen zu bewältigen. ## Support [Section titled “Support”](#support) Benötigen Sie Hilfe bei der Implementierung? * Überprüfen Sie die [Native Purchases-Dokumentation](/docs/plugins/native-purchases/getting-started/) * Schauen Sie sich den [Android-Sandbox-Test-Leitfaden](/docs/plugins/native-purchases/android-sandbox-testing/) an * Besuchen Sie den [Google Play-Entwickler-Support](https://support.google.com/googleplay/android-developer/) # Android-Sandbox-Tests konfigurieren > Erfahren Sie, wie Sie Sandbox-Tests für In-App-Käufe auf Android mit der Google Play Console einrichten. Das Testen von In-App-Käufen erfordert eine ordnungsgemäße Konfiguration in der Google Play Console. Dieser Leitfaden führt Sie durch die Einrichtung von Sandbox-Tests für Ihre Android-App. ## Voraussetzungen [Section titled “Voraussetzungen”](#voraussetzungen) Sie benötigen ein aktives Google Play Console-Konto mit einem aktuellen Abonnement (jährlich erneuert). ## Einrichtungsprozess [Section titled “Einrichtungsprozess”](#einrichtungsprozess) 1. **Testkonto hinzufügen** Navigieren Sie zu Ihrer Google Play Console, wählen Sie Ihre App aus und gehen Sie zu **Einrichtung > Lizenztests**. Fügen Sie das primäre Google-Konto hinzu, das mit dem Android-Gerät verbunden ist, das Sie zum Testen verwenden. ![Testkonto hinzufügen](/native-purchases/android/sandbox-testing/add-testing-account.webp) 2. **Test-Track erstellen** Gehen Sie zu **Tests > Geschlossene Tests** oder **Tests > Interne Tests** und erstellen Sie einen neuen Track. Sie müssen mindestens eine signierte Version Ihrer App in diesen Track hochladen. ![Test-Track erstellen](/native-purchases/android/sandbox-testing/create-testing-track.webp) 3. **Tester-Liste erstellen** Nach dem Einrichten des Test-Tracks erstellen Sie eine E-Mail-Liste und fügen die E-Mail-Adresse des Testgeräts hinzu. ![Tester-Liste erstellen](/native-purchases/android/sandbox-testing/create-tester-list.webp) 4. **Am Testprogramm teilnehmen** Öffnen Sie die Opt-in-URL von Ihrem Testgerät aus und klicken Sie auf die Schaltfläche **“Tester werden”**, um sich anzumelden. ![Am Test teilnehmen](/native-purchases/android/sandbox-testing/join-testing-program.webp) 5. **Signiertes APK hochladen** Generieren Sie ein signiertes APK oder verwenden Sie Android App Bundle, um Ihre App in den Test-Track hochzuladen. Ein Rollout ist nicht erforderlich - laden Sie einfach hoch und warten Sie auf die Genehmigung. ![APK hochladen](/native-purchases/android/sandbox-testing/upload-signed-apk.webp) 6. **Erstellen und testen** Führen Sie Ihre App auf dem Testgerät aus. Beim Versuch eines Kaufs sollten Sie eine Meldung sehen: > “Dies ist eine Testbestellung; es wird Ihnen nichts berechnet.” ![Testkauf](/native-purchases/android/sandbox-testing/test-purchase-confirmation.webp) ## Wichtige Hinweise [Section titled “Wichtige Hinweise”](#wichtige-hinweise) * Testkonten werden für Käufe nicht belastet * Testkäufe verwenden denselben Ablauf wie Produktionskäufe * Sie können alle Abonnementfunktionen testen, einschließlich Testversionen und Einführungsangeboten * Testabonnements haben beschleunigte Verlängerungsperioden für schnellere Tests ## Fehlerbehebung [Section titled “Fehlerbehebung”](#fehlerbehebung) **Produkte werden im Testmodus nicht angezeigt:** * Stellen Sie sicher, dass Ihre App in einen Test-Track hochgeladen wurde * Überprüfen Sie, ob das Testkonto zu Lizenztests hinzugefügt wurde * Prüfen Sie, ob Produkte in der Google Play Console aktiv sind **Fehler “Artikel nicht verfügbar”:** * Warten Sie 2-3 Stunden nach dem Erstellen von Produkten, bis sie verfügbar werden * Stellen Sie sicher, dass der Paketname Ihrer App mit dem in der Play Console übereinstimmt * Überprüfen Sie, ob Sie mit einem Testkonto angemeldet sind **Testkäufe werden als echte Gebühren angezeigt:** * Überprüfen Sie noch einmal, ob das Konto zu Lizenztests hinzugefügt wurde * Stellen Sie sicher, dass Sie den Build aus dem Test-Track verwenden * Überprüfen Sie, ob das Test-Banner während des Kaufs erscheint ## Zusätzliche Ressourcen [Section titled “Zusätzliche Ressourcen”](#zusätzliche-ressourcen) Weitere Details finden Sie in der [offiziellen Google Play-Dokumentation](https://developer.android.com/google/play/billing/test) zum Testen von In-App-Käufen. # Erste Schritte > Erfahren Sie, wie Sie das @capgo/native-purchases-Plugin installieren und verwenden, um Einmalkäufe und Abonnements mit StoreKit 2 und Google Play Billing 7 zu implementieren. 1. **Installieren Sie das Paket** * npm ```sh npm i @capgo/native-purchases ``` * pnpm ```sh pnpm add @capgo/native-purchases ``` * yarn ```sh yarn add @capgo/native-purchases ``` * bun ```sh bun add @capgo/native-purchases ``` 2. **Synchronisieren Sie mit nativen Projekten** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` 3. **Billing-Unterstützung überprüfen** ```typescript import { NativePurchases } from '@capgo/native-purchases'; const { isBillingSupported } = await NativePurchases.isBillingSupported(); if (!isBillingSupported) { throw new Error('Billing ist auf diesem Gerät nicht verfügbar'); } ``` 4. **Produkte direkt aus den Stores laden** ```typescript import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases'; const { products } = await NativePurchases.getProducts({ productIdentifiers: [ 'com.example.premium.monthly', 'com.example.premium.yearly', 'com.example.one_time_unlock' ], productType: PURCHASE_TYPE.SUBS, // Verwenden Sie PURCHASE_TYPE.INAPP für Einmalprodukte }); products.forEach((product) => { console.log(product.title, product.priceString); }); ``` 5. **Kauf- und Wiederherstellungsabläufe implementieren** ```typescript import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases'; const monthlyPlanId = 'monthly-plan'; // Basisplan-ID aus der Google Play Console const transaction = await NativePurchases.purchaseProduct({ productIdentifier: 'com.example.premium.monthly', planIdentifier: monthlyPlanId, // ERFORDERLICH für Android-Abonnements, wird auf iOS ignoriert productType: PURCHASE_TYPE.SUBS, quantity: 1, }); console.log('Transaktions-ID', transaction.transactionId); await NativePurchases.restorePurchases(); ``` * iOS * Erstellen Sie In-App-Produkte und Abonnements in App Store Connect. * Verwenden Sie StoreKit Local Testing oder Sandbox-Tester für QA. * Keine Manifest-Bearbeitungen erforderlich. Stellen Sie sicher, dass Ihre Produkte genehmigt sind. * Android * Erstellen Sie In-App-Produkte und Abonnements in der Google Play Console. * Laden Sie mindestens einen internen Test-Build hoch und fügen Sie Lizenztester hinzu. * Fügen Sie die Billing-Berechtigung zu `AndroidManifest.xml` hinzu: ```xml ``` ## Beispiel für Purchase Service [Section titled “Beispiel für Purchase Service”](#beispiel-für-purchase-service) ```typescript import { NativePurchases, PURCHASE_TYPE, Transaction } from '@capgo/native-purchases'; import { Capacitor } from '@capacitor/core'; class PurchaseService { private premiumProduct = 'com.example.premium.unlock'; private monthlySubId = 'com.example.premium.monthly'; private monthlyPlanId = 'monthly-plan'; // Basisplan-ID (nur Android) async initialize() { const { isBillingSupported } = await NativePurchases.isBillingSupported(); if (!isBillingSupported) throw new Error('Billing nicht verfügbar'); const { products } = await NativePurchases.getProducts({ productIdentifiers: [this.premiumProduct, this.monthlySubId], productType: PURCHASE_TYPE.SUBS, }); console.log('Produkte geladen', products); if (Capacitor.getPlatform() === 'ios') { NativePurchases.addListener('transactionUpdated', (transaction) => { this.handleTransaction(transaction); }); } } async buyPremium(appAccountToken?: string) { const transaction = await NativePurchases.purchaseProduct({ productIdentifier: this.premiumProduct, productType: PURCHASE_TYPE.INAPP, appAccountToken, }); await this.processTransaction(transaction); } async buyMonthly(appAccountToken?: string) { const transaction = await NativePurchases.purchaseProduct({ productIdentifier: this.monthlySubId, planIdentifier: this.monthlyPlanId, // ERFORDERLICH für Android-Abonnements productType: PURCHASE_TYPE.SUBS, appAccountToken, }); await this.processTransaction(transaction); } async restore() { await NativePurchases.restorePurchases(); await this.refreshEntitlements(); } async openManageSubscriptions() { await NativePurchases.manageSubscriptions(); } private async processTransaction(transaction: Transaction) { this.unlockContent(transaction.productIdentifier); this.validateOnServer(transaction).catch(console.error); } private unlockContent(productIdentifier: string) { // Berechtigung lokal speichern console.log('Freigeschaltet', productIdentifier); } private async refreshEntitlements() { const { purchases } = await NativePurchases.getPurchases({ productType: PURCHASE_TYPE.SUBS, }); console.log('Aktuelle Käufe', purchases); } private async handleTransaction(transaction: Transaction) { console.log('StoreKit-Transaktionsaktualisierung:', transaction); await this.processTransaction(transaction); } private async validateOnServer(transaction: Transaction) { await fetch('/api/validate-purchase', { method: 'POST', body: JSON.stringify({ transactionId: transaction.transactionId, receipt: transaction.receipt, purchaseToken: transaction.purchaseToken, }), }); } } ``` ## Erforderliche Kaufoptionen [Section titled “Erforderliche Kaufoptionen”](#erforderliche-kaufoptionen) | Option | Plattform | Beschreibung | | ------------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | | `productIdentifier` | iOS + Android | SKU/Produkt-ID, die in App Store Connect / Google Play Console konfiguriert ist. | | `productType` | Nur Android | `PURCHASE_TYPE.INAPP` oder `PURCHASE_TYPE.SUBS`. Standardmäßig `INAPP`. Immer auf `SUBS` für Abonnements setzen. | | `planIdentifier` | Android-Abonnements | Basisplan-ID aus der Google Play Console. Erforderlich für Abonnements, wird auf iOS und In-App-Käufen ignoriert. | | `quantity` | iOS | Nur für In-App-Käufe, standardmäßig `1`. Android kauft immer ein Element. | | `appAccountToken` | iOS + Android | UUID/String, der den Kauf mit Ihrem Benutzer verknüpft. Muss auf iOS UUID sein; Android akzeptiert jeden verschleierten String bis zu 64 Zeichen. | | `isConsumable` | Android | Auf `true` setzen, um Token nach Gewährung der Berechtigung für Verbrauchsartikel automatisch zu konsumieren. Standard ist `false`. | ## Berechtigungsstatus prüfen [Section titled “Berechtigungsstatus prüfen”](#berechtigungsstatus-prüfen) Verwenden Sie `getPurchases()` für eine plattformübergreifende Ansicht jeder Transaktion, die die Stores melden: ```typescript import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases'; const { purchases } = await NativePurchases.getPurchases({ productType: PURCHASE_TYPE.SUBS, }); purchases.forEach((purchase) => { if (purchase.isActive && purchase.expirationDate) { console.log('iOS-Abo aktiv bis', purchase.expirationDate); } const isAndroidIapValid = ['PURCHASED', '1'].includes(purchase.purchaseState ?? '') && purchase.isAcknowledged; if (isAndroidIapValid) { console.log('In-App-Berechtigung gewähren für', purchase.productIdentifier); } }); ``` ### Plattformverhalten [Section titled “Plattformverhalten”](#plattformverhalten) * **iOS**: Abonnements enthalten `isActive`, `expirationDate`, `willCancel` und StoreKit 2 Listener-Unterstützung. In-App-Käufe erfordern serverseitige Belegvalidierung. * **Android**: `isActive`/`expirationDate` werden nicht ausgefüllt; rufen Sie die Google Play Developer API mit dem `purchaseToken` für den maßgeblichen Status auf. `purchaseState` muss `PURCHASED` und `isAcknowledged` muss `true` sein. ## API-Schnellreferenz [Section titled “API-Schnellreferenz”](#api-schnellreferenz) * `isBillingSupported()` – auf StoreKit / Google Play-Verfügbarkeit prüfen. * `getProduct()` / `getProducts()` – Preis, lokalisierter Titel, Beschreibung, Einführungsangebote abrufen. * `purchaseProduct()` – StoreKit 2 oder Billing Client-Kaufablauf initiieren. * `restorePurchases()` – historische Käufe wiedergeben und mit aktuellem Gerät synchronisieren. * `getPurchases()` – alle iOS-Transaktionen oder Play Billing-Käufe auflisten. * `manageSubscriptions()` – native Abonnementverwaltungs-UI öffnen. * `addListener('transactionUpdated')` – ausstehende StoreKit 2-Transaktionen beim App-Start behandeln (nur iOS). ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) 1. **Store-Preise anzeigen** – Apple erfordert die Anzeige von `product.title` und `product.priceString`; niemals fest codieren. 2. **`appAccountToken` verwenden** – deterministisch eine UUID (v5) aus Ihrer Benutzer-ID generieren, um Käufe mit Konten zu verknüpfen. 3. **Serverseitig validieren** – `receipt` (iOS) / `purchaseToken` (Android) an Ihr Backend zur Überprüfung senden. 4. **Fehler elegant behandeln** – auf Benutzerabbrüche, Netzwerkfehler und nicht unterstützte Billing-Umgebungen prüfen. 5. **Gründlich testen** – folgen Sie dem [iOS-Sandbox-Leitfaden](/docs/plugins/native-purchases/ios-sandbox-testing/) und [Android-Sandbox-Leitfaden](/docs/plugins/native-purchases/android-sandbox-testing/). 6. **Wiederherstellen & Verwalten anbieten** – UI-Schaltflächen hinzufügen, die mit `restorePurchases()` und `manageSubscriptions()` verbunden sind. ## Fehlerbehebung [Section titled “Fehlerbehebung”](#fehlerbehebung) **Produkte werden nicht geladen** * Stellen Sie sicher, dass Bundle-ID / Anwendungs-ID mit Store-Konfiguration übereinstimmt. * Bestätigen Sie, dass Produkt-IDs aktiv und genehmigt (App Store) oder aktiviert (Google Play) sind. * Warten Sie mehrere Stunden nach dem Erstellen von Produkten; Store-Propagierung erfolgt nicht sofort. **Kauf abgebrochen oder hängt** * Benutzer können den Ablauf abbrechen; Aufrufe in `try/catch` einwickeln und benutzerfreundliche Fehlermeldungen anzeigen. * Für Android stellen Sie sicher, dass Testkonten die App aus dem Play Store installieren (interner Track), damit Billing funktioniert. * Überprüfen Sie logcat/Xcode auf Billing-Fehler beim Ausführen auf dem Gerät. **Abonnementstatus inkorrekt** * Verwenden Sie `getPurchases()`, um Store-Daten mit Ihrem lokalen Berechtigungs-Cache zu vergleichen. * Auf Android fragen Sie immer die Google Play Developer API mit dem `purchaseToken` ab, um Ablaufdaten oder Rückerstattungsstatus zu erhalten. * Auf iOS überprüfen Sie `isActive`/`expirationDate` und validieren Sie Belege, um Rückerstattungen oder Widerrufe zu erkennen. # iOS App Store Review-Richtlinien für IAP > Vollständiger Leitfaden zum Bestehen der App Store-Prüfung mit In-App-Käufen und Abonnements, einschließlich häufiger Ablehnungsgründe und Bewährte Methoden. Die Genehmigung Ihrer App im App Store erfordert sorgfältige Beachtung der Apple-Richtlinien, insbesondere bei der Implementierung von In-App-Käufen und Abonnements. Dieser Leitfaden behandelt alles, was Sie wissen müssen, um bei Ihrer ersten Einreichung die Prüfung zu bestehen. ![iOS App Store Review-Prozess](/native-purchases/review-guides/ios-review-hero.webp) ## Anforderungen für In-App-Käufe [Section titled “Anforderungen für In-App-Käufe”](#anforderungen-für-in-app-käufe) ### Preistransparenz (Kritisch) [Section titled “Preistransparenz (Kritisch)”](#preistransparenz-kritisch) Apple verlangt eine kristallklare Offenlegung der Preise vor jedem Kauf: **Unverzichtbare Elemente:** * Genauen Preis vor dem Kauf-Button anzeigen * Abrechnungshäufigkeit anzeigen (z.B. “9,99€/Monat”) * Klar angeben, was Benutzer für ihr Geld bekommen * Angeben, wann Gebühren anfallen **Häufige Ablehnung:** > “Abonnementpreise müssen klar und im Voraus angegeben werden.” :::caution Preiskonsistenz Alle Preise müssen übereinstimmen in: * App Store-Metadaten-Auflistung * In-App-Kaufbildschirmen * Abonnementverwaltungsbildschirmen Selbst eine 1€-Diskrepanz zwischen Store-Auflistung (4,99€) und App (5,99€) führt zu automatischer Ablehnung. ::: ### Präsentation von Abonnementplänen [Section titled “Präsentation von Abonnementplänen”](#präsentation-von-abonnementplänen) **Erforderliche Angaben:** * Alle verfügbaren Abonnement-Stufen zusammen angezeigt * Klarer Vergleich der Funktionen pro Stufe * Keine automatische Standardeinstellung auf Premium-Stufen durch UI-Tricks * Leicht zu findende Kündigungsanweisungen ![UI-Design Do's and Don'ts](/native-purchases/review-guides/ui-design-dos-donts.webp) **Beispiel für konforme UI:** ```typescript import { NativePurchases } from '@capgo/native-purchases'; function SubscriptionScreen() { return (

Wählen Sie Ihren Plan

{/* Alle Stufen gleichwertig anzeigen */} {/* Klare Kündigungsinformationen */} Jederzeit in Einstellungen > Abonnements kündigen. Keine Rückerstattung für Teilzeiträume.
); } ``` ### Käufe wiederherstellen [Section titled “Käufe wiederherstellen”](#käufe-wiederherstellen) **Erforderliche Implementierung:** Jede App mit IAP muss Benutzern die Möglichkeit bieten, frühere Käufe wiederherzustellen, ohne den Support zu kontaktieren. ```typescript import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases'; async function restorePurchases() { try { await NativePurchases.restorePurchases(); const { purchases } = await NativePurchases.getPurchases({ productType: PURCHASE_TYPE.SUBS, }); const activeSub = purchases.find( (purchase) => purchase.isActive && purchase.expirationDate, ); if (activeSub) { unlockPremiumFeatures(); showMessage('Käufe erfolgreich wiederhergestellt!'); return; } const { purchases: iaps } = await NativePurchases.getPurchases({ productType: PURCHASE_TYPE.INAPP, }); const hasIap = iaps.some((purchase) => purchase.productIdentifier === 'premium_unlock'); showMessage( hasIap ? 'Premium-Kauf wiederhergestellt!' : 'Keine früheren Käufe gefunden.', ); } catch (error) { showError('Käufe konnten nicht wiederhergestellt werden. Bitte versuchen Sie es erneut.'); } } // Sichtbaren "Käufe wiederherstellen"-Button hinzufügen ``` ## Häufige Ablehnungsgründe [Section titled “Häufige Ablehnungsgründe”](#häufige-ablehnungsgründe) ### 1. App-Abstürze oder fehlerhafte Funktionalität [Section titled “1. App-Abstürze oder fehlerhafte Funktionalität”](#1-app-abstürze-oder-fehlerhafte-funktionalität) **Warum sie fehlschlägt:** * App stürzt beim Start ab * Kaufvorgang kann nicht abgeschlossen werden * In Screenshots gezeigte Funktionen funktionieren nicht **Prävention:** * Auf echten Geräten testen (nicht nur Simulatoren) * Alle Abonnement-Flows Ende-zu-Ende testen * Quittungsvalidierung funktioniert prüfen * Netzwerkfehlerbehandlung überprüfen ### 2. Metadaten-Unstimmigkeiten [Section titled “2. Metadaten-Unstimmigkeiten”](#2-metadaten-unstimmigkeiten) **Warum sie fehlschlägt:** * Screenshots zeigen Funktionen, die nicht im aktuellen Build sind * Beschreibung erwähnt nicht vorhandene Funktionalität * Preise in Metadaten unterscheiden sich von In-App-Preisen ![Metadaten-Checkliste](/native-purchases/review-guides/metadata-checklist.webp) **Prävention:** ```typescript // Dokumentieren Sie genau, was in jeder Stufe enthalten ist const SUBSCRIPTION_FEATURES = { basic: ['Werbefrei', 'Cloud-Synchronisation', 'Basis-Themes'], premium: ['Werbefrei', 'Cloud-Synchronisation', 'Alle Themes', 'Priority-Support'] }; // Verwenden Sie diese sowohl in Ihrer App ALS AUCH in der App Store-Beschreibung ``` ### 3. Fehlende Berechtigungserklärungen [Section titled “3. Fehlende Berechtigungserklärungen”](#3-fehlende-berechtigungserklärungen) **Warum sie fehlschlägt:** * Kamera/Standort/Gesundheit ohne Erklärung anfordern * Berechtigungsanfragen mehrere Bildschirme tief vergraben * Vage oder allgemeine Berechtigungsbeschreibungen **Prävention:** Aktualisieren Sie Ihre `Info.plist` mit klaren Erklärungen: ```xml NSCameraUsageDescription Kamerazugriff wird benötigt, um Produkt-Barcodes für schnelle Abonnement-Upgrades zu scannen. NSLocationWhenInUseUsageDescription Der Standort hilft uns, relevante lokale Inhalte in Ihrem Premium-Abonnement anzuzeigen. ``` ### 4. Irreführendes Marketing [Section titled “4. Irreführendes Marketing”](#4-irreführendes-marketing) **Warum sie fehlschlägt:** * Behauptungen wie “Nr. 1 App der Welt” ohne Beweis * “Unbegrenzte” Funktionen mit versteckten Limits * Falsche Dringlichkeitstaktiken (“Nur noch 2 Plätze frei!”) ![Beschreibungsrichtlinien Beispiele](/native-purchases/review-guides/description-guidelines-1.webp) ![Zusätzliche Beschreibungsrichtlinien](/native-purchases/review-guides/description-guidelines-2.webp) **Prävention:** * Spezifisch und sachlich in Beschreibungen sein * Superlative ohne Beweise vermeiden * Benutzer nicht mit falscher Knappheit unter Druck setzen ### 5. Versteckter Kündigungsprozess [Section titled “5. Versteckter Kündigungsprozess”](#5-versteckter-kündigungsprozess) **Warum sie fehlschlägt:** * Keine Erwähnung, wie man kündigt * Kündigungsbutton versteckt oder verdeckt * Mehrstufiger Kündigungsprozess ohne Apples nativen Flow **Prävention:** ```typescript // Benutzer immer über Kündigung informieren function SubscriptionInfo() { return (

So kündigen Sie

  1. iPhone-Einstellungen öffnen
  2. Auf Ihren Namen oben tippen
  3. Auf Abonnements tippen
  4. Diese App auswählen und auf Abonnement kündigen tippen

Oder direkt in der App Store-App verwalten.

); } async function openSubscriptionManagement() { // Direkter Link zur iOS-Abonnementverwaltung await NativePurchases.showManageSubscriptions(); } ``` ## Datenschutz & Datennutzung (Abschnitt 5.1.1) [Section titled “Datenschutz & Datennutzung (Abschnitt 5.1.1)”](#datenschutz--datennutzung-abschnitt-511) Apple hat die Datenschutzanforderungen im Jahr 2025 erheblich verschärft. ### Erforderliche Angaben [Section titled “Erforderliche Angaben”](#erforderliche-angaben) **Für jede Berechtigung:** 1. Warum Sie sie benötigen (spezifischer Anwendungsfall) 2. Wann sie verwendet wird 3. Wie Daten gespeichert/weitergegeben werden 4. Ob sie optional oder erforderlich ist ### Beispiel: Ordnungsgemäßer Berechtigungs-Flow [Section titled “Beispiel: Ordnungsgemäßer Berechtigungs-Flow”](#beispiel-ordnungsgemäßer-berechtigungs-flow) ```typescript async function requestCameraPermission() { // Erklärung VOR der Anfrage zeigen await showDialog({ title: 'Kamerazugriff', message: 'Wir benötigen Kamerazugriff, um Barcodes für eine schnelle Produktsuche zu scannen. Ihre Fotos werden niemals hochgeladen oder gespeichert.', buttons: ['Jetzt nicht', 'Erlauben'] }); // Dann Berechtigung anfordern const result = await Camera.requestPermissions(); return result.camera === 'granted'; } ``` ### Datenschutz-Nährwertkennzeichnungen [Section titled “Datenschutz-Nährwertkennzeichnungen”](#datenschutz-nährwertkennzeichnungen) Stellen Sie sicher, dass Ihre App Store-Datenschutzkennzeichnungen genau widerspiegeln: * Kaufverlaufserfassung * E-Mail-Adressen (für Quittungen) * Geräte-IDs (zur Betrugsprävention) * Nutzungsdaten (für Analytics) Ungenaue Datenschutzkennzeichnungen sind ein häufiger Ablehnungsgrund im Jahr 2025. Prüfen Sie Ihre Datenerfassung sorgfältig. ## Checkliste vor der Einreichung [Section titled “Checkliste vor der Einreichung”](#checkliste-vor-der-einreichung) ![Checkliste vor der Einreichung](/native-purchases/review-guides/pre-submission-checklist.webp) 1. **Alle Kauf-Flows testen** * Jede Abonnement-Stufe kaufen * Kostenlose Testversionen testen * Einführungsangebote werden korrekt angewendet prüfen * Käufe wiederherstellen testen * Familienfreigabe prüfen (falls aktiviert) * Auf mehreren Geräten testen 2. **Preiskonsistenz prüfen** * App Store-Metadaten stimmen mit In-App-Preisen überein prüfen * Alle Währungen sind korrekt prüfen * Dauern kostenloser Testversionen stimmen mit Beschreibungen überein bestätigen * Bedingungen für Einführungsangebote sind korrekt prüfen 3. **Alle Texte überprüfen** * Platzhaltertext entfernen * Behauptungen sind testbar prüfen * Grammatik und Rechtschreibung prüfen * Beschreibungen stimmen mit aktuellem Build überein sicherstellen * Erwähnungen von Wettbewerbern entfernen 4. **Berechtigungen testen** * Nur notwendige Berechtigungen anfordern * Klare Erklärungen vor der Anforderung zeigen * “Ablehnen”-Flows testen (App sollte weiterhin funktionieren) * Info.plist-Beschreibungen sind klar prüfen 5. **Testkonto vorbereiten** * Sandbox-Testkonto erstellen * Anmeldedaten in App-Prüfungsnotizen dokumentieren * Testkonto hat aktives Abonnement prüfen * Prüfer kann Kaufvorgang abschließen testen 6. **Metadaten prüfen** * Screenshots stimmen mit aktueller UI überein * App-Vorschauvideo (falls vorhanden) zeigt aktuelle Version * Beschreibung beschreibt Funktionen genau * Alterseinstufung entspricht Inhalt * Datenschutzrichtlinie ist in der App zugänglich 7. **Detaillierte Prüfungsnotizen schreiben** ```plaintext Testkonto: E-Mail: reviewer@test.com Passwort: TestPass123! Testanweisungen: 1. Mit obigem Testkonto anmelden 2. Auf "Auf Premium upgraden"-Button tippen 3. "Monatlich Premium"-Abonnement auswählen 4. Kauf abschließen (keine Gebühr in Sandbox) 5. Premium-Funktionen werden freigeschaltet prüfen Hinweis: Abonnementpreise werden vor dem Kauf klar angezeigt. Kündigungsanweisungen finden Sie unter Einstellungen > Konto. ``` ## Prüfungszeitplan [Section titled “Prüfungszeitplan”](#prüfungszeitplan) ![App Store Review-Zeitplan](/native-purchases/review-guides/review-timeline.webp) **Standardprüfung:** 24-48 Stunden **Spitzenzeiten:** 3-5 Tage (App Store-Feiertagsveröffentlichungen) **Wochenenden:** Keine Prüfungen werden bearbeitet **Beschleunigte Prüfung:** Verfügbar für kritische Fehlerbehebungen (Anfrage über App Store Connect) Tip Reichen Sie früh in der Woche ein, um Wochenendeverzögerungen zu vermeiden. Montags-Einreichungen werden typischerweise bis Mittwoch geprüft. ## Richtlinien-Updates 2025 [Section titled “Richtlinien-Updates 2025”](#richtlinien-updates-2025) ### Neue Anforderungen [Section titled “Neue Anforderungen”](#neue-anforderungen) **1. Offenlegung von AI-Funktionalität** Wenn Ihre App AI für irgendwelche Funktionen verwendet, müssen Sie: * AI-generierte Inhalte klar kennzeichnen * Erklären, wie AI verwendet wird * Inhaltssicherheitsmaßnahmen dokumentieren **2. Erweiterte Abonnement-Klarheit** * Seite-an-Seite-Planvergleiche erforderlich * Keine “Dark Patterns”, die günstigere Optionen verstecken * Klare Downgrade/Upgrade-Pfade **3. Datenschutz-Intensivierung** * Abschnitt 5.1.1-Durchsetzung erhöht * Mehr Kontrolle über Rechtfertigung der Datenerfassung * Strengere Anforderungen für Kinder-Apps ### Was sich seit 2024 geändert hat [Section titled “Was sich seit 2024 geändert hat”](#was-sich-seit-2024-geändert-hat) * Modulare Einreichungen jetzt erlaubt (Produktseiten unabhängig aktualisieren) * In-App-Events können separat eingereicht werden * Strengere Durchsetzung irreführender Abonnement-UIs * Neue Anleitung zu Kryptowährung/NFT-Apps ## Bewährte Methoden für Native Purchases Plugin [Section titled “Bewährte Methoden für Native Purchases Plugin”](#bewährte-methoden-für-native-purchases-plugin) ### Ordnungsgemäße Fehlerbehandlung implementieren [Section titled “Ordnungsgemäße Fehlerbehandlung implementieren”](#ordnungsgemäße-fehlerbehandlung-implementieren) ```typescript import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases'; async function handlePurchase(productId: string) { try { const transaction = await NativePurchases.purchaseProduct({ productIdentifier: productId, productType: PURCHASE_TYPE.SUBS, }); // Erfolg await validateReceiptOnServer(transaction.receipt); showSuccess('Abonnement aktiviert!'); unlockFeatures(); } catch (error: any) { // Spezifische Fehlerfälle behandeln if (error.code === 'USER_CANCELLED') { // Benutzer abgebrochen - keinen Fehler anzeigen console.log('Kauf vom Benutzer abgebrochen'); } else if (error.code === 'PAYMENT_PENDING') { showInfo('Zahlung steht aus. Bitte später erneut prüfen.'); } else if (error.code === 'PRODUCT_ALREADY_PURCHASED') { // Stattdessen wiederherstellen await NativePurchases.restorePurchases(); } else { // Benutzerfreundlichen Fehler anzeigen showError('Kauf kann nicht abgeschlossen werden. Bitte versuchen Sie es erneut.'); } } } ``` ### Ladezustände anzeigen [Section titled “Ladezustände anzeigen”](#ladezustände-anzeigen) ```typescript function PurchaseButton({ productId }: { productId: string }) { const [loading, setLoading] = useState(false); const handlePurchase = async () => { setLoading(true); try { await NativePurchases.purchaseProduct({ productIdentifier: productId }); } finally { setLoading(false); } }; return ( ); } ``` ### Bedingungen klar anzeigen [Section titled “Bedingungen klar anzeigen”](#bedingungen-klar-anzeigen) ```typescript function SubscriptionTerms() { return (

Das Abonnement verlängert sich automatisch, es sei denn, es wird mindestens 24 Stunden vor Ende der aktuellen Periode gekündigt.

Ihr Konto wird innerhalb von 24 Stunden vor Ende der aktuellen Periode für die Verlängerung belastet.

Abonnements können vom Benutzer verwaltet werden und die automatische Verlängerung kann nach dem Kauf in den Kontoeinstellungen deaktiviert werden.

Nutzungsbedingungen | Datenschutzrichtlinie

); } ``` ## Wenn Ihre App abgelehnt wird [Section titled “Wenn Ihre App abgelehnt wird”](#wenn-ihre-app-abgelehnt-wird) ### Schritte zur Lösung [Section titled “Schritte zur Lösung”](#schritte-zur-lösung) 1. **Ablehnung sorgfältig lesen** * Spezifische zitierte Richtlinie notieren (z.B. 3.1.1, 5.1.1) * Genau verstehen, was Apple markiert hat 2. **Problem gründlich beheben** * Nicht nur flicken - Grundursache beheben * Lösung umfassend testen * Dokumentieren, was Sie geändert haben 3. **Im Resolution Center antworten** ```plaintext Vielen Dank für Ihr Feedback. Ich habe das Problem behoben: Problem: Abonnementpreise nicht im Voraus klar Behebung: Explizite Preisanzeige auf Abonnementauswahlbildschirm hinzugefügt, die "9,99€/Monat" vor dem Kauf-Button zeigt. Auch Kündigungsanweisungen auf demselben Bildschirm hinzugefügt. Die Änderungen sind in dieser Einreichung enthalten und können mit dem bereitgestellten Testkonto getestet werden. ``` 4. **Umgehend erneut einreichen** * Wiedereinreichungen werden in der Regel schneller geprüft * Normalerweise innerhalb von 24 Stunden ### Beschwerdeverfahren [Section titled “Beschwerdeverfahren”](#beschwerdeverfahren) Wenn Sie glauben, dass die Ablehnung falsch ist: ![App Store-Klärungsprozess](/native-purchases/review-guides/clarification-process.webp) 1. Auf “Beschwerde” in App Store Connect klicken 2. Klare Beweise liefern: * Screenshots, die Konformität zeigen * Verweise auf spezifische Richtlinien * Erklärung, wie Sie Anforderungen erfüllen 3. Professionell und sachlich sein 4. Testkonto einschließen, wenn Funktionalität schwer zu finden ist ![Anfrage für Dokumente Beispiel](/native-purchases/review-guides/request-documents.webp) ## Zusätzliche Ressourcen [Section titled “Zusätzliche Ressourcen”](#zusätzliche-ressourcen) * [Apple App Store Review-Richtlinien](https://developer.apple.com/app-store/review/guidelines/) * [In-App-Kauf-Richtlinien](https://developer.apple.com/app-store/review/guidelines/#in-app-purchase) * [Bewährte Methoden für Abonnements](https://developer.apple.com/app-store/subscriptions/) * [App Store Connect-Hilfe](https://developer.apple.com/help/app-store-connect/) ## Support [Section titled “Support”](#support) Falls Sie weiterhin Probleme haben: * Überprüfen Sie die [Native Purchases-Dokumentation](/docs/plugins/native-purchases/getting-started/) * Prüfen Sie [häufige Problembehebungen](/docs/plugins/native-purchases/getting-started/#troubleshooting) * Kontaktieren Sie Apple Developer Support für Richtlinien-Klärungen ### Brauchen Sie fachkundige Hilfe? [Section titled “Brauchen Sie fachkundige Hilfe?”](#brauchen-sie-fachkundige-hilfe) Haben Sie Schwierigkeiten mit der App-Prüfung oder benötigen personalisierte Unterstützung? **[Buchen Sie ein Beratungsgespräch mit unserem Team](https://cal.com/team/capgo/capacitor-consulting-services)** für dedizierte Unterstützung bei: * IAP-Implementierungsprüfung und -optimierung * App Store-Prüfungsvorbereitung und -strategie * Überprüfung der Einreichungs-Checkliste * Ablösungslösung und Beschwerden * Vollständiges Testen und Validierung Unsere Experten haben Hunderten von Apps erfolgreich zur Genehmigung verholfen! # iOS Auto-Renewable Subscription erstellen > Schritt-für-Schritt-Anleitung zum Erstellen von automatisch verlängernden Abonnements in App Store Connect für das native-purchases-Plugin. Automatisch verlängernde Abonnements bieten wiederkehrenden Zugriff auf Inhalte, Dienste oder Premium-Funktionen in Ihrer iOS-App. Dieser Leitfaden führt Sie durch die Erstellung von Abonnements in App Store Connect. ## Übersicht [Section titled “Übersicht”](#übersicht) Automatisch verlängernde Abonnements werden am Ende jeder Abrechnungsperiode automatisch verlängert, bis Benutzer kündigen. Sie sind perfekt für: * Premium-Inhalte und -Funktionen * Werbefreie Erlebnisse * Cloud-Speicher und Synchronisation * Streaming-Dienste * Professionelle Tools und Dienstprogramme ## Voraussetzungen [Section titled “Voraussetzungen”](#voraussetzungen) Bevor Sie Abonnements erstellen, müssen Sie: 1. [Eine Abonnementgruppe erstellen](/docs/plugins/native-purchases/ios-subscription-group/), um Ihre Abonnements zu organisieren 2. Eine aktive Apple Developer Program-Mitgliedschaft haben 3. Bank- und Steuerinformationen in App Store Connect vervollständigen ## Ein Abonnement erstellen [Section titled “Ein Abonnement erstellen”](#ein-abonnement-erstellen) 1. **Zu Abonnements navigieren** In App Store Connect wählen Sie Ihre App aus und gehen zu **Monetarisieren > Abonnements**. Wählen Sie Ihre Abonnementgruppe aus oder erstellen Sie bei Bedarf eine neue. ![Zu Abonnements navigieren](/native-purchases/ios/create-subscription/navigate-to-subscriptions.webp) 2. **Neues Abonnement erstellen** Klicken Sie auf das **+**-Symbol neben Ihrer Abonnementgruppe, um ein neues Abonnement hinzuzufügen. 3. **Grundinformationen eingeben** **Referenzname**: Beschreibender Name für Ihre interne Verwendung (wird Kunden nicht angezeigt) * Beispiele: “Premium Monatlich”, “Ultimate Jährlich”, “Basis-Plan” **Produkt-ID**: Eindeutige Kennung für dieses Abonnement (kann später nicht geändert werden) * Format: `com.ihrfirma.ihreapp.premium_monthly` * Verwenden Sie beschreibende Namen in Kleinbuchstaben mit Unterstrichen * Erforderlich für die Konfiguration des native-purchases-Plugins ![Abonnementdetails eingeben](/native-purchases/ios/create-subscription/enter-subscription-info.webp) 4. **Dauer konfigurieren** Wählen Sie die Abonnementdauer aus den verfügbaren Optionen: * 1 Woche * 1 Monat * 2 Monate * 3 Monate * 6 Monate * 1 Jahr Die Dauer bestimmt, wie oft Benutzer abgerechnet werden. 5. **Preise festlegen** Klicken Sie auf **Abonnementpreis hinzufügen**, um Preise zu konfigurieren: **Basis-Territorium**: Wählen Sie Ihren primären Markt (normalerweise Ihr Land) **Preis**: Abonnementpreis festlegen * Apple konvertiert automatisch in andere Währungen * Aus Apples Preisstufen wählen * Wahrgenommenen Wert und Marktraten berücksichtigen ![Preise konfigurieren](/native-purchases/ios/create-subscription/configure-pricing.webp) 6. **Familienfreigabe (Optional)** Entscheiden Sie, ob Sie die Familienfreigabe aktivieren möchten, die bis zu 6 Familienmitgliedern den Zugriff auf das Abonnement ermöglicht. Caution Sobald die Familienfreigabe aktiviert ist, kann sie für dieses Produkt nicht mehr deaktiviert werden. **Aktivieren wenn:** * Inhalt für die Familiennutzung geeignet ist * Sie das Wertversprechen erhöhen möchten * Ihr Geschäftsmodell es unterstützt **Nicht aktivieren wenn:** * Abonnement nur für individuelle Nutzung ist * Inhalt auf den Benutzer zugeschnitten ist * Sie Umsatz pro Benutzer maximieren möchten 7. **Lokalisierungen hinzufügen** Fügen Sie Abonnement-Anzeigeinformationen in allen Sprachen hinzu, die Ihre App unterstützt: **Abonnement-Anzeigename**: Kundenorientierter Name (z.B. “Premium Monatlich”) **Beschreibung**: Kurze Beschreibung dessen, was das Abonnement beinhaltet * Prägnant und nutzenorientiert halten * Hauptfunktionen erwähnen * Wertversprechen hervorheben ![Lokalisierungen hinzufügen](/native-purchases/ios/create-subscription/add-localization.webp) 8. **App Store-Werbebild (Optional)** Laden Sie ein Werbebild für dieses Abonnement hoch (312x390 Pixel): * Wird auf der App Store-Abonnementseite angezeigt * Sollte zum Design Ihrer App passen * Abonnementnamen zur Klarheit einschließen Note Während Bilder für die erste Einreichung optional sind, sind sie für die Werbeanzeige im App Store erforderlich. Sie können sie später hinzufügen. 9. **Speichern und einreichen** Klicken Sie auf **Speichern**, um das Abonnement zu erstellen. **Für erstes Abonnement:** * Muss mit einer neuen App-Version eingereicht werden * In Ihre nächste App Store-Einreichung einbeziehen * Kann nicht unabhängig eingereicht werden **Für nachfolgende Abonnements:** * Kann direkt von der Abonnementseite eingereicht werden * Benötigen keine neue App-Version * Verfügbar, nachdem das erste Abonnement genehmigt wurde ## Abonnementstatus [Section titled “Abonnementstatus”](#abonnementstatus) Ihr Abonnement wird einen dieser Status haben: | Status | Beschreibung | Kann testen? | | -------------------------- | ----------------------------------- | ------------ | | **Fehlende Metadaten** | Unvollständige Einrichtung | Ja (Sandbox) | | **Bereit zur Einreichung** | Vollständig, aber nicht eingereicht | Ja (Sandbox) | | **Wartet auf Prüfung** | An Apple eingereicht | Ja (Sandbox) | | **In Prüfung** | Wird von Apple geprüft | Ja (Sandbox) | | **Genehmigt** | Zum Kauf verfügbar | Ja | | **Abgelehnt** | Benötigt Änderungen | Ja (Sandbox) | Tip Sie können Abonnements im Sandbox-Modus testen, auch wenn sie “Fehlende Metadaten” oder “Bereit zur Einreichung” anzeigen! ## Verwendung in Ihrer App [Section titled “Verwendung in Ihrer App”](#verwendung-in-ihrer-app) Nach der Erstellung verweisen Sie in Ihrer App auf das Abonnement mit der Produkt-ID: ```typescript import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases'; // Abonnementprodukte direkt von StoreKit abrufen const { products } = await NativePurchases.getProducts({ productIdentifiers: [ 'com.ihrfirma.ihreapp.premium_monthly', 'com.ihrfirma.ihreapp.premium_annual', ], productType: PURCHASE_TYPE.SUBS, }); products.forEach((product) => { console.log(`${product.title}: ${product.priceString}`); console.log(`Dauer: ${product.subscriptionPeriod}`); console.log(`Beschreibung: ${product.description}`); }); // Ein Abonnement kaufen (StoreKit 2 behandelt automatisch Einführungspreise und Angebote) try { const transaction = await NativePurchases.purchaseProduct({ productIdentifier: 'com.ihrfirma.ihreapp.premium_monthly', productType: PURCHASE_TYPE.SUBS, }); console.log('Transaktions-ID:', transaction.transactionId); // StoreKit-Quittungen sind auf iOS für serverseitige Validierung enthalten await sendReceiptToBackend(transaction.receipt); } catch (error) { console.error('Kauf fehlgeschlagen:', error); } // Abonnementstatus über die Store-Daten prüfen const { purchases } = await NativePurchases.getPurchases({ productType: PURCHASE_TYPE.SUBS, }); const premium = purchases.find( (purchase) => purchase.productIdentifier === 'com.ihrfirma.ihreapp.premium_monthly', ); if (premium?.isActive) { console.log('Läuft ab:', premium.expirationDate); console.log('Wird verlängert:', premium.willCancel === false); console.log('Store-Status:', premium.subscriptionState); unlockPremiumFeatures(); } else { showPaywall(); } ``` ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) ### Preisstrategie [Section titled “Preisstrategie”](#preisstrategie) * **Monatspläne**: Niedrigere Einstiegshürde, bildet Gewohnheit * **Jahrespläne**: Besserer Wert, höherer LTV, geringere Abwanderung * **Mehrere Stufen**: Basic, Premium, Ultimate für verschiedene Benutzersegmente * **Wettbewerbsanalyse**: Preise ähnlicher Apps recherchieren ### Produkt-IDs [Section titled “Produkt-IDs”](#produkt-ids) * Konsistente Benennung verwenden: `firma.app.stufe_dauer` * Stufe und Dauer in ID einschließen: `premium_monthly`, `ultimate_annual` * Änderung von Produkt-IDs vermeiden (sie sind dauerhaft) * Alle Produkt-IDs für Ihr Team dokumentieren ### Familienfreigabe [Section titled “Familienfreigabe”](#familienfreigabe) * Für familienorientierte Apps aktivieren (Spiele, Bildung, Unterhaltung) * Auswirkung auf Umsatz berücksichtigen * Freigabeverhalten gründlich testen * Freigabefähigkeit im Marketing kommunizieren ### Lokalisierung [Section titled “Lokalisierung”](#lokalisierung) * Alle Abonnementnamen und Beschreibungen übersetzen * Regionale Preisunterschiede berücksichtigen * Anzeige in allen unterstützten Sprachen testen * Kulturell angemessene Marketingsprache verwenden ### Werbebilder [Section titled “Werbebilder”](#werbebilder) * Konsistenten visuellen Stil beibehalten * Abonnementnamen und Hauptvorteil einschließen * Für saisonale Aktionen aktualisieren * Zur gesamten Design-Sprache der App passen ## Häufige Abonnementmuster [Section titled “Häufige Abonnementmuster”](#häufige-abonnementmuster) ### Einzelstufe (Freemium) [Section titled “Einzelstufe (Freemium)”](#einzelstufe-freemium) ```plaintext Kostenlose App + Premium-Abonnement - Basic: Kostenlos (eingeschränkte Funktionen) - Premium Monatlich: 4,99€ - Premium Jährlich: 39,99€ (33% sparen) ``` ### Mehrstufig (Gut, Besser, Am besten) [Section titled “Mehrstufig (Gut, Besser, Am besten)”](#mehrstufig-gut-besser-am-besten) ```plaintext - Basic Monatlich: 4,99€ - Premium Monatlich: 9,99€ - Ultimate Monatlich: 19,99€ - Basic Jährlich: 49,99€ - Premium Jährlich: 99,99€ - Ultimate Jährlich: 199,99€ ``` ### Verbrauchbar + Abonnement-Hybrid [Section titled “Verbrauchbar + Abonnement-Hybrid”](#verbrauchbar--abonnement-hybrid) ```plaintext - Credit-Pakete (verbrauchbar) - Monatsabonnement (unbegrenzte Credits) - Jahresabonnement (unbegrenzt + Bonus-Funktionen) ``` ## Fehlerbehebung [Section titled “Fehlerbehebung”](#fehlerbehebung) **Abonnement lädt nicht in App:** * Produkt-ID stimmt genau überein prüfen (groß-/kleinschreibungsabhängig) * Abonnement ist in Abonnementgruppe prüfen * Bundle-Kennung stimmt mit App Store Connect überein sicherstellen * 2-3 Stunden nach Produkterstellung warten **Abonnement kann nicht eingereicht werden:** * Alle erforderlichen Felder vervollständigen (Name, Beschreibung, Preis) * Mindestens eine Lokalisierung hinzufügen * Bank-/Steuerinformationen sind genehmigt prüfen * Prüfen, ob erstes Abonnement (benötigt App-Version) **Familienfreigabe-Umschalter deaktiviert:** * Bereits aktiviert (kann nicht deaktiviert werden) * In Abonnementdetails prüfen * Apple Support kontaktieren, falls festgefahren **Preisstufe nicht verfügbar:** * Kann in einigen Territorien eingeschränkt sein * Alternative Stufe wählen * Apple für Preisfragen kontaktieren **“Ungültige Produkt-ID”-Fehler:** * Muss Reverse-Domain-Format sein * Darf keine Leerzeichen oder Sonderzeichen enthalten * Auf Tippfehler prüfen * Eindeutigkeit über alle Produkte hinweg prüfen ## Nächste Schritte [Section titled “Nächste Schritte”](#nächste-schritte) * [Ein Einführungsangebot erstellen](/docs/plugins/native-purchases/ios-introductory-offer/), um neue Abonnenten zu gewinnen * [Sandbox-Tests konfigurieren](/docs/plugins/native-purchases/ios-sandbox-testing/), um Ihre Abonnements zu testen * Aktionsangebote für Rückgewinnung und Bindung einrichten * Abonnement-Analytics-Tracking implementieren ## Zusätzliche Ressourcen [Section titled “Zusätzliche Ressourcen”](#zusätzliche-ressourcen) Weitere Details finden Sie in der [offiziellen Apple-Dokumentation zu automatisch verlängernden Abonnements](https://developer.apple.com/documentation/storekit/in-app_purchase/subscriptions_and_offers). # iOS-Abonnement-Einführungsangebot erstellen > Erfahren Sie, wie Sie Einführungsangebote für automatisch verlängernde Abonnements auf iOS erstellen, um neue Abonnenten zu gewinnen und zu konvertieren. Einführungsangebote ermöglichen es Ihnen, berechtigten Benutzern kostenlose Testversionen oder vergünstigte Einführungspreise anzubieten, um Einstiegshürden zu reduzieren und Abonnement-Konversionen zu erhöhen. ## Übersicht [Section titled “Übersicht”](#übersicht) Einführungsangebote sind eines der effektivsten Werkzeuge zum Wachstum Ihrer Abonnentenbasis. Sie ermöglichen es Benutzern: * Ihre Premium-Funktionen risikofrei zu testen * Wert zu erfahren, bevor sie sich verpflichten * Mit einem niedrigeren Preispunkt zu beginnen * Vertrauen in Ihr Produkt aufzubauen ## Angebotstypen [Section titled “Angebotstypen”](#angebotstypen) iOS unterstützt drei Arten von Einführungsangeboten: ### 1. Kostenlose Testversion [Section titled “1. Kostenlose Testversion”](#1-kostenlose-testversion) Kunden erhalten für einen bestimmten Zeitraum kostenlosen Zugang. Nach der Testversion werden ihnen Standardpreise berechnet, wenn sie nicht kündigen. **Beispiele:** * 7 Tage kostenlos * 14 Tage kostenlos * 1 Monat kostenlos **Am besten geeignet für:** * Hochwertige Abonnements * Funktionsreiche Apps * Aufbau von Nutzergewohnheiten ### 2. Vorabzahlung [Section titled “2. Vorabzahlung”](#2-vorabzahlung) Kunden zahlen einen einmaligen reduzierten Preis, der den Einführungszeitraum abdeckt. **Beispiele:** * 1,99 € für 2 Monate (dann 9,99 €/Monat) * 9,99 € für 3 Monate (dann 19,99 €/Monat) **Am besten geeignet für:** * Verbindlichkeitssignale * Cashflow-Bedürfnisse * Testen der Preissensitivität ### 3. Ratenzahlung [Section titled “3. Ratenzahlung”](#3-ratenzahlung) Kunden zahlen über mehrere Abrechnungszyklen einen reduzierten Preis. **Beispiele:** * 1,99 €/Monat für 3 Monate (dann 9,99 €/Monat) * 4,99 €/Monat für 6 Monate (dann 14,99 €/Monat) **Am besten geeignet für:** * Schrittweise Verpflichtung * Langfristige Wertdemonstration * Reduzierung des wahrgenommenen Risikos ## Berechtigungsanforderungen [Section titled “Berechtigungsanforderungen”](#berechtigungsanforderungen) Benutzer können Einführungsangebote nur erhalten, wenn sie: * Noch kein Einführungsangebot für das Produkt erhalten haben * Kein Einführungsangebot für ein Produkt in derselben Abonnementgruppe erhalten haben * Kein aktives Abonnement für das Produkt hatten Note Apple übernimmt die Berechtigungsprüfung automatisch. Das native-purchases Plugin bietet Methoden zur Überprüfung der Berechtigung vor der Präsentation von Angeboten. ## Voraussetzungen [Section titled “Voraussetzungen”](#voraussetzungen) Sie müssen zuerst [ein automatisch verlängerndes Abonnement erstellen](/docs/plugins/native-purchases/ios-create-subscription/), bevor Sie ein Einführungsangebot hinzufügen. ## Erstellen eines Einführungsangebots [Section titled “Erstellen eines Einführungsangebots”](#erstellen-eines-einführungsangebots) 1. **Zum Abonnement navigieren** Gehen Sie in App Store Connect zu Ihrem App-Bereich **Monetarisierung > Abonnements** und wählen Sie das Abonnement aus, dem Sie ein Angebot hinzufügen möchten. 2. **Abonnementpreis hinzufügen** Klicken Sie auf das **+**-Symbol neben “Abonnementpreise”, um das Preis-Modal zu öffnen. 3. **Einführungsangebot erstellen** Wählen Sie **“Einführungsangebot erstellen”** aus den Optionen. ![Einführungsangebot erstellen](/native-purchases/ios/introductory-offer/create-introductory-offer.webp) 4. **Länder und Startdatum konfigurieren** **Länder und Regionen**: Wählen Sie aus, wo das Angebot verfügbar sein wird * Wählen Sie alle Länder für maximale Reichweite * Oder beschränken Sie auf bestimmte Märkte zum Testen **Startdatum**: Wann das Angebot verfügbar wird * Kann sofort oder für die Zukunft geplant sein * Nützlich für die Koordination mit Marketingkampagnen **Enddatum (Optional)**: Wann das Angebot abläuft * Leer lassen für dauerhafte Verfügbarkeit * Datum festlegen für zeitlich begrenzte Aktionen 5. **Angebotstyp auswählen** Wählen Sie einen der drei Angebotstypen: **Kostenlos** (Kostenlose Testversion) * Dauer auswählen (Tage, Wochen, Monate) * Beispiele: 7 Tage, 2 Wochen, 1 Monat **Vorabzahlung** * Einmaligen Zahlungspreis festlegen * Von der Zahlung abgedeckte Dauer festlegen * Beispiel: 1,99 € für 2 Monate **Ratenzahlung** * Reduzierten Preis pro Zeitraum festlegen * Anzahl der Zeiträume festlegen * Beispiel: 2,99 €/Monat für 3 Monate 6. **Überprüfen und bestätigen** Überprüfen Sie die Zusammenfassung, die Folgendes zeigt: * Angebotstyp und Dauer * Preisdetails * Regulärer Preis nach Einführungszeitraum * Verfügbarkeitsdaten und Länder 7. **Speichern** Klicken Sie auf **Speichern**, um das Einführungsangebot zu erstellen. Es wird sofort zum Testen im Sandbox-Modus verfügbar sein. ## Beispiele für Angebotskonfigurationen [Section titled “Beispiele für Angebotskonfigurationen”](#beispiele-für-angebotskonfigurationen) ### Beispiel 1: Standard-Testversion [Section titled “Beispiel 1: Standard-Testversion”](#beispiel-1-standard-testversion) ```plaintext Typ: Kostenlos Dauer: 7 Tage Dann: 9,99 €/Monat ``` **Benutzerreise:** * Tag 1-7: Kostenloser Zugang * Tag 8: Erste Belastung von 9,99 € * Monatliche Belastungen setzen sich fort ### Beispiel 2: Vergünstigter Vorabzahlungszeitraum [Section titled “Beispiel 2: Vergünstigter Vorabzahlungszeitraum”](#beispiel-2-vergünstigter-vorabzahlungszeitraum) ```plaintext Typ: Vorabzahlung Preis: 4,99 € Dauer: 3 Monate Dann: 9,99 €/Monat ``` **Benutzerreise:** * Tag 1: Belastung von 4,99 € * 90 Tage Zugang * Tag 91: Belastung von 9,99 €/Monat ### Beispiel 3: Schrittweise Einführung [Section titled “Beispiel 3: Schrittweise Einführung”](#beispiel-3-schrittweise-einführung) ```plaintext Typ: Ratenzahlung Preis: 2,99 €/Monat Zeiträume: 6 Monate Dann: 9,99 €/Monat ``` **Benutzerreise:** * Monate 1-6: 2,99 €/Monat * Monat 7+: 9,99 €/Monat ## Verwendung in Ihrer App [Section titled “Verwendung in Ihrer App”](#verwendung-in-ihrer-app) Das native-purchases Plugin behandelt automatisch die Präsentation und Berechtigung von Einführungsangeboten: ```typescript import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases'; // Produkte mit Einführungsangebot-Informationen abrufen const { products } = await NativePurchases.getProducts({ productIdentifiers: ['com.yourapp.premium_monthly'], productType: PURCHASE_TYPE.SUBS, }); const product = products[0]; // Einführungsangebot-Details anzeigen (StoreKit sendet lokalisierte Metadaten) if (product.introductoryPrice) { console.log('Einführungspreis:', product.introductoryPriceString); console.log('Einführungszeitraum:', product.introductoryPricePeriod); console.log('Einführungszyklen:', product.introductoryPriceCycles); console.log('Regulärer Preis:', product.priceString); } else { console.log('Kein Einführungsangebot konfiguriert'); } // Kauf (StoreKit wendet automatisch Einführungspreise an, wenn berechtigt) try { const transaction = await NativePurchases.purchaseProduct({ productIdentifier: 'com.yourapp.premium_monthly', productType: PURCHASE_TYPE.SUBS, }); console.log('Abonnement aktiv, Beleglänge:', transaction.receipt?.length); await validateReceiptOnServer(transaction.receipt); } catch (error) { console.error('Kauf fehlgeschlagen:', error); } ``` ## Einführungsangebote für Benutzer anzeigen [Section titled “Einführungsangebote für Benutzer anzeigen”](#einführungsangebote-für-benutzer-anzeigen) ### Bewährte Methoden für UI [Section titled “Bewährte Methoden für UI”](#bewährte-methoden-für-ui) **Klares Wertversprechen:** ```plaintext Testen Sie Premium 7 Tage kostenlos Dann 9,99 €/Monat. Jederzeit kündbar. ``` **Einsparungen betonen:** ```plaintext Starten Sie für nur 1,99 € Erhalten Sie 3 Monate Premium für nur 1,99 € Dann 9,99 €/Monat ``` **Transparente Kommunikation:** ```plaintext Ihre kostenlose Testversion • Zugang zu allen Premium-Funktionen • Keine Belastung für 7 Tage • 9,99 €/Monat nach der Testversion • Jederzeit kündbar, auch während der Testversion ``` ### Beispielimplementierung [Section titled “Beispielimplementierung”](#beispielimplementierung) ```typescript function formatIntroOffer(product: any): string { if (!product.introductoryPrice) { return `${product.priceString} pro ${product.subscriptionPeriod}`; } const intro = product.introductoryPrice; const regular = product.priceString; if (intro.price === 0) { // Kostenlose Testversion return `Testen Sie kostenlos für ${intro.periodString}, dann ${regular}`; } else if (intro.cycles === 1) { // Vorabzahlung return `${intro.priceString} für ${intro.periodString}, dann ${regular}`; } else { // Ratenzahlung return `${intro.priceString} für ${intro.cycles} ${intro.periodString}(e), dann ${regular}`; } } ``` ## Marketing Bewährte Methoden [Section titled “Marketing Bewährte Methoden”](#marketing-bewährte-methoden) ### Strategie für Testversionen-Länge [Section titled “Strategie für Testversionen-Länge”](#strategie-für-testversionen-länge) * **3-7 Tage**: Apps mit schneller Entscheidung, Spiele * **7-14 Tage**: Standard für die meisten Apps * **14-30 Tage**: Komplexe Tools, professionelle Apps * **30+ Tage**: Hochwertige B2B oder Enterprise ### Preispsychologie [Section titled “Preispsychologie”](#preispsychologie) * **0,99 €-1,99 €**: Sehr niedrige Schwelle, gut zum Testen * **50% Rabatt**: Starker wahrgenommener Wert * **Erster Monat kostenlos**: Üblich, vertrautes Muster ### Kommunikations-Timing [Section titled “Kommunikations-Timing”](#kommunikations-timing) * **Vor Testende**: Benutzer an bevorstehende Belastung erinnern * **Wert hervorheben**: Nutzungsstatistiken, Erfolge zeigen * **Einfache Kündigung**: Vertrauen mit transparentem Prozess aufbauen ## Einführungsangebote testen [Section titled “Einführungsangebote testen”](#einführungsangebote-testen) Verwenden Sie Sandbox-Tests, um das Verhalten zu überprüfen: ```typescript import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases'; // Im Sandbox-Modus gelten beschleunigte Abonnementdauern: // - 3 Tage kostenlose Testversion = 3 Minuten // - 1 Woche kostenlose Testversion = 3 Minuten // - 1 Monat kostenlose Testversion = 5 Minuten const { products } = await NativePurchases.getProducts({ productIdentifiers: ['premium_monthly'], productType: PURCHASE_TYPE.SUBS, }); // Kauf mit Einführungsangebot const transaction = await NativePurchases.purchaseProduct({ productIdentifier: 'premium_monthly', productType: PURCHASE_TYPE.SUBS, }); console.log('Einführungskauf-Transaktion:', transaction.transactionId); // Auf beschleunigte Verlängerung warten setTimeout(async () => { const { purchases } = await NativePurchases.getPurchases({ productType: PURCHASE_TYPE.SUBS, }); const premium = purchases.find((purchase) => purchase.productIdentifier === 'premium_monthly'); console.log('Status nach Testversion:', premium?.subscriptionState); }, 180000); // 3 Minuten für wöchentliche Testversion ``` ## Wichtige Hinweise [Section titled “Wichtige Hinweise”](#wichtige-hinweise) ### Berechtigungsregeln [Section titled “Berechtigungsregeln”](#berechtigungsregeln) * Ein Einführungsangebot pro Benutzer pro Abonnementgruppe (lebenslang) * Gilt nur für neue Abonnenten * Kann nach Kündigung nicht erneut verwendet werden * Nicht verfügbar für Abonnement-Upgrades/Crossgrades ### StoreKit API [Section titled “StoreKit API”](#storekit-api) * `introductoryPrice` zeigt Details zum Einführungsangebot * `eligibility`-Methode prüft, ob Benutzer qualifiziert ist * Wird automatisch beim Kauf angewendet * Keine spezielle Kaufmethode erforderlich ### Einschränkungen [Section titled “Einschränkungen”](#einschränkungen) * Nur ein aktives Einführungsangebot pro Abonnement gleichzeitig * Kann nicht mit anderen Rabatttypen kombiniert werden * Berechtigungsregeln können nicht geändert werden * Apple kontrolliert die Berechtigungsprüfung ## Fehlerbehebung [Section titled “Fehlerbehebung”](#fehlerbehebung) **Einführungsangebot wird nicht angezeigt:** * Prüfen Sie, ob das Angebot in App Store Connect aktiviert ist * Überprüfen Sie, ob der Benutzer zuvor kein Einführungsangebot verwendet hat * Stellen Sie sicher, dass der Benutzer nichts in der Gruppe abonniert hat * Testen Sie mit neuem Sandbox-Konto **Berechtigungsprüfung schlägt fehl:** * Warten Sie auf App Store-Synchronisation (kann 2-3 Stunden dauern) * Überprüfen Sie, ob die Produkt-ID korrekt ist * Prüfen Sie die Abonnementgruppen-Konfiguration * Testen Sie in der Sandbox mit frischem Testkonto **Falscher Preis wird angezeigt:** * Prüfen Sie regionale Preiseinstellungen * Überprüfen Sie Währungsumrechnung * Stellen Sie sicher, dass Angebotsdaten aktuell sind * Produktinformationen aktualisieren **Sandbox-Testprobleme:** * Verwenden Sie beschleunigte Dauern (3 Min = 1 Woche) * Erstellen Sie neue Testkonten für jeden Test * Warten Sie, bis die Testversion natürlich abgeschlossen ist * Prüfen Sie Verlängerungsanzahl (max 6 in Sandbox) ## Analytics und Optimierung [Section titled “Analytics und Optimierung”](#analytics-und-optimierung) ### Diese Metriken verfolgen [Section titled “Diese Metriken verfolgen”](#diese-metriken-verfolgen) * Akzeptanzrate für Einführungsangebote * Konversionsrate von Testversion zu Bezahlung * Kündigungen während der Testversion * Verbleib nach erster Belastung * Umsatzauswirkungen ### A/B-Test-Ideen [Section titled “A/B-Test-Ideen”](#ab-test-ideen) * Kostenlose Testversion vs. bezahlte Einführung * Variationen der Testversionen-Länge * Rabattprozentsatz * Einmalzahlung vs. wiederkehrender Rabatt ### Optimierungsstrategie [Section titled “Optimierungsstrategie”](#optimierungsstrategie) ```typescript // Angebots-Performance verfolgen analytics.track('intro_offer_displayed', { product_id: product.identifier, offer_type: product.introductoryPriceType, offer_duration: product.introductoryPricePeriod }); analytics.track('intro_offer_accepted', { product_id: product.identifier }); // Konversion überwachen NativePurchases.addListener('transactionUpdated', (transaction) => { if (transaction.productIdentifier === product.identifier && transaction.isActive) { analytics.track('trial_converted', { transactionId: transaction.transactionId, productId: transaction.productIdentifier, }); } }); ``` ## Nächste Schritte [Section titled “Nächste Schritte”](#nächste-schritte) * [Sandbox-Tests konfigurieren](/docs/plugins/native-purchases/ios-sandbox-testing/), um Ihre Einführungsangebote zu testen * Richten Sie Werbeangebote für Rückgewinnungskampagnen ein * Implementieren Sie Abonnement-Analytics * Erstellen Sie gezielte Marketingkampagnen ## Zusätzliche Ressourcen [Section titled “Zusätzliche Ressourcen”](#zusätzliche-ressourcen) Weitere Details finden Sie in der [offiziellen Apple-Dokumentation zu Einführungsangeboten](https://developer.apple.com/documentation/storekit/in-app_purchase/subscriptions_and_offers/implementing_introductory_offers_in_your_app). # iOS Sandbox-Tests konfigurieren > Erfahren Sie, wie Sie Sandbox-Tests für In-App-Käufe auf iOS mit App Store Connect und Xcode einrichten. Das Testen von In-App-Käufen auf iOS erfordert eine ordnungsgemäße Konfiguration in App Store Connect und auf Ihren Testgeräten. Dieser Leitfaden deckt alles ab, was Sie für den Einstieg in Sandbox-Tests benötigen. ## Voraussetzungen [Section titled “Voraussetzungen”](#voraussetzungen) * **Apple Developer Program**: Aktive Mitgliedschaft mit jährlicher Verlängerung * **Vereinbarungen**: Signierte “Paid Applications Agreement” mit ausgefüllten Bank- und Steuerinformationen * **Xcode-Projekt**: Konfiguriert mit korrekter Bundle-Kennung und Funktionen Note Die Genehmigung der Bank- und Steuereinrichtung kann Stunden bis Tage dauern. Schließen Sie dies rechtzeitig vor dem Testen ab. ## Einrichtungsprozess [Section titled “Einrichtungsprozess”](#einrichtungsprozess) 1. **Paid Applications Agreement unterzeichnen** Navigieren Sie in App Store Connect zu **Vereinbarungen, Steuern und Banking** und vervollständigen Sie: * Unterzeichnen Sie die Paid Applications Agreement * Fügen Sie Ihre Bankinformationen hinzu * Füllen Sie die Steuerformulare aus Warten Sie auf die Genehmigung Ihrer Informationen durch Apple (dies kann 24-48 Stunden dauern). 2. **Sandbox-Testbenutzer erstellen** Gehen Sie in App Store Connect zu **Benutzer und Zugriff > Sandbox-Tester**. Klicken Sie auf die **+**-Schaltfläche, um einen neuen Sandbox-Tester zu erstellen. **Wichtig**: Verwenden Sie eine E-Mail-Adresse, die NICHT bereits mit einer Apple ID verknüpft ist. Sie können E-Mail-Aliase verwenden: * Gmail: `ihreemail+test@gmail.com` * iCloud: `ihreemail+test@icloud.com` ![Sandbox-Tester erstellen](/native-purchases/ios/sandbox-testing/sandbox-tester-setup.webp) 3. **Testgerät konfigurieren (iOS 12+)** Ab iOS 12 müssen Sie sich nicht mehr von Ihrem iTunes-Konto abmelden, um Käufe zu testen. Auf Ihrem iOS-Gerät: 1. Öffnen Sie **Einstellungen** 2. Tippen Sie auf **App Store** 3. Scrollen Sie nach unten 4. Tippen Sie auf **Sandbox-Konto** 5. Melden Sie sich mit Ihrem Sandbox-Testkonto an Tip Dies ist viel bequemer als die alte Methode, sich von Ihrem iTunes-Konto abzumelden! 4. **Xcode-Projekt konfigurieren** Stellen Sie sicher, dass Ihr Xcode-Projekt Folgendes hat: **Bundle-Kennung** * Muss mit der Kennung in Ihrem Developer Center übereinstimmen * Muss mit der Kennung in App Store Connect übereinstimmen **In-App-Kauf-Funktion** 1. Wählen Sie Ihr Projekt in Xcode aus 2. Gehen Sie zu **Signierung & Funktionen** 3. Klicken Sie auf **+ Funktion** 4. Fügen Sie **In-App-Kauf** hinzu 5. **In-App-Kauf-Produkte erstellen** Navigieren Sie in App Store Connect zu Ihrer App und erstellen Sie Ihre In-App-Kauf-Produkte (Abonnements, Verbrauchsgüter usw.). Produkte müssen mindestens im Status “Bereit zur Einreichung” sein für Sandbox-Tests. 6. **Ihre Implementierung testen** Erstellen und führen Sie Ihre App auf einem Testgerät aus. Wenn Sie einen Kauf versuchen, sollten Sie Folgendes sehen: > **\[Umgebung: Sandbox]** Diese Bestätigung zeigt an, dass Sie sich in der Sandbox-Umgebung befinden und kein echtes Geld belastet wird. ## Wichtige Hinweise [Section titled “Wichtige Hinweise”](#wichtige-hinweise) ### Sandbox-Umgebungsmerkmale [Section titled “Sandbox-Umgebungsmerkmale”](#sandbox-umgebungsmerkmale) * **Keine echten Belastungen**: Alle Käufe sind im Sandbox-Modus kostenlos * **Beschleunigte Abonnements**: Abonnementdauern werden für schnelleres Testen verkürzt * 1 Woche Abonnement = 3 Minuten * 1 Monat Abonnement = 5 Minuten * 2 Monate Abonnement = 10 Minuten * 3 Monate Abonnement = 15 Minuten * 6 Monate Abonnement = 30 Minuten * 1 Jahr Abonnement = 1 Stunde * **Auto-Verlängerungslimit**: Abonnements werden in der Sandbox bis zu 6-mal automatisch verlängert * **Sofortige Kündigung**: Gekündigte Abonnements laufen sofort ab ### Sandbox-Kontoverwaltung [Section titled “Sandbox-Kontoverwaltung”](#sandbox-kontoverwaltung) * Erstellen Sie mehrere Testkonten für verschiedene Szenarien * Verwenden Sie Testkonten nur auf Testgeräten * Verwenden Sie keine persönliche Apple ID für Sandbox-Tests * Testkonten können jedes Produkt unabhängig von der Region kaufen ## Sandbox-Tests verwenden [Section titled “Sandbox-Tests verwenden”](#sandbox-tests-verwenden) ```typescript import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases'; const { isBillingSupported } = await NativePurchases.isBillingSupported(); if (!isBillingSupported) { throw new Error('StoreKit wird auf diesem Gerät nicht unterstützt'); } // Produkte abrufen (verwendet automatisch Sandbox, wenn verfügbar) const { products } = await NativePurchases.getProducts({ productIdentifiers: ['premium_monthly'], productType: PURCHASE_TYPE.SUBS, }); // Testkauf durchführen const transaction = await NativePurchases.purchaseProduct({ productIdentifier: 'premium_monthly', productType: PURCHASE_TYPE.SUBS, }); console.log('Testkauf erfolgreich!', transaction.transactionId); ``` ## Überprüfung [Section titled “Überprüfung”](#überprüfung) Bei ordnungsgemäßer Konfiguration sollten Sie Folgendes beobachten: 1. **Sandbox-Banner** während des Kaufs: “\[Umgebung: Sandbox]” 2. **Produkte werden** erfolgreich geladen 3. **Käufe werden abgeschlossen** ohne tatsächliche Belastungen 4. **Belege werden** korrekt validiert 5. **Abonnements werden** automatisch verlängert (mit beschleunigter Rate) ## Fehlerbehebung [Section titled “Fehlerbehebung”](#fehlerbehebung) **Produkte werden nicht geladen:** * Überprüfen Sie, ob die Bundle-Kennung mit App Store Connect übereinstimmt * Prüfen Sie, ob Vereinbarungen unterzeichnet und genehmigt sind * Stellen Sie sicher, dass Produkte mindestens im Status “Bereit zur Einreichung” sind * Warten Sie 2-3 Stunden nach der Erstellung von Produkten **“Verbindung zum iTunes Store nicht möglich”:** * Überprüfen Sie, ob das Sandbox-Konto korrekt konfiguriert ist * Prüfen Sie, ob das Gerät mit dem Internet verbunden ist * Versuchen Sie, sich vom Sandbox-Konto ab- und wieder anzumelden * Starten Sie die App neu **Käufe schlagen stillschweigend fehl:** * Prüfen Sie die Xcode-Konsole auf Fehlermeldungen * Überprüfen Sie, ob die In-App-Kauf-Funktion aktiviert ist * Stellen Sie sicher, dass die E-Mail des Sandbox-Kontos keine echte Apple ID ist * Versuchen Sie, ein neues Sandbox-Testkonto zu erstellen **Belegvalidierungsfehler:** * Verwenden Sie den Sandbox-Belegvalidierungs-Endpunkt beim Testen * Produktions-Endpunkt: `https://buy.itunes.apple.com/verifyReceipt` * Sandbox-Endpunkt: `https://sandbox.itunes.apple.com/verifyReceipt` * Das native-purchases Plugin behandelt dies automatisch **Falsche Abonnementdauer:** * Denken Sie daran, dass Abonnements in der Sandbox beschleunigt sind * Verwenden Sie die obige Umrechnungstabelle für erwartete Dauern * Abonnements werden in der Sandbox maximal 6-mal automatisch verlängert **“Diese Apple ID wurde noch nicht im iTunes Store verwendet”:** * Dies ist bei neuen Sandbox-Konten normal * Fahren Sie mit dem Kauf fort, um das Konto zu aktivieren * Tritt nur bei der ersten Verwendung auf ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) 1. **Erstellen Sie mehrere Testkonten** für verschiedene Testszenarien 2. **Testen Sie alle Abonnementdauern**, um das Verhalten zu überprüfen 3. **Testen Sie Kündigungs- und Verlängerungs**-Abläufe 4. **Überprüfen Sie, ob die Belegvalidierung** korrekt funktioniert 5. **Testen Sie die Wiederherstellung von Käufen**-Funktionalität 6. **Prüfen Sie das Verhalten von Abonnement-Upgrade/Downgrade** 7. **Testen Sie bei schlechten Netzwerkbedingungen** ## Produktion vs. Sandbox [Section titled “Produktion vs. Sandbox”](#produktion-vs-sandbox) | Funktion | Sandbox | Produktion | | ----------------------- | ------------ | ------------------ | | Echte Belastungen | Nein | Ja | | Abonnementdauer | Beschleunigt | Normal | | Auto-Verlängerungslimit | 6-mal | Unbegrenzt | | Kündigungseffekt | Sofort | Ende des Zeitraums | | Beleg-Endpunkt | Sandbox-URL | Produktions-URL | | Nur Testkonten | Ja | Nein | ## Zusätzliche Ressourcen [Section titled “Zusätzliche Ressourcen”](#zusätzliche-ressourcen) Weitere Details finden Sie in der [offiziellen Apple StoreKit-Dokumentation](https://developer.apple.com/documentation/storekit/in-app_purchase/testing_in-app_purchases_with_sandbox) zu Sandbox-Tests. # iOS-Abonnementgruppe erstellen > Erfahren Sie, wie Sie Abonnementgruppen in App Store Connect erstellen und konfigurieren, um die Abonnementangebote Ihrer App zu organisieren. Abonnementgruppen sind für die Organisation und Verwaltung mehrerer Abonnementstufen in Ihrer iOS-App unerlässlich. Das Verständnis ihrer Funktionsweise ist entscheidend für die Implementierung von Upgrade-, Downgrade- und Crossgrade-Funktionen. ## Was ist eine Abonnementgruppe? [Section titled “Was ist eine Abonnementgruppe?”](#was-ist-eine-abonnementgruppe) Eine Abonnementgruppe ist eine Sammlung verwandter Abonnements, zwischen denen Benutzer wählen können. Benutzer können jeweils nur ein Abonnement innerhalb einer Gruppe abonnieren. Wenn sie Abonnements wechseln, handhabt Apple den Übergang automatisch. ## Warum Abonnementgruppen wichtig sind [Section titled “Warum Abonnementgruppen wichtig sind”](#warum-abonnementgruppen-wichtig-sind) Abonnementgruppen ermöglichen: * **Gestaffelte Preise**: Bieten Sie Basic-, Premium- und Ultimate-Pläne an * **Verschiedene Laufzeiten**: Monatliche, jährliche und Lifetime-Optionen * **Upgrade/Downgrade-Logik**: Automatische Handhabung von Abonnementwechseln * **Vereinfachte Verwaltung**: Gruppieren Sie verwandte Abonnements zusammen ## Abonnementstufen [Section titled “Abonnementstufen”](#abonnementstufen) Innerhalb einer Gruppe sollte jedes Abonnement vom höchsten Wert (Stufe 1) zum niedrigsten Wert eingestuft werden. Diese Rangfolge bestimmt, wie Abonnementwechsel klassifiziert werden: ![Abonnementgruppen-Hierarchie](/native-purchases/ios/subscription-group/subscription-group-hierarchy.webp) ### Stufenbeispiele [Section titled “Stufenbeispiele”](#stufenbeispiele) **Stufe 1** (Höchster Wert) * Premium Jährlich (99,99 €/Jahr) * Ultimate Monatlich (19,99 €/Monat) **Stufe 2** (Mittlerer Wert) * Standard Jährlich (49,99 €/Jahr) * Premium Monatlich (9,99 €/Monat) **Stufe 3** (Niedrigster Wert) * Basic Jährlich (29,99 €/Jahr) * Standard Monatlich (4,99 €/Monat) ## Arten von Abonnementwechseln [Section titled “Arten von Abonnementwechseln”](#arten-von-abonnementwechseln) Apple handhabt automatisch drei Arten von Abonnementwechseln basierend auf der Stufenrangfolge: ### 1. Upgrade [Section titled “1. Upgrade”](#1-upgrade) Wechsel zu einem **höherwertigen** Abonnement (z. B. Stufe 2 → Stufe 1). **Verhalten:** * Tritt **sofort** in Kraft * Benutzer erhält **anteilige Rückerstattung** für verbleibende Zeit * Neues Abonnement beginnt sofort **Beispiel:** ```typescript // Benutzer hat derzeit: Standard Monatlich (Stufe 2) // Benutzer upgradet auf: Premium Jährlich (Stufe 1) // Ergebnis: Sofortiger Zugang zu Premium, Rückerstattung für ungenutzte Standard-Zeit ``` ### 2. Downgrade [Section titled “2. Downgrade”](#2-downgrade) Wechsel zu einem **niedrigerwertigen** Abonnement (z. B. Stufe 1 → Stufe 2). **Verhalten:** * Tritt am **nächsten Verlängerungsdatum** in Kraft * Benutzer behält aktuelles Abonnement bis zum Ende des Zeitraums * Neues Abonnement beginnt automatisch nach Ablauf **Beispiel:** ```typescript // Benutzer hat derzeit: Premium Jährlich (Stufe 1) // Benutzer downgradet auf: Standard Monatlich (Stufe 2) // Ergebnis: Premium-Zugang bleibt bis zum jährlichen Verlängerungsdatum, dann Wechsel ``` ### 3. Crossgrade [Section titled “3. Crossgrade”](#3-crossgrade) Wechsel zu einem anderen Abonnement **auf derselben Stufe**. **Verhalten hängt von der Laufzeit ab:** **Verschiedene Laufzeit** → Verhält sich wie **Downgrade** * Tritt am nächsten Verlängerungsdatum in Kraft * Beispiel: Monatlich Premium (Stufe 1) → Jährlich Premium (Stufe 1) **Gleiche Laufzeit** → Verhält sich wie **Upgrade** * Tritt sofort in Kraft * Beispiel: Premium Monatlich (Stufe 1) → Ultimate Monatlich (Stufe 1) ## Erstellen einer Abonnementgruppe [Section titled “Erstellen einer Abonnementgruppe”](#erstellen-einer-abonnementgruppe) 1. **Zu Abonnements navigieren** Wählen Sie in App Store Connect Ihre App aus und gehen Sie zu **Monetarisierung > Abonnements**. 2. **Gruppe erstellen** Klicken Sie auf **+** neben “Abonnementgruppen”, um eine neue Gruppe zu erstellen. 3. **Gruppe benennen** Wählen Sie einen beschreibenden Namen, der die enthaltenen Abonnements widerspiegelt: * “Premium-Zugang” * “Cloud-Speicher-Pläne” * “Pro-Funktionen” 4. **Abonnements hinzufügen** Nach dem Erstellen der Gruppe fügen Sie ihr einzelne Abonnements hinzu. Jedes Abonnement erhält eine Stufenrangfolge. 5. **Stufenrangfolgen festlegen** Ordnen Sie Abonnements vom höchsten Wert (1) zum niedrigsten Wert. Berücksichtigen Sie: * Jahrespläne rangieren typischerweise höher als monatliche * Höherpreisige Stufen rangieren über niedrigpreisigen * Ultimate/Premium-Stufen rangieren am höchsten ## Verwendung in Ihrer App [Section titled “Verwendung in Ihrer App”](#verwendung-in-ihrer-app) Das native-purchases Plugin handhabt automatisch die Abonnementgruppen-Logik: ```typescript import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases'; // Alle Abonnements in einer Gruppe abrufen const { products } = await NativePurchases.getProducts({ productIdentifiers: ['premium_monthly', 'premium_annual', 'ultimate_monthly'], productType: PURCHASE_TYPE.SUBS, }); // Aktuelles Abonnement mit StoreKit-Transaktionen anzeigen const { purchases } = await NativePurchases.getPurchases({ productType: PURCHASE_TYPE.SUBS, }); const activeSubs = purchases.filter((purchase) => purchase.isActive); // Anstehendes Downgrade/Kündigung erkennen (StoreKit setzt willCancel === true) const pendingChange = purchases.find((purchase) => purchase.willCancel === true); if (pendingChange) { console.log('Abonnement wird am folgenden Datum die automatische Verlängerung einstellen', pendingChange.expirationDate); } // Kauf (StoreKit handhabt Upgrades/Downgrades automatisch) await NativePurchases.purchaseProduct({ productIdentifier: 'premium_annual', productType: PURCHASE_TYPE.SUBS, }); // Auf StoreKit-Updates lauschen (wird bei Upgrades/Downgrades/Rückerstattungen ausgelöst) NativePurchases.addListener('transactionUpdated', (transaction) => { console.log('Abonnement aktualisiert:', transaction); }); ``` ## Handhabung von Abonnementwechseln [Section titled “Handhabung von Abonnementwechseln”](#handhabung-von-abonnementwechseln) ### Wechseltyp erkennen [Section titled “Wechseltyp erkennen”](#wechseltyp-erkennen) ```typescript import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases'; // Aktuelle Abonnementinformationen abrufen const { purchases } = await NativePurchases.getPurchases({ productType: PURCHASE_TYPE.SUBS, }); const currentSubscription = purchases.find( (purchase) => purchase.subscriptionState === 'subscribed', ); if (currentSubscription) { // StoreKit meldet, ob Benutzer Auto-Verlängerung gekündigt hat if (currentSubscription.willCancel) { console.log( `Benutzer hat gekündigt. Zugang bleibt bis ${currentSubscription.expirationDate}`, ); } if (currentSubscription.isUpgraded) { console.log('Benutzer hat kürzlich auf diesen Plan upgegradet.'); } } // Auf automatische Upgrades/Downgrades lauschen NativePurchases.addListener('transactionUpdated', (transaction) => { console.log('Abonnement geändert!', transaction); if (transaction.subscriptionState === 'revoked') { revokeAccess(); } else if (transaction.isActive) { unlockPremiumFeatures(); } }); ``` ### Benutzerkommunikation [Section titled “Benutzerkommunikation”](#benutzerkommunikation) Kommunizieren Sie das Wechselverhalten immer klar: **Für Upgrades:** > “Sie erhalten sofortigen Zugang zu Premium-Funktionen. Wir erstatten Ihr aktuelles Abonnement anteilig.” **Für Downgrades:** > “Sie behalten Premium-Zugang bis \[Verlängerungsdatum], dann wechseln Sie zu Standard.” **Für Crossgrades:** > “Ihr Plan wechselt zur jährlichen Abrechnung bei der nächsten Verlängerung am \[Datum].” ## Server-Überwachung [Section titled “Server-Überwachung”](#server-überwachung) Verwenden Sie Apples App Store Server Notifications v2 oder Ihr eigenes Beleg-Validierungs-Backend, um StoreKit-Änderungen in Ihrer Datenbank zu spiegeln. Kombinieren Sie Server-Benachrichtigungen mit dem `transactionUpdated`-Listener, damit sowohl Client als auch Backend synchron bleiben. ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) ### Gruppenorganisation [Section titled “Gruppenorganisation”](#gruppenorganisation) * Halten Sie verwandte Abonnements in derselben Gruppe * Mischen Sie keine unabhängigen Funktionen (z. B. Speicher und Werbung-Entfernung) * Erstellen Sie separate Gruppen für verschiedene Funktionssätze ### Stufenrangfolgen-Strategie [Section titled “Stufenrangfolgen-Strategie”](#stufenrangfolgen-strategie) * Jahrespläne → Höhere Stufe als monatlich (für dieselbe Stufe) * Höherpreisige Stufen → Höhere Stufe * Berücksichtigen Sie den Wert, nicht nur den Preis ### Benutzererfahrung [Section titled “Benutzererfahrung”](#benutzererfahrung) * Zeigen Sie aktuelles Abonnement klar an * Zeigen Sie alle verfügbaren Optionen in der Gruppe an * Geben Sie an, welche Wechsel sofort vs. bei Verlängerung erfolgen * Ermöglichen Sie einfachen Wechsel zwischen Plänen ### Testen [Section titled “Testen”](#testen) * Testen Sie alle Upgrade-Szenarien * Testen Sie alle Downgrade-Szenarien * Überprüfen Sie Crossgrade-Verhalten * Prüfen Sie Webhook-Auslösung ## Gängige Szenarien [Section titled “Gängige Szenarien”](#gängige-szenarien) ### Szenario 1: Drei-Stufen-Monatspläne [Section titled “Szenario 1: Drei-Stufen-Monatspläne”](#szenario-1-drei-stufen-monatspläne) ```plaintext Stufe 1: Ultimate Monatlich (19,99 €) Stufe 2: Premium Monatlich (9,99 €) Stufe 3: Basic Monatlich (4,99 €) ``` * Basic → Premium: Upgrade (sofort) * Premium → Ultimate: Upgrade (sofort) * Ultimate → Premium: Downgrade (bei Verlängerung) * Basic → Ultimate: Upgrade (sofort) ### Szenario 2: Gemischte Laufzeitpläne [Section titled “Szenario 2: Gemischte Laufzeitpläne”](#szenario-2-gemischte-laufzeitpläne) ```plaintext Stufe 1: Premium Jährlich (99,99 €/Jahr) Stufe 2: Premium Monatlich (9,99 €/Monat) ``` * Monatlich → Jährlich: Crossgrade (bei Verlängerung) * Jährlich → Monatlich: Downgrade (bei Verlängerung) ### Szenario 3: Multi-Stufen Multi-Laufzeit [Section titled “Szenario 3: Multi-Stufen Multi-Laufzeit”](#szenario-3-multi-stufen-multi-laufzeit) ```plaintext Stufe 1: Ultimate Jährlich (199 €/Jahr) Stufe 2: Ultimate Monatlich (19,99 €/Monat) Stufe 3: Premium Jährlich (99 €/Jahr) Stufe 4: Premium Monatlich (9,99 €/Monat) Stufe 5: Basic Jährlich (49 €/Jahr) Stufe 6: Basic Monatlich (4,99 €/Monat) ``` Diese Einrichtung bietet maximale Flexibilität bei gleichzeitiger Beibehaltung klarer Upgrade/Downgrade-Logik. ## Fehlerbehebung [Section titled “Fehlerbehebung”](#fehlerbehebung) **Abonnement erscheint nicht in der Gruppe:** * Überprüfen Sie, ob es der richtigen Gruppe zugewiesen ist * Prüfen Sie, ob es mindestens im Status “Bereit zur Einreichung” ist * Stellen Sie sicher, dass die Produkt-ID korrekt ist **Falsches Upgrade/Downgrade-Verhalten:** * Überprüfen Sie Stufenrangfolgen (1 = höchste) * Überprüfen Sie, ob Abonnementstufen sinnvoll sind * Prüfen Sie, ob Stufen korrekt gesetzt sind **Produkte aus verschiedenen Gruppen:** * Benutzer können gleichzeitig mehrere Gruppen abonnieren * Dies ist beabsichtigt - halten Sie verwandte Produkte in derselben Gruppe **getActiveProducts zeigt mehrere Abonnements:** * Prüfen Sie, ob Abonnements in verschiedenen Gruppen sind * Überprüfen Sie, ob Benutzer über Familienfreigabe abonniert ist * Überprüfen Sie den Abonnementstatus in App Store Connect ## Zusätzliche Ressourcen [Section titled “Zusätzliche Ressourcen”](#zusätzliche-ressourcen) Weitere Details finden Sie in der [offiziellen Apple-Dokumentation zu Abonnementgruppen](https://developer.apple.com/app-store/subscriptions/). # @capgo/nativegeocoder > Wandeln Sie zwischen Adressen und geografischen Koordinaten um, indem Sie native Plattform-Geocoding-APIs für präzise Standortdaten verwenden. Forward Geocoding Wandeln Sie Adressen in geografische Koordinaten um 📍 Reverse Geocoding Wandeln Sie Koordinaten in menschenlesbare Adressen um 🏠 Native Genauigkeit Verwendet plattformatives Geocoding für beste Ergebnisse 🎯 Umfassende Dokumentation Schauen Sie sich die [Dokumentation](/docs/plugins/nativegeocoder/getting-started/) an, um das Plugin in nur wenigen Minuten zu meistern. # Erste Schritte > Erfahren Sie, wie Sie das Native Geocoder-Plugin für die Umwandlung von Adressen und Koordinaten in Ihrer Capacitor-App installieren und verwenden. 1. **Paket installieren** * npm ```sh npm i @capgo/nativegeocoder ``` * pnpm ```sh pnpm add @capgo/nativegeocoder ``` * yarn ```sh yarn add @capgo/nativegeocoder ``` * bun ```sh bun add @capgo/nativegeocoder ``` 2. **Mit nativen Projekten synchronisieren** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` 3. **Berechtigungen konfigurieren** ### iOS [Section titled “iOS”](#ios) Fügen Sie die Standortnutzungsbeschreibung zu Ihrer `Info.plist` hinzu: ```xml NSLocationWhenInUseUsageDescription Um Adressen in Koordinaten umzuwandeln ``` ### Android [Section titled “Android”](#android) Fügen Sie Berechtigungen zu Ihrer `AndroidManifest.xml` hinzu: ```xml ``` ## Verwendung [Section titled “Verwendung”](#verwendung) Importieren Sie das Plugin und verwenden Sie seine Geocoding-Methoden: ```typescript import { NativeGeocoder } from '@capgo/nativegeocoder'; // Forward Geocoding: Adresse zu Koordinaten const forwardGeocode = async () => { const results = await NativeGeocoder.forwardGeocode({ addressString: '1600 Amphitheatre Parkway, Mountain View, CA', useLocale: true, maxResults: 1 }); const location = results.addresses[0]; console.log('Breitengrad:', location.latitude); console.log('Längengrad:', location.longitude); }; // Reverse Geocoding: Koordinaten zu Adresse const reverseGeocode = async () => { const results = await NativeGeocoder.reverseGeocode({ latitude: 37.4220656, longitude: -122.0840897, useLocale: true, maxResults: 1 }); const address = results.addresses[0]; console.log('Straße:', address.thoroughfare); console.log('Stadt:', address.locality); console.log('Land:', address.countryName); }; ``` ## API-Referenz [Section titled “API-Referenz”](#api-referenz) ### forwardGeocode(options) [Section titled “forwardGeocode(options)”](#forwardgeocodeoptions) Wandelt einen Adressstring in geografische Koordinaten um. ```typescript interface ForwardGeocodeOptions { addressString: string; useLocale?: boolean; maxResults?: number; apiKey?: string; // Nur Android } interface GeocodeResult { addresses: Address[]; } interface Address { latitude: number; longitude: number; countryCode?: string; countryName?: string; postalCode?: string; administrativeArea?: string; subAdministrativeArea?: string; locality?: string; subLocality?: string; thoroughfare?: string; subThoroughfare?: string; } ``` ### reverseGeocode(options) [Section titled “reverseGeocode(options)”](#reversegeocodeoptions) Wandelt geografische Koordinaten in Adressinformationen um. ```typescript interface ReverseGeocodeOptions { latitude: number; longitude: number; useLocale?: boolean; maxResults?: number; apiKey?: string; // Nur Android } ``` ## Vollständige Beispiele [Section titled “Vollständige Beispiele”](#vollständige-beispiele) ### Adresssuche mit Fehlerbehandlung [Section titled “Adresssuche mit Fehlerbehandlung”](#adresssuche-mit-fehlerbehandlung) ```typescript import { NativeGeocoder } from '@capgo/nativegeocoder'; export class GeocodingService { async searchAddress(address: string): Promise<{lat: number, lng: number} | null> { try { const results = await NativeGeocoder.forwardGeocode({ addressString: address, useLocale: true, maxResults: 5 }); if (results.addresses.length > 0) { const location = results.addresses[0]; return { lat: location.latitude, lng: location.longitude }; } return null; } catch (error) { console.error('Geocoding fehlgeschlagen:', error); return null; } } async getAddressFromCoordinates(lat: number, lng: number): Promise { try { const results = await NativeGeocoder.reverseGeocode({ latitude: lat, longitude: lng, useLocale: true, maxResults: 1 }); if (results.addresses.length > 0) { const address = results.addresses[0]; return this.formatAddress(address); } return null; } catch (error) { console.error('Reverse Geocoding fehlgeschlagen:', error); return null; } } private formatAddress(address: Address): string { const parts = [ address.subThoroughfare, address.thoroughfare, address.locality, address.administrativeArea, address.postalCode, address.countryName ].filter(part => part != null && part !== ''); return parts.join(', '); } } ``` ### Standortauswahl-Komponente [Section titled “Standortauswahl-Komponente”](#standortauswahl-komponente) ```typescript import { NativeGeocoder } from '@capgo/nativegeocoder'; import { Geolocation } from '@capacitor/geolocation'; export class LocationPicker { currentLocation: { lat: number; lng: number } | null = null; currentAddress: string = ''; async getCurrentLocation() { try { // Aktuelle Koordinaten abrufen const position = await Geolocation.getCurrentPosition(); this.currentLocation = { lat: position.coords.latitude, lng: position.coords.longitude }; // Adresse für Koordinaten abrufen const results = await NativeGeocoder.reverseGeocode({ latitude: this.currentLocation.lat, longitude: this.currentLocation.lng, useLocale: true, maxResults: 1 }); if (results.addresses.length > 0) { const address = results.addresses[0]; this.currentAddress = [ address.thoroughfare, address.locality, address.countryName ].filter(Boolean).join(', '); } } catch (error) { console.error('Standort konnte nicht abgerufen werden:', error); } } async searchLocation(query: string) { try { const results = await NativeGeocoder.forwardGeocode({ addressString: query, useLocale: true, maxResults: 10 }); return results.addresses.map(address => ({ coordinates: { lat: address.latitude, lng: address.longitude }, displayName: this.formatDisplayName(address) })); } catch (error) { console.error('Suche fehlgeschlagen:', error); return []; } } private formatDisplayName(address: Address): string { const mainPart = [ address.thoroughfare, address.locality ].filter(Boolean).join(', '); const subPart = [ address.administrativeArea, address.countryName ].filter(Boolean).join(', '); return mainPart + (subPart ? ` (${subPart})` : ''); } } ``` ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) 1. **Zuerst Berechtigungen anfordern** ```typescript import { Geolocation } from '@capacitor/geolocation'; const requestPermissions = async () => { const permissions = await Geolocation.requestPermissions(); if (permissions.location !== 'granted') { throw new Error('Standortberechtigung erforderlich'); } }; ``` 2. **Fehler graceful behandeln** ```typescript try { const results = await NativeGeocoder.forwardGeocode({ addressString: address }); } catch (error) { // Spezifische Fehlerfälle behandeln if (error.message.includes('network')) { console.error('Netzwerkfehler'); } else if (error.message.includes('permission')) { console.error('Berechtigung verweigert'); } } ``` 3. **maxResults weise verwenden** * Für Benutzersuche: Verwenden Sie 5-10 Ergebnisse * Für automatische Konvertierung: Verwenden Sie 1 Ergebnis * Mehr Ergebnisse = langsamere Antwort 4. **Ergebnisse wenn möglich cachen** ```typescript const geocodeCache = new Map(); async function geocodeWithCache(address: string) { if (geocodeCache.has(address)) { return geocodeCache.get(address); } const result = await NativeGeocoder.forwardGeocode({ addressString: address }); geocodeCache.set(address, result); return result; } ``` ## Plattformunterschiede [Section titled “Plattformunterschiede”](#plattformunterschiede) ### iOS [Section titled “iOS”](#ios-1) * Verwendet `CLGeocoder` aus CoreLocation * Kein API-Schlüssel erforderlich * Respektiert automatisch das Gebietsschema des Benutzers ### Android [Section titled “Android”](#android-1) * Verwendet Android Geocoder API * Optionaler Google API-Schlüssel für bessere Ergebnisse * Kann auf den Webservice von Google zurückgreifen ### API-Schlüssel-Konfiguration (Android) [Section titled “API-Schlüssel-Konfiguration (Android)”](#api-schlüssel-konfiguration-android) Für bessere Ergebnisse auf Android können Sie einen Google API-Schlüssel bereitstellen: ```typescript await NativeGeocoder.forwardGeocode({ addressString: address, apiKey: 'IHR_GOOGLE_API_SCHLÜSSEL' // Nur Android }); ``` ## Häufige Probleme [Section titled “Häufige Probleme”](#häufige-probleme) 1. **Keine Ergebnisse zurückgegeben** * Internetverbindung prüfen * Adressformat überprüfen * Mit allgemeinerer Adresse versuchen 2. **Berechtigungsfehler** * Sicherstellen, dass Standortberechtigungen gewährt wurden * Info.plist/AndroidManifest.xml prüfen 3. **Ungenaue Ergebnisse** * Spezifischere Adressen verwenden * Postleitzahlen einbeziehen, wenn verfügbar * Koordinaten für präzise Standorte in Betracht ziehen # @capgo/capacitor-navigation-bar > Legen Sie benutzerdefinierte Farben für die Android-Navigationsleiste fest, um das Theme Ihrer App anzupassen und ein zusammenhängendes visuelles Erlebnis zu schaffen. Android-Anpassung Volle Kontrolle über das Erscheinungsbild der Android-Navigationsleiste 🎨 Dynamisches Theming Ändern Sie Farben dynamisch, um das Theme Ihrer App anzupassen 🌈 Hell/Dunkel-Unterstützung Unterstützung für helle und dunkle Navigationsleistenstile 🌓 Umfassende Dokumentation Schauen Sie sich die [Dokumentation](/docs/plugins/navigation-bar/getting-started/) an, um das Plugin in nur wenigen Minuten zu meistern. # Erste Schritte > Erfahren Sie, wie Sie das Capacitor Navigation Bar-Plugin installieren und konfigurieren, um die Farbe und den Stil der Android-Navigationsleiste anzupassen. 1. **Paket installieren** * npm ```sh npm i @capgo/capacitor-navigation-bar ``` * pnpm ```sh pnpm add @capgo/capacitor-navigation-bar ``` * yarn ```sh yarn add @capgo/capacitor-navigation-bar ``` * bun ```sh bun add @capgo/capacitor-navigation-bar ``` 2. **Mit nativen Projekten synchronisieren** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` 3. **Plugin konfigurieren** **Navigationsleistenfarbe festlegen:** ```typescript import { NavigationBar } from '@capgo/capacitor-navigation-bar'; // Navigationsleistenfarbe festlegen await NavigationBar.setColor({ color: '#FF0000', // Rote Farbe darkButtons: false // Helle Schaltflächen verwenden }); ``` **Dynamische Theme-Unterstützung:** ```typescript // Farbe basierend auf Theme festlegen const isDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches; await NavigationBar.setColor({ color: isDarkMode ? '#000000' : '#FFFFFF', darkButtons: !isDarkMode }); ``` * Android **Mindestanforderungen:** * Android 5.0 (API-Level 21) oder höher * Das Plugin funktioniert nur auf Android-Geräten Keine zusätzliche Einrichtung in AndroidManifest.xml erforderlich. * iOS Dieses Plugin ist nur für Android. iOS hat keine anpassbare Navigationsleiste. 4. **Erweiterte Verwendung** ```typescript import { NavigationBar } from '@capgo/capacitor-navigation-bar'; export class ThemeService { // Transparente Navigationsleiste festlegen async setTransparent() { await NavigationBar.setColor({ color: '#00000000', // Transparent darkButtons: true }); } // App-Theme anpassen async matchAppTheme(primaryColor: string, isLight: boolean) { await NavigationBar.setColor({ color: primaryColor, darkButtons: isLight }); } // Aktuelle Navigationsleistenfarbe abrufen async getCurrentColor() { const result = await NavigationBar.getColor(); console.log('Aktuelle Farbe:', result.color); return result.color; } // Auf Standard zurücksetzen async resetToDefault() { await NavigationBar.setColor({ color: '#000000', darkButtons: false }); } } ``` 5. **Integration mit App-Lebenszyklus** ```typescript import { App } from '@capacitor/app'; import { NavigationBar } from '@capgo/capacitor-navigation-bar'; // Navigationsleiste bei App-Statusänderungen aktualisieren App.addListener('appStateChange', async ({ isActive }) => { if (isActive) { // Navigationsleistenfarbe Ihrer App wiederherstellen await NavigationBar.setColor({ color: '#IhreAppFarbe', darkButtons: true }); } }); // Theme-Änderungen behandeln window.matchMedia('(prefers-color-scheme: dark)') .addEventListener('change', async (e) => { await NavigationBar.setColor({ color: e.matches ? '#121212' : '#FFFFFF', darkButtons: !e.matches }); }); ``` ## API-Referenz [Section titled “API-Referenz”](#api-referenz) ### Methoden [Section titled “Methoden”](#methoden) #### `setColor(options: ColorOptions)` [Section titled “setColor(options: ColorOptions)”](#setcoloroptions-coloroptions) Navigationsleistenfarbe und Schaltflächenstil festlegen. **Parameter:** * `options`: Konfigurationsobjekt * `color`: string - Hex-Farbcode (z. B. ‘#FF0000’) * `darkButtons`: boolean - Dunkle Schaltflächen (true) oder helle Schaltflächen (false) verwenden **Rückgabe:** `Promise` #### `getColor()` [Section titled “getColor()”](#getcolor) Aktuelle Navigationsleistenfarbe abrufen. **Rückgabe:** `Promise<{ color: string }>` ### Interfaces [Section titled “Interfaces”](#interfaces) ```typescript interface ColorOptions { color: string; // Hex-Farbcode darkButtons: boolean; // Schaltflächenstil } interface ColorResult { color: string; // Aktueller Hex-Farbcode } ``` ## Plattformhinweise [Section titled “Plattformhinweise”](#plattformhinweise) ### Android [Section titled “Android”](#android) * Erfordert Android 5.0 (API-Level 21) oder höher * Farbänderungen erfolgen sofort * Transparente Farben werden unterstützt (Alpha-Kanal verwenden) * Die Navigationsleiste ist möglicherweise auf Geräten mit Gestennavigation nicht sichtbar ### iOS [Section titled “iOS”](#ios) * Dieses Plugin hat keine Auswirkungen auf iOS-Geräte * iOS bietet keine APIs zur Anpassung der Systemnavigation ## Gängige Anwendungsfälle [Section titled “Gängige Anwendungsfälle”](#gängige-anwendungsfälle) 1. **Markenkonsistenz**: Navigationsleiste an Primärfarbe Ihrer App anpassen 2. **Theme-Unterstützung**: Dynamisch an helle/dunkle Themes anpassen 3. **Immersive Erlebnisse**: Transparente Navigation für Vollbildinhalte verwenden 4. **Statusanzeige**: Farbe ändern, um App-Zustände anzuzeigen (Aufnahme, Fehler usw.) ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) 1. **Farbkontrast**: Ausreichenden Kontrast zwischen Navigationsleiste und Schaltflächen sicherstellen ```typescript // Gute Kontrastbeispiele setColor({ color: '#FFFFFF', darkButtons: true }); // Weiße Leiste, dunkle Schaltflächen setColor({ color: '#000000', darkButtons: false }); // Schwarze Leiste, helle Schaltflächen ``` 2. **Theme-Konsistenz**: Navigationsleiste aktualisieren, wenn sich das Theme ändert 3. **Barrierefreiheit**: Benutzer mit Sehbehinderungen bei der Farbauswahl berücksichtigen 4. **Leistung**: Häufige Farbwechsel vermeiden, die Benutzer ablenken könnten ## Fehlerbehebung [Section titled “Fehlerbehebung”](#fehlerbehebung) **Navigationsleiste ändert sich nicht:** * Überprüfen Sie, ob das Gerät Android 5.0+ ausführt * Prüfen Sie, ob das Gerät Gestennavigation aktiviert hat * Sicherstellen, dass das Farbformat ein gültiger Hex-Code ist (z. B. ‘#FF0000’) **Schaltflächen nicht sichtbar:** * Überprüfen Sie, ob der `darkButtons`-Parameter zu Ihrer Hintergrundfarbe passt * Helle Hintergründe benötigen `darkButtons: true` * Dunkle Hintergründe benötigen `darkButtons: false` **Farbe erscheint anders:** * Einige Android-Versionen können die Farbe leicht modifizieren * Verwenden Sie vollständig opake Farben für beste Ergebnisse # @capgo/capacitor-nfc > Lesen und schreiben Sie NFC-Tags mit nativer Unterstützung für iOS und Android, um kontaktlose Interaktionen und Smart-Tag-Erlebnisse zu ermöglichen. Tag-Erkennung Erkennen Sie NFC-Tags automatisch, wenn sie sich dem Gerät nähern 📡 NDEF-Datensätze lesen Lesen Sie strukturierte Daten von NFC-Tags einschließlich Text, URLs und benutzerdefinierten Payloads 📖 Auf Tags schreiben Schreiben Sie NDEF-Nachrichten auf kompatible NFC-Tags 📝 Tag-Formatierung Formatieren Sie NDEF-formatierbare Tags zum Schreiben von Daten 🔧 Peer-to-Peer Teilen Sie Daten zwischen Geräten mit Android Beam (nur Android) 📲 Plattformübergreifend Funktioniert auf iOS und Android mit konsistenter API 📱 # Erste Schritte > Erfahren Sie, wie Sie das NFC-Plugin installieren und verwenden, um NFC-Tags in Ihrer Capacitor-App zu lesen und zu schreiben. 1. **Paket installieren** * npm ```sh npm i @capgo/capacitor-nfc ``` * pnpm ```sh pnpm add @capgo/capacitor-nfc ``` * yarn ```sh yarn add @capgo/capacitor-nfc ``` * bun ```sh bun add @capgo/capacitor-nfc ``` 2. **Mit nativen Projekten synchronisieren** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Konfiguration [Section titled “Konfiguration”](#konfiguration) ### iOS-Konfiguration [Section titled “iOS-Konfiguration”](#ios-konfiguration) Fügen Sie die NFC-Verwendungsbeschreibung zu Ihrer `Info.plist` hinzu: ```xml NFCReaderUsageDescription Diese App benötigt NFC-Zugriff zum Lesen und Schreiben von Tags ``` Aktivieren Sie die Near Field Communication Tag Reading-Funktion in Ihrem Xcode-Projekt. ### Android-Konfiguration [Section titled “Android-Konfiguration”](#android-konfiguration) Fügen Sie die NFC-Berechtigung zu Ihrer `AndroidManifest.xml` hinzu: ```xml ``` ## Verwendung [Section titled “Verwendung”](#verwendung) ### Scannen nach NFC-Tags starten [Section titled “Scannen nach NFC-Tags starten”](#scannen-nach-nfc-tags-starten) ```typescript import { CapacitorNfc } from '@capgo/capacitor-nfc'; await CapacitorNfc.startScanning({ invalidateAfterFirstRead: false, // Sitzung offen halten (iOS) alertMessage: 'Halten Sie ein Tag in die Nähe Ihres Geräts.', }); const listener = await CapacitorNfc.addListener('nfcEvent', (event) => { console.log('Tag erkannt:', event.type); console.log('Tag-ID:', event.tag?.id); console.log('NDEF-Nachricht:', event.tag?.ndefMessage); }); ``` ### NFC-Tag lesen [Section titled “NFC-Tag lesen”](#nfc-tag-lesen) ```typescript await CapacitorNfc.addListener('nfcEvent', (event) => { if (event.tag?.ndefMessage) { event.tag.ndefMessage.forEach(record => { console.log('TNF:', record.tnf); console.log('Typ:', record.type); console.log('Payload:', record.payload); // Text-Datensatz dekodieren if (record.tnf === 1 && record.type[0] === 0x54) { // Text-Datensatz const langLen = record.payload[0] & 0x3f; const text = new TextDecoder().decode( new Uint8Array(record.payload.slice(langLen + 1)) ); console.log('Text:', text); } }); } }); ``` ### Auf NFC-Tag schreiben [Section titled “Auf NFC-Tag schreiben”](#auf-nfc-tag-schreiben) ```typescript // Text-Datensatz vorbereiten const encoder = new TextEncoder(); const langBytes = Array.from(encoder.encode('en')); const textBytes = Array.from(encoder.encode('Hello NFC')); const payload = [langBytes.length & 0x3f, ...langBytes, ...textBytes]; await CapacitorNfc.write({ allowFormat: true, records: [ { tnf: 0x01, // TNF Well-known type: [0x54], // 'T' für Text id: [], payload, }, ], }); console.log('Tag erfolgreich geschrieben'); ``` ### URL auf NFC-Tag schreiben [Section titled “URL auf NFC-Tag schreiben”](#url-auf-nfc-tag-schreiben) ```typescript const url = 'https://capgo.app'; const urlBytes = Array.from(new TextEncoder().encode(url)); await CapacitorNfc.write({ allowFormat: true, records: [ { tnf: 0x01, // TNF Well-known type: [0x55], // 'U' für URI id: [], payload: [0x01, ...urlBytes], // 0x01 = https:// }, ], }); ``` ### NFC-Tag löschen [Section titled “NFC-Tag löschen”](#nfc-tag-löschen) ```typescript await CapacitorNfc.erase(); console.log('Tag gelöscht'); ``` ### Tag schreibgeschützt machen [Section titled “Tag schreibgeschützt machen”](#tag-schreibgeschützt-machen) ```typescript await CapacitorNfc.makeReadOnly(); console.log('Tag ist jetzt schreibgeschützt'); ``` ### Scannen stoppen [Section titled “Scannen stoppen”](#scannen-stoppen) ```typescript await listener.remove(); await CapacitorNfc.stopScanning(); ``` ### NFC-Status prüfen [Section titled “NFC-Status prüfen”](#nfc-status-prüfen) ```typescript const { status } = await CapacitorNfc.getStatus(); console.log('NFC-Status:', status); // Mögliche Werte: 'NFC_OK', 'NO_NFC', 'NFC_DISABLED', 'NDEF_PUSH_DISABLED' if (status === 'NFC_DISABLED') { // Systemeinstellungen öffnen await CapacitorNfc.showSettings(); } ``` ### Android Beam (P2P-Freigabe) [Section titled “Android Beam (P2P-Freigabe)”](#android-beam-p2p-freigabe) ```typescript // Daten über Android Beam teilen const message = { records: [ { tnf: 0x01, type: [0x54], // Text id: [], payload: [/* Text-Datensatz-Payload */], }, ], }; await CapacitorNfc.share(message); // Später Freigabe beenden await CapacitorNfc.unshare(); ``` ## API-Referenz [Section titled “API-Referenz”](#api-referenz) ### startScanning(options?) [Section titled “startScanning(options?)”](#startscanningoptions) Beginnen Sie, auf NFC-Tags zu lauschen. ```typescript interface StartScanningOptions { invalidateAfterFirstRead?: boolean; // Nur iOS, Standard ist true alertMessage?: string; // Nur iOS androidReaderModeFlags?: number; // Nur Android } await CapacitorNfc.startScanning(options); ``` ### stopScanning() [Section titled “stopScanning()”](#stopscanning) Stoppen Sie die NFC-Scansitzung. ```typescript await CapacitorNfc.stopScanning(); ``` ### write(options) [Section titled “write(options)”](#writeoptions) Schreiben Sie NDEF-Datensätze auf das zuletzt erkannte Tag. ```typescript interface WriteTagOptions { records: NdefRecord[]; allowFormat?: boolean; // Standard ist true } interface NdefRecord { tnf: number; // Type Name Format type: number[]; // Datensatztyp id: number[]; // Datensatz-ID payload: number[]; // Datensatz-Payload } await CapacitorNfc.write(options); ``` ### erase() [Section titled “erase()”](#erase) Löschen Sie das zuletzt erkannte Tag. ```typescript await CapacitorNfc.erase(); ``` ### makeReadOnly() [Section titled “makeReadOnly()”](#makereadonly) Machen Sie das zuletzt erkannte Tag schreibgeschützt (permanent). ```typescript await CapacitorNfc.makeReadOnly(); ``` ### share(options) [Section titled “share(options)”](#shareoptions) Teilen Sie NDEF-Nachricht über Android Beam (nur Android). ```typescript await CapacitorNfc.share({ records: [...] }); ``` ### unshare() [Section titled “unshare()”](#unshare) Freigabe stoppen (nur Android). ```typescript await CapacitorNfc.unshare(); ``` ### getStatus() [Section titled “getStatus()”](#getstatus) Aktuellen NFC-Adapterstatus abrufen. ```typescript const { status } = await CapacitorNfc.getStatus(); // Gibt zurück: 'NFC_OK' | 'NO_NFC' | 'NFC_DISABLED' | 'NDEF_PUSH_DISABLED' ``` ### showSettings() [Section titled “showSettings()”](#showsettings) System-NFC-Einstellungen öffnen. ```typescript await CapacitorNfc.showSettings(); ``` ## Events [Section titled “Events”](#events) ### nfcEvent [Section titled “nfcEvent”](#nfcevent) Wird ausgelöst, wenn ein NFC-Tag erkannt wird. ```typescript interface NfcEvent { type: 'tag' | 'ndef' | 'ndef-mime' | 'ndef-formattable'; tag?: NfcTag; } interface NfcTag { id: number[]; techTypes: string[]; type: string | null; maxSize: number | null; isWritable: boolean | null; canMakeReadOnly: boolean | null; ndefMessage: NdefRecord[] | null; } ``` ### nfcStateChange [Section titled “nfcStateChange”](#nfcstatechange) Wird ausgelöst, wenn sich die NFC-Adapterverfügbarkeit ändert (nur Android). ```typescript interface NfcStateChangeEvent { status: NfcStatus; enabled: boolean; } ``` ## Vollständiges Beispiel [Section titled “Vollständiges Beispiel”](#vollständiges-beispiel) ```typescript import { CapacitorNfc } from '@capgo/capacitor-nfc'; export class NfcService { private listener: any; async startReading() { // NFC-Status prüfen const { status } = await CapacitorNfc.getStatus(); if (status === 'NO_NFC') { throw new Error('NFC auf diesem Gerät nicht verfügbar'); } if (status === 'NFC_DISABLED') { await CapacitorNfc.showSettings(); return; } // Scannen starten await CapacitorNfc.startScanning({ invalidateAfterFirstRead: false, alertMessage: 'Bereit zum Scannen von NFC-Tags', }); // Auf Tags lauschen this.listener = await CapacitorNfc.addListener('nfcEvent', (event) => { this.handleNfcEvent(event); }); } private handleNfcEvent(event: any) { console.log('NFC-Event:', event.type); if (event.tag?.ndefMessage) { event.tag.ndefMessage.forEach(record => { this.processRecord(record); }); } } private processRecord(record: any) { // Text-Datensätze verarbeiten if (record.tnf === 1 && record.type[0] === 0x54) { const langLen = record.payload[0] & 0x3f; const text = new TextDecoder().decode( new Uint8Array(record.payload.slice(langLen + 1)) ); console.log('Text:', text); } // URI-Datensätze verarbeiten if (record.tnf === 1 && record.type[0] === 0x55) { const uriCode = record.payload[0]; const uri = new TextDecoder().decode( new Uint8Array(record.payload.slice(1)) ); console.log('URI:', uri); } } async writeText(text: string) { const encoder = new TextEncoder(); const langBytes = Array.from(encoder.encode('en')); const textBytes = Array.from(encoder.encode(text)); const payload = [langBytes.length & 0x3f, ...langBytes, ...textBytes]; await CapacitorNfc.write({ allowFormat: true, records: [ { tnf: 0x01, type: [0x54], id: [], payload, }, ], }); } async stopReading() { if (this.listener) { await this.listener.remove(); } await CapacitorNfc.stopScanning(); } } ``` ## NDEF-Datensatztypen [Section titled “NDEF-Datensatztypen”](#ndef-datensatztypen) ### TNF (Type Name Format) [Section titled “TNF (Type Name Format)”](#tnf-type-name-format) * `0x00`: Leer * `0x01`: Well-known (z. B. Text, URI) * `0x02`: MIME-Medientyp * `0x03`: Absolute URI * `0x04`: Externer Typ * `0x05`: Unbekannt * `0x06`: Unverändert * `0x07`: Reserviert ### Gängige Datensatztypen [Section titled “Gängige Datensatztypen”](#gängige-datensatztypen) * **Text**: `type: [0x54]` (‘T’) * **URI**: `type: [0x55]` (‘U’) * **Smart Poster**: `type: [0x53, 0x70]` (‘Sp’) ### URI-Präfixe [Section titled “URI-Präfixe”](#uri-präfixe) * `0x00`: (kein Präfix) * `0x01`: `https://` * `0x02`: `https://` * `0x03`: `http://` * `0x04`: `https://www.` ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) 1. **NFC-Status prüfen**: Überprüfen Sie immer, ob NFC verfügbar und aktiviert ist 2. **Berechtigungen handhaben**: Fordern Sie NFC-Berechtigungen entsprechend an 3. **Scannen stoppen**: Stoppen Sie das Scannen immer, wenn Sie fertig sind, um Akku zu sparen 4. **Fehlerbehandlung**: Umschließen Sie NFC-Operationen mit try-catch-Blöcken 5. **Auf Geräten testen**: NFC-Funktionen funktionieren nicht auf Simulatoren/Emulatoren ## Plattformhinweise [Section titled “Plattformhinweise”](#plattformhinweise) ### iOS [Section titled “iOS”](#ios) * Erfordert iOS 11.0+ * Verwendet Core NFC Framework * Unterstützt Hintergrund-Tag-Lesen (iOS 13+) * Beschränkt auf das Lesen von NDEF-formatierten Tags * Kann Tags nicht im Hintergrund schreiben * Erfordert NFCReaderUsageDescription in Info.plist ### Android [Section titled “Android”](#android) * Erfordert Android 4.4 (API 19)+ * Verwendet Android NFC API * Unterstützt sowohl Vordergrund- als auch Hintergrund-Tag-Lesen * Kann auf Tags schreiben * Unterstützt Android Beam (P2P) auf Geräten mit NFC * Erfordert NFC-Berechtigung in AndroidManifest.xml ### Web [Section titled “Web”](#web) * Auf Web-Plattform nicht unterstützt # @capgo/capacitor-pay > Bieten Sie native Apple Pay- und Google Pay-Checkouts mit einer einzigen TypeScript-API, die sich pro Plattform anpasst. Capgo Pay verbindet die neuesten Apple Pay- und Google Pay-SDKs, sodass Sie ein nahtloses Checkout-Erlebnis über alle Geräte hinweg bieten können. Einheitliche API Rufen Sie `isPayAvailable` und `requestPayment` einmal auf und lassen Sie das Plugin jede Plattform handhaben. Schnelle Einrichtung Liefern Sie vollständig native Zahlungssheets ohne manuelles Verdrahten plattformspezifischen Codes. Tokenisierte Zahlungen Erhalten Sie Zahlungstoken, die für Ihre Gateway- oder PSP-Integrationen bereit sind. Anpassbare Abläufe Konfigurieren Sie Einzelposten, Kontaktfelder und Abrechnungsanforderungen, um Ihren Checkout anzupassen. Tauchen Sie in den Leitfaden für die ersten Schritte ein, um Checklisten für die Einrichtung und Beispiel-Payloads für jede Plattform zu erhalten. # Erste Schritte > Konfigurieren Sie Apple Pay und Google Pay für Ihren Capacitor-Checkout mit dem Pay-Plugin. 1. **Plugin installieren** * npm ```sh npm i @capgo/capacitor-pay ``` * pnpm ```sh pnpm add @capgo/capacitor-pay ``` * yarn ```sh yarn add @capgo/capacitor-pay ``` * bun ```sh bun add @capgo/capacitor-pay ``` 2. **Native Projekte synchronisieren** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Native Voraussetzungen [Section titled “Native Voraussetzungen”](#native-voraussetzungen) * **Apple Pay (iOS)** 1. Erstellen Sie eine Apple Pay-Händler-ID im Apple Developer-Portal. 2. Generieren und laden Sie das von Ihrem Gateway benötigte Zahlungsverarbeitungszertifikat hoch. 3. Aktivieren Sie die **Apple Pay**-Funktion in Xcode und stellen Sie sicher, dass Ihr Bereitstellungsprofil diese enthält. 4. Aktualisieren Sie `Info.plist` mit einer Verwendungsbeschreibung, wenn Ihr Gateway Netzwerkzugriff zur Validierung von Zahlungen benötigt. * **Google Pay (Android)** 1. Melden Sie sich bei der [Google Pay Business Console](https://pay.google.com/business/console/) an. 2. Konfigurieren Sie Ihre Payment Gateway-Parameter oder richten Sie direkte Tokenisierung für Tests ein. 3. Wenn Sie Produktionskarten verwenden möchten, wechseln Sie `environment` zu `PRODUCTION` und registrieren Sie Testkarten für QA. 4. Bestätigen Sie, dass Google Play-Dienste auf dem Gerät verfügbar sind (von Google Pay erforderlich). Weitere schrittweise Screenshots finden Sie in den Anleitungen unter `docs/` im Plugin-Repository. ## Verfügbarkeit prüfen [Section titled “Verfügbarkeit prüfen”](#verfügbarkeit-prüfen) ```typescript import { Pay } from '@capgo/capacitor-pay'; const availability = await Pay.isPayAvailable({ apple: { supportedNetworks: ['visa', 'masterCard', 'amex'] }, google: { isReadyToPayRequest: { allowedPaymentMethods: [ { type: 'CARD', parameters: { allowedAuthMethods: ['PAN_ONLY', 'CRYPTOGRAM_3DS'], allowedCardNetworks: ['VISA', 'MASTERCARD'], }, }, ], }, }, }); if (!availability.available) { console.warn('Native Pay nicht verfügbar:', availability); } ``` ## Zahlung anfordern (Apple Pay) [Section titled “Zahlung anfordern (Apple Pay)”](#zahlung-anfordern-apple-pay) ```typescript const appleResult = await Pay.requestPayment({ apple: { merchantIdentifier: 'merchant.com.example.app', countryCode: 'US', currencyCode: 'USD', supportedNetworks: ['visa', 'masterCard'], paymentSummaryItems: [ { label: 'Starter-Plan', amount: '9.99' }, { label: 'Capgo Store', amount: '9.99' }, ], requiredShippingContactFields: ['name', 'emailAddress'], }, }); const token = appleResult.apple?.paymentData; ``` ## Zahlung anfordern (Google Pay) [Section titled “Zahlung anfordern (Google Pay)”](#zahlung-anfordern-google-pay) ```typescript const googleResult = await Pay.requestPayment({ google: { environment: 'test', paymentDataRequest: { apiVersion: 2, apiVersionMinor: 0, allowedPaymentMethods: [ { type: 'CARD', parameters: { allowedAuthMethods: ['PAN_ONLY', 'CRYPTOGRAM_3DS'], allowedCardNetworks: ['VISA', 'MASTERCARD'], billingAddressRequired: true, billingAddressParameters: { format: 'FULL' }, }, tokenizationSpecification: { type: 'PAYMENT_GATEWAY', parameters: { gateway: 'example', gatewayMerchantId: 'exampleMerchantId', }, }, }, ], merchantInfo: { merchantName: 'Capgo Store', }, transactionInfo: { totalPriceStatus: 'FINAL', totalPrice: '9.99', currencyCode: 'USD', countryCode: 'US', }, }, }, }); const paymentData = googleResult.google?.paymentData; ``` ## Antworten verarbeiten [Section titled “Antworten verarbeiten”](#antworten-verarbeiten) * Tokenisieren Sie die zurückgegebene Payload mit Ihrem Zahlungsverarbeiter und bestätigen Sie die Transaktion serverseitig. * Bieten Sie einen Fallback-Pfad (Karteneingabe, Rechnung) an, wenn native Zahlung nicht verfügbar ist oder der Benutzer das Sheet abbricht. * Cachen Sie das Verfügbarkeitsergebnis, damit Sie die Schaltflächen nicht auf nicht unterstützten Geräten anzeigen. # @capgo/capacitor-pdf-generator > Rendern Sie pixelperfekte PDFs aus Vorlagen, Quittungen oder Berichten, ohne Ihre Capacitor-App zu verlassen. Konvertieren Sie HTML oder entfernte Seiten in teilbare PDFs mit Capgos nativem Renderer für iOS und Android. HTML zu PDF HTML-Vorlagen eingeben und in Sekunden ein teilbares PDF-Dokument erhalten. Entfernte Erfassung Jede zugängliche URL erfassen und das generierte PDF speichern oder verteilen. Flexible Ausgaben Base64-Daten zurückgeben oder das native Teilen-Sheet je nach Workflow auslösen. Dokumentsteuerung Ausrichtung, Seitengröße und Dateinamen für jedes Rendering festlegen. Gehen Sie zum Leitfaden für die ersten Schritte für Plattformhinweise und Verwendungsbeispiele. # Erste Schritte > Rendern Sie HTML und Remote-Seiten in PDFs mit dem Capgo PDF Generator-Plugin. 1. **Plugin installieren** * npm ```sh npm i @capgo/capacitor-pdf-generator ``` * pnpm ```sh pnpm add @capgo/capacitor-pdf-generator ``` * yarn ```sh yarn add @capgo/capacitor-pdf-generator ``` * bun ```sh bun add @capgo/capacitor-pdf-generator ``` 2. **Plattformen synchronisieren** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Aus HTML generieren [Section titled “Aus HTML generieren”](#aus-html-generieren) ```typescript import { PdfGenerator } from '@capgo/capacitor-pdf-generator'; const receiptHtml = `

Capgo Store

Vielen Dank für Ihren Kauf.

`; const pdf = await PdfGenerator.fromData({ data: receiptHtml, documentSize: 'A4', orientation: 'portrait', type: 'base64', fileName: 'receipt.pdf', }); if (pdf.type === 'base64') { const link = document.createElement('a'); link.href = `data:application/pdf;base64,${pdf.base64}`; link.download = 'receipt.pdf'; link.click(); } ``` ## Remote-URL erfassen [Section titled “Remote-URL erfassen”](#remote-url-erfassen) ```typescript await PdfGenerator.fromURL({ url: 'https://docs.capgo.app/invoice?id=123', orientation: 'landscape', documentSize: 'A4', type: 'share', // öffnet den nativen Teilen-Dialog fileName: 'invoice-123.pdf', }); ``` ## Tipps [Section titled “Tipps”](#tipps) * Fügen Sie Inline-CSS in Ihren HTML-String ein oder geben Sie eine `baseUrl` an, damit relative Assets korrekt aufgelöst werden. * Verschieben Sie Base64-Daten auf Mobilgeräten ins Dateisystem (z. B. Capacitor Filesystem), bevor Sie sie teilen. * Die Querformat-Ausrichtung ist ideal für breite Tabellen, während Hochformat für die meisten Dokumente geeignet ist. * Verwenden Sie bereichsbezogene CSS-Schriftarten (z. B. Systemschriftarten) für konsistentes Rendering über Plattformen hinweg. # @capgo/capacitor-pedometer > Greifen Sie auf umfassende Pedometer-Daten für Fitness- und Gesundheitstracking mit Echtzeit-Updates und historischen Datenabfragen zu. Schrittzählung Genaue Schritterkennung und -zählung 👟 Entfernungsverfolgung Zurückgelegte Entfernung in Echtzeit berechnen 📏 Tempo & Kadenz Geh- und Laufgeschwindigkeit überwachen 🏃 Historische Daten Pedometer-Daten aus bestimmten Zeitbereichen abfragen 📊 Etagen-Tracking Erstiegene Etagen auf iOS-Geräten zählen 🪜 Erste Schritte Sehen Sie sich den [Leitfaden für die ersten Schritte](/docs/plugins/pedometer/getting-started/) an, um das Plugin zu installieren und zu konfigurieren. # Erste Schritte mit Pedometer > Erfahren Sie, wie Sie Schrittzählung und Fitness-Tracking in Ihre Capacitor-App integrieren Dieser Leitfaden führt Sie durch die Integration des Capacitor Pedometer-Plugins in Ihre Anwendung. ## Installation [Section titled “Installation”](#installation) Installieren Sie das Plugin mit npm: ```bash npm install @capgo/capacitor-pedometer npx cap sync ``` ## iOS-Konfiguration [Section titled “iOS-Konfiguration”](#ios-konfiguration) Fügen Sie eine Beschreibung der Bewegungsnutzung zu Ihrer `Info.plist` hinzu: ```xml NSMotionUsageDescription Wir benötigen Zugriff auf Ihre Bewegungsdaten, um Schritte und Aktivitäten zu verfolgen ``` ## Android-Konfiguration [Section titled “Android-Konfiguration”](#android-konfiguration) Das Plugin fügt automatisch die `ACTIVITY_RECOGNITION`-Berechtigung hinzu. Für Android 10+ (API 29) müssen Sie diese Berechtigung zur Laufzeit anfordern. ## Grundlegende Verwendung [Section titled “Grundlegende Verwendung”](#grundlegende-verwendung) ### Plugin importieren [Section titled “Plugin importieren”](#plugin-importieren) ```typescript import { CapacitorPedometer } from '@capgo/capacitor-pedometer'; ``` ### Verfügbarkeit prüfen [Section titled “Verfügbarkeit prüfen”](#verfügbarkeit-prüfen) ```typescript const checkPedometer = async () => { const result = await CapacitorPedometer.isAvailable(); console.log('Schrittzählung verfügbar:', result.stepCounting); console.log('Entfernung verfügbar:', result.distance); console.log('Etagen zählen verfügbar:', result.floorCounting); console.log('Tempo verfügbar:', result.pace); console.log('Kadenz verfügbar:', result.cadence); }; ``` ### Berechtigungen anfordern [Section titled “Berechtigungen anfordern”](#berechtigungen-anfordern) ```typescript const requestPermission = async () => { const permission = await CapacitorPedometer.requestPermissions(); console.log('Berechtigungsstatus:', permission.activityRecognition); }; ``` ### Aktuelle Messungen abrufen [Section titled “Aktuelle Messungen abrufen”](#aktuelle-messungen-abrufen) ```typescript const getMeasurements = async () => { const measurements = await CapacitorPedometer.getMeasurement({ startDate: new Date(Date.now() - 24 * 60 * 60 * 1000), // Vor 24 Stunden endDate: new Date() }); console.log('Schritte:', measurements.numberOfSteps); console.log('Entfernung:', measurements.distance, 'Meter'); console.log('Etagen aufgestiegen:', measurements.floorsAscended); console.log('Etagen abgestiegen:', measurements.floorsDescended); }; ``` ### Echtzeit-Updates starten [Section titled “Echtzeit-Updates starten”](#echtzeit-updates-starten) ```typescript // Listener für Echtzeit-Updates hinzufügen CapacitorPedometer.addListener('measurement', (data) => { console.log('Schritte:', data.numberOfSteps); console.log('Entfernung:', data.distance); console.log('Tempo:', data.currentPace); console.log('Kadenz:', data.currentCadence); }); // Updates empfangen starten await CapacitorPedometer.startMeasurementUpdates(); ``` ### Updates stoppen [Section titled “Updates stoppen”](#updates-stoppen) ```typescript await CapacitorPedometer.stopMeasurementUpdates(); CapacitorPedometer.removeAllListeners(); ``` ## Vollständiges Beispiel [Section titled “Vollständiges Beispiel”](#vollständiges-beispiel) ```typescript import { CapacitorPedometer } from '@capgo/capacitor-pedometer'; class PedometerService { private listener: any; async initialize() { // Verfügbarkeit prüfen const available = await CapacitorPedometer.isAvailable(); if (!available.stepCounting) { console.error('Schrittzählung nicht verfügbar'); return false; } // Berechtigungen anfordern const permission = await CapacitorPedometer.requestPermissions(); if (permission.activityRecognition !== 'granted') { console.error('Berechtigung verweigert'); return false; } return true; } async startTracking() { // Listener hinzufügen this.listener = CapacitorPedometer.addListener('measurement', (data) => { this.onMeasurement(data); }); // Updates starten await CapacitorPedometer.startMeasurementUpdates(); console.log('Pedometer-Tracking gestartet'); } async stopTracking() { await CapacitorPedometer.stopMeasurementUpdates(); if (this.listener) { this.listener.remove(); } console.log('Pedometer-Tracking gestoppt'); } onMeasurement(data: any) { console.log('Schritte heute:', data.numberOfSteps); console.log('Entfernung:', (data.distance / 1000).toFixed(2), 'km'); } async getTodaySteps() { const today = new Date(); today.setHours(0, 0, 0, 0); const measurements = await CapacitorPedometer.getMeasurement({ startDate: today, endDate: new Date() }); return measurements.numberOfSteps; } } // Verwendung const pedometer = new PedometerService(); await pedometer.initialize(); await pedometer.startTracking(); ``` ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) 1. **Verfügbarkeit prüfen**: Pedometer-Funktionen immer vor der Verwendung überprüfen 2. **Berechtigungen anfordern**: Berechtigungsanfragen elegant behandeln 3. **Batterieoptimierung**: Updates stoppen, wenn nicht benötigt 4. **Fehlerbehandlung**: Aufrufe in try-catch-Blöcke einwickeln 5. **Hintergrund-Updates**: Hintergrundmodi für kontinuierliches Tracking konfigurieren ## Häufige Probleme [Section titled “Häufige Probleme”](#häufige-probleme) ### Berechtigung verweigert [Section titled “Berechtigung verweigert”](#berechtigung-verweigert) ```typescript const handlePermissionDenied = async () => { const permission = await CapacitorPedometer.checkPermissions(); if (permission.activityRecognition === 'denied') { alert('Berechtigung für Bewegungstracking ist erforderlich. Bitte aktivieren Sie sie in den Einstellungen.'); } }; ``` ### Auf Gerät nicht verfügbar [Section titled “Auf Gerät nicht verfügbar”](#auf-gerät-nicht-verfügbar) ```typescript const checkSupport = async () => { const available = await CapacitorPedometer.isAvailable(); if (!available.stepCounting) { alert('Schrittzählung ist auf diesem Gerät nicht verfügbar'); return false; } return true; }; ``` ## Nächste Schritte [Section titled “Nächste Schritte”](#nächste-schritte) * Erkunden Sie die [API-Referenz](https://github.com/Cap-go/capacitor-pedometer#api) für vollständige Dokumentation * Schauen Sie sich die [Beispiel-App](https://github.com/Cap-go/capacitor-pedometer/tree/main/example) an * Siehe das [Tutorial](/plugins/capacitor-pedometer) für fortgeschrittene Implementierung # @capgo/capacitor-persistent-account > Verwalten Sie Benutzerkonten mit persistentem Speicher, sicherer Sitzungsbehandlung und nahtloser Authentifizierung über App-Sitzungen hinweg. ## Übersicht [Section titled “Übersicht”](#übersicht) Das Capacitor Persistent Account-Plugin ermöglicht sichere Speicherung und Persistenz von Benutzerkontodaten zwischen App-Installationen. Dieses Plugin stellt sicher, dass Benutzerkontoinformationen auch nach App-Neuinstallationen verfügbar bleiben, was nahtloses Benutzererlebnis und Kontinuität des Kontos bietet. Installations-übergreifende Persistenz Daten überleben App-Deinstallations-/Neuinstallationszyklen 💾 Sicherer Speicher Sichere Kontodatenspeicherung mit Systemintegration 🔐 Einfache API Saubere Lese-/Schreibschnittstelle für Kontoverwaltung 📊 Plattformübergreifend Native iOS- und Android-Implementierung 📱 ## Installation [Section titled “Installation”](#installation) ```bash npm install @capgo/capacitor-persistent-account npx cap sync ``` ## Kern-API-Methoden [Section titled “Kern-API-Methoden”](#kern-api-methoden) ### Datenverwaltung [Section titled “Datenverwaltung”](#datenverwaltung) * `saveAccount(options: { data: unknown })` - Speichert Kontodaten sicher im persistenten Speicher * `readAccount()` - Ruft gespeicherte Kontodaten ab, gibt `Promise<{ data: unknown | null }>` zurück ## Hauptfunktionen [Section titled “Hauptfunktionen”](#hauptfunktionen) * **Installations-übergreifende Persistenz**: Kontodaten überleben App-Deinstallation und -Neuinstallation * **Sicherer Speicher**: Verwendet plattformspezifische sichere Speichermechanismen * **Typ-Flexibilität**: Speichert jede serialisierbare Kontodatenstruktur * **Plattformübergreifende Unterstützung**: Native Implementierung für iOS und Android ## Verwendungsbeispiel [Section titled “Verwendungsbeispiel”](#verwendungsbeispiel) ```typescript import { PersistentAccount } from '@capgo/capacitor-persistent-account'; // Ihre Kontodatenstruktur definieren interface UserAccount { userId: string; username: string; email: string; preferences: { theme: string; notifications: boolean; }; } // Kontodaten speichern const accountData: UserAccount = { userId: '12345', username: 'john_doe', email: 'john@example.com', preferences: { theme: 'dark', notifications: true } }; await PersistentAccount.saveAccount({ data: accountData }); // Kontodaten lesen const result = await PersistentAccount.readAccount(); if (result.data) { const account = result.data as UserAccount; console.log('Konto wiederhergestellt:', account.username); } else { console.log('Keine Kontodaten gefunden'); } ``` ## Anwendungsfälle [Section titled “Anwendungsfälle”](#anwendungsfälle) * **Benutzer-Onboarding**: Benutzerfortschritt durch App-Neuinstallationen bewahren * **Kontowiederherstellung**: Benutzersitzungen nach App-Updates wiederherstellen * **Einstellungsspeicherung**: Benutzereinstellungen und Konfigurationen beibehalten * **Offline-First-Apps**: Wesentliche Benutzerdaten lokal speichern ## Plattform-Implementierung [Section titled “Plattform-Implementierung”](#plattform-implementierung) ### iOS [Section titled “iOS”](#ios) * Nutzt iOS Keychain Services für sicheren, persistenten Speicher * Daten überleben App-Löschung und Gerätwiederherstellungen ### Android [Section titled “Android”](#android) * Verwendet Android Account Manager oder Shared Preferences mit Backup * Behält Daten über App-Neuinstallationen und Gerätemigrationen hinweg ## Sicherheitsüberlegungen [Section titled “Sicherheitsüberlegungen”](#sicherheitsüberlegungen) * Kontodaten werden mit plattformspezifischem sicherem Speicher gespeichert * Verschlüsselung sensibler Informationen erwägen * Ordnungsgemäße Datenvalidierung beim Lesen gespeicherter Konten implementieren * Plattformrichtlinien für Benutzerdatenverarbeitung befolgen ## Dokumentation [Section titled “Dokumentation”](#dokumentation) Siehe die [vollständige Dokumentation](/docs/plugins/persistent-account/getting-started/) für detaillierte Implementierungsanleitungen und Bewährte Methoden. # Erste Schritte > Erfahren Sie, wie Sie das Persistent Account-Plugin installieren und verwenden, um Benutzerdaten über App-Installationen hinweg zu erhalten. ## Installation [Section titled “Installation”](#installation) * npm ```bash npm install @capgo/capacitor-persistent-account npx cap sync ``` * yarn ```bash yarn add @capgo/capacitor-persistent-account npx cap sync ``` * pnpm ```bash pnpm add @capgo/capacitor-persistent-account npx cap sync ``` * bun ```bash bun add @capgo/capacitor-persistent-account npx cap sync ``` ## Plattformunterstützung [Section titled “Plattformunterstützung”](#plattformunterstützung) * **iOS**: Verwendet iOS Keychain Services für sicheren, persistenten Speicher * **Android**: Verwendet Android Account Manager oder Shared Preferences mit Backup ## Verwendungsbeispiel [Section titled “Verwendungsbeispiel”](#verwendungsbeispiel) ```typescript import { PersistentAccount } from '@capgo/capacitor-persistent-account'; // Ihre Kontodatenstruktur definieren interface UserAccount { userId: string; username: string; email: string; preferences: { theme: string; notifications: boolean; }; } // Kontodaten speichern const accountData: UserAccount = { userId: '12345', username: 'john_doe', email: 'john@example.com', preferences: { theme: 'dark', notifications: true } }; await PersistentAccount.saveAccount({ data: accountData }); // Kontodaten lesen const result = await PersistentAccount.readAccount(); if (result.data) { const account = result.data as UserAccount; console.log('Konto wiederhergestellt:', account.username); } else { console.log('Keine Kontodaten gefunden'); } ``` ## API-Referenz [Section titled “API-Referenz”](#api-referenz) ### saveAccount(options) [Section titled “saveAccount(options)”](#saveaccountoptions) ```typescript saveAccount(options: { data: unknown }) => Promise ``` Speichert Kontodaten sicher im persistenten Speicher. | Param | Typ | | ------------- | ------------------- | | **`options`** | `{ data: unknown }` | ### readAccount() [Section titled “readAccount()”](#readaccount) ```typescript readAccount() => Promise<{ data: unknown | null }> ``` Ruft gespeicherte Kontodaten ab. **Gibt zurück:** `Promise<{ data: unknown | null }>` ## Praktische Implementierung [Section titled “Praktische Implementierung”](#praktische-implementierung) ### Vollständiges Auth Service-Beispiel [Section titled “Vollständiges Auth Service-Beispiel”](#vollständiges-auth-service-beispiel) ```typescript import { PersistentAccount } from '@capgo/capacitor-persistent-account'; interface UserAccount { userId: string; username: string; email: string; authToken?: string; preferences: { theme: string; language: string; notifications: boolean; }; } class AuthService { // Benutzerkonto nach Anmeldung speichern async saveUserAccount(user: UserAccount) { try { await PersistentAccount.saveAccount({ data: user }); console.log('Benutzerkonto erfolgreich gespeichert'); } catch (error) { console.error('Konto konnte nicht gespeichert werden:', error); throw error; } } // Benutzerkonto beim App-Start wiederherstellen async restoreUserAccount(): Promise { try { const result = await PersistentAccount.readAccount(); if (result.data) { const account = result.data as UserAccount; console.log('Benutzerkonto wiederhergestellt:', account.username); return account; } console.log('Kein gespeichertes Konto gefunden'); return null; } catch (error) { console.error('Konto konnte nicht wiederhergestellt werden:', error); return null; } } // Benutzereinstellungen aktualisieren async updatePreferences(preferences: Partial) { const account = await this.restoreUserAccount(); if (account) { const updatedAccount: UserAccount = { ...account, preferences: { ...account.preferences, ...preferences } }; await this.saveUserAccount(updatedAccount); } } // Kontodaten löschen (bei Abmeldung) async clearAccount() { try { await PersistentAccount.saveAccount({ data: null }); console.log('Kontodaten gelöscht'); } catch (error) { console.error('Konto konnte nicht gelöscht werden:', error); } } } // Verwendung const authService = new AuthService(); // Bei Anmeldung await authService.saveUserAccount({ userId: '12345', username: 'john_doe', email: 'john@example.com', authToken: 'abc123xyz', preferences: { theme: 'dark', language: 'de', notifications: true } }); // Beim App-Start const savedAccount = await authService.restoreUserAccount(); if (savedAccount) { // Benutzer war zuvor angemeldet console.log('Willkommen zurück,', savedAccount.username); } // Einstellungen aktualisieren await authService.updatePreferences({ theme: 'light', notifications: false }); // Bei Abmeldung await authService.clearAccount(); ``` ### App-Initialisierung mit Kontowiederherstellung [Section titled “App-Initialisierung mit Kontowiederherstellung”](#app-initialisierung-mit-kontowiederherstellung) ```typescript import { PersistentAccount } from '@capgo/capacitor-persistent-account'; async function initializeApp() { try { // Versuchen, gespeichertes Konto wiederherzustellen const result = await PersistentAccount.readAccount(); if (result.data) { const account = result.data as UserAccount; // Token ist noch gültig prüfen const isValid = await validateAuthToken(account.authToken); if (isValid) { // Benutzersitzung wiederherstellen setCurrentUser(account); navigateToHome(); } else { // Token abgelaufen, Anmeldung anzeigen navigateToLogin(); } } else { // Kein gespeichertes Konto, Anmeldung anzeigen navigateToLogin(); } } catch (error) { console.error('App konnte nicht initialisiert werden:', error); navigateToLogin(); } } // Beim App-Start aufrufen initializeApp(); ``` ### Mit Backend synchronisieren [Section titled “Mit Backend synchronisieren”](#mit-backend-synchronisieren) ```typescript import { PersistentAccount } from '@capgo/capacitor-persistent-account'; async function syncAccountWithBackend() { const result = await PersistentAccount.readAccount(); if (result.data) { const localAccount = result.data as UserAccount; try { // Neueste Kontodaten vom Server abrufen const response = await fetch(`/api/users/${localAccount.userId}`); const serverAccount = await response.json(); // Serverdaten mit lokalen Einstellungen zusammenführen const mergedAccount: UserAccount = { ...serverAccount, preferences: { ...serverAccount.preferences, ...localAccount.preferences // Lokale Einstellungen haben Priorität } }; // Zusammengeführte Daten speichern await PersistentAccount.saveAccount({ data: mergedAccount }); return mergedAccount; } catch (error) { console.error('Synchronisation mit Backend fehlgeschlagen:', error); // Lokales Konto als Fallback zurückgeben return localAccount; } } return null; } ``` ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) * **Typsicherheit**: Klare TypeScript-Schnittstellen für Ihre Kontodaten definieren * **Validierung**: Daten beim Lesen aus persistentem Speicher immer validieren * **Fehlerbehandlung**: Ordnungsgemäße try-catch-Blöcke für alle Operationen implementieren * **Sicherheit**: Sensible Daten wie Passwörter nicht im Klartext speichern * **Token-Verwaltung**: Auth-Token beim Wiederherstellen von Konten aktualisieren * **Datengröße**: Gespeicherte Daten minimal halten, um schnelle Lese-/Schreibvorgänge zu gewährleisten * **Null-Prüfungen**: Immer prüfen, ob Daten vorhanden sind, bevor sie verwendet werden * **Backup-Strategie**: Für zusätzliche Sicherheit Synchronisation mit Backend erwägen ## Sicherheitsüberlegungen [Section titled “Sicherheitsüberlegungen”](#sicherheitsüberlegungen) * Kontodaten werden mit plattformspezifischen sicheren Speichermechanismen gespeichert * Daten bleiben über App-Deinstallationen und -Neuinstallationen hinweg erhalten * Verschlüsselung sensibler Informationen vor dem Speichern erwägen * Ordnungsgemäße Datenvalidierung beim Lesen gespeicherter Konten implementieren * Plattformrichtlinien für Benutzerdatenverarbeitung befolgen * Authentifizierungstoken mit Ablauf für Sicherheit verwenden * Kontodaten bei Benutzerabmeldung angemessen löschen ## Plattform-Implementierung [Section titled “Plattform-Implementierung”](#plattform-implementierung) ### iOS [Section titled “iOS”](#ios) * Nutzt iOS Keychain Services für sicheren, persistenten Speicher * Daten überleben App-Löschung und Gerätwiederherstellungen * Geschützt durch iOS-Sicherheitsmechanismen ### Android [Section titled “Android”](#android) * Verwendet Android Account Manager oder Shared Preferences mit Backup * Behält Daten über App-Neuinstallationen und Gerätemigrationen hinweg * Geschützt durch Android-Systemsicherheit ## Anwendungsfälle [Section titled “Anwendungsfälle”](#anwendungsfälle) * **Benutzer-Onboarding**: Benutzerfortschritt durch App-Neuinstallationen bewahren * **Kontowiederherstellung**: Benutzersitzungen nach App-Updates wiederherstellen * **Einstellungsspeicherung**: Benutzereinstellungen und Konfigurationen beibehalten * **Offline-First-Apps**: Wesentliche Benutzerdaten lokal speichern * **Sitzungsverwaltung**: Benutzer über App-Neustarts hinweg angemeldet halten * **Gerätemigration**: Benutzerdaten auf neue Geräte übertragen # @capgo/capacitor-photo-library > Erstellen Sie benutzerdefinierte Galerien, die mit robusten Autorisierungs-Flows auf die native Fotobibliothek zugreifen. Capgo Photo Library hilft Ihnen, Alben aufzulisten, durch Assets zu blättern und Miniaturansichten anzufordern, während die Leistung in Schach gehalten wird. Autorisierungs-Helfer Berechtigungen anfordern und eingeschränkte Zugriffszustände elegant behandeln. Seitenweises Abrufen Galerie mit cursor-basiertem Laden und anpassbaren Limits paginieren. Optimierte Miniaturansichten Miniaturansicht-URLs in festen Abmessungen für flüssiges Scrollen von Listen abrufen. Medienauswahl Benutzer Fotos oder Videos mit konfigurierbaren Auswahllimits auswählen lassen. Siehe Leitfaden für die ersten Schritte für Berechtigungseinrichtung und Galeriebeispiele. # Erste Schritte > Integrieren Sie die native Fotobibliothek mit Paginierung und Medienauswahl. 1. **Plugin installieren** * npm ```sh npm i @capgo/capacitor-photo-library ``` * pnpm ```sh pnpm add @capgo/capacitor-photo-library ``` * yarn ```sh yarn add @capgo/capacitor-photo-library ``` * bun ```sh bun add @capgo/capacitor-photo-library ``` 2. **Plattformen synchronisieren** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Plattformberechtigungen [Section titled “Plattformberechtigungen”](#plattformberechtigungen) * **iOS**: `Info.plist` mit `NSPhotoLibraryUsageDescription` aktualisieren (und `NSPhotoLibraryAddUsageDescription`, wenn Sie Medien speichern möchten). * **Android**: Für Android 13+ die Foto-/Videoberechtigungen deklarieren (`READ_MEDIA_IMAGES`, `READ_MEDIA_VIDEO`). Capacitor 5 behandelt dies automatisch, aber überprüfen Sie benutzerdefinierte Manifeste doppelt. ## Zugriff anfordern [Section titled “Zugriff anfordern”](#zugriff-anfordern) ```typescript import { PhotoLibrary } from '@capgo/capacitor-photo-library'; const { state } = await PhotoLibrary.requestAuthorization(); if (state !== 'authorized' && state !== 'limited') { throw new Error('Fotozugriff verweigert'); } ``` ## Galerie laden [Section titled “Galerie laden”](#galerie-laden) ```typescript import { Capacitor } from '@capacitor/core'; const { assets, hasMore, nextPage } = await PhotoLibrary.getLibrary({ limit: 100, includeAlbums: false, thumbnailWidth: 256, thumbnailHeight: 256, }); const gallery = assets.map((asset) => ({ id: asset.id, fileName: asset.fileName, created: asset.creationDate, thumbnailUrl: asset.thumbnail ? asset.thumbnail.webPath ?? Capacitor.convertFileSrc(asset.thumbnail.path) : undefined, photoUrl: asset.file ? asset.file.webPath ?? Capacitor.convertFileSrc(asset.file.path) : undefined, })); ``` Um zusätzliche Seiten abzurufen, rufen Sie `PhotoLibrary.getLibrary({ cursor: nextPage })` auf. ## Benutzer Medien auswählen lassen [Section titled “Benutzer Medien auswählen lassen”](#benutzer-medien-auswählen-lassen) ```typescript const selection = await PhotoLibrary.pickMedia({ selectionLimit: 5, includeVideos: true, }); console.log('Benutzer hat ausgewählt', selection.assets.length, 'Elemente'); ``` ## Zwischengespeicherte Dateien aufräumen [Section titled “Zwischengespeicherte Dateien aufräumen”](#zwischengespeicherte-dateien-aufräumen) Generierte Miniaturansichten und Exporte werden im App-Cache-Verzeichnis unter `photoLibrary` gespeichert. Entfernen Sie sie mit Ihrer bevorzugten Datei-API, wenn Sie Speicherplatz zurückgewinnen müssen. # @capgo/capacitor-printer > Drucken Sie Dokumente, HTML-Inhalte, PDFs, Bilder und Web-Views direkt von Ihrer Capacitor-App auf iOS und Android. Mehrere Formate Drucken Sie HTML, PDFs, Bilder und Web-Inhalte 📄 Native UI Verwendet native Druck-Dialoge auf iOS und Android 🖨️ Einfache Integration Einfache API für schnelle Integration in Ihre App 🚀 Plattformübergreifend Funktioniert auf iOS- und Android-Geräten 📱 Anpassbar Steuern Sie Druckoptionen wie Ausrichtung und Seitengröße ⚙️ Umfassende Dokumentation Lesen Sie die [Dokumentation](/docs/plugins/printer/getting-started/), um das Plugin in wenigen Minuten zu beherrschen. # Erste Schritte > Erfahren Sie, wie Sie das Printer-Plugin installieren und verwenden, um Inhalte aus Ihrer Capacitor-App zu drucken. 1. **Paket installieren** * npm ```sh npm i @capgo/capacitor-printer ``` * pnpm ```sh pnpm add @capgo/capacitor-printer ``` * yarn ```sh yarn add @capgo/capacitor-printer ``` * bun ```sh bun add @capgo/capacitor-printer ``` 2. **Mit nativen Projekten synchronisieren** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Verwendung [Section titled “Verwendung”](#verwendung) Importieren Sie das Plugin und drucken Sie verschiedene Arten von Inhalten: ```typescript import { Printer } from '@capgo/capacitor-printer'; // HTML-Inhalt drucken const printHTML = async () => { await Printer.print({ content: '

Hallo Welt

Dies ist ein Testdruck.

', name: 'test-print' }); }; // PDF-Datei drucken const printPDF = async () => { await Printer.print({ content: 'file:///pfad/zum/dokument.pdf', name: 'mein-dokument' }); }; // Bild drucken const printImage = async () => { await Printer.print({ content: 'file:///pfad/zum/bild.jpg', name: 'mein-bild' }); }; ``` ## API-Referenz [Section titled “API-Referenz”](#api-referenz) ### print(options) [Section titled “print(options)”](#printoptions) Öffnet den nativen Druckdialog mit dem angegebenen Inhalt. ```typescript interface PrintOptions { content: string; // HTML-String, Datei-URI oder Base64-Daten name?: string; // Name des Druckauftrags orientation?: 'portrait' | 'landscape'; grayscale?: boolean; // In Graustufen drucken (Standard: false) } await Printer.print(options); ``` ### canPrint() [Section titled “canPrint()”](#canprint) Prüft, ob das Drucken auf dem Gerät verfügbar ist. ```typescript const { value } = await Printer.canPrint(); console.log('Drucken verfügbar:', value); ``` ## Vollständiges Beispiel [Section titled “Vollständiges Beispiel”](#vollständiges-beispiel) ```typescript import { Printer } from '@capgo/capacitor-printer'; import { Filesystem, Directory } from '@capacitor/filesystem'; export class PrintService { async checkPrintAvailability(): Promise { const { value } = await Printer.canPrint(); return value; } async printHTML(htmlContent: string, jobName: string = 'Dokument') { try { const canPrint = await this.checkPrintAvailability(); if (!canPrint) { throw new Error('Drucken auf diesem Gerät nicht verfügbar'); } await Printer.print({ content: htmlContent, name: jobName, orientation: 'portrait' }); } catch (error) { console.error('Druck fehlgeschlagen:', error); throw error; } } async printDocument(filePath: string, jobName?: string) { await Printer.print({ content: filePath, name: jobName || 'Dokument' }); } async printReceipt(receiptData: any) { const html = `

Quittung

Datum: ${new Date().toLocaleDateString()}

${receiptData.items.map((item: any) => `
${item.name} ${item.price.toFixed(2)}€
`).join('')}
Gesamt: ${receiptData.total.toFixed(2)}€
`; await this.printHTML(html, 'Quittung'); } async printFromURL(url: string) { // Datei zuerst herunterladen const response = await fetch(url); const blob = await response.blob(); const reader = new FileReader(); return new Promise((resolve, reject) => { reader.onloadend = async () => { try { await Printer.print({ content: reader.result as string, name: 'Heruntergeladenes Dokument' }); resolve(true); } catch (error) { reject(error); } }; reader.onerror = reject; reader.readAsDataURL(blob); }); } } ``` ## Erweiterte Verwendung [Section titled “Erweiterte Verwendung”](#erweiterte-verwendung) ### Drucken mit benutzerdefinierten Stilen [Section titled “Drucken mit benutzerdefinierten Stilen”](#drucken-mit-benutzerdefinierten-stilen) ```typescript const printStyledDocument = async () => { const html = `

Professionelles Dokument

Dies ist ein professionell gestaltetes Dokument, bereit zum Drucken.

`; await Printer.print({ content: html, name: 'Gestaltetes Dokument', orientation: 'portrait' }); }; ``` ### Tabellen drucken [Section titled “Tabellen drucken”](#tabellen-drucken) ```typescript const printTable = async (data: any[]) => { const tableRows = data.map(row => ` ${row.id} ${row.name} ${row.value} `).join(''); const html = ` ${tableRows}
ID Name Wert
`; await Printer.print({ content: html, name: 'Datentabelle' }); }; ``` ### Bilder drucken [Section titled “Bilder drucken”](#bilder-drucken) ```typescript const printImageWithDetails = async (imagePath: string, title: string) => { const html = `

${title}

${title} `; await Printer.print({ content: html, name: title }); }; ``` ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) 1. **Verfügbarkeit prüfen**: Vor dem Druckversuch immer prüfen, ob Drucken verfügbar ist 2. **Gültiger Inhalt**: Sicherstellen, dass HTML wohlgeformt ist und Dateipfade gültig sind 3. **Auftragsnamen**: Beschreibende Namen für Druckaufträge verwenden, um Benutzern bei der Identifizierung zu helfen 4. **Styling**: Inline-CSS oder eingebettete Stile für konsistente Druckausgabe verwenden 5. **Fehlerbehandlung**: Druckaufrufe in try-catch-Blöcke für Benutzerfeedback einwickeln ## Fehlerbehebung [Section titled “Fehlerbehebung”](#fehlerbehebung) ### Häufige Probleme [Section titled “Häufige Probleme”](#häufige-probleme) **Druckdialog erscheint nicht**: Mit canPrint() prüfen, ob Drucken verfügbar ist **Inhalt wird nicht gedruckt**: HTML ist gültig und Dateipfade sind korrekt prüfen **Bilder werden nicht angezeigt**: Bildpfade sind absolut und Dateien sind zugänglich sicherstellen **Styling-Probleme**: @page-CSS-Regeln verwenden und Druckausgabe testen # @capgo/capacitor-realtimekit > Fügen Sie Videokonferenzen und Echtzeit-Kommunikation zu Ihrer App mit Cloudflare Calls-Integration und eingebauter UI hinzu. Cloudflare Calls Unterstützt durch Cloudflares globale Infrastruktur ☁️ Eingebaute Meeting-UI Sofort einsatzbereite Videokonferenz-Oberfläche 📹 WebRTC-Unterstützung Hochwertige Echtzeit-Audio- und Videoqualität 🎥 Hintergrundmodi Führen Sie Anrufe fort, wenn die App im Hintergrund ist 🔄 Bildschirmfreigabe Teilen Sie den Bildschirm während Meetings auf Android 📱 Erste Schritte Schauen Sie sich den [Erste-Schritte-Leitfaden](/docs/plugins/realtimekit/getting-started/) an, um das Plugin zu installieren und zu konfigurieren. # Erste Schritte mit RealtimeKit > Erfahren Sie, wie Sie Cloudflare Calls-Videokonferenzen in Ihre Capacitor-App integrieren Dieser Leitfaden führt Sie durch die Integration des Capacitor RealtimeKit-Plugins, um Ihrer Anwendung Videokonferenzen mit Cloudflare Calls hinzuzufügen. ## Installation [Section titled “Installation”](#installation) Installieren Sie das Plugin mit npm: ```bash npm install @capgo/capacitor-realtimekit npx cap sync ``` ## Abhängigkeiten [Section titled “Abhängigkeiten”](#abhängigkeiten) Dieses Plugin verwendet das Cloudflare RealtimeKit SDK: * **iOS**: RealtimeKitCoreiOS (automatisch über Swift Package Manager installiert) * **Android**: `com.cloudflare.realtimekit:ui-android` Version `0.2.2` ### Anpassen der Android RealtimeKit-Version [Section titled “Anpassen der Android RealtimeKit-Version”](#anpassen-der-android-realtimekit-version) Im `build.gradle` Ihrer App: ```kotlin buildscript { ext { realtimekitUiVersion = '0.2.2' // or your desired version } } ``` ## iOS Konfiguration [Section titled “iOS Konfiguration”](#ios-konfiguration) Fügen Sie Folgendes zum `Info.plist` Ihrer App hinzu: ```xml NSCameraUsageDescription We need camera access for video calls NSMicrophoneUsageDescription We need microphone access for audio calls NSPhotoLibraryUsageDescription We need photo library access to share images NSBluetoothPeripheralUsageDescription We need Bluetooth access for audio routing UIBackgroundModes audio voip fetch remote-notification ``` ## Android Konfiguration [Section titled “Android Konfiguration”](#android-konfiguration) Fügen Sie Ihrem `AndroidManifest.xml` die folgenden Berechtigungen hinzu: ```xml ``` ## Grundlegende Verwendung [Section titled “Grundlegende Verwendung”](#grundlegende-verwendung) ### Importieren Sie das Plugin [Section titled “Importieren Sie das Plugin”](#importieren-sie-das-plugin) ```typescript import { CapacitorRealtimekit } from '@capgo/capacitor-realtimekit'; ``` ### Initialisieren Sie das Plugin [Section titled “Initialisieren Sie das Plugin”](#initialisieren-sie-das-plugin) ```typescript async function initializeRealtimeKit() { try { await CapacitorRealtimekit.initialize(); console.log('RealtimeKit initialized'); } catch (error) { console.error('Failed to initialize RealtimeKit:', error); } } ``` ### Starten Sie ein Meeting [Section titled “Starten Sie ein Meeting”](#starten-sie-ein-meeting) ```typescript async function startMeeting(meetingUrl: string) { try { await CapacitorRealtimekit.startMeeting({ url: meetingUrl }); console.log('Meeting started'); } catch (error) { console.error('Failed to start meeting:', error); } } ``` ## Vollständiges Beispiel [Section titled “Vollständiges Beispiel”](#vollständiges-beispiel) Hier finden Sie einen umfassenden Videokonferenzservice: ```typescript import { CapacitorRealtimekit } from '@capgo/capacitor-realtimekit'; export interface MeetingConfig { url: string; displayName?: string; audioEnabled?: boolean; videoEnabled?: boolean; } export class VideoConferenceService { private isInitialized = false; private currentMeetingUrl: string | null = null; async initialize(): Promise { if (this.isInitialized) { console.log('RealtimeKit already initialized'); return true; } try { await CapacitorRealtimekit.initialize(); this.isInitialized = true; console.log('RealtimeKit initialized successfully'); return true; } catch (error) { console.error('Failed to initialize RealtimeKit:', error); return false; } } async startMeeting(config: MeetingConfig): Promise { if (!this.isInitialized) { const initialized = await this.initialize(); if (!initialized) { throw new Error('Failed to initialize RealtimeKit'); } } try { await CapacitorRealtimekit.startMeeting({ url: config.url }); this.currentMeetingUrl = config.url; console.log('Meeting started:', config.url); } catch (error) { console.error('Failed to start meeting:', error); throw error; } } async joinMeeting(meetingUrl: string, displayName?: string): Promise { const config: MeetingConfig = { url: meetingUrl, displayName: displayName }; await this.startMeeting(config); } getCurrentMeetingUrl(): string | null { return this.currentMeetingUrl; } isInMeeting(): boolean { return this.currentMeetingUrl !== null; } async getPluginVersion(): Promise { try { const result = await CapacitorRealtimekit.getPluginVersion(); return result.version; } catch (error) { console.error('Failed to get plugin version:', error); return 'unknown'; } } } // Usage const videoService = new VideoConferenceService(); // Initialize on app start await videoService.initialize(); // Start a meeting await videoService.startMeeting({ url: 'https://your-cloudflare-calls-url.com/meeting/123' }); // Join an existing meeting await videoService.joinMeeting( 'https://your-cloudflare-calls-url.com/meeting/456', 'John Doe' ); ``` ## Einrichtung von Cloudflare-Anrufen [Section titled “Einrichtung von Cloudflare-Anrufen”](#einrichtung-von-cloudflare-anrufen) Um dieses Plugin verwenden zu können, müssen Sie Cloudflare Calls einrichten: ### 1. Cloudflare-Konto erstellen [Section titled “1. Cloudflare-Konto erstellen”](#1-cloudflare-konto-erstellen) Melden Sie sich bei [Cloudflare](https://dash.cloudflare.com/sign-up) an, wenn Sie noch kein Konto haben. ### 2. Anrufe aktivieren API [Section titled “2. Anrufe aktivieren API”](#2-anrufe-aktivieren-api) 1. Navigieren Sie zu Ihrem Cloudflare-Dashboard 2. Gehen Sie zum Abschnitt **Anrufe** 3. Aktivieren Sie die Anrufe API 4. Holen Sie sich Ihre API-Anmeldeinformationen ### 3. Erstellen Sie Besprechungs-URLs [Section titled “3. Erstellen Sie Besprechungs-URLs”](#3-erstellen-sie-besprechungs-urls) Sie benötigen einen Backend-Dienst, um Besprechungs-URLs zu erstellen. Beispiel mit Cloudflare Workers: ```typescript // Cloudflare Worker example export default { async fetch(request: Request): Promise { const { pathname } = new URL(request.url); if (pathname === '/create-meeting') { const meetingId = generateMeetingId(); const meetingUrl = `https://your-app.calls.cloudflare.com/${meetingId}`; return new Response(JSON.stringify({ meetingId, meetingUrl }), { headers: { 'Content-Type': 'application/json' } }); } return new Response('Not found', { status: 404 }); } }; function generateMeetingId(): string { return Math.random().toString(36).substring(2, 15); } ``` ## Erweiterte Nutzung [Section titled “Erweiterte Nutzung”](#erweiterte-nutzung) ### Treffen mit benutzerdefinierter Konfiguration [Section titled “Treffen mit benutzerdefinierter Konfiguration”](#treffen-mit-benutzerdefinierter-konfiguration) ```typescript class MeetingManager { private videoService: VideoConferenceService; constructor() { this.videoService = new VideoConferenceService(); } async createAndJoinMeeting(userName: string): Promise { // Initialize if needed await this.videoService.initialize(); // Call your backend to create a meeting const meetingUrl = await this.createMeetingOnBackend(); // Join the meeting await this.videoService.joinMeeting(meetingUrl, userName); return meetingUrl; } async createMeetingOnBackend(): Promise { const response = await fetch('https://your-api.com/create-meeting', { method: 'POST', headers: { 'Content-Type': 'application/json' } }); const data = await response.json(); return data.meetingUrl; } async shareMeetingLink(meetingUrl: string) { // Use Capacitor Share API if ('share' in navigator) { await (navigator as any).share({ title: 'Join my meeting', text: 'Join me for a video call', url: meetingUrl }); } } } ``` ### Bearbeitungsberechtigungen [Section titled “Bearbeitungsberechtigungen”](#bearbeitungsberechtigungen) ```typescript import { Capacitor } from '@capacitor/core'; async function checkAndRequestPermissions(): Promise { if (Capacitor.getPlatform() === 'web') { // Request browser permissions try { const stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: true }); // Stop the stream, we just needed permission stream.getTracks().forEach(track => track.stop()); return true; } catch (error) { console.error('Permission denied:', error); return false; } } // On native platforms, permissions are requested automatically return true; } // Use before starting a meeting const hasPermissions = await checkAndRequestPermissions(); if (hasPermissions) { await videoService.startMeeting({ url: meetingUrl }); } ``` ## Integration reagieren [Section titled “Integration reagieren”](#integration-reagieren) ```typescript import { useEffect, useState } from 'react'; import { VideoConferenceService } from './VideoConferenceService'; function VideoCallComponent() { const [videoService] = useState(() => new VideoConferenceService()); const [isReady, setIsReady] = useState(false); useEffect(() => { videoService.initialize().then(setIsReady); }, []); const joinMeeting = async (meetingUrl: string) => { if (!isReady) { console.error('Video service not ready'); return; } try { await videoService.joinMeeting(meetingUrl, 'User Name'); } catch (error) { console.error('Failed to join meeting:', error); } }; return (
); } ``` ## Vue-Integration [Section titled “Vue-Integration”](#vue-integration) ```typescript import { ref, onMounted } from 'vue'; import { VideoConferenceService } from './VideoConferenceService'; export default { setup() { const videoService = new VideoConferenceService(); const isReady = ref(false); onMounted(async () => { isReady.value = await videoService.initialize(); }); const joinMeeting = async (meetingUrl: string) => { if (!isReady.value) { console.error('Video service not ready'); return; } try { await videoService.joinMeeting(meetingUrl, 'User Name'); } catch (error) { console.error('Failed to join meeting:', error); } }; return { isReady, joinMeeting }; } }; ``` ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) 1. **Frühzeitig initialisieren**: Initialisieren Sie RealtimeKit, wenn Ihre App startet 2. **Behandeln von Fehlern**: Wickeln Sie Aufrufe immer in Try-Catch-Blöcke ein 3. **Berechtigungen anfordern**: Stellen Sie vor dem Start sicher, dass Sie über Kamera-/Mikrofonberechtigungen verfügen 4. **Netzwerk testen**: Überprüfen Sie die Internetverbindung, bevor Sie beitreten 5. **Hintergrundaudio**: Hintergrundmodi für iOS konfigurieren 6. **Benutzererfahrung**: Ladezustände während der Initialisierung anzeigen 7. **URLs bereinigen**: Überprüfen Sie Besprechungs-URLs vor der Verwendung ## Häufige Probleme [Section titled “Häufige Probleme”](#häufige-probleme) ### Besprechung startet nicht [Section titled “Besprechung startet nicht”](#besprechung-startet-nicht) ```typescript async function troubleshootMeeting(meetingUrl: string) { // Check initialization const version = await videoService.getPluginVersion(); console.log('Plugin version:', version); // Verify URL format if (!meetingUrl.startsWith('https://')) { console.error('Invalid meeting URL, must use HTTPS'); return; } // Try starting meeting try { await videoService.startMeeting({ url: meetingUrl }); } catch (error) { console.error('Meeting failed:', error); } } ``` ### Berechtigung verweigert [Section titled “Berechtigung verweigert”](#berechtigung-verweigert) Stellen Sie unter iOS sicher, dass Info.plist alle erforderlichen Nutzungsbeschreibungen enthält. Überprüfen Sie auf Android, ob AndroidManifest.xml-Berechtigungen vorhanden sind. ### Audioprobleme auf iOS [Section titled “Audioprobleme auf iOS”](#audioprobleme-auf-ios) Stellen Sie sicher, dass der Hintergrund-Audiomodus in Info.plist konfiguriert ist: ```xml UIBackgroundModes audio voip ``` ## Nächste Schritte [Section titled “Nächste Schritte”](#nächste-schritte) * Entdecken Sie die [API Referenz](https://github.com/Cap-go/capacitor-realtimekit#api) für eine vollständige Methodendokumentation * Lesen Sie [Cloudflare Calls Documentation](https://developers.cloudflare.com/calls/) * Schauen Sie sich die \[Beispiel-App] an () – Eine vollständige Implementierung finden Sie im [Tutorial](/plugins/capacitor-realtimekit). # @capgo/capacitor-ricoh360-camera > Steuern Sie Ricoh 360°-Kameras für immersive Panoramafotos und Videoaufnahmen mit vollständiger Geräteintegration. 360°-Aufnahme Nehmen Sie immersive Panoramafotos und -videos auf 🌐 Gerätesteuerung Vollständige Kontrolle über Ricoh Theta-Kameraeinstellungen 📸 Live-Vorschau Echtzeit-360°-Vorschau-Streaming 👁️ Umfassende Dokumentation Schauen Sie sich die [Dokumentation](/docs/plugins/ricoh360-camera/getting-started/) an, um das Plugin in nur wenigen Minuten zu meistern. # Erste Schritte mit Ricoh 360° Camera > Lernen Sie, wie Sie das Ricoh 360° Camera Plugin installieren und verwenden, um Ricoh Theta-Kameras in Ihrer Capacitor-App zu steuern ## Installation [Section titled “Installation”](#installation) * npm ```bash npm install @capgo/capacitor-ricoh360-camera npx cap sync ``` * yarn ```bash yarn add @capgo/capacitor-ricoh360-camera npx cap sync ``` * pnpm ```bash pnpm add @capgo/capacitor-ricoh360-camera npx cap sync ``` * bun ```bash bun add @capgo/capacitor-ricoh360-camera npx cap sync ``` ## Plattformkonfiguration [Section titled “Plattformkonfiguration”](#plattformkonfiguration) ### iOS [Section titled “iOS”](#ios) Fügen Sie Kamera- und Fotobibliotheksberechtigungen zu Ihrer `Info.plist` hinzu: ```xml NSCameraUsageDescription Diese App benötigt Zugriff auf die Kamera, um 360°-Fotos und Videos aufzunehmen NSPhotoLibraryAddUsageDescription Diese App benötigt Zugriff zum Speichern von 360°-Fotos und Videos in Ihrer Bibliothek ``` ### Android [Section titled “Android”](#android) Fügen Sie erforderliche Berechtigungen zu Ihrer `AndroidManifest.xml` hinzu: ```xml ``` ## Unterstützte Geräte [Section titled “Unterstützte Geräte”](#unterstützte-geräte) Dieses Plugin unterstützt Ricoh Theta-Kameras: * Ricoh Theta V * Ricoh Theta Z1 * Ricoh Theta SC2 * Ricoh Theta X ## Verwendungsbeispiel [Section titled “Verwendungsbeispiel”](#verwendungsbeispiel) ```typescript import { Ricoh360Camera } from '@capgo/capacitor-ricoh360-camera'; // Kameraverbindung initialisieren await Ricoh360Camera.initialize(); // Mit Ricoh Theta-Kamera über WiFi verbinden await Ricoh360Camera.connect({ ssid: 'THETAYL12345678', password: '12345678' }); // Live-Vorschau starten await Ricoh360Camera.startPreview({ container: 'preview-container' // ID des HTML-Elements }); // 360°-Foto aufnehmen const result = await Ricoh360Camera.capturePhoto(); console.log('Foto gespeichert:', result.filePath); // 360°-Video aufnehmen starten await Ricoh360Camera.startRecording(); // Nach einiger Zeit Aufnahme beenden setTimeout(async () => { const video = await Ricoh360Camera.stopRecording(); console.log('Video gespeichert:', video.filePath); }, 10000); // Vorschau beenden await Ricoh360Camera.stopPreview(); // Von Kamera trennen await Ricoh360Camera.disconnect(); ``` ## API-Referenz [Section titled “API-Referenz”](#api-referenz) ### initialize() [Section titled “initialize()”](#initialize) ```typescript initialize() => Promise ``` Initialisieren Sie das Kamera-Plugin. ### connect(options) [Section titled “connect(options)”](#connectoptions) ```typescript connect(options: ConnectOptions) => Promise ``` Verbinden Sie sich mit einer Ricoh Theta-Kamera über WiFi. | Parameter | Typ | | ------------- | ---------------- | | **`options`** | `ConnectOptions` | ### disconnect() [Section titled “disconnect()”](#disconnect) ```typescript disconnect() => Promise ``` Trennen Sie sich von der Kamera. ### startPreview(options) [Section titled “startPreview(options)”](#startpreviewoptions) ```typescript startPreview(options: PreviewOptions) => Promise ``` Starten Sie den Live-360°-Vorschau-Stream. | Parameter | Typ | | ------------- | ---------------- | | **`options`** | `PreviewOptions` | ### stopPreview() [Section titled “stopPreview()”](#stoppreview) ```typescript stopPreview() => Promise ``` Beenden Sie den Live-Vorschau-Stream. ### capturePhoto(options?) [Section titled “capturePhoto(options?)”](#capturephotooptions) ```typescript capturePhoto(options?: CaptureOptions) => Promise ``` Nehmen Sie ein 360°-Foto auf. | Parameter | Typ | | ------------- | --------------------------- | | **`options`** | `CaptureOptions` (optional) | **Rückgabe:** `Promise` ### startRecording(options?) [Section titled “startRecording(options?)”](#startrecordingoptions) ```typescript startRecording(options?: RecordingOptions) => Promise ``` Starten Sie die Aufnahme von 360°-Video. | Parameter | Typ | | ------------- | ----------------------------- | | **`options`** | `RecordingOptions` (optional) | ### stopRecording() [Section titled “stopRecording()”](#stoprecording) ```typescript stopRecording() => Promise ``` Beenden Sie die Videoaufnahme. **Rückgabe:** `Promise` ### getCameraInfo() [Section titled “getCameraInfo()”](#getcamerainfo) ```typescript getCameraInfo() => Promise ``` Erhalten Sie Kamerainformationen und -funktionen. **Rückgabe:** `Promise` ### setExposure(options) [Section titled “setExposure(options)”](#setexposureoptions) ```typescript setExposure(options: ExposureOptions) => Promise ``` Legen Sie Kamerabelichtungseinstellungen fest. | Parameter | Typ | | ------------- | ----------------- | | **`options`** | `ExposureOptions` | ### setWhiteBalance(options) [Section titled “setWhiteBalance(options)”](#setwhitebalanceoptions) ```typescript setWhiteBalance(options: WhiteBalanceOptions) => Promise ``` Legen Sie den Weißabgleichsmodus fest. | Parameter | Typ | | ------------- | --------------------- | | **`options`** | `WhiteBalanceOptions` | ## Schnittstellen [Section titled “Schnittstellen”](#schnittstellen) ### ConnectOptions [Section titled “ConnectOptions”](#connectoptions-1) | Eigenschaft | Typ | Beschreibung | | -------------- | -------- | -------------------------- | | **`ssid`** | `string` | WiFi SSID der Ricoh-Kamera | | **`password`** | `string` | WiFi-Passwort der Kamera | ### PreviewOptions [Section titled “PreviewOptions”](#previewoptions) | Eigenschaft | Typ | Beschreibung | | --------------- | -------- | ------------------------------------- | | **`container`** | `string` | HTML-Element-ID für Preview-Container | ### CaptureOptions [Section titled “CaptureOptions”](#captureoptions) | Eigenschaft | Typ | Beschreibung | | ------------------- | --------- | ------------------------------------- | | **`quality`** | `number` | Bildqualität (1-100) (optional) | | **`saveToGallery`** | `boolean` | In Gerätegalerie speichern (optional) | ### RecordingOptions [Section titled “RecordingOptions”](#recordingoptions) | Eigenschaft | Typ | Beschreibung | | ----------------- | -------- | ----------------------------------------------------- | | **`maxDuration`** | `number` | Maximale Aufnahmedauer in Sekunden (optional) | | **`quality`** | `string` | Videoqualität: ‘high’ \| ‘medium’ \| ‘low’ (optional) | ### CaptureResult [Section titled “CaptureResult”](#captureresult) | Eigenschaft | Typ | Beschreibung | | -------------- | -------- | ----------------------------- | | **`filePath`** | `string` | Pfad zur aufgenommenen Datei | | **`fileUrl`** | `string` | URL zum Zugriff auf die Datei | ### CameraInfo [Section titled “CameraInfo”](#camerainfo) | Eigenschaft | Typ | Beschreibung | | --------------------- | -------- | ------------------- | | **`model`** | `string` | Kameramodellname | | **`serialNumber`** | `string` | Kameraserien nummer | | **`firmwareVersion`** | `string` | Firmware-Version | | **`batteryLevel`** | `number` | Akkustand (0-100) | ### ExposureOptions [Section titled “ExposureOptions”](#exposureoptions) | Eigenschaft | Typ | Beschreibung | | ------------- | -------- | --------------------------------------------- | | **`mode`** | `string` | Belichtungsmodus: ‘auto’ \| ‘manual’ \| ‘iso’ | | **`iso`** | `number` | ISO-Wert (100, 200, 400, etc.) (optional) | | **`shutter`** | `number` | Verschlussgeschwindigkeit (optional) | ### WhiteBalanceOptions [Section titled “WhiteBalanceOptions”](#whitebalanceoptions) | Eigenschaft | Typ | Beschreibung | | ----------- | -------- | ----------------------------------------------------------------------------- | | **`mode`** | `string` | Weißabgleich: ‘auto’ \| ‘daylight’ \| ‘cloudy’ \| ‘tungsten’ \| ‘fluorescent’ | ## Vollständiges Beispiel [Section titled “Vollständiges Beispiel”](#vollständiges-beispiel) ```typescript import { Ricoh360Camera } from '@capgo/capacitor-ricoh360-camera'; class Camera360Controller { async setup() { try { // Initialisieren await Ricoh360Camera.initialize(); // Mit Kamera verbinden await Ricoh360Camera.connect({ ssid: 'THETAYL12345678', password: '12345678' }); // Kamerainformationen abrufen const info = await Ricoh360Camera.getCameraInfo(); console.log('Verbunden mit:', info.model); console.log('Akkustand:', info.batteryLevel + '%'); // Belichtung konfigurieren await Ricoh360Camera.setExposure({ mode: 'auto' }); // Weißabgleich konfigurieren await Ricoh360Camera.setWhiteBalance({ mode: 'auto' }); // Vorschau starten await Ricoh360Camera.startPreview({ container: 'camera-preview' }); } catch (error) { console.error('Kamerasetup fehlgeschlagen:', error); } } async takePhoto() { try { const result = await Ricoh360Camera.capturePhoto({ quality: 95, saveToGallery: true }); console.log('Foto aufgenommen:', result.filePath); return result; } catch (error) { console.error('Fotoaufnahme fehlgeschlagen:', error); } } async recordVideo(durationSeconds: number) { try { // Aufnahme starten await Ricoh360Camera.startRecording({ maxDuration: durationSeconds, quality: 'high' }); console.log('Aufnahme gestartet...'); // Nach Dauer beenden setTimeout(async () => { const result = await Ricoh360Camera.stopRecording(); console.log('Video gespeichert:', result.filePath); }, durationSeconds * 1000); } catch (error) { console.error('Videoaufnahme fehlgeschlagen:', error); } } async cleanup() { try { await Ricoh360Camera.stopPreview(); await Ricoh360Camera.disconnect(); console.log('Kamera getrennt'); } catch (error) { console.error('Aufräumen fehlgeschlagen:', error); } } } // Verwendung const camera = new Camera360Controller(); await camera.setup(); await camera.takePhoto(); await camera.recordVideo(10); // 10 Sekunden await camera.cleanup(); ``` ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) * Initialisieren Sie das Plugin immer vor der Verbindung mit der Kamera * Stellen Sie sicher, dass das Gerät mit dem WiFi-Netzwerk der Kamera verbunden ist * Behandeln Sie Fehler elegant, besonders Verbindungsfehler * Stoppen Sie die Vorschau und trennen Sie die Verbindung, wenn Sie fertig sind, um Akku zu sparen * Überprüfen Sie den Kameraakku vor längeren Aufnahmesitzungen * Testen Sie mit echter Ricoh Theta Hardware * Verwenden Sie entsprechende Videoqualität basierend auf Speicherbeschränkungen ## Fehlerbehebung [Section titled “Fehlerbehebung”](#fehlerbehebung) ### Verbindungsprobleme [Section titled “Verbindungsprobleme”](#verbindungsprobleme) * Überprüfen Sie, ob die Kamera eingeschaltet ist * Überprüfen Sie, ob WiFi-SSID und Passwort korrekt sind * Stellen Sie sicher, dass keine andere App mit der Kamera verbunden ist * Setzen Sie WiFi-Einstellungen der Kamera zurück, wenn die Verbindung fehlschlägt ### Vorschau wird nicht angezeigt [Section titled “Vorschau wird nicht angezeigt”](#vorschau-wird-nicht-angezeigt) * Überprüfen Sie, ob das HTML-Container-Element vorhanden ist * Überprüfen Sie, ob Kameraberechtigungen erteilt wurden * Stellen Sie sicher, dass die Vorschau nach erfolgreicher Verbindung gestartet wird ### Aufnahmefehler [Section titled “Aufnahmefehler”](#aufnahmefehler) * Überprüfen Sie den verfügbaren Speicherplatz * Überprüfen Sie, ob der Kameraakku ausreichend ist * Stellen Sie sicher, dass die Kamera nicht im Schlafmodus ist # @capgo/capacitor-screen-orientation > Screen-Ausrichtungs-Plugin mit Unterstützung zur Erkennung der echten physischen Geräteausrichtung - auch wenn die Ausrichtungssperre aktiviert ist. Physische Ausrichtungserkennung Erkennen Sie die echte physische Geräteausrichtung mithilfe von Bewegungssensoren, auch wenn die Ausrichtungssperre aktiviert ist Ausrichtungssperre-Erkennung Bestimmen Sie, ob der Benutzer die Ausrichtungssperre durch Vergleich der physischen vs. UI-Ausrichtung aktiviert hat Plattformübergreifend Funktioniert auf iOS (Core Motion), Android (Beschleunigungsmesser) und Web Echtzeit-Updates Erhalten Sie sofortige Benachrichtigungen, wenn sich die Geräteausrichtung mit Event-Listenern ändert # Erste Schritte mit Screen Orientation > Lernen Sie, wie Sie das Screen Orientation Plugin installieren und verwenden, um Geräteorientierung in Ihrer Capacitor-App zu steuern und zu erkennen 1. **Paket installieren** * npm ```sh npm i @capgo/capacitor-screen-orientation ``` * pnpm ```sh pnpm add @capgo/capacitor-screen-orientation ``` * yarn ```sh yarn add @capgo/capacitor-screen-orientation ``` * bun ```sh bun add @capgo/capacitor-screen-orientation ``` 2. **Mit nativen Projekten synchronisieren** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` 3. **iOS-Konfiguration (optional)** Um physikalische Geräteorientierung mit Bewegungssensoren auf iOS zu erkennen, fügen Sie zu Ihrer `Info.plist` hinzu: ```xml NSMotionUsageDescription Diese App verwendet Bewegungssensoren zur Erkennung der Geräteorientierung. ``` ## Grundlegende Verwendung [Section titled “Grundlegende Verwendung”](#grundlegende-verwendung) ```typescript import { ScreenOrientation } from '@capgo/capacitor-screen-orientation'; // Aktuelle Orientierung abrufen const current = await ScreenOrientation.orientation(); console.log('Aktuelle Orientierung:', current.type); // Im Landscape-Modus sperren await ScreenOrientation.lock({ orientation: 'landscape' }); // Orientierung entsperren await ScreenOrientation.unlock(); // Auf Orientierungsänderungen abhören const listener = await ScreenOrientation.addListener( 'screenOrientationChange', (result) => { console.log('Orientierung geändert:', result.type); } ); ``` ## Erkennung der physikalischen Orientierung [Section titled “Erkennung der physikalischen Orientierung”](#erkennung-der-physikalischen-orientierung) Dieses Plugin hat eine einzigartige Funktion: Es kann die **wahre physikalische Orientierung** des Geräts unter Verwendung von Bewegungssensoren erkennen, auch wenn der Benutzer die Orientierungssperre auf seinem Gerät aktiviert hat. Note Die Benutzeroberfläche respektiert weiterhin die Orientierungssperreinstellung des Benutzers. Motion-Tracking ermöglicht es Ihnen, zu wissen, wie das Gerät physikalisch gehalten wird, aber die Benutzeroberfläche dreht sich nicht, wenn die Orientierungssperre aktiviert ist. ```typescript import { ScreenOrientation } from '@capgo/capacitor-screen-orientation'; // Motion-basiertes Tracking starten await ScreenOrientation.startOrientationTracking({ bypassOrientationLock: true }); // Jetzt zeigen Orientierungsänderungsereignisse die physikalische Orientierung const listener = await ScreenOrientation.addListener( 'screenOrientationChange', (result) => { console.log('Physikalische Orientierung:', result.type); } ); // Überprüfen Sie, ob Orientierungssperre aktiviert ist const lockStatus = await ScreenOrientation.isOrientationLocked(); if (lockStatus.locked) { console.log('Benutzer hat Orientierungssperre aktiviert'); console.log('Physisch:', lockStatus.physicalOrientation); console.log('UI:', lockStatus.uiOrientation); } // Tracking beenden, wenn fertig await ScreenOrientation.stopOrientationTracking(); ``` ## API-Referenz [Section titled “API-Referenz”](#api-referenz) ### orientation() [Section titled “orientation()”](#orientation) Rufen Sie die aktuelle Bildschirmausrichtung ab. ```typescript const result = await ScreenOrientation.orientation(); // Rückgabe: { type: OrientationType } ``` **OrientationType-Werte:** * `'portrait-primary'` - Hochformat, Startknopf unten * `'portrait-secondary'` - Hochformat, umgekehrt * `'landscape-primary'` - Querformat, Startknopf rechts * `'landscape-secondary'` - Querformat, Startknopf links ### lock(options) [Section titled “lock(options)”](#lockoptions) Sperren Sie den Bildschirm auf eine bestimmte Ausrichtung. ```typescript interface OrientationLockOptions { orientation: OrientationLockType; bypassOrientationLock?: boolean; // Motion Tracking aktivieren } await ScreenOrientation.lock({ orientation: 'landscape' }); ``` **OrientationLockType-Werte:** * `'any'` - Beliebige Orientierung * `'natural'` - Natürliche Ausrichtung des Geräts * `'landscape'` - Beliebiger Landscape-Modus * `'portrait'` - Beliebiger Portrait-Modus * `'portrait-primary'` / `'portrait-secondary'` * `'landscape-primary'` / `'landscape-secondary'` ### unlock() [Section titled “unlock()”](#unlock) Entsperren Sie die Bildschirmausrichtung. ```typescript await ScreenOrientation.unlock(); ``` ### startOrientationTracking(options?) [Section titled “startOrientationTracking(options?)”](#startorientationtrackingoptions) Beginnen Sie mit der Verfolgung der physikalischen Geräteausrichtung mit Bewegungssensoren. ```typescript await ScreenOrientation.startOrientationTracking({ bypassOrientationLock: true }); ``` ### stopOrientationTracking() [Section titled “stopOrientationTracking()”](#stoporientationtracking) Stoppen Sie die Motion-basierte Orientierungsverfolgung. ```typescript await ScreenOrientation.stopOrientationTracking(); ``` ### isOrientationLocked() [Section titled “isOrientationLocked()”](#isorientationlocked) Überprüfen Sie, ob die Geräteorientierungssperre aktiviert ist. ```typescript const result = await ScreenOrientation.isOrientationLocked(); // Rückgabe: { // locked: boolean, // physicalOrientation?: OrientationType, // uiOrientation?: OrientationType // } ``` Tip Diese Methode erfordert, dass Motion Tracking aktiv ist, um ordnungsgemäß zu funktionieren. ### addListener(eventName, callback) [Section titled “addListener(eventName, callback)”](#addlistenereventname-callback) Abhören auf Orientierungsänderungen. ```typescript const listener = await ScreenOrientation.addListener( 'screenOrientationChange', (result) => { console.log('Neue Orientierung:', result.type); } ); // Entfernen Sie, wenn fertig await listener.remove(); ``` ### removeAllListeners() [Section titled “removeAllListeners()”](#removealllisteners) Entfernen Sie alle Event-Listener. ```typescript await ScreenOrientation.removeAllListeners(); ``` ## Vollständiges Beispiel [Section titled “Vollständiges Beispiel”](#vollständiges-beispiel) ```typescript import { ScreenOrientation } from '@capgo/capacitor-screen-orientation'; class OrientationManager { private listener: any = null; async init() { // Motion Tracking für Erkennung der physikalischen Ausrichtung starten await ScreenOrientation.startOrientationTracking({ bypassOrientationLock: true }); // Auf Änderungen abhören this.listener = await ScreenOrientation.addListener( 'screenOrientationChange', this.onOrientationChange.bind(this) ); // Anfängliche Orientierung abrufen const { type } = await ScreenOrientation.orientation(); console.log('Anfängliche Orientierung:', type); } onOrientationChange(result: { type: string }) { console.log('Orientierung geändert zu:', result.type); // Benutzeroberfläche basierend auf Orientierung anpassen if (result.type.includes('landscape')) { this.showLandscapeUI(); } else { this.showPortraitUI(); } } showLandscapeUI() { // Landscape-spezifische UI-Anpassungen } showPortraitUI() { // Portrait-spezifische UI-Anpassungen } async lockLandscape() { await ScreenOrientation.lock({ orientation: 'landscape' }); } async lockPortrait() { await ScreenOrientation.lock({ orientation: 'portrait' }); } async freeRotation() { await ScreenOrientation.unlock(); } async checkIfUserHasOrientationLock() { const { locked, physicalOrientation, uiOrientation } = await ScreenOrientation.isOrientationLocked(); if (locked) { console.log(`Gerät wird in ${physicalOrientation} gehalten, aber UI zeigt ${uiOrientation}`); return true; } return false; } async destroy() { await ScreenOrientation.stopOrientationTracking(); if (this.listener) { await this.listener.remove(); } } } ``` ## Plattformnotizen [Section titled “Plattformnotizen”](#plattformnotizen) ### iOS [Section titled “iOS”](#ios) * Verwendet Core Motion Framework für Erkennung der physikalischen Ausrichtung * Erfordert `NSMotionUsageDescription` in Info.plist für Bewegungssensoren * Vollständige Unterstützung für alle Ausrichtungstypen ### Android [Section titled “Android”](#android) * Verwendet Beschleunigungssensor für Erkennung der physikalischen Ausrichtung * Keine zusätzlichen Berechtigungen erforderlich * Vollständige Unterstützung für alle Ausrichtungstypen ### Web [Section titled “Web”](#web) * Verwendet Screen Orientation API * Bewegungssensor-Erkennung ist begrenzt * Einige Browser unterstützen möglicherweise nicht alle Lock-Typen # @capgo/capacitor-screen-recorder > Nehmen Sie den Bildschirm Ihres Geräts mit diesem leistungsstarken Capacitor-Plugin auf, ideal zum Erstellen von Tutorials, Demos und zum Erfassen von App-Interaktionen. Hochqualitative Aufzeichnung Nehmen Sie den Bildschirm in hoher Qualität mit benutzerdefinierten Einstellungen auf 🎥 Audiounterstützung Nehmen Sie mit Mikrofon- und Systemton auf 🎤 Einfache Integration Einfache API mit Start-/Stop-Aufzeichnungsmethoden 😊 Umfassende Dokumentation Lesen Sie die [Dokumentation](/docs/plugins/screen-recorder/getting-started/), um das Plugin in wenigen Minuten zu beherrschen. # Erste Schritte mit Screen Recorder > Lernen Sie, wie Sie das Screen Recorder Plugin installieren und verwenden, um Bildschirmaufnahmen in Ihrer Capacitor-App zu erfassen 1. **Paket installieren** * npm ```sh npm i @capgo/capacitor-screen-recorder ``` * pnpm ```sh pnpm add @capgo/capacitor-screen-recorder ``` * yarn ```sh yarn add @capgo/capacitor-screen-recorder ``` * bun ```sh bun add @capgo/capacitor-screen-recorder ``` 2. **Mit nativen Projekten synchronisieren** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` 3. **Berechtigungen konfigurieren** ### iOS [Section titled “iOS”](#ios) Fügen Sie Verwendungsbeschreibungen zu Ihrer `Info.plist` hinzu: ```xml NSMicrophoneUsageDescription Um Audioaufnahmen mit Bildschirmaufnahmen zu erfassen NSPhotoLibraryUsageDescription Um Bildschirmaufnahmen zu speichern ``` ### Android [Section titled “Android”](#android) Fügen Sie Berechtigungen zu Ihrer `AndroidManifest.xml` hinzu: ```xml ``` ## Verwendung [Section titled “Verwendung”](#verwendung) Importieren Sie das Plugin und verwenden Sie seine Methoden zur Bildschirmaufnahme: ```typescript import { ScreenRecorder } from '@capgo/capacitor-screen-recorder'; // Aufnahme starten const startRecording = async () => { try { await ScreenRecorder.start(); console.log('Aufnahme gestartet'); } catch (error) { console.error('Aufnahmestart fehlgeschlagen:', error); } }; // Aufnahme beenden const stopRecording = async () => { try { const result = await ScreenRecorder.stop(); console.log('Aufnahme gespeichert auf:', result.videoUrl); // Sie können jetzt die Video-URL verwenden // Zum Beispiel freigeben oder abspielen } catch (error) { console.error('Aufnahmebeeendigung fehlgeschlagen:', error); } }; // Überprüfen Sie, ob Bildschirmaufnahme unterstützt wird const checkSupport = async () => { const { value } = await ScreenRecorder.isSupported(); console.log('Bildschirmaufnahme unterstützt:', value); }; // Überprüfen Sie, ob gerade Aufnahme läuft const checkRecordingStatus = async () => { const { value } = await ScreenRecorder.isRecording(); console.log('Derzeit aufnehmen:', value); }; ``` ## API-Referenz [Section titled “API-Referenz”](#api-referenz) ### start() [Section titled “start()”](#start) Starten Sie die Bildschirmaufnahme. ```typescript await ScreenRecorder.start(); ``` ### stop() [Section titled “stop()”](#stop) Beenden Sie die Aufnahme und geben Sie den Videodateipfad zurück. ```typescript interface RecordingResult { videoUrl: string; } const result = await ScreenRecorder.stop(); ``` ### isSupported() [Section titled “isSupported()”](#issupported) Überprüfen Sie, ob die Bildschirmaufnahme auf dem Gerät unterstützt wird. ```typescript const { value } = await ScreenRecorder.isSupported(); // Rückgabe: { value: boolean } ``` ### isRecording() [Section titled “isRecording()”](#isrecording) Überprüfen Sie, ob die Bildschirmaufnahme gerade aktiv ist. ```typescript const { value } = await ScreenRecorder.isRecording(); // Rückgabe: { value: boolean } ``` ## Erweiterte Verwendung [Section titled “Erweiterte Verwendung”](#erweiterte-verwendung) ### Aufnahme mit Optionen [Section titled “Aufnahme mit Optionen”](#aufnahme-mit-optionen) ```typescript // iOS-spezifische Optionen const startWithOptions = async () => { await ScreenRecorder.start({ // iOS nur Optionen recordAudio: true, microphoneAudio: true, showIOSNotification: true, notificationText: "Aufnahme wird durchgeführt..." }); }; ``` ### Vollständiger Aufnahmefluss [Section titled “Vollständiger Aufnahmefluss”](#vollständiger-aufnahmefluss) ```typescript import { ScreenRecorder } from '@capgo/capacitor-screen-recorder'; import { Share } from '@capacitor/share'; export class RecordingService { private isRecording = false; async toggleRecording() { if (this.isRecording) { await this.stopRecording(); } else { await this.startRecording(); } } private async startRecording() { try { // Überprüfen Sie, ob unterstützt const { value: isSupported } = await ScreenRecorder.isSupported(); if (!isSupported) { throw new Error('Bildschirmaufnahme wird nicht unterstützt'); } // Aufnahme starten await ScreenRecorder.start(); this.isRecording = true; console.log('Aufnahme gestartet'); } catch (error) { console.error('Aufnahmestart fehlgeschlagen:', error); throw error; } } private async stopRecording() { try { const result = await ScreenRecorder.stop(); this.isRecording = false; console.log('Aufnahme gespeichert:', result.videoUrl); // Option zum Teilen der Aufnahme await this.shareRecording(result.videoUrl); } catch (error) { console.error('Aufnahmebeeendigung fehlgeschlagen:', error); throw error; } } private async shareRecording(videoUrl: string) { try { await Share.share({ title: 'Bildschirmaufnahme', text: 'Schauen Sie sich meine Bildschirmaufnahme an!', url: videoUrl, dialogTitle: 'Aufnahme teilen' }); } catch (error) { console.error('Aufnahmefreigabe fehlgeschlagen:', error); } } } ``` ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) 1. **Unterstützung überprüfen** ```typescript const { value } = await ScreenRecorder.isSupported(); if (!value) { // Aufnahmefunktion ausblenden oder Alternative anzeigen } ``` 2. **Berechtigungen ordnungsgemäß handhaben** Das System fordert unter iOS automatisch auf Berechtigungen an. Unter Android sollten Sie erforderliche Berechtigungen anfordern. 3. **Benutzerrückmeldung geben** Zeigen Sie klare Indikatoren, wenn die Aufnahme aktiv ist: ```typescript let recordingInterval: any; const startRecording = async () => { await ScreenRecorder.start(); // Aufnahmeindikat anzeigen recordingInterval = setInterval(() => { // UI aktualisieren, um Aufnahmedauer anzuzeigen }, 1000); }; const stopRecording = async () => { clearInterval(recordingInterval); await ScreenRecorder.stop(); }; ``` 4. **Unterbrechen behandeln** ```typescript import { App } from '@capacitor/app'; App.addListener('appStateChange', async ({ isActive }) => { if (!isActive) { // Erwägen Sie, die Aufnahme zu stoppen, wenn die App in den Hintergrund geht const { value } = await ScreenRecorder.isRecording(); if (value) { await ScreenRecorder.stop(); } } }); ``` ## Plattformnotizen [Section titled “Plattformnotizen”](#plattformnotizen) ### iOS [Section titled “iOS”](#ios-1) * Erfordert iOS 11.0+ * Verwendet `ReplayKit` Framework * System zeigt Aufnahmeindikat in der Statusleiste * Der Benutzer muss Aufnahmebeginn bestätigen ### Android [Section titled “Android”](#android-1) * Erfordert Android 5.0 (API 21)+ * Verwendet `MediaProjection` API * Zeigt Benachrichtigung während der Aufnahme * Einige Geräte haben möglicherweise herstellerspezifische Einschränkungen ### Web [Section titled “Web”](#web) * Nicht auf Web-Plattform unterstützt * Gibt `isSupported: false` zurück ## Fehlerbehebung [Section titled “Fehlerbehebung”](#fehlerbehebung) 1. **Aufnahmestart schlägt fehl** * Stellen Sie sicher, dass alle Berechtigungen erteilt wurden * Überprüfen Sie, ob eine andere App bereits aufnimmt * Überprüfen Sie, ob das Gerät Bildschirmaufnahmen unterstützt 2. **Kein Ton in der Aufnahme** * Überprüfen Sie Mikrofonberechtigungen * Stellen Sie sicher, dass die `recordAudio`-Option aktiviert ist * Einige Geräte unterstützen möglicherweise keine Systemtonautzeichnung 3. **Videodatei nicht gefunden** * Überprüfen Sie Dateiberechtigungen * Stellen Sie sicher, dass ausreichend Speicherplatz vorhanden ist * Überprüfen Sie, dass die zurückgegebene Video-URL gültig ist # @capgo/capacitor-shake > Erkennen Sie Schüttelgesten in Ihrer App, um Aktionen auszulösen, Debug-Menüs anzuzeigen oder interaktive Erlebnisse zu erstellen. Einfache Gestenerkennung Erkennen Sie einfach, wenn Benutzer ihr Gerät schütteln 📱 Anpassbare Empfindlichkeit Passen Sie die Empfindlichkeit der Schüttelerkennung an Ihre Bedürfnisse an ⚡ Plattformübergreifend Funktioniert nahtlos auf iOS- und Android-Geräten 🌍 Umfassende Dokumentation Schauen Sie sich die [Dokumentation](/docs/plugins/shake/getting-started/) an, um das Plugin in nur wenigen Minuten zu meistern. # Erste Schritte > Erfahren Sie, wie Sie das Capacitor Shake-Plugin installieren und konfigurieren, um Schüttelgesten zu erkennen und interaktive Erlebnisse in Ihrer App zu erstellen. 1. **Installieren Sie das Paket** * npm ```sh npm i @capgo/capacitor-shake ``` * pnpm ```sh pnpm add @capgo/capacitor-shake ``` * yarn ```sh yarn add @capgo/capacitor-shake ``` * bun ```sh bun add @capgo/capacitor-shake ``` 2. **Mit nativen Projekten synchronisieren** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` 3. **Konfigurieren Sie das Plugin** **Grundlegendes Verwendungsbeispiel:** ```typescript import { Shake } from '@capgo/capacitor-shake'; // Beginnen Sie mit dem Lauschen auf Schüttelgesten await Shake.start(); // Lauschen Sie auf Schüttelereignisse Shake.addListener('shake', () => { console.log('Gerät wurde geschüttelt!'); // Führen Sie hier Ihre Aktion aus }); ``` **Empfindlichkeitskonfiguration:** ```typescript // Schüttelempfindlichkeit konfigurieren await Shake.start({ threshold: 3.5 // Empfindlichkeit anpassen (Standard: 3.5) }); ``` * iOS Für iOS ist keine zusätzliche Einrichtung erforderlich. * Android Für Android ist keine zusätzliche Einrichtung erforderlich. 4. **Beenden Sie das Lauschen auf Schüttelgesten** ```typescript // Schüttelerkennung stoppen, wenn nicht benötigt await Shake.stop(); // Bestimmten Listener entfernen const handle = await Shake.addListener('shake', () => { console.log('Geschüttelt!'); }); handle.remove(); ``` 5. **Beispielimplementierung** ```typescript import { Shake } from '@capgo/capacitor-shake'; import { App } from '@capacitor/app'; export class ShakeService { private shakeListener: any; async initialize() { // Schüttelerkennung starten await Shake.start({ threshold: 4.0 }); // Schüttel-Listener hinzufügen this.shakeListener = await Shake.addListener('shake', () => { this.handleShake(); }); // Beim Pausieren der App aufräumen App.addListener('pause', () => { Shake.stop(); }); // Beim Fortsetzen der App fortsetzen App.addListener('resume', () => { Shake.start({ threshold: 4.0 }); }); } private handleShake() { console.log('Schütteln erkannt!'); // Debug-Menü anzeigen, Daten aktualisieren oder eine beliebige Aktion auslösen } async cleanup() { if (this.shakeListener) { this.shakeListener.remove(); } await Shake.stop(); } } ``` ## API-Referenz [Section titled “API-Referenz”](#api-referenz) ### Methoden [Section titled “Methoden”](#methoden) #### `start(options?: ShakeOptions)` [Section titled “start(options?: ShakeOptions)”](#startoptions-shakeoptions) Beginnen Sie mit dem Lauschen auf Schüttelgesten. **Parameter:** * `options` (optional): Konfigurationsobjekt * `threshold`: number - Empfindlichkeitsschwelle (Standard: 3.5) #### `stop()` [Section titled “stop()”](#stop) Beenden Sie das Lauschen auf Schüttelgesten. #### `addListener('shake', callback: () => void)` [Section titled “addListener('shake', callback: () => void)”](#addlistenershake-callback---void) Fügen Sie einen Listener für Schüttelereignisse hinzu. **Rückgabe:** Promise mit einem Handle zum Entfernen des Listeners ### Schnittstellen [Section titled “Schnittstellen”](#schnittstellen) ```typescript interface ShakeOptions { threshold?: number; // Schüttelempfindlichkeitsschwelle } ``` ## Plattformhinweise [Section titled “Plattformhinweise”](#plattformhinweise) ### iOS [Section titled “iOS”](#ios) * Verwendet den Beschleunigungsmesser des Geräts zur Erkennung von Schüttelgesten * Funktioniert sowohl im Vordergrund als auch im Hintergrund (wenn die App Hintergrundberechtigungen hat) ### Android [Section titled “Android”](#android) * Verwendet den SensorManager zur Erkennung von Schüttelereignissen * Erfordert keine speziellen Berechtigungen * Funktioniert, wenn die App im Vordergrund ist ## Häufige Anwendungsfälle [Section titled “Häufige Anwendungsfälle”](#häufige-anwendungsfälle) 1. **Debug-Menü**: Entwickleroptionen anzeigen, wenn das Gerät geschüttelt wird 2. **Feedback**: Feedback-Formular oder Fehlerbericht auslösen 3. **Aktualisieren**: App-Daten aktualisieren oder Cache leeren 4. **Spiele**: Schütteln als Spielsteuerungsmechanismus verwenden 5. **Rückgängig machen**: Schüttel-zum-Rückgängigmachen-Funktionalität implementieren ## Fehlerbehebung [Section titled “Fehlerbehebung”](#fehlerbehebung) **Schütteln wird nicht erkannt:** * Stellen Sie sicher, dass das Plugin mit `Shake.start()` gestartet wurde * Versuchen Sie, den Schwellenwert anzupassen (niedriger = empfindlicher) * Überprüfen Sie, ob Listener ordnungsgemäß registriert sind **Zu empfindlich/nicht empfindlich genug:** * Passen Sie den `threshold`-Parameter in den `start()`-Optionen an * Werte liegen typischerweise zwischen 2.0 (sehr empfindlich) und 5.0 (weniger empfindlich) # @capgo/capacitor-share-target > Ermöglichen Sie Ihrer App, Text, Bilder und Dateien, die von anderen Apps freigegeben werden, mit plattformübergreifender Unterstützung zu empfangen. Universelles Teilen Empfangen Sie freigegebene Inhalte von jeder App 🔗 Mehrere Inhaltstypen Verwalten Sie Text, Bilder, Videos und Dateien 📁 Plattformübergreifend iOS- und Android-Unterstützung mit nativer Integration 📱 Ereignisgesteuert Hören Sie in Echtzeit auf Freigabeereignisse 📡 Flexible Konfiguration Passen Sie akzeptierte MIME-Typen an 🎛️ Erste Schritte Lesen Sie den [Leitfaden für Erste Schritte](/docs/plugins/share-target/getting-started/), um das Plugin zu installieren und zu konfigurieren. # Erste Schritte mit Share Target > Erfahren Sie, wie Sie dafür sorgen, dass Ihre App freigegebene Inhalte von anderen Apps empfängt Dieser Leitfaden führt Sie durch die Integration des Capacitor Share Target-Plugins, damit Ihre App freigegebene Inhalte von anderen Anwendungen empfangen kann. ## Installation [Section titled “Installation”](#installation) Installieren Sie das Plugin mit npm: ```bash npm install @capgo/capacitor-share-target npx cap sync ``` ## Android Konfiguration [Section titled “Android Konfiguration”](#android-konfiguration) Fügen Sie Ihrem `AndroidManifest.xml` im Tag `` Absichtsfilter hinzu: ### Textinhalt akzeptieren [Section titled “Textinhalt akzeptieren”](#textinhalt-akzeptieren) ```xml ``` ### Bilder akzeptieren [Section titled “Bilder akzeptieren”](#bilder-akzeptieren) ```xml ``` ### Alle Inhaltstypen akzeptieren [Section titled “Alle Inhaltstypen akzeptieren”](#alle-inhaltstypen-akzeptieren) ```xml ``` ## iOS Konfiguration [Section titled “iOS Konfiguration”](#ios-konfiguration) Für iOS müssen Sie eine Share-Erweiterung erstellen: ### 1. Share-Erweiterung in Xcode erstellen [Section titled “1. Share-Erweiterung in Xcode erstellen”](#1-share-erweiterung-in-xcode-erstellen) 1. Öffnen Sie Ihr Projekt in Xcode 2. Gehen Sie zu **Datei > Neu > Ziel** 3. Wählen Sie **Erweiterung teilen** und klicken Sie auf **Weiter** 4. Benennen Sie es (z. B. „ShareExtension“) und klicken Sie auf **Fertig stellen** 5. Wenn Sie dazu aufgefordert werden, aktivieren Sie das Schema ### 2. App-Gruppe konfigurieren [Section titled “2. App-Gruppe konfigurieren”](#2-app-gruppe-konfigurieren) 1. Gehen Sie in Ihrem Haupt-App-Ziel zu **Signaturen und Funktionen** 2. Klicken Sie auf \***+ Capability** und fügen Sie **App-Gruppen** hinzu. 3. Erstellen oder wählen Sie eine App-Gruppe aus (z. B. `group.com.yourcompany.yourapp`). 4. Wiederholen Sie den Vorgang für das Share Extension-Ziel ### 3. Share-Erweiterungscode aktualisieren [Section titled “3. Share-Erweiterungscode aktualisieren”](#3-share-erweiterungscode-aktualisieren) Die Share-Erweiterung muss freigegebene Daten im App-Gruppencontainer speichern, damit Ihre Haupt-App darauf zugreifen kann. Ausführliche Informationen zur Einrichtung von iOS finden Sie in der Share Extension-Dokumentation von [Apple](https://developer.apple.com/documentation/uikit/uiactivityviewcontroller). ## Grundlegende Verwendung [Section titled “Grundlegende Verwendung”](#grundlegende-verwendung) ### Importieren Sie das Plugin [Section titled “Importieren Sie das Plugin”](#importieren-sie-das-plugin) ```typescript import { CapacitorShareTarget } from '@capgo/capacitor-share-target'; ``` ### Auf freigegebene Inhalte achten [Section titled “Auf freigegebene Inhalte achten”](#auf-freigegebene-inhalte-achten) ```typescript CapacitorShareTarget.addListener('shareReceived', (event) => { console.log('Received share event'); console.log('Title:', event.title); console.log('Texts:', event.texts); // Handle shared files if (event.files && event.files.length > 0) { event.files.forEach(file => { console.log(`File: ${file.name}`); console.log(`Type: ${file.mimeType}`); console.log(`URI: ${file.uri}`); }); } }); ``` ## Vollständiges Beispiel [Section titled “Vollständiges Beispiel”](#vollständiges-beispiel) Hier ist ein umfassendes Beispiel für den Umgang mit verschiedenen Arten von geteilten Inhalten: ```typescript import { CapacitorShareTarget } from '@capgo/capacitor-share-target'; class ShareTargetService { private listener: any; initialize() { this.listener = CapacitorShareTarget.addListener('shareReceived', (event) => { this.handleSharedContent(event); }); console.log('Share target listener initialized'); } handleSharedContent(event: any) { console.log('=== Share Received ==='); // Handle title if (event.title) { console.log('Title:', event.title); this.showNotification('Shared: ' + event.title); } // Handle text content if (event.texts && event.texts.length > 0) { event.texts.forEach((text: string, index: number) => { console.log(`Text ${index + 1}:`, text); this.processSharedText(text); }); } // Handle files if (event.files && event.files.length > 0) { console.log(`Received ${event.files.length} file(s)`); event.files.forEach((file: any) => { console.log('File details:'); console.log(' Name:', file.name); console.log(' Type:', file.mimeType); console.log(' URI:', file.uri); this.processSharedFile(file); }); } } processSharedText(text: string) { // Check if it's a URL if (this.isURL(text)) { console.log('Shared URL detected:', text); // Handle URL (e.g., create bookmark) this.saveBookmark(text); } else { console.log('Shared text detected'); // Handle plain text (e.g., create note) this.createNote(text); } } processSharedFile(file: any) { const fileType = file.mimeType.split('/')[0]; switch (fileType) { case 'image': console.log('Processing shared image'); this.handleImage(file); break; case 'video': console.log('Processing shared video'); this.handleVideo(file); break; case 'audio': console.log('Processing shared audio'); this.handleAudio(file); break; case 'application': console.log('Processing shared document'); this.handleDocument(file); break; default: console.log('Processing generic file'); this.handleGenericFile(file); } } handleImage(file: any) { // Process image file console.log('Saving image:', file.name); // Implementation: Save to gallery, upload, etc. } handleVideo(file: any) { // Process video file console.log('Saving video:', file.name); } handleAudio(file: any) { // Process audio file console.log('Saving audio:', file.name); } handleDocument(file: any) { // Process document file console.log('Saving document:', file.name); } handleGenericFile(file: any) { // Process generic file console.log('Saving file:', file.name); } isURL(text: string): boolean { try { new URL(text); return true; } catch { return false; } } saveBookmark(url: string) { console.log('Creating bookmark for:', url); // Implementation } createNote(text: string) { console.log('Creating note with text:', text.substring(0, 50)); // Implementation } showNotification(message: string) { console.log('Notification:', message); // Show toast or notification } cleanup() { if (this.listener) { this.listener.remove(); } } } // Usage const shareTarget = new ShareTargetService(); shareTarget.initialize(); // Cleanup when app closes // shareTarget.cleanup(); ``` ## Integration reagieren [Section titled “Integration reagieren”](#integration-reagieren) ```typescript import { useEffect } from 'react'; import { CapacitorShareTarget } from '@capgo/capacitor-share-target'; function useShareTarget(onShareReceived: (event: any) => void) { useEffect(() => { const listener = CapacitorShareTarget.addListener('shareReceived', onShareReceived); return () => { listener.remove(); }; }, [onShareReceived]); } // Usage in component function App() { useShareTarget((event) => { console.log('Share received:', event); // Handle shared content }); return
Your App
; } ``` ## Vue-Integration [Section titled “Vue-Integration”](#vue-integration) ```typescript import { onMounted, onUnmounted } from 'vue'; import { CapacitorShareTarget } from '@capgo/capacitor-share-target'; export default { setup() { let listener: any; onMounted(() => { listener = CapacitorShareTarget.addListener('shareReceived', (event) => { console.log('Share received:', event); // Handle shared content }); }); onUnmounted(() => { if (listener) { listener.remove(); } }); } }; ``` ## Umgang mit verschiedenen Inhaltstypen [Section titled “Umgang mit verschiedenen Inhaltstypen”](#umgang-mit-verschiedenen-inhaltstypen) ### URLs [Section titled “URLs”](#urls) ```typescript if (event.texts && event.texts.length > 0) { const text = event.texts[0]; if (text.startsWith('http://') || text.startsWith('https://')) { // Handle URL window.open(text, '_blank'); } } ``` ### Bilder [Section titled “Bilder”](#bilder) ```typescript if (event.files) { const images = event.files.filter(f => f.mimeType.startsWith('image/')); images.forEach(async (image) => { // Read and display image const response = await fetch(image.uri); const blob = await response.blob(); const imageUrl = URL.createObjectURL(blob); // Display or process image console.log('Image URL:', imageUrl); }); } ``` ### Mehrere Dateien [Section titled “Mehrere Dateien”](#mehrere-dateien) ```typescript if (event.files && event.files.length > 1) { console.log(`Processing ${event.files.length} files`); const processPromises = event.files.map(file => processFile(file) ); await Promise.all(processPromises); console.log('All files processed'); } ``` ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) 1. **Mehrere Inhaltstypen verarbeiten**: Seien Sie auf den Empfang von Texten, URLs und Dateien vorbereitet 2. **Inhalt validieren**: Überprüfen Sie die MIME-Typen vor der Verarbeitung 3. **Feedback geben**: Zeigen Sie den Benutzern, was sie erhalten haben 4. **Fehlerbehandlung**: Datei-URIs sind möglicherweise ungültig oder nicht zugänglich 5. **Listener bereinigen**: Entfernen Sie Listener, wenn sie nicht benötigt werden 6. **Gründlich testen**: Testen Sie mit verschiedenen Apps und Inhaltstypen 7. **Berechtigungen anfordern**: Für einige Dateitypen sind möglicherweise zusätzliche Berechtigungen erforderlich ## Häufige Probleme [Section titled “Häufige Probleme”](#häufige-probleme) ### Teilen funktioniert nicht auf Android [Section titled “Teilen funktioniert nicht auf Android”](#teilen-funktioniert-nicht-auf-android) Stellen Sie sicher, dass Absichtsfilter in `AndroidManifest.xml` im Tag `` mit `android.intent.action.MAIN` korrekt konfiguriert sind. ### iOS Freigabeerweiterung wird nicht angezeigt [Section titled “iOS Freigabeerweiterung wird nicht angezeigt”](#ios-freigabeerweiterung-wird-nicht-angezeigt) 1. Stellen Sie sicher, dass die App-Gruppe in beiden Zielen konfiguriert ist 2. Stellen Sie sicher, dass die Share-Erweiterung aktiviert ist 3. Überprüfen Sie, ob Info.plist in der Share Extension die richtige Konfiguration hat ### Dateizugriffsfehler [Section titled “Dateizugriffsfehler”](#dateizugriffsfehler) ```typescript try { const response = await fetch(file.uri); const blob = await response.blob(); // Process blob } catch (error) { console.error('Failed to access file:', error); // Show error to user } ``` ## Nächste Schritte [Section titled “Nächste Schritte”](#nächste-schritte) * Entdecken Sie die [API-Referenz](https://github.com/Cap-go/capacitor-share-target#api) für eine vollständige Methodendokumentation * Sehen Sie sich die [Beispiel-App](https://github.com/Cap-go/capacitor-share-target/tree/main/example) für eine erweiterte Nutzung an – Vollständige Implementierungsbeispiele finden Sie im [Tutorial](/plugins/capacitor-share-target). # @capgo/capacitor-sim > Rufen Sie SIM-Betreiber-Metadaten ab und handhaben Sie Berechtigungsabläufe mit einer leichtgewichtigen Capacitor-Brücke. Verwenden Sie Capgo SIM, um Betreiberdaten, Slot-Indizes und Berechtigungsstatus abzufragen, während Sie in Capacitor bleiben. Multi-SIM-Unterstützung Zählen Sie jede installierte SIM zusammen mit Slot-Indizes und Abonnement-IDs auf. Berechtigungshelfer Überprüfen und fordern Sie die auf Android erforderliche Telefonieberechtigung an. Konsistentes Schema Arbeiten Sie mit normalisierten Betreiber-Feldern wie MCC, MNC, ISO-Land und Telefonnummern. iOS-bewusst Behandeln Sie Plattformpecüliarheiten wie begrenzte Betreiberdaten auf iOS 16.4+. Schauen Sie sich den Leitfaden für Erste Schritte an, um Berechtigungen und Verbrauchsmuster zu integrieren. # Erste Schritte > Rufen Sie SIM-Kartendetails ab und verwalten Sie Laufzeitberechtigungen mit dem SIM-Plugin Capgo. 1. **Installieren Sie das Plugin** * npm ```sh npm i @capgo/capacitor-sim ``` * pnpm ```sh pnpm add @capgo/capacitor-sim ``` * yarn ```sh yarn add @capgo/capacitor-sim ``` * bun ```sh bun add @capgo/capacitor-sim ``` 2. **Plattformen synchronisieren** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Berechtigungen [Section titled “Berechtigungen”](#berechtigungen) * **Android**: Stellen Sie sicher, dass `android.permission.READ_PHONE_STATE` (und bei Android 13+, `READ_BASIC_PHONE_STATE`) gewährt wird. Capacitor enthält die Manifest-Deklarationen, aber Sie müssen die Erlaubnis anfordern, bevor Sie `getSimCards` aufrufen. * **iOS**: Anbieterinformationen sind automatisch verfügbar; Die Plattform fordert nicht zur Eingabe zusätzlicher Berechtigungen auf. Beachten Sie die begrenzten Daten, die aufgrund von Betriebssystemeinschränkungen auf iOS 16.4+ zurückgegeben werden. ## Laufzeitzugriff anfordern (Android) [Section titled “Laufzeitzugriff anfordern (Android)”](#laufzeitzugriff-anfordern-android) ```typescript import { Sim } from '@capgo/capacitor-sim'; const status = await Sim.checkPermissions(); if (status.readSimCard !== 'granted') { const updated = await Sim.requestPermissions(); if (updated.readSimCard !== 'granted') { throw new Error('Telephony permission denied'); } } ``` ## SIM-Karten lesen [Section titled “SIM-Karten lesen”](#sim-karten-lesen) ```typescript const { simCards } = await Sim.getSimCards(); simCards.forEach((card, index) => { console.log(`Slot ${card.simSlotIndex ?? index}:`, { carrier: card.carrierName, mcc: card.mobileCountryCode, mnc: card.mobileNetworkCode, country: card.isoCountryCode, subscriptionId: card.subscriptionId, }); }); ``` ## Überlegungen zur Plattform [Section titled “Überlegungen zur Plattform”](#überlegungen-zur-plattform) * **Dual-SIM-Geräte**: Durchlaufen Sie das zurückgegebene Array; Jeder Eintrag entspricht einem Slot. Wenn vorhanden, hilft Ihnen `subscriptionId` bei der Interaktion mit den Telefonie-APIs von Android. * **iOS 16.4+**: Apple schwärzt mehrere Trägerattribute. Erwarten Sie Platzhalterwerte (`--`, `65535`) und planen Sie Fallbacks in Ihrer Benutzeroberfläche. * **Web**: Das Plugin wird mit leeren Daten aufgelöst, da Browser nicht auf SIM-Informationen zugreifen können. # @capgo/capacitor-social-login > Ein Capacitor-Plugin für soziale Authentifizierung mit Google, Apple und Facebook, das den Benutzer-Login nahtlos und sicher macht. ## Video-Anleitung [Section titled “Video-Anleitung”](#video-anleitung) Sehen Sie sich eine kurze Demo der Plugin-Einrichtung und des Login-Ablaufs in Aktion an. [Play](https://youtube.com/watch?v=iTINYxvSJrE) Google-Login Bietet eine reibungslose Login-Erfahrung für Ihre Benutzer Facebook-Login Macht Facebook-Login zum Kinderspiel Apple-Login Apple-Login kann schwierig zu implementieren sein, aber wir machen es einfach Umfassende Dokumentation Schauen Sie sich die [Dokumentation](/docs/plugins/social-login/getting-started/) an, um das Plugin in nur wenigen Minuten zu meistern. # Apple Anmeldung bei Android Apple Die Anmeldung auf Android ist schwierig. Apple bietet keine offizielle Unterstützung für `Sign in with Apple` auf Android, daher ist die Lösung etwas hackig. Android verwendet derzeit Chrome-Tabs, um eine OAuth2-Website anzuzeigen. Dieser Ansatz birgt folgende Herausforderungen: * Schwierige Konfiguration * Ein Backend ist erforderlich ## Den Ablauf auf Android verstehen. [Section titled “Den Ablauf auf Android verstehen.”](#den-ablauf-auf-android-verstehen) Lassen Sie mich den Ablauf auf Android anhand eines Diagramms erläutern: ``` flowchart TD A("await SocialLogin.login()") -->|Im Plugin behandelt|B(Anmelde-URL generieren) B --> |Link übergeben| C (Öffnen Sie den Chrome-Browser) C -> D (Warten Sie, bis sich der Benutzer anmeldet) D --> |Apple leitet zu Ihrem Backend weiter|E(Verarbeitet die von Apple zurückgegebenen Daten) E -> F (Zurück zur App weiterleiten) F -> G(Zurück zu JS) ``` Nachdem Sie nun die Herausforderungen und den Ablauf kennen, beginnen wir mit der Konfiguration. ## Erstellen der Service-ID [Section titled “Erstellen der Service-ID”](#erstellen-der-service-id) 1. Melden Sie sich beim [Apple Entwicklerportal](https://developer.apple.com) an. 2. Klicken Sie auf `Identifiers`. ![Apple Abschnitt „Entwicklerportal-IDs“.](/social-login-assets/apple_dev_portal_iden.png) Sie sollten einen Bildschirm sehen, der so aussieht: ![Apple Bildschirm „Developer Portal Identifiers“.](/social-login-assets/apple_dev_portal_iden_2.png) 1. Stellen Sie sicher, dass in diesem Feld `App IDs` steht. 2. Stellen Sie sicher, dass Sie Ihre App-ID finden können. Wenn Sie Apple Login für IOS nicht konfiguriert haben, müssen Sie eines erstellen. Für mich habe ich bereits eines erstellt. Die App-ID, die ich verwenden werde, ist `me.wcaleniewolny.test.ionic.vue`. Wenn Sie noch keine haben, erstellen Sie bitte eine mit dem [Schritt „App erstellen“](#creating-the-app). 3. Stellen Sie sicher, dass die Funktion `Sign in with Apple` für Ihre App aktiviert ist 1. Klicken Sie auf Ihre App ![Wählen Sie Ihre App aus der Liste aus](/social-login-assets/apple_dev_click_on_app.png) 2. Stellen Sie sicher, dass die Funktion `Sign in with Apple` aktiviert ist ![Kontrollkästchen „Anmelden mit aktivierter Apple-Funktion“ aktivieren](/social-login-assets/apple_dev_sign_in_with_apple_enabled.png) 3. Wenn es nicht aktiviert ist, aktivieren Sie es. 4. Gehen Sie zurück zu „Alle Bezeichner“. ![](/social-login-assets/apple_dev_go_back_iden.png) 5. Klicken Sie auf „App-IDs“ und gehen Sie zu „Dienst-IDs“. ![Navigation zum Abschnitt „Dienst-IDs“.](/social-login-assets/apple_dev_go_to_services_id.png) 6. Erstellen Sie eine neue Kennung 1. Klicken Sie auf den Plus-Button ![Schaltfläche „Neue Service-ID hinzufügen“.](/social-login-assets/apple_dev_iden_add.png) 2. Wählen Sie „Dienst-IDs“ und klicken Sie auf `Continue` ![Auswahl der Option „Service-IDs“.](/social-login-assets/apple_dev_service_and_cont.png) 3. Geben Sie eine Beschreibung und eine Kennung ein und klicken Sie auf `Continuie`. ![Eingeben von Service-ID-Details](/social-login-assets/apple_dev_reg_service_2.png) Dieser `identifiers` wird zum `clientId`, den Sie in der Funktion `initialize` UND `ANDROID_SERVICE_ID` für das Backend übergeben. **Bitte speichern!!!** Die Service-ID muss nicht mit der App-ID übereinstimmen, ich empfehle jedoch, die Service-ID auf `YOUR_APP_ID.service` festzulegen. Zur Erinnerung: Ich verwende `me.wcaleniewolny.test.ionic.vue` als meine App-ID, aber ich verwende `ee.forgr.io.ionic.service2` als Dienst-ID. 4. Bitte überprüfen Sie die Details und klicken Sie auf `Register` ![Bestätigen der Service-ID-Registrierung](/social-login-assets/apple_dev_service_ref_fin.png) 5. Klicken Sie auf den neu erstellten Dienst ![Auswahl der neu erstellten Service-ID](/social-login-assets/apple_dev_open_serv.png) 6. Aktivieren Sie die Option `Sign in with Apple` ![Aktivieren der Anmeldung mit Apple für die Dienst-ID](/social-login-assets/apple_dev_serv_enable_sign_with_apple.png) 7. Konfigurieren Sie den `Sign In with Apple` ![Schaltfläche „Konfigurieren“ für „Anmelden mit Apple“](/social-login-assets/apple_dev_conf_serv_sign_with_apple.png) 8. Stellen Sie sicher, dass die „Primäre App-ID“ auf die im vorherigen Schritt konfigurierte App-ID eingestellt ist ![Dropdown-Liste zum Festlegen der primären App-ID](/social-login-assets/apple_dev_service_prim_id.png) 9. Fügen Sie die Domäne hinzu, auf der Sie Ihr Backend hosten möchten.![Festlegen von Domänen- und Rückgabe-URL-Feldern](/social-login-assets/apple_dev_serv_create_next.png) Dieses Backend **muss** auf HTTPS ausgeführt werden. Was die „Rückgabe-URLs“ betrifft, möchten Sie möglicherweise darauf zurückkommen, nachdem Sie den nächsten Abschnitt dieses Tutorials gelesen und das Backend konfiguriert haben. Für die Zwecke dieses Tutorials verwende ich `https://xyz.wcaleniewolny.me/login/callback` als Rückgabe-URL und `xyz.wcaleniewolny.me` als Domäne. Drücken Sie Weiter. 10. Bestätigen Sie die Daten und klicken Sie auf `Done` ![Bestätigen der Domänen- und Rückgabe-URL-Konfiguration](/social-login-assets/apple_dev_serv_conf_done.png) 11. Klicken Sie auf `Continue` ![Schaltfläche „Weiter“ für die Dienstkonfiguration](/social-login-assets/apple_dev_cont_serv_creat.png) 12. Klicken Sie auf `Save` ![Schaltfläche „Speichern“ für die Dienstkonfiguration](/social-login-assets/apple_dev_cont_serv_creat_save.png) ## Schlüssel erstellen [Section titled “Schlüssel erstellen”](#schlüssel-erstellen) 1. Gehen Sie zurück zu „Alle Bezeichner“. ![Navigationsschaltfläche „Alle Bezeichner“.](/social-login-assets/apple_dev_go_back_iden.png) 2. Klicken Sie auf `Keys` ![Abschnitt „Schlüssel“ im Entwicklerportal Apple](/social-login-assets/apple_dev_key_selc.png) 3. Klicken Sie auf das Plus-Symbol ![Schaltfläche „Neuen Schlüssel hinzufügen“.](/social-login-assets/apple_dev_key_plus.png) 4. Benennen Sie Ihren Schlüssel ![Eingabefeld für den Schlüsselnamen](/social-login-assets/apple_key_name.png) Dieser Name ist nicht wichtig, Sie können alles eingeben. 5. Wählen Sie `Sign in with Apple` und klicken Sie auf `Configure` ![Aktivieren und Konfigurieren der Anmeldung mit Apple für den Schlüssel](/social-login-assets/apple_dev_key_sing_apple_conf.png) 6. Wählen Sie die primäre App-ID aus und drücken Sie `Save`. ![Auswählen der primären App-ID für den Schlüssel](/social-login-assets/apple_dev_key_prim_app_id.png) Dies muss dieselbe App-ID sein wie die ID in den vorherigen Schritten. 7. Klicken Sie auf `Continue` ![Schaltfläche „Weiter“ zur Tastenkonfiguration](/social-login-assets/apple_dev_key_const.png) 8. Klicken Sie auf `Register` ![Schaltfläche „Registrieren“ für die Schlüsselerstellung](/social-login-assets/apple_dev_key_reg.png) 9. Kopieren Sie die Schlüssel-ID und laden Sie den Schlüssel herunter. ![Schlüssel-ID und Download-Schaltflächenbildschirm](/social-login-assets/apple_dev_key_downl.png) **WICHTIG:** Speichern Sie diese ID, im Backend heißt sie `KEY_ID`. Laden Sie den Schlüssel herunter. Stellen Sie sicher, dass Sie diesen Schlüssel niemals weitergeben. 10. Suchen Sie den heruntergeladenen Schlüssel und speichern Sie ihn im Backend-Ordner. ![Schlüsseldatei heruntergeladen](/social-login-assets/apple_dev_downloaded_key.png) ## Abrufen der Team-ID [Section titled “Abrufen der Team-ID”](#abrufen-der-team-id) Um `Login with Apple` auf Android verwenden zu können, benötigen Sie die „Team-ID“. Es wird im Backend verwendet. 1. Gehen Sie zu [dieser Website](https://developer.apple.com/account/) und scrollen Sie nach unten 2. Suchen Sie die „Team-ID“. ![Standort der Team-ID im Entwicklerkonto](/social-login-assets/apple_dev_team_id.png) ## Konfigurieren der App-Weiterleitung [Section titled “Konfigurieren der App-Weiterleitung”](#konfigurieren-der-app-weiterleitung) Wie Sie im Diagramm gesehen haben, führt das Backend einen Schritt namens „Zurück zur App umleiten“ aus. Dies erfordert manuelle Änderungen an Ihrer App. 1. Ändern Sie `AndroidManifest.xml` 1. Öffnen Sie die Datei, ich verwende `AndroidStudio` ![AndroidManifest.xml-Datei in Android Studio](/social-login-assets/studio_android_manifest_file.png) 2. Suchen Sie nach `MainActivity` und fügen Sie den folgenden Intent-Filter hinzu ![Absichtsfiltercode zum Hinzufügen in MainActivity](/social-login-assets/studio_manifest_code_to_add.png) ```xml ``` 2. Ändern Sie `MainActivity` 1. Bitte öffnen Sie das `MainActivity` ![MainActivity.java-Datei in Android Studio](/social-login-assets/studio_main_activ_file.png) 2. Fügen Sie den folgenden Code hinzu: ![Code zum Hinzufügen zu MainActivity für die Verarbeitung von Deep Links](/social-login-assets/studio_main_actv_new_code.png) ```java @Override protected void onNewIntent(Intent intent) { String action = intent.getAction(); Uri data = intent.getData(); if (Intent.ACTION_VIEW.equals(action) && data != null) { PluginHandle pluginHandle = getBridge().getPlugin("SocialLogin"); if (pluginHandle == null) { Log.i("Apple Login Intent", "SocialLogin login handle is null"); return; } Plugin plugin = pluginHandle.getInstance(); if (!(plugin instanceof SocialLoginPlugin)) { Log.i("Apple Login Intent", "SocialLogin plugin instance is not SocialLoginPlugin"); return; } ((SocialLoginPlugin) plugin).handleAppleLoginIntent(intent); return; } super.onNewIntent(intent); } ``` In diesem Beispiel wird davon ausgegangen, dass Sie keine Deep Links konfiguriert haben. Passen Sie in diesem Fall bitte den Code an Note Sie haben gerade einen neuen Deep-Link zu Ihrer App hinzugefügt. Der Deep-Link sieht etwa so aus: `capgo-demo-app://path`. Sie können `android:scheme` und `android:host` ändern, um das Aussehen dieses Deep Links zu ändern. **Wichtig:** In der Backend-Konfiguration wird dieser Deep Link zu `BASE_REDIRECT_URL` ## Backend-Konfiguration [Section titled “Backend-Konfiguration”](#backend-konfiguration) Für Android ist ein Backend erforderlich, aber die Konfiguration eines Backends wirkt sich auch auf IOS aus. Ein Beispiel-Backend finden Sie [hier](https://github.com/WcaleNieWolny/capgo-social-login-backend-demo/blob/main/index.ts)Dieses Beispiel bietet Folgendes: * Eine einfache JSON-Datenbank – Eine Möglichkeit, JWT von den Servern von Apple anzufordern * Eine einfache JWT-Verifizierung Note Ich verwende `PM2`, um dieses Beispiel zu hosten. Ein Beispiel `ecosystem.config.js` finden Sie [hier](https://github.com/WcaleNieWolny/capgo-social-login-backend-demo/blob/main/ecosystem.config.js.example) Angesichts all dessen, was ich in diesem Tutorial gesagt habe, würde der Abschnitt `env` folgendermaßen aussehen: * `ANDROID_SERVICE_ID` = Dienst-ID * `IOS_SERVICE_ID` = App-ID ```js env: { PRIVATE_KEY_FILE: "AuthKey_U93M8LBQK3.p8", KEY_ID: "U93M8LBQK3", TEAM_ID: "UVTJ336J2D", ANDROID_SERVICE_ID: "ee.forgr.io.ionic.starter.service2", IOS_SERVICE_ID: "me.wcaleniewolny.test.ionic.vue", PORT: 3000, REDIRECT_URI: "https://xyz.wcaleniewolny.me/login/callback", BASE_REDIRECT_URL: "capgo-demo-app://path" } ``` #### Verwendung des Plugins [Section titled “Verwendung des Plugins”](#verwendung-des-plugins) Die Verwendung der Funktion `login` ändert sich nicht, sie ist dieselbe wie bei IOS. Weitere Informationen finden Sie in diesem Abschnitt. **Allerdings** ändert sich die Methode `initialize` ein wenig. ```typescript await SocialLogin.initialize({ apple: { clientId: 'ee.forgr.io.ionic.starter.service2', redirectUrl: 'https://appleloginvps.wcaleniewolny.me/login/callback' } }) ``` Danger Beachten Sie, dass das Hinzufügen von `redirectUrl` **Auswirkungen** auf IOS haben wird !!!!! ## Erstellen der App [Section titled “Erstellen der App”](#erstellen-der-app) Note Wenn Sie bereits über eine App-ID verfügen, können Sie diesen Schritt überspringen. Führen Sie diesen Schritt nicht aus, wenn Sie Apple Login für IOS konfiguriert haben. 1. Wenn Sie noch keine App-ID haben, klicken Sie auf die Plus-Schaltfläche ![Schaltfläche „Neuen Bezeichner hinzufügen“ hinzufügen](/social-login-assets/apple_dev_iden_plus.png) 2. Wählen Sie „App-IDs“ und klicken Sie auf „Weiter“. ![Auswählen des App-ID-Typs](/social-login-assets/apple_dev_new_app_id.png) 3. Klicken Sie auf Typ `App` und klicken Sie auf `Continue` ![Auswahl des App-Typs](/social-login-assets/apple_dev_new_app_type.png) 4. Geben Sie die Beschreibung und die App-ID ein ![Eingabe der App-Beschreibung und Bundle-ID](/social-login-assets/apple_dev_new_app_desc_id.png) 5. Aktivieren Sie die `Sign with Apple`-Funktion ![Aktivieren der Anmeldung mit der Apple-Funktion](/social-login-assets/apple_dev_enable_sign_with_apple.png) 6. Klicken Sie auf `Continue` ![Schaltfläche „Weiter“ für die App-Registrierung](/social-login-assets/apple_dev_register_continue.png) 7. Bestätigen Sie die Details und klicken Sie auf `Register` ![Bestätigen der App-Registrierungsdetails](/social-login-assets/apple_dev_confirm_register.png) # Apple Login-Einrichtung > Diese Anleitung bietet detaillierte Anweisungen zur Einrichtung von Apple Login mit Capacitor, die alle notwendigen Schritte und Anforderungen für eine erfolgreiche Integration abdeckt. ### Einführung [Section titled “Einführung”](#einführung) In diesem Leitfaden lernen Sie, wie Sie Apple Login mit Capacitor konfigurieren. Dafür benötigen Sie Folgendes: * ein Apple Developer-Konto * Einen Computer mit macOS (nur iOS) * Xcode installiert (nur iOS) * Ein benutzerdefiniertes Backend (nur Android) # Apple Login auf iOS > Diese umfassende Anleitung führt Sie durch den Prozess der Einrichtung von Apple Login mit Capacitor auf iOS-Geräten und stellt eine nahtlose Integration durch Abdeckung aller notwendigen Schritte und Konfigurationen sicher. Lassen Sie uns aufschlüsseln, was Sie benötigen, um Apple Login auf iOS einzurichten. 1. Konfigurieren Sie die Funktionen Ihrer App. Um dies zu tun, öffnen Sie bitte Xcode, klicken Sie auf `App` ![App XCode](/social-login-assets/app-xcode.png) 2. Stellen Sie sicher, dass Sie das richtige Ziel auswählen. ![XCode App Target](/social-login-assets/xcode-app-target.png) 3. Bitte stellen Sie sicher, dass Sie die Funktion `Sign in with Apple` hinzufügen. ![App XCode Add Capability](/social-login-assets/app-xcode-add-cap.png) ![App XCode Add Capability](/social-login-assets/sign-with-apple-cap.png) Caution Wenn Sie die Funktion `Sign in with Apple` nicht sehen, konfigurieren Sie die [Account & Organizational Data Sharing](https://developer.apple.com/account/resources/services/cwa/configure/) 4. Initialisieren Sie das Apple Login in Ihrer App. Note Ich verwende Vue als mein Framework, die genaue Implementierung variiert je nach Framework Ihrer Wahl ```ts // onMounted ist Vue-spezifisch onMounted(() => { SocialLogin.initialize({ apple: {} }) }); ``` 5. Erstellen Sie eine Schaltfläche, die den Login-Prozess startet. Diese Schaltfläche sollte die folgende Funktion aufrufen: ```ts async function loginApple() { const res = await SocialLogin.login({ provider: 'apple', options: {} }) ``` 6. Führen Sie Ihre App auf einem ***PHYSISCHEN*** Gerät aus und testen Sie sie. Wenn Sie die Schritte genau befolgt haben, sehen Sie nach dem Klicken auf Ihre Schaltfläche den folgenden Bildschirm. ![Apple Sign In prompt on iOS device](/social-login-assets/apple-sign-in-ios-final.png) Das war’s! Sie sind fertig. # Apple Login für Webbrowser > Dieser Leitfaden bietet eine detaillierte Anleitung zur Einrichtung von Apple Login mit Capacitor für Webanwendungen unter Verwendung des @capgo/capacitor-social-login Plugins, um einen nahtlosen Integrationsprozess zu gewährleisten. Die Konfiguration des Web-Logins ist nicht trivial. Es ist schwieriger als die Einrichtung von `Sign in with Apple` auf iOS, aber schwieriger als die Einrichtung von `Sign in with Apple` auf Android. ## Generierung des Dienstes [Section titled “Generierung des Dienstes”](#generierung-des-dienstes) Note Dieser Schritt ist redundant, wenn Sie bereits `Sign in with Apple` auf Android konfiguriert haben. Bitte folgen Sie der Anleitung [hier](/docs/plugins/social-login/apple/android/#creating-the-service-id/), um den Dienst zu generieren. ## Konfiguration der `Return URLs` [Section titled “Konfiguration der Return URLs”](#konfiguration-der-return-urls) 1. **Gehen Sie zu Ihrer Service ID Konfiguration** Im [Apple Developer Portal](https://developer.apple.com) navigieren Sie zu `Identifiers` > `Services IDs` und klicken Sie auf Ihre Service ID. 2. **Konfigurieren Sie Sign in with Apple** Klicken Sie auf `Configure` neben `Sign in with Apple`. ![apple\_dev\_configure\_login](/social-login-assets/apple_dev_configure_login.webp) 3. **Fügen Sie die Return URLs hinzu** Klicken Sie auf die `+` Schaltfläche, um eine neue Return URL hinzuzufügen. ![apple\_dev\_return\_url\_plus](/social-login-assets/apple_dev_return_url_plus.webp) 4. **Fügen Sie die Return URLs hinzu** Fügen Sie Ihre Domain für Ihre Webanwendung unter `Domains and Subdomains` hinzu. Caution Sie **KÖNNEN NICHT** `localhost` oder `127.0.0.1` als Domain hier hinzufügen. Fügen Sie dann Ihre Domain mit dem `https://` Präfix und dem Pfad hinzu, von dem Sie Apple Login aufrufen werden. Wenn Ihre Domain zum Beispiel `https://my-app.com` ist und Sie Apple Login von `/login` aufrufen, sollten Sie `https://my-app.com/login` als Return URL hinzufügen. Caution Sie **MÜSSEN** sowohl die Domain mit als auch ohne abschließenden Schrägstrich hinzufügen. ![apple\_dev\_return\_url\_add](/social-login-assets/apple_dev_return_url_add.webp) 5. **Speichern Sie die Änderungen** 1. Klicken Sie auf die `Next` Schaltfläche, um die Änderungen zu speichern. 2. Klicken Sie auf die `Save` Schaltfläche, um die Änderungen zu speichern. 6. Sie sollten nun bereit sein, den Login für JavaScript zu testen. Bitte beachten Sie, dass Sie nicht von localhost testen können. # Facebook Login Einrichtung > Dieser Leitfaden bietet eine umfassende Anleitung zur Einrichtung von Facebook Login mit Capacitor und gewährleistet eine nahtlose Integration und verbesserte Benutzerauthentifizierung für Ihre Anwendung. ## Einführung [Section titled “Einführung”](#einführung) In diesem Leitfaden lernen Sie, wie Sie Facebook Login mit Capgo Social Login einrichten. Sie benötigen Folgendes: * Ein Facebook Developer Konto * Den Paketnamen/Bundle ID Ihrer App * Zugang zu einem Terminal zum Generieren von Schlüssel-Hashes (Android) ## Allgemeine Einrichtung [Section titled “Allgemeine Einrichtung”](#allgemeine-einrichtung) Wenn Sie noch keine Facebook App erstellt haben, folgen Sie diesen Schritten: 1. Erstellen Sie eine Facebook App Folgen Sie dem Tutorial zum [Erstellen einer App](https://developers.facebook.com/docs/development/create-an-app/) 2. Fügen Sie Facebook Login zu Ihrer App hinzu Fügen Sie in Ihrem Facebook Developer Dashboard das Facebook Login Produkt zu Ihrer App hinzu 3. Bevor Sie Ihre App für die Öffentlichkeit freigeben können, folgen Sie diesem [Tutorial](https://developers.facebook.com/docs/development/release/), um sie zu veröffentlichen ## Wichtige Informationen [Section titled “Wichtige Informationen”](#wichtige-informationen) Hier finden Sie die wichtigsten Informationen, die Sie für die Integration benötigen: 1. `CLIENT_TOKEN`: ![Facebook Developer Dashboard zeigt, wo das Client Token zu finden ist](/social-login-assets/fb_where_to_fiind_client_token.png) 2. `APP_ID`: ![Facebook Developer Dashboard zeigt, wo die App ID zu finden ist](/social-login-assets/fb_where_to_find_app_id.png) 3. `APP_NAME`: ![Facebook Developer Dashboard zeigt, wo der App Name zu finden ist](/social-login-assets/fb_where_to_find_app_name.png) ## Android Einrichtung [Section titled “Android Einrichtung”](#android-einrichtung) 1. Fügen Sie die Internetberechtigung zu Ihrer `AndroidManifest.xml` hinzu Stellen Sie sicher, dass diese Zeile vorhanden ist: ```xml ``` 2. Generieren Sie Ihren Android Schlüssel-Hash Dies ist ein wichtiger Sicherheitsschritt, der von Facebook erforderlich ist. Öffnen Sie Ihr Terminal und führen Sie aus: ```bash keytool -exportcert -alias androiddebugkey -keystore ~/.android/debug.keystore | openssl sha1 -binary | openssl base64 -A ``` Wenn Sie nach einem Passwort gefragt werden, verwenden Sie: `android` Note Für Release-Builds müssen Sie Ihren Release Keystore verwenden: ```bash keytool -exportcert -alias your-key-name -keystore your-keystore-path | openssl sha1 -binary | openssl base64 -A ``` 3. Fügen Sie den Schlüssel-Hash zu Ihrer Facebook App hinzu 1. Gehen Sie zum Dashboard Ihrer App bei Facebook Developers 2. Navigieren Sie zu Settings > Basic 3. Scrollen Sie nach unten zum “Android” Bereich 4. Klicken Sie auf “Add Platform”, falls Android noch nicht hinzugefügt wurde, und füllen Sie die Details aus 5. Fügen Sie den von Ihnen generierten Schlüssel-Hash hinzu 6. Für die Produktion fügen Sie sowohl Debug- als auch Release-Schlüssel-Hashes hinzu 4. Aktualisieren Sie Ihre `AndroidManifest.xml` um Folgendes einzuschließen: ```xml ... ``` Caution Stellen Sie sicher, dass Sie `[APP_ID]` im `android:scheme` Attribut durch Ihre tatsächliche Facebook App ID ersetzen ## iOS Einrichtung [Section titled “iOS Einrichtung”](#ios-einrichtung) 1. Fügen Sie die iOS Plattform in der Facebook Developer Console hinzu 1. Gehen Sie zum Dashboard Ihrer App bei Facebook Developers 2. Navigieren Sie zu Settings > Basic 3. Scrollen Sie ganz nach unten und klicken Sie auf “Add Platform” 4. Wählen Sie iOS und füllen Sie die erforderlichen Details aus 2. Öffnen Sie Ihr Xcode Projekt und navigieren Sie zu Info.plist 3. Fügen Sie die folgenden Einträge zu Ihrer Info.plist hinzu: ```xml FacebookAppID [APP-ID] FacebookClientToken [CLIENT-TOKEN] FacebookDisplayName [APP-NAME] LSApplicationQueriesSchemes fbapi fb-messenger-share-api CFBundleURLTypes CFBundleURLSchemes fb[APP-ID] ``` Caution Ersetzen Sie die folgenden Werte: * `[APP-ID]` durch Ihre Facebook App ID * `[CLIENT-TOKEN]` durch Ihr Client Token * `[APP-NAME]` durch den Namen Ihrer App 4. Ändern Sie die `AppDelegate.swift` ```swift import FBSDKCoreKit @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. // Initialize Facebook SDK FBSDKCoreKit.ApplicationDelegate.shared.application( application, didFinishLaunchingWithOptions: launchOptions ) return true } func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool { // Called when the app was launched with a url. Feel free to add additional processing here, // but if you want the App API to support tracking app url opens, make sure to keep this call if (FBSDKCoreKit.ApplicationDelegate.shared.application( app, open: url, sourceApplication: options[UIApplication.OpenURLOptionsKey.sourceApplication] as? String, annotation: options[UIApplication.OpenURLOptionsKey.annotation] )) { return true; } else { return ApplicationDelegateProxy.shared.application(app, open: url, options: options) } } } ``` ## Verwendung von Facebook Login in Ihrer App [Section titled “Verwendung von Facebook Login in Ihrer App”](#verwendung-von-facebook-login-in-ihrer-app) Caution **Bevor Sie beginnen**: Denken Sie daran, dass bei dem neuen Facebook SDK der Token-Typ, den Sie erhalten, vollständig von der App Tracking Auswahl des Benutzers abhängt, nicht von Ihrer Code-Konfiguration. Implementieren Sie immer sowohl die Verarbeitung von Access Token als auch JWT Token in Ihrem Backend, um sicherzustellen, dass die Authentifizierung für alle Benutzer funktioniert. 1. Initialisieren Sie den Facebook Login in Ihrer App ```typescript import { SocialLogin } from '@capgo/capacitor-social-login'; // Während des App-Starts initialisieren await SocialLogin.initialize({ facebook: { appId: 'APP_ID', clientToken: 'CLIENT_TOKEN', } }) ``` 2. Implementieren Sie die Login-Funktion ```typescript async function loginWithFacebook() { try { const result = await SocialLogin.login({ provider: 'facebook', options: { permissions: ['email', 'public_profile'], limitedLogin: false // Siehe Abschnitt Limited Login unten für wichtige Details } }); console.log('Facebook Login Ergebnis:', result); // Erfolgreichen Login verarbeiten } catch (error) { console.error('Facebook Login Fehler:', error); // Fehler verarbeiten } } ``` Note **Limited Login (nur iOS)**: Setzen Sie `limitedLogin` auf true, wenn Sie Facebooks Limited Login Funktion verwenden möchten. Dies ist eine nur-iOS-Funktion, die verbesserten Datenschutz bietet, indem die während des Logins geteilten Daten eingeschränkt werden. **Wichtige Einschränkungen:** * **Nur iOS**: Limited Login betrifft nur iOS-Geräte und hat keine Auswirkungen auf Android * **ATT Überschreibung**: Selbst wenn Sie `limitedLogin: false` setzen, wird Facebook es automatisch auf `true` zwingen, wenn der Benutzer keine App Tracking Transparency (ATT) Berechtigung erteilt hat * **Beide Fälle immer behandeln**: Ihre App sollte immer darauf vorbereitet sein, sowohl Limited- als auch Full-Login-Szenarien zu behandeln **ATT Status überprüfen:** ```typescript // Überprüfen Sie, ob der Benutzer die Tracking-Berechtigung erteilt hat const trackingStatus = await SocialLogin.providerSpecificCall({ call: 'facebook#requestTracking', options: {} }); console.log('Tracking Status:', trackingStatus.status); // 'authorized', 'denied', 'notDetermined', oder 'restricted' ``` **Empfohlene Implementierung, wenn access\_token bevorzugt wird:** ```typescript async function loginWithFacebook() { try { // ATT Status zuerst überprüfen const trackingStatus = await SocialLogin.providerSpecificCall({ call: 'facebook#requestTracking', options: {} }); const result = await SocialLogin.login({ provider: 'facebook', options: { permissions: ['email', 'public_profile'], limitedLogin: trackingStatus.status === 'denied' // Automatisch basierend auf ATT anpassen } }); // Verschiedene Antworttypen basierend auf Limited Login verarbeiten if (result.result.accessToken) { // Ihre App-Logik sollte mit beiden funktionieren, Limited und Full Login console.log('Login erfolgreich:', result); } } catch (error) { console.error('Facebook Login Fehler:', error); } } ``` **Was passiert bei Limited Login:** * **Reduzierter Datenzugriff**: Einige Benutzerdaten sind möglicherweise nicht verfügbar * **Verschiedene Token-Typen**: Access Tokens können unterschiedliche Fähigkeiten haben * **Datenschutz-Compliance**: Hilft bei der Einhaltung der iOS-Datenschutzanforderungen **Wichtig**: Testen Sie Ihre App immer sowohl mit Limited- als auch mit Full-Login-Szenarien, um sicherzustellen, dass Ihre App in beiden Fällen korrekt funktioniert. Sie können mehr über Limited Login [hier](https://developers.facebook.com/docs/facebook-login/limited-login/) erfahren. 3. **Benutzerprofildaten abrufen** Nach erfolgreichem Login können Sie zusätzliche Profilinformationen abrufen: ```typescript async function getFacebookProfile() { try { const profileResponse = await SocialLogin.providerSpecificCall({ call: 'facebook#getProfile', options: { fields: ['id', 'name', 'email', 'first_name', 'last_name', 'picture'] } }); console.log('Facebook Profil:', profileResponse.profile); return profileResponse.profile; } catch (error) { console.error('Fehler beim Abrufen des Facebook Profils:', error); return null; } } // Beispielverwendung nach dem Login async function loginAndGetProfile() { const loginResult = await loginWithFacebook(); if (loginResult) { const profile = await getFacebookProfile(); if (profile) { console.log('Benutzer ID:', profile.id); console.log('Name:', profile.name); console.log('E-Mail:', profile.email); console.log('Profilbild:', profile.picture?.data?.url); } } } ``` Tip **Verfügbare Profilfelder**: Sie können alle Felder anfordern, die in Facebooks Graph API verfügbar sind. Häufige Felder sind: `id`, `name`, `email`, `first_name`, `last_name`, `picture`, `birthday`, `gender`, `location`, `hometown`. Beachten Sie, dass einige Felder zusätzliche Berechtigungen erfordern können. **Token-Typ-Einschränkung**: Der `getProfile` Aufruf funktioniert nur, wenn Sie ein **Access Token** haben (Standard-Login mit erlaubtem Tracking). Wenn der Benutzer das Tracking abgelehnt hat oder Sie Limited Login verwenden (nur JWT Token), schlägt dieser Aufruf fehl. Verwenden Sie in diesem Fall die in der anfänglichen Login-Antwort bereitgestellten Profildaten. ## ⚠️ Kritisch: Backend Token-Verarbeitung [Section titled “⚠️ Kritisch: Backend Token-Verarbeitung”](#️-kritisch-backend-token-verarbeitung) Danger **KRITISCHES iOS VERHALTEN**: Limited Login und App Tracking Transparency (ATT) sind **NUR-iOS** Funktionen. Auf Android erhalten Sie immer Access Tokens, unabhängig von der `limitedLogin` Einstellung. **iOS Token-Verhalten**: * **`limitedLogin: true`** → Immer JWT Token (nur iOS) * **`limitedLogin: false` + Benutzer ERLAUBT Tracking** → Access Token * **`limitedLogin: false` + Benutzer LEHNT Tracking AB** → JWT Token (iOS überschreibt Ihre Einstellung automatisch) **Android Token-Verhalten**: Immer Access Token, `limitedLogin` Einstellung wird ignoriert. Ihr Backend muss **zwei verschiedene Token-Typen** verarbeiten, da iOS-Benutzer je nach ihrer App Tracking Transparency Auswahl entweder Access Tokens oder JWT Tokens erhalten können, während Android-Benutzer immer Access Tokens erhalten. ### Token-Typen nach Plattform [Section titled “Token-Typen nach Plattform”](#token-typen-nach-plattform) | Plattform | limitedLogin Einstellung | Benutzer ATT Auswahl | Ergebnis Token-Typ | | ----------- | ------------------------ | -------------------- | --------------------------------------- | | **iOS** | `true` | Beliebig | JWT Token | | **iOS** | `false` | Erlaubt Tracking | Access Token | | **iOS** | `false` | Lehnt Tracking ab | JWT Token (automatische Überschreibung) | | **Android** | Beliebig | N/A | Access Token (immer) | ### Backend-Implementierung [Section titled “Backend-Implementierung”](#backend-implementierung) 1. **Token-Typ erkennen und entsprechend verarbeiten** ```typescript async function loginWithFacebook() { try { const loginResult = await SocialLogin.login({ provider: 'facebook', options: { permissions: ['email', 'public_profile'], limitedLogin: false // iOS: abhängig von ATT, Android: ignoriert } }); if (loginResult.accessToken) { // Access Token (Android immer, iOS wenn Tracking erlaubt) return handleAccessToken(loginResult.accessToken.token); } else if (loginResult.idToken) { // JWT Token (nur iOS wenn Tracking abgelehnt oder limitedLogin: true) return handleJWTToken(loginResult.idToken); } } catch (error) { console.error('Facebook Login Fehler:', error); } } ``` 2. **Firebase Integration Beispiel** ```typescript import { OAuthProvider, FacebookAuthProvider, signInWithCredential } from 'firebase/auth'; async function handleAccessToken(accessToken: string, nonce: string) { // Für Access Tokens, verwenden Sie OAuthProvider (neue Methode) const fbOAuth = new OAuthProvider("facebook.com"); const credential = fbOAuth.credential({ idToken: accessToken, rawNonce: nonce }); try { const userResponse = await signInWithCredential(auth, credential); return userResponse; } catch (error) { console.error('Firebase OAuth Fehler:', error); return false; } } async function handleJWTToken(jwtToken: string) { // Für JWT Tokens, senden Sie an Ihr Backend zur Validierung try { const response = await fetch('/api/auth/facebook-jwt', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ jwtToken }) }); const result = await response.json(); return result; } catch (error) { console.error('JWT Validierungsfehler:', error); return false; } } ``` 3. **Backend JWT Validierung** ```typescript // Backend: JWT Token von Facebook validieren import jwt from 'jsonwebtoken'; import { Request, Response } from 'express'; app.post('/api/auth/facebook-jwt', async (req: Request, res: Response) => { const { jwtToken } = req.body; try { // JWT Token mit Facebooks öffentlichem Schlüssel verifizieren // Siehe: https://developers.facebook.com/docs/facebook-login/limited-login/token/validating/#standard-claims const decoded = jwt.verify(jwtToken, getFacebookPublicKey(), { algorithms: ['RS256'], audience: process.env.FACEBOOK_APP_ID, issuer: 'https://www.facebook.com' // Von: https://www.facebook.com/.well-known/openid-configuration/?_rdr }); // Benutzerinformationen aus JWT extrahieren const userInfo = { id: decoded.sub, email: decoded.email, name: decoded.name, isJWTAuth: true }; // Session/Token Ihrer App erstellen const sessionToken = createUserSession(userInfo); res.json({ success: true, token: sessionToken, user: userInfo }); } catch (error) { console.error('JWT Validierung fehlgeschlagen:', error); res.status(401).json({ success: false, error: 'Ungültiger Token' }); } }); ``` 4. **Generischer Backend Token Handler** ```typescript // Beide Token-Typen in Ihrem Backend verarbeiten async function authenticateFacebookUser(tokenData: any) { if (tokenData.accessToken) { // Access Token verarbeiten - mit Facebook Graph API validieren const response = await fetch(`https://graph.facebook.com/me?access_token=${tokenData.accessToken}&fields=id,name,email`); const userInfo = await response.json(); return { user: userInfo, tokenType: 'access_token', expiresIn: tokenData.expiresIn || 3600 }; } else if (tokenData.jwtToken) { // JWT Token verarbeiten - dekodieren und validieren // Siehe: https://developers.facebook.com/docs/facebook-login/limited-login/token/validating/#standard-claims const decoded = jwt.verify(tokenData.jwtToken, getFacebookPublicKey()); return { user: { id: decoded.sub, name: decoded.name, email: decoded.email }, tokenType: 'jwt', expiresIn: decoded.exp - Math.floor(Date.now() / 1000) }; } else { throw new Error('Kein gültiger Token bereitgestellt'); } } ``` ### Wichtige Überlegungen [Section titled “Wichtige Überlegungen”](#wichtige-überlegungen) Danger **Kritisches Nur-iOS-Verständnis**: * **iOS**: Die App Tracking Auswahl des Benutzers bestimmt den Token-Typ, NICHT Ihre Code-Einstellungen (selbst wenn `limitedLogin: false`) * **Android**: Erhält immer Access Tokens, unabhängig von der `limitedLogin` Einstellung * **Limited Login ist NUR-iOS** - Android ignoriert diese Einstellung vollständig **Access Token (Standard Login)**: * ✅ **Android**: Immer verfügbar (iOS-only Einschränkungen gelten nicht) * ✅ **iOS**: Nur wenn der Benutzer explizit App-Tracking erlaubt * ✅ Kann zum Zugriff auf Facebook Graph API verwendet werden * ✅ Längere Ablaufzeiten * ✅ Mehr Benutzerdaten verfügbar * ❌ **Wird auf iOS weniger häufig**, da Benutzer zunehmend Tracking ablehnen **JWT Token (Nur-iOS Datenschutzmodus)**: * ❌ **Android**: Tritt nie auf (nicht unterstützt) * ✅ **iOS**: Wenn Tracking abgelehnt oder `limitedLogin: true` * ✅ Respektiert iOS-Benutzerdatenschutzpräferenzen * ❌ Enthält nur grundlegende Benutzerinformationen * ❌ Kürzere Ablaufzeiten * ❌ Kein Zugriff auf Facebook Graph API * ⚠️ **Jetzt das häufigste Szenario für iOS-Benutzer** **Plattformspezifisches Verhalten**: * **iOS-Apps**: Müssen sowohl Access Tokens ALS AUCH JWT Tokens verarbeiten * **Android-Apps**: Müssen nur Access Tokens verarbeiten * **Plattformübergreifende Apps**: Müssen beide Token-Verarbeitungsmethoden implementieren Tip **Essentiell für iOS**: Sie MÜSSEN beide Token-Verarbeitungsmethoden implementieren. Viele iOS-Entwickler nehmen an, dass sie immer Access Tokens erhalten, und ihre Apps brechen zusammen, wenn Benutzer Tracking ablehnen. ## Anforderungen an sichere Kontexte (Web/Capacitor) [Section titled “Anforderungen an sichere Kontexte (Web/Capacitor)”](#anforderungen-an-sichere-kontexte-webcapacitor) ### Crypto API Einschränkungen [Section titled “Crypto API Einschränkungen”](#crypto-api-einschränkungen) Der aktualisierte Facebook Login-Ablauf erfordert die **Web Crypto API** zur Nonce-Generierung, die nur in **sicheren Kontexten** verfügbar ist: ```typescript // Dies erfordert einen sicheren Kontext (HTTPS oder localhost) async function sha256(message: string) { const msgBuffer = new TextEncoder().encode(message); const hashBuffer = await crypto.subtle.digest("SHA-256", msgBuffer); // ❌ Schlägt in unsicherem Kontext fehl // ... } ``` ### Entwicklungsumgebungsprobleme [Section titled “Entwicklungsumgebungsprobleme”](#entwicklungsumgebungsprobleme) **Häufiges Problem**: `ionic serve` mit HTTP URLs unterbricht Facebook-Authentifizierung | Umgebung | Crypto API Verfügbar | Facebook Login Funktioniert | | --------------------------- | -------------------- | --------------------------- | | `http://localhost:3000` | ✅ Ja | ✅ Ja | | `http://127.0.0.1:3000` | ✅ Ja | ✅ Ja | | `http://192.168.1.100:3000` | ❌ Nein | ❌ Nein | | `https://any-domain.com` | ✅ Ja | ✅ Ja | ### Lösungen für Capacitor-Entwicklung [Section titled “Lösungen für Capacitor-Entwicklung”](#lösungen-für-capacitor-entwicklung) 1. **Verwenden Sie localhost für Web-Tests** ```bash # Anstelle von ionic serve --host=0.0.0.0 ionic serve --host=localhost ``` 2. **Aktivieren Sie HTTPS in Ionic** ```bash ionic serve --ssl ``` 3. **Testen Sie auf echten Geräten** ```bash # Capacitor Apps laufen in sicherem Kontext auf Geräten ionic cap run ios ionic cap run android ``` 4. **Alternative Nonce-Generierung für die Entwicklung** ```typescript async function generateNonce() { if (typeof crypto !== 'undefined' && crypto.subtle) { // Sicherer Kontext - verwenden Sie crypto.subtle return await sha256(Math.random().toString(36).substring(2, 10)); } else { // Fallback für Entwicklung (nicht sicher für Produktion) console.warn('Verwende Fallback-Nonce - nicht sicher für Produktion'); return btoa(Math.random().toString(36).substring(2, 10)); } } ``` ### Firebase Integration Hinweis [Section titled “Firebase Integration Hinweis”](#firebase-integration-hinweis) Aktuelle Firebase-Dokumentation erfordert JWT Tokens mit Nonces für Facebook-Authentifizierung, unabhängig von Login-Einstellungen. Dieser Ansatz funktioniert sowohl mit `limitedLogin: true` als auch `limitedLogin: false`: ```typescript // Beide Modi können JWT Tokens zurückgeben, abhängig von der Benutzerauswahl const loginResult = await SocialLogin.login({ provider: 'facebook', options: { permissions: ['email', 'public_profile'], limitedLogin: false, // true = immer JWT, false = abhängig von Benutzer-Tracking-Auswahl nonce: nonce } }); ``` **Entwicklungseinschränkung**: Wenn Sie `ionic serve` auf einer Netzwerk-IP (nicht localhost) verwenden, schlägt Facebook Login aufgrund von Crypto API Einschränkungen fehl. Verwenden Sie localhost oder HTTPS für Web-Tests. Tip **Produktionssicherheit**: Capacitor Apps auf iOS/Android laufen immer in sicheren Kontexten, daher betrifft diese Einschränkung nur Web-Entwicklungsumgebungen. ## Fehlerbehebung [Section titled “Fehlerbehebung”](#fehlerbehebung) ### Häufige Probleme und Lösungen [Section titled “Häufige Probleme und Lösungen”](#häufige-probleme-und-lösungen) 1. **Schlüssel-Hash-Fehler auf Android** * Überprüfen Sie doppelt, dass Sie den korrekten Schlüssel-Hash zum Facebook Dashboard hinzugefügt haben * Für Release-Builds stellen Sie sicher, dass Sie sowohl Debug- als auch Release-Schlüssel-Hashes hinzugefügt haben * Überprüfen Sie, dass Sie den richtigen Keystore beim Generieren des Hashes verwenden 2. **Facebook Login-Button erscheint nicht** * Überprüfen Sie, dass alle Manifest-Einträge korrekt sind * Prüfen Sie, dass Ihre Facebook App ID und Ihr Client Token korrekt sind * Stellen Sie sicher, dass Sie das SDK ordnungsgemäß initialisiert haben 3. **Häufige iOS-Probleme** * Stellen Sie sicher, dass alle Info.plist-Einträge korrekt sind * Überprüfen Sie, dass URL-Schemas ordnungsgemäß konfiguriert sind * Prüfen Sie, dass Ihre Bundle ID mit der im Facebook Dashboard registrierten übereinstimmt ### Testen [Section titled “Testen”](#testen) 1. **Fügen Sie vor dem Testen Testbenutzer in der Facebook Developer Console hinzu** * Gehen Sie zu Roles > Test Users * Erstellen Sie einen Testbenutzer * Verwenden Sie diese Anmeldedaten zum Testen 2. **Testen Sie sowohl Debug- als auch Release-Builds** * Debug-Build mit Debug-Schlüssel-Hash * Release-Build mit Release-Schlüssel-Hash * Testen Sie sowohl auf Emulator als auch auf physischen Geräten Denken Sie daran, den vollständigen Login-Ablauf zu testen, einschließlich: * Erfolgreicher Login * Login-Abbruch * Fehlerbehandlung * Logout-Funktionalität # Firebase Google Login auf Android > Erfahren Sie, wie Sie Google Sign-In mit Firebase Authentication auf Android mithilfe des Capacitor Social Login Plugins einrichten. ## Einführung [Section titled “Einführung”](#einführung) Dieser Leitfaden hilft Ihnen dabei, Google Sign-In mit Firebase Authentication auf Android zu integrieren. Ich gehe davon aus, dass Sie die [allgemeine Firebase Google Einrichtung](./general/) bereits abgeschlossen haben. Note Ich gehe davon aus, dass Sie Ihre Android-App noch nicht in der Firebase Console erstellt haben. Wenn Sie dies bereits getan haben, werden Ihre Schritte leicht abweichen. ## Einrichtungsschritte [Section titled “Einrichtungsschritte”](#einrichtungsschritte) 1. Gehen Sie zu Ihrer Projektübersicht unter [console.cloud.google.com](https://console.cloud.google.com/) ![Firebase Projektübersicht](/social-login-assets/firebase_project_overview.webp) 2. Klicken Sie auf die `Add app` Schaltfläche ![Firebase App hinzufügen Schaltfläche](/social-login-assets/firebase_project_add_app.webp) Note Es ist möglich, dass Sie hier nach dieser Schaltfläche suchen müssen. Dies gilt nur, wenn Sie bereits eine andere App in der Firebase Console erstellt haben. ![Firebase App hinzufügen Schaltfläche](/social-login-assets/firebase_project_add_app_2.webp) 3. Wählen Sie `Android` ![Firebase App hinzufügen Android Schaltfläche](/social-login-assets/firebase_project_add_app_android.webp) 4. Füllen Sie den ersten Teil des Formulars aus 1. Füllen Sie den `Android package name` aus 1. Öffnen Sie Android Studio bei Ihrer App mit `npx cap open android` 2. Ganz unten im Navigator finden Sie die `Gradle Scripts` ![Gradle Scripts Bereich im Android Studio Projekt-Navigator](/social-login-assets/studio_gradle_scripts.png) 3. Finden Sie `build.gradle` für das Modul `app` ![build.gradle (Module: app) Datei im Gradle Scripts Bereich](/social-login-assets/studio_build_gradle.png) 4. Kopieren Sie die `android.defaultConfig.applicationId`. Dies wird Ihr `package name` in der Firebase Console sein ![Build.gradle Datei zeigt applicationId Konfiguration](/social-login-assets/studio_build_gradle_app_id.png) Note Die hier gezeigte ID wird sich von der unterscheiden, die ich für den Rest des Leitfadens verwenden werde. Ich werde `app.capgo.plugin.SocialLogin` für den Rest des Leitfadens verwenden. 5. Fügen Sie sie in der Firebase Console ein ![Firebase App hinzufügen Android Package Name Feld](/social-login-assets/firebase_project_add_app_android_package_name.webp) 2. Klicken Sie auf die `Register app` Schaltfläche ![Firebase App hinzufügen Android Registrieren Schaltfläche](/social-login-assets/firebase_project_add_app_android_register.webp) 5. Überspringen Sie den Schritt `Download and then add config file` ![Firebase App hinzufügen Android Überspringen 'Download and then add config file'](/social-login-assets/firebase_project_add_app_android_skip_download.webp) 6. Überspringen Sie den Schritt `Add firebase SDK` ![Firebase App hinzufügen Android Überspringen 'Add firebase SDK'](/social-login-assets/firebase_project_add_app_android_skip_download_firebase_sdk.webp) 7. Klicken Sie auf die `Continue to console` Schaltfläche ![Firebase App hinzufügen Android Weiter zur Console Schaltfläche](/social-login-assets/firebase_project_add_app_android_continue_to_console.webp) 8. Wenn Sie nicht automatisch authentifiziert werden, gehen Sie zu `settings` -> `general` -> `your apps` -> finden Sie Ihre Android-App und klicken Sie darauf ![Firebase App hinzufügen Android Einstellungen Allgemein Ihre Apps Schaltfläche](/social-login-assets/firebase_project_add_app_android_settings_general_your_apps.webp) 9. Holen Sie sich Ihren SHA1-Fingerabdruck Folgen Sie den Schritten 10-11 aus der [Google Login Android Einrichtungsanleitung](/docs/plugins/social-login/google/android/#using-google-login-on-android): 1. Öffnen Sie nun das Terminal. Stellen Sie sicher, dass Sie sich im `android` Ordner Ihrer App befinden und führen Sie `./gradlew signInReport` aus ![Terminal zeigt gradlew signInReport Befehl](/social-login-assets/term_sign_report.png) 2. Scrollen Sie zum Anfang dieses Befehls. Sie sollten Folgendes sehen. Kopieren Sie den `SHA1`. ![Terminal-Ausgabe zeigt SHA1-Zertifikat-Fingerabdruck](/social-login-assets/term_sign_report_res.png) Note Ich werde einen leicht anderen SHA1 für den Rest des Leitfadens verwenden, da ich seit dem Schreiben der ursprünglichen Google Login Android Einrichtungsanleitung den Computer gewechselt habe. Caution Der SHA1 ist sehr wichtig, richtig zu bekommen. Wenn Sie einen Fehler machen, wird die Authentifizierung auf seltsame Weise fehlschlagen. Bitte ****[LESEN SIE DIE GOOGLE LOGIN ANDROID EINRICHTUNGSANLEITUNG](/docs/plugins/social-login/google/android/#using-google-login-on-android)****, um es richtig zu machen. 10. Fügen Sie den SHA1 zum Firebase-Projekt hinzu 1. Klicken Sie auf die `Add fingerprint` Schaltfläche ![Firebase App hinzufügen Android Fingerabdruck hinzufügen Schaltfläche](/social-login-assets/firebase_project_add_app_android_add_fingerprint.webp) 2. Fügen Sie den SHA1 ein, den Sie im vorherigen Schritt kopiert haben ![Firebase App hinzufügen Android Fingerabdruck hinzufügen SHA1 Feld](/social-login-assets/firebase_project_add_app_android_add_fingerprint_sha1.webp) 3. Klicken Sie auf die `Save` Schaltfläche ![Firebase App hinzufügen Android Fingerabdruck hinzufügen Speichern Schaltfläche](/social-login-assets/firebase_project_add_app_android_add_fingerprint_add.webp) 11. Holen Sie sich Ihre Web-Client-ID 1. Gehen Sie zu `Build` -> `Authentication` ![Firebase Authentication Menü](/social-login-assets/firebase_select_authentication.webp) 2. Klicken Sie auf die `Sign-in method` Schaltfläche ![Firebase Authentication Anmeldemethode Schaltfläche](/social-login-assets/firebase_select_authentication_sign_in_method.webp) 3. Klicken Sie auf den `Google` Anbieter ![Firebase Authentication Anmeldemethode Google Anbieter](/social-login-assets/firebase_select_authentication_sign_in_method_google.webp) 4. Klicken Sie auf die `Web SDK configuration` Schaltfläche ![Firebase Authentication Anmeldemethode Web SDK Konfiguration Schaltfläche](/social-login-assets/firebase_select_authentication_sign_in_method_web_sdk_configuration.webp) 5. Kopieren Sie die `Web client ID`. Dies wird Ihre `webClientId` in der `initialize` Methode des Plugins sein. ![Firebase Authentication Anmeldemethode Web SDK Konfiguration Web Client ID](/social-login-assets/firebase_select_authentication_sign_in_method_web_sdk_configuration_web_client_id.webp) 12. Verwenden Sie die Web-Client-ID in JS. Note Ich empfehle die Verwendung der `authenticateWithGoogle` Hilfsfunktion, die in der [authUtils.ts](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/authUtils.ts) Datei der Beispiel-App verfügbar ist. An diesem Punkt sind Sie ****TECHNISCH**** bereit, Google Sign-In mit Firebase Authentication auf Android zu verwenden. Ich würde jedoch empfehlen, die Einrichtung in der Google Cloud Console zu überprüfen, wie im nächsten Schritt erklärt. ## Einrichtung in der Google Cloud Console überprüfen [Section titled “Einrichtung in der Google Cloud Console überprüfen”](#einrichtung-in-der-google-cloud-console-überprüfen) Um sicherzustellen, dass die Einrichtung korrekt ist, sollten Sie die Einrichtung in der Google Cloud Console überprüfen. 1. Gehen Sie zu [console.cloud.google.com](https://console.cloud.google.com/) 2. Finden Sie Ihr Projekt 1. Klicken Sie auf die Projektauswahl ![Google Cloud Console Projektauswahl](/social-login-assets/firebase_double_check_gc_project_select.webp) 2. Suchen Sie Ihr Projekt nach dem genauen Namen Ihres Firebase-Projekts und klicken Sie darauf. In meinem Fall ist es `sociallogin-tutorial-app`. ![Firebase Projektauswahl Projekt](/social-login-assets/firebase_gc_project_select_project.webp) 3. Öffnen Sie die Suchleiste und öffnen Sie `credentials` 1. Öffnen Sie die Suchleiste ![Google Cloud Console Suchleiste](/social-login-assets/firebase_double_check_gc_search_bar.webp) 2. Suchen Sie nach `credentials` und klicken Sie auf die `APIs and Services` Option (Nummer 2 auf dem Screenshot) ![Google Cloud Console Credentials Suche](/social-login-assets/firebase_gc_credentials_search.webp) 4. Überprüfen Sie, dass Sie sowohl die Android- als auch die Web-Client-IDs in der Liste sehen. ![Google Cloud Console Credentials Liste](/social-login-assets/firebase_gc_credentials_list.webp) Caution Wenn Sie nicht sowohl die Android- als auch die Web-Client-IDs in der Liste sehen, haben Sie einen Fehler bei der Einrichtung gemacht. Bitte gehen Sie zurück und überprüfen Sie Ihre Schritte. Es ist auch möglich, und es ist mir passiert, dass Sie den Android SHA1-Hash mit derselben App-ID bereits in einem anderen Projekt hinzugefügt haben. Dies führt dazu, dass Firebase keine Android-Client-ID erstellen kann. In diesem Fall müssen Sie den SHA1 aus dem anderen Projekt entfernen sowie auf Firebase (indem Sie die Firebase Console verwenden, um die Android-App zu entfernen) und ihn auf Firebase neu erstellen 5. Überprüfen Sie, dass die Android-Client-ID in der Firebase Console korrekt konfiguriert ist. 1. Klicken Sie auf die `Android` App ![Google Cloud Console Android App Auswahl](/social-login-assets/firebase_gc_android_app_select.webp) 2. Bestätigen Sie, dass der SHA1-Hash korrekt konfiguriert ist und mit dem übereinstimmt, den Sie in den vorherigen Schritten kopiert haben. ![Google Cloud Console Android App SHA1 Konfiguriert](/social-login-assets/firebase_gc_android_app_sha1_configured.webp) 6. Überprüfen Sie, dass die Web-Client-ID in der Firebase Console korrekt konfiguriert ist. 1. Klicken Sie auf die `Web` App ![Google Cloud Console Web App Auswahl](/social-login-assets/firebase_gc_web_app_select.webp) 2. Bestätigen Sie, dass die Client-ID mit der übereinstimmt, die Sie in den vorherigen Schritten kopiert haben. ![Google Cloud Console Web App Client ID Konfiguriert](/social-login-assets/firebase_gc_web_app_client_id_configured.webp) Note Bitte ignorieren Sie die restlichen Einstellungen des Web-Clients. Wir werden dies im [Web-Einrichtungsleitfaden](/docs/plugins/social-login/firebase/google/web/) besprechen. Voilà! Sie sind jetzt bereit, Google Sign-In mit Firebase Authentication auf Android zu verwenden. ## Fehlerbehebung [Section titled “Fehlerbehebung”](#fehlerbehebung) Wenn die Authentifizierung hängt oder fehlschlägt: * Überprüfen Sie, dass die `idToken` Zielgruppe mit Ihrer Firebase Web-Client-ID übereinstimmt * Prüfen Sie, dass Google Sign-In in der Firebase Console aktiviert ist * Stellen Sie sicher, dass der SHA-1-Fingerabdruck korrekt konfiguriert ist * Überprüfen Sie den [Beispiel-App-Code](https://github.com/Cap-go/capacitor-social-login/tree/main/example-app/src/authUtils.ts) als Referenz # Firebase Google Login - Allgemeine Einrichtung > Erfahren Sie, wie Sie Google Sign-In mit Firebase Authentication mithilfe des Capacitor Social Login Plugins einrichten. ## Einführung [Section titled “Einführung”](#einführung) Dieser Leitfaden führt Sie durch die Integration von Google Sign-In mit Firebase Authentication unter Verwendung des Capacitor Social Login Plugins. Diese Einrichtung ermöglicht es Ihnen, natives Google Sign-In auf mobilen Plattformen zu verwenden und gleichzeitig Firebase Auth für die Backend-Authentifizierung zu nutzen. ## Einrichtungsschritte [Section titled “Einrichtungsschritte”](#einrichtungsschritte) 1. Bitte gehen Sie zu [console.cloud.google.com](https://console.cloud.google.com/) 2. Wählen Sie das Projekt aus, das Sie verwenden möchten ![Firebase Projektauswahl](/social-login-assets/firebase_project_select.webp) 3. Gehen Sie zum `Authentication` Menü 1. Klicken Sie auf `build` 2. Klicken Sie auf `Authentication` ![Firebase Authentication Menü](/social-login-assets/firebase_select_authentication.webp) 4. Klicken Sie auf die `Loslegen` Schaltfläche ![Firebase Erste Schritte Schaltfläche](/social-login-assets/firebase_auth_start.webp) 5. Wählen Sie ENTWEDER `Email/Password` UND `Google` ODER NUR `Google` Note Ich werde `Email/Password` UND `Google` auswählen, da ich beide verwenden möchte, aber Sie könnten nur `Google` auswählen. Dies ist etwas, das Sie später ändern können. ![Firebase E-Mail/Passwort und Google auswählen Schaltfläche](/social-login-assets/firebase_google_email_auth.webp) ![Firebase E-Mail-Authentifizierung aktivieren](/social-login-assets/firebase_auth_enable_email.webp) ![Firebase speichern Google-Authentifizierung aktivieren](/social-login-assets/firebase_auth_enable_email_save.webp) ![Firebase Anbieter hinzufügen Schaltfläche aktivieren](/social-login-assets/firebase_auth_add_provider_after_email.webp) ![Firebase Google-Anbieter hinzufügen nach Hinzufügen des E-Mail-Anbieters](/social-login-assets/firebase_auth_add_google_provider_after_email.webp) 6. Aktivieren Sie den `Google` Anbieter ![Firebase Google-Authentifizierung aktivieren](/social-login-assets/firebase_auth_add_google_provider_enable.webp) 7. Fügen Sie die Support-E-Mail hinzu ![Firebase Support-E-Mail hinzufügen](/social-login-assets/firebase_auth_add_support_email_google_auth.webp) ![Firebase Support-E-Mail hinzufügen Teil 2](/social-login-assets/firebase_auth_add_support_email_google_auth_2.webp) 8. Ändern Sie den `Public-facing name for project`. Note Dies wird den Benutzern angezeigt, daher empfehle ich, ihn zu etwas Beschreiberem zu ändern. ![Firebase öffentlich sichtbaren Namen ändern](/social-login-assets/firebase_auth_change_public_facing_name.webp) 9. Klicken Sie auf die `Save` Schaltfläche ![Firebase öffentlich sichtbaren Namen speichern](/social-login-assets/firebase_auth_save_google_auth.webp) Voilà, Sie haben jetzt Google Sign-In mit Firebase Authentication aktiviert 🎉 # Firebase Google Login auf iOS > Erfahren Sie, wie Sie Google Sign-In mit Firebase Authentication auf iOS mithilfe des Capacitor Social Login Plugins einrichten. ## Einführung [Section titled “Einführung”](#einführung) Dieser Leitfaden hilft Ihnen dabei, Google Sign-In mit Firebase Authentication auf iOS zu integrieren. Ich gehe davon aus, dass Sie die [allgemeine Firebase Google Einrichtung](./general/) bereits abgeschlossen haben. Note Ich gehe davon aus, dass Sie Ihre iOS-App noch nicht in der Firebase Console erstellt haben. Wenn Sie dies bereits getan haben, werden Ihre Schritte leicht abweichen. ## Einrichtungsschritte [Section titled “Einrichtungsschritte”](#einrichtungsschritte) 1. Gehen Sie zu Ihrer Projektübersicht unter [console.cloud.google.com](https://console.cloud.google.com/) ![Firebase Projektübersicht](/social-login-assets/firebase_project_overview.webp) 2. Klicken Sie auf die `Add app` Schaltfläche ![Firebase App hinzufügen Schaltfläche](/social-login-assets/firebase_project_add_app.webp) Note Es ist möglich, dass Sie hier nach dieser Schaltfläche suchen müssen. Dies gilt nur, wenn Sie bereits eine andere App in der Firebase Console erstellt haben. ![Firebase App hinzufügen Schaltfläche](/social-login-assets/firebase_project_add_app_2.webp) 3. Wählen Sie `iOS` ![Firebase App hinzufügen iOS Schaltfläche](/social-login-assets/firebase_project_add_app_ios.webp) 4. Füllen Sie den ersten Teil des Formulars aus 1. Füllen Sie die `Apple bundle ID` aus 1. Öffnen Sie Xcode bei Ihrer App mit `npx cap open ios` 2. Doppelklicken Sie auf `App` ![App Target im Xcode Projekt-Navigator](/social-login-assets/xcode_app_click.png) 3. Stellen Sie sicher, dass Sie sich bei `Targets -> App` befinden ![Targets Bereich in Xcode mit App ausgewählt](/social-login-assets/xcode_targets_app.png) 4. Finden Sie Ihre `Bundle Identifier` ![Bundle Identifier Feld in Xcode Projekteinstellungen](/social-login-assets/xcode_bundle_id.png) Note Die hier gezeigte ID wird sich von der unterscheiden, die ich für den Rest des Leitfadens verwenden werde. Ich werde `app.capgo.plugin.SocialLogin` für den Rest des Leitfadens verwenden. 5. Kopieren Sie die `Bundle Identifier` und fügen Sie sie in der Firebase Console ein ![Firebase App hinzufügen iOS Bundle ID Feld](/social-login-assets/firebase_project_add_app_ios_bundle_id.webp) 2. Klicken Sie auf die `Register app` Schaltfläche ![Firebase App hinzufügen iOS Registrieren Schaltfläche](/social-login-assets/firebase_project_add_app_ios_register.webp) 5. Überspringen Sie den Schritt `Download config file` ![Firebase App hinzufügen iOS Überspringen Download Schaltfläche](/social-login-assets/firebase_project_add_app_ios_skip_download.webp) 6. Überspringen Sie den Schritt `Add firebase SDK` ![Firebase App hinzufügen iOS Überspringen Firebase SDK Schaltfläche hinzufügen](/social-login-assets/firebase_project_add_app_ios_skip_download_firebase_sdk_2.webp) 7. Überspringen Sie den Schritt `Add initialization code` ![Firebase App hinzufügen iOS Überspringen Initialisierungscode hinzufügen Schaltfläche](/social-login-assets/firebase_project_add_app_ios_skip_download_firebase_sdk.webp) 8. Klicken Sie auf die `Continue to console` Schaltfläche ![Firebase App hinzufügen iOS Weiter zur Console Schaltfläche](/social-login-assets/firebase_project_add_app_ios_continue_to_console.webp) 9. Holen Sie sich Ihre iOS-Client-ID und Ihre `YOUR_DOT_REVERSED_IOS_CLIENT_ID` 1. Gehen Sie zur Google Cloud Console unter [console.cloud.google.com](https://console.cloud.google.com/) 2. Finden Sie Ihr Projekt 1. Klicken Sie auf die Projektauswahl ![Google Cloud Console Projektauswahl](/social-login-assets/firebase_double_check_gc_project_select.webp) 2. Suchen Sie Ihr Projekt nach dem genauen Namen Ihres Firebase-Projekts und klicken Sie darauf. In meinem Fall ist es `sociallogin-tutorial-app`. ![Firebase Projektauswahl Projekt](/social-login-assets/firebase_gc_project_select_project.webp) 3. Öffnen Sie die Suchleiste und öffnen Sie `credentials` 1. Öffnen Sie die Suchleiste ![Google Cloud Console Suchleiste](/social-login-assets/firebase_double_check_gc_search_bar.webp) 2. Suchen Sie nach `credentials` und klicken Sie auf die `APIs and Services` Option (Nummer 2 auf dem Screenshot) ![Google Cloud Console Credentials Suche](/social-login-assets/firebase_gc_credentials_search.webp) 4. Klicken Sie auf `iOS client for [YOUR_APP_ID] (auto created by Google Service)`. In meinem Fall ist es `sociallogin-tutorial-app`. ![Google Cloud Console Credentials iOS Client ID](/social-login-assets/firebase_gc_credentials_ios_client_id.webp) 5. Kopieren Sie die `Client ID` sowie das `iOS URL scheme`. Dies werden jeweils Ihre `iOSClientId` und `YOUR_DOT_REVERSED_IOS_CLIENT_ID` sein. Note Sie werden die `iOSClientId` in der `initialize` Methode des Plugins übergeben, während Sie die `YOUR_DOT_REVERSED_IOS_CLIENT_ID` in der `Info.plist` Datei Ihrer App verwenden werden, wie im nächsten Teil dieses Leitfadens erklärt. ![Google Cloud Console Credentials iOS Client ID Kopieren](/social-login-assets/firebase_gc_credentials_ios_client_id_copy.webp) 10. Holen Sie sich Ihre Web-Client-ID 1. Gehen Sie zurück zur Firebase Console und gehen Sie zu `Build` -> `Authentication` ![Firebase Authentication Menü](/social-login-assets/firebase_select_authentication.webp) 2. Klicken Sie auf die `Sign-in method` Schaltfläche ![Firebase Authentication Anmeldemethode Schaltfläche](/social-login-assets/firebase_select_authentication_sign_in_method.webp) 3. Klicken Sie auf den `Google` Anbieter ![Firebase Authentication Anmeldemethode Google Anbieter](/social-login-assets/firebase_select_authentication_sign_in_method_google.webp) 4. Klicken Sie auf die `Web SDK configuration` Schaltfläche ![Firebase Authentication Anmeldemethode Web SDK Konfiguration Schaltfläche](/social-login-assets/firebase_select_authentication_sign_in_method_web_sdk_configuration.webp) 5. Kopieren Sie die `Web client ID`. Dies wird Ihre `webClientId` in der `initialize` Methode des Plugins sein. ![Firebase Authentication Anmeldemethode Web SDK Konfiguration Web Client ID](/social-login-assets/firebase_select_authentication_sign_in_method_web_sdk_configuration_web_client_id.webp) 11. Ändern Sie die Info.plist Ihrer App 1. Öffnen Sie Xcode und finden Sie die `Info.plist` Datei ![Info.plist Datei im Xcode Projekt-Navigator](/social-login-assets/xcode_info_file.png) 2. Rechtsklicken Sie auf diese Datei und öffnen Sie sie als Quellcode ![Rechtsklick-Menü zeigt Als Quellcode öffnen Option](/social-login-assets/xcode_open_as_src_code.png) 3. Am Ende Ihrer `Plist` Datei sehen Sie ein `` Tag ![Schließendes dict Tag in Info.plist Datei](/social-login-assets/xcode_dict_tag.png) 4. Fügen Sie das folgende Fragment direkt vor dem schließenden `` Tag ein ![Info.plist mit URL-Schemas-Code vor schließendem dict Tag eingefügt](/social-login-assets/xcode_plist_inserted.png) ```xml CFBundleURLTypes CFBundleURLSchemes YOUR_DOT_REVERSED_IOS_CLIENT_ID GIDClientID YOUR_IOS_CLIENT_ID.apps.googleusercontent.com ``` 5. Ändern Sie die `YOUR_DOT_REVERSED_IOS_CLIENT_ID` in den in Schritt 9 kopierten Wert (das iOS URL-Schema) ![Info.plist mit tatsächlicher umgekehrter Client-ID in URL-Schemas eingefügt](/social-login-assets/xcode_plist_final.png) Caution Stellen Sie sicher, dass dieser Wert **MIT** `com.googleusercontent.apps` **BEGINNT** 12. Ändern Sie die `YOUR_IOS_CLIENT_ID` in die iOS-Client-ID, die Sie in Schritt 9 kopiert haben 13. Speichern Sie die Datei mit `Command + S` 14. Ändern Sie die `AppDelegate.swift` 1. Öffnen Sie das AppDelegate ![AppDelegate.swift Datei im Xcode Projekt-Navigator](/social-login-assets/xcode_app_deleg.png) 2. Fügen Sie `import GoogleSignIn` am Anfang der Datei ein ![AppDelegate.swift mit GoogleSignIn Import hinzugefügt](/social-login-assets/xcode_app_deleg_google_sign_in.png) 3. Finden Sie die `func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:])` Funktion ![Original application openURL Funktion im AppDelegate](/social-login-assets/xcode_app_deleg_app_fn.png) 4. Ändern Sie die Funktion so, dass sie folgendermaßen aussieht ```swift func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool { // Called when the app was launched with a url. Feel free to add additional processing here, // but if you want the App API to support tracking app url opens, make sure to keep this call var handled: Bool handled = GIDSignIn.sharedInstance.handle(url) if handled { return true } return ApplicationDelegateProxy.shared.application(app, open: url, options: options) } ``` ![Geänderte application openURL Funktion mit GoogleSignIn Verarbeitung](/social-login-assets/xcode_app_deleg_app_fn_mod.png) 5. Speichern Sie die Datei mit `Command + S` 15. Verwendung des Google-Logins in Ihrer App An diesem Punkt sind Sie bereit, den Google-Login in Ihrer App zu verwenden. Bitte verwenden Sie die [authUtils.ts](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/authUtils.ts) Datei der Beispiel-App zur Authentifizierung mit Google. Der Benutzer wird bei der ersten Anmeldung automatisch in Firebase Auth erstellt ## Fehlerbehebung [Section titled “Fehlerbehebung”](#fehlerbehebung) Wenn die Authentifizierung hängt oder fehlschlägt: * Überprüfen Sie, dass die `idToken` Zielgruppe mit Ihrer Firebase Web-Client-ID übereinstimmt * Prüfen Sie, dass Google Sign-In in der Firebase Console aktiviert ist * Stellen Sie sicher, dass die Info.plist die korrekten URL-Schemas und GIDClientID hat * Überprüfen Sie, dass `iOSServerClientId` mit Ihrer Web-Client-ID übereinstimmt * Überprüfen Sie den [Beispiel-App-Code](https://github.com/Cap-go/capacitor-social-login/tree/main/example-app/src/authUtils.ts) als Referenz # Firebase Google Login im Web > Erfahren Sie, wie Sie Google Sign-In mit Firebase Authentication im Web mithilfe des integrierten Google Sign-In von Firebase einrichten. ## Einführung [Section titled “Einführung”](#einführung) Das Capacitor Social Login Plugin **unterstützt keine Webplattformen**. Für Webanwendungen sollten Sie Firebases integriertes Google Sign-In direkt verwenden, das einen zuverlässigeren Popup-basierten Authentifizierungsablauf bietet. ## Warum nicht das Plugin im Web verwenden? [Section titled “Warum nicht das Plugin im Web verwenden?”](#warum-nicht-das-plugin-im-web-verwenden) Das Capacitor Social Login Plugin ist für native mobile Plattformen (Android und iOS) konzipiert, wo es plattformspezifische Authentifizierungsabläufe nutzen kann. Für das Web ist Firebases native `signInWithPopup` Methode: * ✅ Zuverlässiger und besser unterstützt * ✅ Verwaltet Browser-Session-Speicherung automatisch * ✅ Bietet bessere Fehlerbehandlung * ✅ Keine zusätzliche Konfiguration erforderlich ## Einrichtungsschritte [Section titled “Einrichtungsschritte”](#einrichtungsschritte) 1. **Firebase-Projekt konfigurieren** Stellen Sie sicher, dass in Ihrem Firebase-Projekt Google Sign-In aktiviert ist: * Gehen Sie zur [Firebase Console](https://console.firebase.google.com/) * Navigieren Sie zu Authentication > Sign-in method * Aktivieren Sie den Google Sign-In Anbieter 2. Fügen Sie Ihre autorisierten Domains hinzu 1. Gehen Sie zu Ihrer Projektübersicht unter [console.cloud.google.com](https://console.cloud.google.com/) ![Firebase Projektübersicht](/social-login-assets/firebase_project_overview.webp) 2. Öffnen Sie das `Authentication` Menü ![Firebase Authentication Menü](/social-login-assets/firebase_select_authentication.webp) 3. Klicken Sie auf die `Settings` Schaltfläche ![Firebase Authentication Anmeldemethode Schaltfläche](/social-login-assets/firebase_select_authentication_settings.webp) 4. Richten Sie die `Authorized domains` ein ![Firebase Authentication Einstellungen Autorisierte Domains](/social-login-assets/firebase_select_authentication_settings_authorized_domains.webp) ## Beispiel-Implementierung [Section titled “Beispiel-Implementierung”](#beispiel-implementierung) Siehe die [authUtils.ts](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/authUtils.ts) Datei in der Beispiel-App für eine vollständige Implementierung, die: * Firebases `signInWithPopup` für Webplattformen verwendet * Das Capacitor Social Login Plugin für Android/iOS Plattformen verwendet * Plattformerkennung automatisch verarbeitet Das Beispiel zeigt, wie Sie die integrierte Firebase-Methode für das Web bedingt verwenden können, während Sie das Plugin für native Plattformen verwenden. ## Zusätzliche Ressourcen [Section titled “Zusätzliche Ressourcen”](#zusätzliche-ressourcen) * [Firebase Authentication Dokumentation](https://firebase.google.com/docs/auth) - Vollständige Firebase Auth Dokumentation * [Firebase Google Sign-In für Web](https://firebase.google.com/docs/auth/web/google-signin) - Offizielle Firebase Anleitung für Google Sign-In im Web * [Google Login Einrichtungsanleitung](/docs/plugins/social-login/google/general/) - Anleitung zum Konfigurieren autorisierter Domains und OAuth Consent Screen # Firebase Integration Einführung > Erfahren Sie, wie Sie Firebase Authentication mit dem Capacitor Social Login Plugin für eine vollständige Authentifizierungslösung integrieren. ## Überblick [Section titled “Überblick”](#überblick) Dieses Tutorial führt Sie durch die Einrichtung von Firebase Authentication mit dem Capacitor Social Login Plugin. Diese Integration ermöglicht es Ihnen, native Social-Login-Anbieter (Google, Apple, Facebook, Twitter) auf mobilen Plattformen zu verwenden und gleichzeitig Firebase Auth für die Backend-Authentifizierung und Firestore für die Datenspeicherung zu nutzen. ## Was Sie lernen werden [Section titled “Was Sie lernen werden”](#was-sie-lernen-werden) * Wie man Firebase Authentication konfiguriert * Wie man das Capacitor Social Login Plugin mit Firebase Auth integriert * Plattformspezifische Einrichtung für Android, iOS und Web ## Was Sie benötigen [Section titled “Was Sie benötigen”](#was-sie-benötigen) Bevor Sie beginnen, stellen Sie sicher, dass Sie Folgendes haben: 1. **Ein Firebase-Projekt** * Erstellen Sie ein Projekt in der [Firebase Console](https://console.firebase.google.com/) * Aktivieren Sie Authentication (E-Mail/Passwort und Google Sign-In) * Holen Sie sich Ihre Firebase-Konfigurationsanmeldedaten 2. **Firebase JS SDK** * Installieren Sie Firebase in Ihrem Projekt: ```bash npm install firebase ``` 3. **Ein Capacitor-Projekt** * Eine vorhandene Capacitor-Anwendung * Capacitor Social Login Plugin installiert: ```bash npm install @capgo/capacitor-social-login npx cap sync ``` ## Beispielanwendung [Section titled “Beispielanwendung”](#beispielanwendung) Eine vollständige funktionierende Beispielanwendung ist im Repository verfügbar: **Code-Repository**: [Sie finden das Code-Repository hier](https://github.com/Cap-go/capacitor-social-login/tree/main/example-app) Die Beispiel-App demonstriert: * E-Mail/Passwort-Authentifizierung mit Firebase * Google Sign-In Integration (Android, iOS und Web) * Einen einfachen Schlüssel-Wert-Speicher unter Verwendung von Firebase Firestore Collections * Benutzerspezifische Datenspeicherung in Firestore Subcollections ## Ein Wort zur Verwendung des Firebase SDK auf Capacitor [Section titled “Ein Wort zur Verwendung des Firebase SDK auf Capacitor”](#ein-wort-zur-verwendung-des-firebase-sdk-auf-capacitor) Wenn Sie das Firebase JS SDK auf Capacitor verwenden, müssen Sie sich bewusst sein, dass Sie bei der Verwendung der Authentifizierungsmethoden die Firebase Auth Instanz etwas anders initialisieren müssen. Auf der Webplattform würden Sie die `getAuth` Funktion verwenden, um die Firebase Auth Instanz zu erhalten. ```typescript import { initializeApp } from 'firebase/app'; import { getAuth } from 'firebase/auth'; const app = initializeApp(firebaseConfig); const auth = getAuth(app); ``` Leider funktioniert dies auf Capacitor nicht und verursacht, dass Firebase Auth hängt. Wie in [diesem Blogbeitrag](https://harryherskowitz.com/2021/08/23/firebase-capacitor.html) angegeben, müssen Sie die `initializeAuth` Funktion verwenden, um die Firebase Auth Instanz zu initialisieren. Dies sieht folgendermaßen aus: ```typescript import { initializeApp } from 'firebase/app'; import { initializeAuth } from 'firebase/auth'; const app = initializeApp(firebaseConfig); function whichAuth() { let auth; if (Capacitor.isNativePlatform()) { auth = initializeAuth(app, { persistence: indexedDBLocalPersistence, }); } else { auth = getAuth(app); } return auth; } export const auth = whichAuth(); ``` ## Nächste Schritte [Section titled “Nächste Schritte”](#nächste-schritte) Fahren Sie mit den Einrichtungsanleitungen fort: * [Firebase Einrichtung](./google/general/) - Firebase-Projekt konfigurieren * [Android Einrichtung](./google/android/) - Android-spezifische Konfiguration * [iOS Einrichtung](./google/ios/) - iOS-spezifische Konfiguration * [Web Einrichtung](./google/web/) - Web-spezifische Konfiguration # Erste Schritte > Entdecken Sie, wie Sie das Capacitor Social Login Plugin installieren und konfigurieren, um die Authentifizierung Ihrer App mit nahtloser Integration für Google, Apple und Facebook Logins zu verbessern. 1. **Installieren Sie das Paket** * npm ```sh npm i @capgo/capacitor-social-login ``` * pnpm ```sh pnpm add @capgo/capacitor-social-login ``` * yarn ```sh yarn add @capgo/capacitor-social-login ``` * bun ```sh bun add @capgo/capacitor-social-login ``` 2. **Mit nativen Projekten synchronisieren** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` 3. **Konfigurieren Sie das Plugin** [![](/icons/google.svg) Google](/docs/plugins/social-login/google/general/) [Konfigurieren Sie Google Sign-In für Ihre App](/docs/plugins/social-login/google/general/) [ Facebook](/docs/plugins/social-login/facebook/) [Richten Sie die Facebook Login Integration ein](/docs/plugins/social-login/facebook/) [ Apple](/docs/plugins/social-login/apple/general/) [Aktivieren Sie Sign in with Apple](/docs/plugins/social-login/apple/general/) # Google Login auf Android > Dieser Leitfaden bietet eine umfassende Anleitung zur Einrichtung von Google Login mit Capacitor für Android-Geräte und erläutert jeden Schritt, um einen reibungslosen Integrationsprozess sicherzustellen und potenzielle Herausforderungen anzusprechen, die auftreten können. ## Einführung [Section titled “Einführung”](#einführung) In diesem Leitfaden lernen Sie, wie Sie Google Login mit Capgo Social Login für Android einrichten. Ich gehe davon aus, dass Sie die [allgemeine Einrichtungsanleitung](/docs/plugins/social-login/google/general/) bereits gelesen haben. ## Verwendung von Google Login auf Android [Section titled “Verwendung von Google Login auf Android”](#verwendung-von-google-login-auf-android) In diesem Teil lernen Sie, wie Sie Google Login in Android einrichten. Caution Das Android SHA1-Zertifikat ist extrem schmerzhaft und ich würde es niemandem wünschen, dies einrichten zu müssen. Die folgenden Schritte gehen vom einfachsten Szenario aus: Eine App, die nicht im Google Play Store veröffentlicht ist und nur vom lokalen Simulator oder Entwicklungs-Hardware-Gerät verwendet wird. Wenn Sie Ihre App im Google Play Store bereitgestellt haben, **MÜSSEN** Sie eine zusätzliche Android-Client-ID hinzufügen, die den SHA1 aus der Google Play Console für Produktions-Releases enthält. Sie finden den SHA1-Hash, den Google Play zum Signieren Ihres Release-Bundles verwendet, unter `Test and release > Setup > App Signing`. Schließlich ist es wichtig zu erwähnen, dass der Fehler **NICHT** offensichtlich sein wird, wenn Sie einen Fehler machen. Er kann sehr schwer zu debuggen sein. Wenn Sie Probleme mit der Einrichtung haben, schauen Sie sich bitte die [Github Issues](https://github.com/Cap-go/capacitor-social-login/issues) an. Zusätzlich können Sie sich den Abschnitt zur Fehlerbehebung des [Google Login Setup für Android](#troubleshooting) für weitere Informationen ansehen. Note Sie können mehrere Android-Client-IDs erstellen. Dies ist erforderlich, wenn Sie mehrere SHA1-Zertifikate haben. 1. Erstellen Sie eine Android-Client-ID. 1. Klicken Sie auf die Suchleiste ![Google Console Suchleiste](/social-login-assets/google_cons_search.png) 2. Suchen Sie nach `credentials` und klicken Sie auf die `APIs and Services` Option (Nummer 2 auf dem Screenshot) ![Suchergebnisse zeigen Credentials-Option mit hervorgehobenen APIs and Services](/social-login-assets/google_cons_cred_search.png) 3. Klicken Sie auf `create credentials` ![Credentials erstellen Schaltfläche in Google Console](/social-login-assets/google_cons_create_cred.png) 4. Wählen Sie `OAuth client ID` ![OAuth client ID Option im Credentials-Erstellungsmenü](/social-login-assets/google_cons_cred_oauth.png) 5. Wählen Sie den `Android` Anwendungstyp ![Anwendungstyp-Auswahl mit hervorgehobener Android-Option](/social-login-assets/google_cons_app_type_android.png) 6. Öffnen Sie Android Studio 7. Ganz unten im Navigator finden Sie die `Gradle Scripts` ![Gradle Scripts Bereich im Android Studio Projekt-Navigator](/social-login-assets/studio_gradle_scripts.png) 8. Finden Sie `build.gradle` für das Modul `app` ![build.gradle (Module: app) Datei im Gradle Scripts Bereich](/social-login-assets/studio_build_gradle.png) 9. Kopieren Sie die `android.defaultConfig.applicationId`. Dies wird Ihr `package name` in der Google Console sein ![Build.gradle Datei zeigt applicationId Konfiguration](/social-login-assets/studio_build_gradle_app_id.png) 10. Öffnen Sie nun das Terminal. Stellen Sie sicher, dass Sie sich im `android` Ordner Ihrer App befinden und führen Sie `./gradlew signInReport` aus ![Terminal zeigt gradlew signInReport Befehl](/social-login-assets/term_sign_report.png) 11. Scrollen Sie zum Anfang dieses Befehls. Sie sollten Folgendes sehen. Kopieren Sie den `SHA1`. ![Terminal-Ausgabe zeigt SHA1-Zertifikat-Fingerabdruck](/social-login-assets/term_sign_report_res.png) 12. Gehen Sie nun zurück zur Google Console. Geben Sie Ihre `applicationId` als `Package Name` und Ihren SHA1 im Zertifikatsfeld ein und klicken Sie auf `create` ![Android-Client-Erstellungsformular mit ausgefüllten Paketnamen- und SHA1-Feldern](/social-login-assets/google_cons_creat_android_client.png) 2. Erstellen Sie einen Web-Client (dies ist für Android erforderlich) 1. Gehen Sie zur `Create credentials` Seite in der Google Console 2. Setzen Sie den Anwendungstyp auf `Web` ![Anwendungstyp-Auswahl mit hervorgehobener Web-Option](/social-login-assets/google_cons_app_type_web.png) 3. Klicken Sie auf `Create` ![Web-Client-Erstellungsformular mit Create-Schaltfläche unten](/social-login-assets/google_cons_web_app_create.png) 4. Kopieren Sie die Client-ID, Sie werden diese als `webClientId` in Ihrem JS/TS Code verwenden ![Client-ID Details zeigen Web-Client-ID zum Kopieren](/social-login-assets/google_cons_copy_web_client_id.png) 3. Ändern Sie Ihre `MainActivity` 1. Bitte öffnen Sie Ihre App in Android Studio. Sie können `cap open android` ausführen 2. Finden Sie `MainActivity.java` 1. Öffnen Sie den `app` Ordner ![App Ordner im Android Studio Projekt-Navigator](/social-login-assets/studio_app_folder.png) 2. Finden Sie `java` ![Java Ordner in Android Studio Projektstruktur](/social-login-assets/studio_app_java.png) 3. Finden Sie Ihre `MainActivity.java` und klicken Sie darauf ![MainActivity.java Datei in Paketstruktur](/social-login-assets/studio_app_java_activity_main.png) 3. Ändern Sie `MainActivity.java`. Bitte fügen Sie den folgenden Code hinzu ```java import ee.forgr.capacitor.social.login.GoogleProvider; import ee.forgr.capacitor.social.login.SocialLoginPlugin; import ee.forgr.capacitor.social.login.ModifiedMainActivityForSocialLoginPlugin; import com.getcapacitor.PluginHandle; import com.getcapacitor.Plugin; import android.content.Intent; import android.util.Log; import com.getcapacitor.BridgeActivity; // ModifiedMainActivityForSocialLoginPlugin ist SEHR SEHR wichtig !!!!!! public class MainActivity extends BridgeActivity implements ModifiedMainActivityForSocialLoginPlugin { @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode >= GoogleProvider.REQUEST_AUTHORIZE_GOOGLE_MIN && requestCode < GoogleProvider.REQUEST_AUTHORIZE_GOOGLE_MAX) { PluginHandle pluginHandle = getBridge().getPlugin("SocialLogin"); if (pluginHandle == null) { Log.i("Google Activity Result", "SocialLogin login handle is null"); return; } Plugin plugin = pluginHandle.getInstance(); if (!(plugin instanceof SocialLoginPlugin)) { Log.i("Google Activity Result", "SocialLogin plugin instance is not SocialLoginPlugin"); return; } ((SocialLoginPlugin) plugin).handleGoogleLoginIntent(requestCode, data); } } // This function will never be called, leave it empty @Override public void IHaveModifiedTheMainActivityForTheUseWithSocialLoginPlugin() {} } ``` 4. Speichern Sie die Datei 4. Verwenden Sie Google Login in Ihrer Anwendung 1. Importieren Sie zuerst `SocialLogin` ```typescript import { SocialLogin } from '@capgo/capacitor-social-login'; ``` 2. Rufen Sie initialize auf. Dies sollte nur einmal aufgerufen werden. ```typescript // onMounted ist Vue-spezifisch // webClientId ist die Client-ID, die Sie im Web-Client-Erstellungsschritt erhalten haben, nicht die Android-Client-ID. onMounted(() => { SocialLogin.initialize({ google: { webClientId: '673324426943-avl4v9ubdas7a0u7igf7in03pdj1dkmg.apps.googleusercontent.com', } }) }) ``` 3. Rufen Sie `SocialLogin.login` auf. Erstellen Sie eine Schaltfläche und führen Sie den folgenden Code beim Klicken aus. ```typescript const res = await SocialLogin.login({ provider: 'google', options: {} }) // Verarbeiten Sie die Antwort console.log(JSON.stringify(res)) ``` 5. Konfigurieren Sie den Emulator zum Testen 1. Gehen Sie in den `Device manager` und klicken Sie auf die Plus-Schaltfläche ![Device Manager in Android Studio mit hervorgehobener Plus-Schaltfläche](/social-login-assets/studio_device_man.png) 2. Erstellen Sie ein virtuelles Gerät ![Virtuelles Gerät erstellen Schaltfläche in Virtueller Gerätekonfiguration](/social-login-assets/studio_create_virt_dev.png) 3. Wählen Sie ein beliebiges Gerät mit einem `Play Store` Symbol ![Hardware-Auswahl zeigt Geräte mit Play Store Unterstützung](/social-login-assets/studio_new_dev_select_hardware.png) Wie Sie sehen können, unterstützt der `pixel 8` die `Play Store` Dienste 4. Klicken Sie auf `next` ![Next Schaltfläche im Geräteerstellungs-Assistenten](/social-login-assets/studio_new_dev_next_1.png) 5. Stellen Sie sicher, dass das OS-Image vom Typ `Google Play` ist. Es **MUSS** vom Typ `Google Play` sein ![System-Image-Auswahl zeigt Google Play Typ Images](/social-login-assets/studio_new_dev_google_play_dev_type.png) 6. Klicken Sie auf next ![Next Schaltfläche im System-Image-Auswahlbildschirm](/social-login-assets/studio_new_dev_next_1.png) 7. Bestätigen Sie Ihr Gerät. Sie können Ihren Emulator nach Belieben benennen ![Gerätekonfigurations-Überprüfungsbildschirm mit Finish-Schaltfläche](/social-login-assets/studio_new_dev_next_3.png) 8. Gehen Sie in den `Device Manager` und starten Sie Ihren Simulator ![Device Manager mit aufgelistetem virtuellem Gerät und Play-Schaltfläche](/social-login-assets/studio_dev_manager.png) 9. Nachdem der Simulator hochgefahren ist, gehen Sie in die Einstellungen ![Android Emulator zeigt Einstellungs-App](/social-login-assets/emul_and_settings_1.png) 10. Gehen Sie zu `Google Play` ![Einstellungsbildschirm mit Google Play Option](/social-login-assets/emul_and_settings_2.png) 11. Klicken Sie auf `Update` und warten Sie etwa 60 Sekunden ![Google Play Update-Bildschirm mit Update-Schaltfläche](/social-login-assets/emul_and_settings_update_play_store.png) 6. Testen Sie Ihre Anwendung Wenn Sie alles richtig gemacht haben, sollten Sie sehen, dass der Google-Login-Ablauf ordnungsgemäß funktioniert: ![Demo des Google-Login-Ablaufs auf Android zeigt Anmeldeprozess und erfolgreiche Authentifizierung](/social-login-assets/google_android_final_login_show.gif) ## Fehlerbehebung [Section titled “Fehlerbehebung”](#fehlerbehebung) Wenn Sie Probleme haben, schauen Sie sich bitte die [Github Issues](https://github.com/Cap-go/capacitor-social-login/issues) an. Die Probleme mit Google Login sind **IMMER** mit dem SHA1-Zertifikat verbunden. Wenn Sie das Entwicklungs-SHA1-Zertifikat nicht erhalten können, versuchen Sie, einen benutzerdefinierten Keystore zu verwenden. [Hier](https://github.com/Cap-go/capacitor-social-login/issues/147#issuecomment-2849742574) ist ein Kommentar, der erklärt, wie Sie einen Keystore zu Ihrem Projekt hinzufügen. # Google Login Einrichtung > Dieser Leitfaden bietet eine umfassende Übersicht zur Einrichtung von Google Login mit Capacitor und erläutert jeden Schritt, um einen nahtlosen Integrationsprozess sicherzustellen und potenzielle Herausforderungen anzusprechen, die auftreten können. ## Einführung [Section titled “Einführung”](#einführung) In diesem Leitfaden lernen Sie, wie Sie Google Login mit Capgo Social Login einrichten. Sie benötigen Folgendes, um Google Login einzurichten: * Ein Google-Konto ## Allgemeine Einrichtung [Section titled “Allgemeine Einrichtung”](#allgemeine-einrichtung) Note Dieser Schritt ist unabhängig von der Plattform erforderlich, die Sie verwenden möchten. In diesem Teil richten Sie den Login-Bildschirm ein, der von Google angezeigt wird. 1. Bitte gehen Sie zu [console.cloud.google.com](https://console.cloud.google.com/) 2. Klicken Sie auf die Projektauswahl ![Google Console Projektauswahl](/social-login-assets/google_cons_project_selector.png) 3. Wenn Sie noch kein Projekt haben, **erstellen Sie bitte ein neues Projekt**. 1. Klicken Sie auf `New project` ![Neues Projekt Schaltfläche in Google Console](/social-login-assets/google_cons_new_project_btn.png) 2. Benennen Sie Ihr Projekt und klicken Sie auf `Create` ![Projekt-Benennungsbildschirm zeigt Namensfeld und Create-Schaltfläche](/social-login-assets/google_cons_name_projec.png) 3. Stellen Sie sicher, dass Sie im richtigen Projekt sind ![Projektname in der Auswahl zeigt korrekte Projektauswahl](/social-login-assets/google_cons_right_proj.png) 4. Beginnen Sie mit der Konfiguration des `OAuth consent screen` 1. Klicken Sie auf die Suchleiste ![Google Console Suchleiste](/social-login-assets/google_cons_search.png) 2. Suchen Sie nach `OAuth consent screen` und klicken Sie darauf ![Suchergebnisse zeigen OAuth Consent Screen Option](/social-login-assets/google_cons_search_2.png) 3. Konfigurieren Sie den Consent Screen Note Ich gehe davon aus, dass Sie eine App entwickeln, die für die Öffentlichkeit zugänglich ist, daher werde ich den `external` Benutzertyp verwenden. Bitte wählen Sie den Benutzertyp aus, der am besten zu Ihnen passt UND klicken Sie auf `create` Klicken Sie auf `create` ![OAuth Consent Screen Benutzertyp-Auswahl mit External und Internal Optionen](/social-login-assets/google_cons_oauth_const_scr.png) 5. Füllen Sie die Informationen über Ihre App aus 1. Beginnen wir mit den `App Information` ![App Information Bereich zeigt App Name und User Support Email Felder](/social-login-assets/google_cons_app_inf.png) * Bitte geben Sie Ihren `App Name` ein Caution **DIES WIRD DEN BENUTZERN ANGEZEIGT** * Geben Sie die `user support email` ein Note Sie können mehr über die Support-E-Mail [hier](https://support.google.com/cloud/answer/10311615#user-support-email\&zippy=%2Cuser-support-email/) erfahren 2. Sie **KÖNNEN** das App-Logo hinzufügen. ![App Logo Upload-Bereich im OAuth Consent Screen](/social-login-assets/google_cons_app_logo.png) Note Dies ist nicht obligatorisch und ich werde diesen Schritt überspringen 3. Sie **SOLLTEN** die `App domain` konfigurieren ![App Domain Konfigurationsbereich mit Feld für autorisierte Domains](/social-login-assets/google_cons_app_doma.png) Note Ich werde das nicht tun, da dies nur eine einfache Demonstration ist, die **NICHT** veröffentlicht wird, aber ich empfehle dringend, diesen Bereich auszufüllen. 4. Sie **MÜSSEN** die E-Mail des Entwicklers angeben ![Developer Contact Information Bereich mit E-Mail-Feld](/social-login-assets/google_cons_dev_cont_inf.png) 5. Klicken Sie auf `save and continue` ![Save and Continue Schaltfläche am unteren Ende des Formulars](/social-login-assets/google_cons_cons_sav_cont.png) 6. Konfigurieren Sie die Scopes 1. Klicken Sie auf `add or remove scopes` ![Add or remove scopes Schaltfläche im Scopes-Konfigurationsbildschirm](/social-login-assets/google_cons_add_rm_sco.png) 2. Wählen Sie die folgenden Scopes aus und klicken Sie auf `update` ![Scope-Auswahl-Dialog mit ausgewählten E-Mail- und Profil-Scopes](/social-login-assets/google_cons_update_scope.png) 3. Klicken Sie auf `save and continue` ![Save and Continue Schaltfläche im Scopes-Bildschirm](/social-login-assets/google_cons_scope_save.png) 7. Fügen Sie einen Testbenutzer hinzu 1. Klicken Sie auf `add users` ![Add users Schaltfläche im Testbenutzer-Bereich](/social-login-assets/google_cons_add_test_usr.png) 2. Geben Sie Ihre Google-E-Mail ein, drücken Sie Enter und klicken Sie auf `add` ![E-Mail-Eingabefeld und Add-Schaltfläche für Testbenutzer](/social-login-assets/google_cons_add_test_usr_2.png) 3. Klicken Sie auf `save and continue` ![Save and Continue Schaltfläche im Testbenutzer-Bildschirm](/social-login-assets/google_cons_test_usr_save.png) 8. Klicken Sie auf `back to dashboard` ![Back to dashboard Schaltfläche am unteren Ende der Abschlussseite](/social-login-assets/google_cons_back_to_dahs.png) 9. Reichen Sie Ihre App zur Überprüfung ein Note Ich empfehle dringend, Ihre App zur Überprüfung einzureichen. Dies liegt außerhalb des Umfangs dieses Tutorials. Sie können mehr [hier](https://support.google.com/cloud/answer/13463073/) erfahren. Dies ist nicht erforderlich für lokale Tests, aber für die Produktion erforderlich. ## Unterschiede zwischen Online-Zugriff und Offline-Zugriff [Section titled “Unterschiede zwischen Online-Zugriff und Offline-Zugriff”](#unterschiede-zwischen-online-zugriff-und-offline-zugriff) Es gibt mehrere Möglichkeiten, Google Login mit Capacitor zu verwenden. Hier ist eine Tabelle, die die Unterschiede zwischen den beiden zusammenfasst: | | Online-Zugriff | Offline-Zugriff | | :----------------------: | :------------: | :-------------: | | Erfordert ein Backend | ❌ | ✅ | | Langlebiges Access Token | ❌ | ✅ | | Einfache Einrichtung | ✅ | ❌ | Note **Offline-Modus** bedeutet, dass Ihr Backend auf Googles APIs zugreifen kann, auch wenn der Benutzer Ihre App nicht aktiv verwendet (d.h. wenn der Benutzer aus Sicht Ihrer App “offline” ist). Langlebige Access Tokens ermöglichen diese Funktionalität, indem sie es dem Backend ermöglichen, Google APIs jederzeit im Namen des Benutzers aufzurufen. Caution **Offline-Modus ERFORDERT einen Backend-Server.** Wenn Sie den Offline-Modus verwenden, erhält das Frontend minimale Informationen (hauptsächlich nur einen Server-Auth-Code). Ohne ein Backend zum Austausch dieses Codes gegen Tokens und Benutzerinformationen bietet der Offline-Modus keine nützlichen Daten für Ihre Frontend-Anwendung. Wenn Sie immer noch nicht wissen, welchen Sie wählen sollten, beachten Sie bitte die folgenden Szenarien: 1. Sie möchten, dass sich der Benutzer anmeldet, unmittelbar danach werden Sie ihm ein benutzerdefiniertes JWT ausstellen. Ihre App wird KEINE Google APIs aufrufen Wählen Sie in diesem Fall Online-Zugriff. 2. Ihre App wird einige Google APIs vom Client aufrufen, aber nie vom Backend Wählen Sie in diesem Fall Online-Zugriff 3. Ihre App wird einige Google APIs vom Backend aufrufen, aber nur wenn der Benutzer die App aktiv verwendet Wählen Sie in diesem Fall Online-Zugriff 4. Ihre App wird regelmäßig den Kalender des Benutzers überprüfen, auch wenn er die App nicht aktiv verwendet Wählen Sie in diesem Fall Offline-Zugriff ## Ein Beispiel-Backend für Online-Zugriff [Section titled “Ein Beispiel-Backend für Online-Zugriff”](#ein-beispiel-backend-für-online-zugriff) In diesem Teil des Tutorials zeige ich, wie Sie den Benutzer auf Ihrem Backend validieren. Dieses Beispiel wird sehr einfach sein und basiert auf den folgenden Technologien: * [Typescript](https://www.typescriptlang.org/) * [Hono](https://hono.dev/) * [Javascript’s fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch) Sie können den Code für dieses Beispiel [hier](https://github.com/WcaleNieWolny/capgo-social-login-backend-demo/blob/141c01d93a85240e31a0d488a89df13c842708b1/index.ts#L135-L153) finden Wie Sie sehen können: ![VS Code zeigt Google-Authentifizierungscode, der Tokens verifiziert](/social-login-assets/vscode_auth_google.png) Die Idee ist ziemlich einfach. Sie senden eine einfache `GET` Anfrage an `https://www.googleapis.com/oauth2/v3/tokeninfo` und diese gibt Ihnen zurück, ob der Token gültig ist oder nicht, und wenn ja, gibt sie Ihnen die E-Mail des Benutzers. Sie gibt Ihnen auch einige andere Informationen über den Benutzer-Token ![Google OAuth Playground zeigt Token-Informationsantwort mit Benutzerdetails](/social-login-assets/google_auth_playground_token_info.png) Von dort aus könnten Sie dem Benutzer Ihr eigenes JWT ausstellen oder eine Art Session-Cookie ausstellen. Die Möglichkeiten sind endlos für die endgültige Auth-Implementierung. Wenn Sie Google API’s aufrufen möchten, empfehle ich dringend, sich [Google’s OAuth 2.0 Playground](https://developers.google.com/oauthplayground) anzusehen. Von dort aus können Sie leicht sehen, welche APIs Sie aufrufen können. ## Verwendung von Offline-Zugriff mit Ihrem eigenen Backend [Section titled “Verwendung von Offline-Zugriff mit Ihrem eigenen Backend”](#verwendung-von-offline-zugriff-mit-ihrem-eigenen-backend) Um Online-Zugriff zu verwenden, benötigen Sie Folgendes: * Einen HTTP-Server In diesem Beispiel werde ich die folgenden Technologien verwenden, um den Offline-Zugriff in meiner App bereitzustellen: * [Hono](https://hono.dev/) * [Hono Zod validator](https://hono.dev/docs/guides/validation#with-zod) * [Zod](https://zod.dev/) * [Hono JWT](https://hono.dev/docs/helpers/jwt#jwt-authentication-helper) * [LowDb](https://www.npmjs.com/package/lowdb) (eine einfache Datenbank) Der Code für dieses Beispiel ist [hier](https://github.com/WcaleNieWolny/capgo-social-login-backend-demo/blob/aac7a8c909f650a8c2cd7f88c97f5f3c594ce9ba/index.ts#L139-L287) zu finden Was den Client-Code betrifft, sieht er so aus: ```typescript import { Capacitor } from '@capacitor/core'; import { GoogleLoginOfflineResponse, SocialLogin } from '@capgo/capacitor-social-login'; import { usePopoutStore } from '@/popoutStore'; // <-- spezifisch für meine App const baseURL = "[redacted]"; async function fullLogin() { await SocialLogin.initialize({ google: { webClientId: '[redacted]', iOSClientId: '[redacted]', iOSServerClientId: 'The same value as webClientId', mode: 'offline' // <-- wichtig } }) const response = await SocialLogin.login({ provider: 'google', options: { forceRefreshToken: true // <-- wichtig } }) if (response.provider === 'google') { const result = response.result as GoogleLoginOfflineResponse const res = await fetch(`${baseURL}/auth/google_offline`, { headers: { "Content-Type": "application/json" }, body: JSON.stringify({ serverAuthCode: result.serverAuthCode, platform: Capacitor.getPlatform() }), method: "POST" }) if (res.status !== 200) { popoutStore.popout("Full google login failed", "check console"); return } const { jwt } = await res.json(); const userinfo = await fetch(`${baseURL}/auth/get_google_user`, { headers: { Authorization: `Bearer ${jwt}` } }) if (userinfo.status !== 200) { popoutStore.popout("Full google (userinfo) login failed", "check console"); return } popoutStore.popout("userinfo res", await userinfo.text()); } } ``` # Google Login auf iOS > Dieser Leitfaden bietet eine umfassende Anleitung zur Konfiguration von Google Login mit Capacitor auf iOS und erläutert jeden Schritt, um einen reibungslosen Integrationsprozess sicherzustellen. ## Einführung [Section titled “Einführung”](#einführung) In diesem Leitfaden lernen Sie, wie Sie Google Login mit Capgo Social Login für iOS einrichten. Ich gehe davon aus, dass Sie die [allgemeine Einrichtungsanleitung](/docs/plugins/social-login/google/general/) bereits gelesen haben. ## Verwendung von Google Login auf iOS [Section titled “Verwendung von Google Login auf iOS”](#verwendung-von-google-login-auf-ios) In diesem Teil lernen Sie, wie Sie Google Login in iOS einrichten. 1. Erstellen Sie eine iOS-Client-ID in der Google Console 1. Klicken Sie auf die Suchleiste ![Google Console Suchleiste](/social-login-assets/google_cons_search.png) 2. Suchen Sie nach `credentials` und klicken Sie auf die `APIs and Services` Option (Nummer 2 auf dem Screenshot) ![Suchergebnisse zeigen Credentials-Option mit hervorgehobenen APIs and Services](/social-login-assets/google_cons_cred_search.png) 3. Klicken Sie auf `create credentials` ![Credentials erstellen Schaltfläche in Google Console](/social-login-assets/google_cons_create_cred.png) 4. Wählen Sie `OAuth client ID` ![OAuth client ID Option im Credentials-Erstellungsmenü](/social-login-assets/google_cons_cred_oauth.png) 5. Wählen Sie den `Application type` auf `iOS` ![Anwendungstyp-Auswahl mit hervorgehobener iOS-Option](/social-login-assets/goolge_cons_cred_type_app_tye.png) 6. Finden Sie die Bundle ID 1. Öffnen Sie Xcode 2. Doppelklicken Sie auf `App` ![App Target im Xcode Projekt-Navigator](/social-login-assets/xcode_app_click.png) 3. Stellen Sie sicher, dass Sie sich bei `Targets -> App` befinden ![Targets Bereich in Xcode mit App ausgewählt](/social-login-assets/xcode_targets_app.png) 4. Finden Sie Ihre `Bundle Identifier` ![Bundle Identifier Feld in Xcode Projekteinstellungen](/social-login-assets/xcode_bundle_id.png) 5. Gehen Sie zurück zur Google Console und fügen Sie Ihre `Bundle Identifier` in `Bundle ID` ein ![Bundle ID Feld im Google Console iOS Client-Erstellungsformular](/social-login-assets/google_cons_ios_bd_id.png) 7. Optional fügen Sie Ihre `App Store ID` oder `Team ID` zur Client-ID hinzu, wenn Sie Ihre App im App Store veröffentlicht haben 8. Nachdem Sie alle Details ausgefüllt haben, klicken Sie auf `create` ![Create Schaltfläche am unteren Ende des iOS Client-Erstellungsformulars](/social-login-assets/google_cons_ios_cred_creat.png) 9. Klicken Sie auf `OK` ![OK Schaltfläche im Client-ID erstellt Bestätigungsdialog](/social-login-assets/google_cons_ios_click_ok.png) 10. Öffnen Sie den neu erstellten iOS-Client ![Neu erstellter iOS-Client in Credentials-Liste](/social-login-assets/google_cons_open_new_ios.png) 11. Kopieren Sie die folgenden Daten ![Client-ID Details zeigen Client-ID und umgekehrte Client-ID zum Kopieren](/social-login-assets/google_cons_ios_what_to_copy.png) Note Die `nr. 1` in diesem Bild wird später zur `iOSClientId` im `initialize` Aufruf. Die `nr. 2` in diesem Bild wird später zu `YOUR_DOT_REVERSED_IOS_CLIENT_ID` 2. Ändern Sie die Info.plist Ihrer App 1. Öffnen Sie Xcode und finden Sie die `Info.plist` Datei ![Info.plist Datei im Xcode Projekt-Navigator](/social-login-assets/xcode_info_file.png) 2. Rechtsklicken Sie auf diese Datei und öffnen Sie sie als Quellcode ![Rechtsklick-Menü zeigt Als Quellcode öffnen Option](/social-login-assets/xcode_open_as_src_code.png) 3. Am Ende Ihrer `Plist` Datei sehen Sie ein `` Tag ![Schließendes dict Tag in Info.plist Datei](/social-login-assets/xcode_dict_tag.png) 4. Fügen Sie das folgende Fragment direkt vor dem schließenden `` Tag ein ![Info.plist mit URL-Schemas-Code vor schließendem dict Tag eingefügt](/social-login-assets/xcode_plist_inserted.png) ```xml CFBundleURLTypes CFBundleURLSchemes YOUR_DOT_REVERSED_IOS_CLIENT_ID ``` 5. Ändern Sie die `YOUR_DOT_REVERSED_IOS_CLIENT_ID` in den im vorherigen Schritt kopierten Wert ![Info.plist mit tatsächlicher umgekehrter Client-ID in URL-Schemas eingefügt](/social-login-assets/xcode_plist_final.png) Caution Stellen Sie sicher, dass dieser Wert **MIT** `com.googleusercontent.apps` **BEGINNT** 6. Speichern Sie die Datei mit `Command + S` 3. Ändern Sie die `AppDelegate.swift` 1. Öffnen Sie das AppDelegate ![AppDelegate.swift Datei im Xcode Projekt-Navigator](/social-login-assets/xcode_app_deleg.png) 2. Fügen Sie `import GoogleSignIn` am Anfang der Datei ein ![AppDelegate.swift mit GoogleSignIn Import hinzugefügt](/social-login-assets/xcode_app_deleg_google_sign_in.png) 3. Finden Sie die `func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:])` Funktion ![Original application openURL Funktion im AppDelegate](/social-login-assets/xcode_app_deleg_app_fn.png) 4. Ändern Sie die Funktion so, dass sie folgendermaßen aussieht ```swift func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool { // Called when the app was launched with a url. Feel free to add additional processing here, // but if you want the App API to support tracking app url opens, make sure to keep this call var handled: Bool handled = GIDSignIn.sharedInstance.handle(url) if handled { return true } return ApplicationDelegateProxy.shared.application(app, open: url, options: options) } ``` ![Geänderte application openURL Funktion mit GoogleSignIn Verarbeitung](/social-login-assets/xcode_app_deleg_app_fn_mod.png) 5. Speichern Sie die Datei mit `Command + S` 4. Richten Sie Google Login in Ihrem JavaScript/TypeScript Code ein 1. Importieren Sie `SocialLogin` und `Capacitor` ```typescript import { SocialLogin } from '@capgo/capacitor-social-login'; import { Capacitor } from '@capacitor/core'; ``` 2. Rufen Sie die initialize Methode auf (dies sollte nur einmal aufgerufen werden) **Grundlegende Einrichtung (Online-Modus - empfohlen für die meisten Apps):** ```typescript // onMounted ist Vue-spezifisch onMounted(() => { SocialLogin.initialize({ google: { iOSClientId: '673324426943-redacted.apps.googleusercontent.com', mode: 'online' // Standard-Modus } }) }) ``` **Erweiterte Einrichtung mit zusätzlichen Client-IDs:** ```typescript onMounted(() => { SocialLogin.initialize({ google: { webClientId: 'YOUR_WEB_CLIENT_ID', // Optional: für Web-Plattform-Unterstützung iOSClientId: 'YOUR_IOS_CLIENT_ID', // Erforderlich: aus Schritt 1 iOSServerClientId: 'YOUR_WEB_CLIENT_ID', // Optional: gleicher Wert wie webClientId, benötigt für einige erweiterte Funktionen mode: 'online' // 'online' oder 'offline' } }) }) ``` Note **Client-ID Anforderungen:** * `iOSClientId`: **Erforderlich** - Muss mit `googleusercontent.com` enden (aus Schritt 1 oben) * `webClientId`: **Optional** - Nur benötigt, wenn Sie auch Web-Plattform unterstützen oder erweiterte Funktionen benötigen * `iOSServerClientId`: **Optional** - Sollte derselbe Wert wie `webClientId` sein, wenn bereitgestellt Für erweiterte Einrichtung mit `webClientId` und `iOSServerClientId`, siehe [Web-Einrichtungsanleitung](/docs/plugins/social-login/google/web/) zur Erstellung dieser Credentials. Caution **Über den Offline-Modus:** Bei Verwendung von `mode: 'offline'` enthält die Login-Antwort nicht direkt Benutzerdaten. Stattdessen erhalten Sie einen Server-Auth-Code, der über Ihren Backend-Server gegen Benutzerinformationen ausgetauscht werden muss. Siehe [allgemeine Einrichtungsanleitung](/docs/plugins/social-login/google/general/#using-offline-access-with-your-own-backend) für Implementierungsdetails. 3. Implementieren Sie die Login-Funktion. Erstellen Sie eine Schaltfläche und führen Sie den folgenden Code beim Klicken aus **Für Online-Modus:** ```typescript const res = await SocialLogin.login({ provider: 'google', options: {} }) // Antwort verarbeiten - enthält Benutzerdaten console.log(JSON.stringify(res)) ``` **Für Offline-Modus:** ```typescript const res = await SocialLogin.login({ provider: 'google', options: { forceRefreshToken: true // Empfohlen für Offline-Modus } }) // res enthält serverAuthCode, keine Benutzerdaten // Senden Sie serverAuthCode an Ihr Backend, um Benutzerinformationen zu erhalten console.log('Server Auth Code:', res.result.serverAuthCode) ``` 5. Testen Sie Ihre Anwendung 1. Bauen Sie Ihre App und führen Sie `cap sync` aus 2. Wenn Sie alles richtig gemacht haben, sollten Sie sehen, dass der Google-Login-Ablauf ordnungsgemäß funktioniert ![Demo des Google-Login-Ablaufs auf iOS zeigt Anmeldeprozess und erfolgreiche Authentifizierung](/social-login-assets/google_final_ios_v2.gif) Note Die Sprache in der Google-Eingabeaufforderung hängt von den Spracheinstellungen Ihres Geräts ab. ## Bekannte Probleme [Section titled “Bekannte Probleme”](#bekannte-probleme) ### Privacy Screen Plugin Inkompatibilität [Section titled “Privacy Screen Plugin Inkompatibilität”](#privacy-screen-plugin-inkompatibilität) Das Google Login Plugin ist inkompatibel mit [@capacitor/privacy-screen](https://github.com/ionic-team/capacitor-privacy-screen). Wenn beide Plugins zusammen verwendet werden, wird die Google-Login-Webansicht durch den Datenschutzbildschirm unterbrochen. **Workaround:** Rufen Sie `await PrivacyScreen.disable();` auf, bevor Sie die Login-Funktion aufrufen: ```typescript import { PrivacyScreen } from '@capacitor/privacy-screen'; import { SocialLogin } from '@capgo/capacitor-social-login'; await PrivacyScreen.disable(); await SocialLogin.login({ provider: 'google', options: {} }); ``` # Google Login im Web > Dieser Leitfaden bietet eine umfassende Anleitung zur Einrichtung von Google Login für Webanwendungen mit Capacitor und dem @capgo/capacitor-social-login Plugin und gewährleistet einen nahtlosen Integrationsprozess durch Abdeckung aller notwendigen Schritte und Konfigurationen. ## Einführung [Section titled “Einführung”](#einführung) In diesem Leitfaden lernen Sie, wie Sie Google Login mit Capgo Social Login für Webanwendungen einrichten. Ich gehe davon aus, dass Sie die [allgemeine Einrichtungsanleitung](/docs/plugins/social-login/google/general/) bereits gelesen haben. ## Verwendung von Google Login im Web [Section titled “Verwendung von Google Login im Web”](#verwendung-von-google-login-im-web) Die Verwendung des Google-Logins im Web ist ziemlich einfach. Um es zu verwenden, müssen Sie Folgendes tun: 1. Erstellen Sie einen Web-Client in der Google Console Note Wenn Sie Google Login für Android bereits konfiguriert haben, können Sie diesen Schritt überspringen, da Sie bereits einen Web-Client erstellt haben. Sie können direkt zu Schritt 2 übergehen. 1. Klicken Sie auf die Suchleiste ![Google Console Suchleiste](/social-login-assets/google_cons_search.png) 2. Suchen Sie nach `credentials` und klicken Sie auf die `APIs and Services` Option (Nummer 2 auf dem Screenshot) ![Suchergebnisse zeigen Credentials-Option mit hervorgehobenen APIs and Services](/social-login-assets/google_cons_cred_search.png) 3. Klicken Sie auf `create credentials` ![Credentials erstellen Schaltfläche in Google Console](/social-login-assets/google_cons_create_cred.png) 4. Wählen Sie `OAuth client ID` ![OAuth client ID Option im Credentials-Erstellungsmenü](/social-login-assets/google_cons_cred_oauth.png) 5. Wählen Sie den `Application type` als `Web application` ![Anwendungstyp-Auswahl mit hervorgehobener Web-Option](/social-login-assets/google_cons_app_type_web.png) 6. Benennen Sie Ihren Client und klicken Sie auf `Create` ![Web-Client-Erstellungsformular mit hervorgehobenem Namensfeld](/social-login-assets/google_cons_web_app_create.png) 7. Kopieren Sie die Client-ID, Sie werden diese als `webClientId` in Ihrer Anwendung verwenden ![Client-ID Details zeigen Web-Client-ID zum Kopieren](/social-login-assets/google_cons_copy_web_client_id.png) 2. Konfigurieren Sie den Web-Client in der Google Console 1. Bitte öffnen Sie die [Credentials-Seite](https://console.cloud.google.com/apis/credentials) und klicken Sie auf Ihren Web-Client ![Credentials-Liste zeigt Web-Client zum Klicken](/social-login-assets/google_cons_open_web_client_id.png) 2. Fügen Sie nun die `Authorized JavaScript origins` hinzu. Dies sollte alle Adressen enthalten, die Sie möglicherweise für Ihre App verwenden. In meinem Fall werde ich **NUR** localhost verwenden, aber da ich einen benutzerdefinierten Port verwende, muss ich sowohl `http://localhost` als auch `http://localhost:5173` hinzufügen 1. Bitte klicken Sie auf `add URI` ![Authorized JavaScript origins Bereich mit ADD URI Schaltfläche](/social-login-assets/google_cons_authorized_js_add_btn.png) 2. Bitte geben Sie Ihre URL ein ![ADD URI Dialog mit eingegebener localhost URL](/social-login-assets/google_cons_authorized_js_typed_url.png) 3. Bitte wiederholen Sie dies, bis Sie alle URLs hinzugefügt haben 4. Wenn Sie fertig sind, sollte Ihr Bildschirm ungefähr so aussehen ![Authorized JavaScript origins mit mehreren hinzugefügten localhost URLs](/social-login-assets/google_cons_authorized_js_final.png) 3. Fügen Sie nun einige `Authorized redirect URIs` hinzu. Dies hängt davon ab, auf welcher Seite Sie das CapacitorSocialLogin Plugin verwenden möchten. In meinem Fall werde ich es auf `http://localhost:5173/auth` verwenden 1. Bitte klicken Sie auf `ADD URI` ![Authorized redirect URIs Bereich mit ADD URI Schaltfläche](/social-login-assets/google_cons_web_add_redirect_url_1.png) 2. Geben Sie Ihre URL ein und klicken Sie erneut auf `ADD URL` ![ADD URI Dialog mit eingegebener Redirect-URL](/social-login-assets/google_cons_web_add_redirect_url_2.png) 4. Klicken Sie auf `save` ![Save Schaltfläche am unteren Ende der Web-Client-Konfiguration](/social-login-assets/google_cons_web_app_save.png) 3. Jetzt sollten Sie bereit sein, `login` von JavaScript wie folgt aufzurufen: 1. Importieren Sie zuerst `SocialLogin` ```typescript import { SocialLogin } from '@capgo/capacitor-social-login'; ``` 2. Rufen Sie dann initialize auf. Dies sollte NUR einmal aufgerufen werden. ```typescript // onMounted ist Vue-spezifisch // webClientId ist die Client-ID, die Sie im Web-Client-Erstellungsschritt erhalten haben, nicht die Android-Client-ID. onMounted(() => { SocialLogin.initialize({ google: { webClientId: '673324426943-avl4v9ubdas7a0u7igf7in03pdj1dkmg.apps.googleusercontent.com', } }) }) ``` Web Redirect Verarbeitung Wenn Sie Google Login im Web verwenden, **MÜSSEN** Sie beim Redirect eine beliebige Funktion des Plugins aufrufen, um das Plugin zu initialisieren, damit es den Redirect verarbeiten und das Popup-Fenster schließen kann. Sie können entweder `isLoggedIn()` ODER `initialize()` aufrufen - beide lösen die Redirect-Verarbeitung aus: ```typescript // Option 1: Rufen Sie isLoggedIn auf, wenn die Redirect-Seite lädt SocialLogin.isLoggedIn({ provider: 'google' }).catch(() => { // Ignorieren Sie das Ergebnis, dies dient nur zur Initialisierung des Plugins }); // Option 2: Rufen Sie initialize auf, wenn die Redirect-Seite lädt SocialLogin.initialize({ google: { webClientId: 'YOUR_WEB_CLIENT_ID.apps.googleusercontent.com', } }).catch(() => { // Ignorieren Sie eventuelle Fehler, dies dient nur zur Verarbeitung des Redirects }); ``` 3. Erstellen Sie eine Login-Schaltfläche, die `SocialLogin.login` beim Klicken aufruft ```typescript const res = await SocialLogin.login({ provider: 'google', options: {} }) // Verarbeiten Sie die Antwort console.log(JSON.stringify(res)); ``` # Migrationsleitfaden von @capacitor-community/apple-sign-in zu @capgo/capacitor-social-login > Dieser umfassende Leitfaden bietet detaillierte Anweisungen für den Übergang vom @capacitor-community/apple-sign-in Plugin zum @capgo/capacitor-social-login Plugin und gewährleistet einen reibungslosen Migrationsprozess mit verbesserten Funktionen. ## Übersicht [Section titled “Übersicht”](#übersicht) Dieser Leitfaden beschreibt den Übergang vom veralteten `@capacitor-community/apple-sign-in` Plugin zum modernen `@capgo/capacitor-social-login` Paket. Das neue Plugin bietet eine einheitliche Schnittstelle für mehrere soziale Authentifizierungsanbieter mit verbesserter TypeScript-Unterstützung und aktiver Wartung. ## Installation [Section titled “Installation”](#installation) 1. Entfernen Sie das alte Paket: ```bash npm uninstall @capacitor-community/apple-sign-in ``` 2. Installieren Sie das neue Paket: ```bash npm install @capgo/capacitor-social-login npx cap sync ``` ## Code-Änderungen [Section titled “Code-Änderungen”](#code-änderungen) ### Import-Änderungen [Section titled “Import-Änderungen”](#import-änderungen) ```diff import { SignInWithApple } from '@capacitor-community/apple-sign-in'; import { SocialLogin } from '@capgo/capacitor-social-login'; ``` ### Initialisierung [Section titled “Initialisierung”](#initialisierung) **Wichtige Änderung**: Das neue Plugin erfordert einen Initialisierungsschritt, der zuvor nicht erforderlich war. ```diff // Keine Initialisierung im alten Paket erforderlich // Für iOS: Basiskonfiguration await SocialLogin.initialize({ apple: {} // Basis-iOS-Konfiguration }); // Für Android: Zusätzliche Konfiguration erforderlich await SocialLogin.initialize({ apple: { clientId: 'YOUR_SERVICE_ID', // Service-ID aus dem Apple Developer Portal redirectUrl: 'https://your-backend.com/callback' // Ihre Backend-Callback-URL } }); ``` **Wichtiger Hinweis**: Für iOS geben Sie eine Basiskonfiguration an, während Android zusätzliche Details einschließlich einer Service-ID und Backend-Callback-URL für webbasierte OAuth-Authentifizierung benötigt. ### Anmelden [Section titled “Anmelden”](#anmelden) Der Login-Prozess vereinfacht sich von mehreren Parametern zu einer saubereren API: ```diff const result = await SignInWithApple.authorize({ clientId: 'com.your.app', redirectURI: 'https://your-app.com/callback', scopes: 'email name', state: '12345', nonce: 'nonce' }); const result = await SocialLogin.login({ provider: 'apple', options: { // Optional: Scopes hinzufügen, falls erforderlich scopes: ['email', 'name'], nonce: 'nonce' } }); ``` Das neue Plugin verwendet `login()` mit `provider: 'apple'` und optionalen Scopes anstelle der Übergabe einzelner Konfigurationswerte wie `clientId` und `redirectURI`. ### Änderungen des Antworttyps [Section titled “Änderungen des Antworttyps”](#änderungen-des-antworttyps) Die Ergebnisse enthalten jetzt ein `accessToken`-Objekt mit Ablaufdetails und einem strukturierten `profile`-Abschnitt, der das flachere Antwortformat des ursprünglichen Pakets ersetzt: ```typescript // Alter Antworttyp interface AppleSignInResponse { response: { user: string; email: string | null; givenName: string | null; familyName: string | null; identityToken: string | null; authorizationCode: string | null; }; } // Neuer Antworttyp interface SocialLoginResponse { provider: 'apple'; result: { accessToken: { token: string; expiresIn?: number; refreshToken?: string; } | null; idToken: string | null; profile: { user: string; email: string | null; givenName: string | null; familyName: string | null; }; }; } ``` ### Neue Funktionen [Section titled “Neue Funktionen”](#neue-funktionen) Das aktualisierte Plugin führt Funktionalität ein, die im Vorgänger nicht verfügbar war: **Login-Status überprüfen** ```diff // Im alten Paket nicht verfügbar const status = await SocialLogin.isLoggedIn({ provider: 'apple' }); ``` **Logout-Funktionalität** ```diff // Im alten Paket nicht verfügbar await SocialLogin.logout({ provider: 'apple' }); ``` Diese Methoden bieten `isLoggedIn()` zur Überprüfung des Authentifizierungsstatus und `logout()`-Funktionalität. ## Plattformspezifische Änderungen [Section titled “Plattformspezifische Änderungen”](#plattformspezifische-änderungen) ### iOS-Einrichtung [Section titled “iOS-Einrichtung”](#ios-einrichtung) **iOS** behält vertraute Einrichtungsverfahren über Xcode-Funktionen bei: 1. Die iOS-Einrichtung bleibt weitgehend gleich. Sie müssen weiterhin: * “Sign In with Apple”-Funktion in Xcode aktivieren * Ihre App im Apple Developer Portal konfigurieren * Keine zusätzlichen Code-Änderungen für iOS erforderlich ### Android-Einrichtung [Section titled “Android-Einrichtung”](#android-einrichtung) **Android** erhält jetzt native Unterstützung über webbasierte OAuth-Authentifizierung: Das neue Plugin bietet Android-Unterstützung von Haus aus, erfordert jedoch zusätzliche Einrichtung: 1. Erstellen Sie eine Services-ID im Apple Developer Portal 2. Konfigurieren Sie einen Web-Authentifizierungsendpunkt 3. Richten Sie Ihre Android-App ein, um den OAuth-Ablauf zu verarbeiten 4. Backend-Service-Konfiguration ist erforderlich Detaillierte Anweisungen zur Android-Einrichtung finden Sie im [Android Setup Guide](/docs/plugins/social-login/apple/android/). ## Hauptvorteile [Section titled “Hauptvorteile”](#hauptvorteile) Das modernisierte Paket bietet: 1. **Einheitliche APIs** über mehrere soziale Anbieter hinweg (Google, Facebook, Apple) 2. **Verbesserte TypeScript-Typisierung** mit besseren Typdefinitionen 3. **Aktive Community-Wartung** im Vergleich zur veralteten Version 4. **Integrierte Android-Unterstützung** durch webbasierte Authentifizierung 5. **Persistente Login-Zustandsverwaltung** 6. **Bessere Fehlerbehandlung** mit konsistenten Fehlertypen ## Breaking Changes [Section titled “Breaking Changes”](#breaking-changes) 1. **Explizite Initialisierung ist jetzt erforderlich** - keine Standardkonfiguration 2. **Struktur des Antwortobjekts hat sich geändert** - verschachteltes Ergebnisformat 3. **Android-Implementierung erfordert einen Backend-Service** für OAuth 4. **Token-Refresh-Behandlung ist anders** - verbesserte Token-Verwaltung 5. **Fehlerbehandlung und Fehlertypen haben sich geändert** - detailliertere Fehler Für detailliertere Einrichtungsanweisungen konsultieren Sie bitte die [offizielle Dokumentation](/docs/plugins/social-login/apple/general/). # Migrationsleitfaden von @capacitor-community/facebook-login zu @capgo/capacitor-social-login > Dieser detaillierte Leitfaden bietet schrittweise Anweisungen für den Übergang vom @capacitor-community/facebook-login Plugin zum @capgo/capacitor-social-login Plugin und gewährleistet einen reibungslosen Migrationsprozess mit erweiterten Funktionen. ## Übersicht [Section titled “Übersicht”](#übersicht) Dieser Leitfaden bietet umfassende Anweisungen für die Migration von `@capacitor-community/facebook-login` zu `@capgo/capacitor-social-login`. Das neue Plugin modernisiert die Facebook-Authentifizierung mit einer einheitlichen API, die mehrere soziale Anbieter unterstützt, verbesserte TypeScript-Unterstützung und erweiterte Funktionen bietet. ## Installation [Section titled “Installation”](#installation) 1. Entfernen Sie das alte Paket: ```bash npm uninstall @capacitor-community/facebook-login ``` 2. Installieren Sie das neue Paket: ```bash npm install @capgo/capacitor-social-login npx cap sync ``` ## Code-Änderungen [Section titled “Code-Änderungen”](#code-änderungen) ### Import-Änderungen [Section titled “Import-Änderungen”](#import-änderungen) ```diff import { FacebookLogin } from '@capacitor-community/facebook-login'; import { SocialLogin } from '@capgo/capacitor-social-login'; ``` ### Initialisierung [Section titled “Initialisierung”](#initialisierung) **Wichtige Änderung**: Das neue Paket erfordert eine explizite Einrichtung in Ihrem Code: ```diff // Altes Paket erforderte keine explizite Initialisierung im Code // Konfiguration erfolgte nur auf nativen Plattformen // Neues Paket erfordert explizite Initialisierung await SocialLogin.initialize({ facebook: { appId: 'YOUR_FACEBOOK_APP_ID', // Erforderlich für Web und Android clientToken: 'YOUR_CLIENT_TOKEN' // Erforderlich für Android } }); ``` ### Login [Section titled “Login”](#login) Die Login-Methode akzeptiert jetzt einen Provider-Parameter: ```diff const FACEBOOK_PERMISSIONS = ['email', 'public_profile']; const result = await FacebookLogin.login({ permissions: FACEBOOK_PERMISSIONS }); const result = await SocialLogin.login({ provider: 'facebook', options: { permissions: ['email', 'public_profile'], limitedLogin: false, nonce: 'optional_nonce' } }); ``` ### Änderungen des Antworttyps [Section titled “Änderungen des Antworttyps”](#änderungen-des-antworttyps) Die Antwortstruktur wurde mit einem umfassenderen Profilobjekt modernisiert: ```typescript // Alter Antworttyp interface FacebookLoginResponse { accessToken: { applicationId: string; userId: string; token: string; expires: string; }; recentlyGrantedPermissions: string[]; recentlyDeniedPermissions: string[]; } // Neuer Antworttyp interface FacebookLoginResponse { provider: 'facebook'; result: { accessToken: { token: string; applicationId?: string; expires?: string; userId?: string; permissions?: string[]; declinedPermissions?: string[]; } | null; idToken: string | null; profile: { userID: string; email: string | null; friendIDs: string[]; birthday: string | null; ageRange: { min?: number; max?: number } | null; gender: string | null; location: { id: string; name: string } | null; hometown: { id: string; name: string } | null; profileURL: string | null; name: string | null; imageURL: string | null; }; }; } ``` **Hauptunterschiede**: * Die Antwort enthält jetzt ein `provider`-Feld zur Identifizierung des Authentifizierungsanbieters * Detaillierteres `profile`-Objekt mit zusätzlichen Benutzerinformationen * Konsistente Struktur über alle sozialen Login-Anbieter hinweg ### Login-Status überprüfen [Section titled “Login-Status überprüfen”](#login-status-überprüfen) ```diff const result = await FacebookLogin.getCurrentAccessToken(); const isLoggedIn = result && result.accessToken; const status = await SocialLogin.isLoggedIn({ provider: 'facebook' }); const isLoggedIn = status.isLoggedIn; ``` ### Logout [Section titled “Logout”](#logout) ```diff await FacebookLogin.logout(); await SocialLogin.logout({ provider: 'facebook' }); ``` ## Plattformspezifische Änderungen [Section titled “Plattformspezifische Änderungen”](#plattformspezifische-änderungen) ### Android-Einrichtung [Section titled “Android-Einrichtung”](#android-einrichtung) Die Konfiguration erfolgt jetzt über die Initialize-Methode: ```diff // AndroidManifest.xml-Änderungen bleiben gleich // strings.xml wird irrelevant // Zusätzlich in Ihrem Code initialisieren: await SocialLogin.initialize({ facebook: { appId: 'your-app-id', clientToken: 'your-client-token' // Neue Anforderung } }); ``` **Wichtig**: Client-Token ist jetzt für die Android-Authentifizierung erforderlich. ### iOS-Einrichtung [Section titled “iOS-Einrichtung”](#ios-einrichtung) 1. Die iOS-Einrichtung in `AppDelegate.swift` bleibt gleich: ```swift import FBSDKCoreKit // In application:didFinishLaunchingWithOptions: FBSDKCoreKit.ApplicationDelegate.shared.application( application, didFinishLaunchingWithOptions: launchOptions ) // In application:openURL:options: ApplicationDelegate.shared.application( app, open: url, sourceApplication: options[UIApplication.OpenURLOptionsKey.sourceApplication] as? String, annotation: options[UIApplication.OpenURLOptionsKey.annotation] ) ``` 2. Die `Info.plist`-Konfiguration bleibt gleich: ```xml CFBundleURLTypes CFBundleURLSchemes fb[APP_ID] FacebookAppID [APP_ID] FacebookClientToken [CLIENT_TOKEN] FacebookDisplayName [APP_NAME] LSApplicationQueriesSchemes fbapi fbauth fb-messenger-share-api fbauth2 fbshareextension ``` ## Breaking Changes [Section titled “Breaking Changes”](#breaking-changes) Zusammenfassung der Breaking Changes bei der Migration: 1. **Explizite Initialisierung ist jetzt erforderlich** - Muss `initialize()` vor der Verwendung aufrufen 2. **Struktur des Antwortobjekts hat sich erheblich geändert** - Neues verschachteltes Ergebnisformat mit erweiterten Profildaten 3. **Client-Token ist jetzt für Android erforderlich** - Zusätzliche Konfiguration erforderlich 4. **Unterschiedliche Methodennamen und Parameterstrukturen** - Provider-basierter Ansatz 5. **Fehlerbehandlung und Fehlertypen haben sich geändert** - Detailliertere Fehlerinformationen ## Hauptvorteile [Section titled “Hauptvorteile”](#hauptvorteile) Das neue Plugin bietet: * **Einheitliche API** über mehrere soziale Anbieter hinweg (Google, Apple, Facebook) * **Verbesserte TypeScript-Unterstützung** mit besseren Typdefinitionen * **Erweiterte Profildaten** mit mehr Benutzerinformationen * **Aktive Wartung** und Community-Support * **Konsistente Fehlerbehandlung** über alle Anbieter hinweg * **Bessere Token-Verwaltung** mit ordnungsgemäßer Ablaufbehandlung Für detailliertere Einrichtungsanweisungen konsultieren Sie bitte die [offizielle Dokumentation](/docs/plugins/social-login/facebook/). # Migrationsleitfaden von @codetrix-studio/capacitor-google-auth zu @capgo/capacitor-social-login > Dieser Leitfaden beschreibt den Übergang vom älteren Google Auth Plugin zum neueren Capgo Social Login Paket, das mehrere soziale Authentifizierungsanbieter vereinheitlicht. ## Übersicht [Section titled “Übersicht”](#übersicht) Dieser Leitfaden bietet umfassende Schritte für die Migration von `@codetrix-studio/capacitor-google-auth` zu `@capgo/capacitor-social-login` und gewährleistet einen reibungslosen Übergang und eine verbesserte Authentifizierungserfahrung. Das neue Plugin vereinheitlicht mehrere soziale Authentifizierungsanbieter unter einer einzigen, konsistenten API. ## Installation [Section titled “Installation”](#installation) 1. Entfernen Sie das alte Paket: ```bash npm uninstall @codetrix-studio/capacitor-google-auth ``` 2. Installieren Sie das neue Paket: ```bash npm install @capgo/capacitor-social-login npx cap sync ``` ## Wichtige Änderungen in der Google Auth-Einrichtung [Section titled “Wichtige Änderungen in der Google Auth-Einrichtung”](#wichtige-änderungen-in-der-google-auth-einrichtung) ### Web Client ID-Anforderung [Section titled “Web Client ID-Anforderung”](#web-client-id-anforderung) **Kritische Änderung**: Das aktualisierte Plugin erfordert die Verwendung einer Web Client ID auf allen Plattformen. Sie müssen: 1. Eine Web Client ID in der Google Cloud Console erstellen, falls Sie noch keine haben ([Wie Sie die Anmeldedaten erhalten](/docs/plugins/social-login/google/general/)) 2. Diese Web Client ID im `webClientId`-Feld für alle Plattformen verwenden 3. Für Android müssen Sie immer noch eine Android Client ID mit Ihrem SHA1 erstellen, aber dies dient nur zu Verifizierungszwecken - das Token wird nicht verwendet ([Android-Einrichtungsleitfaden](/docs/plugins/social-login/google/android/)) ## Code-Änderungen [Section titled “Code-Änderungen”](#code-änderungen) ### Import-Änderungen [Section titled “Import-Änderungen”](#import-änderungen) ```diff import { GoogleAuth } from '@codetrix-studio/capacitor-google-auth'; import { SocialLogin } from '@capgo/capacitor-social-login'; ``` ### Initialisierung [Section titled “Initialisierung”](#initialisierung) Die Einrichtung wandelt sich von einem einfachen `GoogleAuth.initialize()`-Aufruf zu einem strukturierteren `SocialLogin.initialize()` mit verschachtelter Google-Konfiguration: ```diff GoogleAuth.initialize({ clientId: 'CLIENT_ID.apps.googleusercontent.com', scopes: ['profile', 'email'], grantOfflineAccess: true, }); await SocialLogin.initialize({ google: { webClientId: 'WEB_CLIENT_ID.apps.googleusercontent.com', // Web Client ID für alle Plattformen verwenden iOSClientId: 'IOS_CLIENT_ID', // für iOS mode: 'offline' // ersetzt grantOfflineAccess } }); ``` ### Anmelden [Section titled “Anmelden”](#anmelden) Die Login-Methode ändert sich von `GoogleAuth.signIn()` zu `SocialLogin.login()` mit expliziter Provider-Angabe: ```diff const user = await GoogleAuth.signIn(); const res = await SocialLogin.login({ provider: 'google', options: { scopes: ['email', 'profile'], forceRefreshToken: true // falls Sie Refresh-Token benötigen } }); ``` ## Plattformspezifische Änderungen [Section titled “Plattformspezifische Änderungen”](#plattformspezifische-änderungen) ### Android [Section titled “Android”](#android) 1. Aktualisieren Sie Ihre `MainActivity.java` ([Vollständiger Android-Einrichtungsleitfaden](/docs/plugins/social-login/google/android/)): ```diff import ee.forgr.capacitor.social.login.GoogleProvider; import ee.forgr.capacitor.social.login.SocialLoginPlugin; import ee.forgr.capacitor.social.login.ModifiedMainActivityForSocialLoginPlugin; import com.getcapacitor.PluginHandle; import com.getcapacitor.Plugin; import android.content.Intent; import android.util.Log; public class MainActivity extends BridgeActivity { public class MainActivity extends BridgeActivity implements ModifiedMainActivityForSocialLoginPlugin { @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode >= GoogleProvider.REQUEST_AUTHORIZE_GOOGLE_MIN && requestCode < GoogleProvider.REQUEST_AUTHORIZE_GOOGLE_MAX) { PluginHandle pluginHandle = getBridge().getPlugin("SocialLogin"); if (pluginHandle == null) { Log.i("Google Activity Result", "SocialLogin login handle is null"); return; } Plugin plugin = pluginHandle.getInstance(); if (!(plugin instanceof SocialLoginPlugin)) { Log.i("Google Activity Result", "SocialLogin plugin instance is not SocialLoginPlugin"); return; } ((SocialLoginPlugin) plugin).handleGoogleLoginIntent(requestCode, data); } } public void IHaveModifiedTheMainActivityForTheUseWithSocialLoginPlugin() {} } ``` ### iOS [Section titled “iOS”](#ios) 1. Keine größeren Änderungen in AppDelegate.swift erforderlich ([iOS-Einrichtungsleitfaden](/docs/plugins/social-login/google/ios/)) 2. Aktualisieren Sie Ihre Konfiguration in `capacitor.config.json`, wir verwenden sie nicht im neuen Plugin: ```diff { "plugins": { "GoogleAuth": { "scopes": ["profile", "email"], "serverClientId": "xxxxxx-xxxxxxxxxxxxxxxxxx.apps.googleusercontent.com", "forceCodeForRefreshToken": true } } ``` ### Web [Section titled “Web”](#web) 1. Entfernen Sie die Google Sign-In Meta-Tags aus Ihrer `index.html`, falls Sie sie verwendet haben: ```diff ``` ## Änderungen des Antworttyps [Section titled “Änderungen des Antworttyps”](#änderungen-des-antworttyps) Die Authentifizierungsantwort liefert jetzt ein strukturiertes Objekt mit Provider-Informationen, Access-Tokens, ID-Tokens und Benutzerprofildaten: ```typescript interface GoogleLoginResponse { provider: 'google'; result: { accessToken: { token: string; expires: string; // ... andere Token-Felder } | null; idToken: string | null; profile: { email: string | null; familyName: string | null; givenName: string | null; id: string | null; name: string | null; imageUrl: string | null; }; }; } ``` Die Antwortstruktur umfasst: * **provider**: Identifiziert den Authentifizierungsanbieter (`'google'`) * **result.accessToken**: Access-Token-Details mit Ablauf * **result.idToken**: ID-Token für Authentifizierung * **result.profile**: Benutzerprofilinformationen einschließlich E-Mail, Name und Bild-URL ## Zusätzliche Funktionen [Section titled “Zusätzliche Funktionen”](#zusätzliche-funktionen) Das neue Paket unterstützt mehrere soziale Authentifizierungsanbieter über Google hinaus: * [Apple Sign-In](/docs/plugins/social-login/apple/general/) * [Facebook Login](/docs/plugins/social-login/facebook/) Dieser einheitliche Ansatz bietet: * Konsistente API über alle Anbieter hinweg * Verbesserte TypeScript-Unterstützung * Bessere Fehlerbehandlung * Aktive Wartung und Community-Support Konsultieren Sie die [Hauptdokumentation](/docs/plugins/social-login/google/general/) für detaillierte Einrichtungsanweisungen für diese zusätzlichen Anbieter. # V7 Migrationsleitfaden > Leitfaden für die Migration von früheren Versionen zu V7 von @capgo/capacitor-social-login ## Einführung [Section titled “Einführung”](#einführung) Caution Dieser Leitfaden ist für die `V1`/`V7`-Version des Plugins für Personen, die dieses Plugin noch mit der `0.x.x`-Version verwenden. Dieser Leitfaden behandelt Folgendes: * Migration zur V1-Version von der `main`-Version * Migration zur V1-Version von der `development`-Version ## Wichtige Änderungen in V1 [Section titled “Wichtige Änderungen in V1”](#wichtige-änderungen-in-v1) V1 ist lediglich ein Port der Development-Version in Main. Es enthält jedoch viele wichtige Änderungen, die in der `main` V0-Version nicht verfügbar sind. Diese Änderungen umfassen: * Zugriffsberechtigungen für Google-Login * Offline-Modus für Google-Login * Vereinheitlichung der verschiedenen Implementierungen * Umfangreiche Tests wurden durchgeführt, um sicherzustellen, dass alle Implementierungen des Google-Providers zwischen Plattformen gleich funktionieren ## Migration von der V0 Main-Version [Section titled “Migration von der V0 Main-Version”](#migration-von-der-v0-main-version) * Änderungen in der `MainActivity.java` für Android * Bitte folgen Sie dem [Google Setup Guide](/docs/plugins/social-login/google/android/). Suchen Sie speziell nach `MainActivity.java` * Bitte fügen Sie Redirect-URLs in der Google Console hinzu. Ohne Hinzufügen von Redirect-URLs funktioniert Google-Login nicht. * Folgen Sie erneut dem [Google Setup Guide](/docs/plugins/social-login/google/android/). Suchen Sie speziell nach `Authorized redirect URIs` * Bitte stellen Sie sicher, dass Sie nicht `grantOfflineAccess` in der Konfiguration verwenden. Diese Funktion wird in V1 nicht unterstützt. * Bitte stellen Sie sicher, dass die Authentifizierung auf allen Plattformen funktioniert. ## Migration von der V0 Development-Version [Section titled “Migration von der V0 Development-Version”](#migration-von-der-v0-development-version) * Änderungen in der `MainActivity.java` für Android * Bitte folgen Sie dem [Google Setup Guide](/docs/plugins/social-login/google/android/). Suchen Sie speziell nach `MainActivity.java`. In V1 **MÜSSEN** Sie `ModifiedMainActivityForSocialLoginPlugin` in Ihrer Haupt-Activity implementieren. Diese Änderung ist entscheidend für das Funktionieren des Plugins * Bitte fügen Sie Redirect-URLs in der Google Console hinzu. Ohne Hinzufügen von Redirect-URLs funktioniert Google-Login nicht. * Folgen Sie erneut dem [Google Setup Guide](/docs/plugins/social-login/google/android/). Suchen Sie speziell nach `Authorized redirect URIs` * Bitte stellen Sie sicher, dass Typen und Variablennamen korrekt sind. Beachten Sie, dass Typen und Variablen zwischen Development und V1 möglicherweise nicht übereinstimmen. * Bitte stellen Sie sicher, dass die Authentifizierung auf allen Plattformen funktioniert. # Supabase Apple Login auf Android > Erfahren Sie, wie Sie Apple Sign-In mit Supabase-Authentifizierung auf Android über das Capacitor Social Login-Plugin einrichten. ## Voraussetzungen [Section titled “Voraussetzungen”](#voraussetzungen) Diese Anleitung hilft Ihnen bei der Integration von Apple Sign-In mit Supabase-Authentifizierung auf Android. Es wird vorausgesetzt, dass Sie bereits Folgendes abgeschlossen haben: * die [Supabase Apple Login - Allgemeine Einrichtung](./general/). Wichtig Apple Sign-In auf Android erfordert einen Backend-Server, da Apple keine native Android-Unterstützung bietet. Wir werden eine Supabase Edge Function als Backend verwenden. ## Schritt 1: Backend Edge Function bereitstellen [Section titled “Schritt 1: Backend Edge Function bereitstellen”](#schritt-1-backend-edge-function-bereitstellen) Zuerst müssen wir die Supabase Edge Function bereitstellen, die den Apple OAuth-Callback verarbeitet. 1. **Navigieren Sie zu Ihrem Supabase-Projektverzeichnis** ```bash cd ihr-projekt/supabase ``` 2. **Erstellen Sie die Edge Function** (falls sie nicht existiert) ```bash supabase functions new apple-signin-callback ``` 3. **Kopieren Sie den Edge Function-Code** Die vollständige Edge Function-Implementierung ist in der [Beispiel-App](https://github.com/Cap-go/capacitor-social-login/tree/main/example-app/supabase/functions/apple-signin-callback) verfügbar. Kopieren Sie die folgenden Dateien in Ihr Projekt: * `supabase/functions/apple-signin-callback/index.ts` - Haupt-Edge-Function-Code * `supabase/functions/apple-signin-callback/deno.json` - Import-Map für Abhängigkeiten (enthält `jose`-Bibliothek für JWT-Signierung) 4. **JWT-Verifizierung konfigurieren** Der Apple OAuth-Callback-Endpunkt muss öffentlich sein (keine Authentifizierung erforderlich), da Apple dorthin weiterleitet. Aktualisieren Sie Ihre `supabase/config.toml`-Datei: ```toml [functions.apple-signin-callback] enabled = true verify_jwt = false # Wichtig: Auf false setzen für öffentlichen OAuth-Callback import_map = "./functions/apple-signin-callback/deno.json" entrypoint = "./functions/apple-signin-callback/index.ts" ``` Wichtig Das Setzen von `verify_jwt = false` macht diesen Endpunkt öffentlich. Dies ist erforderlich, da die OAuth-Weiterleitung von Apple keine Supabase-Authentifizierungs-Header enthält. Der Endpunkt validiert den OAuth-Ablauf selbst. 5. **Funktion bereitstellen** ```bash supabase functions deploy apple-signin-callback ``` 6. **Ihre Funktions-URL abrufen** Nach der Bereitstellung erhalten Sie eine URL wie: ```plaintext https://ihre-projekt-ref.supabase.co/functions/v1/apple-signin-callback ``` Falls Sie sie nicht finden können, können Sie Folgendes tun: 1. Öffnen Sie `https://supabase.com/dashboard/project/IHRE_PROJEKT_REF/functions` 2. Klicken Sie auf die URL der `apple-signin-callback`-Funktion, um sie zu kopieren. ![Supabase Functions Apple Sign-In Callback](/social-login-assets/supabase_functions_apple_signin_callback.webp) Speichern Sie diese URL Sie benötigen diese URL im nächsten Schritt bei der Konfiguration des Apple Developer Portals. ## Schritt 2: Apple Developer Portal konfigurieren [Section titled “Schritt 2: Apple Developer Portal konfigurieren”](#schritt-2-apple-developer-portal-konfigurieren) Jetzt müssen wir das Apple Developer Portal mit der Backend-URL konfigurieren und alle erforderlichen Werte abrufen. Funktions-URL Stellen Sie sicher, dass Sie Ihre Supabase Edge Function-URL aus Schritt 1 haben, bevor Sie fortfahren. Sie benötigen sie, um die Return-URL im Apple Developer Portal zu konfigurieren. 1. **Folgen Sie der Apple Login Android-Einrichtungsanleitung** Schließen Sie die [Apple Login Android-Einrichtungsanleitung](/docs/plugins/social-login/apple/android/) ab, um: * Eine Service-ID zu erstellen * Einen privaten Schlüssel zu generieren (.p8-Datei) * Ihre Team-ID und Key-ID zu erhalten * Die Return-URL zu konfigurieren 2. **Return-URL im Apple Developer Portal festlegen** Verwenden Sie beim Konfigurieren der Return-URL im Apple Developer Portal (Schritt 6.9 der Apple-Anleitung) Ihre Supabase Edge Function-URL: ```plaintext https://ihre-projekt-ref.supabase.co/functions/v1/apple-signin-callback ``` Wichtig: Supabase Edge Function-URL verwenden Verwenden Sie **NICHT** die Weiterleitungs-URL aus der [Apple Login Android-Einrichtungsanleitung](/docs/plugins/social-login/apple/android/). Diese Anleitung verwendet eine benutzerdefinierte Backend-Server-URL. Für die Supabase-Integration **müssen** Sie stattdessen Ihre Supabase Edge Function-URL verwenden. Exakte Übereinstimmung erforderlich Die Return-URL muss **exakt** mit dem übereinstimmen, was Sie hier konfigurieren. Apple ist sehr streng bei der Übereinstimmung der Weiterleitungs-URI. 3. **Alle erforderlichen Werte sammeln** Nach Abschluss der Apple-Einrichtungsanleitung sollten Sie haben: * **APPLE\_TEAM\_ID**: Ihre Apple Developer Team-ID * **APPLE\_KEY\_ID**: Die Key-ID aus dem Apple Developer Portal * **APPLE\_PRIVATE\_KEY**: Ihre .p8-Privatschlüsseldatei (muss base64-kodiert sein) * **ANDROID\_SERVICE\_ID**: Ihre Apple Service-ID (z.B. `com.example.app.service`) * **BASE\_REDIRECT\_URL**: Ihre Deep-Link-URL (z.B. `capgo-demo-app://path`) Deep-Link-URL Die `BASE_REDIRECT_URL` ist das in Ihrer `AndroidManifest.xml` konfigurierte Deep-Link-Schema. Dorthin leitet das Backend nach der Authentifizierung weiter. Der Wert Ihres Deep-Links hängt vom Code `android:scheme="capgo-demo-app"` ab. Wenn Sie `android:scheme="capgo-demo-app"` in Ihrer `AndroidManifest.xml` gesetzt haben, wird Ihr Deep-Link `capgo-demo-app://path` sein. ## Schritt 3: Supabase Secrets festlegen [Section titled “Schritt 3: Supabase Secrets festlegen”](#schritt-3-supabase-secrets-festlegen) Jetzt müssen wir die Umgebungsvariablen (Secrets) für die Supabase Edge Function konfigurieren. 1. **Ihren privaten Schlüssel kodieren** Kodieren Sie zuerst Ihren privaten Apple-Schlüssel (.p8-Datei) in base64: ```bash base64 -i AuthKey_XXXXX.p8 ``` Kopieren Sie die gesamte Ausgabe (es ist eine einzelne lange Zeichenkette). 2. **Secrets mit Supabase CLI festlegen** ```bash supabase secrets set APPLE_TEAM_ID=ihre_team_id supabase secrets set APPLE_KEY_ID=ihre_key_id supabase secrets set APPLE_PRIVATE_KEY=ihr_base64_kodierter_schluessel supabase secrets set ANDROID_SERVICE_ID=ihre.service.id supabase secrets set BASE_REDIRECT_URL=ihre-app://pfad supabase secrets set APPLE_REDIRECT_URI=https://ihre-projekt-ref.supabase.co/functions/v1/apple-signin-callback ``` Platzhalter ersetzen Ersetzen Sie alle Platzhalterwerte durch Ihre tatsächlichen Werte aus Schritt 2. 3. **Alternative: Secrets im Supabase Dashboard festlegen** Wenn Sie das Dashboard bevorzugen: 1. Gehen Sie zu Ihrem Supabase-Projekt-Dashboard 2. Navigieren Sie zu **Edge Functions** → **Einstellungen** → **Secrets** 3. Fügen Sie jede Secret-Variable mit ihrem Wert hinzu Android-App-Konfiguration Die [Apple Login Android-Einrichtungsanleitung](/docs/plugins/social-login/apple/android/) deckt bereits die Konfiguration Ihrer Android-App ab: * Hinzufügen des Deep-Link-Intent-Filters zu `AndroidManifest.xml` * Hinzufügen des `onNewIntent`-Handlers zu `MainActivity.java` Stellen Sie sicher, dass Sie diese Schritte abgeschlossen haben, bevor Sie fortfahren. Das dort konfigurierte Deep-Link-Schema wird Ihre `BASE_REDIRECT_URL` in Schritt 3 sein. ## Schritt 4: Authentifizierungs-Helper verwenden [Section titled “Schritt 4: Authentifizierungs-Helper verwenden”](#schritt-4-authentifizierungs-helper-verwenden) Jetzt können Sie den Authentifizierungs-Helper in Ihrem App-Code verwenden. ### Implementierung [Section titled “Implementierung”](#implementierung) Die vollständige Implementierung ist in der Datei [`supabaseAuthUtils.ts` der Beispiel-App](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/supabaseAuthUtils.ts) verfügbar. ### Verwendung des Authentifizierungs-Helpers [Section titled “Verwendung des Authentifizierungs-Helpers”](#verwendung-des-authentifizierungs-helpers) ```typescript import { authenticateWithAppleSupabase } from './supabaseAuthUtils'; const result = await authenticateWithAppleSupabase(); if (result.success) { console.log('Angemeldet:', result.user); // Navigieren Sie zu Ihrem authentifizierten Bereich } else { console.error('Fehler:', result.error); } ``` ### Aktualisierung der Helper-Funktion [Section titled “Aktualisierung der Helper-Funktion”](#aktualisierung-der-helper-funktion) Wenn Sie die Helper-Funktion `authenticateWithAppleSupabase` verwenden, **müssen** Sie die folgenden Werte aktualisieren, damit sie mit Ihrer Konfiguration übereinstimmen: 1. **`redirectUrl` aktualisieren** - Setzen Sie dies auf Ihre Supabase Edge Function-URL: ```typescript const redirectUrl = platform === 'android' ? 'https://ihre-projekt-ref.supabase.co/functions/v1/apple-signin-callback' : undefined; ``` 2. **`clientId` aktualisieren** - Setzen Sie dies auf Ihre Apple Service-ID: ```typescript await SocialLogin.initialize({ apple: { clientId: isIOS ? undefined // iOS verwendet automatisch die Bundle-ID : 'ihre.service.id.hier', // Ihre Apple Service-ID für Android redirectUrl: redirectUrl, }, }); ``` Wichtig Ersetzen Sie `'ihre.service.id.hier'` durch Ihre tatsächliche Apple Service-ID (derselbe Wert, den Sie für `ANDROID_SERVICE_ID` in Schritt 3 verwendet haben). Siehe die [vollständige Implementierung](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/supabaseAuthUtils.ts) als Referenz. ## Schritt 5: Integration testen [Section titled “Schritt 5: Integration testen”](#schritt-5-integration-testen) 1. **Ihre Android-App erstellen und ausführen** ```bash npx cap sync android npx cap run android ``` 2. **Authentifizierungsablauf testen** * Tippen Sie auf die Schaltfläche “Mit Apple anmelden” * Sie sollten die Apple OAuth-Seite in einem Browser sehen * Nach der Authentifizierung sollten Sie zurück zu Ihrer App weitergeleitet werden * Überprüfen Sie die Konsolenprotokolle auf Fehler 3. **Ablauf überprüfen** Der vollständige Ablauf sollte sein: 1. Benutzer tippt auf “Mit Apple anmelden” 2. App öffnet Browser mit Apple OAuth 3. Benutzer authentifiziert sich bei Apple 4. Apple leitet weiter zu: `https://ihre-projekt-ref.supabase.co/functions/v1/apple-signin-callback` 5. Edge Function tauscht Code gegen Tokens aus 6. Edge Function leitet weiter zu: `ihre-app://pfad?id_token=...&access_token=...` 7. Android-App empfängt den Deep-Link und verarbeitet das Identitäts-Token 8. App meldet sich bei Supabase mit dem Identitäts-Token an ## Fehlerbehebung [Section titled “Fehlerbehebung”](#fehlerbehebung) Wenn die Authentifizierung fehlschlägt: * **Weiterleitungs-URI stimmt nicht überein**: Überprüfen Sie, ob die Return-URL im Apple Developer Portal exakt mit dem `APPLE_REDIRECT_URI`-Secret übereinstimmt * **Deep-Link funktioniert nicht**: Prüfen Sie, ob der `AndroidManifest.xml`-Intent-Filter mit Ihrer `BASE_REDIRECT_URL` übereinstimmt * **Fehlende Secrets**: Überprüfen Sie, ob alle Secrets korrekt gesetzt sind mit `supabase secrets list` * **Token-Austausch schlägt fehl**: Überprüfen Sie die Edge Function-Logs im Supabase Dashboard auf detaillierte Fehlermeldungen * **App erhält keinen Callback**: Stellen Sie sicher, dass `onNewIntent` ordnungsgemäß in MainActivity implementiert ist * Überprüfen Sie den [Beispiel-App-Code](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/supabaseAuthUtils.ts) als Referenz ## Funktionsweise [Section titled “Funktionsweise”](#funktionsweise) Auf Android verwendet Apple Sign-In einen OAuth-Weiterleitungsablauf: 1. **Initialisierung**: Das Plugin wird mit Ihrer Service-ID und Backend-Weiterleitungs-URL initialisiert 2. **OAuth-Ablauf**: Öffnet einen Browser/Chrome Custom Tab mit der OAuth-Seite von Apple 3. **Backend-Callback**: Apple leitet zu Ihrer Supabase Edge Function mit einem Autorisierungscode weiter 4. **Token-Austausch**: Die Edge Function tauscht den Code gegen Tokens über den Token-Endpunkt von Apple aus 5. **Deep-Link-Weiterleitung**: Die Edge Function leitet zurück zu Ihrer App mit dem Identitäts-Token weiter 6. **Supabase-Authentifizierung**: Die App empfängt das Token und meldet sich bei Supabase an Dieser Ablauf ist notwendig, da Apple keine native Android-Unterstützung für “Mit Apple anmelden” bietet. # Supabase Apple Login - Allgemeine Einrichtung > Erfahren Sie, wie Sie Apple Sign-In mit Supabase-Authentifizierung über das Capacitor Social Login-Plugin integrieren. ## Überblick [Section titled “Überblick”](#überblick) Diese Anleitung hilft Ihnen bei der Integration von Apple Sign-In mit Supabase-Authentifizierung. Apple Sign-In bietet eine sichere, datenschutzorientierte Authentifizierungsmethode, die auf iOS-, Android- und Web-Plattformen funktioniert. ## Voraussetzungen [Section titled “Voraussetzungen”](#voraussetzungen) Bevor Sie beginnen, stellen Sie sicher, dass Sie: 1. [Ein Supabase-Projekt erstellt haben](https://database.new/) 2. Die Anleitung [Apple Login Allgemeine Einrichtung](/docs/plugins/social-login/apple/general/) gelesen haben, um Apple OAuth-Anmeldedaten einzurichten 3. Die jeweiligen plattformspezifischen Anleitungen befolgt haben, um Apple OAuth-Anmeldedaten für Ihre Zielplattform einzurichten: * [Android-Einrichtung](/docs/plugins/social-login/apple/android/) * [iOS-Einrichtung](/docs/plugins/social-login/apple/ios/) * [Web-Einrichtung](/docs/plugins/social-login/apple/web/) Note Bevor Sie mit dem Supabase-Tutorial beginnen, müssen Sie die Client-IDs für die Plattformen generieren, die Sie verwenden möchten. Für iOS ist die Client-ID identisch mit Ihrer App-ID. Für Android und Web ist die Client-ID identisch mit Ihrer Service-ID. Sie werden diese in Schritt 7 dieser Anleitung verwenden. ## Aktivieren des Apple OAuth-Anbieters in Supabase [Section titled “Aktivieren des Apple OAuth-Anbieters in Supabase”](#aktivieren-des-apple-oauth-anbieters-in-supabase) 1. Gehen Sie zu Ihrem [Supabase Dashboard](https://app.supabase.com/) 2. Klicken Sie auf Ihr Projekt ![Supabase Projektauswahl](/social-login-assets/supabase_project_select.webp) 3. Gehen Sie zum Menü `Authentifizierung` ![Supabase Authentifizierungsmenü](/social-login-assets/supabase_authentication_menu.webp) 4. Klicken Sie auf den Tab `Anbieter` ![Supabase Anbieter-Tab](/social-login-assets/supabase_providers_tab.webp) 5. Suchen Sie den `Apple`-Anbieter ![Supabase Apple-Anbieter](/social-login-assets/supabase_apple_provider.webp) 6. Aktivieren Sie den `Apple`-Anbieter ![Supabase Apple-Anbieter aktivieren](/social-login-assets/supabase_apple_provider_enable.webp) 7. Füllen Sie die Client-ID-Konfiguration aus: Note Wenn Sie Apple Login für iOS verwenden, ist die Client-ID identisch mit Ihrer App-ID. Wenn Sie Apple Login für Android oder Web verwenden, ist die Client-ID identisch mit Ihrer Service-ID. Wenn Sie beides verwenden, müssen Sie sowohl die App-ID als auch die Service-ID angeben. ![Supabase Apple-Anbieter Client-ID](/social-login-assets/supabase_apple_provider_client_id.webp) 8. Klicken Sie auf die Schaltfläche `Speichern` ![Supabase Apple-Anbieter speichern](/social-login-assets/supabase_apple_provider_save.webp) Note Sie ****MÜSSEN NICHT**** den `Geheimschlüssel (für OAuth)` einrichten. Wir werden eine benutzerdefinierte Backend-Implementierung durchführen, um die Apple-Anmeldung zu verarbeiten. Voilà, Sie haben jetzt Apple Sign-In mit Supabase-Authentifizierung aktiviert 🎉 ## Verwendung des Authentifizierungs-Helpers [Section titled “Verwendung des Authentifizierungs-Helpers”](#verwendung-des-authentifizierungs-helpers) Die vollständige Implementierung enthält eine Helper-Funktion `authenticateWithAppleSupabase()`, die den gesamten Apple Sign-In-Ablauf mit Supabase verarbeitet. Diese Funktion: * Initialisiert Apple Sign-In mit plattformspezifischer Konfiguration * Verarbeitet den Authentifizierungsablauf (nativ auf iOS, OAuth-Weiterleitung auf Android/Web) * Extrahiert das Identitäts-Token von Apple * Meldet sich bei Supabase mit dem Identitäts-Token an Vollständige Implementierung Die vollständige Implementierung ist in der Datei [`supabaseAuthUtils.ts` der Beispiel-App](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/supabaseAuthUtils.ts) verfügbar. ### Grundlegende Verwendung [Section titled “Grundlegende Verwendung”](#grundlegende-verwendung) ```typescript import { authenticateWithAppleSupabase } from './supabaseAuthUtils'; const result = await authenticateWithAppleSupabase(); if (result.success) { console.log('Angemeldet:', result.user); // Navigieren Sie zu Ihrem authentifizierten Bereich } else { console.error('Fehler:', result.error); } ``` ### Funktionsweise [Section titled “Funktionsweise”](#funktionsweise) Die Helper-Funktion behandelt automatisch plattformspezifische Unterschiede: * **iOS**: Verwendet natives Apple Sign-In (keine Weiterleitungs-URL erforderlich, verwendet automatisch die Bundle-ID) * **Android**: Verwendet OAuth-Weiterleitungsablauf mit Backend-Edge-Funktion (erfordert Service-ID) * **Web**: Verwendet OAuth-Popup-Ablauf (erfordert Service-ID und aktuelle Seiten-URL als Weiterleitung) Die Funktion gibt ein Identitäts-Token von Apple zurück, das dann zur Authentifizierung bei Supabase mit `supabase.auth.signInWithIdToken()` verwendet wird. # Supabase Apple Login auf iOS-Einrichtung > Erfahren Sie, wie Sie Apple Sign-In mit Supabase-Authentifizierung auf iOS integrieren. ## Voraussetzungen [Section titled “Voraussetzungen”](#voraussetzungen) Diese Anleitung hilft Ihnen bei der Integration von Apple Sign-In mit Supabase-Authentifizierung auf iOS. Es wird vorausgesetzt, dass Sie bereits Folgendes abgeschlossen haben: * die [Apple Login iOS-Einrichtung](/docs/plugins/social-login/apple/ios/) * die [Supabase Apple Login - Allgemeine Einrichtung](./general/). ## Implementierung [Section titled “Implementierung”](#implementierung) Die vollständige Implementierung ist in der Datei [`supabaseAuthUtils.ts` der Beispiel-App](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/supabaseAuthUtils.ts) verfügbar. Diese Anleitung erklärt die wichtigsten Konzepte und wie Sie sie verwenden. ### Verwendung des Authentifizierungs-Helpers [Section titled “Verwendung des Authentifizierungs-Helpers”](#verwendung-des-authentifizierungs-helpers) Die Funktion `authenticateWithAppleSupabase` verarbeitet den gesamten Authentifizierungsablauf: ```typescript import { authenticateWithAppleSupabase } from './supabaseAuthUtils'; const result = await authenticateWithAppleSupabase(); if (result.success) { console.log('Angemeldet:', result.user); // Navigieren Sie zu Ihrem authentifizierten Bereich } else { console.error('Fehler:', result.error); } ``` ## Funktionsweise [Section titled “Funktionsweise”](#funktionsweise) Auf iOS verwendet Apple Sign-In die native Implementierung: 1. **Initialisierung**: Das Plugin verwendet automatisch die Bundle-ID Ihrer App (keine `clientId` erforderlich) 2. **Native Anmeldung**: Verwendet Apples native “Mit Apple anmelden”-Schaltfläche und Authentifizierungsablauf 3. **Identitäts-Token**: Apple gibt ein Identitäts-Token (JWT) mit Benutzerinformationen zurück 4. **Supabase-Authentifizierung**: Das Identitäts-Token wird mit `signInWithIdToken()` an Supabase gesendet Die Helper-Funktion erkennt automatisch die iOS-Plattform und konfiguriert alles entsprechend. ## Wichtige Hinweise [Section titled “Wichtige Hinweise”](#wichtige-hinweise) ### Bundle-ID-Konfiguration [Section titled “Bundle-ID-Konfiguration”](#bundle-id-konfiguration) * iOS verwendet automatisch die Bundle-ID Ihrer App für Apple Sign-In * Stellen Sie sicher, dass Ihre Bundle-ID mit der im Apple Developer Portal konfigurierten übereinstimmt * Die Bundle-ID sollte die Funktion “Mit Apple anmelden” aktiviert haben ### Supabase Client-ID [Section titled “Supabase Client-ID”](#supabase-client-id) Konfigurieren Sie in Supabase Ihren Apple-Anbieter mit: * **Client-ID**: Ihre iOS-App-ID (Bundle-ID) - z.B. `app.capgo.plugin.SocialLogin` Wenn Sie auch Android/Web verwenden, müssen Sie sowohl die App-ID als auch die Service-ID im Client-ID-Feld von Supabase angeben (durch Komma getrennt). ## Fehlerbehebung [Section titled “Fehlerbehebung”](#fehlerbehebung) Wenn die Authentifizierung fehlschlägt: * **Bundle-ID stimmt nicht überein**: Überprüfen Sie, ob Ihre Bundle-ID sowohl in Xcode als auch im Apple Developer Portal übereinstimmt * **Funktion nicht aktiviert**: Stellen Sie sicher, dass die Funktion “Mit Apple anmelden” in Xcode aktiviert ist * **Supabase-Konfiguration**: Überprüfen Sie, ob Ihre App-ID in den Supabase Apple-Anbieter-Einstellungen korrekt konfiguriert ist * **Token-Validierung schlägt fehl**: Prüfen Sie, ob das Identitäts-Token von Apple empfangen wird * Überprüfen Sie den [Beispiel-App-Code](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/supabaseAuthUtils.ts) als Referenz # Supabase Apple Login im Web > Erfahren Sie, wie Sie Apple Sign-In mit Supabase-Authentifizierung im Web integrieren. ## Voraussetzungen [Section titled “Voraussetzungen”](#voraussetzungen) Diese Anleitung hilft Ihnen bei der Integration von Apple Sign-In mit Supabase-Authentifizierung im Web. Es wird vorausgesetzt, dass Sie bereits Folgendes abgeschlossen haben: * die [Apple Login Web-Einrichtung](/docs/plugins/social-login/apple/web/) * die [Supabase Apple Login - Allgemeine Einrichtung](./general/). ## Implementierung [Section titled “Implementierung”](#implementierung) Die vollständige Implementierung ist in der Datei [`supabaseAuthUtils.ts` der Beispiel-App](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/supabaseAuthUtils.ts) verfügbar. Diese Anleitung erklärt die wichtigsten Konzepte und wie Sie sie verwenden. ### Verwendung des Authentifizierungs-Helpers [Section titled “Verwendung des Authentifizierungs-Helpers”](#verwendung-des-authentifizierungs-helpers) Die Funktion `authenticateWithAppleSupabase` verarbeitet den gesamten Authentifizierungsablauf: ```typescript import { authenticateWithAppleSupabase } from './supabaseAuthUtils'; const result = await authenticateWithAppleSupabase(); if (result.success) { console.log('Angemeldet:', result.user); // Navigieren Sie zu Ihrem authentifizierten Bereich } else { console.error('Fehler:', result.error); } ``` ## Funktionsweise [Section titled “Funktionsweise”](#funktionsweise) Im Web verwendet Apple Sign-In einen OAuth-Popup-Ablauf: 1. **Initialisierung**: Das Plugin wird mit Ihrer Service-ID und der aktuellen Seiten-URL als Weiterleitungs-URL initialisiert 2. **OAuth-Popup**: Öffnet ein Popup-Fenster mit der OAuth-Seite von Apple 3. **Benutzer-Authentifizierung**: Der Benutzer authentifiziert sich bei Apple im Popup 4. **Identitäts-Token**: Apple gibt ein Identitäts-Token (JWT) mit Benutzerinformationen zurück 5. **Supabase-Authentifizierung**: Das Identitäts-Token wird mit `signInWithIdToken()` an Supabase gesendet Die Helper-Funktion erkennt automatisch die Web-Plattform und konfiguriert alles entsprechend. ## Wichtige Hinweise [Section titled “Wichtige Hinweise”](#wichtige-hinweise) ### Service-ID-Konfiguration [Section titled “Service-ID-Konfiguration”](#service-id-konfiguration) * Web erfordert Ihre Apple Service-ID (wie bei Android) * Die Service-ID muss im Apple Developer Portal mit den richtigen Return-URLs konfiguriert sein * Stellen Sie sicher, dass Ihre Domain zu den erlaubten Domains im Apple Developer Portal hinzugefügt wurde ### Weiterleitungs-URL [Section titled “Weiterleitungs-URL”](#weiterleitungs-url) * Im Web wird die Weiterleitungs-URL automatisch auf `window.location.href` (aktuelle Seiten-URL) gesetzt * Diese muss mit einer der im Apple Developer Portal konfigurierten Return-URLs übereinstimmen * Stellen Sie sicher, dass sowohl die URL mit als auch ohne abschließenden Schrägstrich im Apple Developer Portal konfiguriert sind ### Supabase Client-ID [Section titled “Supabase Client-ID”](#supabase-client-id) Konfigurieren Sie in Supabase Ihren Apple-Anbieter mit: * **Client-ID**: Ihre Apple Service-ID (z.B. `com.example.app.service`) Wenn Sie auch iOS verwenden, müssen Sie sowohl die App-ID als auch die Service-ID im Client-ID-Feld von Supabase angeben (durch Komma getrennt). ### Aktualisierung der Helper-Funktion [Section titled “Aktualisierung der Helper-Funktion”](#aktualisierung-der-helper-funktion) Wenn Sie die Helper-Funktion `authenticateWithAppleSupabase` verwenden, **müssen** Sie die `clientId` aktualisieren, damit sie mit Ihrer Apple Service-ID übereinstimmt: ```typescript await SocialLogin.initialize({ apple: { clientId: isIOS ? undefined // iOS verwendet automatisch die Bundle-ID : 'ihre.service.id.hier', // Ihre Apple Service-ID für Web und Android redirectUrl: redirectUrl, }, }); ``` Wichtig Ersetzen Sie `'ihre.service.id.hier'` durch Ihre tatsächliche Apple Service-ID (derselbe Wert, den Sie im Apple Developer Portal konfiguriert haben). ## Fehlerbehebung [Section titled “Fehlerbehebung”](#fehlerbehebung) Wenn die Authentifizierung fehlschlägt: * **Service-ID stimmt nicht überein**: Überprüfen Sie, ob Ihre Service-ID sowohl im Apple Developer Portal als auch in Ihrem Code übereinstimmt * **Return-URL nicht konfiguriert**: Stellen Sie sicher, dass Ihre aktuelle Seiten-URL (mit und ohne abschließenden Schrägstrich) im Apple Developer Portal konfiguriert ist * **Popup blockiert**: Überprüfen Sie die Browsereinstellungen - einige Browser blockieren Popups standardmäßig * **Domain nicht erlaubt**: Überprüfen Sie, ob Ihre Domain zu den erlaubten Domains im Apple Developer Portal hinzugefügt wurde * **Supabase-Konfiguration**: Überprüfen Sie, ob Ihre Service-ID in den Supabase Apple-Anbieter-Einstellungen korrekt konfiguriert ist * Überprüfen Sie den [Beispiel-App-Code](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/supabaseAuthUtils.ts) als Referenz # Supabase Google Login auf Android > Erfahren Sie, wie Sie Google Sign-In mit Supabase Authentication auf Android unter Verwendung des Capacitor Social Login Plugins einrichten. ## Einführung [Section titled “Einführung”](#einführung) Dieser Leitfaden hilft Ihnen, Google Sign-In mit Supabase Authentication auf Android zu integrieren. Es wird vorausgesetzt, dass Sie bereits Folgendes abgeschlossen haben: * die [Google Login Android-Einrichtung](/docs/plugins/social-login/google/android/) * die [Supabase Google Login - Allgemeine Einrichtung](/docs/plugins/social-login/supabase/google/general/). ## Implementierung [Section titled “Implementierung”](#implementierung) Die vollständige Implementierung ist in der Datei [`supabaseAuthUtils.ts` der Beispiel-App](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/supabaseAuthUtils.ts) verfügbar. Dieser Leitfaden erklärt die Kernkonzepte und wie Sie sie verwenden. ### Verwendung des Authentifizierungs-Helfers [Section titled “Verwendung des Authentifizierungs-Helfers”](#verwendung-des-authentifizierungs-helfers) Die Funktion `authenticateWithGoogleSupabase` behandelt den gesamten Authentifizierungsablauf: ```typescript import { authenticateWithGoogleSupabase } from './supabaseAuthUtils'; const result = await authenticateWithGoogleSupabase(); if (result.success) { console.log('Angemeldet:', result.user); // Navigieren Sie zu Ihrem authentifizierten Bereich } else { console.error('Fehler:', result.error); } ``` ## Wie es funktioniert [Section titled “Wie es funktioniert”](#wie-es-funktioniert) Eine detaillierte Erklärung der Funktionsweise des Authentifizierungsablaufs, einschließlich Nonce-Generierung, JWT-Validierung und Supabase-Anmeldung, finden Sie im [Abschnitt “Wie es funktioniert” im Allgemeinen Einrichtungsleitfaden](/docs/plugins/social-login/supabase/google/general/#how-it-works). Die vollständige Code-Referenz finden Sie im [Abschnitt “Vollständige Code-Referenz” im Allgemeinen Einrichtungsleitfaden](/docs/plugins/social-login/supabase/google/general/#complete-code-reference). ## Wichtige Hinweise [Section titled “Wichtige Hinweise”](#wichtige-hinweise) ### Nonce-Behandlung [Section titled “Nonce-Behandlung”](#nonce-behandlung) Die Nonce-Implementierung folgt dem Muster aus der [React Native Google Sign In Dokumentation](https://react-native-google-signin.github.io/docs/security#usage-with-supabase): * `rawNonce` geht zu Supabase’s `signInWithIdToken()` * Supabase erstellt einen Hash von `rawNonce` und vergleicht ihn mit dem `nonceDigest`, der im ID-Token von Google Sign-In enthalten ist * `nonceDigest` (SHA-256-Hash, hex-codiert) geht zum `nonce`-Parameter in Google Sign-In APIs ### Automatischer Wiederholungsversuch [Section titled “Automatischer Wiederholungsversuch”](#automatischer-wiederholungsversuch) Die Implementierung umfasst automatische Wiederholungslogik: * Wenn die JWT-Validierung beim ersten Versuch fehlschlägt, meldet sie sich ab und versucht es einmal erneut * Dies behandelt Fälle, in denen zwischengespeicherte Tokens möglicherweise falsche Nonces haben * Wenn der Wiederholungsversuch ebenfalls fehlschlägt, wird ein Fehler zurückgegeben ## Fehlerbehebung [Section titled “Fehlerbehebung”](#fehlerbehebung) Wenn die Authentifizierung fehlschlägt: * **Ungültiges Publikum**: Überprüfen Sie, ob Ihre Google Client IDs sowohl in der Google Cloud Console als auch in Supabase übereinstimmen * **Nonce-Fehlanpassung**: Überprüfen Sie die Konsolenprotokolle - die Funktion versucht es automatisch erneut, aber Sie können sich bei Bedarf zuerst manuell abmelden * **Token-Validierung schlägt fehl**: Stellen Sie sicher, dass Sie `mode: 'online'` im Initialize-Aufruf verwenden, um ein idToken zu erhalten * Überprüfen Sie den [Beispiel-App-Code](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/supabaseAuthUtils.ts) als Referenz # Supabase Google Login - Allgemeine Einrichtung > Erfahren Sie, wie Sie Google Sign-In mit Supabase Authentication unter Verwendung des Capacitor Social Login Plugins einrichten. ## Einführung [Section titled “Einführung”](#einführung) Dieser Leitfaden führt Sie durch die Integration von Google Sign-In mit Supabase Authentication unter Verwendung des Capacitor Social Login Plugins. Diese Einrichtung ermöglicht es Ihnen, natives Google Sign-In auf mobilen Plattformen zu verwenden, während Sie Supabase Auth für die Backend-Authentifizierung nutzen. ## Voraussetzungen [Section titled “Voraussetzungen”](#voraussetzungen) Bevor Sie beginnen, stellen Sie sicher, dass Sie Folgendes haben: 1. [Ein Supabase-Projekt erstellt](https://database.new/) 2. Den [Google Login Allgemeinen Einrichtungsleitfaden](/docs/plugins/social-login/google/general/) gelesen haben, um Google OAuth-Anmeldedaten einzurichten 3. Die jeweiligen plattformspezifischen Leitfäden zur Einrichtung von Google OAuth-Anmeldedaten für Ihre Zielplattform befolgt haben: * [Android-Einrichtung](/docs/plugins/social-login/google/android/) * [iOS-Einrichtung](/docs/plugins/social-login/google/ios/) * [Web-Einrichtung](/docs/plugins/social-login/google/web/) Note Bevor Sie mit dem Supabase-Tutorial beginnen, müssen Sie die Client-IDs für die Plattformen generieren, die Sie verwenden möchten. Sie werden diese in Schritt 7 dieses Leitfadens verwenden. ## Aktivierung des Google OAuth-Providers in Supabase [Section titled “Aktivierung des Google OAuth-Providers in Supabase”](#aktivierung-des-google-oauth-providers-in-supabase) 1. Gehen Sie zu Ihrem [Supabase Dashboard](https://app.supabase.com/) 2. Klicken Sie auf Ihr Projekt ![Supabase Projektauswahl](/social-login-assets/supabase_project_select.webp) 3. Gehen Sie zum `Authentication`-Menü ![Supabase Authentication Menü](/social-login-assets/supabase_authentication_menu.webp) 4. Klicken Sie auf den `Providers`-Tab ![Supabase Providers Tab](/social-login-assets/supabase_providers_tab.webp) 5. Finden Sie den `Google`-Provider ![Supabase Google Provider](/social-login-assets/supabase_google_provider.webp) 6. Aktivieren Sie den Provider ![Supabase Google Provider aktivieren](/social-login-assets/supabase_google_provider_enable.webp) 7. Fügen Sie die Client-IDs für die Plattformen hinzu, die Sie verwenden möchten ![Supabase Google Provider Client IDs hinzufügen](/social-login-assets/supabase_google_provider_add_client_ids.webp) Note Dies umfasst die Web Client ID, die iOS Client ID und die Android Client ID. Sie können einige davon überspringen, abhängig von den Plattformen, die Sie verwenden möchten. 8. Klicken Sie auf die `Save`-Schaltfläche ![Supabase Google Provider speichern](/social-login-assets/supabase_google_provider_save.webp) Note Sie ****SOLLTEN NICHT**** `Client Secret (for OAuth)` oder `Callback URL (for OAuth)` einrichten. Einige Quellen könnten auch vorschlagen, `Skip nonce checks` für Google auf iOS zu setzen, aber mit diesem Leitfaden ist dies nicht erforderlich. Voilà, Sie haben jetzt Google Sign-In mit Supabase Authentication aktiviert! ## Wie der Google Sign-In mit Supabase Authentication Helper funktioniert [Section titled “Wie der Google Sign-In mit Supabase Authentication Helper funktioniert”](#wie-der-google-sign-in-mit-supabase-authentication-helper-funktioniert) Dieser Abschnitt erklärt, wie die Google Sign-In-Integration mit Supabase unter der Haube funktioniert. Das Verständnis dieses Ablaufs hilft Ihnen, den Authentifizierungsprozess zu implementieren und Fehler zu beheben. Vollständige Implementierung Die vollständige Implementierung ist in der Datei [`supabaseAuthUtils.ts` der Beispiel-App](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/supabaseAuthUtils.ts) verfügbar. ### 1. Nonce-Generierung [Section titled “1. Nonce-Generierung”](#1-nonce-generierung) Die Implementierung generiert ein sicheres Nonce-Paar gemäß den [Supabase Nonce-Anforderungen](https://react-native-google-signin.github.io/docs/security#usage-with-supabase): ```typescript // URL-sichere Zufalls-Nonce generieren function getUrlSafeNonce(): string { const array = new Uint8Array(32); crypto.getRandomValues(array); return Array.from(array, (byte) => byte.toString(16).padStart(2, '0')).join(''); } // Nonce mit SHA-256 hashen async function sha256Hash(message: string): Promise { const encoder = new TextEncoder(); const data = encoder.encode(message); const hashBuffer = await crypto.subtle.digest('SHA-256', data); const hashArray = Array.from(new Uint8Array(hashBuffer)); return hashArray.map((b) => b.toString(16).padStart(2, '0')).join(''); } // Nonce-Paar generieren async function getNonce(): Promise<{ rawNonce: string; nonceDigest: string }> { const rawNonce = getUrlSafeNonce(); const nonceDigest = await sha256Hash(rawNonce); return { rawNonce, nonceDigest }; } ``` **Ablauf:** * `rawNonce`: URL-sichere Zufallszeichenkette (64 Hex-Zeichen) * `nonceDigest`: SHA-256-Hash von `rawNonce` (hex-codiert) * `nonceDigest` wird an Google Sign-In übergeben → Google fügt den Nonce-Digest in das ID-Token ein * `rawNonce` wird an Supabase übergeben → Supabase hasht die rohe Nonce und vergleicht sie mit der Nonce des Tokens ### 2. Google Sign-In [Section titled “2. Google Sign-In”](#2-google-sign-in) Die Funktion initialisiert das Plugin und meldet sich bei Google an: ```typescript await SocialLogin.initialize({ google: { webClientId: 'YOUR_WEB_CLIENT_ID.apps.googleusercontent.com', // Nur iOS: iOSClientId: 'YOUR_IOS_CLIENT_ID.apps.googleusercontent.com', mode: 'online', // Erforderlich, um idToken zu erhalten }, }); const response = await SocialLogin.login({ provider: 'google', options: { scopes: ['email', 'profile'], nonce: nonceDigest, // SHA-256-gehashte Nonce übergeben }, }); ``` ### 3. JWT-Validierung [Section titled “3. JWT-Validierung”](#3-jwt-validierung) Vor dem Senden des Tokens an Supabase validiert die Implementierung das JWT-Token: ```typescript function validateJWTToken(idToken: string, expectedNonceDigest: string): { valid: boolean; error?: string } { const decodedToken = decodeJWT(idToken); // Überprüfen, ob das Publikum mit Ihren Google Client IDs übereinstimmt const audience = decodedToken.aud; if (!VALID_GOOGLE_CLIENT_IDS.includes(audience)) { return { valid: false, error: 'Invalid audience' }; } // Nonce-Übereinstimmung prüfen const tokenNonce = decodedToken.nonce; if (tokenNonce && tokenNonce !== expectedNonceDigest) { return { valid: false, error: 'Nonce mismatch' }; } return { valid: true }; } ``` **Warum vor Supabase validieren?** Die Validierung des JWT-Tokens vor dem Senden an Supabase dient mehreren wichtigen Zwecken: 1. **Ungültige Anfragen verhindern**: Wenn das Token ein falsches Publikum oder eine Nonce-Fehlanpassung hat, wird Supabase das Token ohnehin ablehnen. Eine vorherige Validierung vermeidet unnötige API-Aufrufe und bietet klarere Fehlermeldungen. 2. **Token-Caching-Probleme**: Auf einigen Plattformen (insbesondere iOS) kann das Google Sign-In SDK Tokens aus Leistungsgründen zwischenspeichern. Wenn ein zwischengespeichertes Token zurückgegeben wird, wurde das Token möglicherweise mit einer anderen Nonce (oder ohne Nonce) generiert, was dazu führt, dass Supabase das Token mit einem “Nonce mismatch”-Fehler ablehnt. Durch Validierung vor dem Senden an Supabase können wir dieses Problem frühzeitig erkennen und automatisch mit einem frischen Token wiederholen. 3. **Sicherheit** (iOS): Auf iOS stellt die Validierung sicher, dass das Token für Ihre spezifischen Google Client IDs ausgestellt wurde, wodurch potenzielle Sicherheitsprobleme durch die Verwendung von Tokens verhindert werden, die für andere Anwendungen bestimmt sind. 4. **Bessere Fehlerbehandlung**: Das frühzeitige Erkennen von Problemen vor Supabase ermöglicht eine automatische Wiederholungslogik, die für die transparente Behandlung von iOS-Caching-Problemen unerlässlich ist. Wenn die Validierung fehlschlägt, führt die Funktion automatisch Folgendes aus: 1. Meldet sich von Google ab (löscht zwischengespeicherte Tokens - kritisch auf iOS) 2. Wiederholt die Authentifizierung einmal (erzwingt Generierung eines frischen Tokens mit korrekter Nonce) 3. Wenn die Wiederholung ebenfalls fehlschlägt, wird ein Fehler zurückgegeben ### 4. Supabase-Anmeldung [Section titled “4. Supabase-Anmeldung”](#4-supabase-anmeldung) Schließlich wird das validierte Token an Supabase gesendet: ```typescript const { data, error } = await supabase.auth.signInWithIdToken({ provider: 'google', token: googleResponse.idToken, nonce: rawNonce, // Rohe (ungehashte) Nonce übergeben }); ``` ## Vollständige Code-Referenz [Section titled “Vollständige Code-Referenz”](#vollständige-code-referenz) Die vollständige Implementierung ist in der Datei [`supabaseAuthUtils.ts` der Beispiel-App](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/supabaseAuthUtils.ts) verfügbar, die Folgendes umfasst: * `getUrlSafeNonce()` - Generiert URL-sichere Zufalls-Nonce * `sha256Hash()` - Hasht String mit SHA-256 * `getNonce()` - Generiert Nonce-Paar * `decodeJWT()` - Dekodiert JWT-Token * `validateJWTToken()` - Validiert JWT-Publikum und Nonce * `authenticateWithGoogleSupabase()` - Haupt-Authentifizierungsfunktion mit automatischer Wiederholung ### Zusätzliche Beispieldateien [Section titled “Zusätzliche Beispieldateien”](#zusätzliche-beispieldateien) * [SupabasePage.tsx](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/SupabasePage.tsx) - Beispielkomponente mit Redirect-Handling (Web) * [SupabaseCreateAccountPage.tsx](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/SupabaseCreateAccountPage.tsx) - Beispiel-Kontoerstellungsseite ## Nächste Schritte [Section titled “Nächste Schritte”](#nächste-schritte) Fahren Sie bitte mit dem plattformspezifischen Einrichtungsleitfaden für Ihre Zielplattform fort: * [Android-Einrichtung](../android/) * [iOS-Einrichtung](../ios/) * [Web-Einrichtung](../web/) # Supabase Google Login auf iOS > Erfahren Sie, wie Sie Google Sign-In mit Supabase Authentication auf iOS unter Verwendung des Capacitor Social Login Plugins einrichten. ## Einführung [Section titled “Einführung”](#einführung) Dieser Leitfaden hilft Ihnen, Google Sign-In mit Supabase Authentication auf iOS zu integrieren. Es wird vorausgesetzt, dass Sie bereits Folgendes abgeschlossen haben: * die [Google Login iOS-Einrichtung](/docs/plugins/social-login/google/ios/) * die [Supabase Google Login - Allgemeine Einrichtung](/docs/plugins/social-login/supabase/google/general/). ## Implementierung [Section titled “Implementierung”](#implementierung) Die vollständige Implementierung ist in der Datei [`supabaseAuthUtils.ts` der Beispiel-App](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/supabaseAuthUtils.ts) verfügbar. Dieser Leitfaden erklärt die Kernkonzepte und wie Sie sie verwenden. ### Verwendung des Authentifizierungs-Helfers [Section titled “Verwendung des Authentifizierungs-Helfers”](#verwendung-des-authentifizierungs-helfers) Die Funktion `authenticateWithGoogleSupabase` behandelt den gesamten Authentifizierungsablauf: ```typescript import { authenticateWithGoogleSupabase } from './supabaseAuthUtils'; const result = await authenticateWithGoogleSupabase(); if (result.success) { console.log('Angemeldet:', result.user); // Navigieren Sie zu Ihrem authentifizierten Bereich } else { console.error('Fehler:', result.error); } ``` ## Wie es funktioniert [Section titled “Wie es funktioniert”](#wie-es-funktioniert) Eine detaillierte Erklärung der Funktionsweise des Authentifizierungsablaufs, einschließlich Nonce-Generierung, JWT-Validierung und Supabase-Anmeldung, finden Sie im [Abschnitt “Wie es funktioniert” im Allgemeinen Einrichtungsleitfaden](/docs/plugins/social-login/supabase/google/general/#how-it-works). ## Wichtige Einschränkungen [Section titled “Wichtige Einschränkungen”](#wichtige-einschränkungen) ### iOS Token-Caching und Nonce-Probleme [Section titled “iOS Token-Caching und Nonce-Probleme”](#ios-token-caching-und-nonce-probleme) iOS Nonce-Caching-Problem Auf iOS kann Google Sign-In Tokens zwischenspeichern, was dazu führen kann, dass die Nonce-Validierung fehlschlägt. Die Funktion `validateJWTToken` erkennt dies und behandelt es automatisch: 1. **Automatische Erkennung**: Die Funktion prüft, ob die Nonce im Token mit dem erwarteten `nonceDigest` übereinstimmt 2. **Automatischer Wiederholungsversuch**: Wenn die Validierung fehlschlägt, meldet sie sich automatisch von Google ab und versucht es einmal erneut 3. **Fehlerbehandlung**: Wenn der Wiederholungsversuch ebenfalls fehlschlägt, wird ein Fehler zurückgegeben **Warum dies geschieht**: Das iOS Google Sign-In SDK speichert Tokens zur Leistungsoptimierung zwischen. Wenn ein zwischengespeichertes Token zurückgegeben wird, wurde es möglicherweise mit einer anderen Nonce (oder ohne Nonce) generiert, was zu einer Fehlanpassung führt. **Die Lösung**: Die Implementierung behandelt dies automatisch durch Abmelden und Wiederholen, wodurch Google gezwungen wird, ein frisches Token mit der korrekten Nonce zu generieren. **Manuelle Problemumgehung** (falls der automatische Wiederholungsversuch nicht funktioniert): ```typescript // Zuerst abmelden, um zwischengespeicherte Tokens zu löschen await SocialLogin.logout({ provider: 'google' }); // Dann authentifizieren const result = await authenticateWithGoogleSupabase(); ``` Dies stellt sicher, dass ein frisches Token mit der korrekten Nonce erhalten wird. Die vollständige Code-Referenz finden Sie im [Abschnitt “Vollständige Code-Referenz” im Allgemeinen Einrichtungsleitfaden](/docs/plugins/social-login/supabase/google/general/#complete-code-reference). ## Wichtige Hinweise [Section titled “Wichtige Hinweise”](#wichtige-hinweise) ### Nonce-Behandlung [Section titled “Nonce-Behandlung”](#nonce-behandlung) Die Nonce-Implementierung folgt dem Muster aus der [React Native Google Sign In Dokumentation](https://react-native-google-signin.github.io/docs/security#usage-with-supabase): * `rawNonce` geht zu Supabase’s `signInWithIdToken()` * Supabase erstellt einen Hash von `rawNonce` und vergleicht ihn mit dem `nonceDigest`, der im ID-Token von Google Sign-In enthalten ist * `nonceDigest` (SHA-256-Hash, hex-codiert) geht zum `nonce`-Parameter in Google Sign-In APIs ### Automatischer Wiederholungsmechanismus [Section titled “Automatischer Wiederholungsmechanismus”](#automatischer-wiederholungsmechanismus) Die Funktion `authenticateWithGoogleSupabase` enthält einen `retry`-Parameter: * Erster Aufruf (`retry=false`): Wenn die Validierung fehlschlägt, meldet sie sich automatisch ab und versucht es einmal erneut * Wiederholungsaufruf (`retry=true`): Wenn die Validierung erneut fehlschlägt, wird sofort ein Fehler zurückgegeben Dies behandelt das iOS-Token-Caching-Problem automatisch. ## Fehlerbehebung [Section titled “Fehlerbehebung”](#fehlerbehebung) Wenn die Authentifizierung fehlschlägt: * **Nonce-Fehlanpassung**: Die Funktion versucht es automatisch erneut - überprüfen Sie die Konsolenprotokolle für Details. Wenn es weiterhin besteht, melden Sie sich zuerst manuell ab * **Ungültiges Publikum**: Überprüfen Sie, ob Ihre Google Client IDs sowohl in der Google Cloud Console als auch in Supabase übereinstimmen (sowohl iOS als auch Web Client IDs) * **Token-Validierung schlägt fehl**: Stellen Sie sicher, dass Sie `mode: 'online'` im Initialize-Aufruf verwenden, um ein idToken zu erhalten * **Info.plist-Konfiguration**: Stellen Sie sicher, dass Info.plist die korrekten URL-Schemas und GIDClientID hat * Überprüfen Sie den [Beispiel-App-Code](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/supabaseAuthUtils.ts) als Referenz # Supabase Google Login im Web > Erfahren Sie, wie Sie Google Sign-In mit Supabase Authentication im Web unter Verwendung des Capacitor Social Login Plugins einrichten. ## Einführung [Section titled “Einführung”](#einführung) Dieser Leitfaden hilft Ihnen, Google Sign-In mit Supabase Authentication im Web zu integrieren. Es wird vorausgesetzt, dass Sie bereits Folgendes abgeschlossen haben: * die [Google Login Web-Einrichtung](/docs/plugins/social-login/google/web/) * die [Supabase Google Login - Allgemeine Einrichtung](/docs/plugins/social-login/supabase/google/general/). ## Implementierung [Section titled “Implementierung”](#implementierung) Die vollständige Implementierung ist in der Datei [`supabaseAuthUtils.ts` der Beispiel-App](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/supabaseAuthUtils.ts) verfügbar. Dieser Leitfaden erklärt die Kernkonzepte und wie Sie sie verwenden. ### Verwendung des Authentifizierungs-Helfers [Section titled “Verwendung des Authentifizierungs-Helfers”](#verwendung-des-authentifizierungs-helfers) Die Funktion `authenticateWithGoogleSupabase` behandelt den gesamten Authentifizierungsablauf: ```typescript import { authenticateWithGoogleSupabase } from './supabaseAuthUtils'; const result = await authenticateWithGoogleSupabase(); if (result.success) { console.log('Angemeldet:', result.user); // Navigieren Sie zu Ihrem authentifizierten Bereich } else { console.error('Fehler:', result.error); } ``` ## Kritisch: Redirect-Behandlung [Section titled “Kritisch: Redirect-Behandlung”](#kritisch-redirect-behandlung) Kritisch: Redirect-Behandlung Bei Verwendung von Google-Login im Web **MÜSSEN** Sie beim Redirect eine beliebige Funktion des Plugins aufrufen, um das Plugin zu initialisieren, damit es den Redirect behandeln und das Popup-Fenster schließen kann. Sie können entweder `isLoggedIn()` ODER `initialize()` aufrufen - beide lösen die Redirect-Behandlung aus. Dies ist für den OAuth-Popup-Ablauf unerlässlich. ### Implementierungsbeispiel [Section titled “Implementierungsbeispiel”](#implementierungsbeispiel) Fügen Sie dies Ihrer Komponente hinzu, die Google Sign-In behandelt: ```typescript import { useEffect } from 'react'; import { SocialLogin } from '@capgo/capacitor-social-login'; function SupabasePage() { // Google-Login-Status beim Mounten überprüfen, um Redirect-Behandlung auszulösen // Dies dient keinem funktionalen Zweck in der UI, stellt aber sicher, // dass ausstehende OAuth-Redirects ordnungsgemäß verarbeitet werden useEffect(() => { const checkGoogleLoginStatus = async () => { try { await SocialLogin.isLoggedIn({ provider: 'google' }); // Wir verwenden das Ergebnis nicht, dies dient nur zur Redirect-Behandlung } catch (error) { // Fehler ignorieren - dies dient nur zur Redirect-Behandlung console.log('Google-Login-Statusüberprüfung abgeschlossen (Redirect-Behandlung)'); } }; checkGoogleLoginStatus(); }, []); // ... Rest Ihrer Komponente } ``` Siehe [SupabasePage.tsx](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/SupabasePage.tsx) für ein vollständiges Beispiel. ## Wie es funktioniert [Section titled “Wie es funktioniert”](#wie-es-funktioniert) Eine detaillierte Erklärung der Funktionsweise des Authentifizierungsablaufs, einschließlich Nonce-Generierung, JWT-Validierung und Supabase-Anmeldung, finden Sie im [Abschnitt “Wie es funktioniert” im Allgemeinen Einrichtungsleitfaden](/docs/plugins/social-login/supabase/google/general/#how-it-works). Die vollständige Code-Referenz finden Sie im [Abschnitt “Vollständige Code-Referenz” im Allgemeinen Einrichtungsleitfaden](/docs/plugins/social-login/supabase/google/general/#complete-code-reference). Siehe auch: * [SupabasePage.tsx](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/SupabasePage.tsx) - Beispielkomponente mit Redirect-Handling * [SupabaseCreateAccountPage.tsx](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/SupabaseCreateAccountPage.tsx) - Beispiel-Kontoerstellungsseite ## Wichtige Hinweise [Section titled “Wichtige Hinweise”](#wichtige-hinweise) ### Redirect-Behandlung [Section titled “Redirect-Behandlung”](#redirect-behandlung) Bei Verwendung von Google-Login im Web **MÜSSEN** Sie beim Redirect eine beliebige Funktion des Plugins aufrufen, um das Plugin zu initialisieren, damit es den Redirect behandeln und das Popup-Fenster schließen kann. Sie können entweder `isLoggedIn()` ODER `initialize()` aufrufen - beide lösen die Redirect-Behandlung aus. Dies ist für den OAuth-Popup-Ablauf unerlässlich. Ohne dies wird das Popup-Fenster nach der Authentifizierung nicht geschlossen. ### Nonce-Behandlung [Section titled “Nonce-Behandlung”](#nonce-behandlung) Die Nonce-Implementierung folgt dem Muster aus der [React Native Google Sign In Dokumentation](https://react-native-google-signin.github.io/docs/security#usage-with-supabase): * `rawNonce` geht zu Supabase’s `signInWithIdToken()` * Supabase erstellt einen Hash von `rawNonce` und vergleicht ihn mit dem `nonceDigest`, der im ID-Token von Google Sign-In enthalten ist * `nonceDigest` (SHA-256-Hash, hex-codiert) geht zum `nonce`-Parameter in Google Sign-In APIs ### Automatischer Wiederholungsversuch [Section titled “Automatischer Wiederholungsversuch”](#automatischer-wiederholungsversuch) Die Implementierung umfasst automatische Wiederholungslogik: * Wenn die JWT-Validierung beim ersten Versuch fehlschlägt, meldet sie sich ab und versucht es einmal erneut * Dies behandelt Fälle, in denen zwischengespeicherte Tokens möglicherweise falsche Nonces haben * Wenn der Wiederholungsversuch ebenfalls fehlschlägt, wird ein Fehler zurückgegeben ## Fehlerbehebung [Section titled “Fehlerbehebung”](#fehlerbehebung) Wenn die Authentifizierung fehlschlägt: * **Redirect funktioniert nicht**: Stellen Sie sicher, dass Sie `isLoggedIn()` beim Mounten der Komponente aufrufen (siehe Beispiel oben) * **Ungültiges Publikum**: Überprüfen Sie, ob Ihre Google Client IDs sowohl in der Google Cloud Console als auch in Supabase übereinstimmen * **Autorisierte Redirect-URLs**: Überprüfen Sie, dass autorisierte Redirect-URLs sowohl in der Google Cloud Console als auch in Supabase konfiguriert sind * **Nonce-Fehlanpassung**: Überprüfen Sie die Konsolenprotokolle - die Funktion versucht es automatisch erneut, aber Sie können sich bei Bedarf zuerst manuell abmelden * **Token-Validierung schlägt fehl**: Stellen Sie sicher, dass Sie `mode: 'online'` im Initialize-Aufruf verwenden, um ein idToken zu erhalten * Überprüfen Sie den [Beispiel-App-Code](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/supabaseAuthUtils.ts) als Referenz # Einführung in die Supabase-Integration > Erfahren Sie, wie Sie Supabase Authentication mit dem Capacitor Social Login Plugin für eine vollständige Authentifizierungslösung integrieren. ## Übersicht [Section titled “Übersicht”](#übersicht) Dieses Tutorial führt Sie durch die Einrichtung von Supabase Authentication mit dem Capacitor Social Login Plugin. Diese Integration ermöglicht es Ihnen, native soziale Login-Anbieter (Google, Apple, Facebook, Twitter) auf mobilen Plattformen zu verwenden, während Sie Supabase Auth für Backend-Authentifizierung und PostgreSQL für Datenspeicherung nutzen. ## Was Sie lernen werden [Section titled “Was Sie lernen werden”](#was-sie-lernen-werden) * Wie Sie Supabase Authentication konfigurieren * Wie Sie das Capacitor Social Login Plugin mit Supabase Auth integrieren * Plattformspezifische Einrichtung für Android, iOS und Web * Wie Sie Nonces sicher für Supabase handhaben ## Was Sie benötigen [Section titled “Was Sie benötigen”](#was-sie-benötigen) Bevor Sie beginnen, stellen Sie sicher, dass Sie Folgendes haben: 1. **Ein Supabase-Projekt** * Erstellen Sie ein Projekt im [Supabase Dashboard](https://app.supabase.com/) * Aktivieren Sie den Google OAuth-Provider * Holen Sie sich Ihre Supabase-Projekt-URL und Anon Key 2. **Supabase JS SDK** * Installieren Sie Supabase in Ihrem Projekt: ```bash npm install @supabase/supabase-js ``` 3. **Ein Capacitor-Projekt** * Eine bestehende Capacitor-Anwendung * Capacitor Social Login Plugin installiert: ```bash npm install @capgo/capacitor-social-login npx cap sync ``` 4. **Plattformspezifische Google-Einrichtung** * Vervollständigen Sie die Google Sign-In-Einrichtung für Ihre Zielplattformen: * [Google Login Android-Einrichtung](/docs/plugins/social-login/google/android/) * [Google Login iOS-Einrichtung](/docs/plugins/social-login/google/ios/) * [Google Login Web-Einrichtung](/docs/plugins/social-login/google/web/) ## Beispielanwendung [Section titled “Beispielanwendung”](#beispielanwendung) Eine vollständige funktionsfähige Beispielanwendung ist im Repository verfügbar: **Code-Repository**: [Sie finden das Code-Repository hier](https://github.com/Cap-go/capacitor-social-login/tree/main/example-app) Die Beispiel-App demonstriert: * E-Mail/Passwort-Authentifizierung mit Supabase * Google Sign-In-Integration (Android, iOS und Web) * Einen einfachen Schlüssel-Wert-Speicher unter Verwendung von Supabase PostgreSQL-Tabellen * Benutzerspezifische Datenspeicherung mit Row Level Security (RLS) ## Wichtige Implementierungsdetails [Section titled “Wichtige Implementierungsdetails”](#wichtige-implementierungsdetails) ### Nonce-Behandlung [Section titled “Nonce-Behandlung”](#nonce-behandlung) Supabase erfordert eine spezielle Nonce-Behandlung für die Sicherheit. Die Implementierung folgt der [React Native Google Sign In Dokumentation](https://react-native-google-signin.github.io/docs/security#usage-with-supabase): * Generieren Sie eine `rawNonce` (URL-sichere Zufallszeichenkette) * Hashen Sie sie mit SHA-256, um `nonceDigest` zu erhalten * Übergeben Sie `nonceDigest` an Google Sign-In * Übergeben Sie `rawNonce` an Supabase (Supabase hasht sie intern für den Vergleich) ### JWT-Validierung [Section titled “JWT-Validierung”](#jwt-validierung) Die Beispielimplementierung umfasst JWT-Validierung, um Folgendes sicherzustellen: * Das Token-Publikum stimmt mit Ihren konfigurierten Google Client IDs überein * Die Nonce stimmt mit dem erwarteten Digest überein * Automatischer Wiederholungsversuch bei Validierungsfehlern (besonders wichtig für iOS) ### Plattformspezifische Überlegungen [Section titled “Plattformspezifische Überlegungen”](#plattformspezifische-überlegungen) * **iOS**: Token-Caching kann Nonce-Probleme verursachen - die Implementierung behandelt dies automatisch * **Web**: Muss `isLoggedIn()` beim Mounten aufrufen, um OAuth-Redirects zu handhaben * **Android**: Standard-Implementierung mit SHA-1-Fingerabdruck-Konfiguration ## Nächste Schritte [Section titled “Nächste Schritte”](#nächste-schritte) Fahren Sie mit den Einrichtungsleitfäden fort: * [Supabase Google Login - Allgemeine Einrichtung](./google/general/) - Übersicht und Voraussetzungen * [Android-Einrichtung](./google/android/) - Android-spezifische Konfiguration * [iOS-Einrichtung](./google/ios/) - iOS-spezifische Konfiguration * [Web-Einrichtung](./google/web/) - Web-spezifische Konfiguration # @capgo/capacitor-speech-recognition > Umfassende gerätebasierte Spracherkennung mit Echtzeit-Transkription, gestreamten Teilergebnissen und plattformübergreifender Parität. Echtzeit-Transkription Erhalten Sie sofortige Teilergebnisse, während Benutzer sprechen Plattformübergreifende Parität Konsistente API und Verhalten über iOS und Android hinweg Segmentierte Sitzungen Teilen Sie die Erkennung basierend auf Stille in Segmente auf Interpunktionsunterstützung Automatische Interpunktion auf iOS 16+ Berechtigungs-Helfer Eingebaute Berechtigungsanfrage und Statusprüfung Sprachunterstützung Zugriff auf die vom Gerät unterstützten Erkennungssprachen Erste Schritte Schauen Sie sich den [Leitfaden für die ersten Schritte](/docs/plugins/speech-recognition/getting-started/) an, um das Plugin zu installieren und zu konfigurieren. # Erste Schritte > Erfahren Sie, wie Sie das Speech Recognition Plugin installieren und verwenden, um Sprachtranskription zu Ihrer Capacitor-App hinzuzufügen 1. **Paket installieren** * npm ```sh npm i @capgo/capacitor-speech-recognition ``` * pnpm ```sh pnpm add @capgo/capacitor-speech-recognition ``` * yarn ```sh yarn add @capgo/capacitor-speech-recognition ``` * bun ```sh bun add @capgo/capacitor-speech-recognition ``` 2. **Mit nativen Projekten synchronisieren** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` 3. **Plattformberechtigungen konfigurieren** (siehe unten) ## Plattformkonfiguration [Section titled “Plattformkonfiguration”](#plattformkonfiguration) ### iOS [Section titled “iOS”](#ios) Fügen Sie die folgenden Schlüssel zur `Info.plist`-Datei Ihrer App hinzu: ```xml NSSpeechRecognitionUsageDescription Wir benötigen Zugriff auf die Spracherkennung, um Ihre Stimme zu transkribieren NSMicrophoneUsageDescription Wir benötigen Zugriff auf Ihr Mikrofon, um Audio für die Transkription aufzunehmen ``` ### Android [Section titled “Android”](#android) Das Plugin fügt automatisch die erforderliche `RECORD_AUDIO`-Berechtigung zu Ihrer `AndroidManifest.xml` hinzu. Es ist keine zusätzliche Konfiguration erforderlich. ## Grundlegende Verwendung [Section titled “Grundlegende Verwendung”](#grundlegende-verwendung) ### Verfügbarkeit überprüfen [Section titled “Verfügbarkeit überprüfen”](#verfügbarkeit-überprüfen) Überprüfen Sie vor der Verwendung der Spracherkennung, ob sie auf dem Gerät verfügbar ist: ```typescript import { SpeechRecognition } from '@capgo/capacitor-speech-recognition'; const checkAvailability = async () => { const { available } = await SpeechRecognition.available(); if (!available) { console.warn('Spracherkennung wird auf diesem Gerät nicht unterstützt'); return false; } return true; }; ``` ### Berechtigungen anfordern [Section titled “Berechtigungen anfordern”](#berechtigungen-anfordern) Fordern Sie die erforderlichen Berechtigungen an, bevor Sie die Erkennung starten: ```typescript const requestPermissions = async () => { const { speechRecognition } = await SpeechRecognition.requestPermissions(); if (speechRecognition === 'granted') { console.log('Berechtigung erteilt'); return true; } else { console.log('Berechtigung verweigert'); return false; } }; ``` ### Erkennung starten [Section titled “Erkennung starten”](#erkennung-starten) Beginnen Sie mit dem Hören auf Sprache mit optionaler Konfiguration: ```typescript // Grundlegende Verwendung await SpeechRecognition.start({ language: 'de-DE', maxResults: 3, partialResults: true, }); // Mit allen Optionen await SpeechRecognition.start({ language: 'de-DE', maxResults: 5, prompt: 'Jetzt sprechen...', // Nur Android popup: false, // Nur Android partialResults: true, addPunctuation: true, // Nur iOS 16+ allowForSilence: 2000, // Nur Android, Millisekunden }); ``` ### Auf Ergebnisse hören [Section titled “Auf Ergebnisse hören”](#auf-ergebnisse-hören) Abonnieren Sie Teilergebnisse, während die Erkennung aktiv ist: ```typescript const partialListener = await SpeechRecognition.addListener( 'partialResults', (event) => { const transcription = event.matches?.[0]; console.log('Teilergebnis:', transcription); } ); // Vergessen Sie nicht, den Listener zu entfernen, wenn Sie fertig sind await partialListener.remove(); ``` ### Erkennung stoppen [Section titled “Erkennung stoppen”](#erkennung-stoppen) Hören Sie auf zuzuhören und räumen Sie Ressourcen auf: ```typescript await SpeechRecognition.stop(); ``` ## Vollständiges Beispiel [Section titled “Vollständiges Beispiel”](#vollständiges-beispiel) Hier ist ein vollständiges Beispiel, das zeigt, wie das Plugin verwendet wird: ```typescript import { SpeechRecognition } from '@capgo/capacitor-speech-recognition'; export class VoiceRecognitionService { private partialListener: any = null; private isListening = false; async initialize(): Promise { // Verfügbarkeit überprüfen const { available } = await SpeechRecognition.available(); if (!available) { throw new Error('Spracherkennung nicht verfügbar'); } // Berechtigungen anfordern const { speechRecognition } = await SpeechRecognition.requestPermissions(); if (speechRecognition !== 'granted') { throw new Error('Berechtigung verweigert'); } return true; } async startListening( onPartialResult: (text: string) => void, onFinalResult: (text: string) => void ): Promise { if (this.isListening) { console.warn('Hört bereits zu'); return; } try { // Teilergebnisse-Listener einrichten this.partialListener = await SpeechRecognition.addListener( 'partialResults', (event) => { const text = event.matches?.[0] || ''; onPartialResult(text); } ); // Erkennung starten const result = await SpeechRecognition.start({ language: 'de-DE', maxResults: 3, partialResults: true, addPunctuation: true, }); this.isListening = true; // Endergebnis behandeln, wenn partialResults false ist if (result.matches && result.matches.length > 0) { onFinalResult(result.matches[0]); } } catch (error) { console.error('Fehler beim Starten der Spracherkennung:', error); throw error; } } async stopListening(): Promise { if (!this.isListening) { return; } try { await SpeechRecognition.stop(); // Listener aufräumen if (this.partialListener) { await this.partialListener.remove(); this.partialListener = null; } this.isListening = false; } catch (error) { console.error('Fehler beim Stoppen der Spracherkennung:', error); throw error; } } async getSupportedLanguages(): Promise { const { languages } = await SpeechRecognition.getSupportedLanguages(); return languages; } async checkListeningState(): Promise { const { listening } = await SpeechRecognition.isListening(); return listening; } } ``` ## API-Referenz [Section titled “API-Referenz”](#api-referenz) ### available() [Section titled “available()”](#available) Überprüft, ob der native Spracherkennungsdienst auf dem aktuellen Gerät verwendbar ist. ```typescript const result = await SpeechRecognition.available(); // Gibt zurück: { available: boolean } ``` ### start(options?) [Section titled “start(options?)”](#startoptions) Beginnt mit der Aufnahme von Audio und der Transkription von Sprache. ```typescript interface SpeechRecognitionStartOptions { language?: string; // Locale-Kennung (z.B. 'de-DE') maxResults?: number; // Maximale Anzahl von Ergebnissen (Standard: 5) prompt?: string; // Nur Android: Dialog-Aufforderung popup?: boolean; // Nur Android: System-Dialog anzeigen partialResults?: boolean; // Teilergebnisse streamen addPunctuation?: boolean; // Nur iOS 16+: Interpunktion hinzufügen allowForSilence?: number; // Nur Android: Stille-Timeout in ms } const result = await SpeechRecognition.start(options); // Gibt zurück: { matches?: string[] } ``` ### stop() [Section titled “stop()”](#stop) Stoppt das Zuhören und räumt native Ressourcen auf. ```typescript await SpeechRecognition.stop(); ``` ### getSupportedLanguages() [Section titled “getSupportedLanguages()”](#getsupportedlanguages) Ruft die vom zugrunde liegenden Erkennungsdienst unterstützten Locales ab. **Hinweis**: Android 13+-Geräte stellen diese Liste nicht mehr bereit; in diesem Fall ist `languages` leer. ```typescript const result = await SpeechRecognition.getSupportedLanguages(); // Gibt zurück: { languages: string[] } ``` ### isListening() [Section titled “isListening()”](#islistening) Gibt zurück, ob das Plugin aktiv auf Sprache hört. ```typescript const result = await SpeechRecognition.isListening(); // Gibt zurück: { listening: boolean } ``` ### checkPermissions() [Section titled “checkPermissions()”](#checkpermissions) Ruft den aktuellen Berechtigungsstatus ab. ```typescript const result = await SpeechRecognition.checkPermissions(); // Gibt zurück: { speechRecognition: 'prompt' | 'prompt-with-rationale' | 'granted' | 'denied' } ``` ### requestPermissions() [Section titled “requestPermissions()”](#requestpermissions) Fordert die Mikrofon- + Spracherkennungsberechtigungen an. ```typescript const result = await SpeechRecognition.requestPermissions(); // Gibt zurück: { speechRecognition: 'prompt' | 'prompt-with-rationale' | 'granted' | 'denied' } ``` ## Event-Listener [Section titled “Event-Listener”](#event-listener) ### partialResults [Section titled “partialResults”](#partialresults) Hören Sie auf Teilergebnisse der Transkription, während `partialResults` aktiviert ist. ```typescript const listener = await SpeechRecognition.addListener( 'partialResults', (event: { matches: string[] }) => { console.log('Teilweise:', event.matches?.[0]); } ); ``` ### segmentResults (nur Android) [Section titled “segmentResults (nur Android)”](#segmentresults-nur-android) Hören Sie auf segmentierte Erkennungsergebnisse. ```typescript const listener = await SpeechRecognition.addListener( 'segmentResults', (event: { matches: string[] }) => { console.log('Segment:', event.matches?.[0]); } ); ``` ### endOfSegmentedSession (nur Android) [Section titled “endOfSegmentedSession (nur Android)”](#endofsegmentedsession-nur-android) Hören Sie auf Abschluss-Events der segmentierten Sitzung. ```typescript const listener = await SpeechRecognition.addListener( 'endOfSegmentedSession', () => { console.log('Segmentierte Sitzung beendet'); } ); ``` ### listeningState [Section titled “listeningState”](#listeningstate) Hören Sie auf Änderungen des nativen Listening-Status. ```typescript const listener = await SpeechRecognition.addListener( 'listeningState', (event: { status: 'started' | 'stopped' }) => { console.log('Listening-Status:', event.status); } ); ``` ### removeAllListeners() [Section titled “removeAllListeners()”](#removealllisteners) Entfernt alle registrierten Listener. ```typescript await SpeechRecognition.removeAllListeners(); ``` ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) 1. **Immer Verfügbarkeit und Berechtigungen überprüfen** ```typescript const { available } = await SpeechRecognition.available(); if (!available) return; const { speechRecognition } = await SpeechRecognition.requestPermissions(); if (speechRecognition !== 'granted') return; ``` 2. **Listener aufräumen** Entfernen Sie Listener immer, wenn sie nicht mehr benötigt werden, um Speicherlecks zu vermeiden: ```typescript await listener.remove(); // oder await SpeechRecognition.removeAllListeners(); ``` 3. **Fehler elegant behandeln** ```typescript try { await SpeechRecognition.start({ language: 'de-DE' }); } catch (error) { console.error('Spracherkennung fehlgeschlagen:', error); // Benutzerfreundliche Fehlermeldung anzeigen } ``` 4. **Visuelles Feedback bereitstellen** Verwenden Sie das `listeningState`-Event, um Benutzern zu zeigen, wann die App aktiv zuhört. 5. **Mit verschiedenen Akzenten und Sprachen testen** Die Genauigkeit der Spracherkennung variiert je nach Sprache und Akzent. Testen Sie gründlich mit Ihrer Zielgruppe. ## Plattformhinweise [Section titled “Plattformhinweise”](#plattformhinweise) ### iOS [Section titled “iOS”](#ios-1) * Erfordert iOS 10.0+ * Verwendet natives `SFSpeechRecognizer` * Unterstützt Interpunktion auf iOS 16+ * Erfordert sowohl Mikrofon- als auch Spracherkennungsberechtigungen * Erkennung kann fehlschlagen, wenn die Gerätesprache nicht mit der angeforderten Sprache übereinstimmt ### Android [Section titled “Android”](#android-1) * Erfordert Android 6.0 (API 23)+ * Verwendet `SpeechRecognizer` API * Unterstützt segmentierte Sitzungen mit konfigurierbarer Stilleerkennung * Android 13+ stellt keine Liste unterstützter Sprachen bereit * Einige Geräte zeigen möglicherweise die System-Erkennungs-UI an ### Web [Section titled “Web”](#web) * Eingeschränkte Unterstützung über Web Speech API * Nicht alle Browser unterstützen Spracherkennung * Erfordert HTTPS-Verbindung * Kann unterschiedliches Verhalten über Browser hinweg haben ## Fehlerbehebung [Section titled “Fehlerbehebung”](#fehlerbehebung) ### Berechtigung verweigert [Section titled “Berechtigung verweigert”](#berechtigung-verweigert) Wenn Berechtigungen verweigert werden, führen Sie Benutzer zu den App-Einstellungen: ```typescript const { speechRecognition } = await SpeechRecognition.checkPermissions(); if (speechRecognition === 'denied') { // Anweisungen zum Aktivieren von Berechtigungen in den Einstellungen anzeigen } ``` ### Keine Ergebnisse zurückgegeben [Section titled “Keine Ergebnisse zurückgegeben”](#keine-ergebnisse-zurückgegeben) * Mikrofon funktioniert überprüfen * Ruhige Umgebung sicherstellen * Sprachcode mit Gerätefunktionen überprüfen * Netzwerkverbindung prüfen (einige Plattformen erfordern sie) ### Erkennung stoppt unerwartet [Section titled “Erkennung stoppt unerwartet”](#erkennung-stoppt-unerwartet) * `isListening()` verwenden, um Status zu überprüfen * Auf `listeningState`-Events hören * Automatischen Neustart implementieren, falls erforderlich ## Nächste Schritte [Section titled “Nächste Schritte”](#nächste-schritte) * [API-Referenz auf GitHub ansehen](https://github.com/Cap-go/capacitor-speech-recognition#api) * [Probleme melden](https://github.com/Cap-go/capacitor-speech-recognition/issues) * [Beispiel-App erkunden](https://github.com/Cap-go/capacitor-speech-recognition/tree/main/example) # @capgo/capacitor-speech-synthesis > Konvertieren Sie Text zu Sprache mit voller Kontrolle über Sprache, Stimme, Tonhöhe, Geschwindigkeit und Lautstärke auf iOS, Android und Web. Mehrere Sprachen Unterstützung für Dutzende von Sprachen und regionalen Varianten 🌍 Stimmauswahl Wählen Sie aus verfügbaren Systemstimmen für jede Sprache 🎤 Feinsteuerung Passen Sie Tonhöhe, Geschwindigkeit und Lautstärke an, um die Sprachausgabe anzupassen ⚙️ Plattformübergreifend Konsistente API auf iOS-, Android- und Web-Plattformen 📱 Echtzeit-Steuerung Pausieren, fortsetzen und brechen Sie die Sprachausgabe spontan ab 🎮 Umfassende Dokumentation Lesen Sie die [Dokumentation](/docs/plugins/speech-synthesis/getting-started/), um das Plugin in wenigen Minuten zu beherrschen. # Erste Schritte > Erfahren Sie, wie Sie das Sprachsynthese-Plugin installieren und verwenden, um Text in Ihrer Capacitor-App in Sprache umzuwandeln. 1. **Paket installieren** * npm ```sh npm i @capgo/capacitor-speech-synthesis ``` * pnpm ```sh pnpm add @capgo/capacitor-speech-synthesis ``` * yarn ```sh yarn add @capgo/capacitor-speech-synthesis ``` * bun ```sh bun add @capgo/capacitor-speech-synthesis ``` 2. **Mit nativen Projekten synchronisieren** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Nutzung [Section titled “Nutzung”](#nutzung) Importieren Sie das Plugin und synthetisieren Sie Sprache aus Text: ```typescript import { SpeechSynthesis } from '@capgo/capacitor-speech-synthesis'; // Basic text-to-speech const speak = async () => { await SpeechSynthesis.speak({ text: 'Hello, world!', language: 'en-US' }); }; // Advanced usage with voice control const speakWithVoice = async () => { await SpeechSynthesis.speak({ text: 'This is a test of speech synthesis.', language: 'en-US', rate: 1.0, // Speech rate: 0.1 to 10.0 pitch: 1.0, // Voice pitch: 0.5 to 2.0 volume: 1.0, // Volume: 0.0 to 1.0 voice: 'com.apple.ttsbundle.Samantha-compact' }); }; ``` ## API Referenz [Section titled “API Referenz”](#api-referenz) ### sprechen(Optionen) [Section titled “sprechen(Optionen)”](#sprechenoptionen) Spricht den angegebenen Text mit den angegebenen Spracheinstellungen. ```typescript interface SpeakOptions { text: string; // Text to speak language?: string; // Language code (e.g., 'en-US', 'es-ES') rate?: number; // Speech rate: 0.1 to 10.0 (default: 1.0) pitch?: number; // Voice pitch: 0.5 to 2.0 (default: 1.0) volume?: number; // Volume: 0.0 to 1.0 (default: 1.0) voice?: string; // Voice identifier (get from getVoices()) } await SpeechSynthesis.speak(options); ``` ### stop() [Section titled “stop()”](#stop) Stoppt jede laufende Sprachsynthese. ```typescript await SpeechSynthesis.stop(); ``` ### getVoices() [Section titled “getVoices()”](#getvoices) Ruft die Liste der verfügbaren Stimmen für die Sprachsynthese ab. ```typescript interface Voice { voiceURI: string; // Unique voice identifier name: string; // Human-readable voice name lang: string; // Language code default: boolean; // Whether this is the default voice } const { voices } = await SpeechSynthesis.getVoices(); console.log('Available voices:', voices); ``` ### getSupportedLanguages() [Section titled “getSupportedLanguages()”](#getsupportedlanguages) Ruft die Liste der unterstützten Sprachcodes ab. ```typescript const { languages } = await SpeechSynthesis.getSupportedLanguages(); console.log('Supported languages:', languages); ``` ### isSpeaking() [Section titled “isSpeaking()”](#isspeaking) Überprüft, ob die Sprachsynthese derzeit aktiv ist. ```typescript const { speaking } = await SpeechSynthesis.isSpeaking(); console.log('Currently speaking:', speaking); ``` ## Vollständiges Beispiel [Section titled “Vollständiges Beispiel”](#vollständiges-beispiel) ```typescript import { SpeechSynthesis } from '@capgo/capacitor-speech-synthesis'; export class TextToSpeechService { private currentVoice: string | undefined; async init() { // Get available voices const { voices } = await SpeechSynthesis.getVoices(); // Find English voice const englishVoice = voices.find(v => v.lang.startsWith('en')); this.currentVoice = englishVoice?.voiceURI; console.log(`Using voice: ${englishVoice?.name}`); } async speak(text: string, options?: Partial) { try { // Stop any ongoing speech await SpeechSynthesis.stop(); // Speak the text await SpeechSynthesis.speak({ text, language: 'en-US', rate: 1.0, pitch: 1.0, volume: 1.0, voice: this.currentVoice, ...options }); } catch (error) { console.error('Speech synthesis failed:', error); throw error; } } async speakWithRate(text: string, rate: number) { await this.speak(text, { rate }); } async stop() { await SpeechSynthesis.stop(); } async checkIfSpeaking(): Promise { const { speaking } = await SpeechSynthesis.isSpeaking(); return speaking; } async listVoicesByLanguage(languageCode: string) { const { voices } = await SpeechSynthesis.getVoices(); return voices.filter(v => v.lang.startsWith(languageCode)); } } ``` ## Erweiterte Nutzung [Section titled “Erweiterte Nutzung”](#erweiterte-nutzung) ### Mehrsprachige Unterstützung [Section titled “Mehrsprachige Unterstützung”](#mehrsprachige-unterstützung) ```typescript // Speak in different languages const speakMultiLanguage = async () => { await SpeechSynthesis.speak({ text: 'Hello!', language: 'en-US' }); await SpeechSynthesis.speak({ text: 'Bonjour!', language: 'fr-FR' }); await SpeechSynthesis.speak({ text: 'こんにちは!', language: 'ja-JP' }); }; ``` ### Sprachauswahl [Section titled “Sprachauswahl”](#sprachauswahl) ```typescript // Select a specific voice const selectVoice = async (languageCode: string) => { const { voices } = await SpeechSynthesis.getVoices(); // Filter voices by language const languageVoices = voices.filter(v => v.lang.startsWith(languageCode) ); // Use the first available voice if (languageVoices.length > 0) { await SpeechSynthesis.speak({ text: 'Testing voice selection', language: languageCode, voice: languageVoices[0].voiceURI }); } }; ``` ### Langtext lesen [Section titled “Langtext lesen”](#langtext-lesen) ```typescript // Split and speak long text in chunks const speakLongText = async (longText: string) => { const sentences = longText.match(/[^.!?]+[.!?]+/g) || [longText]; for (const sentence of sentences) { await SpeechSynthesis.speak({ text: sentence.trim(), language: 'en-US', rate: 0.9 }); // Wait a bit between sentences await new Promise(resolve => setTimeout(resolve, 500)); } }; ``` ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) 1. **Stimmenauswahl**: Überprüfen Sie immer die verfügbaren Stimmen, bevor Sie eine bestimmte Stimmen-ID verwenden 2. **Sprachcodes**: Verwenden Sie Standard-Sprachcodes (z. B. „en-US“, „es-ES“, „fr-FR“). 3. **Ratensteuerung**: Halten Sie die Rate zwischen 0,5 und 2,0 für eine natürlich klingende Sprache 4. **Vor dem Sprechen stoppen**: Rufen Sie stop() auf, bevor Sie mit einer neuen Rede beginnen, um Überschneidungen zu vermeiden 5. **Fehlerbehandlung**: Umfassen Sie alle Aufrufe in Try-Catch-Blöcken für eine robuste Fehlerbehandlung ## Fehlerbehebung [Section titled “Fehlerbehebung”](#fehlerbehebung) ### Häufige Probleme [Section titled “Häufige Probleme”](#häufige-probleme) **Keine Sprachausgabe**: Überprüfen Sie die Lautstärke des Geräts und stellen Sie sicher, dass der Text nicht leer ist **Stimme nicht gefunden**: Verwenden Sie getVoices(), um verfügbare Stimmen auf dem Gerät zu finden **Sprache unterbrochen**: Stoppen Sie jede laufende Sprache, bevor Sie mit der neuen Synthese beginnen **Sprache nicht unterstützt**: Überprüfen Sie getSupportedLanguages() auf verfügbare Sprachen # @capgo/capacitor-stream-call > Aktivieren Sie professionelle Video-Anruffunktionalität in Ihrer App mit dem Stream Video SDK mit Vollbild-Support und Anrufer-Informationen. Stream Video SDK Unterstützt durch Streams professionelle Video-Infrastruktur 🎥 Vollbild-Anrufe Sperrbildschirm- und Vollbild-Anrufunterstützung 📱 Anrufer-Informationen Zeigen Sie Anrufername und Profilbild an 👤 Kamera-Steuerung Wechseln Sie Kameras und schalten Sie Video/Audio um 📹 Anrufverwaltung Akzeptieren, lehnen Sie ab und verwalten Sie Anrufe nahtlos 📞 Mehrsprachig Lokalisierungsunterstützung für iOS und Android 🌍 Erste Schritte Lesen Sie den [Leitfaden für Erste Schritte](/docs/plugins/streamcall/getting-started/), um das Plugin zu installieren und zu konfigurieren. # Erste Schritte > Erfahren Sie, wie Sie das Stream Video SDK für professionelle Videoanrufe in Ihrer Capacitor-App installieren und integrieren. ## Installation [Section titled “Installation”](#installation) * npm ```bash npm install @capgo/capacitor-stream-call npx cap sync ``` * yarn ```bash yarn add @capgo/capacitor-stream-call npx cap sync ``` * pnpm ```bash pnpm add @capgo/capacitor-stream-call npx cap sync ``` * bun ```bash bun add @capgo/capacitor-stream-call npx cap sync ``` ## Voraussetzungen [Section titled “Voraussetzungen”](#voraussetzungen) Sie benötigen ein Stream-Konto und API-Anmeldeinformationen. Registrieren Sie sich bei [Stream](https://getstream.io/), falls Sie noch kein Konto haben. ## Plattform-Konfiguration [Section titled “Plattform-Konfiguration”](#plattform-konfiguration) ### iOS [Section titled “iOS”](#ios) Fügen Sie die erforderlichen Berechtigungen zu Ihrer `Info.plist` hinzu: ```xml NSCameraUsageDescription Diese App benötigt Kamerazugriff für Videoanrufe NSMicrophoneUsageDescription Diese App benötigt Mikrofonzugriff für Videoanrufe ``` ### Android [Section titled “Android”](#android) Fügen Sie die erforderlichen Berechtigungen zu Ihrer `AndroidManifest.xml` hinzu: ```xml ``` ## Verwendungsbeispiel [Section titled “Verwendungsbeispiel”](#verwendungsbeispiel) ```typescript import { StreamCall } from '@capgo/capacitor-stream-call'; // Stream SDK initialisieren await StreamCall.initialize({ apiKey: 'your-stream-api-key', userId: 'user-123', userToken: 'user-token' }); // Einen Anruf erstellen await StreamCall.createCall({ callId: 'call-123', callType: 'default' }); // Einem Anruf beitreten await StreamCall.joinCall({ callId: 'call-123' }); // Kamera aktivieren/deaktivieren await StreamCall.toggleCamera({ enabled: true }); // Mikrofon aktivieren/deaktivieren await StreamCall.toggleMicrophone({ enabled: true }); // Kamera wechseln (vorne/hinten) await StreamCall.switchCamera(); // Den Anruf verlassen await StreamCall.leaveCall(); // Auf Anrufereignisse hören StreamCall.addListener('callStarted', (data) => { console.log('Call started:', data); }); StreamCall.addListener('callEnded', (data) => { console.log('Call ended:', data); }); StreamCall.addListener('participantJoined', (data) => { console.log('Participant joined:', data); }); StreamCall.addListener('participantLeft', (data) => { console.log('Participant left:', data); }); ``` ## API-Referenz [Section titled “API-Referenz”](#api-referenz) ### initialize(options) [Section titled “initialize(options)”](#initializeoptions) ```typescript initialize(options: InitializeOptions) => Promise ``` Initialisiert das Stream Video SDK. | Param | Type | | ------------- | ------------------- | | **`options`** | `InitializeOptions` | ### createCall(options) [Section titled “createCall(options)”](#createcalloptions) ```typescript createCall(options: CreateCallOptions) => Promise ``` Erstellt einen neuen Videoanruf. | Param | Type | | ------------- | ------------------- | | **`options`** | `CreateCallOptions` | ### joinCall(options) [Section titled “joinCall(options)”](#joincalloptions) ```typescript joinCall(options: JoinCallOptions) => Promise ``` Tritt einem bestehenden Videoanruf bei. | Param | Type | | ------------- | ----------------- | | **`options`** | `JoinCallOptions` | ### leaveCall() [Section titled “leaveCall()”](#leavecall) ```typescript leaveCall() => Promise ``` Verlässt den aktuellen Anruf. ### toggleCamera(options) [Section titled “toggleCamera(options)”](#togglecameraoptions) ```typescript toggleCamera(options: { enabled: boolean }) => Promise ``` Aktiviert oder deaktiviert die Kamera. | Param | Type | | ------------- | ---------------------- | | **`options`** | `{ enabled: boolean }` | ### toggleMicrophone(options) [Section titled “toggleMicrophone(options)”](#togglemicrophoneoptions) ```typescript toggleMicrophone(options: { enabled: boolean }) => Promise ``` Aktiviert oder deaktiviert das Mikrofon. | Param | Type | | ------------- | ---------------------- | | **`options`** | `{ enabled: boolean }` | ### switchCamera() [Section titled “switchCamera()”](#switchcamera) ```typescript switchCamera() => Promise ``` Wechselt zwischen Vorder- und Rückkamera. ### setSpeakerphone(options) [Section titled “setSpeakerphone(options)”](#setspeakerphoneoptions) ```typescript setSpeakerphone(options: { enabled: boolean }) => Promise ``` Aktiviert oder deaktiviert den Lautsprecher. | Param | Type | | ------------- | ---------------------- | | **`options`** | `{ enabled: boolean }` | ### sendCallInvite(options) [Section titled “sendCallInvite(options)”](#sendcallinviteoptions) ```typescript sendCallInvite(options: InviteOptions) => Promise ``` Sendet eine Anrufeinladung an einen Benutzer. | Param | Type | | ------------- | --------------- | | **`options`** | `InviteOptions` | ### acceptCall(options) [Section titled “acceptCall(options)”](#acceptcalloptions) ```typescript acceptCall(options: { callId: string }) => Promise ``` Nimmt einen eingehenden Anruf an. | Param | Type | | ------------- | -------------------- | | **`options`** | `{ callId: string }` | ### rejectCall(options) [Section titled “rejectCall(options)”](#rejectcalloptions) ```typescript rejectCall(options: { callId: string }) => Promise ``` Lehnt einen eingehenden Anruf ab. | Param | Type | | ------------- | -------------------- | | **`options`** | `{ callId: string }` | ## Schnittstellen [Section titled “Schnittstellen”](#schnittstellen) ### InitializeOptions [Section titled “InitializeOptions”](#initializeoptions-1) | Prop | Type | Beschreibung | | --------------- | -------- | --------------------------------------- | | **`apiKey`** | `string` | Stream API-Schlüssel | | **`userId`** | `string` | Benutzer-ID | | **`userToken`** | `string` | Benutzer-Authentifizierungstoken | | **`userName`** | `string` | Anzeigename des Benutzers (optional) | | **`userImage`** | `string` | Profilbild-URL des Benutzers (optional) | ### CreateCallOptions [Section titled “CreateCallOptions”](#createcalloptions-1) | Prop | Type | Beschreibung | | -------------- | ---------- | ---------------------------------------------- | | **`callId`** | `string` | Eindeutiger Anruf-Identifikator | | **`callType`** | `string` | Anruftyp (z.B. ‘default’, ‘audio-only’) | | **`members`** | `string[]` | Array von Benutzer-IDs zum Einladen (optional) | ### JoinCallOptions [Section titled “JoinCallOptions”](#joincalloptions-1) | Prop | Type | Beschreibung | | ------------ | -------- | ---------------------- | | **`callId`** | `string` | Anruf-ID zum Beitreten | ### InviteOptions [Section titled “InviteOptions”](#inviteoptions) | Prop | Type | Beschreibung | | ------------ | -------- | ------------------------ | | **`callId`** | `string` | Anruf-ID | | **`userId`** | `string` | Benutzer-ID zum Einladen | ## Event-Listener [Section titled “Event-Listener”](#event-listener) ### Verfügbare Ereignisse [Section titled “Verfügbare Ereignisse”](#verfügbare-ereignisse) * `callStarted` - Anruf wurde gestartet * `callEnded` - Anruf wurde beendet * `participantJoined` - Ein Teilnehmer ist dem Anruf beigetreten * `participantLeft` - Ein Teilnehmer hat den Anruf verlassen * `incomingCall` - Eingehender Anruf empfangen * `callAccepted` - Anruf wurde angenommen * `callRejected` - Anruf wurde abgelehnt * `error` - Ein Fehler ist aufgetreten ### Ereignis-Beispiel [Section titled “Ereignis-Beispiel”](#ereignis-beispiel) ```typescript // Auf eingehende Anrufe hören StreamCall.addListener('incomingCall', (data) => { console.log('Incoming call from:', data.callerId); console.log('Caller name:', data.callerName); // Eingehenden Anruf-UI anzeigen showIncomingCallScreen({ callerId: data.callerId, callerName: data.callerName, callerImage: data.callerImage, callId: data.callId }); }); // Auf Anrufannahme hören StreamCall.addListener('callAccepted', (data) => { console.log('Call accepted'); // Zum Anruf-Bildschirm navigieren }); // Auf Fehler hören StreamCall.addListener('error', (error) => { console.error('Call error:', error.message); // Fehler angemessen behandeln }); // Listener entfernen, wenn fertig const listener = await StreamCall.addListener('callStarted', (data) => { console.log('Call started'); }); // Später... listener.remove(); ``` ## Vollständiges Beispiel [Section titled “Vollständiges Beispiel”](#vollständiges-beispiel) ```typescript import { StreamCall } from '@capgo/capacitor-stream-call'; class VideoCallService { async initialize(userId: string, userName: string) { try { await StreamCall.initialize({ apiKey: 'your-stream-api-key', userId: userId, userToken: await this.getUserToken(userId), userName: userName }); this.setupEventListeners(); } catch (error) { console.error('Failed to initialize Stream:', error); } } setupEventListeners() { // Eingehende Anrufe behandeln StreamCall.addListener('incomingCall', async (data) => { const accepted = await this.showIncomingCallDialog(data); if (accepted) { await StreamCall.acceptCall({ callId: data.callId }); await StreamCall.joinCall({ callId: data.callId }); } else { await StreamCall.rejectCall({ callId: data.callId }); } }); // Anrufereignisse behandeln StreamCall.addListener('callStarted', () => { console.log('Call started'); }); StreamCall.addListener('callEnded', () => { console.log('Call ended'); this.navigateToHome(); }); StreamCall.addListener('participantJoined', (data) => { console.log('Participant joined:', data.participantName); }); } async startCall(recipientId: string) { try { const callId = `call-${Date.now()}`; // Anruf erstellen und beitreten await StreamCall.createCall({ callId: callId, callType: 'default', members: [recipientId] }); await StreamCall.joinCall({ callId: callId }); // Einladung senden await StreamCall.sendCallInvite({ callId: callId, userId: recipientId }); console.log('Call started'); } catch (error) { console.error('Failed to start call:', error); } } async endCall() { try { await StreamCall.leaveCall(); console.log('Call ended'); } catch (error) { console.error('Failed to end call:', error); } } async toggleVideo(enabled: boolean) { await StreamCall.toggleCamera({ enabled }); } async toggleAudio(enabled: boolean) { await StreamCall.toggleMicrophone({ enabled }); } async flipCamera() { await StreamCall.switchCamera(); } private async getUserToken(userId: string): Promise { // Benutzer-Token von Ihrem Backend abrufen const response = await fetch(`/api/stream-token?userId=${userId}`); const data = await response.json(); return data.token; } private async showIncomingCallDialog(data: any): Promise { // Nativen Dialog oder benutzerdefinierte UI anzeigen return confirm(`Incoming call from ${data.callerName}`); } private navigateToHome() { // Zum Startbildschirm navigieren window.location.href = '/'; } } // Verwendung const videoCall = new VideoCallService(); await videoCall.initialize('user-123', 'John Doe'); // Einen Anruf starten await videoCall.startCall('user-456'); // Steuerelemente umschalten await videoCall.toggleVideo(false); // Video deaktivieren await videoCall.toggleAudio(false); // Stummschalten await videoCall.flipCamera(); // Kamera wechseln // Anruf beenden await videoCall.endCall(); ``` ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) * Initialisieren Sie das SDK früh im Lebenszyklus Ihrer App * Berechtigungsanfragen vor dem Start von Anrufen behandeln * Implementieren Sie eine ordnungsgemäße Fehlerbehandlung für Netzwerkprobleme * Listener aufräumen, wenn Komponenten demontiert werden * Auf echten Geräten testen (nicht nur Emulatoren) * Wiederverbindungslogik für Netzwerkunterbrechungen implementieren * Visuelles Feedback für Anrufzustände bereitstellen * Hintergrund-/Vordergrund-Übergänge behandeln ## Lokalisierung [Section titled “Lokalisierung”](#lokalisierung) Das Plugin unterstützt mehrere Sprachen für native UI-Elemente. Konfigurieren Sie dies in Ihren plattformspezifischen Einstellungen. ## Anwendungsfälle [Section titled “Anwendungsfälle”](#anwendungsfälle) * Einzelgespräche per Video * Gruppen-Videokonferenzen * Nur-Audio-Anrufe * Bildschirmfreigabe-Sitzungen * Kundensupport per Video-Chat * Telemedizinische Konsultationen * Remote-Zusammenarbeit # @capgo/capacitor-textinteraction > Entfernen Sie die iOS-Lupenlupe in Kiosk- oder Spielerlebnissen und stellen Sie sie vor der Texteingabe wieder her. Steuern Sie das Textinteraktionssystem von Apple, um die Benutzererfahrung auf Kiosk-Displays, Spielen und interaktiven Installationen anzupassen. iOS-spezifisch Schalten Sie die Lupenlupe und Auswahlgriffe um, die in iOS 15 eingeführt wurden. Einfache API Rufen Sie eine einzelne Methode auf, um den Textinteraktionszustand zu aktivieren oder zu deaktivieren. Sichere Standardwerte Empfängt ein boolesches Ergebnis, damit Sie nicht unterstützte Versionen erkennen können. Kiosk-freundlich Verhindern Sie langes Drücken für Kopieren/Einfügen auf kontrollierten Installationen oder Beschilderungen. Verwenden Sie den Leitfaden für die ersten Schritte für ein minimales Integrationsmuster und Erinnerungen, die Interaktion vor dem Anzeigen von Texteingaben wieder zu aktivieren. # Erste Schritte > Erfahren Sie, wie Sie das Text Interaction-Plugin installieren und verwenden, um die iOS-Textauswahllupe und -lupe zu steuern. ## Installation [Section titled “Installation”](#installation) * npm ```bash npm install @capgo/capacitor-textinteraction npx cap sync ``` * yarn ```bash yarn add @capgo/capacitor-textinteraction npx cap sync ``` * pnpm ```bash pnpm add @capgo/capacitor-textinteraction npx cap sync ``` * bun ```bash bun add @capgo/capacitor-textinteraction npx cap sync ``` ## Plattformunterstützung [Section titled “Plattformunterstützung”](#plattformunterstützung) * **iOS**: iOS 15.0+ (erfordert UITextInteraction API) * **Android**: Keine Operation (gibt nicht unterstützt zurück) * **Web**: Keine Operation (gibt nicht unterstützt zurück) ## Verwendungsbeispiel [Section titled “Verwendungsbeispiel”](#verwendungsbeispiel) ```typescript import { TextInteraction } from '@capgo/capacitor-textinteraction'; // Textinteraktion deaktivieren (Lupenlupe entfernen) const result = await TextInteraction.setEnabled({ enabled: false }); if (result.success) { console.log('Textinteraktion deaktiviert'); } else { console.log('Nicht auf dieser Plattform unterstützt'); } // Textinteraktion vor dem Anzeigen von Texteingaben wieder aktivieren await TextInteraction.setEnabled({ enabled: true }); ``` ## API-Referenz [Section titled “API-Referenz”](#api-referenz) ### setEnabled(options) [Section titled “setEnabled(options)”](#setenabledoptions) ```typescript setEnabled(options: { enabled: boolean }) => Promise<{ success: boolean }> ``` iOS-Textinteraktion aktivieren oder deaktivieren (Lupenlupe und Auswahlgriffe). | Parameter | Typ | | ------------- | ---------------------- | | **`options`** | `{ enabled: boolean }` | **Rückgabe:** `Promise<{ success: boolean }>` * Gibt `{ success: true }` zurück, wenn die Operation erfolgreich war (iOS 15+) * Gibt `{ success: false }` zurück, wenn nicht unterstützt (Android, Web oder iOS < 15) ## Praktische Anwendungsfälle [Section titled “Praktische Anwendungsfälle”](#praktische-anwendungsfälle) ### Kiosk-Anwendungen [Section titled “Kiosk-Anwendungen”](#kiosk-anwendungen) Deaktivieren Sie die Textinteraktion im Kiosk-Modus, um zu verhindern, dass Benutzer auf Kopieren/Einfügen-Menüs zugreifen: ```typescript import { TextInteraction } from '@capgo/capacitor-textinteraction'; async function enterKioskMode() { // Textinteraktion im Kiosk-Modus deaktivieren const result = await TextInteraction.setEnabled({ enabled: false }); if (result.success) { console.log('Kiosk-Modus: Textinteraktion deaktiviert'); } } async function exitKioskMode() { // Textinteraktion beim Verlassen des Kiosk-Modus wieder aktivieren await TextInteraction.setEnabled({ enabled: true }); console.log('Kiosk-Modus: Textinteraktion aktiviert'); } ``` ### Spielerlebnisse [Section titled “Spielerlebnisse”](#spielerlebnisse) Entfernen Sie die Lupenlupe während des Spielens und stellen Sie sie für die Texteingabe wieder her: ```typescript import { TextInteraction } from '@capgo/capacitor-textinteraction'; class GameController { async startGame() { // Textinteraktion während des Spielens deaktivieren await TextInteraction.setEnabled({ enabled: false }); console.log('Spiel gestartet - Textinteraktion deaktiviert'); } async showTextInput() { // Textinteraktion vor dem Anzeigen der Eingabe aktivieren await TextInteraction.setEnabled({ enabled: true }); console.log('Texteingabe bereit - Interaktion aktiviert'); // Eingabefeld anzeigen this.displayInputField(); } async hideTextInput() { // Eingabe ausblenden und Interaktion wieder deaktivieren this.hideInputField(); await TextInteraction.setEnabled({ enabled: false }); } } ``` ### Interaktive Installationen [Section titled “Interaktive Installationen”](#interaktive-installationen) Steuern Sie die Textinteraktion für digitale Beschilderung und interaktive Displays: ```typescript import { TextInteraction } from '@capgo/capacitor-textinteraction'; async function setupInteractiveDisplay() { // Textinteraktion für den Anzeigemodus deaktivieren await TextInteraction.setEnabled({ enabled: false }); // Touch-Handler für Display-Interaktion einrichten document.addEventListener('touchstart', handleDisplayTouch); } async function enableUserInput() { // Textinteraktion aktivieren, wenn der Benutzer Daten eingeben muss const result = await TextInteraction.setEnabled({ enabled: true }); if (result.success) { showInputForm(); } else { // Fallback für nicht unterstützte Plattformen showInputFormWithoutTextInteraction(); } } ``` ### Formularverwaltung [Section titled “Formularverwaltung”](#formularverwaltung) Textinteraktion basierend auf dem Formularstatus umschalten: ```typescript import { TextInteraction } from '@capgo/capacitor-textinteraction'; class FormManager { private textInteractionEnabled = true; async disableTextSelection() { const result = await TextInteraction.setEnabled({ enabled: false }); this.textInteractionEnabled = false; return result.success; } async enableTextSelection() { const result = await TextInteraction.setEnabled({ enabled: true }); this.textInteractionEnabled = true; return result.success; } async onFormFocus() { // Immer vor dem Anzeigen der Tastatur aktivieren await this.enableTextSelection(); } async onFormBlur() { // Optional nach Abschluss der Eingabe deaktivieren await this.disableTextSelection(); } } ``` ### Bedingte Textinteraktion [Section titled “Bedingte Textinteraktion”](#bedingte-textinteraktion) Plattformbewusste Textinteraktionssteuerung: ```typescript import { TextInteraction } from '@capgo/capacitor-textinteraction'; import { Capacitor } from '@capacitor/core'; async function toggleTextInteraction(enabled: boolean) { // Nur auf iOS versuchen if (Capacitor.getPlatform() === 'ios') { const result = await TextInteraction.setEnabled({ enabled }); if (result.success) { console.log(`Textinteraktion ${enabled ? 'aktiviert' : 'deaktiviert'}`); } else { console.log('Textinteraktion wird auf dieser iOS-Version nicht unterstützt'); } } else { console.log('Textinteraktionssteuerung nur auf iOS verfügbar'); } } // Verwendung await toggleTextInteraction(false); // Deaktivieren await toggleTextInteraction(true); // Aktivieren ``` ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) * **Vor der Texteingabe immer wieder aktivieren**: Deaktivieren Sie die Textinteraktion bei Bedarf, aber aktivieren Sie sie immer wieder, bevor Sie Texteingabefelder oder Tastaturen anzeigen * **Auf Plattformunterstützung prüfen**: Überprüfen Sie `result.success`, um nicht unterstützte Plattformen ordnungsgemäß zu behandeln * **iOS-spezifische Funktion**: Dieses Plugin ist nur für iOS; implementieren Sie Fallbacks für Android und Web * **Benutzererfahrung**: Berücksichtigen Sie UX-Auswirkungen der Deaktivierung der Textinteraktion * **Zustandsverwaltung**: Verfolgen Sie den aktivierten/deaktivierten Zustand, um redundante Aufrufe zu vermeiden ## Wichtige Erinnerungen [Section titled “Wichtige Erinnerungen”](#wichtige-erinnerungen) 1. **Für Texteingaben wieder aktivieren**: Rufen Sie immer `setEnabled({ enabled: true })` auf, bevor Sie Texteingabefelder anzeigen, da Benutzer sonst Text nicht richtig auswählen oder bearbeiten können 2. **Plattformerkennung**: Das Plugin gibt `{ success: false }` auf Android, Web und iOS-Versionen unter 15.0 zurück 3. **Kein visuelles Feedback**: Das Deaktivieren der Textinteraktion entfernt die Lupe, bietet aber kein visuelles Feedback - implementieren Sie Ihre eigenen UI-Indikatoren, falls erforderlich 4. **Sichere Standardwerte**: Das Textinteraktionssystem ist standardmäßig aktiviert und entspricht dem Standardverhalten von iOS ## Einschränkungen [Section titled “Einschränkungen”](#einschränkungen) * Nur iOS 15.0+ (erfordert UITextInteraction API) * Keine Auswirkungen auf Android- oder Web-Plattformen * Kann nicht selektiv nur die Lupe oder nur die Auswahlgriffe deaktivieren * Kein Callback, wenn Benutzer versuchen, deaktivierte Funktionen zu verwenden ## Anwendungsfälle [Section titled “Anwendungsfälle”](#anwendungsfälle) * **Kiosk-Anwendungen**: Verhindern Sie Kopieren/Einfügen auf kontrollierten Installationen * **Spielerlebnisse**: Entfernen Sie die Lupe während des Spielens * **Interaktive Displays**: Steuern Sie die Textinteraktion auf digitaler Beschilderung * **Benutzerdefinierte Texteditoren**: Erstellen Sie spezialisierte Textbearbeitungserlebnisse * **Sicherheit**: Verhindern Sie Textauswahl in sensiblen Bereichen ## Fehlerbehebung [Section titled “Fehlerbehebung”](#fehlerbehebung) ### Textinteraktion wird nicht deaktiviert [Section titled “Textinteraktion wird nicht deaktiviert”](#textinteraktion-wird-nicht-deaktiviert) * Überprüfen Sie, ob die iOS-Version 15.0 oder höher ist * Überprüfen Sie, ob `result.success` `true` zurückgibt * Stellen Sie sicher, dass kein anderer Code die Textinteraktion wieder aktiviert ### Benutzer können Text nicht bearbeiten [Section titled “Benutzer können Text nicht bearbeiten”](#benutzer-können-text-nicht-bearbeiten) * Stellen Sie sicher, dass Sie `setEnabled({ enabled: true })` aufgerufen haben, bevor Sie Texteingaben anzeigen * Überprüfen Sie, ob der Aktivierungsaufruf erfolgreich war, indem Sie `result.success` überprüfen * Testen Sie auf einem echten iOS-Gerät (nicht nur im Simulator) # @capgo/capacitor-twilio-voice > Integrieren Sie die Twilio Voice API für hochwertige VoIP-Anrufe mit Anrufverwaltung, Audiosteuerung und Echtzeitkommunikation. ## Übersicht [Section titled “Übersicht”](#übersicht) Das Capacitor Twilio Voice Plugin ermöglicht hochwertige VoIP-Anruffunktionen in iOS- und Android-Anwendungen unter Verwendung der Twilio Voice API. Dieses Plugin bietet umfassende Anrufverwaltung, Authentifizierung und Audiosteuerung für die Erstellung professioneller Anruferlebnisse. VoIP-Anrufe Hochwertige Sprachanrufe über Internet mit Twilio Voice API Anrufverwaltung Anrufe tätigen, annehmen, ablehnen und beenden mit vollständiger Lebenszykluskontrolle Audiosteuerung Stummschaltung, Lautsprecherumschaltung und Audio-Routing-Optionen Plattformunterstützung Native iOS- und Android-Implementierung mit Push-Benachrichtigungen ## Installation [Section titled “Installation”](#installation) ```bash npm install @capgo/capacitor-twilio-voice npx cap sync ``` ## Kern-API-Methoden [Section titled “Kern-API-Methoden”](#kern-api-methoden) ### Authentifizierung [Section titled “Authentifizierung”](#authentifizierung) * `login(options: { accessToken: string })` - Authentifizierung mit Twilio unter Verwendung eines Zugriffstokens * `logout()` - Benutzersitzung beenden und Anrufstatus löschen * `isLoggedIn()` - Aktuellen Authentifizierungsstatus prüfen ### Anrufverwaltung [Section titled “Anrufverwaltung”](#anrufverwaltung) * `makeCall(options: { to: string })` - Ausgehenden Anruf zu angegebener Nummer initiieren * `acceptCall(options: { callSid: string })` - Eingehenden Anruf annehmen * `rejectCall(options: { callSid: string })` - Eingehenden Anruf ablehnen * `endCall(options?: { callSid?: string })` - Aktiven Anruf beenden * `muteCall(options: { muted: boolean, callSid?: string })` - Anrufaudio stummschalten/Stummschaltung aufheben * `setSpeaker(options: { enabled: boolean })` - Lautsprecherausgabe umschalten ## Ereignis-Listener [Section titled “Ereignis-Listener”](#ereignis-listener) Das Plugin bietet umfassende Ereignisbehandlung für: * Registrierungsereignisse für Verbindungsstatus * Anrufstatusänderungen (verbunden, getrennt, klingelnd) * Qualitätswarnungen und Verbindungsprobleme * Benachrichtigungen über eingehende Anrufe ## Plattform-Konfiguration [Section titled “Plattform-Konfiguration”](#plattform-konfiguration) ### iOS-Einrichtung [Section titled “iOS-Einrichtung”](#ios-einrichtung) * Erfordert PushKit-Integration für eingehende Anrufe * Zertifikatskonfiguration für Produktionsnutzung * Mikrofonberechtigungen in Info.plist ### Android-Einrichtung [Section titled “Android-Einrichtung”](#android-einrichtung) * Firebase-Einrichtung für Push-Benachrichtigungen * Mikrofonberechtigungen in AndroidManifest.xml * Hintergrunddienst-Konfiguration ## Schnellstart-Beispiel [Section titled “Schnellstart-Beispiel”](#schnellstart-beispiel) ```typescript import { TwilioVoice } from '@capgo/capacitor-twilio-voice'; // Authentifizierung mit Twilio await TwilioVoice.login({ accessToken: 'ihr-twilio-zugriffstoken' }); // Anruf tätigen await TwilioVoice.makeCall({ to: '+1234567890' }); // Auf Anrufereignisse hören TwilioVoice.addListener('callConnected', (data) => { console.log('Anruf verbunden:', data); }); ``` ## Dokumentation [Section titled “Dokumentation”](#dokumentation) Schauen Sie sich die [vollständige Dokumentation](/docs/plugins/twilio-voice/getting-started/) für detaillierte Einrichtungsanweisungen, erweiterte Konfiguration und Integrationsbeispiele an. # Erste Schritte > Erfahren Sie, wie Sie Twilio Voice für VoIP-Anrufe in Ihrer Capacitor-App installieren und integrieren. ## Installation [Section titled “Installation”](#installation) * npm ```bash npm install @capgo/capacitor-twilio-voice npx cap sync ``` * yarn ```bash yarn add @capgo/capacitor-twilio-voice npx cap sync ``` * pnpm ```bash pnpm add @capgo/capacitor-twilio-voice npx cap sync ``` * bun ```bash bun add @capgo/capacitor-twilio-voice npx cap sync ``` ## Voraussetzungen [Section titled “Voraussetzungen”](#voraussetzungen) Sie benötigen ein Twilio-Konto und Zugriffstokens für die Authentifizierung. Registrieren Sie sich bei [Twilio](https://www.twilio.com/), wenn Sie noch kein Konto haben. ## Plattform-Konfiguration [Section titled “Plattform-Konfiguration”](#plattform-konfiguration) ### iOS [Section titled “iOS”](#ios) Fügen Sie erforderliche Berechtigungen zu Ihrer `Info.plist` hinzu: ```xml NSMicrophoneUsageDescription Diese App benötigt Mikrofonzugriff für Sprachanrufe ``` Für eingehende Anrufe mit PushKit: 1. Aktivieren Sie Push-Benachrichtigungen in den Xcode-Funktionen 2. Fügen Sie den VoIP-Hintergrundmodus hinzu 3. Konfigurieren Sie das VoIP-Zertifikat in der Twilio-Konsole ### Android [Section titled “Android”](#android) Fügen Sie erforderliche Berechtigungen zu Ihrer `AndroidManifest.xml` hinzu: ```xml ``` Konfigurieren Sie Firebase für Push-Benachrichtigungen: 1. Fügen Sie `google-services.json` zu Ihrem Android-Projekt hinzu 2. Konfigurieren Sie FCM in der Twilio-Konsole ## Verwendungsbeispiel [Section titled “Verwendungsbeispiel”](#verwendungsbeispiel) ```typescript import { TwilioVoice } from '@capgo/capacitor-twilio-voice'; // Authentifizierung mit Twilio await TwilioVoice.login({ accessToken: 'ihr-twilio-zugriffstoken' }); // Anruf tätigen await TwilioVoice.makeCall({ to: '+1234567890' }); // Auf Anrufereignisse hören TwilioVoice.addListener('callConnected', (data) => { console.log('Anruf verbunden:', data.callSid); }); TwilioVoice.addListener('callDisconnected', (data) => { console.log('Anruf getrennt:', data.callSid); }); TwilioVoice.addListener('incomingCall', (data) => { console.log('Eingehender Anruf von:', data.from); // Anruf annehmen TwilioVoice.acceptCall({ callSid: data.callSid }); // Oder ablehnen // TwilioVoice.rejectCall({ callSid: data.callSid }); }); // Anruf stummschalten/Stummschaltung aufheben await TwilioVoice.muteCall({ muted: true, callSid: 'aktuelle-anruf-sid' }); // Lautsprecher umschalten await TwilioVoice.setSpeaker({ enabled: true }); // Anruf beenden await TwilioVoice.endCall({ callSid: 'aktuelle-anruf-sid' }); // Abmelden await TwilioVoice.logout(); ``` ## API-Referenz [Section titled “API-Referenz”](#api-referenz) ### login(options) [Section titled “login(options)”](#loginoptions) ```typescript login(options: { accessToken: string }) => Promise ``` Authentifizierung mit Twilio unter Verwendung eines Zugriffstokens. | Param | Type | | ------------- | ------------------------- | | **`options`** | `{ accessToken: string }` | ### logout() [Section titled “logout()”](#logout) ```typescript logout() => Promise ``` Benutzer-Sitzung beenden und Anrufstatus löschen. ### isLoggedIn() [Section titled “isLoggedIn()”](#isloggedin) ```typescript isLoggedIn() => Promise<{ loggedIn: boolean }> ``` Aktuellen Authentifizierungsstatus prüfen. **Gibt zurück:** `Promise<{ loggedIn: boolean }>` ### makeCall(options) [Section titled “makeCall(options)”](#makecalloptions) ```typescript makeCall(options: { to: string; params?: Record }) => Promise<{ callSid: string }> ``` Ausgehenden Anruf zu einer angegebenen Nummer initiieren. | Param | Type | | ------------- | ------------------------------------------------- | | **`options`** | `{ to: string; params?: Record }` | **Gibt zurück:** `Promise<{ callSid: string }>` ### acceptCall(options) [Section titled “acceptCall(options)”](#acceptcalloptions) ```typescript acceptCall(options: { callSid: string }) => Promise ``` Eingehenden Anruf annehmen. | Param | Type | | ------------- | --------------------- | | **`options`** | `{ callSid: string }` | ### rejectCall(options) [Section titled “rejectCall(options)”](#rejectcalloptions) ```typescript rejectCall(options: { callSid: string }) => Promise ``` Eingehenden Anruf ablehnen. | Param | Type | | ------------- | --------------------- | | **`options`** | `{ callSid: string }` | ### endCall(options?) [Section titled “endCall(options?)”](#endcalloptions) ```typescript endCall(options?: { callSid?: string }) => Promise ``` Aktiven Anruf beenden. | Param | Type | | ------------- | --------------------------------- | | **`options`** | `{ callSid?: string }` (optional) | ### muteCall(options) [Section titled “muteCall(options)”](#mutecalloptions) ```typescript muteCall(options: { muted: boolean; callSid?: string }) => Promise ``` Anrufaudio stummschalten oder Stummschaltung aufheben. | Param | Type | | ------------- | -------------------------------------- | | **`options`** | `{ muted: boolean; callSid?: string }` | ### setSpeaker(options) [Section titled “setSpeaker(options)”](#setspeakeroptions) ```typescript setSpeaker(options: { enabled: boolean }) => Promise ``` Lautsprecherausgabe umschalten. | Param | Type | | ------------- | ---------------------- | | **`options`** | `{ enabled: boolean }` | ### sendDigits(options) [Section titled “sendDigits(options)”](#senddigitsoptions) ```typescript sendDigits(options: { digits: string; callSid?: string }) => Promise ``` DTMF-Töne während eines Anrufs senden. | Param | Type | | ------------- | -------------------------------------- | | **`options`** | `{ digits: string; callSid?: string }` | ## Ereignis-Listener [Section titled “Ereignis-Listener”](#ereignis-listener) ### Verfügbare Ereignisse [Section titled “Verfügbare Ereignisse”](#verfügbare-ereignisse) * `registered` - Erfolgreich bei Twilio registriert * `unregistered` - Von Twilio abgemeldet * `registrationFailed` - Registrierung fehlgeschlagen * `incomingCall` - Eingehender Anruf empfangen * `callConnected` - Anruf erfolgreich verbunden * `callDisconnected` - Anruf getrennt * `callRinging` - Ausgehender Anruf klingelt * `callReconnecting` - Anruf stellt Verbindung wieder her * `callReconnected` - Anruf nach Unterbrechung wieder verbunden * `qualityWarning` - Warnung zur Anrufqualität * `error` - Ein Fehler ist aufgetreten ### Ereignisbeispiele [Section titled “Ereignisbeispiele”](#ereignisbeispiele) ```typescript // Eingehende Anrufe verarbeiten TwilioVoice.addListener('incomingCall', (data) => { console.log('Eingehender Anruf von:', data.from); console.log('Anruf-SID:', data.callSid); // Anrufbildschirm für eingehenden Anruf anzeigen showIncomingCallScreen({ from: data.from, callSid: data.callSid }); }); // Anrufstatusänderungen verarbeiten TwilioVoice.addListener('callConnected', (data) => { console.log('Anruf verbunden:', data.callSid); startCallTimer(); }); TwilioVoice.addListener('callDisconnected', (data) => { console.log('Anruf beendet:', data.callSid); stopCallTimer(); hideCallScreen(); }); // Qualitätswarnungen verarbeiten TwilioVoice.addListener('qualityWarning', (data) => { console.warn('Warnung zur Anrufqualität:', data.warning); showQualityWarning(data.warning); }); // Fehler verarbeiten TwilioVoice.addListener('error', (error) => { console.error('Twilio-Fehler:', error.message); handleError(error); }); // Listener entfernen, wenn fertig const listener = await TwilioVoice.addListener('callConnected', (data) => { console.log('Verbunden'); }); // Später... listener.remove(); ``` ## Vollständiges Beispiel [Section titled “Vollständiges Beispiel”](#vollständiges-beispiel) ```typescript import { TwilioVoice } from '@capgo/capacitor-twilio-voice'; class VoiceCallService { private currentCallSid: string | null = null; private isMuted = false; private isSpeakerOn = false; async initialize(accessToken: string) { try { // Bei Twilio anmelden await TwilioVoice.login({ accessToken }); // Anmeldestatus prüfen const { loggedIn } = await TwilioVoice.isLoggedIn(); console.log('Anmeldestatus:', loggedIn); // Ereignis-Listener einrichten this.setupEventListeners(); } catch (error) { console.error('Initialisierung fehlgeschlagen:', error); } } setupEventListeners() { // Registrierungsereignisse TwilioVoice.addListener('registered', () => { console.log('Erfolgreich bei Twilio registriert'); }); TwilioVoice.addListener('registrationFailed', (error) => { console.error('Registrierung fehlgeschlagen:', error); }); // Eingehender Anruf TwilioVoice.addListener('incomingCall', async (data) => { console.log('Eingehender Anruf von:', data.from); const accepted = await this.showIncomingCallDialog(data.from); if (accepted) { await TwilioVoice.acceptCall({ callSid: data.callSid }); this.currentCallSid = data.callSid; } else { await TwilioVoice.rejectCall({ callSid: data.callSid }); } }); // Anrufereignisse TwilioVoice.addListener('callConnected', (data) => { console.log('Anruf verbunden'); this.currentCallSid = data.callSid; this.showCallScreen(); }); TwilioVoice.addListener('callDisconnected', () => { console.log('Anruf getrennt'); this.currentCallSid = null; this.hideCallScreen(); }); TwilioVoice.addListener('callRinging', () => { console.log('Anruf klingelt...'); }); // Qualitätswarnungen TwilioVoice.addListener('qualityWarning', (data) => { console.warn('Warnung zur Anrufqualität:', data.warning); this.showQualityIndicator(data.warning); }); } async makeCall(phoneNumber: string) { try { const result = await TwilioVoice.makeCall({ to: phoneNumber, params: { // Optionale benutzerdefinierte Parameter customerId: 'customer-123' } }); this.currentCallSid = result.callSid; console.log('Anruf initiiert:', result.callSid); } catch (error) { console.error('Anruf fehlgeschlagen:', error); } } async endCall() { if (this.currentCallSid) { await TwilioVoice.endCall({ callSid: this.currentCallSid }); this.currentCallSid = null; } } async toggleMute() { this.isMuted = !this.isMuted; await TwilioVoice.muteCall({ muted: this.isMuted, callSid: this.currentCallSid || undefined }); } async toggleSpeaker() { this.isSpeakerOn = !this.isSpeakerOn; await TwilioVoice.setSpeaker({ enabled: this.isSpeakerOn }); } async sendDTMF(digits: string) { if (this.currentCallSid) { await TwilioVoice.sendDigits({ digits, callSid: this.currentCallSid }); } } async logout() { await TwilioVoice.logout(); } private async showIncomingCallDialog(from: string): Promise { // Nativen Dialog oder benutzerdefinierte UI anzeigen return confirm(`Eingehender Anruf von ${from}`); } private showCallScreen() { // Anruf-UI anzeigen console.log('Anrufbildschirm anzeigen'); } private hideCallScreen() { // Anruf-UI ausblenden console.log('Anrufbildschirm ausblenden'); } private showQualityIndicator(warning: string) { // Qualitätswarnung anzeigen console.log('Qualitätswarnung:', warning); } } // Verwendung const voiceService = new VoiceCallService(); // Mit Zugriffstoken von Ihrem Backend initialisieren const token = await fetchTwilioToken(); await voiceService.initialize(token); // Anruf tätigen await voiceService.makeCall('+1234567890'); // Anruf steuern await voiceService.toggleMute(); await voiceService.toggleSpeaker(); await voiceService.sendDTMF('1'); // Anruf beenden await voiceService.endCall(); // Abmelden await voiceService.logout(); ``` ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) * Zugriffstokens von Ihrem Backend-Server abrufen, niemals in der App einbetten * Token-Aktualisierungslogik für Sitzungen mit langer Laufzeit implementieren * Netzwerkunterbrechungen mit Wiederverbindungslogik handhaben * Visuelles Feedback für Anrufstatus bereitstellen * Auf echten Geräten mit Push-Benachrichtigungen testen * Ordnungsgemäße Fehlerbehandlung implementieren * Listener aufräumen, wenn Komponenten unmounten * Mikrofonberechtigungen vor dem Tätigen von Anrufen anfordern ## Sicherheitsüberlegungen [Section titled “Sicherheitsüberlegungen”](#sicherheitsüberlegungen) * Niemals Twilio-Anmeldedaten in der App speichern * Zugriffstokens auf Ihrem Backend generieren * Token-Ablauf und -Aktualisierung implementieren * HTTPS für alle Token-Anfragen verwenden * Eingehende Anrufe serverseitig validieren ## Fehlerbehebung [Section titled “Fehlerbehebung”](#fehlerbehebung) ### Anrufe werden nicht verbunden [Section titled “Anrufe werden nicht verbunden”](#anrufe-werden-nicht-verbunden) * Überprüfen Sie, ob das Zugriffstoken gültig und nicht abgelaufen ist * Netzwerkverbindung prüfen * Stellen Sie sicher, dass Mikrofonberechtigungen erteilt sind * Überprüfen Sie, ob das Twilio-Konto ordnungsgemäß konfiguriert ist ### Push-Benachrichtigungen funktionieren nicht [Section titled “Push-Benachrichtigungen funktionieren nicht”](#push-benachrichtigungen-funktionieren-nicht) * Überprüfen Sie, ob PushKit/FCM-Zertifikate in Twilio konfiguriert sind * Prüfen Sie, ob das Gerät für Push-Benachrichtigungen registriert ist * Mit Produktionszertifikaten testen ### Audio-Probleme [Section titled “Audio-Probleme”](#audio-probleme) * Mikrofonberechtigungen prüfen * Lautsprecher-/Bluetooth-Einstellungen überprüfen * Audio-Routing auf echten Geräten testen ## Anzeige des Anrufernamens (CapacitorTwilioCallerName) [Section titled “Anzeige des Anrufernamens (CapacitorTwilioCallerName)”](#anzeige-des-anrufernamens-capacitortwiliocallername) Standardmäßig zeigen eingehende Anrufe die Telefonnummer oder Client-ID des Anrufers an. Sie können dies anpassen, indem Sie einen `CapacitorTwilioCallerName`-Parameter von Ihrem TwiML-Backend übergeben, um stattdessen einen freundlichen Namen anzuzeigen. ### Backend-Einrichtung [Section titled “Backend-Einrichtung”](#backend-einrichtung) Wenn Sie Ihre TwiML-Antwort für ``-Wahl generieren, fügen Sie den `CapacitorTwilioCallerName`-Parameter hinzu: ```java // Java-Beispiel Parameter callerNameParam = new Parameter.Builder() .name("CapacitorTwilioCallerName") .value("Max Mustermann") .build(); Client client = new Client.Builder(identity) .parameter(callerNameParam) .build(); Dial dial = new Dial.Builder() .client(client) .build(); ``` Node.js-Beispiel ```javascript const VoiceResponse = require('twilio').twiml.VoiceResponse; const response = new VoiceResponse(); const dial = response.dial(); dial.client({ name: 'CapacitorTwilioCallerName', value: 'Max Mustermann' }, identity); ``` ### Wie es funktioniert [Section titled “Wie es funktioniert”](#wie-es-funktioniert) 1. Wenn Ihr Backend einen eingehenden Anruf erhält, generiert es TwiML, um den Anruf weiterzuleiten 2. Fügen Sie den `CapacitorTwilioCallerName`-Parameter mit dem Anzeigenamen des Anrufers hinzu 3. Das Plugin extrahiert diesen Parameter automatisch und verwendet ihn für: * iOS CallKit-Bildschirm für eingehende Anrufe * Android-Benachrichtigung für eingehende Anrufe * Das `from`-Feld in `incomingCall`-Ereignissen * Das `pendingInvites`-Array im Anrufstatus Wenn `CapacitorTwilioCallerName` nicht bereitgestellt wird, fällt das Plugin auf die Telefonnummer oder Client-ID des Anrufers zurück. # @capgo/capacitor-updater > Die Kerntechnologie, die Live-Updates in Capacitor-Apps ermöglicht und es Ihnen erlaubt, Updates ohne App-Store-Genehmigungen zu übertragen. Over-the-Air-Updates Übertragen Sie Updates sofort ohne App-Store-Prüfungen Erweiterte API Volle Kontrolle über den Update-Ablauf mit umfassenden API-Methoden Selbst-Hosting bereit Betreiben Sie Ihren eigenen Update-Server für vollständige Kontrolle Umfassende Dokumentation Schauen Sie sich die [Plugin-API](/docs/plugins/updater/api/) an, um das Plugin in nur wenigen Minuten zu meistern. ## Schnellzugriff [Section titled “Schnellzugriff”](#schnellzugriff) [Live-Updates-Dokumentation ](/docs/live-updates/)Erfahren Sie mehr über Kanäle, Update-Verhalten, Rollbacks und mehr im Hauptabschnitt Live-Updates. [Erste-Schritte-Anleitung ](/docs/getting-started/quickstart/)Folgen Sie unserer Schnellstartanleitung, um Live-Updates zu Ihrer App hinzuzufügen. ## Plugin-Dokumentation [Section titled “Plugin-Dokumentation”](#plugin-dokumentation) Diese technische Referenz umfasst: * **[Plugin-API](/docs/plugins/updater/api/)** - Alle verfügbaren Methoden und ihre Verwendung * **[Plugin-Einstellungen](/docs/plugins/updater/settings/)** - Konfigurationsoptionen für capacitor.config * **[Bekannte Probleme](/docs/plugins/updater/known-issues/)** - Häufige Probleme und Lösungen * **[Debugging](/docs/plugins/updater/debugging/)** - So beheben Sie Update-Probleme * **[Ereignisse](/docs/plugins/updater/events/)** - Verfügbare Update-Ereignisse * **[Lokale Entwicklung](/docs/plugins/updater/local-dev/getting-started/)** - Updates lokal testen * **[Selbst-gehosteter Modus](/docs/plugins/updater/self-hosted/getting-started/)** - Betreiben Sie Ihren eigenen Update-Server ## Installation [Section titled “Installation”](#installation) ```bash npm install @capgo/capacitor-updater npx cap sync ``` Für Einrichtung und Konfiguration siehe die [Erste-Schritte-Anleitung](/docs/getting-started/quickstart/). # Funktionen und Einstellungen > Alle verfügbaren Methoden und Einstellungen des Plugins # Updater-Plugin-Konfiguration [Section titled “Updater-Plugin-Konfiguration”](#updater-plugin-konfiguration) Weitere Informationen finden Sie im Github [Readme](https://github.com/Cap-go/capacitor-updater). CapacitorUpdater kann mit diesen Optionen konfiguriert werden:| Stütze | Geben Sie | ein Beschreibung | Standard | Seit | | ------------- | ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------- | ------- | | **`appReadyTimeout`** | `Nummer` | Konfigurieren Sie die Anzahl der Millisekunden, die das native Plugin warten soll, bevor ein Update als „fehlgeschlagen“ betrachtet wird. Verfügbar auf Android, iOS und Electron. | `10000 // (10 Sekunden)` | | | **`responseTimeout`** | `Nummer` | Konfigurieren Sie die Anzahl der Millisekunden, die das native Plugin warten soll, bevor das Zeitlimit API berücksichtigt wird. Verfügbar auf Android, iOS und Electron. | `20000 // (20 Sekunden)` | | | **`autoDeleteFailed`** | `boolean` | Konfigurieren Sie, ob das Plugin fehlgeschlagene Bundles automatisch löschen soll. Verfügbar auf Android, iOS und Elektron. | `true` | | | **`autoDeletePrevious`** | `boolean` | Konfigurieren Sie, ob das Plugin nach einem erfolgreichen Update automatisch vorherige Bundles löschen soll. Verfügbar auf Android, iOS und Electron. | `true` | | | **`autoUpdate`** | `boolean` | Konfigurieren Sie, ob das Plugin Auto Update über einen Update-Server verwenden soll. Verfügbar auf Android, iOS und Electron. | `true` | | | **`resetWhenUpdate`** | `boolean` | Löschen Sie zuvor heruntergeladene Bundles automatisch, wenn ein neueres natives App-Bundle auf dem Gerät installiert wird. Verfügbar auf Android, iOS und Electron. | `true` | | | **`updateUrl`** | `string` | Konfigurieren Sie die URL/den Endpunkt, an den Update-Prüfungen gesendet werden. Verfügbar auf Android, iOS und Electron.| `https://plugin.capgo.app/updates` | | | **`channelUrl`** | `string` | Konfigurieren Sie die URL/den Endpunkt für Kanalvorgänge. Verfügbar für Android, iOS und Electron. | `https://plugin.capgo.app/channel_self` | | | **`statsUrl`** | `string` | Konfigurieren Sie die URL/den Endpunkt, an den Update-Statistiken gesendet werden. Verfügbar auf Android, iOS und Electron. Auf „“ setzen, um die Statistikberichterstattung zu deaktivieren. | `https://plugin.capgo.app/stats` | | | **`publicKey`** | `string` | Konfigurieren Sie den öffentlichen Schlüssel für die End-to-End-Live-Update-Verschlüsselung Version 2. Verfügbar auf Android, iOS und Electron. | `undefiniert` | 6.2.0 | | **`version`** | `string` | Konfigurieren Sie die aktuelle Version der App. Dies wird für die erste Update-Anfrage verwendet. Wenn nicht festgelegt, erhält das Plugin die Version aus dem nativen Code. Verfügbar auf Android, iOS und Electron.| `undefiniert` | 4.17.48 | | **`directUpdate`** | `boolean | ‘immer’ | ‘atInstall’ | ‘onLaunch’` | Konfigurieren Sie, wann das Plugin Updates direkt installieren soll. Nur für den AutoUpdate-Modus. Funktioniert gut für Apps mit weniger als 10 MB und mit Uploads, die mit der Flagge —delta erfolgen. Zip-Dateien oder Apps mit mehr als 10 MB werden für Benutzer relativ langsam aktualisiert. – false: Führen Sie niemals direkte Aktualisierungen durch (Standardverhalten verwenden: beim Start herunterladen, im Hintergrund festlegen) – atInstall: Direkte Aktualisierung nur, wenn die App installiert oder aus dem Store aktualisiert wird, andernfalls als „directUpdate = false“ fungieren – onLaunch: Direkte Aktualisierung nur bei installierter App, Aktualisierung aus dem Store oder nach App-Kill, ansonsten als „directUpdate = false“ – immer: Direkte Aktualisierung in allen vorherigen Fällen (App installiert, aus dem Store aktualisiert, nach App-Kill oder App-Fortsetzung), niemals als „directUpdate = false“ fungieren – true: (veraltet) Das Gleiche wie „immer“ für Abwärtskompatibilität. Verfügbar auf Android, iOS und Electron. | `false` | 5.1.0 | | **`autoSplashscreen`** | `boolean` | Behandeln Sie das Ausblenden des Begrüßungsbildschirms automatisch, wenn Sie DirectUpdate verwenden. Wenn diese Option aktiviert ist, blendet das Plugin den Begrüßungsbildschirm automatisch aus, nachdem Updates angewendet wurden oder wenn kein Update erforderlich ist. Dadurch entfällt die Notwendigkeit, manuell auf appReady-Ereignisse zu warten und SplashScreen.hide() aufzurufen. Funktioniert nur, wenn directUpdate auf „atInstall“, „always“ oder true gesetzt ist. Erfordert die Installation und Konfiguration des Plugins @capacitor/splash-screen mit launchAutoHide: false. Erfordert die Aktivierung von autoUpdate und directUpdate. Verfügbar für Android und iOS. | `false` | 7.6.0 | | **`periodCheckDelay`** | `Nummer` | Konfigurieren Sie den Verzögerungszeitraum für die Periodenaktualisierungsprüfung. Die Einheit ist in Sekunden. Verfügbar auf Android, iOS und Electron. Darf nicht kürzer als 600 Sekunden (10 Minuten) sein. | `600 // (10 Minuten)` | | | **`localS3`** | `boolean` | Konfigurieren Sie CLI so, dass ein lokaler Server zum Testen oder ein selbstgehosteter Update-Server verwendet wird.| `undefiniert` | 4.17.48 | | **`localHost`** | `string` | Konfigurieren Sie CLI so, dass ein lokaler Server zum Testen oder ein selbstgehosteter Update-Server verwendet wird. | `undefiniert` | 4.17.48 | | **`localWebHost`** | `string` | Konfigurieren Sie CLI so, dass ein lokaler Server zum Testen oder ein selbstgehosteter Update-Server verwendet wird. | `undefiniert` | 4.17.48 | | **`localSupa`** | `string` | Konfigurieren Sie CLI so, dass ein lokaler Server zum Testen oder ein selbstgehosteter Update-Server verwendet wird. | `undefiniert` | 4.17.48 | | **`localSupaAnon`** | `string` | Konfigurieren Sie CLI so, dass zum Testen ein lokaler Server verwendet wird.| `undefiniert` | 4.17.48 | | **`localApi`** | `string` | Konfigurieren Sie CLI so, dass zum Testen eine lokale API verwendet wird. | `undefiniert` | 6.3.3 | | **`localApiFiles`** | `string` | Konfigurieren Sie CLI so, dass zum Testen eine lokale Datei-API verwendet wird. | `undefiniert` | 6.3.3 | | **`allowModifyUrl`** | `boolean` | Ermöglichen Sie dem Plugin, updateUrl, statsUrl undchannelUrl dynamisch von der JavaScript-Seite aus zu ändern. | `false` | 5.4.0 | | **`defaultChannel`** | `string` | Legen Sie in der Konfiguration den Standardkanal für die App fest. Groß- und Kleinschreibung beachten. Diese Einstellung überschreibt den in der Cloud festgelegten Standardkanal, berücksichtigt jedoch weiterhin die in der Cloud vorgenommenen Überschreibungen. | `undefined` | 5.5.0 | | **`appId`** | `string` | Konfigurieren Sie die App-ID für die App in der Konfiguration. | `undefiniert` | 6.0.0 | | **`keepUrlPathAfterReload`** | `boolean` | Konfigurieren Sie das Plugin so, dass der URL-Pfad nach einem Neuladen erhalten bleibt. WARNUNG: Wenn ein Neuladen ausgelöst wird, wird „window\.history“ gelöscht. | `false` | 6.8.0 | | **`disableJSLogging`** | `boolean` | Deaktivieren Sie die JavaScript-Protokollierung des Plugins. Wenn „true“, meldet sich das Plugin nicht an der JavaScript-Konsole an. es wird nur das native Protokoll erstellt | `false` | 7.3.0 | | **`shakeMenu`** | `boolean` | Aktivieren Sie die Schüttelgeste, um das Update-Menü zu Debug-/Testzwecken anzuzeigen | `false` | 7.5.0 |## Beispiele In `capacitor.config.json`: ```json { "plugins": { "CapacitorUpdater": { "appReadyTimeout": 1000 // (1 second), "responseTimeout": 10 // (10 second), "autoDeleteFailed": false, "autoDeletePrevious": false, "autoUpdate": false, "resetWhenUpdate": false, "updateUrl": https://example.com/api/auto_update, "channelUrl": https://example.com/api/channel, "statsUrl": https://example.com/api/stats, "publicKey": undefined, "version": undefined, "directUpdate": undefined, "autoSplashscreen": undefined, "periodCheckDelay": undefined, "localS3": undefined, "localHost": undefined, "localWebHost": undefined, "localSupa": undefined, "localSupaAnon": undefined, "localApi": undefined, "localApiFiles": undefined, "allowModifyUrl": undefined, "defaultChannel": undefined, "appId": undefined, "keepUrlPathAfterReload": undefined, "disableJSLogging": undefined, "shakeMenu": undefined } } } ``` In `capacitor.config.ts`: ```ts import { CapacitorConfig } from '@capacitor/cli'; const config: CapacitorConfig = { plugins: { CapacitorUpdater: { appReadyTimeout: 1000 // (1 second), responseTimeout: 10 // (10 second), autoDeleteFailed: false, autoDeletePrevious: false, autoUpdate: false, resetWhenUpdate: false, updateUrl: https://example.com/api/auto_update, channelUrl: https://example.com/api/channel, statsUrl: https://example.com/api/stats, publicKey: undefined, version: undefined, directUpdate: undefined, autoSplashscreen: undefined, periodCheckDelay: undefined, localS3: undefined, localHost: undefined, localWebHost: undefined, localSupa: undefined, localSupaAnon: undefined, localApi: undefined, localApiFiles: undefined, allowModifyUrl: undefined, defaultChannel: undefined, appId: undefined, keepUrlPathAfterReload: undefined, disableJSLogging: undefined, shakeMenu: undefined, }, }, }; export default config; ``` * [`notifyAppReady()`](#notifyappready) * [`setUpdateUrl(...)`](#setupdateurl) * [`setStatsUrl(...)`](#setstatsurl) * [`setChannelUrl(...)`](#setchannelurl) * [`download(...)`](#download) * [`next(...)`](#next) * [`set(...)`](#set) * [`delete(...)`](#delete) * [`list(...)`](#list) * [`reset(...)`](#reset) * [`current()`](#aktuell) * [`reload()`](#reload) * [`setMultiDelay(...)`](#setmultidelay) * [`cancelDelay()`](#canceldelay) * [`getLatest(...)`](#getlatest) * [`setChannel(...)`](#setchannel) * [`unsetChannel(...)`](#unsetchannel) * [`getChannel()`](#getchannel) * [`listChannels()`](#listchannels) * [`setCustomId(...)`](#setcustomid) * [`getBuiltinVersion()`](#getbuiltinversion) * [`getDeviceId()`](#getdeviceid) * [`getPluginVersion()`](#getpluginversion) * [`isAutoUpdateEnabled()`](#isautoupdateenabled) * [`removeAllListeners()`](#removealllisteners) * [`addListener('download', ...)`](#addlistenerdownload-) * [`addListener('noNeedUpdate', ...)`](#addlistenernoneedupdate-) * [`addListener('updateAvailable', ...)`](#addlistenerupdateavailable-) * [`addListener('downloadComplete', ...)`](#addlistenerdownloadcomplete-) * [`addListener('majorAvailable', ...)`](#addlistenermajoravailable-) * [`addListener('updateFailed', ...)`](#addlistenerupdatefailed-) * [`addListener('downloadFailed', ...)`](#addlistenerdownloadfailed-) * [`addListener('appReloaded', ...)`](#addlistenerappreloaded-) * [`addListener('appReady', ...)`](#addlistenerappready-) * [`isAutoUpdateAvailable()`](#isautoupdateavailable) * [`getNextBundle()`](#getnextbundle) * [`setShakeMenu(...)`](#setshakemenu) * [`isShakeMenuEnabled()`](#isshakemenuenabled) * [Schnittstellen](#interfaces) * [Typ-Aliase](#type-aliases) # Methoden [Section titled “Methoden”](#methoden) ## notifyAppReady() [Section titled “notifyAppReady()”](#notifyappready) ```typescript notifyAppReady() => Promise ``` Capacitor Updater benachrichtigen, dass das aktuelle Bundle funktioniert (ein Rollback erfolgt, wenn diese Methode nicht bei jedem App-Start aufgerufen wird) Standardmäßig sollte diese Methode in den ersten 10 Sekunden nach dem App-Start aufgerufen werden, andernfalls kommt es zu einem Rollback. Ändern Sie dieses Verhalten mit {@link appReadyTimeout} **Rückgaben:** `Promise` *** ## setUpdateUrl(…) [Section titled “setUpdateUrl(…)”](#setupdateurl) ```typescript setUpdateUrl(options: UpdateUrl) => Promise ``` Legen Sie die updateUrl für die App fest. Diese wird verwendet, um nach Updates zu suchen. | Param | Geben Sie | ein Beschreibung | | ------------- | ----------- | ------------------------------------------------------------------- | | **`options`** | `UpdateUrl` | enthält die URL, die zum Suchen nach Updates verwendet werden soll. | **Seit:** 5.4.0 *** ## setStatsUrl(…) [Section titled “setStatsUrl(…)”](#setstatsurl) ```typescript setStatsUrl(options: StatsUrl) => Promise ``` Legen Sie die statsUrl für die App fest. Diese wird zum Senden von Statistiken verwendet. Durch die Übergabe einer leeren Zeichenfolge wird die Statistikerfassung deaktiviert.| Param | Geben Sie | ein Beschreibung | | ------------- | --------------------------------------------- | ----------------------------------------------- | | **`options`** | `StatsUrl` | enthält die URL, die zum Senden von Statistiken verwendet werden soll. | **Seit:** 5.4.0 *** ## setChannelUrl(…) [Section titled “setChannelUrl(…)”](#setchannelurl) ```typescript setChannelUrl(options: ChannelUrl) => Promise ``` Legen Sie die Kanal-URL für die App fest. Diese wird zum Festlegen des Kanals verwendet. | Param | Geben Sie | ein Beschreibung | | ------------- | ------------ | --------------------------------------------------------------------- | | **`options`** | `ChannelUrl` | enthält die URL, die zum Einstellen des Kanals verwendet werden soll. | **Seit:** 5.4.0 *** ## herunterladen(…) [Section titled “herunterladen(…)”](#herunterladen) ```typescript download(options: DownloadOptions) => Promise ``` Laden Sie ein neues Paket von der angegebenen URL herunter. Es sollte eine ZIP-Datei mit darin enthaltenen Dateien oder mit einer eindeutigen ID für alle Ihre Dateien sein | Param | Geben Sie | ein Beschreibung | | ------------- | ----------------- | ----------------------------------------------------------------------------------------------- | | **`options`** | `DownloadOptions` | Der {@link [DownloadOptions](#downloadoptions)} zum Herunterladen einer neuen Bundle-ZIP-Datei. | **Rückgaben:** `Promise` *** ## weiter(…) [Section titled “weiter(…)”](#weiter) ```typescript next(options: BundleId) => Promise ``` Legen Sie das nächste Bundle fest, das beim Neuladen der App verwendet werden soll. | Param | Geben Sie | ein Beschreibung | | ------------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------- | | **`options`** | `BundleId` | Enthält die ID des nächsten Bundles, das beim nächsten App-Start festgelegt werden soll. {@link [BundleInfo.id](#bundleinfo)} | **Rückgaben:** `Promise` *** ## gesetzt(…) [Section titled “gesetzt(…)”](#gesetzt) ```typescript set(options: BundleId) => Promise ``` Legt das aktuelle Bundle fest und lädt die App sofort neu.| Param | Geben Sie | ein Beschreibung | | ------------- | --------------------------------------------- | ------------------------------------------------------------------------------------------------- | | **`options`** | `BundleId` | Ein {@link [BundleId](#bundleid)}-Objekt, das die neue Bundle-ID enthält, die als aktuell festgelegt werden soll. | *** ## löschen(…) [Section titled “löschen(…)”](#löschen) ```typescript delete(options: BundleId) => Promise ``` Löscht das angegebene Bundle aus dem nativen App-Speicher. Verwenden Sie es mit {@link list}, um die gespeicherten Bundle-IDs abzurufen. | Param | Geben Sie | ein Beschreibung | | ------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **`options`** | `BundleId` | Ein {@link [BundleId](#bundleid)}-Objekt, das die ID eines zu löschenden Bundles enthält (beachten Sie, dass dies die Bundle-ID ist, NICHT der Versionsname) | *** ## Liste(…) [Section titled “Liste(…)”](#liste) ```typescript list(options?: ListOptions | undefined) => Promise ``` Holen Sie sich alle lokal heruntergeladenen Bundles in Ihre App | Param | Geben Sie | ein Beschreibung | | ------------- | ------------- | ----------------------------------------------------------------- | | **`options`** | `ListOptions` | Der {@link [ListOptions](#listoptions)} zum Auflisten von Bundles | **Rückgaben:** `Promise` *** ## zurücksetzen(…) [Section titled “zurücksetzen(…)”](#zurücksetzen) ```typescript reset(options?: ResetOptions | undefined) => Promise ``` Setzen Sie die App auf das Bundle `builtin` (das an Apple App Store / Google Play Store gesendete Paket) oder das zuletzt erfolgreich geladene Bundle zurück. | Param | Geben Sie | ein Beschreibung | | ------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **`options`** | `ResetOptions` | Enthält {@link [ResetOptions.toLastSuccessful](#resetoptions)}, `true` wird auf das integrierte Bundle zurückgesetzt und `false` wird auf das zuletzt erfolgreich geladene Bundle zurückgesetzt. | *** ## aktuell() [Section titled “aktuell()”](#aktuell) ````typescript current() => Promise ```Rufen Sie das aktuelle Bundle ab. Wenn keines festgelegt ist, wird `builtin` zurückgegeben. currentNative ist das Originalpaket, das auf dem Gerät installiert ist **Rückgaben:** Promise<CurrentBundleResult> -------------------- ## reload() ```typescript reload() => Promise ```` Laden Sie die Ansicht neu *** ## setMultiDelay(…) [Section titled “setMultiDelay(…)”](#setmultidelay) ```typescript setMultiDelay(options: MultiDelayConditions) => Promise ``` Legt ein {@link [DelayCondition](#delaycondition)}-Array fest, das Bedingungen enthält, die das Plugin verwendet, um die Aktualisierung zu verzögern. Nachdem alle Bedingungen erfüllt sind, wird der Update-Vorgang wie gewohnt erneut ausgeführt, sodass das Update nach einem Hintergrund- oder Beenden der App installiert wird. Für den Typ `date` sollte der Wert eine iso8601-Datumszeichenfolge sein. Für den Typ `background` sollte der Wert eine Zahl in Millisekunden sein. Für den Typ `nativeVersion` sollte der Wert die Versionsnummer sein. Für den Typ `kill` wird der Wert nicht verwendet. Die Funktion weist ein inkonsistentes Verhalten auf. Die Option kill löst das Update nach dem ersten Kill aus und nicht wie andere Optionen nach dem nächsten Hintergrund. Dies wird in einer zukünftigen Hauptversion behoben. | Param | Geben Sie | ein Beschreibung | | ------------- | ---------------------- | ------------------------------------------------------------------------------------------------------ | | **`options`** | `MultiDelayConditions` | Enthält das Array {@link [MultiDelayConditions](#multidelayconditions)} mit festzulegenden Bedingungen | **Seit:** 4.3.0 *** ## cancelDelay() [Section titled “cancelDelay()”](#canceldelay) ```typescript cancelDelay() => Promise ``` Bricht einen {@link [DelayCondition](#delaycondition)} ab, um ein Update sofort zu verarbeiten. **Seit:** 4.0.0 *** ## getLatest(…) [Section titled “getLatest(…)”](#getlatest) ```typescript getLatest(options?: GetLatestOptions | undefined) => Promise ``` Holen Sie sich das neueste Paket, das über die Update-URL verfügbar ist \| Param | Geben Sie | ein | ------------- | ------------------------------------------------------------- | | **`options`** | `GetLatestOptions` | **Rückgaben:** `Promise` **Seit:** 4.0.0 *** ## setChannel(…) [Section titled “setChannel(…)”](#setchannel) ```typescript setChannel(options: SetChannelOptions) => Promise ``` Legt den Kanal für dieses Gerät fest. Damit dies funktioniert, muss auf dem Kanal `allow_device_self_set` aktiviert sein.**Wichtige Hinweise:** – Verwenden Sie diese Methode nicht, um den Kanal beim Booten festzulegen. Verwenden Sie stattdessen `defaultChannel` in Ihrer Capacitor-Konfiguration. – Diese Methode ist für die Verwendung gedacht, nachdem die App fertig ist und der Benutzer interagiert hat (z. B. wenn er sich für ein Betaprogramm entscheidet). * **Öffentliche Kanäle können nicht selbst zugewiesen werden.** Wenn ein Kanal als `public` markiert ist, wird beim Aufruf von `setChannel()` ein Fehler zurückgegeben. Um einen öffentlichen Kanal zu verwenden, rufen Sie stattdessen `unsetChannel()` auf – das Gerät greift automatisch auf den passenden öffentlichen Kanal zurück. * Verwenden Sie `listChannels()`, um herauszufinden, welche Kanäle verfügbar sind und ob sie eine Selbstzuweisung zulassen. | Param | Geben Sie | ein Beschreibung | | ------------- | ------------------- | ----------------------------------------------------------------------------------------- | | **`options`** | `SetChannelOptions` | Ist der Kanal {@link [SetChannelOptions](#setchanneloptions)}, der festgelegt werden soll | **Rückgaben:** `Promise` **Seit:** 4.7.0 *** ## unsetChannel(…) [Section titled “unsetChannel(…)”](#unsetchannel) ```typescript unsetChannel(options: UnsetChannelOptions) => Promise ``` Deaktivieren Sie die Kanalüberschreibung für dieses Gerät. Nach dem Aufruf dieser Methode erhält das Gerät automatisch Updates vom **öffentlichen Kanal**, der seinen Bedingungen (Plattform, Gerätetyp, Build-Typ) entspricht. Dies ist nützlich, wenn: * Sie möchten ein Gerät zurück zum Standard-Update-Track verschieben * Sie möchten einen öffentlichen Kanal verwenden (da öffentliche Kanäle nicht über `setChannel()` selbst zugewiesen werden können) \| Param | Geben Sie | ein | ------------- | ------------------------------------------------------------------- | | **`options`** | `UnsetChannelOptions` | **Seit:** 4.7.0 *** ## getChannel() [Section titled “getChannel()”](#getchannel) ```typescript getChannel() => Promise ``` Holen Sie sich den Kanal für dieses Gerät **Rückgaben:** `Promise` **Seit:** 4.8.0 *** \##listChannels() ```typescript listChannels() => Promise ``` Listen Sie alle für dieses Gerät verfügbaren Kanäle auf. Gibt Kanäle zurück, die mit der aktuellen Umgebung des Geräts (Plattform, Emulator/reales Gerät, Entwickler-/Produkt-Build) kompatibel sind und entweder öffentlich sind oder Selbstzuweisung zulassen. Jeder Kanal im Ergebnis umfasst: * `public`: Wenn `true`, ist dies ein **Standardkanal**. Sie können es nicht mit `setChannel()` selbst zuweisen. Wenn Sie stattdessen Ihre Kanalzuweisung mit `unsetChannel()` entfernen, erhält das Gerät automatisch Updates von diesem öffentlichen Kanal. * `allow_self_set`: Wenn `true`, handelt es sich um einen **selbst zuweisbaren Kanal**. Mit `setChannel()` können Sie das Gerät explizit diesem Kanal zuordnen. **Rückgaben:** `Promise` **Seit:** 7.5.0 *** ## setCustomId(…) [Section titled “setCustomId(…)”](#setcustomid) ````typescript setCustomId(options: SetCustomIdOptions) => Promise ```Legen Sie eine benutzerdefinierte ID für dieses Gerät fest | Param | Geben Sie | ein Beschreibung | | ------------- | ----------------------------------------------------------------- | ----------------------------------------------------------------------------------- | | **`options`** | SetCustomIdOptions | ist die {@link SetCustomIdOptions} customId, die festgelegt werden soll | **Seit:** 4.9.0 -------------------- ## getBuiltinVersion() ```typescript getBuiltinVersion() => Promise ```` Rufen Sie die native App-Version oder die integrierte Version ab, sofern in der Konfiguration festgelegt **Rückgaben:** `Promise` **Seit:** 5.2.0 *** ## getDeviceId() [Section titled “getDeviceId()”](#getdeviceid) ```typescript getDeviceId() => Promise ``` Erhalten Sie eine eindeutige ID, die zur Identifizierung des Geräts verwendet wird (an den Auto-Update-Server gesendet). **Rückgaben:** `Promise` *** ## getPluginVersion() [Section titled “getPluginVersion()”](#getpluginversion) ```typescript getPluginVersion() => Promise ``` Holen Sie sich die native Capacitor Updater-Plugin-Version (an den Auto-Update-Server gesendet). **Rückgaben:** `Promise` *** ## isAutoUpdateEnabled() [Section titled “isAutoUpdateEnabled()”](#isautoupdateenabled) ```typescript isAutoUpdateEnabled() => Promise ``` Rufen Sie den Status der Konfiguration für die automatische Aktualisierung ab. **Rückgaben:** `Promise` *** ## removeAllListeners() [Section titled “removeAllListeners()”](#removealllisteners) ```typescript removeAllListeners() => Promise ``` Entfernen Sie alle Listener für dieses Plugin. **Seit:** 1.0.0 *** ## addListener(‘download’, …) [Section titled “addListener(‘download’, …)”](#addlistenerdownload) ```typescript addListener(eventName: 'download', listenerFunc: (state: DownloadEvent) => void) => Promise ``` Achten Sie auf das Bundle-Download-Ereignis in der App. Wird ausgelöst, sobald ein Download gestartet wurde, während des Downloads und wenn er beendet ist. Dadurch erhalten Sie während des Downloads alle Download-Prozentsätze zurück \| Param | Geben Sie | ein | ------------------- | ------------------------------------------------------------ | | **`eventName`** | `‘download’` | | **`listenerFunc`** | `(Status: DownloadEvent) => void` | **Rückgaben:** `Promise` **Seit:** 2.0.11 *** ## addListener(‘noNeedUpdate’, …) [Section titled “addListener(‘noNeedUpdate’, …)”](#addlistenernoneedupdate) ```typescript addListener(eventName: 'noNeedUpdate', listenerFunc: (state: NoNeedEvent) => void) => Promise ``` Achten Sie darauf, dass kein Aktualisierungsereignis erforderlich ist. Dies ist nützlich, wenn Sie bei jedem Start der App eine erzwungene Prüfung durchführen möchten \| Param | Geben Sie | ein | ------------------- | ----------------------------------------------------------------------- | | **`eventName`** | `‘noNeedUpdate’` | | **`listenerFunc`** | `(state: NoNeedEvent) => void` |**Rückgaben:** `Promise` **Seit:** 4.0.0 *** ## addListener(‘updateAvailable’, …) [Section titled “addListener(‘updateAvailable’, …)”](#addlistenerupdateavailable) ```typescript addListener(eventName: 'updateAvailable', listenerFunc: (state: UpdateAvailableEvent) => void) => Promise ``` Warten Sie auf verfügbare Update-Ereignisse. Dies ist nützlich, wenn Sie bei jedem Start der App eine Überprüfung erzwingen möchten \| Param | Geben Sie | ein | ------------------- | ------------------------------------------------------------- | | **`eventName`** | `‘updateAvailable’` | | **`listenerFunc`** | `(Status: UpdateAvailableEvent) => void` | **Rückgaben:** `Promise` **Seit:** 4.0.0 *** ## addListener(‘downloadComplete’, …) [Section titled “addListener(‘downloadComplete’, …)”](#addlistenerdownloadcomplete) ```typescript addListener(eventName: 'downloadComplete', listenerFunc: (state: DownloadCompleteEvent) => void) => Promise ``` Hören Sie sich den Download anKomplette Veranstaltungen. \| Param | Geben Sie | ein | ------------------- | ---------------------------------------------------------------------------- | | **`eventName`** | `‘downloadComplete’` | | **`listenerFunc`** | `(Status: DownloadCompleteEvent) => void` | **Returns:** `Promise` **Seit:** 4.0.0 *** ## addListener(‘majorAvailable’, …) [Section titled “addListener(‘majorAvailable’, …)”](#addlistenermajoravailable) ```typescript addListener(eventName: 'majorAvailable', listenerFunc: (state: MajorAvailableEvent) => void) => Promise ``` Warten Sie auf ein größeres Update-Ereignis in der App und informieren Sie Sie, wenn ein größeres Update blockiert wird, indem Sie „disableAutoUpdateBreaking“ festlegen \| Param | Geben Sie | ein | ------------------- | ------------------------------------------------------------ | | **`eventName`** | `‘majorAvailable’` | | **`listenerFunc`** | `(Status: MajorAvailableEvent) => void` | **Rückgaben:** `Promise` **Seit:** 2.3.0 *** ## addListener(‘updateFailed’, …) [Section titled “addListener(‘updateFailed’, …)”](#addlistenerupdatefailed) ```typescript addListener(eventName: 'updateFailed', listenerFunc: (state: UpdateFailedEvent) => void) => Promise ``` Achten Sie auf das Ereignis „Update-Fehler“ in der App und informieren Sie Sie, wenn die Installation des Updates beim nächsten App-Start fehlgeschlagen ist \| Param | Geben Sie | ein | ------------------- | ----------------------------------------------------------------------------------- | | **`eventName`** | `‘updateFailed’` | | **`listenerFunc`** | `(state: UpdateFailedEvent) => void` |**Rückgaben:** `Promise` **Seit:** 2.3.0 *** ## addListener(‘downloadFailed’, …) [Section titled “addListener(‘downloadFailed’, …)”](#addlistenerdownloadfailed) ```typescript addListener(eventName: 'downloadFailed', listenerFunc: (state: DownloadFailedEvent) => void) => Promise ``` Achten Sie auf das Ereignis „Download-Fehler“ in der App und informieren Sie Sie, wenn ein Bundle-Download fehlgeschlagen ist \| Param | Geben Sie | ein | ------------------- | ------------------------------------------------------------ | | **`eventName`** | `‘downloadFailed’` | | **`listenerFunc`** | `(Status: DownloadFailedEvent) => void` | **Rückgaben:** `Promise` **Seit:** 4.0.0 *** ## addListener(‘appReloaded’, …) [Section titled “addListener(‘appReloaded’, …)”](#addlistenerappreloaded) ```typescript addListener(eventName: 'appReloaded', listenerFunc: () => void) => Promise ``` Achten Sie auf ein Neuladeereignis in der App und informieren Sie Sie, wenn ein Neuladen stattgefunden hat \| Param | Geben Sie | ein | ------------------- | -------------------------- | | **`eventName`** | `‘appReloaded’` | | **`listenerFunc`** | `() => void` | **Rückgaben:** `Promise` **Seit:** 4.3.0 *** ## addListener(‘appReady’, …) [Section titled “addListener(‘appReady’, …)”](#addlistenerappready) ```typescript addListener(eventName: 'appReady', listenerFunc: (state: AppReadyEvent) => void) => Promise ``` Achten Sie auf das App-Ready-Ereignis in der App und informieren Sie Sie, wenn die App einsatzbereit ist \| Param | Geben Sie | ein | ------------------- | ------------------------------------------------------------ | | **`eventName`** | `‘appReady’` | | **`listenerFunc`** | `(Status: AppReadyEvent) => void` | **Returns:** `Promise` **Seit:** 5.1.0 *** ## isAutoUpdateAvailable() [Section titled “isAutoUpdateAvailable()”](#isautoupdateavailable) ```typescript isAutoUpdateAvailable() => Promise ``` Rufen Sie ab, ob die automatische Aktualisierung verfügbar ist (nicht durch serverUrl deaktiviert). **Rückgaben:** `Promise` *** ## getNextBundle() [Section titled “getNextBundle()”](#getnextbundle) ```typescript getNextBundle() => Promise ``` Holen Sie sich das nächste Bundle, das beim Neuladen der App verwendet wird. Gibt null zurück, wenn kein nächstes Bundle festgelegt ist. **Rückgaben:** `Promise` **Seit:** 6.8.0 *** ## setShakeMenu(…) [Section titled “setShakeMenu(…)”](#setshakemenu) ```typescript setShakeMenu(options: SetShakeMenuOptions) => Promise ``` Aktivieren oder deaktivieren Sie das Shake-Menü für Debugging-/Testzwecke| Param | Geben Sie | ein Beschreibung | | ------------- | ------------------------------------------------------------------- | -------------------------------------------------------- | | **`options`** | `SetShakeMenuOptions` | Enthält einen aktivierten booleschen Wert zum Aktivieren oder Deaktivieren des Shake-Menüs | **Seit:** 7.5.0 *** ## isShakeMenuEnabled() [Section titled “isShakeMenuEnabled()”](#isshakemenuenabled) ```typescript isShakeMenuEnabled() => Promise ``` Rufen Sie den aktuellen Status des Shake-Menüs ab **Rückgaben:** `Promise` **Seit:** 7.5.0 *** ## Schnittstellen [Section titled “Schnittstellen”](#schnittstellen) ### AppReadyResult [Section titled “AppReadyResult”](#appreadyresult) \| Stütze | Geben Sie | ein | ------------ | ------------------------------------------------- | | **`bundle`** | `BundleInfo` | ### BundleInfo [Section titled “BundleInfo”](#bundleinfo) \| Stütze | Geben Sie | ein | ---------------- | ----------------------------------------------------- | | **`id`** | `string` | | **`version`** | `string` | | **`downloaded`** | `string` | | **`checksum`** | `string` | | **`status`** | `BundleStatus` | ### UpdateUrl [Section titled “UpdateUrl”](#updateurl) \| Stütze | Geben Sie | ein | --------- | ------------------- | | **`url`** | `string` | ### StatsUrl [Section titled “StatsUrl”](#statsurl) \| Stütze | Geben Sie | ein | --------- | ------------------- | | **`url`** | `string` | ### Kanal-URL [Section titled “Kanal-URL”](#kanal-url) \| Stütze | Geben Sie | ein | --------- | ------------------- | | **`url`** | `string` | ### Download-Optionen [Section titled “Download-Optionen”](#download-optionen) Diese URL und Versionen werden zum Herunterladen des Bundles vom Server verwendet. Wenn Sie ein Backend verwenden, werden alle Informationen von der Methode getLatest bereitgestellt. Wenn Sie kein Backend verwenden, müssen Sie die URL und die Version des Bundles angeben. Prüfsumme und SessionKey sind erforderlich, wenn Sie das Bundle mit dem Befehl „encrypt“ CLI verschlüsselt haben. Sie sollten diese als Ergebnis des Befehls erhalten.| Stütze | Geben Sie | ein Beschreibung | Standard | Seit | | ---------------- | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------- | ----- | | **`url`** | `string` | Die URL der Bundle-ZIP-Datei (z. B. dist.zip), die heruntergeladen werden soll. (Dies kann eine beliebige URL sein. Zum Beispiel: Amazon S3, ein GitHub-Tag, jeder andere Ort, an dem Sie Ihr Paket gehostet haben.) | | | | **`version`** | `string` | Der Versionscode/Name dieses Bundles/dieser Version | | | | **`sessionKey`** | `string` | Der Sitzungsschlüssel für das Update, wenn das Bundle mit einem Sitzungsschlüssel | verschlüsselt ist `undefiniert` | 4.0.0 | | **`checksum`** | `string` | Die Prüfsumme für das Update sollte in sha256 vorliegen und mit einem privaten Schlüssel verschlüsselt sein, wenn das Bundle verschlüsselt ist | `undefiniert` | 4.0.0 | | **`manifest`** | `ManifestEntry[]` | Das Manifest für Delta (Manifest)-Downloads mehrerer Dateien | `undefiniert` | 6.1.0 | ### ManifestEntry [Section titled “ManifestEntry”](#manifestentry) \| Stütze | Geben Sie | ein | ------------------- | ------------ | | **`file_name`** | `string | null` | | **`file_hash`** | `string | null` | | **`download_url`** | `string | null` | ### BundleId [Section titled “BundleId”](#bundleid) \| Stütze | Geben Sie | ein | -------- | ------------------- | | **`id`** | `string` | ### BundleListResult [Section titled “BundleListResult”](#bundlelistresult) \| Stütze | Geben Sie | ein | ------------- | ------------------------- | | **`bundles`** | `BundleInfo[]` | ### Listenoptionen| Stütze | Geben Sie | ein Beschreibung | Standard | Seit | [Section titled “Listenoptionen| Stütze | Geben Sie | ein Beschreibung | Standard | Seit |”](#listenoptionen-stütze--geben-sie--ein-beschreibung--standard--seit) \| --------- | -------------------- | ----------------------------------------------------------------------- | ------------------- | ------ | | **`raw`** | `boolean` | Ob die Rohpaketliste oder das Manifest zurückgegeben werden soll. Bei „true“ versucht die Liste, die interne Datenbank statt Dateien auf der Festplatte zu lesen. | `false` | 6.14.0 | ### Optionen zurücksetzen [Section titled “Optionen zurücksetzen”](#optionen-zurücksetzen) \| Stütze | Geben Sie | ein | ---------------------- | -------------------- | | **`toLastSuccessful`** | `boolean` | ### CurrentBundleResult [Section titled “CurrentBundleResult”](#currentbundleresult) \| Stütze | Geben Sie | ein | ------------ | ------------------------------------------------- | | **`bundle`** | `BundleInfo` | | **`native`** | `string` | ### MultiDelayConditions [Section titled “MultiDelayConditions”](#multidelayconditions) \| Stütze | Geben Sie | ein | --------------------- | -------------- | | **`delayConditions`** | `DelayCondition[]` | ### Verzögerungsbedingung [Section titled “Verzögerungsbedingung”](#verzögerungsbedingung) | Stütze | Geben Sie | ein Beschreibung | | ----------- | ---------------- | ---------------------------------------------------- | | **`kind`** | `DelayUntilNext` | Richten Sie Verzögerungsbedingungen in setMultiDelay | | **`value`** | `string` | | ### Neueste Version| Stütze | Geben Sie | ein Beschreibung | Seit | [Section titled “Neueste Version| Stütze | Geben Sie | ein Beschreibung | Seit |”](#neueste-version-stütze--geben-sie--ein-beschreibung--seit) \| ---------------- | ------------- | -------------------------- | ----- | | **`version`** | `string` | Ergebnis der getLatest-Methode | 4.0.0 | | **`checksum`** | `string` | | 6 | | **`major`** | `boolean` | | | | **`message`** | `string` | | | | **`sessionKey`** | `string` | | | | **`error`** | `string` | | | | **`old`** | `string` | | | | **`url`** | `string` | | | | **`manifest`** | `ManifestEntry[]` | | 6.1 | ### GetLatestOptions [Section titled “GetLatestOptions”](#getlatestoptions) | Stütze | Geben Sie | ein Beschreibung | Standard | Seit | | ------------- | --------- | -------------------------------------------------------------------------------------------------------------------------- | -------- | ---- | | **`channel`** | `string` | Der Kanal muss die neueste Version erhalten, damit dies funktioniert. Der Kanal muss „self\_assign“ zulassen `undefiniert` | 6.8.0 | | ### ChannelRes [Section titled “ChannelRes”](#channelres) | Stütze | Geben Sie | ein Beschreibung | Seit | | ------------- | --------- | ----------------------------------------- | ----- | | **`status`** | `string` | Aktueller Status des eingestellten Kanals | 4.7.0 | | **`error`** | `string` | | | | **`message`** | `string` | | | ### SetChannelOptions [Section titled “SetChannelOptions”](#setchanneloptions) \| Stütze | Geben Sie | ein | --------- | -------------------- | | **`channel`** | `string` | | **`triggerAutoUpdate`** | `boolean` | ### UnsetChannelOptions [Section titled “UnsetChannelOptions”](#unsetchanneloptions) \| Stütze | Geben Sie | ein | --------- | -------------------- | | **`triggerAutoUpdate`** | `boolean` | ### GetChannelRes| Stütze | Geben Sie | ein Beschreibung | Seit | [Section titled “GetChannelRes| Stütze | Geben Sie | ein Beschreibung | Seit |”](#getchannelres-stütze--geben-sie--ein-beschreibung--seit) \| -------------- | -------------------- | -------------- | ----- | | **`channel`** | `string` | Aktueller Status des Get-Kanals | 4.8.0 | | **`error`** | `string` | | | | **`message`** | `string` | | | | **`status`** | `string` | | | | **`allowSet`** | `boolean` | | | ### ListChannelsResult [Section titled “ListChannelsResult”](#listchannelsresult) | Stütze | Geben Sie | ein Beschreibung | Seit | | -------------- | --------------- | ---------------------------- | ----- | | **`channels`** | `ChannelInfo[]` | Liste der verfügbaren Kanäle | 7.5.0 | ### ChannelInfo [Section titled “ChannelInfo”](#channelinfo) | Stütze | Geben Sie | ein Beschreibung | Seit | | -------------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | | **`id`** | `string` | Die Kanal-ID | 7.5.0 | | **`name`** | `string` | Der Kanalname | 7.5.0 | | **`public`** | `boolean` | Wenn „true“, handelt es sich um einen Standard-/Fallback-Kanal. Geräte können sich nicht selbst öffentlichen Kanälen zuweisen. Wenn ein Gerät stattdessen seine Kanalüberschreibung entfernt (mithilfe von `unsetChannel()`), erhält es automatisch Aktualisierungen vom entsprechenden öffentlichen Kanal. | 7.5.0 | | **`allow_self_set`** | `boolean` | Wenn „true“, können sich Geräte mithilfe von `setChannel()` explizit selbst diesem Kanal zuweisen. Dies wird normalerweise für Betatests, A/B-Tests oder Opt-in-Update-Tracks verwendet. | 7.5.0 | ### SetCustomIdOptions [Section titled “SetCustomIdOptions”](#setcustomidoptions) \| Stütze | Geben Sie | ein | -------------- | ------------------- | | **`customId`** | `string` | ### BuiltinVersion [Section titled “BuiltinVersion”](#builtinversion) \| Stütze | Geben Sie | ein | ------------- | ------------------- | | **`version`** | `string` | ### Geräte-ID [Section titled “Geräte-ID”](#geräte-id) \| Stütze | Geben Sie | ein | -------------- | ------------------- | | **`deviceId`** | `string` | ### PluginVersion [Section titled “PluginVersion”](#pluginversion) \| Stütze | Geben Sie | ein | ------------- | ------------------- | | **`version`** | `string` | ### AutoUpdateEnabled [Section titled “AutoUpdateEnabled”](#autoupdateenabled) \| Stütze | Geben Sie | ein | ------------- | -------------------- | | **`enabled`** | `boolean` | ### PluginListenerHandle [Section titled “PluginListenerHandle”](#pluginlistenerhandle) \| Stütze | Geben Sie | ein | ------------ | ----------------------------------------- | | **`remove`** | `() => Versprechen` | ### DownloadEvent| Stütze | Geben Sie | ein Beschreibung | Seit | [Section titled “DownloadEvent| Stütze | Geben Sie | ein Beschreibung | Seit |”](#downloadevent-stütze--geben-sie--ein-beschreibung--seit) \| ------------- | ------------------------------------------------- | ---------------------------------------------- | ----- | | **`percent`** | `Nummer` | Aktueller Downloadstatus, zwischen 0 und 100. | 4.0.0 | | **`bundle`** | `BundleInfo` | | | ### NoNeedEvent [Section titled “NoNeedEvent”](#noneedevent) | Stütze | Geben Sie | ein Beschreibung | Seit | | ------------ | ------------ | --------------------------------------------- | ----- | | **`bundle`** | `BundleInfo` | Aktueller Downloadstatus, zwischen 0 und 100. | 4.0.0 | ### UpdateAvailableEvent [Section titled “UpdateAvailableEvent”](#updateavailableevent) | Stütze | Geben Sie | ein Beschreibung | Seit | | ------------ | ------------ | --------------------------------------------- | ----- | | **`bundle`** | `BundleInfo` | Aktueller Downloadstatus, zwischen 0 und 100. | 4.0.0 | ### DownloadCompleteEvent [Section titled “DownloadCompleteEvent”](#downloadcompleteevent) | Stütze | Geben Sie | ein Beschreibung | Seit | | ------------ | ------------ | ----------------------------------------------------- | ----- | | **`bundle`** | `BundleInfo` | Wird ausgegeben, wenn ein neues Update verfügbar ist. | 4.0.0 | ### MajorAvailableEvent [Section titled “MajorAvailableEvent”](#majoravailableevent) | Stütze | Geben Sie | ein Beschreibung | Seit | | ------------- | --------- | --------------------------------------------------------- | ----- | | **`version`** | `string` | Wird ausgegeben, wenn ein neues Hauptpaket verfügbar ist. | 4.0.0 | ### UpdateFailedEvent [Section titled “UpdateFailedEvent”](#updatefailedevent) | Stütze | Geben Sie | ein Beschreibung | Seit | | ------------ | ------------ | ------------------------------------------------------------------------ | ----- | | **`bundle`** | `BundleInfo` | Wird ausgegeben, wenn die Installation eines Updates fehlgeschlagen ist. | 4.0.0 | ### DownloadFailedEvent [Section titled “DownloadFailedEvent”](#downloadfailedevent) | Stütze | Geben Sie | ein Beschreibung | Seit | | ------------- | --------- | ----------------------------------------------- | ----- | | **`version`** | `string` | Wird ausgegeben, wenn ein Download fehlschlägt. | 4.0.0 | ### AppReadyEvent| Stütze | Geben Sie | ein Beschreibung | Seit | [Section titled “AppReadyEvent| Stütze | Geben Sie | ein Beschreibung | Seit |”](#appreadyevent-stütze--geben-sie--ein-beschreibung--seit) \| ------------ | ------------------------------------------------- | ------------------------------------- | ----- | | **`bundle`** | `BundleInfo` | Wird ausgegeben, wenn die App einsatzbereit ist. | 5.2.0 | | **`status`** | `string` | | | ### AutoUpdateVerfügbar [Section titled “AutoUpdateVerfügbar”](#autoupdateverfügbar) \| Stütze | Geben Sie | ein | --------------- | -------------------- | | **`available`** | `boolean` | ### SetShakeMenuOptions [Section titled “SetShakeMenuOptions”](#setshakemenuoptions) \| Stütze | Geben Sie | ein | ------------- | -------------------- | | **`enabled`** | `boolean` | ### ShakeMenuEnabled [Section titled “ShakeMenuEnabled”](#shakemenuenabled) \| Stütze | Geben Sie | ein | ------------- | -------------------- | | **`enabled`** | `boolean` | ## Geben Sie Aliase ein [Section titled “Geben Sie Aliase ein”](#geben-sie-aliase-ein) ### BundleStatus [Section titled “BundleStatus”](#bundlestatus) ausstehend: Das Bundle muss noch als nächstes Bundle **SET** werden. wird heruntergeladen: Das Bundle wird heruntergeladen. Erfolg: Das Bundle wurde heruntergeladen und kann als nächstes Bundle **SET** werden. Fehler: Das Bundle konnte nicht heruntergeladen werden. `‘success’ | ‘Fehler’ | ‘ausstehend’ | ‘Herunterladen’` ### DelayUntilNext [Section titled “DelayUntilNext”](#delayuntilnext) `‘Hintergrund’ | ‘töten’ | ‘nativeVersion’ | ‘Datum’` # Cordova > Untersuchung der möglichen Verfügbarkeit des capacitor-updater-Plugins für Cordova und der Herausforderungen bei seiner Entwicklung. Sie haben sich gefragt, ob dieses Plugin jemals für Cordova verfügbar sein wird. Wir haben ein F\&E-Repository dafür gestartet, aber es ist eine enorme Menge an Arbeit. ## Probleme [Section titled “Probleme”](#probleme) Wir wissen, dass wir es schaffen können, aber dafür müssen wir den gesamten Code der Cordova-Codebasis lesen, wie wir es für Capacitor getan haben, um zu verstehen, wie es mit allen Capgo-Funktionen funktioniert. Die Android-Version ist einfacher zu erstellen, da beide Java verwenden, aber iOS erfordert eine vollständige Neuschreibung, da Swift in Cordova noch nicht gut unterstützt wird. ## Lösung [Section titled “Lösung”](#lösung) In der Zwischenzeit können Sie Folgendes tun: * [Unterstützen Sie uns](https://github.com/sponsors/cap-go) auf GitHub und wir können dies priorisieren. Dies wird mindestens 1 Monat Arbeit erfordern. * Engagieren Sie uns als [Berater](https://capgo.app/consulting/), wir sind es gewohnt, großen Unternehmen bei der Migration zu Capacitor zu helfen, es dauert normalerweise einen Monat, und der [Nutzen](https://ionic.io/resources/articles/capacitor-vs-cordova-modern-hybrid-app-development) ist enorm für Ihr Team # Debugging > Wie Sie Ihre Capgo-Updates debuggen und Probleme mit Ihrer Konfiguration verstehen ## Cloud-Logs verstehen: [Section titled “Cloud-Logs verstehen:”](#cloud-logs-verstehen) ### Vom Backend gesendet [Section titled “Vom Backend gesendet”](#vom-backend-gesendet) | Code | Beschreibung | | -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **InvalidIp** | Der Benutzer befindet sich in einem Google-Rechenzentrum und das Update ist weniger als 4 Stunden alt. Dies wird durchgeführt, um zu verhindern, dass Geräte von Google-Bots als Geräte in Ihrem Konto gezählt werden. | | **needPlanUpgrade** (vorher **needUpgrade**) | Zeigt an, dass Sie das Limit Ihres Plans erreicht haben und das Gerät keine Updates erhält, bis Sie upgraden oder bis zum nächsten Monat. | | **noNew** | Das Gerät hat die neueste verfügbare Version. | | **disablePlatformIos** | Das Gerät läuft auf der iOS-Plattform, aber diese ist in den Kanaleinstellungen deaktiviert. | | **disablePlatformAndroid** | Das Gerät läuft auf der Android-Plattform, aber diese ist in den Kanaleinstellungen deaktiviert. | | **disableAutoUpdate** | ”major" | | **disableAutoUpdateUnderNative** | Das Gerät hat Version (`1.2.3`), und der Kanal hat ein Update (`1.2.2`) unter der Geräteversion zum Senden, aber das ist in den Kanaleinstellungen deaktiviert. | | **disableDevBuild** | Das Gerät hat einen Dev-Build, aber dieser ist in den Kanaleinstellungen deaktiviert. | | **disableEmulator** | Das Gerät ist ein Emulator, aber dieser ist in den Kanaleinstellungen deaktiviert. | | **cannotGetBundle** | Fehler beim Generieren einer gültigen signierten URL für den Bundle-Download. Dies tritt auf, wenn die Bundle-URL-Generierung fehlschlägt oder eine ungültige URL zurückgibt (die nicht mit http/https beginnt) und kein Manifest als Fallback verfügbar ist. | | **cannotUpdateViaPrivateChannel** | Das Gerät hat versucht, sich selbst mit einem privaten Kanal zu verknüpfen, aber die Kanaleinstellungen erlauben keine Selbstzuordnung von Geräten (`allow_device_self_set` ist false) und der Kanal ist nicht öffentlich. | | **channelMisconfigured** | Der Kanal ist so konfiguriert, dass Auto-Update nach Versionsnummer deaktiviert wird (`disable_auto_update: 'version_number'`), aber das `min_update_version`-Feld des Bundles ist null, was es unmöglich macht zu bestimmen, welche Geräte das Update erhalten sollten. | | **disableAutoUpdateMetadata** | Auto-Update ist durch Versionsnummer-Metadaten deaktiviert. Der Kanal erfordert, dass die Version des Geräts mindestens `min_update_version` ist, aber die aktuelle Version des Geräts liegt unter diesem Schwellenwert. | | **disableAutoUpdateToMajor** | Kanaleinstellung `disable_auto_update: 'major'` verhindert Updates, die die Hauptversionsnummer erhöhen würden (z.B. Blockierung von 1.x.x beim Update auf 2.x.x). | | **disableAutoUpdateToMinor** | Kanaleinstellung `disable_auto_update: 'minor'` verhindert Updates, die die Nebenversionsnummer erhöhen würden (z.B. Blockierung von 1.2.x beim Update auf 1.3.x). | | **disableAutoUpdateToPatch** | Kanaleinstellung `disable_auto_update: 'patch'` verhindert Updates, die die Patch-Versionsnummer erhöhen würden, oder erlaubt nur Patch-Level-Updates innerhalb derselben major.minor-Version (z.B. 1.2.3 kann auf 1.2.4 updaten, aber nicht auf 1.2.2 oder 1.3.0). | | **missingBundle** | Das diesem Kanal zugewiesene Bundle hat keinen herunterladbaren Inhalt. Das bedeutet, dass das Bundle keine `external_url`, keinen `r2_path` hat, es ist keine eingebaute Version und es sind keine Manifest-Einträge zum Download verfügbar. | | **NoChannelOrOverride** | Für diese App ist kein Standardkanal konfiguriert und das Gerät hat keine spezifische Kanal-Überschreibung zugewiesen. Mindestens eines muss vorhanden sein, damit Updates funktionieren. | | **rateLimited** | Das Gerät wurde aufgrund übermäßiger Anfragen ratenlimitiert. | ### Vom Gerät gesendet [Section titled “Vom Gerät gesendet”](#vom-gerät-gesendet) | Code | Beschreibung | | -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **ping** | Interne Testaktion zur Überprüfung, ob das Stats-System korrekt funktioniert. | | **get** | Informationen zum Herunterladen der neuen Version wurden an das Gerät gesendet. | | **delete** | Ein Bundle wurde auf dem Gerät gelöscht. | | **set** | Ein Bundle wurde auf dem Gerät gesetzt. | | **set\_fail** | Das Bundle konnte nicht gesetzt werden. | | **reset** | Das Gerät wurde auf das `builtin`-Bundle zurückgesetzt. | | **download\_XX** | Ein neues Bundle wurde heruntergeladen - Fortschritt angegeben durch XX% (in 10%-Schritten). | | **download\_complete** | Das neue Bundle wurde vollständig heruntergeladen. | | **download\_manifest\_start** | Das Gerät hat mit dem Herunterladen des Update-Manifests begonnen. | | **download\_manifest\_complete** | Das Gerät hat das Herunterladen des Update-Manifests abgeschlossen. | | **download\_zip\_start** | Das Gerät hat mit dem Herunterladen des Bundle-Archivs begonnen. | | **download\_zip\_complete** | Das Gerät hat das Herunterladen des Bundle-Archivs abgeschlossen. | | **download\_manifest\_file\_fail** | Ein Manifest-Eintrag konnte nicht heruntergeladen werden; die Stats-Payload gibt `version_name` in der Form `version:fileName` an, um die Datei zu lokalisieren. | | **download\_manifest\_checksum\_fail** | Die Manifest-Datei hat die Prüfsummenvalidierung nicht bestanden. | | **download\_manifest\_brotli\_fail** | Die Manifest-Datei konnte nicht mit Brotli dekomprimiert werden. | | **download\_fail** | Das neue Bundle konnte nicht heruntergeladen werden. | | **update\_fail** | Das neue Bundle wurde installiert, konnte aber `notifyAppReady` nicht aufrufen. | | **checksum\_fail** | Das neue Bundle hat die Prüfsummenvalidierung nicht bestanden. Wenn Sie Capgo Cloud verwenden und diesen Fehler erhalten, bedeutet dies typischerweise, dass Ihre App-Version einen anderen Prüfsummentyp erwartet. Die neueste Version des CLI und der Plugins (Version 5.10.0+, 6.25.0+ oder 7+) verwenden SHA256-Prüfsummen, während ältere Plugins CRC32 verwendeten. Wenn Sie einen Prüfsummenfehler sehen, überprüfen Sie, ob die Prüfsumme CRC32 (ein kürzerer Hash) anstelle von SHA256 ist. Dies deutet normalerweise darauf hin, dass das Bundle mit einer alten Version des CLI hochgeladen wurde. Überprüfen Sie Ihre Bundle-Version im Capgo-Dashboard - seit Version 5.10.0/6.25.0/7 erstellte Bundles sollten SHA256 verwenden. Wenn Sie CRC32-Prüfsummen sehen, stellen Sie sicher, dass Sie die neueste Plugin-Version lokal installiert haben (das CLI prüft Ihre lokale Plugin-Version, um zu bestimmen, welcher Prüfsummentyp hochgeladen werden soll), aktualisieren Sie dann Ihr CLI und laden Sie das Bundle erneut hoch. | | **windows\_path\_fail** | Die ZIP-Datei enthält Dateien mit Windows-Pfaden, die illegal sind | | **canonical\_path\_fail** | Der Pfad der Dateien ist nicht kanonisch | | **directory\_path\_fail** | Es gibt einen Fehler im Pfad der ZIP-Dateien | | **unzip\_fail** | Entpacken fehlgeschlagen | | **low\_mem\_fail** | Download fehlgeschlagen wegen geringem Speicher auf dem Gerät | | **app\_moved\_to\_background** | Die Anwendung wechselte in den Hintergrund. | | **app\_moved\_to\_foreground** | Die Anwendung wechselte in den Vordergrund. | | **decrypt\_fail** | Entschlüsselung des heruntergeladenen Bundles fehlgeschlagen. | | **getChannel** | Der aktuelle Kanal für das Gerät wurde abgefragt. | | **setChannel** | Ein Kanal wurde erfolgreich für das Gerät gesetzt. | | **uninstall** | Die Anwendung wurde deinstalliert oder Capgo-Daten gelöscht. | | **blocked\_by\_server\_url** | Server.url ist in Ihrer Capacitor-Konfiguration vorhanden, dies lässt Capacitor eine Remote-URL bereitstellen und lokale Dateien ignorieren, während unser Updater für die Funktion mit lokalen Dateien gemacht ist. Server.url wird von den Capacitor-Machern als schlechte Praxis in der Produktion angesehen und führt zu vielen Problemen und Plugins, die nicht richtig funktionieren. | ### Bundle-Status [Section titled “Bundle-Status”](#bundle-status) * `SUCCESS`: Bundle-Installation abgeschlossen * `ERROR`: Installation oder Download fehlgeschlagen * `PENDING`: Download abgeschlossen, Release ausstehend * `DELETED`: Bundle gelöscht, wird noch für Statistiken angezeigt * `DOWNLOADING`: Derzeit wird ein Bundle heruntergeladen ## Geräte-Logs verstehen: [Section titled “Geräte-Logs verstehen:”](#geräte-logs-verstehen) ### Debug-Befehl: [Section titled “Debug-Befehl:”](#debug-befehl) Es gibt einen Debug-Befehl für Capgo-Cloud-Benutzer. ```bash npx @capgo/cli@latest app debug ``` Dies ermöglicht es Ihnen, alle Ereignisse in der App zu überprüfen und eine Lösung zu finden, wenn Updates nicht stattfinden. ### iOS [Section titled “iOS”](#ios) Um Ihre Logs in Xcode zu finden: [Getting the Device Log in Xcode ](https://intercom.help/deploygate/en/articles/4682692-getting-the-device-log-in-xcode) ### Android: [Section titled “Android:”](#android) Um Ihre Logs in Android Studio zu finden: [View logs with Logcat ](https://developer.android.com/studio/debug/am-logcat) ### Erklärung der Logs [Section titled “Erklärung der Logs”](#erklärung-der-logs) * `Failed to download from` **=>** identisch mit **download\_fail** * `notifyAppReady was not called, roll back current bundle` => identisch mit **update\_fail** ## Das heruntergeladene Bundle auf Ihrem Gerät finden [Section titled “Das heruntergeladene Bundle auf Ihrem Gerät finden”](#das-heruntergeladene-bundle-auf-ihrem-gerät-finden) ### iOS [Section titled “iOS”](#ios-1) Um auf iOS zu debuggen, müssen Sie die App auf Ihren Computer dumpen, Sie können dies folgendermaßen tun: Xcode hat eine eingebaute Funktion zur Inspektion des Dateisystems von entwicklerinstallierten Apps auf einem iOS-Gerät. ![Xcode Window-Menü zeigt Geräte- und Simulator-Option](/ios_debug_update_1.webp) Um dies zu erreichen: * Verbinden Sie Ihr Gerät mit Ihrem Mac und wählen Sie Fenster > Geräte in der Xcode-Menüleiste. * Wählen Sie Ihr Gerät im linken Bereich unter dem Abschnitt Geräte aus. * Dies zeigt eine Liste der entwicklerinstallierten Apps für dieses Gerät an. * Wählen Sie die App aus, die Sie inspizieren möchten, und wählen Sie dann das 3-Punkte-Symbol am unteren Bildschirmrand aus. * Hier können Sie das aktuelle Dateisystem anzeigen, indem Sie einen Snapshot davon herunterladen. ![Xcode-Geräte-Panel zeigt App-Container-Download-Option](/ios_debug_update_2.webp) Durch Auswahl von Container herunterladen… wird ein Snapshot des Dateisystems als .xcappdata-Datei heruntergeladen und exportiert, die Sie durchsuchen können. ![Heruntergeladene xcappdata-Datei mit Paketinhalt anzeigen-Kontextmenü](/ios_debug_update_3.webp) Klicken Sie mit der rechten Maustaste auf diese Datei und wählen Sie Paketinhalt anzeigen, um den Ordner zu öffnen. Öffnen Sie den Ordner App Data, und Sie sollten nun einige Ordner wie Documents, Library, tmp usw. sehen. ![iOS-App-Container-Ordnerstruktur zeigt Documents- und Library-Ordner](/ios_debug_update_4.webp) Dann finden Sie eine Version in 2 Ordnern: `library/NoCloud/ionic_built_snapshots` ist nach dem App-Neustart erforderlich und `documents/versions` für Hot Reload ### Android [Section titled “Android”](#android-1) Um auf Android zu debuggen, müssen Sie vom Android Studio aus auf das Gerät zugreifen: * Klicken Sie auf Ansicht > Tool-Fenster > Gerätedatei-Explorer oder klicken Sie auf die Schaltfläche Gerätedatei-Explorer in der Tool-Fenster-Leiste, um den Gerätedatei-Explorer zu öffnen. * Wählen Sie ein Gerät aus der Dropdown-Liste aus. * Öffnen Sie den Pfad **data/data/APP\_NAME/** wobei **APP\_NAME Ihre App-ID ist.** ![Android Studio Gerätedatei-Explorer zeigt App-Daten-Verzeichnis](/android_debug_update.webp) Dann finden Sie den `versions`-Ordner, um alle Versionen zu sehen Wussten Sie schon? Auf Android werden alle Versionen in einem Ordner gespeichert, im Gegensatz zu iOS, wo sie an zwei Orten dupliziert werden müssen. ## iOS-Produktions-Crash-Logs verstehen [Section titled “iOS-Produktions-Crash-Logs verstehen”](#ios-produktions-crash-logs-verstehen) [How to review your app's crash logs ](https://developer.apple.com/news/?id=nra79npr) # Ereignisse > Alle Ereignisse, auf die Sie im Capacitor Updater Plugin lauschen können, um den Update-Status und Fortschritt zu überwachen Das Capacitor Updater Plugin bietet mehrere Ereignisse, auf die Sie lauschen können, um den Update-Prozess zu überwachen und auf verschiedene Zustände zu reagieren. ## Event-Listener-Einrichtung [Section titled “Event-Listener-Einrichtung”](#event-listener-einrichtung) Um auf Ereignisse zu lauschen, verwenden Sie die `addListener`-Methode auf dem `CapacitorUpdater`-Objekt: ```typescript import { CapacitorUpdater } from '@capgo/capacitor-updater'; // Add a listener const listener = await CapacitorUpdater.addListener('eventName', (event) => { // Handle the event }); // Remove the listener when no longer needed listener.remove(); // Remove all listeners await CapacitorUpdater.removeAllListeners(); ``` ## Verfügbare Ereignisse [Section titled “Verfügbare Ereignisse”](#verfügbare-ereignisse) ### `download` [Section titled “download”](#download) Wird während des Bundle-Download-Prozesses ausgelöst. Bietet Informationen zum Download-Fortschritt. ```typescript CapacitorUpdater.addListener('download', (event) => { console.log(`Download progress: ${event.percent}%`); console.log('Bundle info:', event.bundle); }); ``` **Event-Daten:** * `percent`: number - Download-Fortschritt in Prozent (0-100) * `bundle`: BundleInfo - Informationen über das heruntergeladene Bundle Note Dieses Ereignis wird mehrfach während des Downloads ausgelöst, um den Fortschritt zu melden. ### `noNeedUpdate` [Section titled “noNeedUpdate”](#noneedupdate) Wird ausgelöst, wenn eine Überprüfung auf Updates feststellt, dass kein Update erforderlich ist. ```typescript CapacitorUpdater.addListener('noNeedUpdate', (event) => { console.log('App is up to date'); console.log('Current bundle:', event.bundle); }); ``` **Event-Daten:** * `bundle`: BundleInfo - Informationen über das aktuelle Bundle ### `updateAvailable` [Section titled “updateAvailable”](#updateavailable) Wird ausgelöst, wenn ein neues Update zum Download verfügbar ist. ```typescript CapacitorUpdater.addListener('updateAvailable', (event) => { console.log('Update available'); console.log('New bundle:', event.bundle); // You can trigger a download here if needed }); ``` **Event-Daten:** * `bundle`: BundleInfo - Informationen über das verfügbare Update-Bundle ### `downloadComplete` [Section titled “downloadComplete”](#downloadcomplete) Wird ausgelöst, wenn ein Bundle-Download erfolgreich abgeschlossen wurde. ```typescript CapacitorUpdater.addListener('downloadComplete', (event) => { console.log('Download completed'); console.log('Downloaded bundle:', event.bundle); // You might want to set this bundle as next }); ``` **Event-Daten:** * `bundle`: BundleInfo - Informationen über das heruntergeladene Bundle ### `majorAvailable` [Section titled “majorAvailable”](#majoravailable) Wird ausgelöst, wenn ein größeres Update verfügbar ist, aber durch Auto-Update-Einstellungen blockiert wird. ```typescript CapacitorUpdater.addListener('majorAvailable', (event) => { console.log('Major update available:', event.version); // Notify user about major update }); ``` **Event-Daten:** * `version`: string - Die Versionsnummer des größeren Updates Tip Größere Updates erfordern normalerweise Benutzerbestätigung oder App-Store-Updates. Verwenden Sie dieses Ereignis, um Benutzer entsprechend zu benachrichtigen. ### `updateFailed` [Section titled “updateFailed”](#updatefailed) Wird ausgelöst, wenn ein Update beim nächsten App-Start nicht installiert werden konnte. ```typescript CapacitorUpdater.addListener('updateFailed', (event) => { console.error('Update failed to install'); console.log('Failed bundle:', event.bundle); // Handle rollback or retry logic }); ``` **Event-Daten:** * `bundle`: BundleInfo - Informationen über das Bundle, dessen Installation fehlgeschlagen ist ### `downloadFailed` [Section titled “downloadFailed”](#downloadfailed) Wird ausgelöst, wenn ein Bundle-Download fehlgeschlagen ist. ```typescript CapacitorUpdater.addListener('downloadFailed', (event) => { console.error('Download failed for version:', event.version); // Handle download retry logic }); ``` **Event-Daten:** * `version`: string - Die Version, deren Download fehlgeschlagen ist ### `appReloaded` [Section titled “appReloaded”](#appreloaded) Wird ausgelöst, wenn die App neu geladen wurde. ```typescript CapacitorUpdater.addListener('appReloaded', () => { console.log('App has been reloaded'); // Perform any necessary reinitialization }); ``` **Event-Daten:** Keine ### `appReady` [Section titled “appReady”](#appready) Wird ausgelöst, wenn die App nach einem Update einsatzbereit ist. ```typescript CapacitorUpdater.addListener('appReady', (event) => { console.log('App is ready'); console.log('Current bundle:', event.bundle); console.log('Status:', event.status); }); ``` **Event-Daten:** * `bundle`: BundleInfo - Informationen über das aktuelle Bundle * `status`: string - Der Bereitschaftsstatus Caution Denken Sie daran, `notifyAppReady()` innerhalb des konfigurierten Timeouts (Standard 10 Sekunden) aufzurufen, um ein automatisches Rollback zu verhindern. ## BundleInfo-Objekt [Section titled “BundleInfo-Objekt”](#bundleinfo-objekt) Viele Ereignisse enthalten ein `BundleInfo`-Objekt mit den folgenden Eigenschaften: ```typescript interface BundleInfo { id: string; // Unique bundle identifier version: string; // Bundle version downloaded: string; // Download timestamp checksum?: string; // Bundle checksum (if available) status: BundleStatus; // Bundle status } ``` Wobei `BundleStatus` sein kann: * `'success'` - Bundle erfolgreich heruntergeladen * `'error'` - Bundle-Download/Installation fehlgeschlagen * `'pending'` - Bundle wartet darauf, als nächstes gesetzt zu werden * `'downloading'` - Bundle wird gerade heruntergeladen ## Beispiel: Vollständiger Update-Ablauf [Section titled “Beispiel: Vollständiger Update-Ablauf”](#beispiel-vollständiger-update-ablauf) Hier ist ein Beispiel für die Behandlung des vollständigen Update-Ablaufs mit Ereignissen: Caution Diese `UpdateManager`-Klasse enthält nicht den erforderlichen `CapacitorUpdater.notifyAppReady()`-Aufruf für den Auto-Update-Ablauf. Weitere Informationen finden Sie in der Dokumentation zum [korrekten Platzieren des notifyAppReady-Aufrufs](/docs/plugins/updater/auto-update#placing-notifyappready-call-correctly). ```typescript import { CapacitorUpdater } from '@capgo/capacitor-updater'; export class UpdateManager { private listeners: any[] = []; async setupListeners() { // Listen for available updates this.listeners.push( await CapacitorUpdater.addListener('updateAvailable', async (event) => { console.log('Update available:', event.bundle.version); // Auto-download the update await CapacitorUpdater.download({ url: event.bundle.url, version: event.bundle.version }); }) ); // Monitor download progress this.listeners.push( await CapacitorUpdater.addListener('download', (event) => { console.log(`Downloading: ${event.percent}%`); // Update UI progress bar this.updateProgressBar(event.percent); }) ); // Handle download completion this.listeners.push( await CapacitorUpdater.addListener('downloadComplete', async (event) => { console.log('Download complete:', event.bundle.version); // Set as next bundle await CapacitorUpdater.next({ id: event.bundle.id }); }) ); // Handle failures this.listeners.push( await CapacitorUpdater.addListener('downloadFailed', (event) => { console.error('Download failed:', event.version); this.showError('Update download failed. Please try again later.'); }) ); this.listeners.push( await CapacitorUpdater.addListener('updateFailed', (event) => { console.error('Update installation failed:', event.bundle.version); this.showError('Update installation failed. The app has been rolled back.'); }) ); // Handle app ready this.listeners.push( await CapacitorUpdater.addListener('appReady', async (event) => { console.log('App ready with bundle:', event.bundle.version); }) ); } cleanup() { // Remove all listeners when no longer needed this.listeners.forEach(listener => listener.remove()); this.listeners = []; } private updateProgressBar(percent: number) { // Update your UI progress bar } private showError(message: string) { // Show error to user } } ``` ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) 1. **Rufen Sie immer `notifyAppReady()` auf**: Bei Verwendung von Auto-Update rufen Sie diese Methode immer nach der Initialisierung Ihrer App auf, um ein Rollback zu verhindern. 2. **Behandeln Sie Fehler ordnungsgemäß**: Implementieren Sie eine ordnungsgemäße Fehlerbehandlung für Download- und Update-Fehler. 3. **Geben Sie Benutzerfeedback**: Verwenden Sie das Download-Fortschrittsereignis, um den Update-Fortschritt für Benutzer anzuzeigen. 4. **Räumen Sie Event-Listener auf**: Entfernen Sie Event-Listener, wenn sie nicht mehr benötigt werden, um Speicherlecks zu vermeiden. 5. **Testen Sie Update-Szenarien**: Testen Sie verschiedene Update-Szenarien einschließlich Fehler, Rollbacks und größere Updates. # Erste Schritte > Kurzanleitung zur Integration von Live-Updates in Ihre Capacitor-App mit dem Updater-Plugin. ## Installation [Section titled “Installation”](#installation) * npm ```bash npm install @capgo/capacitor-updater npx cap sync ``` * yarn ```bash yarn add @capgo/capacitor-updater npx cap sync ``` * pnpm ```bash pnpm add @capgo/capacitor-updater npx cap sync ``` * bun ```bash bun add @capgo/capacitor-updater npx cap sync ``` ## Schnellstart [Section titled “Schnellstart”](#schnellstart) Für die meisten Benutzer empfehlen wir, der [Haupt-Schnellstartanleitung](/docs/getting-started/quickstart/) zu folgen, die sowohl die Plugin-Installation als auch die Cloud-Integration von Capgo abdeckt. Dieser Einstiegsleitfaden konzentriert sich auf die technischen Plugin-Details für fortgeschrittene Benutzer, die die zugrunde liegenden Mechanismen verstehen oder selbst gehostete Updates implementieren möchten. ## Übersicht [Section titled “Übersicht”](#übersicht) Das Capacitor Updater-Plugin ermöglicht Over-the-Air-Updates (OTA) für Ihre Capacitor-Anwendungen. Auf diese Weise können Sie Updates für Ihre App pushen, ohne App-Store-Bewertungen durchgehen zu müssen. ## Wie es funktioniert [Section titled “Wie es funktioniert”](#wie-es-funktioniert) 1. **Bundle-Download**: Das Plugin lädt Update-Bundles (ZIP-Dateien mit Ihren Web-Assets) herunter. 2. **Extraktion**: Bundles werden in den Speicher des Geräts extrahiert 3. **Hot Reload**: Die App wechselt zum neuen Bundle, ohne dass ein Neustart erforderlich ist 4. **Fallback**: Wenn ein Update fehlschlägt, wird die App auf die vorherige Arbeitsversion zurückgesetzt ## Nutzungsmodi [Section titled “Nutzungsmodi”](#nutzungsmodi) ### 1. Auto-Update-Modus (empfohlen) [Section titled “1. Auto-Update-Modus (empfohlen)”](#1-auto-update-modus-empfohlen) Der einfachste Weg, das Plugin mit automatischer Updateverwaltung zu nutzen: ```typescript import { CapacitorUpdater } from '@capgo/capacitor-updater'; // Plugin handles everything automatically // Configure in capacitor.config.ts ``` Zu Ihrem `capacitor.config.ts` hinzufügen: ```typescript { plugins: { CapacitorUpdater: { autoUpdate: true, updateUrl: 'https://your-update-server.com/api/updates' } } } ``` ### 2. Manueller Modus [Section titled “2. Manueller Modus”](#2-manueller-modus) Für erweiterte Kontrolle über den Aktualisierungsprozess: ```typescript import { CapacitorUpdater } from '@capgo/capacitor-updater'; // Download an update const bundle = await CapacitorUpdater.download({ url: 'https://your-server.com/updates/v1.0.1.zip', version: '1.0.1' }); // Set the bundle (will be used on next app start) await CapacitorUpdater.set({ id: bundle.id }); // Or reload immediately await CapacitorUpdater.reload(); ``` ## Plattformkonfiguration [Section titled “Plattformkonfiguration”](#plattformkonfiguration) ### iOS [Section titled “iOS”](#ios) Keine zusätzliche Konfiguration erforderlich. Das Plugin funktioniert sofort. ### Android [Section titled “Android”](#android) Keine zusätzliche Konfiguration erforderlich. Das Plugin funktioniert sofort. ## Grundlegende API-Nutzung [Section titled “Grundlegende API-Nutzung”](#grundlegende-api-nutzung) ### Laden Sie ein Update herunter [Section titled “Laden Sie ein Update herunter”](#laden-sie-ein-update-herunter) ```typescript import { CapacitorUpdater } from '@capgo/capacitor-updater'; const bundle = await CapacitorUpdater.download({ url: 'https://example.com/update.zip', version: '1.0.1' }); console.log('Downloaded bundle:', bundle.id); ``` ### Aktives Bundle festlegen [Section titled “Aktives Bundle festlegen”](#aktives-bundle-festlegen) ```typescript // Set bundle to be used on next app start await CapacitorUpdater.set({ id: bundle.id }); ``` ### Mit neuem Bundle neu laden [Section titled “Mit neuem Bundle neu laden”](#mit-neuem-bundle-neu-laden) ```typescript // Reload app immediately with new bundle await CapacitorUpdater.reload(); ``` ### Bundles auflisten [Section titled “Bundles auflisten”](#bundles-auflisten) ```typescript const { bundles } = await CapacitorUpdater.list(); console.log('Available bundles:', bundles); ``` ### Ein Bundle löschen [Section titled “Ein Bundle löschen”](#ein-bundle-löschen) ```typescript await CapacitorUpdater.delete({ id: 'bundle-id' }); ``` ### Holen Sie sich das aktuelle Bundle [Section titled “Holen Sie sich das aktuelle Bundle”](#holen-sie-sich-das-aktuelle-bundle) ```typescript const { bundle } = await CapacitorUpdater.current(); console.log('Current bundle:', bundle.version); ``` ## Ereignis-Listener [Section titled “Ereignis-Listener”](#ereignis-listener) Achten Sie auf Aktualisierungsereignisse: ```typescript import { CapacitorUpdater } from '@capgo/capacitor-updater'; // Listen for download progress CapacitorUpdater.addListener('download', (info) => { console.log('Download progress:', info.percent); }); // Listen for download completion CapacitorUpdater.addListener('downloadComplete', (bundle) => { console.log('Download complete:', bundle.version); }); // Listen for update failures CapacitorUpdater.addListener('updateFailed', (error) => { console.error('Update failed:', error); }); // Listen for successful updates CapacitorUpdater.addListener('updateAvailable', (info) => { console.log('Update available:', info.version); }); ``` ## Konfigurationsoptionen [Section titled “Konfigurationsoptionen”](#konfigurationsoptionen) Konfigurieren Sie das Plugin in Ihrem `capacitor.config.ts`: ```typescript { plugins: { CapacitorUpdater: { // Auto-update settings autoUpdate: true, updateUrl: 'https://api.example.com/updates', // Update behavior resetWhenUpdate: true, directUpdate: false, // Version settings version: '1.0.0', // Security allowModifyUrl: false, // Stats collection statsUrl: 'https://api.example.com/stats', // Channel (for Capgo cloud) defaultChannel: 'production' } } } ``` ## Integrationsmuster [Section titled “Integrationsmuster”](#integrationsmuster) ### Mit Capgo Cloud [Section titled “Mit Capgo Cloud”](#mit-capgo-cloud) Der einfachste Einstieg: ```typescript // Install the Capgo CLI npm install -g @capgo/cli // Login to Capgo npx @capgo/cli login // Upload your first bundle npx @capgo/cli bundle upload // The plugin auto-updates from Capgo cloud ``` Weitere Informationen finden Sie in der [Haupt-Schnellstartanleitung](/docs/getting-started/quickstart/). ### Selbstgehostete Updates [Section titled “Selbstgehostete Updates”](#selbstgehostete-updates) Hosten Sie Ihren eigenen Update-Server: ```typescript // Configure your update endpoint { plugins: { CapacitorUpdater: { autoUpdate: true, updateUrl: 'https://your-server.com/api/check-update' } } } ``` Ihr Server sollte Folgendes zurückgeben: ```json { "version": "1.0.1", "url": "https://your-server.com/updates/1.0.1.zip" } ``` Ausführliche Informationen finden Sie unter [Selbstgehosteter Modus](/docs/plugins/updater/self-hosted/getting-started/). ### Manueller Aktualisierungsablauf [Section titled “Manueller Aktualisierungsablauf”](#manueller-aktualisierungsablauf) Vollständige Kontrolle über Updates: ```typescript import { CapacitorUpdater } from '@capgo/capacitor-updater'; async function checkAndUpdate() { // Check for updates from your server const response = await fetch('https://api.example.com/check-update'); const { version, url } = await response.json(); // Download the update const bundle = await CapacitorUpdater.download({ url, version }); // Notify bundle is ready await CapacitorUpdater.notifyAppReady(); // Set as next version await CapacitorUpdater.set({ id: bundle.id }); // Reload when ready await CapacitorUpdater.reload(); } ``` ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) * Rufen Sie immer `notifyAppReady()` auf, wenn Ihre App erfolgreich geladen wird * Testen Sie Updates gründlich, bevor Sie sie in die Produktion überführen * Implementieren Sie eine ordnungsgemäße Fehlerbehandlung bei Netzwerkausfällen * Verwenden Sie konsistent Versionsnummern * Halten Sie die Paketgrößen klein, um schnellere Downloads zu ermöglichen * Überwachen Sie die Erfolgsraten von Updates ## Nächste Schritte- [Plugin API Referenz](/docs/plugins/updater/api/) - Vollständige API Dokumentation [Section titled “Nächste Schritte- Plugin API Referenz - Vollständige API Dokumentation”](#nächste-schritte--plugin-api-referenz---vollständige-api-dokumentation) – [Plugin-Einstellungen](/docs/plugins/updater/settings/) – Alle Konfigurationsoptionen – [Ereignisse](/docs/plugins/updater/events/) – Verfügbare Update-Ereignisse * [Selbstgehosteter Modus](/docs/plugins/updater/self-hosted/getting-started/) – Führen Sie Ihren eigenen Update-Server aus – [Lokale Entwicklung](/docs/plugins/updater/local-dev/getting-started/) – Updates lokal testen – [Debugging](/docs/plugins/updater/debugging/) – Anleitung zur Fehlerbehebung ## Unterstützung [Section titled “Unterstützung”](#unterstützung) – [Bekannte Probleme](/docs/plugins/updater/known-issues/) – Häufige Probleme und Lösungen * [GitHub Diskussionen](https://github.com/Cap-go/capacitor-updater/discussions) - Community-Unterstützung * [Discord](https://discord.gg/VnYRvBfgA6) – Echtzeit-Chat # Bekannte Probleme > Bekannte Probleme mit Capacitor und Capgo, und unserem Updater. Diese Seite hilft Ihnen, das seltsame Problem zu verstehen, das Sie mit unserem Tool haben können ## Ionic Live Reload [Section titled “Ionic Live Reload”](#ionic-live-reload) * Wenn Sie entwickeln und die Ionic Live Reload-Funktion aus dem CLI verwenden, wird das Plugin überschrieben, sodass Sie Ihr Update nie sehen werden. ## Quasar Live Reload [Section titled “Quasar Live Reload”](#quasar-live-reload) * Es verwendet das gleiche System wie Ionic unter der Haube, sodass Sie Ihre Updates nicht sehen werden. ## Updates schlagen fehl [Section titled “Updates schlagen fehl”](#updates-schlagen-fehl) * Dies geschieht normalerweise, wenn große Updates (> 20MB) übertragen werden. Ein großer Prozentsatz der Benutzer wird die letzte Version nicht erhalten. In der Vergangenheit mussten Benutzer die App offen halten, bis der Download abgeschlossen war. Jetzt verwenden wir Background-Downloads, aber diese sind auf einige Sekunden begrenzt. ## Android [Section titled “Android”](#android) ### Kann nicht herunterladen [Section titled “Kann nicht herunterladen”](#kann-nicht-herunterladen) Wir haben einige Probleme mit Geräten in Indien gesehen und Benutzer in ein Gespräch geholt, sie verschiedene DNS-Server ausprobieren lassen, und es funktionierte. Wenn Sie also das Problem haben, versuchen Sie, einen anderen DNS-Server wie Cloudflare oder Google DNS zu verwenden. Cloudflare: 1.1.1.1 und 1.0.0.1 Google DNS: 8.8.8.8 und 8.8.4.4 oder dns.google [How to setup a preferred DNS server on Android? ](https://www.androidpolice.com/use-preferred-dns-server-android-tutorial/) ### Selbst gehostet [Section titled “Selbst gehostet”](#selbst-gehostet) Wenn Sie ein selbst gehostetes Update übertragen, beachten Sie, dass Sie keinen “HTTP”-Endpunkt verwenden können, da dies gegen die Sicherheitsrichtlinien von Android-Apps verstößt. Wenn Sie dies trotzdem tun möchten, folgen Sie dieser Anleitung: [How to allow all Network connection types HTTP and HTTPS in Android (9) Pie? ](https://stackoverflow.com/a/51902630/5511370) ### Unzip [Section titled “Unzip”](#unzip) Unzip-Problem: DEFLATED-Einträge können einen EXT-Deskriptor haben Wenn Sie Ihr Bundle mit etwas anderem als dem CLI gezippt haben, könnte das Format Ihrer ZIP-Datei falsch sein. Bitte verwenden Sie den CLI-Befehl `npx @capgo/cli zip BUNDLE_FOLDER`. Dies ist ein bekanntes Problem von Java: [Unzip issue: DEFLATED entries can have EXT descriptor ](https://bugs.openjdk.org/browse/JDK-8143613) ### Clearfix-Problem [Section titled “Clearfix-Problem”](#clearfix-problem) * Wenn Sie Probleme mit usesCleartextTraffic haben, liegt das daran, dass das Plugin der von Sonar Cloud empfohlenen Best Practice folgt. In 90% der Fälle wird es einwandfrei funktionieren, aber mit einigen Plugins verursacht das Probleme. Um dies zu beheben, fügen Sie in `android/app/src/main/AndroidManifest.xml` im ``-Schlüssel hinzu: ```xml tools:replace="android:usesCleartextTraffic" xmlns:tools="http://schemas.android.com/tools" ``` ## iOS [Section titled “iOS”](#ios) ### Privacy Manifest [Section titled “Privacy Manifest”](#privacy-manifest) Fügen Sie den `NSPrivacyAccessedAPICategoryUserDefaults`-Dictionary-Schlüssel zu Ihrem [Privacy Manifest](https://capacitorjs.com/docs/ios/privacy-manifest) hinzu (normalerweise `ios/App/PrivacyInfo.xcprivacy`): ```xml NSPrivacyAccessedAPITypes NSPrivacyAccessedAPIType NSPrivacyAccessedAPICategoryUserDefaults NSPrivacyAccessedAPITypeReasons CA92.1 ``` Wir empfehlen, [`CA92.1`](https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_use_of_required_reason_api#4278401) als Grund für den Zugriff auf die [`UserDefaults`](https://developer.apple.com/documentation/foundation/userdefaults)-API anzugeben. ### Netzwerkberechtigungen [Section titled “Netzwerkberechtigungen”](#netzwerkberechtigungen) Bei Verwendung eines lokalen Servers zum Testen von Updates wird die App nach Netzwerkberechtigungen fragen. Dies ist normales Verhalten und tritt nicht auf, wenn Sie einen Remote-Server verwenden. ## Beide Betriebssysteme [Section titled “Beide Betriebssysteme”](#beide-betriebssysteme) Beim manuellen Modus von Updates sind einige Ereignisse nicht einfach zu erfassen. Zum Beispiel wird das Update-Fehler-Ereignis kurz vor dem Neuladen Ihres JS-Codes ausgelöst, sodass Sie es nicht erfassen können. Eine Alternative besteht darin, die Bundles aufzulisten und Fehlerstatistiken zu überprüfen, um zu wissen, ob das Update fehlschlägt. Wir müssen in Zukunft einen besseren Weg finden, damit umzugehen, aber es ist keine Priorität, da der Auto-Modus die empfohlene Art ist, Updates durchzuführen. PRs sind willkommen, um uns dabei zu helfen, dies zu verbessern. ## CLI [Section titled “CLI”](#cli) Wenn Ihr CLI Probleme hat, irgendetwas zu tun, Prüfen Sie, ob **appId** und **appName** in Ihrer **capacitor.config.ts** vorhanden sind Folgen Sie der Anleitung aus der offiziellen Dokumentation: [Capacitor Configuration ](https://capacitorjs.com/docs/config) # Verwendung des capacitor updater mit selbst gehostetem Capgo > Wie Sie den capacitor updater mit selbst gehostetem Capgo verwenden, um mit Ihrer eigenen Instanz von Capgo vollständig autonom zu sein ## Was wird dieses Tutorial abdecken? [Section titled “Was wird dieses Tutorial abdecken?”](#was-wird-dieses-tutorial-abdecken) Dieses Tutorial zeigt, wie man capacitor updater in einer Entwicklungsumgebung mit selbst gehostetem Capgo verwendet. ## Anforderungen [Section titled “Anforderungen”](#anforderungen) 1. [Geklontes Capgo](https://github.com/Cap-go/capgo) ## Erste Schritte [Section titled “Erste Schritte”](#erste-schritte) Um den capacitor updater mit selbst gehostetem Capgo zu verwenden, bearbeiten Sie die `capacitor.config.ts` aus Ihrem App-Verzeichnis und setzen Sie es wie folgt: ```ts const config: CapacitorConfig = { appId: 'com.demo.app', appName: 'demoApp', webDir: 'dist', bundledWebRuntime: false, plugins: { CapacitorUpdater : { statsUrl: "https://localhost:54321/functions/v1/stats", channelUrl: "https://localhost:54321/functions/v1/channel_self", updateUrl: "https://localhost:54321/functions/v1/updates" }, }, }; ``` Dies ermöglicht es Ihnen, lokales Capgo in der Entwicklung zu verwenden. Standardmäßig reicht dies jedoch nicht aus. > Standardmäßig erwarten sowohl iOS als auch Android, dass Sie HTTPS verwenden. Sie müssen ein Tool wie Ngrock oder localcan verwenden, um Ihre API in HTTPS zu proxyen. Es gibt eine Möglichkeit in Android, [Klartext-Kommunikation](https://developer.android.com/topic/security/risks/cleartext) zu aktivieren. Dies kann erreicht werden, indem [AndroidManifest.xml](https://github.com/Cap-go/capacitor-updater/blob/main/android/src/main/AndroidManifest.xml) modifiziert und `android:usesCleartextTraffic="true"` im `application`-Tag hinzugefügt wird. Ein vollständiges Beispiel dieser Änderung kann [hier](https://gist.github.com/WcaleNieWolny/061a015acdebe35eaf3afd7030797701) eingesehen werden. Es könnte auch ein Problem geben, das verhindert, dass die Android-App eine Verbindung herstellt. Wenn Sie keine Anfragen an Edge-Funktionen sehen, führen Sie Folgendes aus: ```bash adb reverse tcp:54321 tcp:54321 ``` # Verwendung des CLI mit selbst gehostetem Capgo > Diese Anleitung erklärt, wie Sie das CLI mit einer selbst gehosteten Capgo-Instanz verwenden, damit Sie Capgo-Tools auf Ihrem eigenen Server anstelle des Cloud-Dienstes nutzen können. ## Was wird dieses Tutorial abdecken? [Section titled “Was wird dieses Tutorial abdecken?”](#was-wird-dieses-tutorial-abdecken) Dieses Tutorial zeigt, wie man das CLI in einer Entwicklungsumgebung mit selbst gehostetem Capgo verwendet. ## Erste Schritte [Section titled “Erste Schritte”](#erste-schritte) Um das CLI mit selbst gehostetem Capgo zu verwenden, bearbeiten Sie die `capacitor.config.ts` aus Ihrem App-Verzeichnis und setzen Sie es wie folgt: ```ts const config: CapacitorConfig = { appId: 'com.demo.app', appName: 'demoApp', webDir: 'dist', bundledWebRuntime: false, plugins: { CapacitorUpdater : { localHost: "http://localhost:5173", localWebHost: "http://localhost:5173", localSupa: "http://localhost:54321", localSupaAnon: "siehe_hinweise", }, }, }; ``` Hinweis: Um `localSupaAnon` zu erhalten, folgen Sie bitte [diesem Tutorial](/docs/plugin/self-hosted/local-dev/getting-started/) und kopieren Sie den `anon key` in `localSupaAnon` # Beitragen > Eine detaillierte Anleitung, wie Sie zu den Open-Source-Projekten von Capgo beitragen können, einschließlich der Vorteile des Beitragens, der erforderlichen Schritte und der verfügbaren Ressourcen zur Unterstützung der Beitragenden ## Warum beitragen? [Section titled “Warum beitragen?”](#warum-beitragen) Zunächst einmal, vielen Dank, dass Sie in Erwägung ziehen, zu den Open-Source-Projekten von Capgo beizutragen! Es sind Menschen wie Sie, die die Open-Source-Projekte von Capgo zu so großartigen Werkzeugen machen. Hier sind einige Gründe, warum Sie in Erwägung ziehen sollten, beizutragen: * Das Beitragen zu den Open-Source-Projekten von Capgo ist eine großartige Möglichkeit, etwas Geld mit den [zahlreichen Bounties](https://console.algora.io/org/Capgo) zu verdienen, die vom Capgo-Team angeboten werden * Das Beitragen zu den Open-Source-Projekten von Capgo ist eine großartige Möglichkeit, eine Funktion hinzuzufügen, die Sie gerne sehen würden * Das Beitragen zu den Open-Source-Projekten von Capgo ist eine großartige Möglichkeit, einen Fehler zu beheben, auf den Sie gestoßen sind * [Die Hauptlizenz von Capgo](https://github.com/Cap-go/capgo/blob/main/LICENSE) verlangt, dass Sie alle Änderungen, die Sie daran vornehmen, als Open Source veröffentlichen. Indem Sie Ihren Code beitragen, können Sie Ihre Änderungen als Open Source behalten und andere sie nutzen lassen ## Wie man beiträgt [Section titled “Wie man beiträgt”](#wie-man-beiträgt) * Zunächst müssen Sie das Repository forken, zu dem Sie beitragen möchten * Zweitens müssen Sie Ihre Änderungen in dieses Repository committen und pushen * Schließlich müssen Sie einen Pull Request öffnen * Das war’s! Jetzt müssen Sie nur noch warten, bis das Capgo-Team Ihren PR überprüft ## Weitere zu lesende Dokumente [Section titled “Weitere zu lesende Dokumente”](#weitere-zu-lesende-dokumente) * Capgos [CONTRIBUTING.MD](https://github.com/Cap-go/capgo/blob/main/CONTRIBUTING.md) * Capgos [BOUNTY.md](https://github.com/Cap-go/capgo/blob/main/BOUNTY.md) # Erste Schritte > Dieses Tutorial zeigt, wie Sie Supabase von Grund auf starten und Edge-Funktionen starten, um Capgo selbst auszuführen ## Was wird in diesem Tutorial behandelt? [Section titled “Was wird in diesem Tutorial behandelt?”](#was-wird-in-diesem-tutorial-behandelt) In diesem Tutorial erfahren Sie, wie Sie die Supabase von Grund auf neu starten und Kantenfunktionen starten ## Anforderungen [Section titled “Anforderungen”](#anforderungen) 1. Geklont [capgo](https://github.com/Cap-go/capgo) 2. [supabase](https://supabase.com/) ## Erste Schritte [Section titled “Erste Schritte”](#erste-schritte) Um zu beginnen, laufen Sie ```bash supabase start ``` Als nächstes sollte etwa Folgendes angezeigt werden: ```js Started supabase local development setup. API URL: http://localhost:54321 GraphQL URL: http://localhost:54321/graphql/v1 DB URL: postgresql://postgres:postgres@localhost:54322/postgres Studio URL: http://localhost:54323 Inbucket URL: http://localhost:54324 JWT secret: [truncated] anon key: supa_key_anon service_role key: supa_key_admin ``` Öffnen Sie als Nächstes `configs.json` und legen Sie die folgenden Werte fest: ```json { "base_domain": { "prod": "console.capgo.app", "development": "development.console.capgo.app", "local": "localhost:3332" }, "supa_anon": { "prod": "supa_key_anon", "development": "supa_key_anon", "local": "supa_key_anon" }, "supa_url": { "prod": "http://localhost:54321", "development": "http://localhost:54321", "local": "http://localhost:54321" } } ``` Dabei ist `supa_key_anon` der Wert aus dem vorherigen Schritt. Danger ⚠️ Übertragen Sie `configs.json` nicht in das Remote-Repository Überprüfen Sie als Nächstes, ob Sie zu [localhost:54323](http://localhost:54323/projects) wechseln können und ob die Tabelle `users` in etwa so aussieht ![](/supabase.webp) Wenn es Kantenfunktionen startet, indem es Folgendes ausführt: ```bash supabase functions serve ``` und starten Sie das Frontend, indem Sie Folgendes ausführen: ```bash bun run serve ``` # CapacitorUpdater.notifyAppReady()-Aufruf korrekt platzieren > Wie Sie den CapacitorUpdater.notifyAppReady()-Aufruf in Ihrer App korrekt platzieren, um einen korrekten Auto-Update-Ablauf zu gewährleisten. Die Platzierung des `CapacitorUpdater.notifyAppReady()`-Aufrufs ist entscheidend für den korrekten Auto-Update-Ablauf. Wenn dies nicht korrekt durchgeführt wird, kann die App nicht auf die neueste Version aktualisiert werden. Jede Version, die `notifyAppReady()` nicht innerhalb von 10 Sekunden aufruft, wird als ungültig markiert und durch die vorherige gültige Version oder die Standard-(integrierte) Version ersetzt. In diesem Leitfaden zeigen wir Ihnen, wie Sie den `notifyAppReady()`-Aufruf in Ihrer App korrekt platzieren, um einen korrekten Auto-Update-Ablauf zu gewährleisten. ### `notifyAppReady()`-Aufrufplatzierungsfragebogen [Section titled “notifyAppReady()-Aufrufplatzierungsfragebogen”](#notifyappready-aufrufplatzierungsfragebogen) 1. **What language/framework do you primarily use in your app?** ![JS + DOM API](/icons/js.svg) JS + DOM API ![TS + DOM API](/icons/ts.svg) TS + DOM API ![React](/icons/react.svg) React ![Angular](/icons/angular.svg) Angular ![Vue](/icons/vue.svg) Vue ![Svelte](/icons/svelte.svg) Svelte ![Qwik](/icons/qwik.svg) Qwik 2. **** 3. **** 4. **** Note Wenn Sie Ihr Framework nicht im Fragebogen finden können, zögern Sie nicht, uns auf [Discord](https://discord.capgo.app) zu fragen. # Auto Update > Wie Sie den Update-Endpunkt von Capgo mit dem Auto-Update-Plugin im Self-Hosted-Modus verwenden, wofür sie verwendet werden und was zu erwarten ist Diese Dokumentation erklärt, wie Sie Ihren Auto-Update-Server ausführen. ## Stellen Sie Ihr Bundle bereit [Section titled “Stellen Sie Ihr Bundle bereit”](#stellen-sie-ihr-bundle-bereit) Stellen Sie sicher, dass Ihr Bundle über HTTPS bereitgestellt wird und der Server die richtigen CORS-Header hat, um der App das Herunterladen des Updates zu ermöglichen. z.B. `https://myserver.com/app/updates/updates.json` Falls Sie nicht vertraut mit der Bereitstellung eines Bundles sind, empfehlen wir Ihnen, Capgo Cloud auszuprobieren oder ein Beispiel hier zu sehen: [Ein Bundle bereitstellen ](/docs/self-hosted/auto-update/update-endpoint) ## Konfiguration [Section titled “Konfiguration”](#konfiguration) Fügen Sie eine `updateUrl` zu Ihrer `capacitor.config.json` hinzu. ```json { "plugins": { "CapacitorUpdater": { "updateUrl": "https://myserver.com/app/updates/updates.json", } } } ``` Caution Wenn Sie ein Self-Hosted-Update pushen, beachten Sie, dass Sie keinen “HTTP”-Endpunkt verwenden können, da dies gegen die Sicherheitsrichtlinien von Android-Apps verstößt. Zu Testzwecken können Sie es [zulassen](https://stackoverflow.com/questions/45940861/android-8-cleartext-http-traffic-not-permitted). ## Update-API [Section titled “Update-API”](#update-api) Das Plugin wird jedes Mal, wenn die App geöffnet wird, einen POST-Aufruf an Ihre API durchführen, mit diesem Body: ```typescript interface AppInfos { "platform": "ios" | "android", "device_id": "UUID_of_device_unique_by_install", "app_id": "APPID_FROM_CAPACITOR_CONFIG", "custom_id": "your_custom_id_set_on_runtime", "plugin_version": "PLUGIN_VERSION", "version_build": "VERSION_NUMBER_FROM_NATIVE_CODE", "version_code": "VERSION_CODE_FROM_NATIVE_CODE", "version_name": "LAST_DOWNLOADER_VERSION" | "builtin" "version_os": "VERSION_OF_SYSTEM_OS", "is_emulator": boolean, "is_prod": boolean, } ``` Die Server-API sollte in JSON an das capacitor-updater-Plugin antworten. Mit diesen Daten, wenn ein Update notwendig ist: ```json { "version": "1.2.3", "url": "https://myserver.com/app/updates/my-new-app-2.0.0.zip", "checksum": "sha256_checksum_of_bundle" } ``` Im Auto-Update-Modus sollte der Server die Versionen vergleichen und die richtige zurückgeben. Wenn der URL-Schlüssel vorhanden ist, startet das Plugin den Download-Prozess. Wenn Sie “message” und “error”-Schlüssel hinzufügen, wird die Version nicht gesetzt, und die Nachricht wird stattdessen in den Protokollen angezeigt. Der `version`-Schlüssel sollte im [`semver`](https://semver.org/)-Format sein. Die ZIP-Datei sollte `index.html` als Datei im Root haben, oder nur einen Ordner im Root mit `index.html` darin. Sie können den Befehl der CLI verwenden, um Ihr Bundle zu zippen: Erstellen Sie ein Bundle mit Ihren Dateien, das Sie von Ihrem Server bereitstellen ```bash npx @capgo/cli bundle zip --path [/path/to/my/bundle] ``` ## Generierung von Bundle-Checksumme [Section titled “Generierung von Bundle-Checksumme”](#generierung-von-bundle-checksumme) **Wichtig:** Sie müssen die Capgo CLI verwenden, um Ihre Bundle-ZIP-Datei zu erstellen. Das Capgo-Plugin erfordert ein bestimmtes ZIP-Format und eine Struktur, die nur durch die Verwendung des offiziellen CLI-Tools garantiert ist. Standard-ZIP-Dienstprogramme können inkompatible Archive erstellen. Um die Checksumme für Ihr Bundle zu generieren, verwenden Sie den Capgo CLI-ZIP-Befehl mit dem `--json`-Flag: Erstellen Sie ein Bundle mit Checksummen-Informationen ```bash npx @capgo/cli bundle zip [appId] --json ``` Dieser Befehl wird: * Eine ordnungsgemäß formatierte ZIP-Datei erstellen, die mit dem Capgo-Plugin kompatibel ist * Die SHA256-Checksumme zur Integritätsprüfung generieren * Bundle-Informationen im JSON-Format ausgeben Beispielausgabe: ```json { "version": "1.2.3", "checksum": "a1b2c3d4e5f6789...", "size": 1234567 } ``` Verwenden Sie den `checksum`-Wert aus dieser Ausgabe in Ihrer API-Antwort, um sicherzustellen, dass das Plugin die Bundle-Integrität vor der Installation überprüfen kann. # > Eine umfassende Anleitung zur Verwendung des manuellen Update-Plugins in einer selbst gehosteten Umgebung, in der die Schritte und Prozesse detailliert beschrieben werden, die bei der Verwaltung verschlüsselter Pakete für sichere und effiziente Updates erforderlich sind. ## End-to-End-Verschlüsselung [Section titled “End-to-End-Verschlüsselung”](#end-to-end-verschlüsselung) Ab Version 4.15.0 ermöglicht das Plugin den Versand verschlüsselter Updates. ### Schritt 1: Erstellen Sie einen privaten Schlüssel [Section titled “Schritt 1: Erstellen Sie einen privaten Schlüssel”](#schritt-1-erstellen-sie-einen-privaten-schlüssel) Create a private key ```bash npx @capgo/cli key create ``` ### Schritt 2: Erstellen und komprimieren Sie Ihr Paket [Section titled “Schritt 2: Erstellen und komprimieren Sie Ihr Paket”](#schritt-2-erstellen-und-komprimieren-sie-ihr-paket) Create bundle zip with checksum ```bash npx @capgo/cli bundle zip [appId] --key-v2 --json ``` Das Flag `--key-v2` verwendet das neue Verschlüsselungssystem mit besseren Prüfsummen und das Flag `--json` gibt die Bundle-Informationen einschließlich der Prüfsumme aus, die Sie für die Verschlüsselung benötigen. ### Schritt 3: Verschlüsseln Sie Ihr Bundle [Section titled “Schritt 3: Verschlüsseln Sie Ihr Bundle”](#schritt-3-verschlüsseln-sie-ihr-bundle) Encrypt bundled zip with checksum ```bash npx @capgo/cli encrypt [path/to/zip] [checksum] ``` Der Parameter `checksum` ist die SHA256-Prüfsumme, die vom Befehl zip in Schritt 2 generiert wurde. Der Befehl encrypt gibt einen `ivSessionKey` zurück und generiert eine verschlüsselte Prüfsumme. Denken Sie daran, den Schlüssel `ivSessionKey` in der Update-Payload in `session_key` umzubenennen. ### Schritt 4: In Ihrer Update-Payload verwenden [Section titled “Schritt 4: In Ihrer Update-Payload verwenden”](#schritt-4-in-ihrer-update-payload-verwenden) ```json { "version": "1.2.3", "url": "https://myserver.com/app/updates/my-new-app-2.0.0.zip", "session_key": "encrypted_session_key", "checksum": "encrypted_checksum_from_encrypt_command" } ``` Der `session_key` ist der vom encrypt-Befehl zurückgegebene `ivSessionKey` und der `checksum` ist die verschlüsselte Prüfsumme, die während der Verschlüsselung generiert wurde (nicht die ursprüngliche Prüfsumme vom Zip-Befehl). Dann kann Ihre App den privaten Schlüssel zum Entschlüsseln des `session_key` und den entschlüsselten `session_key` zum Entschlüsseln des Updates verwenden. Die verschlüsselte Prüfsumme gewährleistet die Überprüfung der Bundle-Integrität. ## Erfahren Sie mehr [Section titled “Erfahren Sie mehr”](#erfahren-sie-mehr) [Leitfaden zur Ende-zu-Ende-Verschlüsselung ](https://capgo.app/blog/introducing-end-to-end-security-to-capacitor-updater-with-code-signing/)Deep dive into how Capgo's encryption system works with RSA + AES cryptography [Selbstgehostete Live-Updates ](https://capgo.app/blog/self-hosted-live-updates/)Complete workflow for setting up self-hosted updates # Erste Schritte > Ein umfassender Leitfaden zum Einrichten und Verwalten Ihres eigenen Auto-Update-Servers für nahtlose Anwendungsupdates und Wartung Diese Dokumentation erklärt, wie Sie Ihren eigenen Auto-Update-Server ausführen. ## Einführung [Section titled “Einführung”](#einführung) Wenn Sie diese Arbeit hilfreich finden, bitte unterstützen Sie meine Arbeit, indem Sie ein [Github-Sponsor](https://github.com/sponsors/riderx) werden. Ich habe die Wette eingegangen, all den Code, den ich hier gebaut habe, Open-Source zu machen, anstatt ihn hinter einer Paywall zu verstecken. Indem ich ihn öffne, anstatt zu kämpfen und mich zu verstecken, glaube ich, dass wir die Welt besser machen können. Darüber hinaus möchte ich mich auf die Capgo-Werkzeuge konzentrieren und ein offenes und transparentes Geschäft aufbauen. Aber damit es möglich ist, ist es notwendig, dass wir alle unseren Teil dazu beitragen, einschließlich Ihnen 🥹. Wenn Capgo nicht zu Ihnen passt, zahlen Sie Ihren eigenen Preis und [unterstützen Sie einen unabhängigen Entwickler](https://github.com/sponsors/riderx) nach Ihren Bedingungen. [Erwägen Sie einen Beitrag ](/docs/plugin/self-hosted/contributing/) ## Paritäts-Features [Section titled “Paritäts-Features”](#paritäts-features) Wenn Sie sich für Ihren eigenen Server entscheiden, verlieren Sie den 5-Minuten-Setup-Ablauf.\ Sie müssen alle diese Funktionen selbst implementieren. | Features | Capgo | Self-Hosted | | ------------------------------------ | ----- | ----------- | | Updates | ✅ | 🚧 | | Automatische Wiederherstellung | ✅ | 🚧 | | E-Mail-Benachrichtigungen bei Fehler | ✅ | 🚧 | | Kanäle | ✅ | 🚧 | | Kanäl-Überschreitung | ✅ | 🚧 | | Geräte-Überschreitung | ✅ | 🚧 | | Kanäl-Einstellungen | ✅ | 🚧 | | Geräte-Einstellungen | ✅ | 🚧 | | Benutzerdefinierte ID | ✅ | 🚧 | | Kanäle automatisch festlegen | ✅ | 🚧 | | API-Kanäle | ✅ | 🚧 | | Update-Statistiken | ✅ | 🚧 | | Download-Fehler-Statistiken | ✅ | 🚧 | | App-Nutzungsstatistiken | ✅ | 🚧 | | Update-Verschlüsselung | ✅ | 🚧 | | Differenzielle Updates | ✅ | ❌ | Danger Wenn Sie ein schlechtes Update an Ihre Benutzer senden, können Sie deren App beschädigen. > Beachten Sie, dass Sie Capgo Cloud und Ihren Server nicht gleichzeitig nutzen können. Danger Die Over-the-Air (OTA)-Update-Funktion ist nur für Änderungen an HTML-, CSS- und JavaScript-Dateien anwendbar. Wenn Sie Änderungen am nativen Code vornehmen, z. B. an Capacitor-Plugins, müssen Sie die Anwendung zum App Store zur Genehmigung erneut einreichen. ## Wählen Sie zwischen Auto und Manuell [Section titled “Wählen Sie zwischen Auto und Manuell”](#wählen-sie-zwischen-auto-und-manuell) Im Auto-Modus wird ein Teil der Logik vom nativen Code verarbeitet, Updates werden serverseitig entschieden, dies ist sicherer und ermöglicht granulare Updates, teilweise Bereitstellung auf einem Gerät oder einer Gruppe und mehr. Im manuellen Modus wird die gesamte Logik vom JS verarbeitet. [Auto Update ](/docs/plugin/self-hosted/auto-update/) [Manuell ](/docs/plugin/self-hosted/manual-update/) ## Installieren Sie Capacitor Updater [Section titled “Installieren Sie Capacitor Updater”](#installieren-sie-capacitor-updater) Installieren Sie den Capacitor Updater ```bash npm install @capgo/capacitor-updater npx cap sync ``` ## Bereiten Sie Ihr Bundle vor [Section titled “Bereiten Sie Ihr Bundle vor”](#bereiten-sie-ihr-bundle-vor) Um Updates an Ihre App zu senden, müssen Sie sie zippen. Die beste Möglichkeit, um sicherzustellen, dass Ihre ZIP-Datei gut ist, besteht darin, die Capgo CLI zum Zippen zu verwenden. Erstellen Sie ein Bundle mit Ihren Dateien, das Sie von Ihrem Server bereitstellen ```bash npx @capgo/cli@latest bundle zip ``` Sie müssen diese ZIP-Datei selbst von Ihrem Server bereitstellen. [Auto Update ](/docs/plugin/self-hosted/auto-update/) [Manuell ](/docs/plugin/self-hosted/manual-update/) Note Wenn dies wie viel Arbeit klingt, versuchen Sie die Testversion von Capgo Cloud. Note Differenzielle Updates funktionieren nicht bei manuellen Updates, können aber bei Auto-Updates funktionieren. Derzeit ist es nicht dokumentiert, da es ein ziemlich komplexer Prozess ist # Kanal API Endpunkt > So erstellen Sie Kanalverwaltungsendpunkte für selbstgehostete Capgo, um Gerätekanalzuweisungen und Kanalabfragen zu verarbeiten Kanäle sind ein zentraler Mechanismus zum Verwalten von App-Updates in Capgo. Im selbstgehosteten Modus müssen Sie Kanalendpunkte implementieren, um Gerätezuweisungen, Kanalabfragen und Kanalverwaltungsvorgänge zu verarbeiten. ## Kanäle verstehen [Section titled “Kanäle verstehen”](#kanäle-verstehen) Mit Kanälen können Sie: * **Update-Verteilung steuern**: Weisen Sie verschiedenen Benutzergruppen verschiedene App-Versionen zu * **A/B-Tests**: Testen Sie neue Funktionen mit bestimmten Benutzersegmenten * **Stufenweise Einführung**: Stellen Sie Updates schrittweise bereit, um das Risiko zu minimieren * **Umgebungstrennung**: Separate Entwicklungs-, Staging- und Produktionsaktualisierungen ## Konfiguration [Section titled “Konfiguration”](#konfiguration) Konfigurieren Sie die Kanalendpunkt-URL in Ihrem `capacitor.config.json`: ```json { "plugins": { "CapacitorUpdater": { "channelUrl": "https://myserver.com/api/channel_self" } } } ``` ## Kanaloperationen [Section titled “Kanaloperationen”](#kanaloperationen) Das Plugin führt verschiedene Kanaloperationen aus, die Ihr Endpunkt verarbeiten muss: ### 1. Kompatible Kanäle auflisten (GET-Anfrage) [Section titled “1. Kompatible Kanäle auflisten (GET-Anfrage)”](#1-kompatible-kanäle-auflisten-get-anfrage) Wenn das Plugin `listChannels()` aufruft, sendet es eine GET-Anfrage, um alle Kanäle abzurufen, die mit dem Gerät kompatibel sind. Dadurch werden Kanäle zurückgegeben, die zur Geräteumgebung (Entwickler/Produkt, Emulator/reales Gerät) passen und entweder öffentlichen Zugriff oder Selbstzuweisung ermöglichen. #### Anforderungsformat [Section titled “Anforderungsformat”](#anforderungsformat) ```typescript // GET /api/channel_self // Headers: { "Content-Type": "application/json" } // Query parameters: interface ListChannelsRequest { app_id: string platform: "ios" | "android" | "electron" is_emulator: boolean is_prod: boolean key_id?: string } ``` #### Antwortformat [Section titled “Antwortformat”](#antwortformat) ```json [ { "id": 1, "name": "production", "public": true, "allow_self_set": false }, { "id": 2, "name": "beta", "public": false, "allow_self_set": true } ] ``` #### Kanaltypen verstehen [Section titled “Kanaltypen verstehen”](#kanaltypen-verstehen) Die Antwort enthält zwei wichtige Flags für jeden Kanal: * **`public: true`**: Dies ist ein **Standardkanal**. Geräte können sich mit `setChannel()` nicht selbst zuweisen. Wenn ein Gerät stattdessen seine Kanalzuweisung entfernt (mithilfe von `unsetChannel()`), erhält es automatisch Updates von diesem öffentlichen Kanal, wenn er den Bedingungen des Geräts entspricht. * **`allow_self_set: true`**: Dies ist ein **selbst zuweisbarer Kanal**. Mit `setChannel()` können sich Geräte explizit diesem Kanal zuordnen. Dies ist nützlich für Betatests, A/B-Tests oder um Benutzern die Möglichkeit zu geben, sich für bestimmte Update-Tracks zu entscheiden. Note Ein Kanal kann entweder `public` ODER `allow_self_set` sein, normalerweise jedoch nicht beides. Öffentliche Kanäle dienen als Standard-Fallback, während selbst zuweisbare Kanäle eine explizite Einwilligung erfordern. ### 2. Kanal abrufen (PUT-Anfrage) [Section titled “2. Kanal abrufen (PUT-Anfrage)”](#2-kanal-abrufen-put-anfrage) Wenn das Plugin `getChannel()` aufruft, sendet es eine PUT-Anfrage, um die aktuelle Kanalzuweisung des Geräts abzurufen. #### Anforderungsformat [Section titled “Anforderungsformat”](#anforderungsformat-1) ```typescript // PUT /api/channel_self // Headers: { "Content-Type": "application/json" } // Body: interface GetChannelRequest { device_id: string app_id: string platform: "ios" | "android" | "electron" plugin_version: string version_build: string version_code: string version_name: string is_emulator: boolean is_prod: boolean defaultChannel?: string channel?: string // For newer plugin versions, contains local channel override } ``` #### Antwortformat [Section titled “Antwortformat”](#antwortformat-1) ```json { "status": "ok", "channel": "production", "allowSet": true, "message": "", "error": "" } ``` ### 3. Kanal einstellen (POST-Anfrage) [Section titled “3. Kanal einstellen (POST-Anfrage)”](#3-kanal-einstellen-post-anfrage) Wenn das Plugin `setChannel()` aufruft, sendet es eine POST-Anfrage, um das Gerät einem bestimmten Kanal zuzuweisen. #### Anforderungsformat [Section titled “Anforderungsformat”](#anforderungsformat-2) ```typescript // POST /api/channel_self interface SetChannelRequest { device_id: string app_id: string channel: string platform: "ios" | "android" | "electron" plugin_version: string version_build: string version_code: string version_name: string is_emulator: boolean is_prod: boolean } ``` #### Antwortformat [Section titled “Antwortformat”](#antwortformat-2) ```json { "status": "ok", "message": "Device assigned to channel successfully", "error": "" } ``` #### Fehlerfälle [Section titled “Fehlerfälle”](#fehlerfälle) Wenn ein Gerät versucht, sich einem **öffentlichen Kanal** (einem mit `public: true`) zuzuweisen, sollte Ihr Endpunkt einen Fehler zurückgeben: ```json { "status": "error", "error": "public_channel_self_set_not_allowed", "message": "This channel is public and does not allow device self-assignment. Unset the channel and the device will automatically use the public channel." } ``` Wenn ein Gerät versucht, sich selbst einem Kanal zuzuweisen, der keine Selbstzuweisung zulässt: ```json { "status": "error", "error": "channel_self_set_not_allowed", "message": "This channel does not allow devices to self associate" } ``` ### 4. Kanal deaktivieren (DELETE-Anfrage) [Section titled “4. Kanal deaktivieren (DELETE-Anfrage)”](#4-kanal-deaktivieren-delete-anfrage) Wenn das Plugin `unsetChannel()` aufruft, sendet es eine DELETE-Anfrage, um die Kanalzuweisung des Geräts zu entfernen. #### Anforderungsformat [Section titled “Anforderungsformat”](#anforderungsformat-3) ```typescript // DELETE /api/channel_self interface UnsetChannelRequest { device_id: string app_id: string platform: "ios" | "android" | "electron" plugin_version: string version_build: string version_code: string version_name: string } ``` ## Implementierungsbeispiel [Section titled “Implementierungsbeispiel”](#implementierungsbeispiel) Hier ist ein JavaScript-Beispiel für die Implementierung des Kanalendpunkts: ```typescript interface ChannelRequest { device_id: string app_id: string channel?: string platform: "ios" | "android" | "electron" plugin_version: string version_build: string version_code: string version_name: string } interface ChannelResponse { status: "ok" | "error" channel?: string allowSet?: boolean message?: string error?: string } export const handler = async (event) => { const method = event.httpMethod || event.method const body = JSON.parse(event.body || '{}') as ChannelRequest const { device_id, app_id, channel, platform } = body try { switch (method) { case 'GET': return await getDeviceChannel(device_id, app_id) case 'POST': return await setDeviceChannel(device_id, app_id, channel!, platform) case 'DELETE': return await unsetDeviceChannel(device_id, app_id) default: return { status: "error", error: "Method not allowed" } } } catch (error) { return { status: "error", error: error.message } } } async function getDeviceChannel(deviceId: string, appId: string): Promise { // Query your database for device channel assignment const assignment = await database.getDeviceChannel(deviceId, appId) if (assignment) { return { status: "ok", channel: assignment.channel, allowSet: assignment.allowSelfAssign } } // Return default channel if no assignment found return { status: "ok", channel: "production", // Your default channel allowSet: true } } async function setDeviceChannel( deviceId: string, appId: string, channel: string, platform: string ): Promise { // Validate channel exists and allows self-assignment const channelConfig = await database.getChannelConfig(channel, appId) if (!channelConfig) { return { status: "error", error: "Channel not found" } } if (!channelConfig.allowDeviceSelfSet) { return { status: "error", error: "Channel does not allow self-assignment" } } // Check platform restrictions if (platform === "ios" && !channelConfig.ios) { return { status: "error", error: "Channel not available for iOS" } } if (platform === "android" && !channelConfig.android) { return { status: "error", error: "Channel not available for Android" } } if (platform === "electron" && !channelConfig.electron) { return { status: "error", error: "Channel not available for Electron" } } // Save the assignment await database.setDeviceChannel(deviceId, appId, channel) return { status: "ok", message: "Device assigned to channel successfully" } } async function unsetDeviceChannel(deviceId: string, appId: string): Promise { // Remove device channel assignment await database.removeDeviceChannel(deviceId, appId) return { status: "ok", message: "Device channel assignment removed" } } ``` ## Kanalkonfiguration [Section titled “Kanalkonfiguration”](#kanalkonfiguration) Ihr Kanalsystem sollte diese Konfigurationsoptionen unterstützen: ```typescript interface ChannelConfig { name: string appId: string // Platform targeting ios: boolean // Allow updates to iOS devices android: boolean // Allow updates to Android devices electron: boolean // Allow updates to Electron apps // Device type restrictions allow_emulator: boolean // Allow updates on emulator/simulator devices allow_device: boolean // Allow updates on real/physical devices // Build type restrictions allow_dev: boolean // Allow updates on development builds (is_prod=false) allow_prod: boolean // Allow updates on production builds (is_prod=true) // Channel assignment public: boolean // Default channel - devices fall back to this when no override allowDeviceSelfSet: boolean // Allow devices to self-assign via setChannel() // Update policies disableAutoUpdate: "major" | "minor" | "version_number" | "none" disableAutoUpdateUnderNative: boolean } ``` ### GerätefilterlogikBeim Auflisten kompatibler Kanäle (GET-Anfrage) sollten Sie Kanäle anhand dieser Bedingungen filtern: [Section titled “GerätefilterlogikBeim Auflisten kompatibler Kanäle (GET-Anfrage) sollten Sie Kanäle anhand dieser Bedingungen filtern:”](#gerätefilterlogikbeim-auflisten-kompatibler-kanäle-get-anfrage-sollten-sie-kanäle-anhand-dieser-bedingungen-filtern) 1. **Plattformprüfung**: Der Kanal muss die Plattform des Geräts zulassen (`ios`, `android` oder `electron`) 2. **Gerätetypprüfung**: * Wenn `is_emulator=true`: Kanal muss `allow_emulator=true` haben * Wenn `is_emulator=false`: Kanal muss `allow_device=true` haben 3. **Überprüfung des Build-Typs**: * Wenn `is_prod=true`: Kanal muss `allow_prod=true` haben * Wenn `is_prod=false`: Kanal muss `allow_dev=true` haben 4. **Sichtbarkeitsprüfung**: Der Kanal muss entweder `public=true` ODER `allow_device_self_set=true` sein ```typescript // Example filtering logic function getCompatibleChannels( platform: 'ios' | 'android' | 'electron', isEmulator: boolean, isProd: boolean, channels: ChannelConfig[] ): ChannelConfig[] { return channels.filter(channel => { // Platform check if (!channel[platform]) return false // Device type check if (isEmulator && !channel.allow_emulator) return false if (!isEmulator && !channel.allow_device) return false // Build type check if (isProd && !channel.allow_prod) return false if (!isProd && !channel.allow_dev) return false // Must be accessible (public or self-assignable) if (!channel.public && !channel.allowDeviceSelfSet) return false return true }) } ``` ## Beispiel für ein Datenbankschema [Section titled “Beispiel für ein Datenbankschema”](#beispiel-für-ein-datenbankschema) Sie müssen Kanalkonfigurationen und Gerätezuweisungen speichern: ```sql -- Channels table CREATE TABLE channels ( id SERIAL PRIMARY KEY, name VARCHAR(255) NOT NULL, app_id VARCHAR(255) NOT NULL, -- Platform targeting ios BOOLEAN DEFAULT true, android BOOLEAN DEFAULT true, electron BOOLEAN DEFAULT true, -- Device type restrictions allow_emulator BOOLEAN DEFAULT true, -- Allow emulator/simulator devices allow_device BOOLEAN DEFAULT true, -- Allow real/physical devices -- Build type restrictions allow_dev BOOLEAN DEFAULT true, -- Allow development builds allow_prod BOOLEAN DEFAULT true, -- Allow production builds -- Channel assignment public BOOLEAN DEFAULT false, -- Default channel (fallback) allow_device_self_set BOOLEAN DEFAULT false, -- Allow self-assignment -- Update policies disable_auto_update VARCHAR(50) DEFAULT 'none', disable_auto_update_under_native BOOLEAN DEFAULT false, created_at TIMESTAMP DEFAULT NOW(), UNIQUE(name, app_id) ); -- Device channel assignments table CREATE TABLE device_channels ( id SERIAL PRIMARY KEY, device_id VARCHAR(255) NOT NULL, app_id VARCHAR(255) NOT NULL, channel_name VARCHAR(255) NOT NULL, assigned_at TIMESTAMP DEFAULT NOW(), UNIQUE(device_id, app_id) ); ``` ## Fehlerbehandlung [Section titled “Fehlerbehandlung”](#fehlerbehandlung) Behandeln Sie häufige Fehlerszenarien: ```typescript // Channel not found { "status": "error", "error": "Channel 'beta' not found" } // Self-assignment not allowed { "status": "error", "error": "Channel does not allow device self-assignment" } // Platform not supported { "status": "error", "error": "Channel not available for this platform" } // Invalid request { "status": "error", "error": "Missing required field: device_id" } ``` ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) 1. **Sicherheit**: Überprüfen Sie alle Kanalzuweisungen anhand Ihrer Geschäftsregeln 2. **Protokollierung**: Protokollieren Sie alle Kanalvorgänge zur Überwachung und zum Debuggen 3. **Leistung**: Kanalkonfigurationen zwischenspeichern, um Datenbankabfragen zu reduzieren 4. **Validierung**: Überprüfen Sie die Authentizität von Geräte-ID und App-ID 5. **Ratenbegrenzung**: Implementieren Sie eine Ratenbegrenzung, um Missbrauch zu verhindern ## Integration mit Updates [Section titled “Integration mit Updates”](#integration-mit-updates) Kanalzuweisungen funktionieren mit Ihrem [Update API Endpoint](/docs/plugin/self-hosted/handling-updates/). Wenn ein Gerät ein Update anfordert, überprüfen Sie seine Kanalzuweisung, um zu bestimmen, welche Version bereitgestellt werden soll: ```typescript async function getUpdateForDevice(deviceId: string, appId: string) { // Get device's channel assignment const channelAssignment = await getDeviceChannel(deviceId, appId) const channel = channelAssignment.channel || 'production' // Get the version assigned to this channel const channelVersion = await getChannelVersion(channel, appId) return { version: channelVersion.version, url: channelVersion.url, checksum: channelVersion.checksum } } ``` Dadurch entsteht ein vollständiges, selbst gehostetes Kanalverwaltungssystem, das Ihnen die volle Kontrolle darüber gibt, wie Updates an Ihre Benutzer verteilt werden. # Statistik API > Wie Sie den Statistik-Endpunkt von Capgo mit dem Auto-Update-Plugin im selbstgehosteten Modus verwenden, wofür sie verwendet werden und was Sie erwartet ## Statistik API [Section titled “Statistik API”](#statistik-api) Ab Version 1.3.0 kann das Update-System Statistiken senden! Standardmäßig werden alle Statistiken an unseren Server gesendet, um die Nutzung und Forschung zu verstehen. Note Für Statistiken werden keine privaten Daten gesendet, sondern nur zufällige UUID, Versionsaktualisierung, native App-Version, Plattform, Aktion und App-ID. Wenn Sie diese Daten stattdessen an Ihren Server senden möchten, ändern Sie die Konfiguration unten: ```tsx // capacitor.config.json { "appId": "**.***.**", "appName": "Name", "plugins": { "CapacitorUpdater": { "statsUrl": "YOUR_URL" } } } ``` ## Datenstruktur [Section titled “Datenstruktur”](#datenstruktur) Was Ihr Server erhält, ist: ```tsx interface AppInfosStats { "action": "set", // can be set, delete, set_fail, reset, revert // Then it's the same info as update "app_id": "**.***.**", // app identifier in the store "device_id": "*******", // unique id per app install "platform": "ios", // or android, or electron "custom_id": "user_1", // represent your user "version_name": "1.2.3", // version of the web build "version_build": "1.2.0", // version of the native build "version_code": "120", // build number of the native build "version_os": "16", // OS version of the device "plugin_version": "4.0.0"// to make your api behave differently with different plugins "is_emulator": false, "is_prod": false, } ``` Sie können es auch mit einer leeren Zeichenfolge vollständig deaktivieren. Bedenken Sie, dass Statistiken privatfreundlich gestaltet sind und mir helfen, zu verstehen, wie die Leute das Plugin nutzen, um Probleme zu lösen und es zu verbessern. ## Erwartetes „Kein Update“-Verhalten [Section titled “Erwartetes „Kein Update“-Verhalten”](#erwartetes-kein-update-verhalten) Wenn Ihr Update-Endpunkt **keine neue Version** hat, sollte er mit einer Fehlernutzlast wie der folgenden antworten: ```json { "error": "no_new_version_available", "message": "No new version available" } ``` Der Code `error` muss genau `no_new_version_available` sein. Der `message` kann eine beliebige Zeichenfolge sein (nur für Protokollierung/Debugging). Dies ist das erwartete Verhalten und wird weiterhin mit HTTP `200` zurückgegeben. Wenn Ihr Update-Endpunkt stattdessen eine `200`-Antwort ohne `url` zurückgibt, behandelt das Plugin dies als Download-Fehler und sendet eine `download_fail`-Statistik. ## Implementierungsbeispiel [Section titled “Implementierungsbeispiel”](#implementierungsbeispiel) Hier ist ein Beispiel für Code in JavaScript zum Speichern der Statistiken des Plugins: ```typescript interface AppInfos { version_name: string action: 'ping' | 'delete' | 'reset' | 'set' | 'get' | 'set_fail' | 'update_fail' | 'download_fail' | 'windows_path_fail' | 'canonical_path_fail' | 'directory_path_fail' | 'unzip_fail' | 'low_mem_fail' | 'download_10' | 'download_20' | 'download_30' | 'download_40' | 'download_50' | 'download_60' | 'download_70' | 'download_80' | 'download_90' | 'download_complete' | 'download_manifest_start' | 'download_manifest_complete' | 'download_zip_start' | 'download_zip_complete' | 'download_manifest_file_fail' | 'download_manifest_checksum_fail' | 'download_manifest_brotli_fail' | 'decrypt_fail' | 'app_moved_to_foreground' | 'app_moved_to_background' | 'uninstall' | 'needPlanUpgrade' | 'missingBundle' | 'noNew' | 'disablePlatformIos' | 'disablePlatformAndroid' | 'disableAutoUpdateToMajor' | 'cannotUpdateViaPrivateChannel' | 'disableAutoUpdateToMinor' | 'disableAutoUpdateToPatch' | 'channelMisconfigured' | 'disableAutoUpdateMetadata' | 'disableAutoUpdateUnderNative' | 'disableDevBuild' | 'disableEmulator' | 'cannotGetBundle' | 'checksum_fail' | 'NoChannelOrOverride' | 'setChannel' | 'getChannel' | 'rateLimited' | 'disableAutoUpdate' | 'InvalidIp' | 'keyMismatch' | 'blocked_by_server_url' version_build: string version_code: string version_os: string plugin_version: string platform: string app_id: string device_id: string custom_id?: string is_prod?: boolean is_emulator?: boolean } export const handler: Handler = async (event) => { const body = JSON.parse(event.body || '{}') as AppInfos const { platform, app_id, action, version_code, version_os, device_id, version_name, version_build, plugin_version, } = body console.log('update asked', platform, app_id, action, version_os, version_code, device_id, version_name, version_build, plugin_version) // Save it in your database return { status: 'ok' } } ``` Dieser Endpunkt sollte einen JSON zurückgeben: ```json { "status": "ok" } ``` ## Aktionen [Section titled “Aktionen”](#aktionen) Detaillierte Beschreibungen aller Aktionscodes und ihrer Bedeutung finden Sie in der Debugging-Dokumentation: * **Vom Gerät gesendete Aktionen**: Siehe Abschnitt [Debug-Dokumentation – Vom Gerät gesendet](/docs/plugins/updater/debugging/#sent-from-the-device). * **Vom Backend gesendete Aktionen**: Siehe Abschnitt [Debug-Dokumentation – Vom Backend gesendet](/docs/plugins/updater/debugging/#sent-from-the-backend). [Umgang mit Updates ](/docs/plugin/self-hosted/handling-updates/) # Aktualisieren Sie den Endpunkt API > So erstellen Sie Ihren Update-Server-Endpunkt API, um auf Capgo-Plugin-Anfragen mit den richtigen Bundle-Informationen und Prüfsummen zu antworten Hier ist ein Beispiel für Code in JavaScript zum Senden eines Updates an das Plugin ```typescript interface AppInfos { version_name: string version_build: string version_os: string custom_id?: string is_prod?: boolean is_emulator?: boolean plugin_version: string platform: string app_id: string device_id: string } export const handler: Handler = async (event) => { const body = JSON.parse(event.body || '{}') as AppInfos const { platform, app_id, version_os, device_id, version_name, version_build, plugin_version, } = body console.log('update asked', platform, app_id, version_os, device_id, version_name, version_build, plugin_version) if (version_name === '1.0.0') { return { version: '1.0.1', url: 'https://apiurl.com/mybuild_101.zip', checksum: 'sha256_checksum_of_bundle', } } else if (version_name === '1.0.1') { return { version: '1.0.2', url: 'https://apiurl.com/mybuild_102.zip', checksum: 'sha256_checksum_of_bundle', } } else { return { message: 'Error version not found' version: '', url: '', } } } ``` ## Antwortformat [Section titled “Antwortformat”](#antwortformat) Für **nicht verschlüsselte Bundles** sollte Ihr Endpunkt Folgendes zurückgeben: ```json { "version": "1.0.2", "url": "https://apiurl.com/mybuild_102.zip", "checksum": "sha256_checksum_of_bundle" } ``` Für **verschlüsselte Bundles** müssen Sie auch den Sitzungsschlüssel angeben: ```json { "version": "1.0.2", "url": "https://apiurl.com/mybuild_102.zip", "checksum": "encrypted_checksum_from_encrypt_command", "session_key": "ivSessionKey_from_encrypt_command" } ``` Und wenn kein Update oder Fehler vorliegt, fügen Sie den Schlüssel `message` und optional einen `error` hinzu: ```json { "message": "Version not found", "error": "The backend crashed", "version": "1.0.2", } ``` ## Feldbeschreibungen [Section titled “Feldbeschreibungen”](#feldbeschreibungen) * **`checksum`**: SHA256-Hash Ihrer Bundle-Zip-Datei zur Integritätsüberprüfung * **`session_key`**: Nur für verschlüsselte Bundles erforderlich – dies ist der vom Verschlüsselungsbefehl zurückgegebene `ivSessionKey` * **`version`**: Versionskennung im Semver-Format * **`url`**: HTTPS-URL, unter der das Bundle heruntergeladen werden kann ## Bundle-Erstellung [Section titled “Bundle-Erstellung”](#bundle-erstellung) Informationen zum Erstellen kompatibler Bundles und zum Generieren von Prüfsummen finden Sie in der [Dokumentation zum automatischen Update](/docs/plugin/self-hosted/auto-update/#generating-bundle-checksum). Informationen zu verschlüsselten Bundles finden Sie in der [Dokumentation zu verschlüsselten Bundles](/docs/plugin/self-hosted/encrypted-bundles/), in der der vollständige Verschlüsselungsworkflow erläutert wird. # Manuelles Update > Ein detaillierter Leitfaden zur Nutzung des manuellen Update-Plugins in einer Self-Hosted- Umgebung mit umfassenden Anweisungen zur Konfiguration und Nutzung um Updates effektiv ohne automatische Prozesse zu verwalten. ## Konfiguration [Section titled “Konfiguration”](#konfiguration) Fügen Sie dies zu Ihrer `capacitor.config.json` hinzu, um Auto-Update zu deaktivieren. ```tsx // capacitor.config.json { "appId": "**.***.**", "appName": "Name", "plugins": { "CapacitorUpdater": { "autoUpdate": false, } } } ``` ## Nutzung [Section titled “Nutzung”](#nutzung) Sie können dieses Beispiel verwenden oder die Logik in Ihrer App neu erstellen. Caution Wir zwingen den Benutzer, die App mit einer statischen, im Code deklarierten Version zu aktualisieren. Dies ist nicht empfohlen. Sie sollten eine dynamische Version von Ihrem Server verwenden. Danger Wir führen in diesem Beispiel keine Versionsprüfung, Entschlüsselung oder Checksummen-Validierung durch. Sie sollten dies selbst durchführen. ```tsx import { CapacitorUpdater } from '@capgo/capacitor-updater' import { SplashScreen } from '@capacitor/splash-screen' import { App } from '@capacitor/app' let data = {version: ""} CapacitorUpdater.notifyAppReady() App.addListener('appStateChange', async(state) => { if (state.isActive) { // Führen Sie den Download während aktiver App-Zeit durch, um fehlgeschlagene Downloads zu verhindern data = await CapacitorUpdater.download({ version: '0.0.4', url: 'https://github.com/Cap-go/demo-app/releases/download/0.0.4/dist.zip', }) } if (!state.isActive && data.version !== "") { // Führen Sie den Wechsel durch, wenn der Benutzer die App verlässt SplashScreen.show() try { await CapacitorUpdater.set(data) } catch (err) { console.log(err) SplashScreen.hide() // falls der Wechsel fehlschlägt, ansonsten muss die neue App ihn ausblenden } } }) ``` Note Wenn dies wie viel Arbeit klingt, versuchen Sie die [Capgo-Testversion](https://capgo.app/register/). Sie werden all dies für Sie übernehmen. # Settings > Alle verfügbaren Einstellungen für Capacitor Updater, alle Konfigurationen, die Sie in Ihrer Kondensatorkonfiguration festlegen können, und deren Verwendung Um eine detailliertere Kontrolle über das Update-System zu haben, können Sie es mit den folgenden Einstellungen konfigurieren: Tip Alle Änderungen an diesen Einstellungen in der Datei „capacitor.config“ erfordern eine Synchronisierung der Plattform und eine Freigabe im Store, damit Produktions-Apps sie empfangen können. ## `allowModifyUrl` [Section titled “allowModifyUrl”](#allowmodifyurl) > Ermöglichen Sie dem Plugin, updateUrl, statsUrl undchannelUrl dynamisch von der JavaScript-Seite aus zu ändern. Verfügbar auf Android, iOS und Electron. Standard: `false` capacitor.config.json ```json { "plugins": { "CapacitorUpdater": { "allowModifyUrl": true } } } ``` ## `appId` [Section titled “appId”](#appid) > Konfigurieren Sie die App-ID für die App in der Konfiguration. Verfügbar auf Android, iOS und Electron. Standard: `undefined` capacitor.config.json ```json { "plugins": { "CapacitorUpdater": { "appId": "com.example.app" } } } ``` ## `appReadyTimeout` [Section titled “appReadyTimeout”](#appreadytimeout) > Konfigurieren Sie die Anzahl der Millisekunden, die das native Plugin warten soll, bevor ein Update als „fehlgeschlagen“ betrachtet wird. Verfügbar auf Android, iOS und Electron. Standard: `10000` (10 Sekunden) capacitor.config.json ```json { "plugins": { "CapacitorUpdater": { "appReadyTimeout": 1000 } } } ``` ## `autoDeleteFailed` [Section titled “autoDeleteFailed”](#autodeletefailed) > Konfigurieren Sie, ob das Plugin fehlerhafte Bundles automatisch löschen soll. Verfügbar auf Android, iOS und Electron. Standard: `true` capacitor.config.json ```json { "plugins": { "CapacitorUpdater": { "autoDeleteFailed": false } } } ``` ## `autoDeletePrevious` [Section titled “autoDeletePrevious”](#autodeleteprevious) > Konfigurieren Sie, ob das Plugin nach einem erfolgreichen Update automatisch vorherige Bundles löschen soll. Verfügbar auf Android, iOS und Electron. Standard: `true` capacitor.config.json ```json { "plugins": { "CapacitorUpdater": { "autoDeletePrevious": false } } } ``` ## `autoSplashscreen` [Section titled “autoSplashscreen”](#autosplashscreen) > Automatisches Ausblenden des Begrüßungsbildschirms bei Verwendung von DirectUpdate. Wenn diese Option aktiviert ist, blendet das Plugin den Begrüßungsbildschirm automatisch aus, nachdem Updates angewendet wurden oder wenn kein Update erforderlich ist. Dadurch entfällt die Notwendigkeit, manuell auf appReady-Ereignisse zu warten und SplashScreen.hide() aufzurufen. Funktioniert nur, wenn directUpdate auf „atInstall“, „always“ oder true gesetzt ist. Erfordert die Installation und Konfiguration des Plugins @capacitor/splash-screen mit launchAutoHide: false. Erfordert die Aktivierung von autoUpdate und directUpdate. Verfügbar auf Android, iOS und Electron. Standard: `false` capacitor.config.json ```json { "plugins": { "CapacitorUpdater": { "autoUpdate": true, "directUpdate": "atInstall", "autoSplashscreen": true } } } ``` ## `autoUpdate` [Section titled “autoUpdate”](#autoupdate) > Konfigurieren Sie, ob das Plugin Auto Update über einen Update-Server verwenden soll. Verfügbar auf Android, iOS und Electron. Standard: `true` capacitor.config.json ```json { "plugins": { "CapacitorUpdater": { "autoUpdate": false } } } ``` ## `channelUrl` [Section titled “channelUrl”](#channelurl) > Konfigurieren Sie die URL/den Endpunkt für Kanaloperationen. Verfügbar auf Android, iOS und Electron. Standard: `https://plugin.capgo.app/channel_self` capacitor.config.json ```json { "plugins": { "CapacitorUpdater": { "channelUrl": "https://example.com/api/channel" } } } ``` ## `defaultChannel` [Section titled “defaultChannel”](#defaultchannel) > Legen Sie in der Konfiguration den Standardkanal für die App fest. Groß- und Kleinschreibung beachten. Diese Einstellung überschreibt den in der Cloud festgelegten Standardkanal, berücksichtigt jedoch weiterhin die in der Cloud vorgenommenen Überschreibungen. Verfügbar auf Android, iOS und Electron. Standard: `undefined` capacitor.config.json ```json { "plugins": { "CapacitorUpdater": { "defaultChannel": "production" } } } ``` ## `directUpdate` [Section titled “directUpdate”](#directupdate) > Konfigurieren Sie, wann das Plugin Updates direkt installieren soll. Nur für den AutoUpdate-Modus. Funktioniert gut für Apps mit weniger als 10 MB und mit Uploads, die mit der Flagge —delta erfolgen. Zip-Dateien oder Apps mit mehr als 10 MB werden für Benutzer relativ langsam aktualisiert.Optionen: * `false`: Niemals direkte Updates durchführen (Standardverhalten verwenden: beim Start herunterladen, im Hintergrund festlegen) * „atInstall“: Direktes Update nur, wenn die App installiert ist, aus dem Store aktualisiert, andernfalls als „directUpdate = false“ fungieren * „onLaunch“: Direktes Update nur bei installierter App, Aktualisierung aus dem Store oder nach App-Kill, andernfalls als „directUpdate = false“ fungieren * „Immer“: Direktes Update in allen vorherigen Fällen (App installiert, aus dem Store aktualisiert, nach App-Kill oder App-Fortsetzung), niemals als „directUpdate = false“ fungieren * `true`: (veraltet) Aus Gründen der Abwärtskompatibilität dasselbe wie „always“. Verfügbar für Android, iOS und Electron. Standard: `false` capacitor.config.json ```json { "plugins": { "CapacitorUpdater": { "autoUpdate": true, "directUpdate": "atInstall" } } } ``` ## `disableJSLogging` [Section titled “disableJSLogging”](#disablejslogging) > Deaktivieren Sie die JavaScript-Protokollierung des Plugins. Wenn „true“, meldet sich das Plugin nicht an der JavaScript-Konsole an. Es wird nur das native Protokoll erstellt. Verfügbar auf Android, iOS und Electron. Standard: `false` capacitor.config.json ```json { "plugins": { "CapacitorUpdater": { "disableJSLogging": true } } } ``` ## `keepUrlPathAfterReload` [Section titled “keepUrlPathAfterReload”](#keepurlpathafterreload) > Konfigurieren Sie das Plugin so, dass der URL-Pfad nach einem Neuladen erhalten bleibt. Caution Wenn ein Neuladen ausgelöst wird, wird „window\.history“ gelöscht. Verfügbar auf Android, iOS und Electron. Standard: `false` capacitor.config.json ```json { "plugins": { "CapacitorUpdater": { "keepUrlPathAfterReload": true } } } ``` ## `periodCheckDelay` [Section titled “periodCheckDelay”](#periodcheckdelay) > Konfigurieren Sie den Verzögerungszeitraum für die Periodenaktualisierungsprüfung. Die Einheit ist in Sekunden. Darf nicht kürzer als 600 Sekunden (10 Minuten) sein. Verfügbar auf Android, iOS und Electron. Standard: `600` (10 Minuten) capacitor.config.json ```json { "plugins": { "CapacitorUpdater": { "periodCheckDelay": 600 // (10 minutes) } } } ``` ## `publicKey` [Section titled “publicKey”](#publickey) > Konfigurieren Sie den öffentlichen Schlüssel für die End-to-End-Live-Update-Verschlüsselung Version 2. Verfügbar auf Android, iOS und Electron. Standard: `undefined` capacitor.config.json ```json { "plugins": { "CapacitorUpdater": { "publicKey": "YOUR_PUBLIC_KEY" } } } ``` ## `resetWhenUpdate` [Section titled “resetWhenUpdate”](#resetwhenupdate) > Löschen Sie zuvor heruntergeladene Bundles automatisch, wenn ein neueres natives App-Bundle auf dem Gerät installiert wird. Verfügbar auf Android, iOS und Electron. Standard: `true` capacitor.config.json ```json { "plugins": { "CapacitorUpdater": { "resetWhenUpdate": false } } } ``` ## `responseTimeout` [Section titled “responseTimeout”](#responsetimeout) > Konfigurieren Sie die Anzahl der Millisekunden, die das native Plugin warten soll, bevor das Zeitlimit API berücksichtigt wird. Verfügbar auf Android, iOS und Electron. Standard: `20` (20 Sekunden) capacitor.config.json ```json { "plugins": { "CapacitorUpdater": { "responseTimeout": 10 // (10 seconds) } } } ``` ## `shakeMenu` [Section titled “shakeMenu”](#shakemenu) > Aktivieren Sie die Schüttelgeste, um das Update-Menü zu Debug-/Testzwecken anzuzeigen. Verfügbar auf Android, iOS und Electron. Standard: `false` capacitor.config.json ```json { "plugins": { "CapacitorUpdater": { "shakeMenu": true } } } ``` ## `statsUrl` [Section titled “statsUrl”](#statsurl) > Konfigurieren Sie die URL/den Endpunkt, an den Update-Statistiken gesendet werden. Verfügbar auf Android, iOS und Electron. Auf „“ setzen, um die Statistikberichterstattung zu deaktivieren. Standard: `https://plugin.capgo.app/stats` capacitor.config.json ```json { "plugins": { "CapacitorUpdater": { "statsUrl": "https://example.com/api/stats" } } } ``` ## `updateUrl` [Section titled “updateUrl”](#updateurl) > Konfigurieren Sie die URL/den Endpunkt, an den Update-Prüfungen gesendet werden. Verfügbar auf Android, iOS und Electron. Standard: `https://plugin.capgo.app/updates` capacitor.config.json ```json { "plugins": { "CapacitorUpdater": { "updateUrl": "https://example.com/api/auto_update" } } } ``` ## `version` [Section titled “version”](#version) > Konfigurieren Sie die aktuelle Version der App. Dies wird für die erste Update-Anfrage verwendet. Wenn nicht festgelegt, erhält das Plugin die Version aus dem nativen Code. Verfügbar auf Android, iOS und Electron. Standard: `undefined` capacitor.config.json ```json { "plugins": { "CapacitorUpdater": { "version": "1.0.0" } } } ``` ## Entwicklungseinstellungen [Section titled “Entwicklungseinstellungen”](#entwicklungseinstellungen) ### `localApi`> Konfigurieren Sie CLI so, dass zum Testen eine lokale API verwendet wird. [Section titled “localApi> Konfigurieren Sie CLI so, dass zum Testen eine lokale API verwendet wird.”](#localapi-konfigurieren-sie-cli-so-dass-zum-testen-eine-lokale-api-verwendet-wird) Standard: `undefined` capacitor.config.json ```json { "plugins": { "CapacitorUpdater": { "localApi": "http://localhost:54321/functions/v1" } } } ``` ### `localApiFiles` [Section titled “localApiFiles”](#localapifiles) > Konfigurieren Sie CLI so, dass zum Testen eine lokale Datei-API verwendet wird. Standard: `undefined` capacitor.config.json ```json { "plugins": { "CapacitorUpdater": { "localApiFiles": "http://localhost:54321/functions/v1/files" } } } ``` ### `localHost` [Section titled “localHost”](#localhost) > Konfigurieren Sie CLI so, dass ein lokaler Server zum Testen oder ein selbstgehosteter Update-Server verwendet wird. Standard: `undefined` capacitor.config.json ```json { "plugins": { "CapacitorUpdater": { "localHost": "http://localhost:5173" } } } ``` ### `localSupa` [Section titled “localSupa”](#localsupa) > Konfigurieren Sie CLI so, dass ein lokaler Server zum Testen oder ein selbstgehosteter Update-Server verwendet wird. Standard: `undefined` capacitor.config.json ```json { "plugins": { "CapacitorUpdater": { "localSupa": "http://localhost:54321" } } } ``` ### `localSupaAnon` [Section titled “localSupaAnon”](#localsupaanon) > Konfigurieren Sie CLI so, dass zum Testen ein lokaler Server verwendet wird. Standard: `undefined` capacitor.config.json ```json { "plugins": { "CapacitorUpdater": { "localSupaAnon": "YOUR_LOCAL_ANON_KEY" } } } ``` ### `localWebHost` [Section titled “localWebHost”](#localwebhost) > Konfigurieren Sie CLI so, dass ein lokaler Server zum Testen oder ein selbstgehosteter Update-Server verwendet wird. Standard: `undefined` capacitor.config.json ```json { "plugins": { "CapacitorUpdater": { "localWebHost": "http://localhost:5173" } } } ``` Tip In der [Capgo Web-App](https://console.capgo.app/login) sind zusätzliche Einstellungen verfügbar, die pro Kanal konfiguriert werden können, ohne dass eine native App-Version erforderlich ist. # @capgo/capacitor-uploader > Laden Sie Dateien im Hintergrund hoch mit Fortschrittsanzeige, wiederaufnehmbaren Uploads und netzwerkbewusster Handhabung für Capacitor-Apps. Hintergrund-Uploads Setzen Sie Uploads auch dann fort, wenn die App im Hintergrund läuft Fortschrittsanzeige Echtzeit-Upload-Fortschritt mit detaillierten Statistiken Wiederaufnehmbare Uploads Automatisches Fortsetzen unterbrochener Uploads Umfassende Dokumentation Lesen Sie die [Dokumentation](/docs/plugins/uploader/getting-started/), um das Plugin in nur wenigen Minuten zu meistern. # Erste Schritte > Erfahren Sie, wie Sie das Background Uploader Plugin für zuverlässige Datei-Uploads in Ihrer Capacitor-App installieren und verwenden. 1. **Paket installieren** * npm ```sh npm i @capgo/capacitor-uploader ``` * pnpm ```sh pnpm add @capgo/capacitor-uploader ``` * yarn ```sh yarn add @capgo/capacitor-uploader ``` * bun ```sh bun add @capgo/capacitor-uploader ``` 2. **Mit nativen Projekten synchronisieren** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` 3. **Berechtigungen konfigurieren** ### iOS [Section titled “iOS”](#ios) Fügen Sie Hintergrundmodi zu Ihrer `Info.plist` hinzu: ```xml UIBackgroundModes processing ``` ### Android [Section titled “Android”](#android) Fügen Sie Berechtigungen zu Ihrer `AndroidManifest.xml` hinzu: ```xml ``` ## Verwendung [Section titled “Verwendung”](#verwendung) Importieren Sie das Plugin und verwenden Sie seine Methoden zum Hochladen von Dateien: ```typescript import { Uploader } from '@capgo/capacitor-uploader'; // Einen Upload starten const startUpload = async () => { const upload = await Uploader.startUpload({ filePath: 'file:///path/to/your/file.jpg', serverUrl: 'https://your-server.com/upload', method: 'POST', headers: { 'Authorization': 'Bearer your-token' }, parameters: { 'userId': '12345', 'type': 'profile' }, notificationTitle: 'Foto wird hochgeladen' }); console.log('Upload-ID:', upload.id); }; // Auf Upload-Ereignisse hören const listener = await Uploader.addListener('events', (event) => { switch (event.name) { case 'uploading': console.log(`Upload ${event.payload.id}: ${event.payload.percent}%`); break; case 'completed': console.log('Upload abgeschlossen:', event.payload.id); console.log('Statuscode:', event.payload.statusCode); break; case 'failed': console.error('Upload fehlgeschlagen:', event.payload.id); console.error('Fehler:', event.payload.error); break; } }); // Denken Sie daran, den Listener zu entfernen, wenn er nicht mehr benötigt wird // listener.remove(); // Einen Upload abbrechen const cancelUpload = async (uploadId: string) => { await Uploader.cancelUpload({ id: uploadId }); }; // Alle aktiven Uploads abrufen const getActiveUploads = async () => { const uploads = await Uploader.getUploads(); console.log('Aktive Uploads:', uploads); }; ``` ## API-Referenz [Section titled “API-Referenz”](#api-referenz) ### startUpload(options) [Section titled “startUpload(options)”](#startuploadoptions) Startet einen neuen Datei-Upload. ```typescript interface UploadOptions { filePath: string; serverUrl: string; method?: 'POST' | 'PUT'; headers?: { [key: string]: string }; parameters?: { [key: string]: string }; notificationTitle?: string; notificationBody?: string; useUtf8Charset?: boolean; maxRetries?: number; } ``` ### cancelUpload(options) [Section titled “cancelUpload(options)”](#canceluploadoptions) Bricht einen laufenden Upload ab. ```typescript interface CancelOptions { id: string; } ``` ### getUploads() [Section titled “getUploads()”](#getuploads) Gibt alle aktiven Uploads zurück. ### removeUpload(options) [Section titled “removeUpload(options)”](#removeuploadoptions) Entfernt einen Upload aus der Warteschlange. ```typescript interface RemoveOptions { id: string; } ``` ## Ereignisse [Section titled “Ereignisse”](#ereignisse) ### events [Section titled “events”](#events) Alle Upload-Ereignisse werden über einen einzigen `events`-Listener übermittelt. Das Ereignis enthält ein `name`-Feld zur Identifizierung des Typs: ```typescript Uploader.addListener('events', (event: UploadEvent) => { switch (event.name) { case 'uploading': // Upload läuft console.log(`Fortschritt: ${event.payload.percent}%`); console.log(`ID: ${event.payload.id}`); break; case 'completed': // Upload erfolgreich abgeschlossen console.log(`Abgeschlossen: ${event.payload.id}`); console.log(`Statuscode: ${event.payload.statusCode}`); break; case 'failed': // Upload fehlgeschlagen console.error(`Fehlgeschlagen: ${event.payload.id}`); console.error(`Fehler: ${event.payload.error}`); break; } }); ``` **Ereignistypen:** * `uploading` - Fortschritts-Update mit `percent` und `id` * `completed` - Upload abgeschlossen mit `id` und `statusCode` * `failed` - Upload fehlgeschlagen mit `id` und `error`-Nachricht ## Erweiterte Funktionen [Section titled “Erweiterte Funktionen”](#erweiterte-funktionen) ### Multipart-Form-Upload [Section titled “Multipart-Form-Upload”](#multipart-form-upload) ```typescript const uploadWithFormData = async () => { const upload = await Uploader.startUpload({ filePath: 'file:///path/to/photo.jpg', serverUrl: 'https://api.example.com/upload', method: 'POST', parameters: { 'name': 'profile-photo', 'description': 'Benutzerprofilfoto' }, headers: { 'X-API-Key': 'your-api-key' } }); }; ``` ### Binär-Upload [Section titled “Binär-Upload”](#binär-upload) ```typescript const uploadBinary = async () => { const upload = await Uploader.startUpload({ filePath: 'file:///path/to/data.bin', serverUrl: 'https://api.example.com/binary', method: 'PUT', headers: { 'Content-Type': 'application/octet-stream' } }); }; ``` ### Netzwerkbewusstes Hochladen [Section titled “Netzwerkbewusstes Hochladen”](#netzwerkbewusstes-hochladen) ```typescript import { Network } from '@capacitor/network'; const smartUpload = async () => { const status = await Network.getStatus(); if (status.connectionType === 'wifi') { // Große Datei-Uploads über WLAN starten await startLargeUpload(); } else if (status.connectionType === 'cellular') { // Für später in die Warteschlange einreihen oder Benutzer warnen console.log('Verwendet mobile Daten'); } }; ``` ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) 1. **Alle Ereignistypen behandeln** ```typescript const setupUploadHandlers = () => { Uploader.addListener('events', (event) => { switch (event.name) { case 'uploading': handleProgress(event.payload); break; case 'completed': handleCompleted(event.payload); break; case 'failed': handleError(event.payload); break; } }); }; ``` 2. **Listener aufräumen** ```typescript // Listener hinzufügen const listener = await Uploader.addListener('events', handleEvent); // Aufräumen, wenn fertig listener.remove(); ``` 3. **Fehlgeschlagene Uploads wiederholen** ```typescript const retryUpload = async (filePath: string, serverUrl: string) => { try { await Uploader.startUpload({ filePath, serverUrl, maxRetries: 3 }); } catch (error) { console.error('Upload nach Wiederholungen fehlgeschlagen:', error); } }; ``` 4. **Upload-Benachrichtigungen anzeigen** ```typescript await Uploader.startUpload({ filePath: 'file:///path/to/file', serverUrl: 'https://server.com/upload', notificationTitle: 'Datei wird hochgeladen', notificationBody: 'Ihre Datei wird hochgeladen...' }); ``` ## Plattformhinweise [Section titled “Plattformhinweise”](#plattformhinweise) ### iOS [Section titled “iOS”](#ios-1) * Verwendet `URLSession` für Hintergrund-Uploads * Erfordert Hintergrundverarbeitungsfähigkeit * Uploads werden fortgesetzt, wenn die App angehalten wird ### Android [Section titled “Android”](#android-1) * Verwendet `WorkManager` für zuverlässige Uploads * Zeigt Vordergrund-Dienstbenachrichtigung während Uploads an * Respektiert Akkuoptimierungseinstellungen # @capgo/capacitor-video-player > Leistungsstarker nativer Videoplayer mit Unterstützung für Vollbild, Einbettungsmodi, Untertitel und umfassende Wiedergabesteuerung. Mehrere Modi Vollbild- und eingebettete Wiedergabemodi Untertitel-Unterstützung Zeigen Sie Untertitel in mehreren Sprachen an Volle Kontrolle Wiedergabe-, Pausen-, Such-, Lautstärke- und Wiedergabegeschwindigkeitssteuerung Umfassende Dokumentation Lesen Sie die [Dokumentation](/docs/plugins/video-player/getting-started/), um die Videowiedergabe zu integrieren. # Erste Schritte > Erfahren Sie, wie Sie die native Videowiedergabe in Ihre Capacitor-App integrieren. 1. **Paket installieren** * npm ```sh npm i @capgo/capacitor-video-player ``` * pnpm ```sh pnpm add @capgo/capacitor-video-player ``` * yarn ```sh yarn add @capgo/capacitor-video-player ``` * bun ```sh bun add @capgo/capacitor-video-player ``` 2. **Mit nativen Projekten synchronisieren** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Grundlegende Verwendung [Section titled “Grundlegende Verwendung”](#grundlegende-verwendung) ```typescript import { CapacitorVideoPlayer } from '@capgo/capacitor-video-player'; // Initialize a video player const playVideo = async () => { const result = await CapacitorVideoPlayer.initPlayer({ mode: 'fullscreen', url: 'https://example.com/video.mp4', playerId: 'myPlayer', componentTag: 'div' }); console.log('Player initialized:', result); }; // Play the video await CapacitorVideoPlayer.play({ playerId: 'myPlayer' }); // Pause the video await CapacitorVideoPlayer.pause({ playerId: 'myPlayer' }); // Get current time const { value } = await CapacitorVideoPlayer.getCurrentTime({ playerId: 'myPlayer' }); console.log('Current time:', value); // Seek to position await CapacitorVideoPlayer.setCurrentTime({ playerId: 'myPlayer', seektime: 30 // seconds }); // Set volume (0-1) await CapacitorVideoPlayer.setVolume({ playerId: 'myPlayer', volume: 0.5 }); ``` ## API Referenz [Section titled “API Referenz”](#api-referenz) ### initPlayer(Optionen) [Section titled “initPlayer(Optionen)”](#initplayeroptionen) Initialisieren Sie eine Videoplayer-Instanz. ```typescript await CapacitorVideoPlayer.initPlayer({ mode: 'fullscreen', // or 'embedded' url: 'https://example.com/video.mp4', playerId: 'player1', subtitle: 'https://example.com/subtitles.vtt', language: 'en', rate: 1.0, exitOnEnd: true, loopOnEnd: false, pipEnabled: true, showControls: true }); ``` ### abspielen(Optionen) [Section titled “abspielen(Optionen)”](#abspielenoptionen) Spielen Sie das Video ab. ```typescript await CapacitorVideoPlayer.play({ playerId: 'player1' }); ``` ### Pause(Optionen) [Section titled “Pause(Optionen)”](#pauseoptionen) Halten Sie das Video an. ```typescript await CapacitorVideoPlayer.pause({ playerId: 'player1' }); ``` ### getDuration(Optionen) [Section titled “getDuration(Optionen)”](#getdurationoptionen) Erhalten Sie die Videodauer in Sekunden. ```typescript const { value } = await CapacitorVideoPlayer.getDuration({ playerId: 'player1' }); console.log('Duration:', value, 'seconds'); ``` ### getCurrentTime(Optionen) [Section titled “getCurrentTime(Optionen)”](#getcurrenttimeoptionen) Aktuelle Wiedergabeposition in Sekunden abrufen. ```typescript const { value } = await CapacitorVideoPlayer.getCurrentTime({ playerId: 'player1' }); ``` ### setCurrentTime(Optionen) [Section titled “setCurrentTime(Optionen)”](#setcurrenttimeoptionen) Suchen Sie nach einer bestimmten Zeit. ```typescript await CapacitorVideoPlayer.setCurrentTime({ playerId: 'player1', seektime: 60 // seconds }); ``` ### setVolume(Optionen) [Section titled “setVolume(Optionen)”](#setvolumeoptionen) Lautstärke einstellen (0,0 bis 1,0). ```typescript await CapacitorVideoPlayer.setVolume({ playerId: 'player1', volume: 0.8 }); ``` ### getVolume(Optionen) [Section titled “getVolume(Optionen)”](#getvolumeoptionen) Aktuelle Lautstärke abrufen. ```typescript const { value } = await CapacitorVideoPlayer.getVolume({ playerId: 'player1' }); ``` ### setMuted(Optionen) [Section titled “setMuted(Optionen)”](#setmutedoptionen) Schalten Sie das Video stumm oder heben Sie die Stummschaltung auf. ```typescript await CapacitorVideoPlayer.setMuted({ playerId: 'player1', muted: true }); ``` ### setRate(Optionen) [Section titled “setRate(Optionen)”](#setrateoptionen) Wiedergabegeschwindigkeit einstellen. ```typescript await CapacitorVideoPlayer.setRate({ playerId: 'player1', rate: 1.5 // 1.5x speed }); ``` ### stopAllPlayers() [Section titled “stopAllPlayers()”](#stopallplayers) Stoppen Sie alle aktiven Spieler. ```typescript await CapacitorVideoPlayer.stopAllPlayers(); ``` \###exitPlayer() Beenden Sie den Videoplayer. ```typescript await CapacitorVideoPlayer.exitPlayer(); ``` ## Vollständiges Beispiel [Section titled “Vollständiges Beispiel”](#vollständiges-beispiel) ```typescript import { CapacitorVideoPlayer } from '@capgo/capacitor-video-player'; export class VideoPlayerService { private playerId = 'mainPlayer'; async initializePlayer(videoUrl: string, subtitleUrl?: string) { try { const result = await CapacitorVideoPlayer.initPlayer({ mode: 'fullscreen', url: videoUrl, playerId: this.playerId, subtitle: subtitleUrl, language: 'en', rate: 1.0, exitOnEnd: true, loopOnEnd: false, pipEnabled: true, bkmodeEnabled: true, showControls: true, displayMode: 'all' }); console.log('Player initialized:', result); return result; } catch (error) { console.error('Failed to initialize player:', error); throw error; } } async togglePlayPause() { const { value: isPlaying } = await CapacitorVideoPlayer.isPlaying({ playerId: this.playerId }); if (isPlaying) { await CapacitorVideoPlayer.pause({ playerId: this.playerId }); } else { await CapacitorVideoPlayer.play({ playerId: this.playerId }); } } async seekForward(seconds: number = 10) { const { value: currentTime } = await CapacitorVideoPlayer.getCurrentTime({ playerId: this.playerId }); await CapacitorVideoPlayer.setCurrentTime({ playerId: this.playerId, seektime: currentTime + seconds }); } async seekBackward(seconds: number = 10) { const { value: currentTime } = await CapacitorVideoPlayer.getCurrentTime({ playerId: this.playerId }); await CapacitorVideoPlayer.setCurrentTime({ playerId: this.playerId, seektime: Math.max(0, currentTime - seconds) }); } async setPlaybackSpeed(speed: number) { await CapacitorVideoPlayer.setRate({ playerId: this.playerId, rate: speed }); } async getProgress() { const { value: currentTime } = await CapacitorVideoPlayer.getCurrentTime({ playerId: this.playerId }); const { value: duration } = await CapacitorVideoPlayer.getDuration({ playerId: this.playerId }); return { currentTime, duration, percentage: (currentTime / duration) * 100 }; } async cleanup() { await CapacitorVideoPlayer.stopAllPlayers(); } } ``` ## Plattformhinweise [Section titled “Plattformhinweise”](#plattformhinweise) ### iOS [Section titled “iOS”](#ios) * Erfordert iOS 10.0+ * Verwendet nativen AVPlayer * Unterstützt Bild-in-Bild * Hintergrundwiedergabe unterstützt ### Android [Section titled “Android”](#android) * Erfordert Android 5.0 (API 21)+ * Verwendet ExoPlayer * Unterstützt Chromecast * Benutzerdefinierte Akzentfarben verfügbar ### Web [Section titled “Web”](#web) * Verwendet den HTML5-Videoplayer * Nur eingebetteter Modus * Eingeschränkte native Funktionen ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) 1. **Spieler aufräumen**: Stoppen Sie die Spieler immer, wenn Sie fertig sind 2. **Fehler behandeln**: Spieleraufrufe in Try-Catch einschließen 3. **Eindeutige Spieler-IDs**: Verwenden Sie eindeutige IDs für mehrere Spieler 4. **Überprüfen Sie den Wiedergabestatus**: Überprüfen Sie den Status vor dem Betrieb 5. **Ressourcenverwaltung**: Spieler freigeben, um Speicher freizugeben # @capgo/capacitor-volume-buttons > Erkennen und reagieren Sie auf Drücke der Lautstärketasten auf mobilen Geräten für benutzerdefinierte Steuerungen und Interaktionen. Hardware-Integration Lauschen Sie auf physische Lautstärketasten-Drücke Hoch/Runter-Erkennung Unterscheiden Sie zwischen Lautstärke hoch und runter Einfache API Einfache Event-Listener-Schnittstelle Umfassende Dokumentation Schauen Sie sich die [Dokumentation](/docs/plugins/volume-buttons/getting-started/) an, um mit der Verwendung von Lautstärketasten zu beginnen. # Erste Schritte > Erfahren Sie, wie Sie Lautstärketasten-Drücke in Ihrer Capacitor-App erkennen. 1. **Installieren Sie das Paket** * npm ```sh npm i @capgo/capacitor-volume-buttons ``` * pnpm ```sh pnpm add @capgo/capacitor-volume-buttons ``` * yarn ```sh yarn add @capgo/capacitor-volume-buttons ``` * bun ```sh bun add @capgo/capacitor-volume-buttons ``` 2. **Synchronisieren Sie mit nativen Projekten** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Verwendung [Section titled “Verwendung”](#verwendung) ```typescript import { VolumeButtons } from '@capgo/capacitor-volume-buttons'; // Listen for volume button presses VolumeButtons.addListener('volumeButtonPressed', (event) => { console.log('Volume button pressed:', event.direction); if (event.direction === 'up') { console.log('Volume up pressed'); // Handle volume up } else if (event.direction === 'down') { console.log('Volume down pressed'); // Handle volume down } }); // Remove all listeners when done await VolumeButtons.removeAllListeners(); ``` ## API-Referenz [Section titled “API-Referenz”](#api-referenz) ### addListener(‘volumeButtonPressed’, callback) [Section titled “addListener(‘volumeButtonPressed’, callback)”](#addlistenervolumebuttonpressed-callback) Lauschen Sie auf Lautstärketasten-Drücke. ```typescript const handle = VolumeButtons.addListener('volumeButtonPressed', (event) => { console.log('Direction:', event.direction); // 'up' or 'down' }); // Remove specific listener await handle.remove(); ``` **Event-Daten:** * `direction`: `'up' | 'down'` - Richtung des Tastendrucks ### removeAllListeners() [Section titled “removeAllListeners()”](#removealllisteners) Entfernen Sie alle registrierten Listener. ```typescript await VolumeButtons.removeAllListeners(); ``` ### getPluginVersion() [Section titled “getPluginVersion()”](#getpluginversion) Abrufen der Plugin-Version. ```typescript const { version } = await VolumeButtons.getPluginVersion(); console.log('Plugin version:', version); ``` ## Vollständiges Beispiel [Section titled “Vollständiges Beispiel”](#vollständiges-beispiel) ```typescript import { VolumeButtons } from '@capgo/capacitor-volume-buttons'; export class VolumeButtonController { private listener: any; async initialize() { this.listener = VolumeButtons.addListener( 'volumeButtonPressed', this.handleVolumeButton.bind(this) ); console.log('Volume button listener initialized'); } private handleVolumeButton(event: { direction: 'up' | 'down' }) { switch (event.direction) { case 'up': this.handleVolumeUp(); break; case 'down': this.handleVolumeDown(); break; } } private handleVolumeUp() { console.log('Volume up pressed'); // Your custom logic for volume up // e.g., navigate forward, increase brightness, etc. } private handleVolumeDown() { console.log('Volume down pressed'); // Your custom logic for volume down // e.g., navigate backward, decrease brightness, etc. } async cleanup() { if (this.listener) { await this.listener.remove(); } await VolumeButtons.removeAllListeners(); console.log('Volume button listener cleaned up'); } } ``` ## Anwendungsfälle [Section titled “Anwendungsfälle”](#anwendungsfälle) ### Foto-/Videoaufnahme [Section titled “Foto-/Videoaufnahme”](#foto-videoaufnahme) Verwenden Sie Lautstärketasten als Kamera-Auslöser: ```typescript VolumeButtons.addListener('volumeButtonPressed', async (event) => { // Any volume button triggers camera await capturePhoto(); }); ``` ### Seitennavigation [Section titled “Seitennavigation”](#seitennavigation) Navigieren Sie durch Inhalte mit Lautstärketasten: ```typescript let currentPage = 0; VolumeButtons.addListener('volumeButtonPressed', (event) => { if (event.direction === 'up') { currentPage++; showPage(currentPage); } else { currentPage = Math.max(0, currentPage - 1); showPage(currentPage); } }); ``` ### Mediensteuerung [Section titled “Mediensteuerung”](#mediensteuerung) Steuern Sie die Medienwiedergabe: ```typescript VolumeButtons.addListener('volumeButtonPressed', (event) => { if (event.direction === 'up') { skipForward(); } else { skipBackward(); } }); ``` ### Spielsteuerung [Section titled “Spielsteuerung”](#spielsteuerung) Verwenden Sie Lautstärketasten für Spielaktionen: ```typescript VolumeButtons.addListener('volumeButtonPressed', (event) => { if (event.direction === 'up') { player.jump(); } else { player.crouch(); } }); ``` ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) 1. **Räumen Sie Listener auf** Entfernen Sie immer Listener, wenn Ihre Komponente unmountiert wird: ```typescript useEffect(() => { const listener = VolumeButtons.addListener('volumeButtonPressed', handler); return () => { listener.remove(); }; }, []); ``` 2. **Geben Sie visuelles Feedback** Zeigen Sie Benutzern, dass Lautstärketasten für benutzerdefinierte Steuerungen verwendet werden: ```typescript VolumeButtons.addListener('volumeButtonPressed', (event) => { showNotification(`Volume ${event.direction} pressed`); performAction(event.direction); }); ``` 3. **Berücksichtigen Sie Barrierefreiheit** Denken Sie daran, dass einige Benutzer auf Lautstärketasten für die tatsächliche Lautstärkeregelung angewiesen sind. Bieten Sie alternative Steuerungen an. 4. **Entprellen Sie schnelle Drücke** Verhindern Sie versehentliches Doppeldrücken: ```typescript let lastPress = 0; const DEBOUNCE_MS = 300; VolumeButtons.addListener('volumeButtonPressed', (event) => { const now = Date.now(); if (now - lastPress < DEBOUNCE_MS) { return; // Ignore rapid presses } lastPress = now; handlePress(event.direction); }); ``` 5. **Dokumentieren Sie das Verhalten** Machen Sie den Benutzern klar, dass Lautstärketasten eine benutzerdefinierte Funktionalität haben: ```typescript function showVolumeButtonHelp() { alert('Use Volume Up/Down buttons to navigate pages'); } ``` ## Plattform-Hinweise [Section titled “Plattform-Hinweise”](#plattform-hinweise) ### iOS [Section titled “iOS”](#ios) * Funktioniert auf iOS 10.0+ * Lautstärketasten werden zuverlässig erkannt * System-Lautstärke-UI kann kurz erscheinen ### Android [Section titled “Android”](#android) * Funktioniert auf Android 5.0 (API 21)+ * Erfordert, dass die App im Vordergrund ist * Einige Geräte können eine leichte Verzögerung haben ### Web [Section titled “Web”](#web) * Auf der Web-Plattform nicht unterstützt * Listener werden nicht ausgelöst ## Fehlerbehebung [Section titled “Fehlerbehebung”](#fehlerbehebung) **Events werden nicht ausgelöst:** * Stellen Sie sicher, dass die App den Fokus hat * Überprüfen Sie, ob Listener ordnungsgemäß registriert sind * Überprüfen Sie, ob das Plugin installiert und synchronisiert ist **System-Lautstärkeänderungen stören:** * Dies ist erwartetes Verhalten - das Plugin erkennt Drücke, verhindert aber nicht System-Lautstärkeänderungen * Berücksichtigen Sie UX, die mit Lautstärkeänderungen funktioniert **Mehrere Events werden ausgelöst:** * Implementieren Sie Entprellen, um schnelle Drücke zu behandeln * Überprüfen Sie auf doppelte Listener-Registrierung # @capgo/capacitor-wechat > Integrieren Sie WeChat SDK in Ihre Capacitor-App für nahtlose Authentifizierung, Inhaltsfreigabe, Zahlungen und Mini-Programm-Zugriff. OAuth-Authentifizierung Aktivieren Sie sichere WeChat-Anmeldung für nahtlose Benutzerauthentifizierung 🔐 Inhaltsfreigabe Teilen Sie Text, Bilder, Links, Musik, Video und Mini-Programme auf WeChat 📤 WeChat Pay Akzeptieren Sie Zahlungen über WeChat Pay für nahtlose Transaktionen 💳 Mini-Programme Starten Sie WeChat Mini-Programme direkt von Ihrer App aus 🚀 Plattformübergreifend Funktioniert auf iOS und Android mit nativem WeChat SDK 📱 Umfassende Dokumentation Lesen Sie die [Dokumentation](/docs/plugins/wechat/getting-started/), um das Plugin in wenigen Minuten zu beherrschen. # Erste Schritte > Erfahren Sie, wie Sie das WeChat-Plugin installieren und verwenden, um die WeChat-Funktionalität in Ihre Capacitor-App zu integrieren. 1. **Paket installieren** * npm ```sh npm i @capgo/capacitor-wechat ``` * pnpm ```sh pnpm add @capgo/capacitor-wechat ``` * yarn ```sh yarn add @capgo/capacitor-wechat ``` * bun ```sh bun add @capgo/capacitor-wechat ``` 2. **Mit nativen Projekten synchronisieren** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Konfiguration [Section titled “Konfiguration”](#konfiguration) ### WeChat-App-Registrierung [Section titled “WeChat-App-Registrierung”](#wechat-app-registrierung) Bevor Sie dieses Plugin verwenden, müssen Sie Ihre App auf der \[WeChat Open Platform] () registrieren, um eine App-ID zu erhalten. ### iOS Konfiguration [Section titled “iOS Konfiguration”](#ios-konfiguration) Fügen Sie Folgendes zu Ihrem `Info.plist` hinzu: ```xml LSApplicationQueriesSchemes weixin weixinULAPI CFBundleURLTypes CFBundleTypeRole Editor CFBundleURLName weixin CFBundleURLSchemes YOUR_WECHAT_APP_ID ``` Sie müssen WeChat SDK in Ihr iOS-Projekt integrieren. Fügen Sie WeChat SDK zu Ihrem `Podfile` hinzu oder laden Sie es von der \[WeChat Open Platform] () herunter. ### Android Konfiguration [Section titled “Android Konfiguration”](#android-konfiguration) Fügen Sie Folgendes zu Ihrem `AndroidManifest.xml` hinzu: ```xml ``` Sie müssen WeChat SDK in Ihr Android-Projekt integrieren. Fügen Sie die WeChat-Abhängigkeit SDK zu Ihrem `build.gradle` hinzu oder laden Sie sie von der \[WeChat Open Platform] () herunter. ## Nutzung [Section titled “Nutzung”](#nutzung) ### Überprüfen Sie die WeChat-Installation [Section titled “Überprüfen Sie die WeChat-Installation”](#überprüfen-sie-die-wechat-installation) ```typescript import { CapacitorWechat } from '@capgo/capacitor-wechat'; const checkWeChat = async () => { const { installed } = await CapacitorWechat.isInstalled(); if (installed) { console.log('WeChat is installed'); } else { console.log('WeChat is not installed'); } }; ``` ### Authentifizierung [Section titled “Authentifizierung”](#authentifizierung) ```typescript const loginWithWeChat = async () => { try { const { code, state } = await CapacitorWechat.auth({ scope: 'snsapi_userinfo', // or 'snsapi_login' state: 'my_random_state' }); // Send code to your backend to exchange for access token const response = await fetch('https://yourapi.com/auth/wechat', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ code }) }); const { access_token, openid } = await response.json(); console.log('Logged in with WeChat:', openid); } catch (error) { console.error('WeChat auth failed:', error); } }; ``` ### Inhalte teilen [Section titled “Inhalte teilen”](#inhalte-teilen) #### Text teilen [Section titled “Text teilen”](#text-teilen) ```typescript const shareText = async () => { await CapacitorWechat.share({ scene: 0, // 0 = Chat, 1 = Moments, 2 = Favorite type: 'text', text: 'Hello from Capacitor WeChat!' }); }; ``` #### Link teilen [Section titled “Link teilen”](#link-teilen) ```typescript const shareLink = async () => { await CapacitorWechat.share({ scene: 1, // Share to Moments type: 'link', title: 'Check out this awesome app!', description: 'Built with Capacitor and WeChat SDK', link: 'https://capacitorjs.com', thumbUrl: 'https://capacitorjs.com/icon.png' }); }; ``` #### Bild teilen [Section titled “Bild teilen”](#bild-teilen) ```typescript const shareImage = async () => { await CapacitorWechat.share({ scene: 0, type: 'image', imageUrl: 'https://example.com/image.png', // or base64 data thumbUrl: 'https://example.com/thumb.png' }); }; ``` ### WeChat-Bezahlung [Section titled “WeChat-Bezahlung”](#wechat-bezahlung) ```typescript const payWithWeChat = async () => { // First, get payment parameters from your server const paymentParams = await fetchPaymentParamsFromServer(); try { await CapacitorWechat.sendPaymentRequest({ partnerId: paymentParams.partnerId, prepayId: paymentParams.prepayId, nonceStr: paymentParams.nonceStr, timeStamp: paymentParams.timeStamp, package: paymentParams.package, // Usually 'Sign=WXPay' sign: paymentParams.sign }); console.log('Payment successful!'); } catch (error) { console.error('Payment failed:', error); } }; ``` ### Öffnen Sie das Miniprogramm [Section titled “Öffnen Sie das Miniprogramm”](#öffnen-sie-das-miniprogramm) ```typescript const openMiniProgram = async () => { await CapacitorWechat.openMiniProgram({ username: 'gh_xxxxxxxxxxxxx', // Mini program original ID path: 'pages/index/index', // Path within mini program type: 0 // 0 = Release, 1 = Test, 2 = Preview }); }; ``` ## API Referenz [Section titled “API Referenz”](#api-referenz) ### isInstalled() [Section titled “isInstalled()”](#isinstalled) Überprüfen Sie, ob die WeChat-App auf dem Gerät installiert ist. ```typescript const result = await CapacitorWechat.isInstalled(); // Returns: { installed: boolean } ``` ### auth(Optionen) [Section titled “auth(Optionen)”](#authoptionen) Authentifizieren Sie den Benutzer mit WeChat OAuth. ```typescript interface WechatAuthOptions { scope: string; // 'snsapi_userinfo' or 'snsapi_login' state?: string; // Optional state parameter for CSRF protection } const result = await CapacitorWechat.auth(options); // Returns: { code: string, state?: string } ``` ### teilen(Optionen) [Section titled “teilen(Optionen)”](#teilenoptionen) Teilen Sie Inhalte mit WeChat. ```typescript interface WechatShareOptions { scene: number; // 0 = Session (chat), 1 = Timeline (moments), 2 = Favorite type: 'text' | 'image' | 'link' | 'music' | 'video' | 'miniprogram'; text?: string; // For type 'text' title?: string; // For type 'link', 'music', 'video', 'miniprogram' description?: string; // For type 'link', 'music', 'video', 'miniprogram' link?: string; // For type 'link' imageUrl?: string; // Image URL or base64 data thumbUrl?: string; // Thumbnail URL or base64 data mediaUrl?: string; // For type 'music' or 'video' miniProgramUsername?: string; // For type 'miniprogram' miniProgramPath?: string; // For type 'miniprogram' miniProgramType?: number; // For type 'miniprogram': 0 = Release, 1 = Test, 2 = Preview miniProgramWebPageUrl?: string; // Fallback URL for type 'miniprogram' } await CapacitorWechat.share(options); ``` ### sendPaymentRequest(Optionen) [Section titled “sendPaymentRequest(Optionen)”](#sendpaymentrequestoptionen) Senden Sie eine Zahlungsanfrage an WeChat Pay. ```typescript interface WechatPaymentOptions { partnerId: string; // Merchant ID prepayId: string; // Prepay ID from unified order API nonceStr: string; // Random string timeStamp: string; // Timestamp package: string; // Usually 'Sign=WXPay' sign: string; // Signature } await CapacitorWechat.sendPaymentRequest(options); ``` ### openMiniProgram(Optionen) [Section titled “openMiniProgram(Optionen)”](#openminiprogramoptionen) Öffnen Sie das WeChat-Miniprogramm. ```typescript interface WechatMiniProgramOptions { username: string; // Mini-program username (original ID) path?: string; // Path to open in mini-program type?: number; // 0 = Release, 1 = Test, 2 = Preview } await CapacitorWechat.openMiniProgram(options); ``` ### ChooseRechnung(Optionen) [Section titled “ChooseRechnung(Optionen)”](#chooserechnungoptionen) Wählen Sie Rechnung von WeChat (für Business-Apps). ```typescript interface WechatInvoiceOptions { appId: string; signType: string; cardSign: string; timeStamp: string; nonceStr: string; } const result = await CapacitorWechat.chooseInvoice(options); // Returns: { cards: string[] } ``` ### getPluginVersion() [Section titled “getPluginVersion()”](#getpluginversion) Holen Sie sich die native Plugin-Version. ```typescript const result = await CapacitorWechat.getPluginVersion(); // Returns: { version: string } ``` ## Vollständiges Beispiel [Section titled “Vollständiges Beispiel”](#vollständiges-beispiel) ```typescript import { CapacitorWechat } from '@capgo/capacitor-wechat'; export class WeChatService { async init() { const { installed } = await CapacitorWechat.isInstalled(); if (!installed) { throw new Error('WeChat is not installed'); } } async login() { const { code } = await CapacitorWechat.auth({ scope: 'snsapi_userinfo', state: Math.random().toString(36).substring(7) }); // Exchange code for access token on your backend const response = await fetch('/api/auth/wechat', { method: 'POST', body: JSON.stringify({ code }) }); return response.json(); } async shareToChat(title: string, description: string, link: string, imageUrl: string) { await CapacitorWechat.share({ scene: 0, // Chat type: 'link', title, description, link, thumbUrl: imageUrl }); } async shareToMoments(title: string, description: string, link: string, imageUrl: string) { await CapacitorWechat.share({ scene: 1, // Moments type: 'link', title, description, link, thumbUrl: imageUrl }); } } ``` ## Wichtige Hinweise [Section titled “Wichtige Hinweise”](#wichtige-hinweise) 1. **WeChat SDK-Integration**: Dieses Plugin stellt die Capacitor-Schnittstelle bereit, Sie müssen jedoch das offizielle WeChat SDK in Ihre nativen Projekte integrieren. 2. **App-Registrierung**: Sie müssen Ihre App auf der \[Offenen WeChat-Plattform] () registrieren, um eine App-ID zu erhalten. 3. **Universelle Links (iOS)**: Für iOS 13+ müssen Sie universelle Links für WeChat-Rückrufe konfigurieren. 4. **Backend-Integration**: Authentifizierungs- und Zahlungsfunktionen erfordern eine Backend-Integration, um Codes gegen Token auszutauschen und Zahlungsparameter vorzubereiten. 5. **Testen**: WeChat-Funktionen müssen auf physischen Geräten getestet werden – sie funktionieren nicht in Simulatoren. ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) 1. **Überprüfen Sie immer, ob WeChat installiert ist**, bevor Sie versuchen, Funktionen zu nutzen. 2. **Fehler elegant behandeln** mit Try-Catch-Blöcken. 3. **Statusparameter verwenden** in Authentifizierungsanfragen für den CSRF-Schutz. 4. **Überprüfen Sie Zahlungsantworten** in Ihrem Backend, bevor Sie Zugriff gewähren. 5. **Komprimieren Sie Bilder** vor dem Teilen, um den Datenverbrauch zu reduzieren. ## Plattformhinweise [Section titled “Plattformhinweise”](#plattformhinweise) ### iOS [Section titled “iOS”](#ios) * Erfordert iOS 10.0+ * Verwendet den offiziellen WeChat SDK für iOS * Erfordert Universal Links-Konfiguration für iOS 13+### Android * Erfordert Android 5.0 (API 21)+ * Verwendet den offiziellen WeChat SDK für Android – Erfordert die WXEntryActivity-Konfiguration ### Web [Section titled “Web”](#web) – Wird auf der Webplattform nicht unterstützt # @capgo/capacitor-wifi > Verwalten Sie WiFi-Verbindungen, scannen Sie Netzwerke und überwachen Sie den Konnektivitätsstatus in Ihrer Capacitor-App. Netzwerk-Scanning Scannen und entdecken Sie verfügbare WiFi-Netzwerke 📡 Verbindungsverwaltung Verbinden und trennen Sie WiFi-Netzwerke programmatisch 🔌 Statusüberwachung Überwachen Sie den WiFi-Verbindungsstatus in Echtzeit 📊 Plattformübergreifend Funktioniert auf iOS- und Android-Geräten 📱 Netzwerkinformationen Erhalten Sie detaillierte Informationen über verbundene Netzwerke 🔍 Umfassende Dokumentation Schauen Sie sich die [Dokumentation](/docs/plugins/wifi/getting-started/) an, um das Plugin in nur wenigen Minuten zu meistern. # Erste Schritte > Erfahren Sie, wie Sie das WiFi-Plugin installieren und verwenden, um die Netzwerkkonnektivität in Ihrer Capacitor-App zu verwalten. 1. **Installieren Sie das Paket** * npm ```sh npm i @capgo/capacitor-wifi ``` * pnpm ```sh pnpm add @capgo/capacitor-wifi ``` * yarn ```sh yarn add @capgo/capacitor-wifi ``` * bun ```sh bun add @capgo/capacitor-wifi ``` 2. **Mit nativen Projekten synchronisieren** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` 3. **Berechtigungen konfigurieren** **Android**: Fügen Sie zu `AndroidManifest.xml` hinzu: ```xml ``` **iOS**: Fügen Sie zu `Info.plist` hinzu: ```xml NSLocationWhenInUseUsageDescription Wir benötigen Standortzugriff zum Scannen von WiFi-Netzwerken ``` ## Verwendung [Section titled “Verwendung”](#verwendung) Importieren Sie das Plugin und verwalten Sie die WiFi-Konnektivität: ```typescript import { CapacitorWifi } from '@capgo/capacitor-wifi'; // Aktuelle WiFi-Verbindungsinformationen abrufen const getWifiInfo = async () => { const info = await CapacitorWifi.getWifiInfo(); console.log('Verbunden mit:', info.ssid); console.log('IP-Adresse:', info.ip); }; // Nach verfügbaren Netzwerken scannen const scanNetworks = async () => { const { networks } = await CapacitorWifi.scan(); networks.forEach(network => { console.log(`SSID: ${network.ssid}, Signal: ${network.level}`); }); }; // Mit einem Netzwerk verbinden const connectToWifi = async () => { await CapacitorWifi.connect({ ssid: 'MeinNetzwerk', password: 'meinpasswort' }); }; ``` ## API-Referenz [Section titled “API-Referenz”](#api-referenz) ### getWifiInfo() [Section titled “getWifiInfo()”](#getwifiinfo) Ruft Informationen über das aktuell verbundene WiFi-Netzwerk ab. ```typescript interface WifiInfo { ssid: string; // Netzwerkname bssid: string; // MAC-Adresse des Access Points ip: string; // Geräte-IP-Adresse frequency: number; // Netzwerkfrequenz (MHz) linkSpeed: number; // Verbindungsgeschwindigkeit (Mbps) signalStrength: number; // Signalstärke (0-100) } const info = await CapacitorWifi.getWifiInfo(); ``` ### scan() [Section titled “scan()”](#scan) Scannt nach verfügbaren WiFi-Netzwerken. ```typescript interface WifiNetwork { ssid: string; // Netzwerkname bssid: string; // MAC-Adresse level: number; // Signalpegel (dBm) frequency: number; // Netzwerkfrequenz capabilities: string; // Sicherheitsfähigkeiten } const { networks } = await CapacitorWifi.scan(); ``` ### connect(options) [Section titled “connect(options)”](#connectoptions) Verbindet sich mit einem WiFi-Netzwerk. ```typescript interface ConnectOptions { ssid: string; // Netzwerkname password?: string; // Netzwerkpasswort (falls gesichert) isHiddenSsid?: boolean; // Ob SSID versteckt ist } await CapacitorWifi.connect({ ssid: 'MeinNetzwerk', password: 'meinpasswort' }); ``` ### disconnect() [Section titled “disconnect()”](#disconnect) Trennt die Verbindung vom aktuellen WiFi-Netzwerk. ```typescript await CapacitorWifi.disconnect(); ``` ### getSSID() [Section titled “getSSID()”](#getssid) Ruft die SSID des aktuell verbundenen Netzwerks ab. ```typescript const { ssid } = await CapacitorWifi.getSSID(); console.log('Verbunden mit:', ssid); ``` ### getIP() [Section titled “getIP()”](#getip) Ruft die aktuelle Geräte-IP-Adresse ab. ```typescript const { ip } = await CapacitorWifi.getIP(); console.log('IP-Adresse:', ip); ``` ## Vollständiges Beispiel [Section titled “Vollständiges Beispiel”](#vollständiges-beispiel) ```typescript import { CapacitorWifi } from '@capgo/capacitor-wifi'; export class WifiService { async getCurrentNetwork() { try { const info = await CapacitorWifi.getWifiInfo(); return { name: info.ssid, strength: this.getSignalQuality(info.signalStrength), speed: `${info.linkSpeed} Mbps`, ip: info.ip }; } catch (error) { console.error('WiFi-Informationen konnten nicht abgerufen werden:', error); return null; } } async scanAndConnect(targetSsid: string, password: string) { try { // Nach Netzwerken scannen const { networks } = await CapacitorWifi.scan(); // Zielnetzwerk finden const targetNetwork = networks.find(n => n.ssid === targetSsid); if (!targetNetwork) { throw new Error(`Netzwerk ${targetSsid} nicht gefunden`); } console.log(`Netzwerk mit Signal gefunden: ${targetNetwork.level} dBm`); // Mit Netzwerk verbinden await CapacitorWifi.connect({ ssid: targetSsid, password: password }); console.log('Erfolgreich verbunden!'); return true; } catch (error) { console.error('Verbindung fehlgeschlagen:', error); return false; } } async findBestNetwork(preferredNetworks: string[]) { const { networks } = await CapacitorWifi.scan(); // Auf bevorzugte Netzwerke filtern const available = networks.filter(n => preferredNetworks.includes(n.ssid) ); if (available.length === 0) { return null; } // Nach Signalstärke sortieren available.sort((a, b) => b.level - a.level); return available[0]; } async monitorConnection(callback: (info: WifiInfo | null) => void) { const checkConnection = async () => { try { const info = await CapacitorWifi.getWifiInfo(); callback(info); } catch (error) { callback(null); } }; // Alle 5 Sekunden überprüfen const interval = setInterval(checkConnection, 5000); // Erste Überprüfung await checkConnection(); // Aufräumfunktion zurückgeben return () => clearInterval(interval); } private getSignalQuality(strength: number): string { if (strength >= 80) return 'Ausgezeichnet'; if (strength >= 60) return 'Gut'; if (strength >= 40) return 'Akzeptabel'; return 'Schwach'; } async getNetworkSecurity(ssid: string): Promise { const { networks } = await CapacitorWifi.scan(); const network = networks.find(n => n.ssid === ssid); if (!network) { return 'Unbekannt'; } const caps = network.capabilities.toLowerCase(); if (caps.includes('wpa3')) return 'WPA3'; if (caps.includes('wpa2')) return 'WPA2'; if (caps.includes('wpa')) return 'WPA'; if (caps.includes('wep')) return 'WEP'; return 'Offen'; } } ``` ## Erweiterte Verwendung [Section titled “Erweiterte Verwendung”](#erweiterte-verwendung) ### Netzwerkqualitätsbewertung [Section titled “Netzwerkqualitätsbewertung”](#netzwerkqualitätsbewertung) ```typescript const assessNetworkQuality = async () => { const info = await CapacitorWifi.getWifiInfo(); const quality = { signal: info.signalStrength >= 70 ? 'Ausgezeichnet' : info.signalStrength >= 50 ? 'Gut' : info.signalStrength >= 30 ? 'Akzeptabel' : 'Schwach', speed: info.linkSpeed >= 100 ? 'Schnell' : info.linkSpeed >= 50 ? 'Mittel' : 'Langsam', frequency: info.frequency >= 5000 ? '5GHz' : '2,4GHz' }; console.log('Netzwerkqualität:', quality); return quality; }; ``` ### Automatisches Verbinden mit bevorzugten Netzwerken [Section titled “Automatisches Verbinden mit bevorzugten Netzwerken”](#automatisches-verbinden-mit-bevorzugten-netzwerken) ```typescript const autoConnect = async (preferredNetworks: Array<{ ssid: string, password: string }>) => { const { networks } = await CapacitorWifi.scan(); for (const preferred of preferredNetworks) { const found = networks.find(n => n.ssid === preferred.ssid); if (found) { try { await CapacitorWifi.connect({ ssid: preferred.ssid, password: preferred.password }); console.log(`Verbunden mit ${preferred.ssid}`); return true; } catch (error) { console.error(`Verbindung mit ${preferred.ssid} fehlgeschlagen`); } } } return false; }; ``` ### Netzwerkwechselerkennung [Section titled “Netzwerkwechselerkennung”](#netzwerkwechselerkennung) ```typescript class NetworkMonitor { private currentSsid: string | null = null; private listeners: Array<(ssid: string | null) => void> = []; async start() { setInterval(async () => { try { const { ssid } = await CapacitorWifi.getSSID(); if (ssid !== this.currentSsid) { this.currentSsid = ssid; this.notifyListeners(ssid); } } catch (error) { if (this.currentSsid !== null) { this.currentSsid = null; this.notifyListeners(null); } } }, 3000); } onNetworkChange(callback: (ssid: string | null) => void) { this.listeners.push(callback); } private notifyListeners(ssid: string | null) { this.listeners.forEach(listener => listener(ssid)); } } ``` ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) 1. **Berechtigungen**: Fordern Sie Standortberechtigungen an, bevor Sie Netzwerke scannen 2. **Fehlerbehandlung**: Wickeln Sie WiFi-Operationen immer in try-catch-Blöcke ein 3. **Benutzer-Feedback**: Zeigen Sie Ladeindikatoren während Netzwerkoperationen an 4. **Sicherheit**: Speichern Sie WiFi-Passwörter niemals im Klartext 5. **Testen**: Testen Sie auf echten Geräten, da WiFi-APIs möglicherweise nicht in Emulatoren funktionieren ## Plattformunterschiede [Section titled “Plattformunterschiede”](#plattformunterschiede) ### iOS [Section titled “iOS”](#ios) * Erfordert Standortberechtigungen zum Scannen von Netzwerken * Kann nicht programmatisch mit Netzwerken verbinden (öffnet Einstellungen) * Eingeschränkter Zugriff auf Netzwerkdetails ### Android [Section titled “Android”](#android) * Vollständige programmatische WiFi-Kontrolle * Erfordert Standortberechtigungen zum Scannen von Netzwerken * Kann programmatisch verbinden/trennen ## Fehlerbehebung [Section titled “Fehlerbehebung”](#fehlerbehebung) ### Häufige Probleme [Section titled “Häufige Probleme”](#häufige-probleme) **Scan gibt leer zurück**: Überprüfen Sie, ob Standortberechtigungen erteilt wurden **Kann nicht mit Netzwerk verbinden**: Überprüfen Sie, ob das Passwort korrekt ist und das Netzwerk in Reichweite ist **getWifiInfo schlägt fehl**: Stellen Sie sicher, dass das Gerät mit einem WiFi-Netzwerk verbunden ist **Berechtigung verweigert**: Fügen Sie erforderliche Berechtigungen zu Plattformkonfigurationsdateien hinzu # @capgo/capacitor-youtube-player > Integrieren Sie YouTube-Videowiedergabe mit voller Kontrolle über Player-Status, Playlists, Qualität und Events in Ihre Capacitor-Anwendungen. Vollständige YouTube-API Vollständiger Zugriff auf die YouTube Player API Playlist-Unterstützung Laden und steuern Sie YouTube-Playlists Qualitätskontrolle Passen Sie die Wiedergabequalität dynamisch an Event-Listener Reagieren Sie auf Player-Statusänderungen und Events # Erste Schritte > Erfahren Sie, wie Sie YouTube-Videowiedergabe in Ihre Capacitor-App integrieren. 1. **Installieren Sie das Paket** * npm ```sh npm i @capgo/capacitor-youtube-player ``` * pnpm ```sh pnpm add @capgo/capacitor-youtube-player ``` * yarn ```sh yarn add @capgo/capacitor-youtube-player ``` * bun ```sh bun add @capgo/capacitor-youtube-player ``` 2. **Synchronisieren Sie mit nativen Projekten** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Grundlegende Verwendung [Section titled “Grundlegende Verwendung”](#grundlegende-verwendung) ```typescript import { YoutubePlayer } from '@capgo/capacitor-youtube-player'; // Initialize the player const result = await YoutubePlayer.initialize({ playerId: 'youtube-player', playerSize: { width: 640, height: 360 }, videoId: 'dQw4w9WgXcQ', fullscreen: false, playerVars: { autoplay: 1, controls: 1 } }); // Play video await YoutubePlayer.playVideo(result.player); // Pause video await YoutubePlayer.pauseVideo(result.player); // Listen to player events YoutubePlayer.addEventListener(result.player, 'onStateChange', (event) => { console.log('Player state:', event.data); }); ``` ## API Reference [Section titled “API Reference”](#api-reference) ### initialize(options) [Section titled “initialize(options)”](#initializeoptions) Initialize a YouTube player instance. ```typescript const { player, playerReady } = await YoutubePlayer.initialize({ playerId: 'my-player', playerSize: { width: 1280, height: 720 }, videoId: 'VIDEO_ID', fullscreen: false, playerVars: { autoplay: 0, controls: 1, rel: 0 } }); ``` ### Video Control Methods [Section titled “Video Control Methods”](#video-control-methods) ```typescript // Play await YoutubePlayer.playVideo(playerId); // Pause await YoutubePlayer.pauseVideo(playerId); // Stop await YoutubePlayer.stopVideo(playerId); // Seek to time await YoutubePlayer.seekTo(playerId, seconds, allowSeekAhead); // Load video by ID await YoutubePlayer.loadVideoById(playerId, { videoId: 'VIDEO_ID' }); // Cue video (load without playing) await YoutubePlayer.cueVideoById(playerId, { videoId: 'VIDEO_ID' }); ``` ### Playlist Control [Section titled “Playlist Control”](#playlist-control) ```typescript // Load playlist await YoutubePlayer.loadPlaylist(playerId, { listType: 'playlist', list: 'PLAYLIST_ID' }); // Cue playlist await YoutubePlayer.cuePlaylist(playerId, { playlist: ['VIDEO_ID_1', 'VIDEO_ID_2'], index: 0 }); // Navigate playlist await YoutubePlayer.nextVideo(playerId); await YoutubePlayer.previousVideo(playerId); await YoutubePlayer.playVideoAt(playerId, index); ``` ### Audio Control [Section titled “Audio Control”](#audio-control) ```typescript // Mute/Unmute await YoutubePlayer.mute(playerId); await YoutubePlayer.unMute(playerId); const { result } = await YoutubePlayer.isMuted(playerId); console.log('Muted:', result.value); // Volume (0-100) await YoutubePlayer.setVolume(playerId, 50); const { result } = await YoutubePlayer.getVolume(playerId); ``` ### Playback Control [Section titled “Playback Control”](#playback-control) ```typescript // Playback rate await YoutubePlayer.setPlaybackRate(playerId, 1.5); const { result } = await YoutubePlayer.getPlaybackRate(playerId); // Available rates const { result } = await YoutubePlayer.getAvailablePlaybackRates(playerId); console.log('Available rates:', result.value); // Loop and shuffle await YoutubePlayer.setLoop(playerId, true); await YoutubePlayer.setShuffle(playerId, true); ``` ### Quality Control [Section titled “Quality Control”](#quality-control) ```typescript // Set quality await YoutubePlayer.setPlaybackQuality(playerId, 'hd720'); // Get current quality const { result } = await YoutubePlayer.getPlaybackQuality(playerId); // Get available qualities const { result } = await YoutubePlayer.getAvailableQualityLevels(playerId); ``` ### Player Information [Section titled “Player Information”](#player-information) ```typescript // Duration const { result } = await YoutubePlayer.getDuration(playerId); // Current time const { result } = await YoutubePlayer.getCurrentTime(playerId); // Loaded fraction const { result } = await YoutubePlayer.getVideoLoadedFraction(playerId); // Player state const { result } = await YoutubePlayer.getPlayerState(playerId); // Video URL const { result } = await YoutubePlayer.getVideoUrl(playerId); ``` ### Event Listeners [Section titled “Event Listeners”](#event-listeners) ```typescript // State change YoutubePlayer.addEventListener(playerId, 'onStateChange', (event) => { console.log('State:', event.data); // -1, 0, 1, 2, 3, 5 }); // Ready YoutubePlayer.addEventListener(playerId, 'onReady', (event) => { console.log('Player ready'); }); // Error YoutubePlayer.addEventListener(playerId, 'onError', (event) => { console.error('Player error:', event.data); }); // Quality change YoutubePlayer.addEventListener(playerId, 'onPlaybackQualityChange', (event) => { console.log('Quality:', event.data); }); // Rate change YoutubePlayer.addEventListener(playerId, 'onPlaybackRateChange', (event) => { console.log('Rate:', event.data); }); // Remove listener YoutubePlayer.removeEventListener(playerId, 'onStateChange', callback); ``` ## Complete Example [Section titled “Complete Example”](#complete-example) ```typescript import { YoutubePlayer } from '@capgo/capacitor-youtube-player'; export class YouTubeService { private playerId: string | null = null; async initPlayer(videoId: string) { try { const { player, playerReady } = await YoutubePlayer.initialize({ playerId: 'main-player', playerSize: { width: 1280, height: 720 }, videoId, fullscreen: false, playerVars: { autoplay: 0, controls: 1, modestbranding: 1, rel: 0 } }); this.playerId = player; // Set up event listeners YoutubePlayer.addEventListener(player, 'onReady', () => { console.log('Player is ready'); }); YoutubePlayer.addEventListener(player, 'onStateChange', (event) => { this.handleStateChange(event.data); }); YoutubePlayer.addEventListener(player, 'onError', (event) => { console.error('YouTube error:', event.data); }); return player; } catch (error) { console.error('Failed to initialize player:', error); throw error; } } private handleStateChange(state: number) { switch (state) { case -1: console.log('Unstarted'); break; case 0: console.log('Ended'); break; case 1: console.log('Playing'); break; case 2: console.log('Paused'); break; case 3: console.log('Buffering'); break; case 5: console.log('Video cued'); break; } } async play() { if (!this.playerId) return; await YoutubePlayer.playVideo(this.playerId); } async pause() { if (!this.playerId) return; await YoutubePlayer.pauseVideo(this.playerId); } async loadVideo(videoId: string) { if (!this.playerId) return; await YoutubePlayer.loadVideoById(this.playerId, { videoId }); } async loadPlaylist(playlistId: string) { if (!this.playerId) return; await YoutubePlayer.loadPlaylist(this.playerId, { listType: 'playlist', list: playlistId, index: 0 }); } async setQuality(quality: 'small' | 'medium' | 'large' | 'hd720' | 'hd1080') { if (!this.playerId) return; await YoutubePlayer.setPlaybackQuality(this.playerId, quality); } async getProgress() { if (!this.playerId) return { current: 0, duration: 0 }; const current = await YoutubePlayer.getCurrentTime(this.playerId); const duration = await YoutubePlayer.getDuration(this.playerId); return { current: current.result.value, duration: duration.result.value }; } async destroy() { if (!this.playerId) return; await YoutubePlayer.destroy(this.playerId); this.playerId = null; } } ``` ## Player States [Section titled “Player States”](#player-states) | State | Value | Description | | --------- | ----- | ------------------ | | UNSTARTED | -1 | Video not started | | ENDED | 0 | Video has ended | | PLAYING | 1 | Video is playing | | PAUSED | 2 | Video is paused | | BUFFERING | 3 | Video is buffering | | CUED | 5 | Video is cued | ## Quality Levels [Section titled “Quality Levels”](#quality-levels) * `small`: 240p * `medium`: 360p * `large`: 480p * `hd720`: 720p * `hd1080`: 1080p * `highres`: >1080p * `default`: Auto quality ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) 1. **Initialize once** Create player instance once and reuse it: ```typescript let player = await YoutubePlayer.initialize(options); // Reuse player for different videos await YoutubePlayer.loadVideoById(player, { videoId: 'NEW_ID' }); ``` 2. **Handle errors gracefully** ```typescript YoutubePlayer.addEventListener(playerId, 'onError', (event) => { switch (event.data) { case 2: console.error('Invalid parameter'); break; case 100: console.error('Video not found'); break; case 101: case 150: console.error('Embedding not allowed'); break; } }); ``` 3. **Clean up** Destroy player when component unmounts: ```typescript useEffect(() => { return () => { if (playerId) { YoutubePlayer.destroy(playerId); } }; }, []); ``` 4. **Respect user preferences** ```typescript // Don't autoplay unless user initiated playerVars: { autoplay: 0 } ``` 5. **Monitor playback** ```typescript setInterval(async () => { const time = await YoutubePlayer.getCurrentTime(playerId); updateProgressBar(time.result.value); }, 1000); ``` ## Platform Notes [Section titled “Platform Notes”](#platform-notes) ### iOS [Section titled “iOS”](#ios) * Works on iOS 9.0+ * Uses WKWebView with YouTube iframe API * Fullscreen supported ### Android [Section titled “Android”](#android) * Works on Android 5.0 (API 21)+ * Uses WebView with YouTube iframe API * Fullscreen supported ### Web [Section titled “Web”](#web) * Direct YouTube iframe integration * Full API support * Best performance ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) **Player not loading:** * Check video ID is valid * Verify internet connection * Ensure YouTube embedding is allowed for the video **Events not firing:** * Make sure listeners are added after initialization * Check player is ready before adding listeners **Quality issues:** * Check available quality levels first * Some videos don’t support all qualities * Quality changes may not be immediate # @capgo/capacitor-zip > Komprimieren und extrahieren Sie ZIP-Archive mühelos auf iOS-, Android- und Web-Plattformen. Zip & Entpacken Erstellen und extrahieren Sie ZIP-Archive ganz einfach 📦 Plattformübergreifend Funktioniert nahtlos auf iOS, Android und Web 📱 Einfache API Benutzerfreundliche Schnittstelle für Komprimierungsvorgänge 🚀 Kostenlos & Open Source Völlig kostenlos ohne Einschränkungen 💰 Fortschrittsverfolgung Überwachen Sie Komprimierungs- und Extraktionsfortschritt ⚙️ Umfassende Dokumentation Lesen Sie die [Dokumentation](/docs/plugins/zip/getting-started/), um das Plugin in wenigen Minuten zu beherrschen. # Erste Schritte > Erfahren Sie, wie Sie das Zip-Plugin installieren und verwenden, um Dateien in Ihrer Capacitor-App zu komprimieren und zu extrahieren. 1. **Paket installieren** * npm ```sh npm i @capgo/capacitor-zip ``` * pnpm ```sh pnpm add @capgo/capacitor-zip ``` * yarn ```sh yarn add @capgo/capacitor-zip ``` * bun ```sh bun add @capgo/capacitor-zip ``` 2. **Mit nativen Projekten synchronisieren** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Nutzung [Section titled “Nutzung”](#nutzung) Importieren Sie das Plugin und komprimieren oder entpacken Sie die Dateien: ```typescript import { CapacitorZip } from '@capgo/capacitor-zip'; // Zip a folder const zipFolder = async () => { await CapacitorZip.zip({ source: 'file:///path/to/folder', destination: 'file:///path/to/archive.zip' }); console.log('Folder zipped successfully!'); }; // Unzip an archive const unzipArchive = async () => { await CapacitorZip.unzip({ source: 'file:///path/to/archive.zip', destination: 'file:///path/to/output/folder' }); console.log('Archive extracted successfully!'); }; ``` ## API Referenz [Section titled “API Referenz”](#api-referenz) ### zip(Optionen) [Section titled “zip(Optionen)”](#zipoptionen) Erstellt ein ZIP-Archiv aus einem Quellordner. ```typescript interface ZipOptions { source: string; // Path to folder to compress destination: string; // Path for output ZIP file } await CapacitorZip.zip({ source: 'file:///path/to/folder', destination: 'file:///path/to/archive.zip' }); ``` ### entpacken(Optionen) [Section titled “entpacken(Optionen)”](#entpackenoptionen) Extrahiert Dateien aus einem ZIP-Archiv. ```typescript interface UnzipOptions { source: string; // Path to ZIP file destination: string; // Path to extract to } await CapacitorZip.unzip({ source: 'file:///path/to/archive.zip', destination: 'file:///path/to/output/folder' }); ``` ## Vollständiges Beispiel [Section titled “Vollständiges Beispiel”](#vollständiges-beispiel) ```typescript import { CapacitorZip } from '@capgo/capacitor-zip'; import { Filesystem, Directory } from '@capacitor/filesystem'; export class ZipService { async createBackup() { try { const timestamp = Date.now(); const sourceDir = `${Filesystem.Directory.Data}/userdata`; const destPath = `${Filesystem.Directory.Documents}/backup_${timestamp}.zip`; await CapacitorZip.zip({ source: sourceDir, destination: destPath }); console.log('Backup created:', destPath); return destPath; } catch (error) { console.error('Backup failed:', error); throw error; } } async restoreBackup(backupPath: string) { try { const destDir = `${Filesystem.Directory.Data}/userdata`; // Clear existing data await Filesystem.rmdir({ path: 'userdata', directory: Directory.Data, recursive: true }); // Extract backup await CapacitorZip.unzip({ source: backupPath, destination: destDir }); console.log('Backup restored successfully'); } catch (error) { console.error('Restore failed:', error); throw error; } } async compressFiles(filePaths: string[], outputPath: string) { try { // Create temporary directory const tempDir = `${Filesystem.Directory.Cache}/temp_zip_${Date.now()}`; await Filesystem.mkdir({ path: tempDir, directory: Directory.Cache, recursive: true }); // Copy files to temp directory for (const filePath of filePaths) { const fileName = filePath.split('/').pop(); await Filesystem.copy({ from: filePath, to: `${tempDir}/${fileName}` }); } // Zip the temp directory await CapacitorZip.zip({ source: tempDir, destination: outputPath }); // Clean up temp directory await Filesystem.rmdir({ path: tempDir, directory: Directory.Cache, recursive: true }); return outputPath; } catch (error) { console.error('Compression failed:', error); throw error; } } async extractSpecificFiles(zipPath: string, fileNames: string[]) { try { // Extract to temp location const tempDir = `${Filesystem.Directory.Cache}/temp_extract_${Date.now()}`; await CapacitorZip.unzip({ source: zipPath, destination: tempDir }); // Read only specific files const extractedFiles: { [key: string]: string } = {}; for (const fileName of fileNames) { const content = await Filesystem.readFile({ path: `${tempDir}/${fileName}`, directory: Directory.Cache }); extractedFiles[fileName] = content.data; } // Clean up await Filesystem.rmdir({ path: tempDir, directory: Directory.Cache, recursive: true }); return extractedFiles; } catch (error) { console.error('Extraction failed:', error); throw error; } } } ``` ## Erweiterte Nutzung [Section titled “Erweiterte Nutzung”](#erweiterte-nutzung) ### Erstellen von Archiven mit Fortschrittsverfolgung [Section titled “Erstellen von Archiven mit Fortschrittsverfolgung”](#erstellen-von-archiven-mit-fortschrittsverfolgung) ```typescript const zipWithProgress = async (source: string, destination: string) => { console.log('Starting compression...'); try { await CapacitorZip.zip({ source, destination }); console.log('Compression complete!'); } catch (error) { console.error('Compression failed:', error); throw error; } }; ``` ### Batch-Archivierungsvorgänge [Section titled “Batch-Archivierungsvorgänge”](#batch-archivierungsvorgänge) ```typescript const batchZip = async (folders: string[]) => { const results = []; for (const folder of folders) { const folderName = folder.split('/').pop(); const zipPath = `${folder}_${Date.now()}.zip`; try { await CapacitorZip.zip({ source: folder, destination: zipPath }); results.push({ folder, zipPath, success: true }); } catch (error) { results.push({ folder, error, success: false }); } } return results; }; ``` ### Sichere Archiverstellung [Section titled “Sichere Archiverstellung”](#sichere-archiverstellung) ```typescript const createSecureBackup = async (dataPath: string) => { // Zip the data const zipPath = `${Filesystem.Directory.Documents}/secure_backup.zip`; await CapacitorZip.zip({ source: dataPath, destination: zipPath }); // Optionally encrypt the zip file here using a crypto plugin return zipPath; }; ``` ### Archivvalidierung [Section titled “Archivvalidierung”](#archivvalidierung) ```typescript const validateArchive = async (zipPath: string): Promise => { try { const tempDir = `${Filesystem.Directory.Cache}/validate_${Date.now()}`; // Try to extract await CapacitorZip.unzip({ source: zipPath, destination: tempDir }); // Clean up await Filesystem.rmdir({ path: tempDir, directory: Directory.Cache, recursive: true }); return true; } catch (error) { console.error('Archive validation failed:', error); return false; } }; ``` ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) 1. **Pfadvalidierung**: Validieren Sie immer Quell- und Zielpfade vor Vorgängen 2. **Fehlerbehandlung**: Wickeln Sie alle Zip-/Unzip-Vorgänge in Try-Catch-Blöcke ein 3. **Bereinigung**: Entfernen Sie temporäre Dateien und Ordner nach den Vorgängen 4. **Große Dateien**: Seien Sie vorsichtig bei großen Archiven auf Mobilgeräten 5. **Berechtigungen**: Stellen Sie sicher, dass Ihre App über die erforderlichen Dateisystemberechtigungen verfügt ## Fehlerbehebung [Section titled “Fehlerbehebung”](#fehlerbehebung) ### Häufige Probleme [Section titled “Häufige Probleme”](#häufige-probleme) **Zip-Vorgang schlägt fehl**: Stellen Sie sicher, dass der Quellpfad vorhanden und zugänglich ist **Entpacken schlägt fehl**: Überprüfen Sie, ob die ZIP-Datei gültig ist und der Zielpfad beschreibbar ist **Nicht genügend Speicherplatz**: Überwachen Sie den verfügbaren Speicherplatz vor dem Betrieb **Berechtigung verweigert**: Überprüfen Sie die Dateisystemberechtigungen in Ihrer App-Konfiguration # API-Übersicht > Erkunden Sie Capgos öffentliche API zur Verwaltung von Ressourcen wie Organisationen, Geräten, Kanälen und Bundles mit RESTful-HTTP-Methoden und Authentifizierung. Dies ist die Dokumentation der öffentlichen API von Capgo Cloud. Die API ermöglicht es Ihnen, Ihre Capgo-Ressourcen programmatisch zu verwalten, einschließlich Organisationen, Geräten, Kanälen und Bundles. Sie ist RESTful gestaltet und verwendet Standard-HTTP-Methoden. ## Authentifizierung [Section titled “Authentifizierung”](#authentifizierung) Alle API-Endpunkte erfordern eine Authentifizierung. Um Ihre Anfragen zu authentifizieren, fügen Sie Ihren API-Schlüssel im `authorization`-Header hinzu. Beispiel: ```bash curl -H "authorization: your-api-key" https://api.capgo.app/organization/ ``` [API-Schlüssel erhalten ](https://console.capgo.app/dashboard/apikeys/)Generieren Sie Ihren API-Schlüssel im Capgo-Dashboard ## Ratenbegrenzung [Section titled “Ratenbegrenzung”](#ratenbegrenzung) Die API implementiert Ratenbegrenzungen, um eine faire Nutzung zu gewährleisten. Aktuelle Limits sind: * 100 Anfragen pro Minute für Standardkonten * 1000 Anfragen pro Minute für Unternehmenskonten Wenn Sie diese Limits überschreiten, erhalten Sie eine 429 (Too Many Requests)-Antwort. ## Antwortformat [Section titled “Antwortformat”](#antwortformat) Alle Antworten sind im JSON-Format. Erfolgreiche Antworten enthalten normalerweise entweder ein `data`-Objekt oder ein `status`-Feld. Fehlerantworten enthalten ein `error`-Feld mit einer Beschreibung des Problems. Beispiel erfolgreiche Antwort: ```json { "status": "ok", "data": { ... } } ``` Beispiel Fehlerantwort: ```json { "error": "Invalid API key", "status": "KO" } ``` ## Verfügbare Endpunkte [Section titled “Verfügbare Endpunkte”](#verfügbare-endpunkte) [Organisationen ](/docs/public-api/organizations/)Erstellen und verwalten Sie Organisationen, aktualisieren Sie Einstellungen und handhaben Sie Konfigurationen auf Organisationsebene [API-Schlüssel ](/docs/public-api/api-keys/)Generieren, listen und widerrufen Sie API-Schlüssel für sicheren Zugriff auf die Capgo-API [Mitglieder ](/docs/public-api/members/)Verwalten Sie Organisationsmitglieder, Rollen und Berechtigungen [Statistiken ](/docs/public-api/statistics/)Greifen Sie auf detaillierte Analysen zur App-Nutzung, Speicherung und Bandbreitenverbrauch zu [Kanäle ](/docs/public-api/channels/)Steuern Sie App-Update-Kanäle, Versionen und Update-Richtlinien [Geräte ](/docs/public-api/devices/)Verfolgen und verwalten Sie Geräte, auf denen Ihre App läuft, einschließlich Versions- und Kanalzuweisungen [Bundles ](/docs/public-api/bundles/)Verwalten Sie App-Bundles, einschließlich Hochladen, Auflisten und Verwalten von Versionen ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) 1. **Fehlerbehandlung**: Prüfen Sie immer auf Fehlerantworten und behandeln Sie diese angemessen 2. **Ratenbegrenzung**: Implementieren Sie exponentielles Backoff bei Erreichen von Ratenlimits 3. **Caching**: Cachen Sie Antworten, wenn angemessen, um API-Aufrufe zu reduzieren 4. **Versionierung**: Verfolgen Sie API-Änderungen über unser Changelog # API-Schlüssel > Umfassende Dokumentation für den API-Schlüssel-Endpunkt mit Details zu Authentifizierung, Berechtigungen und Verwaltung innerhalb der Capgo API API-Schlüssel werden verwendet, um Anfragen an die Capgo API zu authentifizieren. Jeder Schlüssel kann unterschiedliche Berechtigungen (Modi) haben, um Zugriffsebenen zu steuern. Schlüssel sind organisationsspezifisch und sollten sorgfältig verwaltet werden, da sie Zugriff auf Ihre Capgo-Ressourcen gewähren. ## Schlüssel-Modi [Section titled “Schlüssel-Modi”](#schlüssel-modi) * **read**: Kann nur Daten lesen, keine Änderungen erlaubt * **upload**: Kann lesen, ändern und neue Bundles hochladen * **write**: Kann Daten lesen, ändern und Bundles hochladen * **all**: Vollzugriff auf alle Operationen Schlüssel-Modi folgen einem gestuften Schema. Wenn Sie einen Upload-Schlüssel haben und dann einen Write-Schlüssel erstellen, kann der Write-Schlüssel alles tun, was der Upload-Schlüssel konnte. Bitte schauen Sie sich das folgende Diagramm an, um besser zu verstehen, wie API-Schlüssel funktionieren. ![Ein Diagramm, das erklärt, wie API-Schlüssel funktionieren](/capgo_apikeys_diagram.webp) ## Unterschlüssel mit eingeschränkten Rechten [Section titled “Unterschlüssel mit eingeschränkten Rechten”](#unterschlüssel-mit-eingeschränkten-rechten) Sie können Unterschlüssel mit eingeschränktem Zugriff auf bestimmte Apps oder Organisationen erstellen. Dies ist nützlich, um den Zugriff auf bestimmte Ressourcen einzuschränken und gleichzeitig Operationen auf anderen zu ermöglichen. Verwenden Sie die Parameter `limited_to_apps` und `limited_to_orgs` beim Erstellen eines Schlüssels, um diese Einschränkungen zu definieren. ## Bewährte Methoden für Sicherheit [Section titled “Bewährte Methoden für Sicherheit”](#bewährte-methoden-für-sicherheit) 1. **Prinzip der geringsten Berechtigung**: Verwenden Sie immer den restriktivsten Modus, der Ihrer Integration noch ermöglicht zu funktionieren 2. **Regelmäßige Rotation**: Rotieren Sie Ihre API-Schlüssel regelmäßig 3. **Sichere Speicherung**: Speichern Sie API-Schlüssel sicher und committen Sie sie niemals in die Versionskontrolle 4. **Überwachung**: Überwachen Sie die Nutzung von API-Schlüsseln und widerrufen Sie kompromittierte Schlüssel sofort 5. **Eingeschränkte Unterschlüssel**: Verwenden Sie Unterschlüssel mit eingeschränkten Rechten für spezifische Integrationen, um Risiken zu minimieren ## Endpunkte [Section titled “Endpunkte”](#endpunkte) ### GET [Section titled “GET”](#get) `https://api.capgo.app/apikey/` Ruft alle API-Schlüssel ab, die mit Ihrem Konto verknüpft sind. #### Response-Typ [Section titled “Response-Typ”](#response-typ) ```typescript interface ApiKey { created_at: string | null id: number key: string mode: 'read' | 'write' | 'upload' | 'all' name: string updated_at: string | null user_id: string limited_to_apps?: string[] limited_to_orgs?: string[] } ``` #### Beispiel-Anfrage [Section titled “Beispiel-Anfrage”](#beispiel-anfrage) ```bash curl -H "authorization: your-api-key" https://api.capgo.app/apikey/ ``` #### Beispiel-Antwort [Section titled “Beispiel-Antwort”](#beispiel-antwort) ```json { "data": [ { "id": 1, "key": "ak_123...", "mode": "read", "name": "CI/CD Read Key", "created_at": "2024-01-01T00:00:00Z", "updated_at": "2024-01-01T00:00:00Z", "user_id": "user_123" }, { "id": 2, "key": "ak_456...", "mode": "upload", "name": "Deploy Bot", "created_at": "2024-01-02T00:00:00Z", "updated_at": "2024-01-02T00:00:00Z", "user_id": "user_123", "limited_to_apps": ["com.demo.app"] } ] } ``` ### POST [Section titled “POST”](#post) `https://api.capgo.app/apikey/` Erstellt einen neuen API-Schlüssel für eine bestimmte Organisation. #### Query-Parameter [Section titled “Query-Parameter”](#query-parameter) ```typescript interface ApiKeyCreate { name: string mode: 'read' | 'write' | 'upload' | 'all' limited_to_apps?: string[] limited_to_orgs?: string[] } ``` #### Beispiel-Anfrage [Section titled “Beispiel-Anfrage”](#beispiel-anfrage-1) ```bash curl -X POST \ -H "authorization: your-api-key" \ -H "Content-Type: application/json" \ -d '{ "name": "Limited Read Key", "mode": "read", "limited_to_apps": ["com.demo.app"] }' \ https://api.capgo.app/apikey/ ``` #### Beispiel-Antwort [Section titled “Beispiel-Antwort”](#beispiel-antwort-1) ```json { "apikey": { "id": 3, "key": "ak_789...", "mode": "read", "name": "Limited Read Key", "created_at": "2024-02-12T00:00:00Z", "user_id": "user_123", "limited_to_apps": ["com.demo.app"] } } ``` ### DELETE [Section titled “DELETE”](#delete) `https://api.capgo.app/apikey/:id/` Löscht einen bestehenden API-Schlüssel. Verwenden Sie dies, um den Zugriff sofort zu widerrufen. #### Parameter [Section titled “Parameter”](#parameter) * `id`: Die ID des zu löschenden API-Schlüssels (numerischer Identifikator, nicht der Schlüssel-String selbst) #### Beispiel-Anfrage [Section titled “Beispiel-Anfrage”](#beispiel-anfrage-2) ```bash curl -X DELETE -H "authorization: your-api-key" https://api.capgo.app/apikey/1/ ``` #### Erfolgs-Antwort [Section titled “Erfolgs-Antwort”](#erfolgs-antwort) ```json { "success": true } ``` ## Häufige Anwendungsfälle [Section titled “Häufige Anwendungsfälle”](#häufige-anwendungsfälle) 1. **CI/CD-Integration**: Erstellen Sie schreibgeschützte Schlüssel für CI-Pipelines, um den Deployment-Status zu überprüfen 2. **Deployment-Automatisierung**: Verwenden Sie Upload-Modus-Schlüssel für automatisierte Deployment-Skripte 3. **Überwachungs-Tools**: Verwenden Sie Read-Modus-Schlüssel für externe Überwachungsintegrationen 4. **Admin-Zugriff**: Verwenden Sie All-Modus-Schlüssel sparsam für administrative Tools 5. **Eingeschränkter Zugriff**: Erstellen Sie Unterschlüssel mit eingeschränkten Rechten für bestimmte Apps oder Organisationen für Drittanbieter-Integrationen ## Fehlerbehandlung [Section titled “Fehlerbehandlung”](#fehlerbehandlung) Häufige Fehlerszenarien und ihre Antworten: ```json // Invalid mode { "error": "Invalid mode specified. Must be one of: read, write, upload, all", "status": "KO" } // Key not found { "error": "API key not found", "status": "KO" } // Permission denied { "error": "Insufficient permissions to manage API keys", "status": "KO" } ``` # Apps > Detaillierte Dokumentation für den Apps-Endpunkt, die umfassende Einblicke in die Verwaltung und Interaktion mit Capacitor-Anwendungen über die Capgo-Plattform bietet. Apps sind die grundlegenden Entitäten in Capgo. Jede App repräsentiert eine einzigartige Capacitor-Anwendung, die Sie über die Plattform verwalten und aktualisieren können. Die Apps-API ermöglicht es Ihnen, App-Konfigurationen zu erstellen, abzurufen, zu aktualisieren und zu löschen. ## Apps verstehen [Section titled “Apps verstehen”](#apps-verstehen) Eine App in Capgo repräsentiert Ihre Capacitor-Anwendung und umfasst: * **App ID**: Eindeutiger Identifikator für Ihre Anwendung * **Name**: Lesbarer Name Ihrer Anwendung * **Icons**: Visuelle Identifikatoren für Ihre App im Dashboard * **Konfiguration**: Einstellungen, die steuern, wie Updates bereitgestellt werden * **Eigentum**: Organisations- und Benutzerzugriffsinformationen * **Nutzungsstatistiken**: Metriken zu Installationen und Updates ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) 1. **Namenskonvention**: Verwenden Sie klare, identifizierbare Namen für Ihre Apps 2. **Sicherheit**: Schützen Sie Ihre API-Schlüssel und Zugangsberechtigungen 3. **Organisation**: Gruppieren Sie verwandte Apps unter derselben Organisation 4. **Überwachung**: Überprüfen Sie regelmäßig App-Statistiken und -Leistung 5. **Backup**: Pflegen Sie Konfigurations-Backups für kritische Apps ## Endpunkte [Section titled “Endpunkte”](#endpunkte) ### GET [Section titled “GET”](#get) `https://api.capgo.app/app/` Abrufen von Informationen über Ihre Apps. #### Query-Parameter [Section titled “Query-Parameter”](#query-parameter) * `page`: Optional. Seitennummer für Paginierung * `limit`: Optional. Anzahl der Ergebnisse pro Seite (Standard: 50) * `org_id`: Optional. Filtern von Apps nach Organisations-ID. Falls nicht angegeben, werden Apps aus allen Organisationen zurückgegeben, auf die der Benutzer Zugriff hat Um eine bestimmte App abzurufen: * Verwenden Sie die App-ID im URL-Pfad: `https://api.capgo.app/app/:app_id` #### Antworttyp [Section titled “Antworttyp”](#antworttyp) ```typescript interface App { app_id: string created_at: string | null default_upload_channel: string icon_url: string id: string | null last_version: string | null name: string | null owner_org: string retention: number transfer_history: Json[] | null updated_at: string | null user_id: string | null } ``` #### Beispielanfrage [Section titled “Beispielanfrage”](#beispielanfrage) ```bash # Alle Apps abrufen curl -H "authorization: your-api-key" \ "https://api.capgo.app/app/" # Apps von einer bestimmten Organisation abrufen curl -H "authorization: your-api-key" \ "https://api.capgo.app/app/?org_id=046a36ac-e03c-4590-9257-bd6c9dba9ee8" # Bestimmte App abrufen curl -H "authorization: your-api-key" \ "https://api.capgo.app/app/com.demo.app" ``` #### Beispielantwort [Section titled “Beispielantwort”](#beispielantwort) ```json { "data": [ { "app_id": "com.demo.app", "created_at": "2024-01-01T00:00:00Z", "default_upload_channel": "dev", "icon_url": "https://example.com/icon.png", "id": "550e8400-e29b-41d4-a716-446655440000", "last_version": "1.0.0", "name": "Demo App", "owner_org": "046a36ac-e03c-4590-9257-bd6c9dba9ee8", "retention": 2592000, "transfer_history": null, "updated_at": "2024-01-01T00:00:00Z", "user_id": "6aa76066-55ef-4238-ade6-0b32334a4097" } ] } ``` ### POST [Section titled “POST”](#post) `https://api.capgo.app/app/` Erstellen Sie eine neue App. #### Request Body [Section titled “Request Body”](#request-body) ```typescript interface CreateApp { app_id: string name: string icon?: string owner_org: string } ``` #### Beispielanfrage [Section titled “Beispielanfrage”](#beispielanfrage-1) ```bash # Neue App erstellen curl -X POST \ -H "authorization: your-api-key" \ -H "Content-Type: application/json" \ -d '{ "name": "My New App", "app_id": "com.demo.myapp", // diese ID ist einzigartig in Capgo und kann von keinem Account wiederverwendet werden. "icon": "https://example.com/icon.png", "owner_org": "046a36ac-e03c-4590-9257-bd6c9dba9ee8" }' \ https://api.capgo.app/app/ ``` #### Erfolgsantwort [Section titled “Erfolgsantwort”](#erfolgsantwort) ```json { "app_id": "My New App", "created_at": "2024-01-01T00:00:00Z", "default_upload_channel": "dev", "icon_url": "https://example.com/icon.png", "id": "550e8400-e29b-41d4-a716-446655440000", "name": "My New App", "owner_org": "046a36ac-e03c-4590-9257-bd6c9dba9ee8", "retention": 2592000, "updated_at": "2024-01-01T00:00:00Z" } ``` ### PUT [Section titled “PUT”](#put) `https://api.capgo.app/app/:app_id` Aktualisieren Sie eine bestehende App. Die App-ID wird im URL-Pfad angegeben. #### Request Body [Section titled “Request Body”](#request-body-1) ```typescript interface UpdateApp { name?: string icon?: string retention?: number } ``` #### Beispielanfrage [Section titled “Beispielanfrage”](#beispielanfrage-2) ```bash curl -X PUT \ -H "authorization: your-api-key" \ -H "Content-Type: application/json" \ -d '{ "name": "Updated App Name", "icon": "https://example.com/updated-icon.png", "retention": 45 }' \ https://api.capgo.app/app/com.demo.app ``` #### Erfolgsantwort [Section titled “Erfolgsantwort”](#erfolgsantwort-1) ```json { "app_id": "com.demo.app", "created_at": "2024-01-01T00:00:00Z", "default_upload_channel": "dev", "icon_url": "https://example.com/updated-icon.png", "id": "550e8400-e29b-41d4-a716-446655440000", "name": "Updated App Name", "owner_org": "046a36ac-e03c-4590-9257-bd6c9dba9ee8", "retention": 45, "updated_at": "2024-01-01T00:00:00Z" } ``` ### DELETE [Section titled “DELETE”](#delete) `https://api.capgo.app/app/:app_id` Löschen Sie eine App und alle zugehörigen Ressourcen. Die App-ID wird im URL-Pfad angegeben. Verwenden Sie dies mit äußerster Vorsicht, da diese Aktion nicht rückgängig gemacht werden kann. #### Beispielanfrage [Section titled “Beispielanfrage”](#beispielanfrage-3) ```bash curl -X DELETE \ -H "authorization: your-api-key" \ https://api.capgo.app/app/com.demo.app ``` #### Erfolgsantwort [Section titled “Erfolgsantwort”](#erfolgsantwort-2) ```json { "status": "ok" } ``` ## Fehlerbehandlung [Section titled “Fehlerbehandlung”](#fehlerbehandlung) Häufige Fehlerszenarien und ihre Antworten: ```json // App nicht gefunden { "error": "App not found", "status": "KO" } // Doppelte benutzerdefinierte ID { "error": "Custom ID already in use", "status": "KO" } // Ungültige Parameter { "error": "Invalid app configuration", "status": "KO" } // Berechtigung verweigert { "error": "Insufficient permissions to manage app", "status": "KO" } // Organisationszugriff verweigert { "status": "You do not have access to this organization" } ``` ## Häufige Anwendungsfälle [Section titled “Häufige Anwendungsfälle”](#häufige-anwendungsfälle) 1. **Neue App erstellen** ```typescript // Neue App einrichten { "name": "Production App", "owner_org": "046a36ac-e03c-4590-9257-bd6c9dba9ee8" } ``` 2. **App-Konfiguration aktualisieren** ```typescript // App-Name und Icon ändern { "name": "Rebranded App Name", "icon": "https://example.com/new-icon.png" } ``` 3. **Aufbewahrungsrichtlinie festlegen** ```typescript // Automatische Bundle-Bereinigung konfigurieren { "retention": 30 // Bundles 30 Tage aufbewahren } ``` 4. **Apps nach Organisation abrufen** ```bash # Alle Apps in einer bestimmten Organisation auflisten curl -H "authorization: your-api-key" \ "https://api.capgo.app/app/?org_id=046a36ac-e03c-4590-9257-bd6c9dba9ee8" ``` ## Ressourcenverwaltung [Section titled “Ressourcenverwaltung”](#ressourcenverwaltung) 1. **Speicheroptimierung**: Überwachen Sie die Speichernutzung und legen Sie angemessene Aufbewahrungsrichtlinien fest 2. **Organisation**: Gruppieren Sie verwandte Apps unter einer einzigen Organisation 3. **Zugriffskontrolle**: Verwalten Sie, welche Teammitglieder App-Einstellungen ändern können 4. **Backup-Strategie**: Sichern Sie kritische App-Konfigurationen und -Einstellungen # Bundles > Detaillierte Anleitung zur Verwaltung von Capgo-Bundles, einschließlich Auflistung, Löschen, Bundle-(Versions-)Verwaltung und Speicheroptimierung für App-Update-Pakete. Bundles sind die Kernaktualisierungspakete in Capgo. Jedes Bundle enthält die Web-Assets (HTML, CSS, JS), aus denen der Inhalt Ihrer App besteht. Mit den Bundles API können Sie diese Update-Pakete verwalten, einschließlich deren Auflistung und Löschung. ## Bundles verstehen [Section titled “Bundles verstehen”](#bundles-verstehen) Ein Bundle stellt ein bestimmtes Bundle (Version) des Webinhalts Ihrer App dar und umfasst: * **Bundle (Version)**: Semantische Versionsnummer für das Bundle * **Prüfsumme**: Eindeutiger Hash zur Überprüfung der Bundle-Integrität * **Speicherinformationen**: Details darüber, wo und wie das Bundle gespeichert wird * **Native Anforderungen**: Mindestanforderungen an die native App-Version * **Metadaten**: Erstellungszeit, Eigentum und andere Tracking-Informationen ## Manuelle Bundle-Erstellung (ohne CLI) [Section titled “Manuelle Bundle-Erstellung (ohne CLI)”](#manuelle-bundle-erstellung-ohne-cli) So erstellen und laden Sie Bundles manuell ohne Verwendung von Capgo CLI: ### Schritt 1: Erstellen Sie Ihre App [Section titled “Schritt 1: Erstellen Sie Ihre App”](#schritt-1-erstellen-sie-ihre-app) Erstellen Sie zunächst die Web-Assets Ihrer App: ```bash npm run build ``` ### Schritt 2: Bundle-Zip mit denselben Paketen wie Capgo CLI erstellen [Section titled “Schritt 2: Bundle-Zip mit denselben Paketen wie Capgo CLI erstellen”](#schritt-2-bundle-zip-mit-denselben-paketen-wie-capgo-cli-erstellen) **Wichtig**: Verwenden Sie genau die gleichen JavaScript-Pakete, die Capgo CLI intern verwendet, um die Kompatibilität sicherzustellen. #### Erforderliche Pakete installieren [Section titled “Erforderliche Pakete installieren”](#erforderliche-pakete-installieren) ```bash npm install adm-zip @tomasklaen/checksum ``` #### Erstellen Sie ein Zip-Bundle mit JavaScript (dasselbe wie Capgo CLI) [Section titled “Erstellen Sie ein Zip-Bundle mit JavaScript (dasselbe wie Capgo CLI)”](#erstellen-sie-ein-zip-bundle-mit-javascript-dasselbe-wie-capgo-cli) Hinweis: In den folgenden Beispielen bezieht sich `version` auf den Bundle-(Versions-)Namen, der von API verwendet wird. ```javascript const fs = require('node:fs'); const path = require('node:path'); const os = require('node:os'); const AdmZip = require('adm-zip'); const { checksum: getChecksum } = require('@tomasklaen/checksum'); // Exact same implementation as Capgo CLI function zipFileUnix(filePath) { const zip = new AdmZip(); zip.addLocalFolder(filePath); return zip.toBuffer(); } async function zipFileWindows(filePath) { console.log('Zipping file windows mode'); const zip = new AdmZip(); const addToZip = (folderPath, zipPath) => { const items = fs.readdirSync(folderPath); for (const item of items) { const itemPath = path.join(folderPath, item); const stats = fs.statSync(itemPath); if (stats.isFile()) { const fileContent = fs.readFileSync(itemPath); zip.addFile(path.join(zipPath, item).split(path.sep).join('/'), fileContent); } else if (stats.isDirectory()) { addToZip(itemPath, path.join(zipPath, item)); } } }; addToZip(filePath, ''); return zip.toBuffer(); } // Main zipFile function (exact same logic as CLI) async function zipFile(filePath) { if (os.platform() === 'win32') { return zipFileWindows(filePath); } else { return zipFileUnix(filePath); } } async function createBundle(inputPath, outputPath, version) { // Create zip using exact same method as Capgo CLI const zipped = await zipFile(inputPath); // Write to file fs.writeFileSync(outputPath, zipped); // Calculate checksum using exact same package as CLI const checksum = await getChecksum(zipped, 'sha256'); return { filename: path.basename(outputPath), version: version, size: zipped.length, checksum: checksum }; } // Usage async function main() { try { const result = await createBundle('./dist', './my-app-1.2.3.zip', '1.2.3'); console.log('Bundle info:', JSON.stringify(result, null, 2)); } catch (error) { console.error('Error creating bundle:', error); } } main(); ``` ### Schritt 3: Berechnen Sie die SHA256-Prüfsumme mit demselben Paket wie CLI [Section titled “Schritt 3: Berechnen Sie die SHA256-Prüfsumme mit demselben Paket wie CLI”](#schritt-3-berechnen-sie-die-sha256-prüfsumme-mit-demselben-paket-wie-cli) ```javascript const fs = require('node:fs'); const { checksum: getChecksum } = require('@tomasklaen/checksum'); async function calculateChecksum(filePath) { const fileBuffer = fs.readFileSync(filePath); // Use exact same package and method as Capgo CLI const checksum = await getChecksum(fileBuffer, 'sha256'); return checksum; } // Usage async function main() { const checksum = await calculateChecksum('./my-app-1.2.3.zip'); console.log('Checksum:', checksum); } main(); ``` ### Schritt 4: Bundle in Ihren Speicher hochladen [Section titled “Schritt 4: Bundle in Ihren Speicher hochladen”](#schritt-4-bundle-in-ihren-speicher-hochladen) Laden Sie Ihre ZIP-Datei in einen beliebigen über das Internet zugänglichen Speicher hoch: ```bash # Example: Upload to your server via scp scp my-app-1.2.3.zip user@your-server.com:/var/www/bundles/ # Example: Upload to S3 using AWS CLI aws s3 cp my-app-1.2.3.zip s3://your-bucket/bundles/ # Example: Upload via curl to a custom endpoint curl -X POST https://your-storage-api.com/upload \ -H "Authorization: Bearer YOUR_TOKEN" \ -F "file=@my-app-1.2.3.zip" ``` **Wichtig**: Ihr Bundle muss über eine HTTPS-URL **öffentlich zugänglich** sein (keine Authentifizierung erforderlich). Die Server von Capgo müssen das Paket von dieser URL herunterladen. Beispiele für gültige öffentliche URLs: * `https://your-storage.com/bundles/my-app-1.2.3.zip` * `https://github.com/username/repo/releases/download/v1.2.3/bundle.zip` * `https://cdn.jsdelivr.net/gh/username/repo@v1.2.3/dist.zip` ### Schritt 5: Bundle registrieren bei Capgo API [Section titled “Schritt 5: Bundle registrieren bei Capgo API”](#schritt-5-bundle-registrieren-bei-capgo-api) Registrieren Sie das externe Bundle bei Capgo mit direkten API-Aufrufen: ```javascript async function registerWithCapgo(appId, version, bundleUrl, checksum, apiKey) { const fetch = require('node-fetch'); // Create bundle (version) const response = await fetch('https://api.capgo.app/bundle/', { method: 'POST', headers: { 'Content-Type': 'application/json', 'authorization': apiKey }, body: JSON.stringify({ app_id: appId, version: version, external_url: bundleUrl, checksum: checksum }) }); if (!response.ok) { throw new Error(`Failed to create bundle: ${response.statusText}`); } const data = await response.json(); console.log('Bundle created:', data); return data; } ``` #### API Parameter [Section titled “API Parameter”](#api-parameter) | Parameter | Beschreibung | Erforderlich | | -------------- | ---------------------------------------------------------------------------------------------------------------------------- | ------------ | | `app_id` | Ihre App-ID | Ja | | `version` | Bundle (Version) semantische Version (z. B. „1.2.3“) | Ja | | `external_url` | **Öffentlich zugänglich** HTTPS-URL, unter der das Bundle heruntergeladen werden kann (keine Authentifizierung erforderlich) | Ja | | `checksum` | SHA256-Prüfsumme der ZIP-Datei | Ja | ## Anforderungen an die Bundle-Struktur [Section titled “Anforderungen an die Bundle-Struktur”](#anforderungen-an-die-bundle-struktur) Ihre Bundle-Zip-Datei muss diesen Anforderungen entsprechen: 1. **Root-Indexdatei**: Muss `index.html` auf der Root-Ebene haben 2. **Capacitor-Integration**: Muss `notifyAppReady()` in Ihrem App-Code aufrufen 3. **Asset-Pfade**: Verwenden Sie relative Pfade für alle Assets ### Gültige Bundle-Struktur [Section titled “Gültige Bundle-Struktur”](#gültige-bundle-struktur) ```plaintext bundle.zip ├── index.html ├── assets/ │ ├── app.js │ └── styles.css └── images/ ``` ## Vollständiges Beispiel für einen manuellen Workflow [Section titled “Vollständiges Beispiel für einen manuellen Workflow”](#vollständiges-beispiel-für-einen-manuellen-workflow) Einfaches Node.js-Skript zum Komprimieren, Prüfen der Summe und Hochladen nach Capgo: ```javascript const fs = require('node:fs'); const os = require('node:os'); const AdmZip = require('adm-zip'); const { checksum: getChecksum } = require('@tomasklaen/checksum'); const fetch = require('node-fetch'); async function deployToCapgo() { const APP_ID = 'com.example.app'; const VERSION = '1.2.3'; const BUNDLE_URL = 'https://your-storage.com/bundles/app-1.2.3.zip'; const API_KEY = process.env.CAPGO_API_KEY; // 1. Create zip (same as Capgo CLI) const zip = new AdmZip(); zip.addLocalFolder('./dist'); const zipped = zip.toBuffer(); // 2. Calculate checksum (same as Capgo CLI) const checksum = await getChecksum(zipped, 'sha256'); console.log('Checksum:', checksum); // 3. Upload to your storage (replace with your upload logic) // fs.writeFileSync('./bundle.zip', zipped); // ... upload bundle.zip to your storage ... // 4. Register with Capgo API const response = await fetch('https://api.capgo.app/bundle/', { method: 'POST', headers: { 'Content-Type': 'application/json', 'authorization': API_KEY }, body: JSON.stringify({ app_id: APP_ID, version: VERSION, external_url: BUNDLE_URL, checksum: checksum }) }); if (!response.ok) { throw new Error(`Failed: ${response.statusText}`); } console.log('Bundle registered with Capgo!'); } deployToCapgo().catch(console.error); ``` Abhängigkeiten installieren: ```bash npm install adm-zip @tomasklaen/checksum node-fetch ``` ## Prüfsummenüberprüfung [Section titled “Prüfsummenüberprüfung”](#prüfsummenüberprüfung) ### JavaScript Prüfsummenberechnung (wie Capgo CLI) [Section titled “JavaScript Prüfsummenberechnung (wie Capgo CLI)”](#javascript-prüfsummenberechnung-wie-capgo-cli) Verwenden Sie genau dasselbe Paket und dieselbe Methode, die Capgo CLI intern verwendet: ````javascript const fs = require('node:fs'); const { checksum: getChecksum } = require('@tomasklaen/checksum'); async function calculateChecksum(filePath) { const fileBuffer = fs.readFileSync(filePath); // Use exact same package and method as Capgo CLI const checksum = await getChecksum(fileBuffer, 'sha256'); return checksum; } // Verify checksum matches async function verifyChecksum(filePath, expectedChecksum) { const actualChecksum = await calculateChecksum(filePath); const isValid = actualChecksum === expectedChecksum; console.log(`File: ${filePath}`); console.log(`Expected: ${expectedChecksum}`); console.log(`Actual: ${actualChecksum}`); console.log(`Valid: ${isValid}`); return isValid; } // Usage async function main() { const bundleChecksum = await calculateChecksum('./my-app-1.2.3.zip'); console.log('SHA256 Checksum:', bundleChecksum); } main(); ```### Prüfsummen-Wichtigkeit - **Bundle-Integrität**: Stellt sicher, dass das Bundle während der Übertragung nicht beschädigt wurde - **API Überprüfung**: Capgo überprüft Prüfsummen, bevor Pakete akzeptiert werden - **Plugin-Überprüfung**: Das mobile Plugin überprüft Prüfsummen, bevor Updates angewendet werden ## Bewährte Methoden 1. **Bundle-(Versions-)Management**: Semantische Versionierung konsequent verwenden 2. **Speicheroptimierung**: Entfernen Sie nicht verwendete Bundles regelmäßig 3. **Bundle-(Versions-)Kompatibilität**: Legen Sie entsprechende Mindestanforderungen für die native Version fest 4. **Backup-Strategie**: Backups kritischer Bundles (Versionen) verwalten ## Endpunkte ### GET `https://api.capgo.app/bundle/` Paketinformationen abrufen. Gibt 50 Bundles pro Seite zurück. #### Abfrageparameter - `app_id`: Erforderlich. Die ID Ihrer App - `page`: Optional. Seitenzahl für die Paginierung #### Antworttyp ```typescript interface Bundle { app_id: string bucket_id: string | null checksum: string | null created_at: string | null deleted: boolean external_url: string | null id: number minUpdateVersion: string | null name: string native_packages: Json[] | null owner_org: string r2_path: string | null session_key: string | null storage_provider: string updated_at: string | null user_id: string | null } ```` #### Beispielanfrage [Section titled “Beispielanfrage”](#beispielanfrage) ```bash # Get all bundles curl -H "authorization: your-api-key" \ "https://api.capgo.app/bundle/?app_id=app_123" # Get next page curl -H "authorization: your-api-key" \ "https://api.capgo.app/bundle/?app_id=app_123&page=1" ``` #### Beispielantwort [Section titled “Beispielantwort”](#beispielantwort) ```json { "data": [ { "id": 1, "app_id": "app_123", "name": "1.0.0", "checksum": "abc123...", "minUpdateVersion": "1.0.0", "storage_provider": "r2", "created_at": "2024-01-01T00:00:00Z", "updated_at": "2024-01-01T00:00:00Z", "deleted": false, "owner_org": "org_123", "user_id": "user_123" } ] } ``` ### LÖSCHEN [Section titled “LÖSCHEN”](#löschen) `https://api.capgo.app/bundle/` Löschen Sie ein oder alle Bundles für eine App. Seien Sie vorsichtig, da diese Aktion nicht rückgängig gemacht werden kann. #### Abfrageparameter [Section titled “Abfrageparameter”](#abfrageparameter) So löschen Sie ein bestimmtes Bundle: ```typescript interface BundleDelete { app_id: string version: string } ``` Zum Löschen aller Bundles: ```typescript interface BundleDeleteAll { app_id: string } ``` #### Beispielanfragen [Section titled “Beispielanfragen”](#beispielanfragen) ```bash # Delete specific bundle curl -X DELETE \ -H "authorization: your-api-key" \ -H "Content-Type: application/json" \ -d '{ "app_id": "app_123", "version": "1.0.0" }' \ https://api.capgo.app/bundle/ # Delete all bundles curl -X DELETE \ -H "authorization: your-api-key" \ -H "Content-Type: application/json" \ -d '{ "app_id": "app_123" }' \ https://api.capgo.app/bundle/ ``` #### Erfolgsantwort [Section titled “Erfolgsantwort”](#erfolgsantwort) ```json { "status": "ok" } ``` ### POST [Section titled “POST”](#post) `https://api.capgo.app/bundle/` Erstellen Sie ein neues Bundle mit externer URL. #### Anforderungstext [Section titled “Anforderungstext”](#anforderungstext) ```typescript interface CreateBundleBody { app_id: string version: string external_url: string // Must be publicly accessible HTTPS URL checksum: string } ``` #### Beispielanfrage [Section titled “Beispielanfrage”](#beispielanfrage-1) ```bash curl -X POST \ -H "authorization: your-api-key" \ -H "Content-Type: application/json" \ -d '{ "app_id": "com.example.app", "version": "1.2.3", "external_url": "https://your-storage.com/bundles/app-1.2.3.zip", "checksum": "a1b2c3d4e5f6789abcdef123456789abcdef123456789abcdef123456789abcd" }' \ https://api.capgo.app/bundle/ ``` #### Erfolgsantwort [Section titled “Erfolgsantwort”](#erfolgsantwort-1) ```json { "status": "ok" } ``` ### POST (Metadaten) [Section titled “POST (Metadaten)”](#post-metadaten) `https://api.capgo.app/bundle/metadata` Aktualisieren Sie Bundle-Metadaten wie Link- und Kommentarinformationen. #### Anforderungstext [Section titled “Anforderungstext”](#anforderungstext-1) ```typescript interface UpdateMetadataBody { app_id: string version_id: number // bundle (version) id link?: string comment?: string } ``` #### Beispielanfrage [Section titled “Beispielanfrage”](#beispielanfrage-2) ```bash curl -X POST \ -H "authorization: your-api-key" \ -H "Content-Type: application/json" \ -d '{ "app_id": "app_123", "version_id": 456, "link": "https://github.com/myorg/myapp/releases/tag/v1.0.0", "comment": "Fixed critical bug in authentication" }' \ https://api.capgo.app/bundle/metadata ``` #### Erfolgsantwort [Section titled “Erfolgsantwort”](#erfolgsantwort-2) ```json { "status": "success" } ``` ### PUT [Section titled “PUT”](#put) `https://api.capgo.app/bundle/` Legen Sie ein Bundle auf einen bestimmten Kanal fest. Dadurch wird ein Bundle (eine Version) mit einem Vertriebskanal verknüpft. #### Anforderungstext [Section titled “Anforderungstext”](#anforderungstext-2) ```typescript interface SetChannelBody { app_id: string version_id: number // bundle (version) id channel_id: number } ``` #### Beispielanfrage [Section titled “Beispielanfrage”](#beispielanfrage-3) ```bash curl -X PUT \ -H "authorization: your-api-key" \ -H "Content-Type: application/json" \ -d '{ "app_id": "app_123", "version_id": 456, "channel_id": 789 }' \ https://api.capgo.app/bundle/ ``` #### Erfolgsantwort [Section titled “Erfolgsantwort”](#erfolgsantwort-3) ```json { "status": "success", "message": "Bundle 1.0.0 set to channel production" } ``` ## Fehlerbehandlung [Section titled “Fehlerbehandlung”](#fehlerbehandlung) Häufige Fehlerszenarien und ihre Antworten: ```json // Bundle not found { "error": "Bundle not found", "status": "KO" } // Invalid bundle (version) format { "error": "Invalid version format", "status": "KO" } // Storage error { "error": "Failed to delete bundle from storage", "status": "KO" } // Permission denied { "error": "Insufficient permissions to manage bundles", "status": "KO" } ``` ## Häufige Anwendungsfälle [Section titled “Häufige Anwendungsfälle”](#häufige-anwendungsfälle) 1. **Alte Bundles (Versionen) bereinigen** ```typescript // Delete outdated beta bundles (versions) { "app_id": "app_123", "version": "1.0.0-beta.1" } ``` 2. **App-Reset** ```typescript // Remove all bundles to start fresh { "app_id": "app_123" } ``` ## Überlegungen zur Lagerung [Section titled “Überlegungen zur Lagerung”](#überlegungen-zur-lagerung) 1. **Aufbewahrungsrichtlinie**: Legen Sie fest, wie lange alte Bundles aufbewahrt werden sollen 2. **Größenverwaltung**: Überwachen Sie Bundle-Größen und Speichernutzung 3. **Backup-Strategie**: Erwägen Sie die Sicherung kritischer Bundles (Versionen) 4. **Kostenoptimierung**: Entfernen Sie unnötige Bundles, um die Speicherkosten zu optimieren # Kanäle > Leitfaden zur Verwaltung von App-Update-Kanälen in Capgo, einschließlich Versionskontrolle, Plattform-Targeting und Update-Richtlinien. Kanäle sind der zentrale Mechanismus zur Verwaltung von App-Updates in Capgo. Sie ermöglichen es Ihnen zu kontrollieren, wie und wann Ihre Benutzer Updates erhalten, und ermöglichen Funktionen wie A/B-Tests, gestaffelte Rollouts und plattformspezifische Updates. ## Kanäle verstehen [Section titled “Kanäle verstehen”](#kanäle-verstehen) Ein Kanal stellt einen Verteilungsweg für Ihre App-Updates dar. Jeder Kanal kann mit spezifischen Regeln und Einschränkungen konfiguriert werden: * **Versionskontrolle**: Geben Sie an, welche Version Benutzer erhalten * **Plattform-Targeting**: Zielen Sie auf bestimmte Plattformen ab (iOS/Android) * **Update-Richtlinien**: Kontrollieren Sie, wie Updates bereitgestellt werden * **Gerätebeschränkungen**: Verwalten Sie, welche Geräte auf Updates zugreifen können ## Kanal-Konfigurationsoptionen [Section titled “Kanal-Konfigurationsoptionen”](#kanal-konfigurationsoptionen) * **public**: Als Standardkanal für neue Geräte festlegen * **disableAutoUpdateUnderNative**: Verhindert Updates, wenn die native App-Version des Geräts neuer ist als die im Kanal verfügbare Update-Version (z.B. Gerät ist auf Version 1.2.3, aber Kanal hat 1.2.2) * **disableAutoUpdate**: Update-Verhalten steuern (“major”, “minor”, “version\_number”, “none”) * **ios/android**: Für bestimmte Plattformen aktivieren/deaktivieren * **allow\_device\_self\_set**: Geräten erlauben, ihren Kanal selbst zu wählen * **allow\_emulator**: Updates auf Emulator-Geräten zulassen * **allow\_dev**: Updates auf Entwicklungs-Builds zulassen ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) 1. **Test-Kanal**: Pflegen Sie einen Test-Kanal für interne Validierung 2. **Gestaffelter Rollout**: Verwenden Sie mehrere Kanäle für schrittweise Update-Bereitstellung 3. **Plattform-Trennung**: Erstellen Sie bei Bedarf separate Kanäle für iOS und Android 4. **Versionskontrolle**: Verwenden Sie semantische Versionierung für klare Update-Pfade ## Endpunkte [Section titled “Endpunkte”](#endpunkte) ### POST [Section titled “POST”](#post) `https://api.capgo.app/channel/` Erstellen oder aktualisieren Sie eine Kanal-Konfiguration. #### Request Body [Section titled “Request Body”](#request-body) ```typescript type disable_update = "major" | "minor" | "version_number" | "none" interface ChannelSet { app_id: string channel: string version?: string public?: boolean disableAutoUpdateUnderNative?: boolean disableAutoUpdate?: disable_update ios?: boolean android?: boolean allow_device_self_set?: boolean allow_emulator?: boolean allow_dev?: boolean } ``` #### Beispielanfrage [Section titled “Beispielanfrage”](#beispielanfrage) ```bash curl -X POST \ -H "authorization: your-api-key" \ -H "Content-Type: application/json" \ -d '{ "app_id": "app_123", "channel": "beta", "version": "1.2.0", "public": false, "disableAutoUpdate": "minor", "ios": true, "android": true, "allow_emulator": true }' \ https://api.capgo.app/channel/ ``` #### Erfolgsantwort [Section titled “Erfolgsantwort”](#erfolgsantwort) ```json { "status": "ok" } ``` ### GET [Section titled “GET”](#get) `https://api.capgo.app/channel/` Abrufen von Kanal-Informationen. Gibt 50 Kanäle pro Seite zurück. #### Query-Parameter [Section titled “Query-Parameter”](#query-parameter) * `app_id`: Erforderlich. Die ID Ihrer App * `page`: Optional. Seitennummer für Paginierung * `channel`: Optional. Spezifischer Kanalname zum Abrufen #### Beispielanfragen [Section titled “Beispielanfragen”](#beispielanfragen) ```bash # Alle Kanäle abrufen curl -H "authorization: your-api-key" \ "https://api.capgo.app/channel/?app_id=app_123" # Bestimmten Kanal abrufen curl -H "authorization: your-api-key" \ "https://api.capgo.app/channel/?app_id=app_123&channel=beta" # Nächste Seite abrufen curl -H "authorization: your-api-key" \ "https://api.capgo.app/channel/?app_id=app_123&page=1" ``` #### Antworttyp [Section titled “Antworttyp”](#antworttyp) ```typescript interface Channel { id: number; created_at: string; name: string; app_id: string; version: { id: number, name: string }; created_by: string; updated_at: string; public: boolean; disableAutoUpdateUnderNative: boolean; disableAutoUpdate: boolean; allow_emulator: boolean; allow_dev: boolean; } ``` #### Beispielantwort [Section titled “Beispielantwort”](#beispielantwort) ```json { "data": [ { "id": 1, "name": "production", "app_id": "app_123", "version": { "id": 1, "name": "1.0.0" }, "created_at": "2024-01-01T00:00:00Z", "updated_at": "2024-01-01T00:00:00Z", "created_by": "user_123", "public": true, "disableAutoUpdateUnderNative": false, "disableAutoUpdate": false, "allow_emulator": false, "allow_dev": false } ] } ``` ### DELETE [Section titled “DELETE”](#delete) `https://api.capgo.app/channel/` Löschen Sie einen Kanal. Beachten Sie, dass dies alle Geräte betrifft, die diesen Kanal verwenden. #### Query-Parameter [Section titled “Query-Parameter”](#query-parameter-1) ```typescript interface Channel { channel: string app_id: string } ``` #### Beispielanfrage [Section titled “Beispielanfrage”](#beispielanfrage-1) ```bash curl -X DELETE \ -H "authorization: your-api-key" \ -H "Content-Type: application/json" \ -d '{ "app_id": "app_123", "channel": "beta" }' \ https://api.capgo.app/channel/ ``` #### Erfolgsantwort [Section titled “Erfolgsantwort”](#erfolgsantwort-1) ```json { "status": "ok" } ``` ## Fehlerbehandlung [Section titled “Fehlerbehandlung”](#fehlerbehandlung) Häufige Fehlerszenarien und ihre Antworten: ```json // Kanal nicht gefunden { "error": "Channel not found", "status": "KO" } // Ungültiges Versionsformat { "error": "Invalid version format. Use semantic versioning", "status": "KO" } // Ungültige Update-Richtlinie { "error": "Invalid disableAutoUpdate value", "status": "KO" } // Berechtigung verweigert { "error": "Insufficient permissions to manage channels", "status": "KO" } ``` ## Häufige Anwendungsfälle [Section titled “Häufige Anwendungsfälle”](#häufige-anwendungsfälle) 1. **Beta-Tests** ```typescript { "app_id": "app_123", "channel": "beta", "version": "1.2.0-beta", "public": false, "allow_emulator": true, "allow_dev": true } ``` 2. **Produktions-Rollout** ```typescript { "app_id": "app_123", "channel": "production", "version": "1.2.0", "public": true, "disableAutoUpdate": "minor" } ``` 3. **Plattformspezifische Updates** ```typescript { "app_id": "app_123", "channel": "ios-hotfix", "version": "1.2.1", "ios": true, "android": false } ``` # Devices > Dokumentation zur Verwaltung von Geräten über die Capgo-API, einschließlich Tracking, Versionierung und Kanalzuweisungen für App-Installationen. Devices repräsentieren einzelne Installationen Ihrer App, die von Capgo verwaltet werden. Die Devices-API ermöglicht es Ihnen, Geräte zu verfolgen und zu verwalten, einschließlich ihrer Versionen, Kanäle und Update-Status. Datenaufbewahrung Gerätedaten werden für **90 Tage** ab der letzten Geräteaktivität aufbewahrt. Geräte, die sich in diesem Zeitraum nicht mit Capgo verbunden haben, werden automatisch aus dem System entfernt. Aktive Geräte werden kontinuierlich verfolgt, wenn sie nach Updates suchen. ## Geräte verstehen [Section titled “Geräte verstehen”](#geräte-verstehen) Jedes Gerät hat einzigartige Eigenschaften und Zustände: * **Platform**: iOS oder Android * **Version**: Aktuelle Bundle-Version und native Build-Version * **Environment**: Produktion oder Entwicklung, Emulator oder physisches Gerät * **Channel**: Aktuelle Update-Kanalzuweisung * **Custom ID**: Optionaler Identifikator für Ihre eigenen Tracking-Zwecke ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) 1. **Versionsverfolgung**: Überwachen Sie Geräteversionen, um die Update-Akzeptanz sicherzustellen 2. **Kanalverwaltung**: Weisen Sie Geräte basierend auf Testanforderungen geeigneten Kanälen zu 3. **Umgebungsbewusstsein**: Behandeln Sie verschiedene Umgebungen (Prod/Dev/Emulator) angemessen 4. **Benutzerdefinierte Identifikation**: Verwenden Sie benutzerdefinierte IDs zur Integration mit Ihren bestehenden Systemen ## Endpoints [Section titled “Endpoints”](#endpoints) ### POST [Section titled “POST”](#post) `https://api.capgo.app/device/` Verknüpfen Sie ein Gerät mit einer bestimmten Version oder einem Kanal. #### Request Body [Section titled “Request Body”](#request-body) ```typescript interface DeviceLink { app_id: string device_id: string version_id?: string // version name channel?: string // channel name } ``` #### Beispielanfrage [Section titled “Beispielanfrage”](#beispielanfrage) ```bash curl -X POST \ -H "authorization: your-api-key" \ -H "Content-Type: application/json" \ -d '{ "app_id": "app_123", "device_id": "device_456", "channel": "beta" }' \ https://api.capgo.app/device/ ``` #### Erfolgsantwort [Section titled “Erfolgsantwort”](#erfolgsantwort) ```json { "status": "ok" } ``` ### GET [Section titled “GET”](#get) `https://api.capgo.app/device/` Abrufen von Geräteinformationen. Verwendet cursor-basierte Paginierung für effizientes Abrufen großer Gerätelisten. #### Query-Parameter [Section titled “Query-Parameter”](#query-parameter) * `app_id`: Erforderlich. Die ID Ihrer App * `device_id`: Optional. Spezifische Geräte-ID zum Abrufen eines einzelnen Geräts * `cursor`: Optional. Cursor aus vorheriger Antwort für Paginierung * `limit`: Optional. Anzahl der Geräte pro Seite (Standard: 50) #### Beispielanfrages [Section titled “Beispielanfrages”](#beispielanfrages) ```bash # Get all devices (first page) curl -H "authorization: your-api-key" \ "https://api.capgo.app/device/?app_id=app_123" # Get specific device curl -H "authorization: your-api-key" \ "https://api.capgo.app/device/?app_id=app_123&device_id=device_456" # Get next page using cursor curl -H "authorization: your-api-key" \ "https://api.capgo.app/device/?app_id=app_123&cursor=2024-01-01T00:00:00Z|device_456" ``` #### Antworttyp (Liste) [Section titled “Antworttyp (Liste)”](#antworttyp-liste) Bei Anforderung mehrerer Geräte (kein `device_id` Parameter): ```typescript interface DeviceListResponse { data: Device[]; nextCursor?: string; // Als 'cursor' Parameter übergeben, um nächste Seite zu erhalten hasMore: boolean; // true wenn weitere Seiten verfügbar } interface Device { updated_at: string; device_id: string; custom_id: string; version?: number; version_name: string | null; channel?: string; app_id: string; platform: "ios" | "android"; plugin_version: string; os_version: string; version_build: string; is_prod: boolean; is_emulator: boolean; } ``` #### Antworttyp (Einzelnes Gerät) [Section titled “Antworttyp (Einzelnes Gerät)”](#antworttyp-einzelnes-gerät) Bei Anforderung eines bestimmten Geräts mit `device_id` Parameter wird das Geräteobjekt direkt zurückgegeben: ```typescript interface Device { updated_at: string; device_id: string; custom_id: string; version?: number; version_name: string | null; channel?: string; app_id: string; platform: "ios" | "android"; plugin_version: string; os_version: string; version_build: string; is_prod: boolean; is_emulator: boolean; } ``` #### Beispielantwort (Liste) [Section titled “Beispielantwort (Liste)”](#beispielantwort-liste) ```json { "data": [ { "device_id": "device_456", "custom_id": "test-device-1", "version": 1, "version_name": "1.0.0", "app_id": "app_123", "platform": "ios", "plugin_version": "5.0.0", "os_version": "17.0", "version_build": "1", "is_prod": true, "is_emulator": false, "updated_at": "2024-01-01T00:00:00Z" } ], "nextCursor": "2024-01-01T00:00:00Z|device_456", "hasMore": true } ``` #### Beispielantwort (Einzelnes Gerät) [Section titled “Beispielantwort (Einzelnes Gerät)”](#beispielantwort-einzelnes-gerät) ```json { "device_id": "device_456", "custom_id": "test-device-1", "version": 1, "version_name": "1.0.0", "app_id": "app_123", "platform": "ios", "plugin_version": "5.0.0", "os_version": "17.0", "version_build": "1", "is_prod": true, "is_emulator": false, "updated_at": "2024-01-01T00:00:00Z", "channel": "production" } ``` ### DELETE [Section titled “DELETE”](#delete) `https://api.capgo.app/device/` Trennen Sie ein Gerät von seiner Kanalüberschreibung. Dies setzt das Gerät zurück, um seinen Standardkanal zu verwenden. Gerät löschen Gerätedaten werden für **90 Tage** ab der letzten Geräteaktivität aufbewahrt. Geräte, die sich in diesem Zeitraum nicht mit Capgo verbunden haben, werden automatisch aus dem System entfernt. Aktive Geräte werden kontinuierlich verfolgt, wenn sie nach Updates suchen. Sie können ein Gerät mit diesem Endpunkt nicht löschen, es betrifft nur die Kanalüberschreibung. #### Query Parameters [Section titled “Query Parameters”](#query-parameters) ```typescript interface Device { device_id: string app_id: string } ``` #### Beispielanfrage [Section titled “Beispielanfrage”](#beispielanfrage-1) ```bash curl -X DELETE \ -H "authorization: your-api-key" \ -H "Content-Type: application/json" \ -d '{ "app_id": "app_123", "device_id": "device_456" }' \ https://api.capgo.app/device/ ``` #### Erfolgsantwort [Section titled “Erfolgsantwort”](#erfolgsantwort-1) ```json { "status": "ok" } ``` ## Fehlerbehandlung [Section titled “Fehlerbehandlung”](#fehlerbehandlung) Häufige Fehlerszenarien und ihre Antworten: ```json // Device not found { "error": "Device not found", "status": "KO" } // Invalid version { "error": "Version not found", "status": "KO" } // Invalid channel { "error": "Channel not found", "status": "KO" } // Permission denied { "error": "Insufficient permissions to manage devices", "status": "KO" } ``` ## Häufige Anwendungsfälle [Section titled “Häufige Anwendungsfälle”](#häufige-anwendungsfälle) 1. **Beta Device Registration** ```typescript { "app_id": "app_123", "device_id": "device_456", "channel": "beta" } ``` 2. **Version Override** ```typescript { "app_id": "app_123", "device_id": "device_456", "version_id": "1.1.0" } ``` 3. **Reset to Default Channel** ```typescript // Use DELETE endpoint to remove overrides ``` ## Tipps zur Geräteverwaltung [Section titled “Tipps zur Geräteverwaltung”](#tipps-zur-geräteverwaltung) 1. **Überwachung**: Überprüfen Sie regelmäßig den Gerätestatus und die Versionsverteilung 2. **Testen**: Verwenden Sie benutzerdefinierte IDs, um Testgeräte einfach zu identifizieren 3. **Fehlerbehebung**: Verfolgen Sie Geräte-Updates und Kanalzuweisungen 4. **Versionskontrolle**: Überwachen Sie native App-Versionen, um Kompatibilität sicherzustellen # Mitglieder > Umfassender Leitfaden zur Verwaltung von Organisationsmitgliedern in Capgo, einschließlich Rollen, Berechtigungen und Bewährte Methoden für Sicherheit und Zusammenarbeit. Organisationsmitglieder sind Benutzer, die Zugriff auf Ihre Capgo-Organisation haben. Jedes Mitglied hat eine spezifische Rolle, die seine Berechtigungen innerhalb der Organisation bestimmt. Die effektive Verwaltung von Mitgliedern ist entscheidend für die Aufrechterhaltung von Sicherheit und Zusammenarbeit in Ihrem Team. ## Mitgliederrollen [Section titled “Mitgliederrollen”](#mitgliederrollen) ### Reguläre Rollen [Section titled “Reguläre Rollen”](#reguläre-rollen) * **read**: Kann Ressourcen anzeigen, aber keine Änderungen vornehmen * **upload**: Kann neue Bundles hochladen und Ressourcen anzeigen * **write**: Kann Ressourcen ändern und Bundles hochladen * **admin**: Kann Organisationseinstellungen und Mitglieder verwalten * **super\_admin**: Hat volle Kontrolle über die Organisation ### Einladungsrollen [Section titled “Einladungsrollen”](#einladungsrollen) * **invite\_read**: Ausstehende Einladung für Lesezugriff * **invite\_upload**: Ausstehende Einladung für Upload-Zugriff * **invite\_write**: Ausstehende Einladung für Schreibzugriff * **invite\_admin**: Ausstehende Einladung für Admin-Zugriff * **invite\_super\_admin**: Ausstehende Einladung für Super-Admin-Zugriff ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) 1. **Rollenzuweisung**: Befolgen Sie das Prinzip der geringsten Privilegien bei der Zuweisung von Rollen 2. **Regelmäßige Überprüfungen**: Überprüfen Sie regelmäßig den Mitgliederzugriff und entfernen Sie ungenutzte Konten 3. **Onboarding**: Haben Sie einen klaren Prozess für das Hinzufügen neuer Mitglieder und die Zuweisung von Rollen 4. **Offboarding**: Entfernen Sie umgehend den Zugriff für Mitglieder, die die Organisation verlassen ## Endpunkte [Section titled “Endpunkte”](#endpunkte) ### POST [Section titled “POST”](#post) `https://api.capgo.app/organization/members/` Fügen Sie ein neues Mitglied zu einer Organisation hinzu oder aktualisieren Sie die Rolle eines bestehenden Mitglieds. Beachten Sie, dass Sie nur Benutzer einladen können, die bereits ein Capgo-Konto haben - die E-Mail muss einem bestehenden Capgo-Benutzer entsprechen. #### Request Body [Section titled “Request Body”](#request-body) ```typescript interface MemberCreate { orgId: string email: string role: "read" | "upload" | "write" | "admin" | "super_admin" } ``` #### Beispielanfrage [Section titled “Beispielanfrage”](#beispielanfrage) ```bash curl -X POST \ -H "authorization: your-api-key" \ -H "Content-Type: application/json" \ -d '{ "orgId": "org_123", "email": "newmember@example.com", "role": "write" }' \ https://api.capgo.app/organization/members/ ``` #### Erfolgsantwort [Section titled “Erfolgsantwort”](#erfolgsantwort) ```json { "status": "OK", "data": { "uid": "user_789", "email": "newmember@example.com", "role": "invite_write", "image_url": null } } ``` Hinweise: * Beim Hinzufügen eines neuen Mitglieds erhält dieses eine Einladungs-E-Mail. Seine Rolle wird mit “invite\_” vorangestellt, bis es die Einladung annimmt. * Der Benutzer muss bereits ein Capgo-Konto haben, bevor er eingeladen werden kann. Wenn er kein Konto hat, sollte er zuerst eines unter erstellen ### GET [Section titled “GET”](#get) `https://api.capgo.app/organization/members/` Abrufen aller Mitglieder einer Organisation. #### Query-Parameter [Section titled “Query-Parameter”](#query-parameter) ```typescript interface MemberQuery { orgId: string } ``` #### Antworttyp [Section titled “Antworttyp”](#antworttyp) ```typescript interface Member { uid: string; email: string; image_url: string; role: "invite_read" | "invite_upload" | "invite_write" | "invite_admin" | "invite_super_admin" | "read" | "upload" | "write" | "admin" | "super_admin"; } ``` #### Beispielanfrage [Section titled “Beispielanfrage”](#beispielanfrage-1) ```bash curl -H "authorization: your-api-key" \ "https://api.capgo.app/organization/members/?orgId=org_123" ``` #### Beispielantwort [Section titled “Beispielantwort”](#beispielantwort) ```json { "data": [ { "uid": "user_123", "email": "john@example.com", "image_url": "https://example.com/avatar.png", "role": "admin" }, { "uid": "user_456", "email": "jane@example.com", "image_url": "https://example.com/avatar2.png", "role": "write" }, { "uid": "user_789", "email": "bob@example.com", "image_url": null, "role": "invite_read" } ] } ``` ### DELETE [Section titled “DELETE”](#delete) `https://api.capgo.app/organization/members/` Entfernen Sie ein Mitglied aus einer Organisation. Dies widerruft sofort seinen Zugriff. #### Request Body [Section titled “Request Body”](#request-body-1) ```typescript interface MemberDelete { orgId: string email: string } ``` #### Beispielanfrage [Section titled “Beispielanfrage”](#beispielanfrage-2) ```bash curl -X DELETE \ -H "authorization: your-api-key" \ -H "Content-Type: application/json" \ -d '{ "orgId": "org_123", "email": "user@example.com" }' \ https://api.capgo.app/organization/members/ ``` #### Erfolgsantwort [Section titled “Erfolgsantwort”](#erfolgsantwort-1) ```json { "status": "OK" } ``` ## Fehlerbehandlung [Section titled “Fehlerbehandlung”](#fehlerbehandlung) Häufige Fehlerszenarien und ihre Antworten: ```json // Mitglied nicht gefunden { "error": "Member not found", "status": "KO" } // Ungültige Rolle { "error": "Invalid role specified", "status": "KO" } // Berechtigung verweigert { "error": "Insufficient permissions to manage members", "status": "KO" } // Letzter Admin kann nicht entfernt werden { "error": "Cannot remove the last admin from the organization", "status": "KO" } // Ungültige E-Mail { "error": "Invalid email format", "status": "KO" } // Mitglied existiert bereits { "error": "Member already exists in organization", "status": "KO" } ``` ## Häufige Anwendungsfälle [Section titled “Häufige Anwendungsfälle”](#häufige-anwendungsfälle) 1. **Team-Erweiterung**: Hinzufügen neuer Teammitglieder mit geeigneten Rollen 2. **Zugriffskontrolle**: Verwaltung von Mitgliederberechtigungen bei sich ändernden Verantwortlichkeiten 3. **Sicherheitsaudit**: Regelmäßige Überprüfung der Mitgliederliste und Rollen 4. **Team-Umstrukturierung**: Aktualisierung von Rollen bei organisatorischen Änderungen # Organizations > Leitfaden zur Verwaltung von Organisationen in Capgo, der die Erstellung, Einstellungen, Aktualisierungen und den Abruf von Organisationsdetails umfasst. Organisationen sind die Entitäten der obersten Ebene in Capgo. Sie ermöglichen Ihnen, Apps, Teammitglieder und Ressourcen unter einem einzigen Dach zu gruppieren. Jede Organisation kann mehrere Mitglieder mit unterschiedlichen Rollen und Berechtigungen haben. ## Häufige Anwendungsfälle [Section titled “Häufige Anwendungsfälle”](#häufige-anwendungsfälle) * Erstellen einer neuen Organisation für Ihr Unternehmen * Organisationseinstellungen verwalten * Aktualisierung der Organisationsinformationen * Abrufen von Organisationsdetails ## Endpunkte [Section titled “Endpunkte”](#endpunkte) ### GET [Section titled “GET”](#get) `https://api.capgo.app/organization/` Rufen Sie Organisationsinformationen ab. Wenn `orgId` in den Parametern angegeben ist, wird eine einzelne Organisation zurückgegeben. Andernfalls werden alle zugänglichen Organisationen zurückgegeben. #### Abfrageparameter [Section titled “Abfrageparameter”](#abfrageparameter) * `orgId` (optional): Die ID der spezifischen abzurufenden Organisation #### Antworttyp [Section titled “Antworttyp”](#antworttyp) ```typescript interface Organization { id: string created_by: string created_at: string updated_at: string logo: string | null name: string management_email: string customer_id: string | null } ``` #### Beispielanfrage [Section titled “Beispielanfrage”](#beispielanfrage) ```bash # Get all organizations curl -H "authorization: your-api-key" https://api.capgo.app/organization/ # Get specific organization curl -H "authorization: your-api-key" https://api.capgo.app/organization/?orgId=org_123 ``` #### Beispielantwort [Section titled “Beispielantwort”](#beispielantwort) ```json { "data": { "id": "org_123", "name": "My Company", "created_at": "2024-01-01T00:00:00Z", "updated_at": "2024-01-01T00:00:00Z", "logo": "https://example.com/logo.png", "management_email": "admin@example.com", "customer_id": "cus_123" } } ``` ### POST [Section titled “POST”](#post) `https://api.capgo.app/organization/` Erstellen Sie eine neue Organisation. #### Anforderungstext [Section titled “Anforderungstext”](#anforderungstext) ```typescript interface OrganizationCreate { name: string } ``` #### Beispielanfrage [Section titled “Beispielanfrage”](#beispielanfrage-1) ```bash curl -X POST \ -H "authorization: your-api-key" \ -H "Content-Type: application/json" \ -d '{ "name": "New Organization" }' \ https://api.capgo.app/organization/ ``` #### Beispielantwort [Section titled “Beispielantwort”](#beispielantwort-1) ```json { "status": "Organization created", "id": "org_456" } ``` ### PUT [Section titled “PUT”](#put) `https://api.capgo.app/organization/` Aktualisieren Sie eine vorhandene Organisation. Erfordert die Rolle eines Administrators. #### Anforderungstext [Section titled “Anforderungstext”](#anforderungstext-1) ```typescript interface OrganizationUpdate { orgId: string logo?: string name?: string management_email?: string } ``` #### Beispielanfrage [Section titled “Beispielanfrage”](#beispielanfrage-2) ```bash curl -X PUT \ -H "authorization: your-api-key" \ -H "Content-Type: application/json" \ -d '{ "orgId": "org_123", "name": "New Company Name", "management_email": "newemail@example.com" }' \ https://api.capgo.app/organization/ ``` #### Beispielantwort [Section titled “Beispielantwort”](#beispielantwort-2) ```json { "status": "Organization updated", "data": { "id": "org_123", "name": "New Company Name", "management_email": "newemail@example.com" } } ``` ### LÖSCHEN [Section titled “LÖSCHEN”](#löschen) `https://api.capgo.app/organization/` Löschen Sie eine vorhandene Organisation. Erfordert die Rolle eines Administrators. Diese Aktion ist irreversibel und entfernt alle zugehörigen Apps, Bundles (Versionen) und Ressourcen. #### Abfrageparameter [Section titled “Abfrageparameter”](#abfrageparameter-1) * `orgId`: Die ID der zu löschenden Organisation #### Beispielanfrage [Section titled “Beispielanfrage”](#beispielanfrage-3) ```bash curl -X DELETE \ -H "authorization: your-api-key" \ https://api.capgo.app/organization/?orgId=org_123 ``` #### Beispielantwort [Section titled “Beispielantwort”](#beispielantwort-3) ```json { "status": "Organization deleted", "id": "org_123" } ``` ## Fehlerbehandlung [Section titled “Fehlerbehandlung”](#fehlerbehandlung) Häufige Fehlerszenarien und ihre Antworten: ```json // Invalid API key { "error": "Invalid API key", "status": "KO" } // Missing required field { "error": "Name is required", "status": "KO" } // Insufficient permissions { "error": "Admin role required", "status": "KO" } ``` ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) 1. **Benennung**: Verwenden Sie klare, aussagekräftige Namen für Organisationen 2. **Rollen**: Weisen Sie den Teammitgliedern entsprechende Rollen zu 3. **E-Mail**: Verwenden Sie eine Gruppen-E-Mail für „management\_email“, um Probleme mit persönlichen E-Mail-Änderungen zu vermeiden 4. **Logo**: Hosten Sie Logos auf einem zuverlässigen CDN und verwenden Sie HTTPS-URLs # Statistiken > Ausführlicher Leitfaden zur Statistik von API-Endpunkten, der Einblicke in monatliche aktive Benutzer (MAU), Speicher und Bandbreitenmetriken für Apps und Organisationen bietet. Die Statistiken-Endpunkte bieten detaillierte Analysen zu Ihren Apps und Organisationen. Sie können monatlich aktive Benutzer (MAU), Speichernutzung und Bandbreitenverbrauch über verschiedene Zeiträume hinweg verfolgen. Diese Daten sind für die Überwachung des App-Wachstums, der Ressourcennutzung und der Planungskapazität unerlässlich. ## Die Metriken verstehen [Section titled “Die Metriken verstehen”](#die-metriken-verstehen) * **MAU (monatlich aktive Benutzer)**: Anzahl der eindeutigen Geräte, die in den letzten 30 Tagen auf Ihre App zugegriffen haben * **Speicher**: Gesamtgröße aller gespeicherten Bundles und Ressourcen in Bytes * **Bandbreite**: Gesamtdatenübertragung für Bundle-Downloads in Bytes ## Bewährte Methoden [Section titled “Bewährte Methoden”](#bewährte-methoden) 1. **Regelmäßige Überwachung**: Überprüfen Sie die Statistiken regelmäßig, um Wachstum und Nutzungsmuster zu verfolgen 2. **Ressourcenplanung**: Verwenden Sie Speicher- und Bandbreitenmetriken für die Kapazitätsplanung 3. **Benutzerengagement**: Verfolgen Sie MAU, um Trends beim Benutzerengagement zu verstehen 4. **Kostenmanagement**: Überwachen Sie die Ressourcennutzung, um die Kosten zu optimieren ## Endpunkte [Section titled “Endpunkte”](#endpunkte) ### GET /statistics/app/:app\_id/ [Section titled “GET /statistics/app/:app\_id/”](#get-statisticsappapp_id) Rufen Sie Statistiken für eine bestimmte App ab. Dieser Endpunkt ist nützlich für die Überwachung der Leistung einzelner Apps. #### Abfrageparameter [Section titled “Abfrageparameter”](#abfrageparameter) ```typescript interface StatsQuery { from: Date // Start date for the statistics (format: YYYY-MM-DD) to: Date // End date for the statistics (format: YYYY-MM-DD) } ``` #### Beispielanfrage [Section titled “Beispielanfrage”](#beispielanfrage) ```bash curl -H "authorization: your-api-key" \ "https://api.capgo.app/statistics/app/com.demo.app/?from=2024-01-01&to=2024-02-01" ``` #### Beispielantwort [Section titled “Beispielantwort”](#beispielantwort) ```json [ { "date": "2024-01-01", "mau": 1500, "storage": 536870912, // 512MB in bytes "bandwidth": 1073741824 // 1GB in bytes }, { "date": "2024-01-02", "mau": 1550, "storage": 537919488, // 513MB in bytes "bandwidth": 1074790400 // 1.01GB in bytes } ] ``` ### GET /statistics/org/:org\_id/ [Section titled “GET /statistics/org/:org\_id/”](#get-statisticsorgorg_id) Rufen Sie Statistiken für eine bestimmte Organisation ab. Nützlich für die Überwachung der Nutzung auf Organisationsebene. #### Abfrageparameter [Section titled “Abfrageparameter”](#abfrageparameter-1) ```typescript interface StatsQuery { from: Date // Start date for the statistics (format: YYYY-MM-DD) to: Date // End date for the statistics (format: YYYY-MM-DD) breakdown: boolean // default false, optional if true it return the breakdown by app noAccumulate: boolean // default false, optional if true it will not accumulate data and just return day by day result } ``` #### Beispielanfrage [Section titled “Beispielanfrage”](#beispielanfrage-1) ```bash curl -H "authorization: your-api-key" \ "https://api.capgo.app/statistics/org/046a36ac-e03c-4590-9257-bd6c9dba9ee8/?from=2024-01-01&to=2024-02-01" ``` #### Beispielantwort [Section titled “Beispielantwort”](#beispielantwort-1) ```json [ { "date": "2024-01-01", "mau": 10000, "storage": 536870912, // 512MB in bytes "bandwidth": 1073741824 // 1GB in bytes }, { "date": "2024-01-02", "mau": 10200, "storage": 537919488, // 513MB in bytes "bandwidth": 1074790400 // 1.01GB in bytes } ] ``` ### GET /statistics/user/ [Section titled “GET /statistics/user/”](#get-statisticsuser) Erhalten Sie aggregierte Statistiken für alle Organisationen, auf die Sie Zugriff haben. Perfekt für die allgemeine Nutzungsüberwachung. #### Abfrageparameter [Section titled “Abfrageparameter”](#abfrageparameter-2) ```typescript interface StatsQuery { from: Date // Start date for the statistics (format: YYYY-MM-DD) to: Date // End date for the statistics (format: YYYY-MM-DD) } ``` #### Beispielanfrage [Section titled “Beispielanfrage”](#beispielanfrage-2) ```bash curl -H "authorization: your-api-key" \ "https://api.capgo.app/statistics/user/?from=2024-01-01&to=2024-02-01" ``` #### Beispielantwort [Section titled “Beispielantwort”](#beispielantwort-2) ```json [ { "date": "2024-01-01", "mau": 25000, "storage": 1073741824, // 1GB in bytes "bandwidth": 2147483648 // 2GB in bytes }, { "date": "2024-01-02", "mau": 25500, "storage": 1074790400, // 1.01GB in bytes "bandwidth": 2148532224 // 2.01GB in bytes } ] ``` ### GET /statistics/app/:app\_id/bundle\_usage [Section titled “GET /statistics/app/:app\_id/bundle\_usage”](#get-statisticsappapp_idbundle_usage) Erhalten Sie Bundle-Nutzungsstatistiken für eine bestimmte App, die die Verteilung von Bundles (Versionen) unter Benutzern über einen bestimmten Zeitraum anzeigen. #### Abfrageparameter [Section titled “Abfrageparameter”](#abfrageparameter-3) ```typescript interface BundleUsageQuery { from: Date // Start date for the statistics (format: YYYY-MM-DD) to: Date // End date for the statistics (format: YYYY-MM-DD) } ``` #### Beispielanfrage [Section titled “Beispielanfrage”](#beispielanfrage-3) ```bash curl -H "authorization: your-api-key" \ "https://api.capgo.app/statistics/app/com.demo.app/bundle_usage?from=2024-01-01&to=2024-02-01" ``` #### Beispielantwort [Section titled “Beispielantwort”](#beispielantwort-3) ```json { "labels": ["2024-01-01", "2024-01-02", "2024-01-03"], "datasets": [ { "label": "1.0.0", "data": [60.5, 58.2, 55.3] }, { "label": "1.0.1", "data": [39.5, 41.8, 44.7] } ] } ``` ## Fehlerbehandlung [Section titled “Fehlerbehandlung”](#fehlerbehandlung) Häufige Fehlerszenarien und ihre Antworten: ```json // Invalid body { "status": "Invalid body", "error": "Invalid date format or missing parameters" } // Permission denied { "status": "You can't access this app", "error": "Insufficient permissions to access statistics" } // Permission denied for organization { "status": "You can't access this organization", "error": "Insufficient permissions to access organization statistics" } // No organizations found for user statistics { "status": "No organizations found", "error": "No organizations found" } // Internal server error { "status": "Cannot get app statistics", "error": "Internal server error message" } ``` ## Häufige Anwendungsfälle [Section titled “Häufige Anwendungsfälle”](#häufige-anwendungsfälle) 1. **Wachstumsverfolgung**: Überwachen Sie das MAU-Wachstum im Laufe der Zeit 2. **Ressourcenoptimierung**: Verfolgen Sie die Speicher- und Bandbreitennutzung, um die Kosten zu optimieren 3. **Kapazitätsplanung**: Nutzen Sie Trends, um den zukünftigen Ressourcenbedarf zu planen 4. **Nutzungsberichte**: Erstellen Sie regelmäßige Nutzungsberichte für Stakeholder 5. **Bundle (version) Distribution Analysis**: Understand how users are distributed across different app bundles (versions) with bundle usage statistics ## Tipps zur Analyse [Section titled “Tipps zur Analyse”](#tipps-zur-analyse) 1. **Zeiträume vergleichen**: Sehen Sie sich die Trends im Monats- oder Jahresvergleich an 2. **Track-Verhältnisse**: Überwachen Sie die Bandbreite pro Benutzer oder Speicher pro App 3. **Benachrichtigungen festlegen**: Erstellen Sie Benachrichtigungen für ungewöhnliche Nutzungsspitzen 4. **Regelmäßige Backups**: Exportieren Sie regelmäßig Statistiken zur historischen Analyse 5. **Bundle-(Versions-)Annahme**: Verwenden Sie die Bundle-Nutzung, um die Akzeptanzraten neuer Bundles (Versionen) zu verfolgen. # Migration von AppFlow zu Capgo > Vollständiger Leitfaden zur Migration Ihrer App von Ionic AppFlow zu Capgo ## AppFlow-Konfigurationsreferenz [Section titled “AppFlow-Konfigurationsreferenz”](#appflow-konfigurationsreferenz) Notieren Sie vor der Migration Ihre aktuelle AppFlow-Konfiguration in `capacitor.config.ts`: ```typescript import { CapacitorConfig } from '@capacitor/cli'; const config: CapacitorConfig = { plugins: { LiveUpdates: { appId: 'your-app-id', channel: 'Production', autoUpdateMethod: 'background', // oder 'always latest', 'force update' maxVersions: 2 } } }; ``` Diese Konfiguration hilft Ihnen, AppFlow-Funktionen den Capgo-Äquivalenten zuzuordnen. ## Warum zu Capgo migrieren? [Section titled “Warum zu Capgo migrieren?”](#warum-zu-capgo-migrieren) Mit der Ankündigung der Einstellung von Ionic AppFlow bietet die Migration zu Capgo einen nahtlosen Übergang für Ihren Mobile-App-Entwicklungsworkflow. Capgo bietet verbesserte Funktionen, bessere Leistung und erhebliche Kosteneinsparungen bei Beibehaltung aller wichtigen Funktionalitäten. ### Hauptvorteile [Section titled “Hauptvorteile”](#hauptvorteile) * Schnellere Update-Bereitstellung (< 1 Minute vs. 10 Minuten) * Günstigere Preisgestaltung (14$/Monat vs. 499$/Monat) * Ende-zu-Ende-Verschlüsselung in allen Plänen enthalten * Verbesserte Kontrolle über Update-Kanäle * Umfassende CI/CD-Integrationsoptionen ## Migrationsschritte [Section titled “Migrationsschritte”](#migrationsschritte) ### 1. Live-Updates Migration [Section titled “1. Live-Updates Migration”](#1-live-updates-migration) #### Vorherige Abhängigkeiten entfernen [Section titled “Vorherige Abhängigkeiten entfernen”](#vorherige-abhängigkeiten-entfernen) ```bash npm uninstall @ionic/appflow ``` #### Capgo installieren [Section titled “Capgo installieren”](#capgo-installieren) ```bash npm install @capgo/capacitor-updater npx cap sync ``` #### Konfiguration aktualisieren [Section titled “Konfiguration aktualisieren”](#konfiguration-aktualisieren) Fügen Sie die Capgo-Konfiguration zu Ihrer `capacitor.config.json` hinzu: ```json { "plugins": { "CapacitorUpdater": { "autoUpdate": true } } } ``` ### 2. CI/CD Migration [Section titled “2. CI/CD Migration”](#2-cicd-migration) Capgo bietet flexible CI/CD-Optionen: #### Option 1: Bestehende CI/CD verwenden [Section titled “Option 1: Bestehende CI/CD verwenden”](#option-1-bestehende-cicd-verwenden) Folgen Sie unseren detaillierten Tutorials zur Einrichtung von CI/CD mit beliebten Plattformen: * [iOS Build-Einrichtung](https://capgo.app/blog/automatic-capacitor-ios-build-github-action/) * [Android Build-Einrichtung](https://capgo.app/blog/automatic-capacitor-android-build-github-action/) * [GitHub Actions Integration](https://capgo.app/blog/automatic-capacitor-ios-build-github-action/) #### Option 2: CI/CD als Service [Section titled “Option 2: CI/CD als Service”](#option-2-cicd-als-service) Lassen Sie uns Ihre CI/CD-Einrichtung mit unserem [verwalteten Service](https://cal.com/team/capgo/mobile-ci-cd-done-for-you) übernehmen. ### 3. Kanal-Einrichtung [Section titled “3. Kanal-Einrichtung”](#3-kanal-einrichtung) 1. Kanäle im Capgo-Dashboard erstellen: ```bash npx @capgo/cli channel create production npx @capgo/cli channel create staging ``` 2. Kanal-Einstellungen konfigurieren: ```bash # Produktionskanal einrichten npx @capgo/cli channel update production --no-downgrade --no-upgrade # Staging-Kanal einrichten npx @capgo/cli channel update staging ``` ### 4. Migration testen [Section titled “4. Migration testen”](#4-migration-testen) 1. **Live-Updates testen** ```bash # Test-Bundle erstellen und hochladen npx @capgo/cli bundle create --channel staging ``` 2. **Update-Empfang überprüfen** * App auf Testgerät installieren * Überprüfen, ob Updates korrekt empfangen werden * Update-Installationsprozess verifizieren * Wiederherstellungsfunktion testen ## Fehlerbehebung [Section titled “Fehlerbehebung”](#fehlerbehebung) ### Häufige Probleme [Section titled “Häufige Probleme”](#häufige-probleme) #### Updates werden nicht empfangen [Section titled “Updates werden nicht empfangen”](#updates-werden-nicht-empfangen) * Kanal-Konfiguration überprüfen * Geräte-Logs überprüfen * Netzwerkverbindung sicherstellen * Bundle-Versionsformat validieren ## Nächste Schritte [Section titled “Nächste Schritte”](#nächste-schritte) 1. [Capgo-Konto erstellen](/register/) 2. [Schnellstart-Anleitung](/docs/getting-started/quickstart/) befolgen 3. [CI/CD-Integration](/docs/getting-started/cicd-integration/) einrichten 4. [Live-Updates](/docs/live-updates/) konfigurieren Für Unternehmensteams, die während der Migration dedizierten Support benötigen, [vereinbaren Sie einen Termin mit unserem Team](https://cal.com/team/capgo/capgo-enterprise-inquiry). # Migration von Capawesome Cloud zu Capgo > Schritt-für-Schritt-Anleitung zum Wechsel von Capawesome Cloud zu Capgo mit nativer OTA-Sicherheit, Beobachtbarkeit auf Geräteebene und vollständiger Automatisierung. > ⚡️ Capgo automatisiert Kanäle, Bundle-Bereinigung, Rollbacks, Analysen und CLI-Uploads nativ. Verwenden Sie diesen Leitfaden, um die minimalen Schritte für die Migration durchzuführen und optional jedes benutzerdefinierte Verhalten nachzubilden, das Sie noch benötigen. ## Übersicht [Section titled “Übersicht”](#übersicht) 1. Sammeln Sie Ihre vorhandene Capawesome Cloud-Konfiguration (App-ID, Kanäle, Signierungsschlüssel, CLI-Tokens), damit Sie diese später archivieren oder prüfen können. 2. Installieren Sie das Capgo-Plugin, entfernen Sie das Capawesome SDK und rufen Sie `CapacitorUpdater.notifyAppReady()` auf. 3. Konfigurieren Sie optionales Verhalten (manuelle Downloads, Bundle-Pinning, Neuladen), falls Sie diese Abläufe heute verwenden. Mit Capgo müssen Sie nur unser Plugin installieren und `CapacitorUpdater.notifyAppReady()` aufrufen. Alles andere – Kanäle, Bundle-Bereinigung, Rollbacks, Analysen und CLI-Automatisierung – wird nativ gehandhabt. Die folgenden Abschnitte führen direkt durch jede Aufgabe. ## Bevor Sie beginnen [Section titled “Bevor Sie beginnen”](#bevor-sie-beginnen) * Stellen Sie sicher, dass Ihr Projekt bereits Capacitor 5 oder höher verwendet. * Installieren Sie die Capgo CLI (`npm install -g @capgo/cli`), wenn Sie Bundles von CI/CD pushen möchten. ## Schritt 1 – Capgo installieren und das Capawesome SDK entfernen [Section titled “Schritt 1 – Capgo installieren und das Capawesome SDK entfernen”](#schritt-1--capgo-installieren-und-das-capawesome-sdk-entfernen) ```bash npm uninstall @capawesome/capacitor-live-update npm install @capgo/capacitor-updater npx cap sync ``` Das ist der einzige obligatorische Austausch. Der native Code von Capgo wird mit dem Plugin ausgeliefert; keine zusätzlichen JavaScript-Helfer erforderlich. ## Schritt 2 – Minimale Konfiguration [Section titled “Schritt 2 – Minimale Konfiguration”](#schritt-2--minimale-konfiguration) Die vorherige Einrichtung erforderte die Zuordnung von Dutzenden von Optionen in `capacitor.config`. Capgo erkennt Ihr Projekt automatisch, sodass die minimale Konfiguration wie folgt aussieht: capacitor.config.ts ```ts import { CapacitorConfig } from '@capacitor/cli' const config: CapacitorConfig = { plugins: { CapacitorUpdater: { autoUpdate: true, autoDeletePrevious: true, periodCheckDelay: 10 * 60 * 1000, // optional: alle 10 Minuten prüfen }, }, } export default config ``` Alles, was Capawesome als manuelle Flags auflistet (`defaultChannel`, `autoDeleteBundles`, Aufbewahrungsrichtlinien usw.), wird über das Capgo-Dashboard oder die API verwaltet. Sie müssen diese Schlüssel nur überschreiben, wenn Sie ein Verhalten wünschen, das von den Capgo-Standards abweicht. ### Schnellreferenz zur Konfiguration [Section titled “Schnellreferenz zur Konfiguration”](#schnellreferenz-zur-konfiguration) | Capawesome-Option | Capgo-Entsprechung | Müssen Sie es setzen? | | ---------------------------- | --------------------------------------------------------------------- | --------------------------------------------------------------- | | `appId` | Wird vom Capgo-Dashboard übernommen, sobald Sie ein Projekt erstellen | Nur wenn Sie mehrere Projekte in einer Binärdatei verwenden | | `defaultChannel` | Kanal-Regeln im Dashboard/API verwaltet | Optional; die meisten Teams setzen dies serverseitig | | `autoDeleteBundles` | `autoDeletePrevious: true` (Standard) | Bereits aktiviert | | `publicKey` | In der Capgo-Konsole verwaltet | Nur wenn Sie Schlüssel manuell rotieren | | `maxVersions` / Aufbewahrung | Bundle-Aufbewahrungsrichtlinie | Zentral in Capgo konfiguriert (1 Monat Standard, 24 Monate max) | ## Schritt 3 – `notifyAppReady()` aufrufen (der einzige erforderliche Hook) [Section titled “Schritt 3 – notifyAppReady() aufrufen (der einzige erforderliche Hook)”](#schritt-3--notifyappready-aufrufen-der-einzige-erforderliche-hook) Der alte Workflow führte benutzerdefinierte Listener ein (`checkForUpdates()`, `retryDownload()`, Splash-Screen verbergen usw.). Capgo führt diese Schritte nativ durch. Die einzige API, die Sie aufrufen müssen, ist: ```ts import { CapacitorUpdater } from '@capgo/capacitor-updater' CapacitorUpdater.notifyAppReady() ``` Dies bestätigt, dass die App erfolgreich gestartet wurde. Wenn die Bestätigung nie eintrifft, führt Capgo automatisch ein Rollback des Bundles durch – kein zusätzliches JavaScript erforderlich. **Das ist alles – Capgo handhabt Hintergrundprüfungen, Splash-Sichtbarkeit und Rollbacks nativ.** Optional: Benutzerdefinierte Logik vor dem Verbergen des Splash-Screens ausführen ```ts import { CapacitorUpdater } from '@capgo/capacitor-updater' import { SplashScreen } from '@capacitor/splash-screen' CapacitorUpdater.addListener('appReady', () => { // Führen Sie bei Bedarf Diagnosen oder Protokollierung aus SplashScreen.hide() }) CapacitorUpdater.notifyAppReady() ``` ## Schritt 4 – API-Aufrufe zuordnen (meistens optional) [Section titled “Schritt 4 – API-Aufrufe zuordnen (meistens optional)”](#schritt-4--api-aufrufe-zuordnen-meistens-optional) In Capgo lassen Sie normalerweise den Auto-Updater laufen; manuelle APIs bleiben verfügbar, wenn Sie volle Kontrolle wünschen. | Capawesome Cloud | Capgo-Entsprechung | Benötigen Sie es? | | -------------------------------- | ------------------------------ | ----------------------------------------------------------------------- | | `LiveUpdate.fetchLatestBundle()` | `CapacitorUpdater.getLatest()` | Nur bei Implementierung Ihres eigenen Download-Workflows | | `LiveUpdate.downloadBundle()` | `CapacitorUpdater.download()` | Optional: natives Auto-Update lädt bereits herunter | | `LiveUpdate.setNextBundle()` | `CapacitorUpdater.next()` | Optional: Dashboard pinnt Bundles automatisch | | `LiveUpdate.reload()` | `CapacitorUpdater.reload()` | Optional; Capgo erzwingt obligatorische Bundles nach `notifyAppReady()` | | `LiveUpdate.getCurrentBundle()` | `CapacitorUpdater.current()` | Optionale Diagnose | Wenn Sie beim nativen Auto-Update-Verhalten bleiben, können Sie das Capawesome JavaScript vollständig löschen. ### Beispiele für manuelle Steuerung [Section titled “Beispiele für manuelle Steuerung”](#beispiele-für-manuelle-steuerung) **Das neueste Bundle herunterladen** Capgo ```ts import { CapacitorUpdater } from '@capgo/capacitor-updater' const downloadUpdate = async () => { const latest = await CapacitorUpdater.getLatest() if (latest?.url) { const bundle = await CapacitorUpdater.download({ url: latest.url, version: latest.version, }) console.log('Bundle heruntergeladen', bundle?.id) } } ``` Capawesome Cloud ```ts import { LiveUpdate } from '@capawesome/capacitor-live-update' const downloadUpdate = async () => { const result = await LiveUpdate.fetchLatestBundle() if (result.downloadUrl) { await LiveUpdate.downloadBundle({ bundleId: result.bundleId, url: result.downloadUrl, }) console.log('Bundle heruntergeladen') } } ``` **Das nächste Bundle festlegen** Capgo ```ts import { CapacitorUpdater } from '@capgo/capacitor-updater' const setNextBundle = async () => { await CapacitorUpdater.next({ id: 'bundle-id-123' }) } ``` Capawesome Cloud ```ts import { LiveUpdate } from '@capawesome/capacitor-live-update' const setNextBundle = async () => { await LiveUpdate.setNextBundle({ bundleId: 'bundle-id-123' }) } ``` **Das heruntergeladene Bundle sofort anwenden** Capgo ```ts import { CapacitorUpdater } from '@capgo/capacitor-updater' const applyUpdate = async () => { await CapacitorUpdater.reload() } ``` Capawesome Cloud ```ts import { LiveUpdate } from '@capawesome/capacitor-live-update' const applyUpdate = async () => { await LiveUpdate.reload() } ``` ## Schritt 5 – Update-Strategien: Wie Capgo sie handhabt [Section titled “Schritt 5 – Update-Strategien: Wie Capgo sie handhabt”](#schritt-5--update-strategien-wie-capgo-sie-handhabt) Capawesome dokumentiert drei Strategien. Hier ist, wie sie sich übersetzen: ### Hintergrund-Updates [Section titled “Hintergrund-Updates”](#hintergrund-updates) * **Vorheriger Workflow**: Im Code konfigurieren und Downloads manuell planen. * **Capgo**: Standardmäßig aktiviert (`autoUpdate: true`). Kein zusätzlicher Code erforderlich. ### Immer aktuell [Section titled “Immer aktuell”](#immer-aktuell) * **Vorheriger Workflow**: Einen `App.resume`-Listener hinzufügen, `download` aufrufen, dann `set`. * **Capgo**: Hintergrund-Auto-Update führt bereits die Prüfung nach Wiederaufnahme durch. Sie benötigen den manuellen Listener nur, wenn Sie ein benutzerdefiniertes Intervall möchten. Optional: Manuelle Wiederaufnahmeprüfung ```ts import { App } from '@capacitor/app' import { CapacitorUpdater } from '@capgo/capacitor-updater' App.addListener('resume', async () => { const latest = await CapacitorUpdater.getLatest() if (latest?.url) { const downloaded = await CapacitorUpdater.download({ url: latest.url, version: latest.version, }) if (downloaded) { await CapacitorUpdater.next({ id: downloaded.id }) } } }) ``` ### Erzwungenes Update [Section titled “Erzwungenes Update”](#erzwungenes-update) * **Vorheriger Workflow**: Eingabeaufforderungslogik verdrahten und Neuladen erzwingen. * **Capgo**: Markieren Sie das Bundle als “obligatorisch” im Dashboard und lauschen Sie dann auf das `majorAvailable`-Ereignis (wird nach `notifyAppReady()` ausgegeben), um Benutzer zum Upgrade in Ihrer App zu zwingen. ## Schritt 6 – Bundles bereitstellen [Section titled “Schritt 6 – Bundles bereitstellen”](#schritt-6--bundles-bereitstellen) Wenn Sie sich zuvor auf `capawesome live-update deploy` verlassen haben, bietet Capgo einen ähnlichen CLI-Workflow, und Sie können Bereitstellungen auch vollständig über die API automatisieren. ```bash # Einmal authentifizieren (speichert ein Token in Ihrer CI-Umgebung) capgo login # Ein neues Bundle hochladen (erkennt Plattform/Version automatisch) capgo bundle upload --path dist --channel production ``` Da Capgo die Bundle-Gesundheit automatisch verfolgt, erhalten Sie auch: * Audit-Protokolle auf Geräteebene für jede Installation. * Automatische Aufbewahrung (standardmäßig einen Monat) mit konfigurierbaren Limits bis zu 24 Monaten. * Echtzeit-Latenzmetriken unter [status.capgo.app/history](https://status.capgo.app/history). ## Migrations-Zeitplan [Section titled “Migrations-Zeitplan”](#migrations-zeitplan) * **Inventar & Installation**: 10 Minuten (`npm install`, altes Plugin entfernen). * **Konfiguration & Bereitschaft**: 5 Minuten (`notifyAppReady`). * **Plausibilitätsprüfungen**: 15 Minuten (optionale manuelle Tests oder Listener). * **Erste Bereitstellung**: 10 Minuten mit Capgo CLI oder CI-Integration. In der Praxis sind Teams in weniger als einer Stunde fertig. Wenn Sie Capawesome-Projektdetails bereitstellen, können wir sogar Kanäle und Gerätelisten für Sie importieren. ## Capgo-Support [Section titled “Capgo-Support”](#capgo-support) * **Migrations-Concierge**: Buchen Sie eine Sitzung unter [cal.com/team/capgo/demo](https://cal.com/team/capgo/demo). * **Community**: Treten Sie dem [Capgo Discord](https://discord.gg/VCXxSVjefW) bei. * **Issue-Tracker**: [github.com/Cap-go/capacitor-updater/issues](https://github.com/Cap-go/capacitor-updater/issues). Capgo ist für langfristige Zuverlässigkeit konzipiert: native Delta-Updates, verschlüsselte Bundles, automatische Rollbacks und Analysen, die kein benutzerdefiniertes JavaScript erfordern. Sobald Sie migriert haben, können Sie den wartungsintensiven Code löschen und die Plattform Updates automatisch ausführen lassen. # Von V2 auf V3 > So aktualisieren Sie von V2 auf V3 Diese Dokumentation erklärt, wie Sie auf Version 3 von auto-update aktualisieren können ## Zuerst migrieren Sie zu den aktuellsten Tools: [Section titled “Zuerst migrieren Sie zu den aktuellsten Tools:”](#zuerst-migrieren-sie-zu-den-aktuellsten-tools) ```bash npm remove -g capgo npm remove capacitor-updater npm i @capgo/cli npm i @capgo/capacitor-updater@3 npx cap sync ``` ## Entfernen Sie alle Ihre vorherigen Konfigurationen: [Section titled “Entfernen Sie alle Ihre vorherigen Konfigurationen:”](#entfernen-sie-alle-ihre-vorherigen-konfigurationen) ```json { CapacitorUpdater: { autoUpdateURL: "https", }, } ``` um nur dies übrig zu lassen: ```json { "CapacitorUpdater": { "autoUpdate": true } } ``` > ⚠️ Wenn Sie Ihren eigenen Server mit `autoUpdateURL` verwendet haben, werde ich diesen Leitfaden bald für Sie aktualisieren. Schauen Sie sich in der Zwischenzeit die neue Upload-Option `external` an, mit der Sie nur den Link Ihrer ZIP-Datei senden können, nicht den Code in der Capgo-Cloud. Dies wurde für Unternehmen mit strengen Datenschutzrichtlinien entwickelt. Im externen Modus wird der Code niemals auf dem Capgo-Server landen, wir speichern nur die URL und senden sie an das Gerät, das sie direkt herunterlädt. Auf die standardmäßige Art wird der Code gezippt und auf unserem Server gespeichert, aber wir werden ihn niemals öffnen oder anderweitig verwenden ## Was sich ändert [Section titled “Was sich ändert”](#was-sich-ändert) Alle Konfigurationen werden serverseitig für auto-update, um Ihnen mehr Kontrolle darüber zu geben, wie Sie ein Update an Benutzer senden Dies ermöglicht uns das Zurücksetzen und sogar das Bereitstellen für nur einen Benutzer mit Kanälen! Diese Einstellungen werden der Weboberfläche hinzugefügt: * Deaktivieren der Zurücksetzung unter der nativen Version * Deaktivieren von Updates über Major-Versionen > ⚠️ Diese werden standardmäßig für alle Kanäle aktiviert Dies wird auch die Notwendigkeit häufiger Plugin-Updates beseitigen, die meisten Updates werden serverseitig durchgeführt, und Sie erhalten sie ohne Änderungen auf Ihrer Seite > ⚠️ Zurücksetzen, wenn ein Update zum Standard wird. Wenn Sie es vorziehen, nicht alle heruntergeladenen Versionen beim Update aus dem Store zu entfernen, tun Sie dies: ```json { "CapacitorUpdater": { "autoUpdate": true, "resetWhenUpdate": false } } ``` ## Aktualisieren Sie Ihren Code [Section titled “Aktualisieren Sie Ihren Code”](#aktualisieren-sie-ihren-code) Aktualisieren Sie zuletzt alle Ihre Imports in JS von: ```plaintext import { CapacitorUpdater } from 'capacitor-updater' ``` zu ```plaintext import { CapacitorUpdater } from '@capgo/capacitor-updater' ``` Dann bauen Sie Ihren Code erneut `npm run build` und kopieren Sie die Assets noch einmal `npx cap copy` Sie sollten jetzt in der Lage sein, das neueste auto-update System zu testen Senden Sie Ihre Version mit: ```plaintext npx @capgo/cli@latest bundle upload ``` anstelle von ```plaintext npx capgo upload ``` ## Zukünftige Entwicklung [Section titled “Zukünftige Entwicklung”](#zukünftige-entwicklung) Derzeit wird nur der erste öffentliche Kanal verwendet, in Zukunft wird public sich für mehrere öffentliche Kanäle ändern, wenn mehr als einer eingerichtet ist ## Häufige Probleme: [Section titled “Häufige Probleme:”](#häufige-probleme) * Build-Problem nach dem Upgrade: Wenn Sie den Quellcode des Plugins bereits in Android Studio oder Xcode geöffnet haben, entfernt die Synchronisierung diese manchmal nicht, das ist die Ursache des Problems. Öffnen Sie die native IDE und entfernen Sie `capacitor-updater` manuell und führen Sie `npx cap sync` aus, das sollte das Problem lösen # Von V3 zu V4 > So aktualisieren Sie von V3 auf V4 ## Warum dieses Upgrade [Section titled “Warum dieses Upgrade”](#warum-dieses-upgrade) Nach vielen Gesprächen mit Ihnen in der Discord-Community habe ich festgestellt, dass der manuelle Modus zu manuell und nicht sicher in der Anwendung war. Zum Beispiel war ein automatisches Zurücksetzen nicht möglich, sodass bei einem fehlgeschlagenen Update der Benutzer die App entfernen und neu installieren musste, was eine sehr schlechte Nutzererfahrung darstellt. In der Zwischenzeit habe ich dies als Gelegenheit genutzt, Ihnen mehr Freiheit zu geben und den gesamten schlechten Code zu entfernen. ## Installation [Section titled “Installation”](#installation) `npm i @capgo/capacitor-updater@4` ## Auto-Update Cloud [Section titled “Auto-Update Cloud”](#auto-update-cloud) Wenn Sie das Grundbeispiel in Ihrer App verwenden, können Sie sicher auf die neue Version migrieren. Viel Spaß! ## Auto-Update Self-Hosted [Section titled “Auto-Update Self-Hosted”](#auto-update-self-hosted) Für Sie ist es weiterhin einfach, die Änderungen sind: * Der Name der Einstellung von `autoUpdateUrl` zu `updateUrl` * Die Endpoint-Methode wurde von `GET` zu `POST` geändert ## Manuelle Benutzer [Section titled “Manuelle Benutzer”](#manuelle-benutzer) Für Sie ist dies die bedeutendste Änderung, aber zum Besten! Sie erhalten viele Verbesserungen, lesen Sie sorgfältig. ## Änderungen [Section titled “Änderungen”](#änderungen) * `autoUpdateUrl` wird zu `updateUrl`, da diese Einstellung jetzt auch im manuellen Modus verwendet werden kann * Löschung von `cancelDelay` und `delayUpdate` zugunsten von `setDelay` * Kein `versionName` mehr in set * Änderung des `version`-Schlüssels, der in den meisten Funktionen zum Objekt `BundleInfo` zurückgegeben wurde ```typescript interface BundleInfo { id: string; version: string; downloaded: string; status: 'success' | 'error' | 'pending' | 'downloading' } ``` * Umbenennung irreführender Namen (auch wenn die Erklärung nicht klar sein kann, ist die Verwendung der neuen Namen einfach zu verstehen): * was als `version` bezeichnet wurde, wird jetzt als `bundle` bezeichnet * `id` bezieht sich auf die alte `version`, die eine zufällige Zeichenfolge von 10 Zeichen war, diese `id` ist die einzige vertrauenswürdige und eindeutige Möglichkeit, auf Ihre Bundles zuzugreifen, Beispiel `7Dfcd2RedN` * `version` bezieht sich jetzt auf den `versionName`, den Sie für ein Bundle wählen, Beispiel `100` * `updateUrl` wechselt von `get` zu `post`, da benutzerdefinierte Header für einige von Ihnen ein Problem darstellten und post logischer ist, alle vorherigen Header gehen in den Body und das Präfix `cap_` verschwindet * `versionName`-Methode wird gelöscht, zugunsten von `getId` * list gibt jetzt eine Liste von `BundleInfo` zurück * Umbenennung von `getId` in `getDeviceId` * `autoUpdate` wird standardmäßig auf true gesetzt, wenn Sie den manuellen Modus verwenden, setzen Sie es auf false ## Neuheiten [Section titled “Neuheiten”](#neuheiten) * Methode `getLatest`, diese Methode ermöglicht es Ihnen, von Ihrem mit `updateUrl` eingestellten Server die letzte verfügbare Version zu erhalten * Methode `setDelay`, die `{kind: "background" | "kill" | "nativeVersion" | "date", value?: string}` als Argument nimmt, um Verzögerungen für verschiedene Modi einzustellen * Methode `next`, um die Version beim nächsten Hintergrundprozess festzulegen, im Gegensatz zu `set`, das es sofort ausführt * Methode `isAutoUpdateEnabled`, um zu erfahren, ob Sie sich im Auto-Update-Kontext befinden * Event `downloadComplete`, wenn der Download 100% erreicht * Hinzugefügtes Pflichtfeld `version` in der Download-Methode * `notifyAppReady` wird auch im manuellen Modus obligatorisch, wenn nicht nach 10 Sekunden aufgerufen, kehrt die App zur vorherigen Version zurück ## Mitwirkende [Section titled “Mitwirkende”](#mitwirkende) [@lincolnthree](https://github.com/lincolnthree/) Vielen Dank für den Start dieser Arbeit, es wäre unmöglich gewesen, dieses Update ohne Sie zum Laufen zu bringen # Von V4 zu V5 > So aktualisieren Sie von V4 auf V5 ## Warum dieses Upgrade [Section titled “Warum dieses Upgrade”](#warum-dieses-upgrade) Diese Major-Version folgt der Capacitor Major-Version Befolgen Sie zunächst die Migrationsanleitung von Capacitor: [https://capacitorjs.com/docs/updating/5-0](https://capacitorjs.com/docs/updating/5-0/) ## Installation [Section titled “Installation”](#installation) `npm i @capgo/capacitor-updater@5` `Dann synchronisieren Sie den nativen Code-Update:` `npx cap sync` Das war’s! Ziemlich einfach! ## Manueller Modus [Section titled “Manueller Modus”](#manueller-modus) Wenn Sie selbst das Update mit getLatest geholt haben, gibt es eine kleine Änderung Wenn Sie bereits auf dem neuesten Stand sind, wird es in den catch-Block gehen Jede Antwort, die sich von verfügbaren Updates unterscheidet, wird dies tun # Von V5 auf V6 > So aktualisieren Sie von V5 auf V6 ## Warum dieses Upgrade [Section titled “Warum dieses Upgrade”](#warum-dieses-upgrade) Diese Major-Version folgt der Capacitor Major-Version Befolgen Sie zunächst die Migrationsanleitung von Capacitor: [https://capacitorjs.com/docs/updating/6-0](https://capacitorjs.com/docs/updating/6-0/) ## Installation [Section titled “Installation”](#installation) `npm i @capgo/capacitor-updater@6` `Dann synchronisieren Sie das native Code-Update:` `npx cap sync` Das war’s! Ganz einfach! # Von V6 zu V7 > Eine detaillierte Anleitung zum Übergang von Version 6 zu Version 7 des Capgo Updaters, in der die notwendigen Schritte und Überlegungen für einen erfolgreichen Upgrade-Prozess dargelegt werden, um die Kompatibilität mit den neuesten Capacitor-Funktionen und -Verbesserungen sicherzustellen. ## Warum dieses Upgrade [Section titled “Warum dieses Upgrade”](#warum-dieses-upgrade) Diese Hauptversion folgt der Capacitor-Hauptversion Folgen Sie zunächst dem Migrationsleitfaden von Capacitor: [https://capacitorjs.com/docs/updating/7-0](https://capacitorjs.com/docs/updating/7-0/) ## Installation [Section titled “Installation”](#installation) `npm i @capgo/capacitor-updater@7` `Dann das Update des nativen Codes synchronisieren:` `npx cap sync` Das war’s! Ziemlich einfach! ## Verschlüsselungsmigration [Section titled “Verschlüsselungsmigration”](#verschlüsselungsmigration) Wenn Sie die `key-v1`-Verschlüsselungsmethode verwenden, müssen Sie zum neuen Verschlüsselungssystem migrieren, da `key-v1` in V7 nicht mehr unterstützt wird. \[\[memory:96112]] Folgen Sie dem Leitfaden zur Verschlüsselungsmigration hier: [Leitfaden zur Verschlüsselungsmigration](/docs/cli/migrations/encryption/) ## Konfigurationsänderungen [Section titled “Konfigurationsänderungen”](#konfigurationsänderungen) Wir empfehlen, die folgenden Eigenschaften in Ihrer `capacitor.config`-Datei hinzuzufügen: * `capacitorUpdater` * `appId` * `version` * `autoUpdate` Diese Einstellungen sollten helfen, das Plugin und sein Verhalten besser zu verwalten. # Von V7 zu V8 > Eine detaillierte Anleitung zum Übergang von Version 7 zu Version 8 des Capgo-Updaters, mit den notwendigen Schritten und Überlegungen für einen erfolgreichen Upgrade-Prozess, der die Kompatibilität mit Capacitor 8-Features und -Verbesserungen gewährleistet. ## Warum dieses Upgrade [Section titled “Warum dieses Upgrade”](#warum-dieses-upgrade) Diese Hauptversion folgt der Hauptversion 8 von Capacitor Folgen Sie zuerst dem Migrations-Leitfaden von Capacitor: [https://capacitorjs.com/docs/updating/8-0](https://capacitorjs.com/docs/updating/8-0/) ## iOS-Mindestversionsanforderung [Section titled “iOS-Mindestversionsanforderung”](#ios-mindestversionsanforderung) Das iOS-Mindest-Deployment-Ziel wurde auf **15** erhöht, um sicherzustellen, dass iOS-Geräte mit [CVE-2022-36943](https://nvd.nist.gov/vuln/detail/CVE-2022-36943) ausgeschlossen werden. Dies ist die Mindestversion der iOS-Zip-Bibliothek, die den Sicherheits-Fix implementiert hat. ## Installation [Section titled “Installation”](#installation) `npm i @capgo/capacitor-updater@8` Synchronisieren Sie dann das native Code-Update: `npx cap sync` Das war’s! Ganz einfach! ## Was ist neu in V8 [Section titled “Was ist neu in V8”](#was-ist-neu-in-v8) Version 8 von capacitor-updater bietet vollständige Kompatibilität mit Capacitor 8 und stellt sicher, dass Ihre App die neuesten mobilen OS-Features und -Verbesserungen nutzen kann. ### Wichtige Updates [Section titled “Wichtige Updates”](#wichtige-updates) * **Capacitor 8 Kompatibilität**: Vollständige Unterstützung für die erweiterten nativen Funktionen von Capacitor 8 * **Leistungsverbesserungen**: Optimierter Update-Bereitstellungs- und Installationsprozess * **Verbesserte Stabilität**: Fehlerbehebungen und Stabilitätsverbesserungen seit v7 * **Beibehaltene API-Kompatibilität**: Keine Breaking Changes an der Plugin-API seit v7 ## Konfiguration [Section titled “Konfiguration”](#konfiguration) Die Konfiguration bleibt dieselbe wie bei v7. Ihre bestehenden `capacitor.config`-Einstellungen funktionieren weiterhin: ```typescript { plugins: { CapacitorUpdater: { appId: 'your-app-id', version: '1.0.0', autoUpdate: true, // ... weitere Einstellungen } } } ``` ## Migrations-Checkliste [Section titled “Migrations-Checkliste”](#migrations-checkliste) * [ ] Dem v8-[Migrations-Leitfaden](https://capacitorjs.com/docs/updating/8-0) von Capacitor folgen, auf Breaking Changes prüfen * [ ] iOS-Mindest-Deployment-Ziel auf 15 erhöhen (erforderlich für CVE-2022-36943-Fix) * [ ] @capgo/capacitor-updater auf ^8.0.0 aktualisieren * [ ] `npx cap sync` ausführen * [ ] Ihre App gründlich auf iOS und Android testen ## Benötigen Sie Hilfe? [Section titled “Benötigen Sie Hilfe?”](#benötigen-sie-hilfe) Wenn Sie während der Migration auf Probleme stoßen, bitte: 1. Schauen Sie in unsere [Dokumentation](/docs/live-updates/) 2. Besuchen Sie unsere [Discord-Community](https://discord.com/invite/VnYRvBfgA6) 3. Öffnen Sie ein Issue auf [GitHub](https://github.com/Cap-go/capacitor-updater/issues) # Introduzione > Einführung in die Cagpo Webapp ## Was ist das? [Section titled “Was ist das?”](#was-ist-das) Cagpo verfügt über eine umfangreiche Webapp zur Verwaltung Ihrer Projekte. Diese Webapp wurde mit Vuejs entwickelt und ist Open Source. Den Quellcode finden Sie auf [GitHub](https://github.com/Cap-go/capgo/tree/main/src/)\ Die Webapp ist [hier](https://console.capgo.app/) verfügbar\ Mit dieser Webapp können Sie [Kanäle verwalten](/docs/webapp/channels/), [Versionen verwalten](/docs/webapp/bundles/), [Geräte verwalten](/docs/webapp/devices/), [Protokolle einsehen](/docs/webapp/logs/), [Abrechnung verwalten](/docs/webapp/settings/) und [Ihr Konto verwalten](/docs/webapp/settings/) ## Wie benutze ich es? [Section titled “Wie benutze ich es?”](#wie-benutze-ich-es) Zunächst müssen Sie ein Konto erstellen oder sich anmelden. Dies ist relativ einfach und kann durch Klicken auf die Schaltfläche `Anmelden` in der Bildschirmmitte erfolgen. Nach der Anmeldung werden Sie zum Dashboard weitergeleitet. Dies ist die Hauptseite der Webapp. Von hier aus können Sie zu allen anderen Seiten navigieren. ![login page](/login.webp) ## Ein Konto erstellen [Section titled “Ein Konto erstellen”](#ein-konto-erstellen) Sie müssen auf `Kostenloses Konto erstellen` im unteren Teil des Anmeldeformulars klicken. Von da an ist es so einfach wie das Ausfüllen eines Formulars und das Befolgen der Anweisungen. ![create account](/create-account.webp) # API-Schlüssel verwalten > API-Schlüssel verwalten ## Was kann ich mit den API-Schlüsseln tun? [Section titled “Was kann ich mit den API-Schlüsseln tun?”](#was-kann-ich-mit-den-api-schlüsseln-tun) Ein API-Schlüssel kann neu generiert, entfernt oder ein neuer hinzugefügt werden ## Wie kann ich alle API-Schlüssel sehen? [Section titled “Wie kann ich alle API-Schlüssel sehen?”](#wie-kann-ich-alle-api-schlüssel-sehen) Sie müssen zur [API-Schlüssel-Seite](https://console.capgo.app/dashboard/apikeys/) gehen und dort sehen Sie alle Ihre API-Schlüssel ## Wie füge ich einen neuen API-Schlüssel hinzu? [Section titled “Wie füge ich einen neuen API-Schlüssel hinzu?”](#wie-füge-ich-einen-neuen-api-schlüssel-hinzu) Um einen neuen API-Schlüssel hinzuzufügen, klicken Sie auf den kleinen Plus-Button ![add apikey](/apikeys-add.webp) und wählen Sie dann die Berechtigungen aus, die Sie dem neuen API-Schlüssel geben möchten. Sie können auswählen: * Lesen - der API-Schlüssel kann alle Daten lesen * Upload - der API-Schlüssel kann neue Versionen über die CLI hochladen und lesen * Alles - der API-Schlüssel kann alles tun ![select perm](/apikeys-select-perm.webp) ## Wie entferne ich einen API-Schlüssel? [Section titled “Wie entferne ich einen API-Schlüssel?”](#wie-entferne-ich-einen-api-schlüssel) Um einen API-Schlüssel zu entfernen, klicken Sie auf den kleinen Papierkorb-Button ![remove apikey](/apikeys-remove.webp) Und bestätigen Sie dann das Entfernen des API-Schlüssels ## Wie generiere ich einen API-Schlüssel neu? [Section titled “Wie generiere ich einen API-Schlüssel neu?”](#wie-generiere-ich-einen-api-schlüssel-neu) Um einen API-Schlüssel neu zu generieren, klicken Sie auf den kleinen Aktualisieren-Button ![generate apikey](/apikeys-regenerate.webp) Und bestätigen Sie dann die Neugenerierung des API-Schlüssels # Bundle > Entdecken Sie, wie man Bundles verwaltet ## Alle Bundles anzeigen [Section titled “Alle Bundles anzeigen”](#alle-bundles-anzeigen) Schauen wir uns zunächst die Bundles-Seite an. Sie können darauf zugreifen, indem Sie [auf Ihre App klicken](/docs/webapp/main-page) und dann [auf den Bundles-Tab klicken](/docs/webapp/main-app-page) ![bundle list](/bundles.webp) ## Ein Bundle löschen [Section titled “Ein Bundle löschen”](#ein-bundle-löschen) Es gibt zwei Möglichkeiten, ein Bundle zu löschen: * Normal * Unsicher Die unsichere Möglichkeit zum Löschen eines Bundles wurde am 12. August 2024 zu Capgo hinzugefügt Der Unterschied zwischen den beiden Methoden liegt in der Möglichkeit, die Versionsnummer nach dem Löschen wiederzuverwenden Wenn Sie zum Beispiel eine Version `100` auf normale Weise löschen und später versuchen, eine Version `100` hochzuladen, wird dies fehlschlagen Wenn Sie diese Version über das unsichere Löschen entfernen, können Sie eine Version `100` hochladen Danger Das unsichere Löschen einer Version und erneutes Hochladen ist WIRKLICH gefährlich Es kann alle Arten von Fehlern und unvorhersehbarem Verhalten im Plugin verursachen Es sollte NIEMALS für Bundles verwendet werden, die in einem öffentlichen Kanal verwendet wurden Aus diesem Grund erfordert das unsichere Löschen einer Version “super\_admin”-Berechtigungen ## Ein bestimmtes Bundle verwalten [Section titled “Ein bestimmtes Bundle verwalten”](#ein-bestimmtes-bundle-verwalten) Sobald Sie die Liste aller Bundles sehen, klicken Sie auf das Bundle, das Sie verwalten möchten. Danach sollten Sie etwa Folgendes sehen: ![bundle info](/bundle-info.webp) Gehen wir alle Elemente auf dieser Seite durch *** Zuerst sehen Sie einen `Channel`. Dieser zeigt Ihnen an, für welchen Kanal dieses Bundle bestimmt ist. Sie können den Kanal durch Anklicken ändern Nach dem Klicken sollten Sie etwa Folgendes sehen: ![bundle change](/bundle-change.webp) `Set bundle to channel` ermöglicht es Ihnen, dieses Bundle als Standard für jeden Kanal zu verknüpfen\ `Open channel` öffnet die Kanalseite für den Kanal, für den dieses Bundle bestimmt ist\ `Unlink bundle from channel` trennt die Verknüpfung dieses Bundles vom zugehörigen Kanal (**WARNUNG**: Wenn das Bundle mit mehr als einem Kanal verknüpft ist, haben Sie keine Auswahlmöglichkeit, welcher Kanal getrennt werden soll) ### Bundle herunterladen [Section titled “Bundle herunterladen”](#bundle-herunterladen) Um ein Bundle herunterzuladen, klicken Sie auf den Wert **Zip app bundle** im Information-Tab. Ein Bestätigungsdialog erscheint, und nach Bestätigung wird das Bundle direkt auf Ihr Gerät heruntergeladen. # 채널 > Kanäle sind eine Möglichkeit, die Updates deiner App zu verwalten. Du kannst mehrere Kanäle haben und jeder Kanal kann mehrere Versionen enthalten. Dies ermöglicht es dir, mehrere Versionen deiner App gleichzeitig in Produktion zu haben. ## Kanäle verwalten [Section titled “Kanäle verwalten”](#kanäle-verwalten) Schauen wir uns zunächst die Kanäle-Seite an. Sie können darauf zugreifen, indem Sie [auf Ihre App klicken](/docs/webapp/main-page) und dann [auf den Kanäle-Tab klicken](/docs/webapp/main-app-page) ![channel list](/channels.webp) ## Einen Kanal erstellen [Section titled “Einen Kanal erstellen”](#einen-kanal-erstellen) Wie Sie sehen können, gibt es einen Plus-Button in der unteren rechten Ecke (`1` im Bild). Wenn Sie darauf klicken, öffnet sich ein Modal, in dem Sie einen neuen Kanal erstellen können. ![new channel](/new_channel_modal.webp) Nachdem Sie auf `Hinzufügen` geklickt haben, sollte ein neuer Kanal in der Liste erscheinen. ![after channel create](/post-channel-create.webp) ## Was bedeutet fehlkonfiguriert? [Section titled “Was bedeutet fehlkonfiguriert?”](#was-bedeutet-fehlkonfiguriert) Manchmal ist die Konfiguration eines Kanals nicht gültig. In diesem Fall erhalten Sie eine große Warnung und die Spalte `Fehlkonfiguriert` zeigt `Ja` für einen oder mehrere Kanäle an. Mehr dazu erfahren Sie [hier](/docs/cli/commands/#disable-updates-strategy) ## Einen Kanal löschen [Section titled “Einen Kanal löschen”](#einen-kanal-löschen) Das Löschen eines Kanals ist unkompliziert. Klicken Sie einfach auf das Papierkorb-Symbol und bestätigen Sie die Löschung (`2` im Bild) ## Einen Kanal verwalten [Section titled “Einen Kanal verwalten”](#einen-kanal-verwalten) Wenn Sie auf den Kanalnamen klicken, öffnet sich ein Modal, in dem Sie die Kanaleinstellungen verwalten können (`3` im Bild). ![Kanaleinstellungen](/channel_settings.webp) Die Kanaleinstellungsseite enthält alle Konfigurationsoptionen für Ihren Kanal. Gehen wir jede Einstellung durch. *** Erstens der `Standardkanal`-Schalter. Wenn aktiviert, wird dieser Kanal zum Standard für neue Geräte. Eine umfassende Erklärung, wie Standardkanäle funktionieren, einschließlich der Einrichtung plattformspezifischer Standards (einer für iOS, einer für Android), finden Sie im Abschnitt [Standardkanal-Konfiguration](/docs/webapp/main-app-page/#standardkanal-konfiguration). *** Zweitens die `iOS`-Einstellung. Dies ist relativ einfach. Wenn dies falsch ist, dürfen iOS-Geräte keine Updates von diesem Kanal herunterladen. Drittens ist die `Android`-Einstellung. Dies ist ähnlich wie `iOS`. Wenn dies falsch ist, dürfen Android-Geräte keine Updates von diesem Kanal herunterladen. Viertens ist die Einstellung `Auto-Downgrade unter Native deaktivieren`. Wenn dies wahr ist, wird es unmöglich sein, von einer nativen Version herabzustufen. Das bedeutet, wenn Sie eine Version `120` im App Store oder Play Store hochgeladen haben und versuchen, die Kanalversion auf `110` zu setzen, wird das Update (Downgrade) fehlschlagen. Fünftens ist `Auto-Update deaktivieren`. Diese Einstellung ist ziemlich komplex, und Sie können mehr darüber [hier](/docs/cli/commands/#disable-updates-strategy) erfahren. Was `Entwicklungs-Build erlauben` betrifft: Wenn dies wahr ist, dürfen Entwicklungs-Builds Updates von diesem Kanal herunterladen. Wenn nicht, wird jede Update-Anfrage, bei der `prod` auf false gesetzt ist, abgelehnt. Dies ist hauptsächlich für Testzwecke nützlich. Siebens ist `Emulatoren erlauben`. Wenn dies falsch ist, wird Capgo jede Update-Anfrage ablehnen, die von einem Emulator kommt. Dies ist hauptsächlich für Testzwecke nützlich. Achtens ist `Geräten erlauben, sich selbst zuzuordnen`. Wenn dies wahr ist, wird die [setChannel](/docs/plugin/api/#setchannel)-Methode verfügbar sein. Wenn dies auf falsch gesetzt ist und Sie versuchen, die [setChannel](/docs/plugin/api/#setchannel)-Methode mit diesem Kanal aufzurufen, wird der Aufruf fehlschlagen. # Geräte > Eine umfassende Anleitung zur Nutzung der Geräteseite zur effektiven Verwaltung und Konfiguration Ihrer Geräte, einschließlich Filteroptionen und detaillierter Konfigurationseinstellungen für einzelne Geräte. Datenaufbewahrung Gerätedaten werden **90 Tage** ab der letzten Geräteaktivität aufbewahrt. Geräte, die sich innerhalb dieses Zeitraums nicht mit Capgo verbunden haben, werden automatisch aus der Liste entfernt. Aktive Geräte werden kontinuierlich verfolgt, wenn sie nach Updates suchen. ## Liste aller Geräte anzeigen [Section titled “Liste aller Geräte anzeigen”](#liste-aller-geräte-anzeigen) Schauen wir uns zunächst die Geräteseite an. Sie können darauf zugreifen, indem Sie [auf Ihre App klicken](/docs/webapp/main-page) und dann [auf den Geräte-Tab klicken](/docs/webapp/main-app-page). ![Geräteliste](/devices.webp) 1. **Geräte-Tab** - Klicken Sie auf diesen Tab in der Navigationsleiste, um auf die Geräteseite zuzugreifen 2. **Filter** - Klicken Sie hier, um Geräte zu filtern nach: * **Überschreiben** - Zeigt nur Geräte an, die einen [benutzerdefinierten Kanal](/docs/plugin/api/#setchannel) oder eine benutzerdefinierte Version haben * **Benutzerdefinierte ID** - Zeigt nur Geräte mit einer benutzerdefinierten Kennung an ## Ein Gerät konfigurieren [Section titled “Ein Gerät konfigurieren”](#ein-gerät-konfigurieren) Um ein bestimmtes Gerät zu konfigurieren, klicken Sie in der Tabelle darauf. Sie sollten dann etwa Folgendes sehen: ![Ein Gerät anzeigen](/device-specific.webp) 1. **Information** - Zeigt Gerätedetails wie Geräte-ID, Letztes Update, Plattform, Plugin-Version, Version, Builtin-Version, OS-Version und ob es sich um eine Produktions-App handelt 2. **Deployments** - Zeigt den Deployment-Verlauf für dieses Gerät an 3. **Logs** - Zeigt die mit diesem Gerät verbundenen Protokolle an 4. **Kanal-Überschreibung** - Überschreibt den Kanal für dieses spezifische Gerät. Wenn eingestellt, wird dieses Gerät nicht den öffentlichen (Standard-) Kanal verwenden und nur Updates vom ausgewählten Kanal erhalten ### Benutzerdefinierte ID [Section titled “Benutzerdefinierte ID”](#benutzerdefinierte-id) Die `Benutzerdefinierte ID` ist ein sehr nützliches Feld, das Ihnen hilft, das Gerät mit einer ID zu verbinden, die Sie für Ihre eigenen Benutzer erkennen können. Verwenden Sie es, um leicht zu identifizieren, welches Gerät zu welchem Benutzer gehört. Die benutzerdefinierte ID kann nur vom Gerät selbst mit `CapacitorUpdater.setCustomId({ customId: user });` gesetzt werden. # Protokolle > Verstehen und nutzen Sie die Anwendungsprotokolle von Capgo für Diagnose und Verfolgung der Aktionen Ihrer Geräte mit den Updates. ## Anwendungsprotokolle verstehen [Section titled “Anwendungsprotokolle verstehen”](#anwendungsprotokolle-verstehen) Die Protokollseite bietet eine detaillierte Historie von Update-Ereignissen und Diagnoseinformationen für Ihre Anwendung. Dies ist entscheidend für die Überwachung des Update-Prozesses, die Fehlerbehebung und das Verständnis, wie Ihre Geräte mit Capgo interagieren. Sie können darauf zugreifen, indem Sie [auf Ihre App klicken](/docs/webapp/main-page) und dann [auf den “Protokolle”-Tab klicken (in einigen älteren Screenshots oder Dokumentationen zuvor als “Updates” bezeichnet)](/docs/webapp/main-app-page). Von dort aus sollten Sie eine Seite wie diese sehen, die eine Liste von Protokolleinträgen anzeigt: ![Protokollseite Übersicht der Hauptschnittstelle](/logs.webp) Protokollseite Übersicht Die Protokollseiten-Oberfläche umfasst: 1. **Protokolle-Tab** - Der Navigations-Tab zum Zugriff auf die Protokollansicht 2. **Neu laden-Schaltfläche** - Aktualisiert die Protokollliste mit den neuesten Daten 3. **Zeitbereich & Aktionsfilter** - Filtern Sie Protokolle nach Datumsbereich und Aktionstyp (siehe Abschnitte unten) Jede Zeile zeigt: * **Zeitstempel** (UTC) * **Geräte-ID** * **Aktionscode** (was passiert ist) * **Versionsname** (Bundle oder `builtin`) Klicken Sie auf eine Zeile, um zur Geräte-Detailseite für die vollständige Historie zu springen. ### Nach Datumsbereich filtern [Section titled “Nach Datumsbereich filtern”](#nach-datumsbereich-filtern) Sie können Protokolle nach einem bestimmten Zeitraum mit dem Datumsauswähler filtern: ![Datumsbereichsauswahl zum Filtern von Protokollen](/logs_date_range.webp) Datumsbereichsfilter 1. **Schnellauswahl** - Wählen Sie gängige Zeitbereiche: Letzte 1h, 3h, 6h oder 12h 2. **Startzeit** - Legen Sie eine benutzerdefinierte Startzeit für den Bereich fest 3. **Endzeit** - Legen Sie eine benutzerdefinierte Endzeit für den Bereich fest 4. **Kalender** - Wählen Sie bestimmte Daten mit der Kalenderansicht Klicken Sie auf “Auswählen”, um den gewählten Datumsbereich anzuwenden, oder “Abbrechen”, um die Auswahl zu schließen. ### Nach Aktionstyp filtern [Section titled “Nach Aktionstyp filtern”](#nach-aktionstyp-filtern) Das Aktionen-Dropdown ermöglicht es Ihnen, Protokolle nach bestimmten Ereignistypen zu filtern: ![Aktionsfilter-Dropdown zum Filtern von Protokollen nach Ereignistyp](/logs_filters.webp) Aktionsfilter Verfügbare Aktionsfilter umfassen: * **Geräte-Heartbeat** - Periodische Gesundheitsprüfungen von Geräten * **Versionslöschung angefordert** - Wenn eine Bundle-Version gelöscht wird * **Auf Standardversion zurücksetzen** - Wenn ein Gerät zum eingebauten Bundle zurückkehrt * **Version erfolgreich installiert** - Erfolgreiche Bundle-Installation * **Neue Version an Gerät gesendet** - Wenn Capgo ein Update an ein Gerät sendet * **Versionsinstallation fehlgeschlagen** - Wenn die Bundle-Installation fehlschlägt Verwenden Sie das Suchfeld oben, um bestimmte Aktionstypen schnell zu finden. Sie können mehrere Aktionen auswählen, um Protokolle anzuzeigen, die einem der ausgewählten Typen entsprechen. ### Beispiel-Protokollausschnitt (Testdaten) [Section titled “Beispiel-Protokollausschnitt (Testdaten)”](#beispiel-protokollausschnitt-testdaten) | Zeit (UTC) | Geräte-ID | Aktion | Version | Was es bedeutet | | ------------------- | --------- | -------------------------- | ------- | ---------------------------------------------------------------- | | 2025-01-14 10:00:01 | `A1B2C3` | `get` | 2.4.1 | Gerät hat Capgo gefragt, ob ein Update verfügbar ist | | 2025-01-14 10:00:03 | `A1B2C3` | `download_manifest_start` | 2.4.1 | Manifest-Abruf gestartet; SDK ist dabei, Dateien herunterzuladen | | 2025-01-14 10:00:07 | `A1B2C3` | `download_40` | 2.4.1 | Bundle-Download ist zu 40% abgeschlossen | | 2025-01-14 10:00:12 | `A1B2C3` | `download_zip_complete` | 2.4.1 | Zip-Download abgeschlossen | | 2025-01-14 10:00:13 | `A1B2C3` | `set` | 2.4.1 | Bundle installiert und für nächsten Start markiert | | 2025-01-14 10:05:00 | `B9C8D7` | `disableAutoUpdateToMajor` | 1.9.0 | Kanal-Richtlinie hat einen Sprung zu 2.x blockiert | | 2025-01-14 10:05:05 | `B9C8D7` | `rateLimited` | builtin | Gerät hat das Anfragelimit erreicht; SDK wartet bis zum Neustart | ## Beispiel-Protokollszenarien [Section titled “Beispiel-Protokollszenarien”](#beispiel-protokollszenarien) Um Ihnen zu helfen zu verstehen, was die Protokolle aussagen, hier sind Beispielsequenzen, die echte Geräte-Update-Verläufe zeigen: ### Erfolgreicher Update-Ablauf [Section titled “Erfolgreicher Update-Ablauf”](#erfolgreicher-update-ablauf) So sieht ein gesundes Update in Ihren Protokollen aus: | Zeit | Geräte-ID | Aktion | Version | Was es bedeutet | | -------- | ---------- | ------------------- | ------- | -------------------------------------------------------------- | | 10:00:01 | `a1b2c3d4` | `get` | 1.2.0 | Gerät hat nach Updates gesucht und Version 1.2.0 Info erhalten | | 10:00:02 | `a1b2c3d4` | `download_10` | 1.2.0 | Download gestartet, 10% abgeschlossen | | 10:00:03 | `a1b2c3d4` | `download_50` | 1.2.0 | Download bei 50% | | 10:00:05 | `a1b2c3d4` | `download_complete` | 1.2.0 | Download erfolgreich abgeschlossen | | 10:00:06 | `a1b2c3d4` | `set` | 1.2.0 | Bundle installiert und aktiviert | ### Gerät bereits aktuell [Section titled “Gerät bereits aktuell”](#gerät-bereits-aktuell) Wenn ein Gerät prüft, aber bereits die neueste Version hat: | Zeit | Geräte-ID | Aktion | Version | Was es bedeutet | | -------- | ---------- | ------- | ------- | ------------------------------------------------------------- | | 14:30:00 | `e5f6g7h8` | `noNew` | 1.2.0 | Gerät ist bereits auf der neuesten Version, kein Update nötig | ### Fehlgeschlagenes Update mit Rollback [Section titled “Fehlgeschlagenes Update mit Rollback”](#fehlgeschlagenes-update-mit-rollback) Wenn ein Update fehlschlägt und das Gerät zurückrollt: | Zeit | Geräte-ID | Aktion | Version | Was es bedeutet | | -------- | ---------- | ------------------- | ------- | ---------------------------------------------------------------------------------- | | 11:15:00 | `i9j0k1l2` | `get` | 1.3.0 | Gerät hat Update-Info erhalten | | 11:15:02 | `i9j0k1l2` | `download_complete` | 1.3.0 | Download abgeschlossen | | 11:15:03 | `i9j0k1l2` | `set` | 1.3.0 | Bundle wurde gesetzt | | 11:15:10 | `i9j0k1l2` | `update_fail` | 1.3.0 | App abgestürzt oder `notifyAppReady()` wurde nicht aufgerufen - Rollback ausgelöst | | 11:15:11 | `i9j0k1l2` | `reset` | builtin | Gerät ist zur eingebauten Version zurückgekehrt | **Aktion erforderlich**: Prüfen Sie, ob Ihre App `notifyAppReady()` nach erfolgreicher Initialisierung aufruft. Siehe [Plugin-Dokumentation](/docs/plugin/api/#notifyappready) für Details. ### Download-Fehler [Section titled “Download-Fehler”](#download-fehler) Wenn Netzwerkprobleme den Download verhindern: | Zeit | Geräte-ID | Aktion | Version | Was es bedeutet | | -------- | ---------- | --------------- | ------- | --------------------------------------------------------------------- | | 09:45:00 | `m3n4o5p6` | `get` | 1.2.0 | Gerät hat Update-Info erhalten | | 09:45:01 | `m3n4o5p6` | `download_30` | 1.2.0 | Download gestartet aber… | | 09:45:15 | `m3n4o5p6` | `download_fail` | 1.2.0 | Download fehlgeschlagen (Netzwerk-Timeout, Verbindung verloren, etc.) | **Aktion erforderlich**: Das Gerät wird beim nächsten App-Start automatisch erneut versuchen. Keine Aktion erforderlich, es sei denn, dies passiert häufig. ### Plan-Limit erreicht [Section titled “Plan-Limit erreicht”](#plan-limit-erreicht) Wenn Ihr Konto sein Geräte-Limit erreicht: | Zeit | Geräte-ID | Aktion | Version | Was es bedeutet | | -------- | ---------- | ----------------- | ------- | ------------------------------------------------------------------------------------------------- | | 16:00:00 | `q7r8s9t0` | `needPlanUpgrade` | - | Dieses Gerät erhält keine Updates, bis Sie upgraden oder der Abrechnungszyklus zurückgesetzt wird | **Aktion erforderlich**: [Upgraden Sie Ihren Plan](/docs/webapp/settings/) oder warten Sie auf den nächsten Abrechnungszyklus. ### Kanal-Konfiguration blockiert Updates [Section titled “Kanal-Konfiguration blockiert Updates”](#kanal-konfiguration-blockiert-updates) Wenn Kanal-Einstellungen ein Update verhindern: | Zeit | Geräte-ID | Aktion | Version | Was es bedeutet | | -------- | ---------- | -------------------------- | ------- | --------------------------------------------------------------------------------------------- | | 12:00:00 | `u1v2w3x4` | `disableAutoUpdateToMajor` | 2.0.0 | Gerät auf v1.x kann nicht automatisch auf v2.x aktualisieren (Major-Version-Sprung blockiert) | | 12:05:00 | `y5z6a7b8` | `disableEmulator` | 1.2.0 | Emulator erkannt, und Kanal blockiert Emulatoren | | 12:10:00 | `c9d0e1f2` | `disableDevBuild` | 1.2.0 | Dev-Build erkannt, und Kanal blockiert Dev-Builds | **Aktion erforderlich**: Dies sind beabsichtigte Schutzmaßnahmen. Wenn Sie diese Updates zulassen möchten, ändern Sie Ihre [Kanal-Einstellungen](/docs/webapp/channels/). ## Protokollcodes (Capgo Backend Enum) [Section titled “Protokollcodes (Capgo Backend Enum)”](#protokollcodes-capgo-backend-enum) Diese Codes stammen aus dem `stats_action` Enum, das von der Dashboard-API verwendet wird (`capgo/src/types/supabase.types.ts`). Wenn Sie einen neuen Code in der UI sehen, wurde er vom SDK oder Backend ausgegeben und gegen diese Liste validiert. **Happy Path & Lebenszyklus** | Code(s) | Bedeutung | | ------------------------------------------------------- | ------------------------------------------------------------------------ | | `get` | Gerät hat Capgo nach dem aktuellen Kanal-Manifest gefragt | | `download_manifest_start`, `download_manifest_complete` | Manifest-Download begann / beendet (für Delta- oder Multi-Datei-Bundles) | | `download_zip_start`, `download_zip_complete` | Zip-Archiv-Download begann / beendet | | `download_10` … `download_90` | Download-Fortschritt-Meilensteine | | `download_complete` | Gesamtes Bundle heruntergeladen | | `set` | Bundle für nächsten Start bereitgestellt | | `reset` | Gerät ist zum eingebauten Bundle zurückgekehrt | | `delete` | Bundle aus lokalem Speicher entfernt | | `uninstall` | App-Deinstallation erkannt | | `app_moved_to_foreground`, `app_moved_to_background` | App-Lebenszyklus-Ereignisse vom SDK aufgezeichnet | | `ping` | Gesundheits-/Heartbeat-Check vom Gerät | | `setChannel`, `getChannel` | Kanal überschrieben oder via SDK-Aufruf abgerufen | **Konfigurations- oder Richtlinien-Blockierungen** | Code(s) | Warum das Update blockiert wurde | | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------- | | `disableAutoUpdate`, `disableAutoUpdateToMajor`, `disableAutoUpdateToMinor`, `disableAutoUpdateToPatch`, `disableAutoUpdateMetadata`, `disableAutoUpdateUnderNative` | Kanal-Strategie verbietet diesen Semver-Sprung | | `disablePlatformIos`, `disablePlatformAndroid` | Plattform ist auf dem Kanal deaktiviert | | `disableDevBuild`, `disableEmulator` | Dev-Builds oder Emulatoren nicht erlaubt | | `cannotUpdateViaPrivateChannel`, `NoChannelOrOverride`, `channelMisconfigured` | Kanal-Auswahl oder Override fehlgeschlagen | | `missingBundle`, `cannotGetBundle` | Manifest verweist auf ein Bundle, das Capgo nicht bereitstellen kann | | `needPlanUpgrade` | Organisation hat ihr Plan-/Geräte-Limit erreicht | | `rateLimited` | Zu viele Anfragen; SDK drosselt bis zum Neustart | | `blocked_by_server_url`, `backend_refusal`, `InvalidIp` | Server-seitige Regel hat die Anfrage blockiert | **Download / Integrität / Installations-Fehler** | Code(s) | Bedeutung | | ------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | | `download_fail` | Bundle-Download fehlgeschlagen (Netzwerk- oder Antwort-Fehler) | | `download_manifest_file_fail`, `download_manifest_checksum_fail`, `download_manifest_brotli_fail` | Manifest-Datei konnte nicht abgerufen oder validiert werden | | `checksum_fail`, `checksum_required` | Integritätsprüfung fehlgeschlagen oder Prüfsumme fehlt | | `unzip_fail`, `directory_path_fail`, `canonical_path_fail`, `windows_path_fail` | Dateisystem- oder Entpack-Validierung fehlgeschlagen | | `decrypt_fail` | Entschlüsselung fehlgeschlagen (verschlüsseltes Bundle) | | `update_fail` | Bundle installiert, aber App hat nie `notifyAppReady()` aufgerufen; Rollback ausgelöst | | `download_zip_*` ohne nachfolgendes `set` | Download beendet, aber Installationsphase nie abgeschlossen | ➡️ Benötigen Sie tiefergehende Anleitung pro Code? Siehe **[Vollständige Protokollcode-Referenz und Debugging-Anleitung](/docs/plugins/updater/debugging/#understanding-cloud-logs)**. ## Mehr Details zu einem Protokoll erhalten [Section titled “Mehr Details zu einem Protokoll erhalten”](#mehr-details-zu-einem-protokoll-erhalten) Wenn Sie auf einen bestimmten Protokolleintrag klicken, werden Sie typischerweise zur [Geräte-Seite](/docs/webapp/devices/) weitergeleitet. Dies ermöglicht es Ihnen, die vollständige Historie für dieses bestimmte Gerät zu sehen, was sehr hilfreich sein kann, um gerätespezifische Probleme zu diagnostizieren oder seinen Update-Verlauf zu verstehen. # App-Seite > Was zeigt die App-Seite der Capgo-Webapp? Und was können Sie damit tun. ## Was zeigt die App-Seite? [Section titled “Was zeigt die App-Seite?”](#was-zeigt-die-app-seite) Zunächst werfen wir einen Blick auf die Hauptseite der App: In Capgo repräsentiert eine App Ihre mobile Anwendung, die mit dem Live-Update-System von Capgo integriert ist. Sie ermöglicht es Ihnen, Updates, Kanäle und Geräte nahtlos zu verwalten. ![Hauptseiten-Screenshot](/main-app-page.webp) Schauen wir uns das genauer an. Die Haupt-App-Seite ist in mehrere Schlüsselbereiche unterteilt: 1. **Obere Navigationsleiste:** Bietet Zugang zu verschiedenen Bereichen Ihrer App-Verwaltung: * **Dashboard (1):** Die aktuelle Ansicht, die wichtige Metriken und Zusammenfassungen anzeigt. * **Informationen (2):** Zeigt die Kerneinstellungen und Details Ihrer App an (siehe Abschnitt “App-Informationen” unten). * **[Bundles (3)](/docs/webapp/bundles/):** Verwalten Sie die Versionen und Releases Ihrer App. * **[Kanäle (4)](/docs/webapp/channels/):** Konfigurieren und verwalten Sie verschiedene Update-Kanäle (z.B. Produktion, Beta). * **[Geräte (5)](/docs/webapp/devices/):** Anzeigen und Verwalten registrierter Geräte, einschließlich spezifischer Überschreibungen. * **[Logs (6)](/docs/webapp/logs/):** Zugriff auf detaillierte Logs und Fehlerberichte für Ihre App. * **Builds (7):** Anzeigen und Verwalten Ihrer App-Builds. 2. **Statistik-Anzeige:** Visualisiert wichtige Metriken für den letzten Abrechnungszeitraum (Daten spiegeln die Nutzung ab Ihrem Abrechnungstag wider, nicht ab dem 1. des Monats): * **Monatlich aktive Benutzer (8):** Verfolgt die Anzahl der einzigartigen aktiven Benutzer über die Zeit. * **Speicher (9):** Zeigt den aktuellen Speicherverbrauch. * **Bandbreite (10):** Zeigt die Bandbreitennutzung an. * **Aktives Bundle (11):** Zeigt die Verteilung aktiver Geräte über verschiedene App-Bundles. 3. **Zusammenfassungskarten:** Bietet einen schnellen Überblick über wichtige Zahlen: * **Bundle-Uploads (12):** Gesamtzahl der hochgeladenen App-Bundles. * **Update-Statistiken (13):** Gesamtzahl der durchgeführten Updates, einschließlich Anfragen, Installationen und Fehler. * **Deployment-Statistiken (14):** Gesamtzahl der Deployments. 4. **Anzeigeoptionen (15-18):** Steuerelemente zum Filtern und Anzeigen der Statistiken: * **Täglich (15):** Statistiken auf täglicher Basis anzeigen. * **Kumulativ (16):** Kumulative Statistiken über die Zeit anzeigen. * **Abrechnungszeitraum (17):** Statistiken für den aktuellen Abrechnungszeitraum anzeigen. * **Datumsbereich (18):** Einen benutzerdefinierten Datumsbereich auswählen (z.B. letzte 30 Tage). ## App-Informationen [Section titled “App-Informationen”](#app-informationen) Dieser Abschnitt entspricht dem Tab “Informationen” (1) in der oberen Navigationsleiste. Hier können Sie wichtige Details und Einstellungen für Ihre Anwendung anzeigen und verwalten. ![App-Informationen-Seite](/app_info.webp) App-Informationen-Seite Hier ist eine Aufschlüsselung der verfügbaren Felder und Aktionen: * **App-Symbol (2):** Zeigt das Symbol Ihrer Anwendung an. Sie können auf die Schaltfläche “Ändern” klicken, um ein neues Symbol hochzuladen. * **App-ID (3):** Eine eindeutige Kennung für Ihre Anwendung innerhalb von Capgo. Diese ID ist nicht bearbeitbar. * **App-Name (4):** Der Anzeigename für Ihre Anwendung. Sie können diesen bei Bedarf ändern. * **Standard-Upload-Kanal (5):** Gibt den Standardkanal an, in den neue Bundles hochgeladen werden. Sie können auf das Bearbeiten-Symbol klicken, um einen anderen Standardkanal auszuwählen. * **Standard-Download-Kanal (6):** Gibt den Standardkanal an, von dem Geräte Updates herunterladen. Sie können auf das Bearbeiten-Symbol klicken, um einen anderen Standardkanal auszuwählen. Siehe den Abschnitt [Standardkanal-Konfiguration](#standardkanal-konfiguration) unten für detaillierte Informationen. * **Bundles automatisch löschen, die nicht verwendet werden (nach x Sekunden) (7):** Diese Einstellung ermöglicht es Ihnen, alte Bundles automatisch zu löschen, die für eine bestimmte Dauer (in Sekunden) nicht verwendet wurden. Setzen Sie auf `0`, um das automatische Löschen zu deaktivieren. Dies hilft bei der Speicherverwaltung und hält Ihre Bundle-Liste sauber. * **Bundle-Metadaten dem Plugin bereitstellen (8):** Wenn aktiviert, werden Bundle-Link und Kommentarfelder an das Capacitor Updater Plugin gesendet. Diese Funktion erfordert Plugin-Version 7.35.0 oder höher. * **App-Eigentum übertragen (9):** Dieser Abschnitt bietet eine Option, die Übertragung Ihrer Anwendung an eine andere Organisation zu initiieren, der Sie angehören. * **App löschen-Schaltfläche:** Löscht Ihre Anwendung dauerhaft aus Capgo. Diese Aktion ist unwiderruflich und entfernt alle zugehörigen Daten, Bundles, Kanäle und Geräte. * **Aktualisieren-Schaltfläche:** Speichert alle Änderungen, die Sie an den bearbeitbaren Feldern auf dieser Seite vorgenommen haben (z.B. App-Name, Standard-Upload-Kanal, Einstellung für automatisches Bundle-Löschen). ## Standardkanal-Konfiguration [Section titled “Standardkanal-Konfiguration”](#standardkanal-konfiguration) Der **Standard-Download-Kanal** ist eine der wichtigsten Einstellungen für Ihre App. Er bestimmt, von welchem Kanal neue Geräte Updates erhalten, wenn sie sich zum ersten Mal mit Capgo verbinden. ### Wie Standardkanäle funktionieren [Section titled “Wie Standardkanäle funktionieren”](#wie-standardkanäle-funktionieren) Wenn ein Gerät ein Update von Capgo anfordert, bestimmt das System anhand der folgenden Prioritätsreihenfolge (höchste Priorität zuerst), welcher Kanal verwendet wird: 1. **Erzwungene Gerätezuordnung**: Wenn die Geräte-ID explizit einem Kanal in den Kanaleinstellungen zugeordnet ist, gewinnt dieser Kanal immer. 2. **Cloud-Override**: Wenn dem Gerät über `setChannel()` oder manuell im Dashboard ein Kanal zugewiesen wurde, wird dieser Override verwendet. 3. **Capacitor-Config `defaultChannel`**: Falls in Ihrer `capacitor.config.*`-Datei gesetzt, wird dies für Test-/Beta-Builds verwendet. 4. **Standard-Download-Kanal**: Die hier konfigurierte Einstellung—diese wird von \~99% Ihrer Produktionsnutzer verwendet. ### Plattformspezifische Standardkanäle [Section titled “Plattformspezifische Standardkanäle”](#plattformspezifische-standardkanäle) Sie können **zwei separate Standardkanäle** konfigurieren—einen für iOS und einen für Android. Dies ist nützlich, wenn: * Sie Updates für eine Plattform vor der anderen ausrollen möchten * Sie unterschiedliche Update-Strategien pro Plattform benötigen * Sie eine neue Version auf einer Plattform testen, während die andere stabil bleibt So richten Sie plattformspezifische Standardkanäle ein: 1. Erstellen Sie zwei Kanäle (z.B. `production-ios` und `production-android`) 2. Aktivieren Sie in den Einstellungen jedes Kanals nur die relevante Plattform (iOS- oder Android-Schalter) 3. Markieren Sie beide Kanäle als “Standard” - Capgo erlaubt dies, wenn Kanäle unterschiedliche Plattformen ansprechen Wenn ein Gerät ein Update anfordert: * iOS-Geräte erhalten Updates vom iOS-aktivierten Standardkanal * Android-Geräte erhalten Updates vom Android-aktivierten Standardkanal ### Einzelner Standardkanal (Empfohlen für die meisten Apps) [Section titled “Einzelner Standardkanal (Empfohlen für die meisten Apps)”](#einzelner-standardkanal-empfohlen-für-die-meisten-apps) Für die meisten Anwendungen ist ein einzelner Standardkanal, der beide Plattformen unterstützt, der einfachste Ansatz: 1. Erstellen Sie einen Kanal (z.B. `production`) 2. Stellen Sie sicher, dass sowohl iOS- als auch Android-Schalter aktiviert sind 3. Markieren Sie ihn als Standardkanal Dies gewährleistet konsistentes Verhalten über alle Plattformen hinweg und vereinfacht Ihren Release-Workflow. ### Ändern des Standardkanals [Section titled “Ändern des Standardkanals”](#ändern-des-standardkanals) Wenn Sie den Standardkanal ändern: * **Neue Geräte** beginnen sofort, Updates vom neuen Standard zu erhalten * **Bestehende Geräte**, die bereits eine Kanalzuweisung haben (über Override oder Zwang), wechseln NICHT automatisch * Um bestehende Geräte zu verschieben, müssen Sie entweder: * `setChannel()` verwenden, um sie programmatisch zu überschreiben * Sie im Dashboard zum neuen Kanal zwingen * Ihre Geräteeinträge löschen (sie registrieren sich dann mit dem neuen Standard neu) > **Tipp**: Testen Sie Ihren neuen Standardkanal immer zuerst mit einer kleinen Gruppe erzwungener Geräte, bevor Sie ihn zum Standard für alle Nutzer machen. # Page principale > Diese Seite erklärt die Hauptseite der Webanwendung ## Was zeigt die Hauptseite an? [Section titled “Was zeigt die Hauptseite an?”](#was-zeigt-die-hauptseite-an) Schauen wir uns zunächst die Hauptseite an: ![Main page screeshot](/main-page.webp) Beginnen wir von vorne. Das Erste, was Sie sehen, sind die **Statistiken**. Diese enthalten app-spezifische Statistiken. Diese Aktualisierungen hängen von der Nutzung der App ab. Das Zweite, was Sie sehen, ist die Liste aller verfügbaren Apps. Diese ändert sich je nachdem, wie viele Apps Sie erstellt haben. Sie können auf jede App klicken, um weitere Details darüber zu sehen. Das Dritte, was Sie sehen, ist die Schaltfläche **API-Schlüssel**. Diese Schaltfläche führt Sie zur [API-Schlüssel](/docs/webapp/api-keys/) Seite. Als Nächstes sehen Sie die Schaltfläche **Capgo testen**. Diese Schaltfläche ändert sich je nach Ihrem Vor- und Nachnamen und führt Sie zur [Einstellungen](/docs/webapp/settings/) oder Support-Seite. # 二要素認証 > Verwalten Sie die Zwei-Faktor-Authentifizierung, um Ihr Capgo-Konto besser zu schützen. ## Was ist 2FA? [Section titled “Was ist 2FA?”](#was-ist-2fa) 2FA ist eine Sicherheitsmaßnahme, die verhindert, dass ein böswilliger Akteur Ihr Capgo-Konto übernehmen kann\ Dies wird erreicht, indem ein zusätzlicher Code erforderlich ist. Dieser Code wird auf Ihrem Mobiltelefon oder einem Hardware-2FA-Schlüssel gespeichert. Er ändert sich alle 30 Sekunden, was es unmöglich macht, ihn zu erraten. ## Voraussetzungen für diese Anleitung [Section titled “Voraussetzungen für diese Anleitung”](#voraussetzungen-für-diese-anleitung) * Ein Android- oder iOS-Telefon * Internetverbindung ## Was behandelt diese Anleitung? [Section titled “Was behandelt diese Anleitung?”](#was-behandelt-diese-anleitung) Diese Anleitung zeigt, wie man 2FA mit `Google Authenticator` einrichtet. Es gibt auch andere Apps für ähnliche Zwecke, aber ich kann in dieser Anleitung nicht das gesamte Thema 2FA abdecken. ## Wie richtet man 2FA ein? [Section titled “Wie richtet man 2FA ein?”](#wie-richtet-man-2fa-ein) Laden Sie zunächst die `Google Authenticator`-App herunter. Wenn Sie Android nutzen, können Sie sie aus dem [Play Store](https://play.google.com/store/apps/details/?id=comgoogleandroidappsauthenticator2) herunterladen. Auf iOS können Sie sie aus dem [App Store](https://appsapplecom/us/app/google-authenticator/id388497605/) herunterladen. Gehen Sie als Nächstes zu den [Capgo-Kontoeinstellungen](https://console.capgo.app/dashboard/settings/account/). Dort sollten Sie einen grünen Button sehen, der so aussieht: ![Button MFA](/button-mfa.webp) Klicken Sie darauf, und es sollte ein QR-Code erscheinen. ![Enable MFA](/enable-mfa.webp) Danger ⚠️ Wichtiger Hinweis:\ Teilen Sie diesen QR-Code niemals mit anderen, sonst könnten Sie aus Ihrem Konto ausgeloggt werden Öffnen Sie als Nächstes den `Google Authenticator` auf Ihrem Telefon. Folgen Sie dann diesen Schritten: Klicken Sie auf den Plus-Button ![MFA auth plus](/mfa-auth-plus.webp) Klicken Sie danach auf den Kamera-Button ![MFA scan QR](/mfa-scan-qr.webp) Dadurch wird die Kameravorschau geöffnet. Scannen Sie den QR-Code, der auf Ihrem PC angezeigt wird. Danach sollten Sie so etwas sehen: ![MFA final code](/mfa-final-code.webp) Gehen Sie dann zurück zum PC und klicken Sie auf den Verifizieren-Button ![Verify MFA](/enable-mfa-verify.webp) Dadurch öffnet sich ein Fenster zum Eingeben des 2FA-Codes. In meinem Fall ist dieser Code `095101`, aber er wird bei Ihnen anders sein\ Nachdem Sie diesen Code eingegeben haben, klicken Sie bitte auf den `verify`-Button ![MFA verify code final](/mfa-verify-final-code.webp) Wenn Sie den richtigen Code eingegeben und auf `verify` geklickt haben, sollten Sie ein Pop-up wie dieses sehen ![MFA enabled](/mfa-enabled.webp) Glückwunsch!\ Sie haben die 2FA-Authentifizierung aktiviert 🎉 ## Wie man sich mit 2FA einloggt [Section titled “Wie man sich mit 2FA einloggt”](#wie-man-sich-mit-2fa-einloggt) Beim nächsten Login in Ihr Konto sehen Sie ein Fenster wie dieses ![MFA required](/mfa-required.webp) Öffnen Sie den Authenticator und kopieren Sie Ihren Authentifizierungscode ![MFA login](/mfa-login.webp) Danger ⚠️ Wichtiger Hinweis:\ Drücken Sie `verify`, bevor sich der Code aktualisiert, sonst ändert sich der Code und der alte ist nicht mehr gültig Geben Sie dann den Code in das Login-Formular ein und drücken Sie auf `verify` ![MFA login](/mfa-final-login-verify.webp) Wenn Sie den richtigen Code eingegeben haben, sollten Sie das Capgo-Dashboard sehen ## Wie man 2FA deaktiviert [Section titled “Wie man 2FA deaktiviert”](#wie-man-2fa-deaktiviert) Um 2FA zu deaktivieren, gehen Sie bitte zu den [Capgo-Kontoeinstellungen](https://console.capgo.app/dashboard/settings/account/). Dort sollten Sie einen roten Button sehen, der so aussieht: ![MFA disable button](/mfa-disable-button.webp) Klicken Sie darauf und Sie sollten einen Bildschirm wie diesen sehen ![MFA disabled](/mfa-disable-2.webp) Drücken Sie einfach den `disable`-Button und das war’s # Organisationssicherheit > Konfigurieren Sie umfassende Sicherheitsrichtlinien für Ihre Organisation, einschließlich 2FA-Durchsetzung, Passwortrichtlinien und API-Schlüsselkontrollen zum Schutz Ihrer Apps und Daten. Capgo bietet umfassende Sicherheitskontrollen, die Organisationsadministratoren ermöglichen, Sicherheitsrichtlinien für alle Mitglieder durchzusetzen. Diese Funktionen helfen Ihnen, Compliance-Anforderungen zu erfüllen, sensible Daten zu schützen und eine starke Sicherheitslage aufrechtzuerhalten. ## Übersicht [Section titled “Übersicht”](#übersicht) Die Organisationssicherheitseinstellungen ermöglichen Super-Admins die Konfiguration von: * **Zwei-Faktor-Authentifizierung (2FA) Durchsetzung** - Alle Mitglieder zur Aktivierung von 2FA verpflichten * **Passwortrichtlinie** - Anforderungen an die Passwortkomplexität festlegen * **API-Schlüssel-Sicherheit** - Sichere API-Schlüssel und Ablaufrichtlinien durchsetzen ![Organisationssicherheitseinstellungen](/org_security.webp) Die Sicherheitsseite ist in klar gekennzeichnete Abschnitte unterteilt: 1. **Sicherheits-Tab** - Zugriff auf alle Sicherheitseinstellungen über die Seitenleiste der Organisationseinstellungen 2. **2FA-Durchsetzung** - Umschalter und Statusanzeige für Zwei-Faktor-Authentifizierungsanforderungen 3. **Passwortrichtlinie** - Konfiguration der Passwortkomplexitätsregeln für Organisationsmitglieder 4. **API-Schlüssel-Richtlinie** - Einstellungen für sichere API-Schlüssel und Ablaufanforderungen 5. **API-Schlüssel-Ablauf** - Kontrolle, ob API-Schlüssel ein Ablaufdatum haben müssen Note Nur Benutzer mit der Rolle **super\_admin** können Organisationssicherheitseinstellungen konfigurieren. Andere Mitglieder können ihren Compliance-Status einsehen, aber die Richtlinien nicht ändern. ## Zugriff auf Sicherheitseinstellungen [Section titled “Zugriff auf Sicherheitseinstellungen”](#zugriff-auf-sicherheitseinstellungen) 1. Navigieren Sie zu Ihren Organisationseinstellungen, indem Sie in der Seitenleiste auf **Einstellungen** klicken 2. Klicken Sie oben auf der Einstellungsseite auf den Tab **Organisation** 3. Wählen Sie den Tab **Sicherheit** aus der Organisationsnavigationsleiste (hervorgehoben mit einem Schild-Symbol) ## Zwei-Faktor-Authentifizierung (2FA) Durchsetzung [Section titled “Zwei-Faktor-Authentifizierung (2FA) Durchsetzung”](#zwei-faktor-authentifizierung-2fa-durchsetzung) Die 2FA-Durchsetzung erfordert, dass alle Organisationsmitglieder die Zwei-Faktor-Authentifizierung für ihre Konten aktiviert haben. Dies fügt eine kritische Sicherheitsebene hinzu, indem sowohl ein Passwort als auch ein Verifizierungscode erforderlich sind. ### Was passiert, wenn 2FA durchgesetzt wird [Section titled “Was passiert, wenn 2FA durchgesetzt wird”](#was-passiert-wenn-2fa-durchgesetzt-wird) * Mitglieder ohne 2FA werden **sofort blockiert** und können nicht auf Organisations-Apps zugreifen * Sowohl das Web-Dashboard als auch die CLI setzen diese Anforderung durch * Neue Mitglieder müssen 2FA aktivieren, bevor sie auf Organisationsressourcen zugreifen können * Das System verfolgt in Echtzeit, welche Mitglieder 2FA aktiviert haben ### Das 2FA-Status-Panel verstehen [Section titled “Das 2FA-Status-Panel verstehen”](#das-2fa-status-panel-verstehen) Die Sicherheitsseite zeigt ein umfassendes **Mitglieder 2FA-Status** Panel, das anzeigt: * **Gesamtmitglieder** - Die Gesamtzahl der Mitglieder in Ihrer Organisation * **2FA aktiviert** (grüner Indikator) - Mitglieder, die die Zwei-Faktor-Authentifizierung erfolgreich aktiviert haben * **2FA nicht aktiviert** (orangefarbener Warnindikator) - Mitglieder, die 2FA noch einrichten müssen Wenn Mitglieder 2FA nicht aktiviert haben, erscheinen sie in einem **Mitglieder ohne 2FA** Warnfeld. Dieses Feld zeigt: * Die E-Mail-Adresse jedes Mitglieds und seine Rolle in der Organisation * Eine **E-Mail-Liste kopieren** Schaltfläche, um alle betroffenen E-Mail-Adressen schnell für die Kommunikation zu kopieren ### 2FA-Durchsetzung aktivieren [Section titled “2FA-Durchsetzung aktivieren”](#2fa-durchsetzung-aktivieren) 1. Navigieren Sie zu **Organisationseinstellungen > Sicherheit** 2. Suchen Sie den Abschnitt **2FA für alle Mitglieder erforderlich** oben auf der Seite 3. Überprüfen Sie das **Mitglieder 2FA-Status** Panel, um zu sehen, welche Mitglieder betroffen sein werden 4. Wenn es Mitglieder ohne 2FA gibt, verwenden Sie die **E-Mail-Liste kopieren** Schaltfläche, um sie vor der Aktivierung zu benachrichtigen 5. Schalten Sie den Schalter neben **2FA für alle Mitglieder erforderlich** um, um die Durchsetzung zu aktivieren 6. Der Schalter zeigt den Status **Deaktiviert** oder **Aktiviert** auf der rechten Seite an Caution Vor der Aktivierung der 2FA-Durchsetzung: * Benachrichtigen Sie betroffene Mitglieder im Voraus und teilen Sie die [2FA-Einrichtungsanleitung](/docs/webapp/mfa/) * Geben Sie ihnen Zeit, 2FA für ihre Konten zu aktivieren * Mitglieder ohne 2FA verlieren sofort den Zugang, wenn Sie die Durchsetzung aktivieren * Erwägen Sie, die im Warnfeld “Mitglieder ohne 2FA” aufgelisteten Mitglieder zu kontaktieren ### CLI-Unterstützung für 2FA-Durchsetzung [Section titled “CLI-Unterstützung für 2FA-Durchsetzung”](#cli-unterstützung-für-2fa-durchsetzung) Sie können die 2FA-Durchsetzung auch über die CLI verwalten: ```shell # 2FA-Durchsetzung aktivieren npx @capgo/cli organization set YOUR_ORG_ID --enforce-2fa # 2FA-Durchsetzung deaktivieren npx @capgo/cli organization set YOUR_ORG_ID --no-enforce-2fa # Mitglieder-2FA-Status prüfen npx @capgo/cli organization members YOUR_ORG_ID ``` Für detaillierte Informationen zur 2FA-Durchsetzung siehe die [2FA-Durchsetzungsanleitung](/docs/webapp/2fa-enforcement/). ## Passwortrichtlinie [Section titled “Passwortrichtlinie”](#passwortrichtlinie) Passwortrichtlinien ermöglichen es Ihnen, Anforderungen an die Passwortkomplexität für alle Organisationsmitglieder durchzusetzen. Wenn das Passwort eines Mitglieds die Richtlinienanforderungen nicht erfüllt, muss es sein Passwort aktualisieren, bevor es auf Organisationsressourcen zugreifen kann. Der Abschnitt Passwortrichtlinie (mit Indikator **3** im Übersichtsbild markiert) bietet einen einfachen Schalter zur Durchsetzung von Passwortanforderungen in Ihrer gesamten Organisation. ### Wie die Passwortrichtlinie funktioniert [Section titled “Wie die Passwortrichtlinie funktioniert”](#wie-die-passwortrichtlinie-funktioniert) Wenn Sie die Passwortrichtlinie aktivieren: * Alle Organisationsmitglieder müssen die Anforderungen an die Passwortkomplexität erfüllen * Benutzer, die die Anforderungen nicht erfüllen, werden gesperrt, bis sie ihr Passwort aktualisieren * Die Richtlinie gilt für alle Mitglieder unabhängig von ihrer Rolle ### Passwortrichtlinie aktivieren [Section titled “Passwortrichtlinie aktivieren”](#passwortrichtlinie-aktivieren) 1. Gehen Sie zu **Organisationseinstellungen > Sicherheit** 2. Scrollen Sie nach unten, um den Abschnitt **Passwortrichtlinie** zu finden 3. Lesen Sie die Beschreibung: “Organisationsmitglieder zur Verwendung von Passwörtern verpflichten, die bestimmte Komplexitätsanforderungen erfüllen” 4. Schalten Sie den **Passwortrichtlinie durchsetzen** Schalter ein, um sie zu aktivieren 5. Die Schalterbeschreibung lautet: “Wenn aktiviert, müssen alle Organisationsmitglieder die Passwortanforderungen erfüllen, um auf die Organisation zugreifen zu können” ### Verfügbare Passwortanforderungen [Section titled “Verfügbare Passwortanforderungen”](#verfügbare-passwortanforderungen) | Einstellung | Beschreibung | Bereich | | ------------------------------ | ---------------------------------------------------------------------- | ------------- | | **Mindestlänge** | Mindestanzahl erforderlicher Zeichen | 6-128 Zeichen | | **Großbuchstabe erforderlich** | Passwort muss mindestens einen Großbuchstaben (A-Z) enthalten | Ein/Aus | | **Zahl erforderlich** | Passwort muss mindestens eine Ziffer (0-9) enthalten | Ein/Aus | | **Sonderzeichen erforderlich** | Passwort muss mindestens ein Sonderzeichen (!@#$%^&\*, etc.) enthalten | Ein/Aus | ### Mitglieder-Compliance-Tracking [Section titled “Mitglieder-Compliance-Tracking”](#mitglieder-compliance-tracking) Wenn eine Passwortrichtlinie aktiv ist, können Sie die Compliance überwachen: * **Gesamtmitglieder**: Anzahl der Mitglieder in Ihrer Organisation * **Konform**: Mitglieder, deren Passwörter die Richtlinienanforderungen erfüllen * **Nicht konform**: Mitglieder, die ihre Passwörter aktualisieren müssen Nicht konforme Mitglieder werden mit ihren E-Mail-Adressen aufgelistet. Sie können die E-Mail-Liste kopieren, um sie über die Richtlinie und erforderliche Passwortänderungen zu informieren. Tip Wenn Sie eine Passwortrichtlinie zum ersten Mal aktivieren, werden Mitglieder mit nicht konformen Passwörtern bei ihrer nächsten Anmeldung aufgefordert, ihr Passwort zu ändern. Sie werden nicht sofort gesperrt, aber ihr Zugriff kann eingeschränkt werden, bis sie die Anforderungen erfüllen. ### Bewährte Methoden für Passwortrichtlinien [Section titled “Bewährte Methoden für Passwortrichtlinien”](#bewährte-methoden-für-passwortrichtlinien) * **Mit vernünftigen Anforderungen beginnen**: Eine Mindestlänge von 10-12 Zeichen mit Groß-/Kleinschreibung und Zahlen bietet gute Sicherheit ohne übermäßig restriktiv zu sein * **Änderungen kommunizieren**: Benachrichtigen Sie Ihr Team, bevor Sie neue Passwortanforderungen aktivieren * **Übergangszeit einplanen**: Geben Sie den Mitgliedern Zeit, ihre Passwörter zu aktualisieren * **Passwort-Manager empfehlen**: Empfehlen Sie Teammitgliedern die Verwendung von Passwort-Managern zur Generierung und Speicherung sicherer Passwörter ## API-Schlüssel-Sicherheit [Section titled “API-Schlüssel-Sicherheit”](#api-schlüssel-sicherheit) Capgo bietet zwei Sicherheitskontrollen für API-Schlüssel: Durchsetzung sicherer (gehashter) API-Schlüssel und Anforderung von Ablaufdaten. Der Abschnitt API-Schlüssel-Richtlinie (mit Indikator **4** im Übersichtsbild markiert) ist durch ein Schlüssel-Symbol gekennzeichnet. ### Sichere API-Schlüssel durchsetzen [Section titled “Sichere API-Schlüssel durchsetzen”](#sichere-api-schlüssel-durchsetzen) Die erste Option im Abschnitt API-Schlüssel-Richtlinie ist **Sichere API-Schlüssel durchsetzen**. Wenn aktiviert, erfordert diese Einstellung, dass alle API-Schlüssel in Ihrer Organisation im sicheren/gehashten Format erstellt werden. Gehashte API-Schlüssel sind sicherer, weil: * Der tatsächliche Schlüsselwert niemals auf unseren Servern gespeichert wird * Nur Sie (und Ihre Systeme) Zugriff auf den vollständigen Schlüssel haben * Selbst wenn unsere Datenbank kompromittiert würde, könnten Ihre Schlüssel nicht verwendet werden Die Schalterbeschreibung lautet: “Wenn aktiviert, können nur sichere (gehashte) API-Schlüssel auf diese Organisation zugreifen. Klartext-API-Schlüssel werden abgelehnt.” Note Bestehende Legacy-API-Schlüssel (nicht gehasht) funktionieren weiterhin, aber Mitglieder können keine neuen nicht gehashten Schlüssel erstellen, wenn diese Richtlinie aktiviert ist. ### Sichere API-Schlüssel aktivieren [Section titled “Sichere API-Schlüssel aktivieren”](#sichere-api-schlüssel-aktivieren) 1. Gehen Sie zu **Organisationseinstellungen > Sicherheit** 2. Scrollen Sie nach unten, um den Abschnitt **API-Schlüssel-Richtlinie** zu finden (achten Sie auf das Schlüssel-Symbol) 3. Suchen Sie den Schalter **Sichere API-Schlüssel durchsetzen** 4. Schalten Sie den Schalter um, um die Durchsetzung sicherer API-Schlüssel zu aktivieren 5. Bestehende Schlüssel sind nicht betroffen; die Richtlinie gilt für die Erstellung neuer Schlüssel ### API-Schlüssel-Ablaufrichtlinie [Section titled “API-Schlüssel-Ablaufrichtlinie”](#api-schlüssel-ablaufrichtlinie) Die zweite Option (mit Indikator **5** im Übersichtsbild markiert) ist **API-Schlüssel-Ablauf erforderlich**. Sie können verlangen, dass alle API-Schlüssel ein Ablaufdatum haben, wodurch ihre Gültigkeitsdauer begrenzt wird. Dies ist eine bewährte Sicherheitspraxis, die: * Das Expositionsfenster begrenzt, wenn ein Schlüssel kompromittiert wird * Regelmäßige Schlüsselrotation sicherstellt * Hilft, Compliance-Anforderungen für das Credential-Management zu erfüllen Die Schalterbeschreibung lautet: “Wenn aktiviert, müssen alle API-Schlüssel für diese Organisation ein Ablaufdatum haben” ### Ablaufrichtlinie konfigurieren [Section titled “Ablaufrichtlinie konfigurieren”](#ablaufrichtlinie-konfigurieren) 1. Gehen Sie zu **Organisationseinstellungen > Sicherheit** 2. Finden Sie den Abschnitt **API-Schlüssel-Richtlinie** 3. Suchen Sie den Schalter **API-Schlüssel-Ablauf erforderlich** (unter Sichere API-Schlüssel durchsetzen) 4. Schalten Sie den Schalter um, um die Ablaufanforderung zu aktivieren 5. Nach der Aktivierung setzen Sie die **Maximalen Ablauftage** (1-365 Tage) * Dies begrenzt, wie weit in der Zukunft Ablaufdaten gesetzt werden können * Beispiel: Die Einstellung von 90 Tagen bedeutet, dass Schlüssel höchstens 90 Tage ab Erstellung ablaufen können Caution Wenn Sie die Ablaufanforderung aktivieren: * Neue API-Schlüssel müssen ein Ablaufdatum haben * Bestehende Schlüssel ohne Ablaufdatum funktionieren weiterhin * Erwägen Sie, bestehende Schlüssel zu überprüfen und solche ohne Ablaufdatum zu rotieren ### Empfohlene API-Schlüssel-Richtlinien [Section titled “Empfohlene API-Schlüssel-Richtlinien”](#empfohlene-api-schlüssel-richtlinien) | Anwendungsfall | Sichere Schlüssel | Ablauf | Max. Tage | | ------------------------- | ----------------- | ------------ | --------- | | **Entwicklung** | Empfohlen | Optional | 30-90 | | **CI/CD-Pipelines** | Erforderlich | Erforderlich | 90-180 | | **Produktion** | Erforderlich | Erforderlich | 30-90 | | **Enterprise/Compliance** | Erforderlich | Erforderlich | 30-60 | ## Compliance und Auditing [Section titled “Compliance und Auditing”](#compliance-und-auditing) Organisationssicherheitsfunktionen helfen Ihnen, verschiedene Compliance-Anforderungen zu erfüllen: | Standard | Relevante Funktionen | | ------------- | --------------------------------------------------------------------- | | **SOC 2** | 2FA-Durchsetzung, Passwortrichtlinien, API-Schlüsselkontrollen | | **ISO 27001** | Alle Sicherheitsfunktionen helfen beim Nachweis der Zugriffskontrolle | | **HIPAA** | Starke Authentifizierung und Zugriffsverwaltung | | **DSGVO** | Datenschutz durch Zugriffskontrollen | | **PCI DSS** | Multi-Faktor-Authentifizierung, sichere Passwörter | ### Compliance-Status überwachen [Section titled “Compliance-Status überwachen”](#compliance-status-überwachen) Das Sicherheits-Dashboard bietet Echtzeit-Einblick in: * Wie viele Mitglieder 2FA aktiviert haben * Passwortrichtlinien-Compliance in Ihrer Organisation * Übernahme der API-Schlüssel-Sicherheit Verwenden Sie die Funktion “E-Mail-Liste kopieren”, um Listen nicht konformer Mitglieder einfach für gezielte Kommunikation zu exportieren. ## Fehlerbehebung [Section titled “Fehlerbehebung”](#fehlerbehebung) ### ”Zugriff verweigert: Sicherheitsrichtlinie nicht erfüllt” [Section titled “”Zugriff verweigert: Sicherheitsrichtlinie nicht erfüllt””](#zugriff-verweigert-sicherheitsrichtlinie-nicht-erfüllt) **Problem**: Ein Mitglied kann nicht auf die Organisation zugreifen. **Lösungen**: 1. Prüfen Sie, ob 2FA durchgesetzt wird - das Mitglied muss [2FA aktivieren](/docs/webapp/mfa/) 2. Prüfen Sie, ob eine Passwortrichtlinie aktiv ist - das Mitglied muss sein Passwort aktualisieren 3. Überprüfen Sie den Compliance-Status des Mitglieds im Sicherheits-Dashboard ### Sicherheitsfunktionen können nicht aktiviert werden [Section titled “Sicherheitsfunktionen können nicht aktiviert werden”](#sicherheitsfunktionen-können-nicht-aktiviert-werden) **Problem**: Sicherheitsschalter sind deaktiviert oder reagieren nicht. **Lösungen**: * Stellen Sie sicher, dass Sie die Rolle **super\_admin** in der Organisation haben * Überprüfen Sie Ihre Netzwerkverbindung * Versuchen Sie, die Seite zu aktualisieren * Kontaktieren Sie den Support, wenn das Problem weiterhin besteht ### API-Schlüsselerstellung schlägt fehl [Section titled “API-Schlüsselerstellung schlägt fehl”](#api-schlüsselerstellung-schlägt-fehl) **Problem**: Neue API-Schlüssel können nicht erstellt werden. **Lösungen**: * Wenn sichere Schlüssel durchgesetzt werden, stellen Sie sicher, dass Sie den Erstellungsablauf für sichere Schlüssel verwenden * Wenn ein Ablaufdatum erforderlich ist, setzen Sie ein Ablaufdatum innerhalb des zulässigen Bereichs * Überprüfen Sie die Einstellung für die maximalen Ablauftage ## Nächste Schritte [Section titled “Nächste Schritte”](#nächste-schritte) * [2FA für Ihr Konto einrichten](/docs/webapp/mfa/) * [Details zur 2FA-Durchsetzung erfahren](/docs/webapp/2fa-enforcement/) * [API-Schlüssel verwalten](/docs/webapp/api-keys/) * [Organisationsverwaltung](/docs/webapp/organization-system/) # Organisationssystem > Eine detaillierte Anleitung zur Verwaltung und Organisation Ihrer Teams und Anwendungen in Ihrem Capgo-Konto, um sichere Zusammenarbeit und effiziente Zugriffskontrolle für alle beteiligten Mitglieder zu gewährleisten. ## Was ist das Organisationssystem? [Section titled “Was ist das Organisationssystem?”](#was-ist-das-organisationssystem) Das Organisationssystem ist ein System in Capgo, das es Ihnen ermöglicht, Ihre Apps sicher mit Mitgliedern Ihres Teams zu teilen. ## Übersicht der Organisationseinstellungen [Section titled “Übersicht der Organisationseinstellungen”](#übersicht-der-organisationseinstellungen) ![Organisationseinstellungen](/orgs-settings.webp) | Nummer | Element | Beschreibung | | ------ | -------------------- | -------------------------------------------------------------------------- | | 1 | Einstellungen | Zugriff auf Einstellungen über das Seitenleistenmenü | | 2 | Organisation-Tab | Wechsel zu Organisationseinstellungen (vs. persönliche Kontoeinstellungen) | | 3 | Allgemein | Organisationsname, Logo und Management-E-Mail anzeigen und bearbeiten | | 4 | Mitglieder | Teammitglieder und deren Berechtigungen verwalten | | 5 | Sicherheit | Sicherheitsrichtlinien konfigurieren (2FA, Passwortanforderungen) | | 6 | Audit-Logs | Aktivitätsprotokolle Ihrer Organisation anzeigen | | 7 | Pläne | Abonnementplan anzeigen und verwalten | | 8 | Nutzung | Nutzungsstatistiken Ihrer Organisation überwachen | | 9 | Credits | Zusätzliche Credits anzeigen und kaufen | | 10 | Webhooks | Webhook-Integrationen konfigurieren | | 11 | Abrechnung | Rechnungsinformationen und Rechnungen verwalten | | 12 | Organisationsauswahl | Zwischen verschiedenen Organisationen wechseln | ### So greifen Sie auf die Organisationseinstellungen zu [Section titled “So greifen Sie auf die Organisationseinstellungen zu”](#so-greifen-sie-auf-die-organisationseinstellungen-zu) 1. Klicken Sie auf **Einstellungen** (1) in der linken Seitenleiste 2. Wählen Sie den **Organisation**-Tab (2) oben aus 3. Verwenden Sie die Tabs (3-11), um zwischen verschiedenen Einstellungsbereichen zu navigieren ### So wechseln Sie Organisationen [Section titled “So wechseln Sie Organisationen”](#so-wechseln-sie-organisationen) Klicken Sie auf das **Organisationsauswahl**-Dropdown (12) oben links in der Seitenleiste, um Organisationen anzuzeigen und zwischen ihnen zu wechseln, auf die Sie Zugriff haben. ## Mitglieder verwalten [Section titled “Mitglieder verwalten”](#mitglieder-verwalten) ![Organisationsmitglieder](/org-show-members.webp) Der Mitgliederbereich zeigt alle Benutzer an, die Zugriff auf Ihre Organisation haben, mit: * **Mitglied**: Benutzer-Avatar, Name und E-Mail * **Rolle**: Aktuelle Berechtigungsstufe (Lesen, Upload, Schreiben, Admin, Super Admin) * **Aktionen**: Mitglieder bearbeiten oder entfernen ### So laden Sie ein neues Mitglied ein [Section titled “So laden Sie ein neues Mitglied ein”](#so-laden-sie-ein-neues-mitglied-ein) ![Mitglied einladen](/orgs-add-member.webp) 1. Klicken Sie auf die **+ Hinzufügen**-Schaltfläche (1) 2. Geben Sie die E-Mail-Adresse des Benutzers im Modal ein (2) 3. Klicken Sie auf **Einladen** ### Berechtigungen auswählen [Section titled “Berechtigungen auswählen”](#berechtigungen-auswählen) ![Berechtigungen auswählen](/select-perm-orgs.webp) Nach Eingabe der E-Mail wählen Sie die Berechtigungsstufe für den eingeladenen Benutzer: * **Lesen** - Nur Lesezugriff * **Upload** - Kann neue Versionen hochladen * **Schreiben** - Kann Einstellungen und Inhalte ändern * **Admin** - Vollzugriff außer Abrechnung und Löschung * **Super Admin** - Vollständige Organisationskontrolle ### Einladung abschließen [Section titled “Einladung abschließen”](#einladung-abschließen) ![Einladungsformular](/invite_org_user.webp) Füllen Sie die restlichen Details aus: * **E-Mail** - Aus dem vorherigen Schritt vorausgefüllt * **Rolle** - Zeigt die ausgewählte Berechtigungsstufe an * **Vorname** - Optional: Vorname des Eingeladenen * **Nachname** - Optional: Nachname des Eingeladenen * **Captcha** - Bestätigen Sie, dass Sie ein Mensch sind Klicken Sie auf **Einladung senden**, um den Vorgang abzuschließen. ## Berechtigungsübersicht [Section titled “Berechtigungsübersicht”](#berechtigungsübersicht) | Berechtigung | Lesen | Upload | Schreiben | Admin | Super Admin | | ---------------------------------- | ----- | ------ | --------- | ----- | ----------- | | App-Statistiken anzeigen | ✅ | ✅ | ✅ | ✅ | ✅ | | App-Kanäle anzeigen | ✅ | ✅ | ✅ | ✅ | ✅ | | Geräte anzeigen | ✅ | ✅ | ✅ | ✅ | ✅ | | Logs anzeigen | ✅ | ✅ | ✅ | ✅ | ✅ | | Bundles anzeigen | ✅ | ✅ | ✅ | ✅ | ✅ | | App löschen | ❌ | ❌ | ❌ | ❌ | ✅ | | Kanal löschen | ❌ | ❌ | ❌ | ✅ | ✅ | | Version löschen | ❌ | ❌ | ✅ | ✅ | ✅ | | Org-Einstellungen ändern | ❌ | ❌ | ❌ | ✅ | ✅ | | Org-Benutzer verwalten | ❌ | ❌ | ❌ | ✅ | ✅ | | Kanaleinstellungen ändern | ❌ | ❌ | ✅ | ✅ | ✅ | | Neue Version hochladen | ❌ | ✅ | ✅ | ✅ | ✅ | | Geräte ändern | ❌ | ❌ | ✅ | ✅ | ✅ | | Aktuelle Version des Kanals ändern | ❌ | ❌ | ✅ | ✅ | ✅ | | Neuen Kanal erstellen | ❌ | ❌ | ❌ | ✅ | ✅ | | Version ändern (Metadaten) | ❌ | ❌ | ✅ | ✅ | ✅ | | Abrechnung verwalten | ❌ | ❌ | ❌ | ❌ | ✅ | | Version unsicher löschen | ❌ | ❌ | ❌ | ❌ | ✅ | ## Abrechnung [Section titled “Abrechnung”](#abrechnung) Jeder mit einer **Super Admin**-Berechtigung kann die Abrechnung für eine bestimmte Organisation verwalten. Pläne sind mit einer Organisation und nicht mit Ihrem persönlichen Konto verknüpft. Danger ⚠️ Der Kauf eines Plans wirkt sich NUR auf die Organisation aus, die Sie aktuell ausgewählt haben ## FAQ [Section titled “FAQ”](#faq) ### Kann ich mehr als eine Organisation erstellen? [Section titled “Kann ich mehr als eine Organisation erstellen?”](#kann-ich-mehr-als-eine-organisation-erstellen) Nein, noch nicht. ### Wie kann ich Sicherheitsrichtlinien für meine Organisation konfigurieren? [Section titled “Wie kann ich Sicherheitsrichtlinien für meine Organisation konfigurieren?”](#wie-kann-ich-sicherheitsrichtlinien-für-meine-organisation-konfigurieren) Super Admins können umfassende Sicherheitsrichtlinien konfigurieren, einschließlich: * **2FA-Durchsetzung** - Alle Mitglieder müssen die Zwei-Faktor-Authentifizierung aktivieren * **Passwortrichtlinie** - Anforderungen an die Passwortkomplexität festlegen (Länge, Großbuchstaben, Zahlen, Sonderzeichen) * **API-Schlüssel-Sicherheit** - Sichere/gehashte API-Schlüssel und Ablaufrichtlinien durchsetzen Siehe die [Organisationssicherheit](/docs/webapp/organization-security/) Dokumentation für detaillierte Konfigurationsanweisungen. # Zahlungssystem > Zahlungsverwaltung in Ihrem Capgo-Konto. In diesem Abschnitt zeigen wir Ihnen, wie Sie Ihre Karte, Ihren Plan und Ihre Credits für Capgo verwalten ## Worum geht es? [Section titled “Worum geht es?”](#worum-geht-es) Diese Seite beantwortet einige Fragen zum Zahlungssystem in capgo. ## Pläne [Section titled “Pläne”](#pläne) #### F: Wie kann ich meinen capgo-Plan upgraden? [Section titled “F: Wie kann ich meinen capgo-Plan upgraden?”](#f-wie-kann-ich-meinen-capgo-plan-upgraden) A: Sie können Ihren capgo-Plan upgraden, indem Sie zu [den Einstellungen](/docs/webapp/settings/#how-to-get-to-the-settings-page) gehen und auf den **Pläne**-Tab (1) klicken. ![Pläne-Seite](/plans-subscribe.webp) Auf dieser Seite können Sie: * Zwischen **Monatlicher Plan** und **Jährlich** wechseln (mit 20% Rabatt für jährliche Zahlung) * Aus vier Plänen wählen: **Solo**, **Maker**, **Team** oder **Enterprise** * Auf **Abonnieren** (2) bei dem Plan klicken, der am besten zu Ihren Bedürfnissen passt Jeder Plan zeigt: * Monatlicher Preis und jährlicher Gesamtbetrag * Enthaltene monatlich aktive Benutzer und Überschreitungspreise * Speicher- und Bandbreitenkontingente * Build-Minuten/Stunden * Funktionen wie dedizierter Support, benutzerdefinierte Domain und SOC II-Konformität Wenn Sie sich nicht auf einen Plan festlegen möchten, können Sie stattdessen **Credits** verwenden - klicken Sie auf “Mehr über Credits erfahren”, um zu sehen, wie die nutzungsbasierte Abrechnung funktioniert. Nach dem Klicken auf Abonnieren öffnet sich eine Stripe-Checkout-Seite, auf der Sie Ihre Zahlungsinformationen sicher eingeben können. ## Credits [Section titled “Credits”](#credits) Credits bieten eine flexible Alternative zum Upgrade Ihres Plans. Sie sind nützlich, wenn: * **Temporäre Spitzen** - Sie vorübergehend über Ihre Planlimits hinausgehen (z.B. ein viraler Moment, saisonaler Traffic) und nicht dauerhaft upgraden möchten * **Plan-Mismatch** - Der nächste Plan-Tier aus anderen Gründen nicht zu Ihren Bedürfnissen passt (vielleicht benötigen Sie mehr Bandbreite, aber nicht mehr MAU) * **Pay-as-you-go Präferenz** - Sie lieber nur für das bezahlen, was Sie über Ihren Basisplan hinaus nutzen #### F: Wie greife ich auf die Credits-Seite zu? [Section titled “F: Wie greife ich auf die Credits-Seite zu?”](#f-wie-greife-ich-auf-die-credits-seite-zu) A: Gehen Sie zu [den Einstellungen](/docs/webapp/settings/#how-to-get-to-the-settings-page) und klicken Sie auf den **Credits**-Tab (1). ![Credits-Seite](/buy_credits.webp) Die Credits-Seite zeigt: 1. **Credits-Tab** - Navigieren Sie hier von den Organisationseinstellungen aus 2. **Credits-Guthaben** - Zeigt Ihre verfügbaren Credits (z.B. 0,00 / 0,00) und die in der aktuellen Periode verwendeten Credits 3. **Verwendete Credits (USD)** - Der geschätzte Dollarwert der während der aktuellen Abrechnungsperiode verbrauchten Credits, plus verbleibende verfügbare Credits 4. **Mehr Credits benötigt?** - Kaufen Sie sofort zusätzliche Credits, um Ihre Benutzer ohne Unterbrechung weiterhin Updates zu ermöglichen. Wählen Sie aus voreingestellten Beträgen ($50, $100, $500, $5000) oder geben Sie einen benutzerdefinierten Betrag ein. Der geschätzte Gesamtbetrag inkl. Steuern wird vor dem Checkout angezeigt 5. **Credit-Preise** - Erweitern Sie diesen Abschnitt, um die Preisstufen für verschiedene Nutzungsarten (MAU, Speicher, Bandbreite) zu sehen 6. **Credit-Transaktionen** - Sehen Sie Ihre Credit-Kauf- und Nutzungshistorie ein #### F: Wie kaufe ich Credits? [Section titled “F: Wie kaufe ich Credits?”](#f-wie-kaufe-ich-credits) A: Wählen Sie auf der Credits-Seite den gewünschten Betrag aus (oder verwenden Sie das Dropdown für benutzerdefinierte Beträge) und klicken Sie dann auf **Credits kaufen**. Sie werden zur Stripe-Checkout-Seite weitergeleitet, wo Sie die Menge anpassen und die Zahlung abschließen können. #### F: Wann werden Credits verwendet? [Section titled “F: Wann werden Credits verwendet?”](#f-wann-werden-credits-verwendet) A: Credits werden automatisch verbraucht, wenn Sie Ihre Planlimits für monatlich aktive Benutzer, Speicher oder Bandbreite überschreiten. Sie bieten eine flexible Möglichkeit, Überschreitungen ohne Serviceunterbrechung zu handhaben. #### F: Sind die Zahlungen sicher? [Section titled “F: Sind die Zahlungen sicher?”](#f-sind-die-zahlungen-sicher) A: Ja, Zahlungen werden vollständig von Stripe verwaltet. Capgo erhält niemals Zugriff auf Ihre Kreditkartendaten. Stripe nimmt Sicherheit sehr ernst. [Mehr über Stripes Sicherheitsrichtlinien erfahren](https://stripe.com/docs/security/) #### F: Wird capgo meinen Plan automatisch upgraden, wenn ich das Limit überschreite? [Section titled “F: Wird capgo meinen Plan automatisch upgraden, wenn ich das Limit überschreite?”](#f-wird-capgo-meinen-plan-automatisch-upgraden-wenn-ich-das-limit-überschreite) A: Nein, capgo wird niemals Ihren Plan ändern. #### F: Wird capgo mir eine E-Mail senden, wenn mein Plan seine Grenzen erreicht? [Section titled “F: Wird capgo mir eine E-Mail senden, wenn mein Plan seine Grenzen erreicht?”](#f-wird-capgo-mir-eine-e-mail-senden-wenn-mein-plan-seine-grenzen-erreicht) A: Ja, capgo wird Ihnen eine E-Mail mit Informationen über die Nutzung senden. #### F: Wird der von mir gekaufte Plan die Organisationen beeinflussen, zu denen ich eingeladen wurde? [Section titled “F: Wird der von mir gekaufte Plan die Organisationen beeinflussen, zu denen ich eingeladen wurde?”](#f-wird-der-von-mir-gekaufte-plan-die-organisationen-beeinflussen-zu-denen-ich-eingeladen-wurde) A: Nein, der Plan wird nur die Organisation beeinflussen, die Sie aktuell ausgewählt haben. Bitte beachten Sie [die Organisations-Dokumentation](/docs/webapp/organization-system/#q-how-is-billing-done-within-orgs). #### F: Was, wenn ich einen maßgeschneiderten Plan benötige? [Section titled “F: Was, wenn ich einen maßgeschneiderten Plan benötige?”](#f-was-wenn-ich-einen-maßgeschneiderten-plan-benötige) A: [Bitte kontaktieren Sie den capgo-Support direkt](/docs/getting-help#support-by-chat) #### F: Wie sieht die Rückerstattungsrichtlinie für capgo aus? [Section titled “F: Wie sieht die Rückerstattungsrichtlinie für capgo aus?”](#f-wie-sieht-die-rückerstattungsrichtlinie-für-capgo-aus) A: Eine Rückerstattungsrichtlinie finden Sie [hier](https://capgo.app/return/) # Impostazioni > So ändern Sie die Benutzereinstellungen ## So gelangen Sie zur Einstellungsseite [Section titled “So gelangen Sie zur Einstellungsseite”](#so-gelangen-sie-zur-einstellungsseite) Klicken Sie zuerst auf **Ihren Namen und Ihr Bild**. Klicken Sie dann auf **Einstellungen**. ![open settings](/settings-go.webp) ## Benutzereinstellungen ändern [Section titled “Benutzereinstellungen ändern”](#benutzereinstellungen-ändern) Um Benutzereinstellungen zu ändern, füllen Sie einfach das Formular im Konto aus und klicken Sie dann auf **Aktualisieren** ![save change in account](/account-save.webp) Dann sollte eine Bestätigung erscheinen ![acount updated](/account-updated.webp) ## Passwort ändern [Section titled “Passwort ändern”](#passwort-ändern) Um das Passwort zu ändern, gehen Sie zur Einstellungsseite und klicken Sie auf **Passwort**. Füllen Sie dann das Formular aus und klicken Sie auf **Aktualisieren** ![update password](/update-passwd.webp) Wenn das Passwort nicht den Capgo-Passwort-Sicherheitsregeln entspricht, erhalten Sie eine Fehlermeldung ![wrong password](/passwd-error.webp) # Welcome to Capgo Documentation > Master Capgo Cloud for instant app updates and explore our comprehensive collection of Capacitor plugins to enhance your mobile development ## 🚀 Capgo Cloud - Live Updates Made Simple [Section titled “🚀 Capgo Cloud - Live Updates Made Simple”](#-capgo-cloud---live-updates-made-simple) Instant Updates Deploy JavaScript, HTML, and CSS updates directly to users without app store delays. Fix bugs and ship features in minutes, not days. 3-Step Integration Get started with just `npx @capgo/cli@latest init [APIKEY]` and start pushing updates immediately with our simple integration. Mobile & Desktop Same live update system for Capacitor mobile apps and Electron desktop apps. One platform, all your apps. Complete Guide Learn everything from [quick setup](/docs/getting-started/quickstart/) to advanced deployment strategies in our comprehensive documentation. ## 📚 What’s in This Documentation [Section titled “📚 What’s in This Documentation”](#-whats-in-this-documentation) [Capgo Cloud Setup ](/docs/getting-started/quickstart/)Complete guides for integrating live updates, managing channels, CI/CD integration, and monitoring your deployments. [Electron Updater ](/docs/plugins/electron-updater/)Live updates for Electron desktop apps. Same powerful system, now for desktop applications. [20+ Capacitor Plugins ](/docs/plugins/)Explore our collection of production-ready plugins for biometrics, purchases, camera, storage, and more native features. [CLI & Public API ](/docs/cli/overview/)Automate your workflow with our CLI tools and integrate Capgo into your existing systems with our REST API. [Enterprise Solutions ](/enterprise/)Dedicated support, SLAs, custom features, and advanced security options for teams that need more. ## 🎯 Quick Links [Section titled “🎯 Quick Links”](#-quick-links) * **First time?** Start with the [5-minute quickstart](/docs/getting-started/quickstart/) * **Need a plugin?** Browse our [plugin collection](/docs/plugins/) or request [custom development](/consulting/) * **Having issues?** Check the [FAQ](/docs/faq/) or join our [Discord](https://discord.capgo.app) * **Enterprise needs?** Check our [enterprise solutions](/enterprise/) or [contact us](mailto:support@capgo.app) # What Happens When Capgo is Unavailable > Understanding Capgo behavior when service is down, canceled, or when you exceed plan limits # What Happens When Capgo is Unavailable [Section titled “What Happens When Capgo is Unavailable”](#what-happens-when-capgo-is-unavailable) Capgo is designed to be resilient and non-blocking. Your app continues to work even when Capgo services are temporarily unavailable. This document explains what happens in various scenarios. ## When Capgo Service is Down [Section titled “When Capgo Service is Down”](#when-capgo-service-is-down) If Capgo’s servers are temporarily unavailable or experiencing an outage: * **Your app continues to work normally** — Users can still open and use your app * **Updates are not downloaded** — The automatic update check fails silently * **No error is shown to users** — The update process gracefully fails in the background * **Existing updates remain** — Any previously downloaded updates stay on the device * **Updates resume automatically** — Once service is restored, normal update behavior resumes Capgo is designed with a “fail-safe” approach: if update services are unavailable, your app simply ignores the update and continues functioning normally. ## When You Cancel Capgo [Section titled “When You Cancel Capgo”](#when-you-cancel-capgo) After canceling your Capgo subscription: * **Existing updates remain on user devices** — Users who already downloaded updates keep those versions * **No new updates are downloaded** — The app stops checking for and downloading updates * **App functionality is unaffected** — Your app continues to work without any changes * **Historical data is retained** — Your dashboard and analytics data remains accessible for a period * **You can reactivate anytime** — Resubscribing restores full update functionality immediately The update mechanism gracefully degrades — your app becomes “static” but fully functional after cancellation. ## When You Exceed Your Plan Limits [Section titled “When You Exceed Your Plan Limits”](#when-you-exceed-your-plan-limits) If you exceed your plan’s MAU (Monthly Active Users), bandwidth, or storage limits: ### MAU Limits [Section titled “MAU Limits”](#mau-limits) * **New users may not receive updates** — The update check continues but may be throttled * **Existing users are unaffected** — Users who already have updates continue using them * **Analytics may be incomplete** — New user tracking might be paused ### Bandwidth Limits [Section titled “Bandwidth Limits”](#bandwidth-limits) * **Update downloads stop** — Users won’t receive new updates until you add credits or upgrade * **App remains functional** — Existing app version continues to work * **No error to users** — Update failures are silent and non-blocking ### Storage Limits [Section titled “Storage Limits”](#storage-limits) * **New uploads may fail** — You won’t be able to upload new app versions * **Existing versions remain available** — Previously uploaded bundles still work * **You can upgrade or add storage** — Resolving the limit restores full functionality ## Summary Table [Section titled “Summary Table”](#summary-table) | Scenario | App Works | Updates Download | Existing Updates Stay | | --------------------- | --------- | ------------------------------- | --------------------- | | Capgo Down | ✅ Yes | ❌ No | ✅ Yes | | Subscription Canceled | ✅ Yes | ❌ No | ✅ Yes | | MAU Limit Exceeded | ✅ Yes | ⚠️ May stop | ✅ Yes | | Bandwidth Limit | ✅ Yes | ❌ No | ✅ Yes | | Storage Limit | ✅ Yes | ⚠️ Downloads may be unavailable | ✅ Yes | ## Best Practices [Section titled “Best Practices”](#best-practices) 1. **Always test your app offline** — Ensure core functionality works without updates 2. **Keep a fallback plan** — Have a traditional app store update process as backup 3. **Monitor your limits** — Keep track of usage to avoid unexpected behavior 4. **Test cancellation scenarios** — Verify your app behaves correctly when updates stop ## Need Help? [Section titled “Need Help?”](#need-help) If you have questions about Capgo’s availability policies or need assistance, please [contact support](/docs/getting-help). # Introduction to Capgo Build > Build iOS and Android apps in the cloud without local setup. No Mac required for iOS builds. Capgo Build is a cloud-based native app compilation service for Capacitor apps. It lets you build iOS and Android apps without maintaining local development environments - no Xcode, no Android Studio, no Mac hardware required. ## What Capgo Build Does [Section titled “What Capgo Build Does”](#what-capgo-build-does) Capgo Build compiles the **native parts** of your Capacitor app in the cloud: * **iOS builds** run on dedicated Apple Silicon (Mac Mini M4) machines * **Android builds** run in isolated Docker containers * **Automatic code signing** handles certificates, provisioning profiles, and keystores * **Direct store submission** uploads signed apps to App Store Connect and Google Play You trigger builds with a single CLI command that works from anywhere - your local machine, GitHub Actions, GitLab CI, or any CI/CD pipeline. ## When to Use Capgo Build vs Live Updates [Section titled “When to Use Capgo Build vs Live Updates”](#when-to-use-capgo-build-vs-live-updates) Capgo offers two complementary features for updating your app. Here’s when to use each: | Scenario | Live Updates | Capgo Build | | --------------------------------------------------------- | :----------: | :---------: | | Bug fix in JavaScript/TypeScript code | ✓ | | | UI changes (HTML, CSS, images) | ✓ | | | Updating web dependencies | ✓ | | | Adding or removing a Capacitor plugin | | ✓ | | Updating a native SDK version | | ✓ | | Changing native permissions (Info.plist, AndroidManifest) | | ✓ | | Updating Capacitor version | | ✓ | | Modifying native code (Swift, Kotlin, Java) | | ✓ | | Changing app icon or splash screen | | ✓ | | First app store submission | | ✓ | Note **Live Updates** push JavaScript changes instantly without app store review. **Capgo Build** creates new native binaries when you change native code. Most teams use Live Updates daily and Capgo Build occasionally when native changes are needed. ## Why Use Capgo Build [Section titled “Why Use Capgo Build”](#why-use-capgo-build) No Mac Required for iOS Build and ship iOS apps without Mac hardware. Anyone on Windows, Linux, or any CI/CD system can trigger iOS builds and publish to TestFlight. Skip Local Environment Setup No need to install Xcode, Android Studio, or manage SDK versions. Capgo Build handles all native tooling - you just run the CLI command. Centralized Credentials Store your certificates and keystores in your CI/CD secrets once. Any team member can trigger builds without needing signing credentials on their local machine. Works With Any CI/CD A single CLI command integrates with any pipeline. GitHub Actions, GitLab CI, Jenkins - trigger builds as part of your existing workflow. Real-Time Build Logs Watch your build progress live in your terminal. Logs stream via Server-Sent Events so you can debug issues instantly as they happen. Direct Store Submission Signed apps upload directly to App Store Connect and Google Play. No manual steps between build completion and store submission. ## How It Works [Section titled “How It Works”](#how-it-works) When you run the build command: 1. **Upload** - The CLI zips only what’s needed (native platform folder + native dependencies) and uploads to secure cloud storage 2. **Build** - Your app compiles on dedicated infrastructure using Fastlane 3. **Sign** - Certificates and keystores are applied (they exist only in memory during the build) 4. **Submit** - Signed apps are uploaded directly to App Store Connect or Google Play 5. **Cleanup** - All build artifacts and credentials are automatically deleted Your source code stays on your machine. Only the platform-specific native code is uploaded. ## Security Model [Section titled “Security Model”](#security-model) Capgo Build is designed with zero credential storage: * **Runtime-only credentials** - Certificates and keystores are never stored in Capgo. They are uploaded and removed immediately after the build finishes. * **Ephemeral environments** - Each build runs in isolation and is destroyed after completion * **No log storage** - Build logs stream to your terminal only, never stored on Capgo servers * **Minimal upload** - Only the native platform you request is uploaded, not your full codebase. [See exactly what gets uploaded](/docs/cli/cloud-build/getting-started/#what-gets-built) ## Pricing [Section titled “Pricing”](#pricing) Build time is the only cost: * Build minutes are included in your Capgo plan * Extra minutes available via credit system * iOS builds run on Mac Mini M4 (2x cost multiplier due to hardware costs) * Android builds run in Docker containers (1x cost) * No storage fees ## Next Steps [Section titled “Next Steps”](#next-steps) [Getting Started ](/docs/cli/cloud-build/getting-started/)Create your first build and see Capgo Build in action. [Credentials Setup ](/docs/cli/cloud-build/credentials/)Configure certificates for iOS and keystores for Android. [iOS Builds ](/docs/cli/cloud-build/ios/)Complete guide to building and submitting iOS apps. [Android Builds ](/docs/cli/cloud-build/android/)Complete guide to building and submitting Android apps. # Android Builds > Configure and build Android apps with Capgo Cloud Build Build and submit Android apps to Google Play Store using Capgo’s dedicated infrastructure. ## What you will learn [Section titled “What you will learn”](#what-you-will-learn) * You will learn how to upload your app via Capgo Native build * You will learn how to configure the credentials for Capgo Native Build ## Prerequisites [Section titled “Prerequisites”](#prerequisites) * You need to have an active Google Developer account * You need to have Android Studio installed * Your app must be able to build successfully with Android Studio Note I will use the following app for the tutorial: ## The first manual build [Section titled “The first manual build”](#the-first-manual-build) Before we can start thinking about building the app with Capgo, we should first set it up, and do a first Android build by hand. There are some advantages to doing a manual build first: * You will prepare the credentials for the later Capgo build * You will create a record on the Play Store Console ### Building the app manually with Android Studio [Section titled “Building the app manually with Android Studio”](#building-the-app-manually-with-android-studio) Before we can start building the app with Capgo, we need to build the app manually with Android Studio. 1. Open Android Studio Run `bunx cap open android` to open the Android Studio project. 2. Click on `Build` -> `Generate Signed App Bundles / APKs` ![Android Studio generate signed app bundles / APKs](/native-build-assets/android-studio-generate-signed-app-bundles-apks.webp) 3. Select `Android App Bundle` and click on `Next` ![Android Studio select Android App Bundle](/native-build-assets/android-studio-select-android-app-bundle.webp) ### Creating a Keystore [Section titled “Creating a Keystore”](#creating-a-keystore) Right now, you are missing the keystore file. This file is used to sign your app, which lets Google know that it’s you who built the app. To generate it, we will use the GUI method provided by Android Studio. There is also a way to do this with the command line, but we will not cover that in this tutorial. 1. Click on `Create new` ![Android Studio create new keystore](/native-build-assets/android-studio-create-new-keystore.webp) 2. Fill in the Key Store path ![Android Studio fill in key store path](/native-build-assets/android-studio-fill-in-key-store-path.webp) Caution This **IS TO BE SAVED** - it will be used later 3. Set the Key Store password ![Android Studio set key store password](/native-build-assets/android-studio-set-key-store-password.webp) Note I recommend using the same password for the Key Store and the Key Alias. Caution This **IS TO BE SAVED** - it will be used later 4. Fill the rest of the form 1. Keep the Key Alias as is (key0) 2. Fill the certificate details. I have filled it with fake details, but you should fill it with your own details. ![Android Studio fill rest of the form](/native-build-assets/android-studio-fill-rest-of-the-form.webp) 5. Click on `OK` ![Android Studio click on OK](/native-build-assets/android-studio-click-on-ok.webp) ### Finishing the manual build [Section titled “Finishing the manual build”](#finishing-the-manual-build) 1. Make sure all of the details for the keystore have been filled in correctly and click on `Next` ![Android Studio make sure all of the details for the keystore have been filled in correctly](/native-build-assets/android-studio-make-sure-all-of-the-details-for-the-keystore-have-been-filled-in-correctly.webp) 2. Select the `release` build variant and click on `Create` ![Android Studio select release build variant](/native-build-assets/android-studio-select-release-build-variant.webp) 3. After the build succeeds, you should see the following screen ![Android Studio after build succeed](/native-build-assets/android-studio-after-build-succeed.webp) 1. This popup indicates that the build succeeded. 2. Click on the `locate` button - this will open the file explorer and you should see the build there. 4. Make sure you can see the build in the file explorer ![Android Studio make sure you can see the build in the file explorer](/native-build-assets/android-studio-make-sure-you-can-see-the-build-in-the-file-explorer.webp) ### Creating the app on the Play Store Console [Section titled “Creating the app on the Play Store Console”](#creating-the-app-on-the-play-store-console) 1. Go to [Google Play Console](https://play.google.com/console/) 2. Select the correct developer account ![Google Play Console select correct developer account](/native-build-assets/google-play-console-select-correct-developer-account.webp) 3. Click on `Create app` ![Google Play Console create app](/native-build-assets/google-play-console-create-app.webp) 4. Choose the app name and the language ![Google Play Console choose the app name and the language](/native-build-assets/google-play-console-choose-the-app-name-and-the-language.webp) 5. Select the app category and if the app is paid or free ![Google Play Console select the app category and if the app is paid or free](/native-build-assets/google-play-console-select-the-app-category-and-if-the-app-is-paid-or-free.webp) 6. Accept the terms and conditions ![Google Play Console accept the terms and conditions](/native-build-assets/google-play-console-accept-the-terms-and-conditions.webp) Caution Make sure you read the terms and conditions before accepting them. 7. Click on `Create` ![Google Play Console click on create](/native-build-assets/google-play-console-click-on-create.webp) ### Creating the internal testing group [Section titled “Creating the internal testing group”](#creating-the-internal-testing-group) Now that you have created the app, you can create an internal testing group. Since I won’t actually publish the app for everyone on Play Store, I will need to create an internal testing group. However, if you are going to publish the app for everyone, you can likely skip this step, although I don’t recommend it. 1. Go to `internal testing` Click on `Test and release` -> `Testing` -> `Internal testing` ![Google Play Console internal testing](/native-build-assets/google-play-console-internal-testing.webp) 2. Click on `Testers` ![Google Play Console testers](/native-build-assets/google-play-console-testers.webp) 3. Click on `Create email list` ![Google Play Console create email list](/native-build-assets/google-play-console-create-email-list.webp) 4. Name the email list ![Google Play Console name the email list](/native-build-assets/google-play-console-name-the-email-list.webp) 5. Add the email addresses of the testers ![Google Play Console add the email addresses of the testers](/native-build-assets/google-play-console-add-the-email-addresses-of-the-testers.webp) 6. Press `Enter` and click on `Save` ![Google Play Console press enter and click on save](/native-build-assets/google-play-console-press-enter-and-click-on-save.webp) 7. Click on `Create group` ![Google Play Console create group](/native-build-assets/google-play-console-create-group.webp) 8. Make sure that the new list is selected and click on `Save` ![Google Play Console make sure that the new list is selected and click on save](/native-build-assets/google-play-console-make-sure-that-the-new-list-is-selected-and-click-on-save.webp) ### Uploading the app to the internal testing group [Section titled “Uploading the app to the internal testing group”](#uploading-the-app-to-the-internal-testing-group) Now that you have created the internal testing group, you can upload the app to the internal testing group. 1. Go to `Test and release` -> `Testing` -> `Internal testing` ![Google Play Console internal testing](/native-build-assets/google-play-console-internal-testing.webp) 2. Click on the `Releases` button ![Google Play Console releases button](/native-build-assets/google-play-console-releases-button.webp) 3. Click on `Create new release` ![Google Play Console create new release](/native-build-assets/google-play-console-create-new-release.webp) 4. Click on `Upload` ![Google Play Console upload](/native-build-assets/google-play-console-upload.webp) 5. Select the AAB (or APK) file ![Google Play Console select AAB (or APK) file](/native-build-assets/google-play-console-select-apk-file.webp) Caution Make sure you select the AAB (or APK) file that you built manually with Android Studio. If this file doesn’t use the same keystore as the one you will use for the Capgo build, the Capgo Native Build will fail. 6. Wait for the AAB (or APK) file to be uploaded 7. Click on `Next` ![Google Play Console next](/native-build-assets/google-play-console-next-step.webp) 8. Fix the errors Personally, at this stage I see this error ![Google Play Console big scary warning](/native-build-assets/google-play-console-big-scary-warning.webp) This is because I haven’t verified my phone number yet. I will do that and continue the tutorial. 9. Click on `Save and publish` This will publish the app to the internal testing group. ![Google Play Console save and publish](/native-build-assets/google-play-console-save-and-publish.webp) 10. Confirm the publication ![Google Play Console confirm publication](/native-build-assets/google-play-console-confirm-publication.webp) 11. Make sure the app is published ![Google Play Console make sure the app is published](/native-build-assets/google-play-console-make-sure-the-app-is-published.webp) 12. Get your temporary app name ![Google Play Console get your temporary app name](/native-build-assets/google-play-console-get-your-temporary-app-name.webp) ### Accept the invitation to internal testing group [Section titled “Accept the invitation to internal testing group”](#accept-the-invitation-to-internal-testing-group) Now that you have uploaded the app to the internal testing group, you can accept the invitation to the internal testing group. 1. Go to `Test and release` -> `Testing` -> `Internal testing` ![Google Play Console internal testing](/native-build-assets/google-play-console-internal-testing.webp) 2. Click on `Testers` ![Google Play Console testers](/native-build-assets/google-play-console-testers.webp) 3. Click on `Copy link` ![Google Play Console copy link](/native-build-assets/google-play-console-copy-link.webp) 4. Send the link to your phone, open it in the browser and click on `Accept` ![Google Play Console accept invitation](/native-build-assets/google-play-console-accept-invitation.webp) 5. Confirm the invitation has been accepted and click on “download it on Play Store” ![Google Play Console download it on Play Store](/native-build-assets/google-play-console-download-it-on-play-store.webp) 6. Install the app 1. If you had installed the app before using Android Studio, click on the `uninstall` button ![Google Play Console uninstall app](/native-build-assets/google-play-console-uninstall-app.webp) 2. Click on the `install` button ![Google Play Console install app](/native-build-assets/google-play-console-install-app.webp) 3. Open the app and confirm it has downloaded successfully [](/native-build-assets/screen-20260215-072439-1771136671038.mp4 "App downloaded successfully from Play Store internal testing") Caution If you do not see the app in Play Store, make sure you have selected the correct account in Play Store. ## Configuring Capgo Native Build (Android) [Section titled “Configuring Capgo Native Build (Android)”](#configuring-capgo-native-build-android) Now, you are ready to start the setup of Capgo Native Build. Congratulations 🎉! | Requirement | Flag | Description | Required | | --------------------------- | -------------------------------------- | ---------------------------------------------------------------------------------- | ---------------------- | | Keystore file | `--keystore ` | Path to your `.jks`/`.keystore` file used to sign the APK/AAB. | Yes | | Keystore alias | `--keystore-alias ` | Alias name of the key inside the keystore. | Yes | | Keystore key password | `--keystore-key-password ` | Password for the key. If key/store passwords match, you can provide only one. | Look at the note below | | Keystore store password | `--keystore-store-password ` | Password for the keystore. If key/store passwords match, you can provide only one. | Look at the note below | | Google Play service account | `--play-config ` | JSON service account file for Play Store uploads. | Yes | ```bash bunx @capgo/cli build credentials save --platform android \ --keystore ./path/to/keystore.jks \ --keystore-alias "your-alias" \ --keystore-key-password "key-password" \ --keystore-store-password "store-password" \ --play-config ./play-store-service-account.json ``` Note If key and store passwords are identical, provide only one of `--keystore-key-password` or `--keystore-store-password`. ### Keystore, keystore password, keystore key password, keystore alias [Section titled “Keystore, keystore password, keystore key password, keystore alias”](#keystore-keystore-password-keystore-key-password-keystore-alias) If you have followed the [manual build instructions](/docs/cli/cloud-build/android/#building-the-app-manually-with-android-studio), you should have the keystore already generated. If you have not followed the instructions, please follow them to generate the keystore. ### Google Play service account [Section titled “Google Play service account”](#google-play-service-account) Generating the Google Play service account is a manual and complex process. Yet, it is required to upload your app to Google Play. Please keep in mind the following things: * You **NEED** to be the [owner of the Developer Account](https://support.google.com/googleplay/android-developer/thread/238025575?hl=en\&msgid=238033420). Otherwise, you will not be able to setup the service account. * You will need to create a new Google Cloud Project (separate from your Google Play Account) Let’s begin. 1. Go to [Google Cloud Console](https://console.cloud.google.com/) 2. Click on the project selector ![Google Console Project Selector](/social-login-assets/google_cons_project_selector.png) 3. If you already have a project, select it. Otherwise, create a new project: Note The screenshots below are illustrative - use a name appropriate for your project. 1. Click on `New project` ![New Project button in Google Console](/social-login-assets/google_cons_new_project_btn.png) 2. Name your project and click `Create` ![Project naming screen showing name field and Create button](/social-login-assets/google_cons_name_projec.png) 3. Ensure that you are on the right project ![Project name showing in the selector indicating correct project selection](/social-login-assets/google_cons_right_proj.png) 4. Let’s click on the search bar and search for `service accounts` and click on it ![Google Console search bar](/social-login-assets/google_cons_search.png) ![Google Console search bar](/native-build-assets/google-console-search-bar.webp) 5. Let’s click on `Create service account` ![Create Service Account button in Google Console](/native-build-assets/google_cons_create_service_account_btn.webp) 6. Fill in the form for the service account and click on `Done` 1. I recommend setting the name to `Capgo Native Build Service Account` 2. For the Service Account ID, I recommend setting it to `capgo-native-build-service-acc` 3. As for the description, you don’t have to fill it in, but I recommend filling it with `Allows Capgo Native Build to build and submit the app to the Play Store` ![Google Console fill in the form for the service account](/native-build-assets/google-console-fill-in-the-form-for-the-service-account.webp) 7. Click on the newly created service account You should now see the newly created service account in the list. Click on it. ![Google Console newly created service account](/native-build-assets/google-console-newly-created-service-account.webp) Note Copy this email address as well, you will need it later. 8. Click on the `Keys` tab ![Keys tab in Google Console](/native-build-assets/google_cons_keys_tab.webp) 9. Click on `Add Key` and `Create new key` ![Google Console add key and create new key](/native-build-assets/google-console-add-key-and-create-new-key.webp) 10. Click on `JSON` and `Create` ![Create Key button in Google Console](/native-build-assets/google_cons_create_key_btn.webp) Caution Clicking on `Create` will create a new key and it will download the JSON file to your local machine. You need to save the JSON file in a safe place. It **WILL** be used later. 11. Download the JSON file The JSON file should have been downloaded automatically. You can click on `close` to close the window. ![Google Console download key](/native-build-assets/google-console-download-key.webp) ### Granting Play Store API access to the service account [Section titled “Granting Play Store API access to the service account”](#granting-play-store-api-access-to-the-service-account) The newly created service account does not yet have access to the Play Store API. To grant it, head to the Play Store Console. 1. Go to [Google Play Console](https://play.google.com/console/) 2. Select the correct developer account ![Google Play Console select correct developer account](/native-build-assets/google-play-console-select-correct-developer-account.webp) 3. Click on `Users and permissions` ![Google Play Console users and permissions](/native-build-assets/google-play-console-users-and-permissions.webp) 4. Click on `Invite new users` ![Google Play Console invite new users](/native-build-assets/google-play-console-invite-new-users.webp) 5. Copy the email address of the service account ![Google Play Console copy email address of the service account](/native-build-assets/google-play-console-copy-email-address-of-the-service-account.webp) 6. Go to `Account permissions` and grant the minimum required permissions: * In `App permissions`, grant access to your app. * In `Releases`, enable `Create, edit, and roll out releases`. * If your workflow uses Play App Signing, enable the related signing permission. * If you are unsure, use `Admin` only during setup, then reduce permissions afterward. ![Google Play Console grant permissions](/native-build-assets/google-play-console-grant-permissions.webp) 7. Click on `Invite user` ![Google Play Console invite user](/native-build-assets/google-play-console-invite-user.webp) 8. Confirm the invitation ![Google Play Console confirm invitation](/native-build-assets/google-play-console-confirm-invitation.webp) 9. Confirm that the user has been invited ![Google Play Console confirm that the user has been invited](/native-build-assets/google-play-console-confirm-that-the-user-has-been-invited.webp) ### Saving the credentials [Section titled “Saving the credentials”](#saving-the-credentials) You are now ready to save the credentials and run your first build. You can save the credentials using the following command: ```bash bunx @capgo/cli build credentials save --platform android \ --keystore ./path/to/keystore.jks \ --keystore-alias "your-alias" \ --keystore-key-password "key-password" \ --keystore-store-password "store-password" \ --play-config ./play-store-service-account.json ``` ### CI/CD setup (GitHub Actions) [Section titled “CI/CD setup (GitHub Actions)”](#cicd-setup-github-actions) If you already completed [Keystore, keystore password, keystore key password, keystore alias](#keystore-keystore-password-keystore-key-password-keystore-alias) and [Google Play service account](#google-play-service-account), you already have everything needed for CI/CD. This section only covers how to pass those values as GitHub Actions secrets and environment variables. #### 1) Convert credential files to single-line base64 [Section titled “1) Convert credential files to single-line base64”](#1-convert-credential-files-to-single-line-base64) ```bash # Android keystore (.jks or .keystore) base64 -i ./path/to/keystore.jks | tr -d '\n' > keystore_base64.txt # Google Play service account JSON base64 -i ./play-store-service-account.json | tr -d '\n' > play_config_base64.txt ``` Tip GitHub secrets should be single-line values. `tr -d '\n'` removes line breaks from base64 output. #### 2) Create repository secrets [Section titled “2) Create repository secrets”](#2-create-repository-secrets) In `GitHub > Repository > Settings > Secrets and variables > Actions`, add: | Secret name | Value | | ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | | `CAPGO_TOKEN` | Your Capgo API token | | `APP_ID` | Your Capgo app ID (example: `com.example.app`) | | `ANDROID_KEYSTORE_FILE` | Content of `keystore_base64.txt` | | `KEYSTORE_KEY_ALIAS` | Keystore alias from [Keystore, keystore password, keystore key password, keystore alias](#keystore-keystore-password-keystore-key-password-keystore-alias) | | `KEYSTORE_KEY_PASSWORD` | Keystore key password | | `KEYSTORE_STORE_PASSWORD` | Keystore store password | | `PLAY_CONFIG_JSON` | Content of `play_config_base64.txt` | Note If key and store passwords are the same, you can provide just one of `KEYSTORE_KEY_PASSWORD` or `KEYSTORE_STORE_PASSWORD`. #### 3) Use env vars in your GitHub Actions workflow [Section titled “3) Use env vars in your GitHub Actions workflow”](#3-use-env-vars-in-your-github-actions-workflow) .github/workflows/android-build.yml ```yaml name: Android Cloud Build on: workflow_dispatch: push: branches: [main] jobs: android-build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: oven-sh/setup-bun@v2 with: bun-version: latest - name: Request Android build with Capgo run: bunx @capgo/cli@latest build ${{ secrets.APP_ID }} --platform android env: CAPGO_TOKEN: ${{ secrets.CAPGO_TOKEN }} ANDROID_KEYSTORE_FILE: ${{ secrets.ANDROID_KEYSTORE_FILE }} KEYSTORE_KEY_ALIAS: ${{ secrets.KEYSTORE_KEY_ALIAS }} KEYSTORE_KEY_PASSWORD: ${{ secrets.KEYSTORE_KEY_PASSWORD }} KEYSTORE_STORE_PASSWORD: ${{ secrets.KEYSTORE_STORE_PASSWORD }} PLAY_CONFIG_JSON: ${{ secrets.PLAY_CONFIG_JSON }} ``` Tip With this setup, CI sends credentials through environment variables only. You do not need to commit signing files or upload them as workflow artifacts. ### Running the build [Section titled “Running the build”](#running-the-build) Congratulations 🎉! You are now ready to run your first build. Run: ```bash bunx @capgo/cli build com.example.app --platform android ``` And this will start the build process 🍾🥂 # Configuration Options > Complete reference for all Cloud Build CLI flags, environment variables, and credential keys Complete reference for every Cloud Build configuration option. Use this page to find the CLI flag, environment variable, or credential key for any build setting. ## Configuration Precedence [Section titled “Configuration Precedence”](#configuration-precedence) Every build option can be set in multiple ways. When the same option is set in multiple places, higher-priority sources win: ``` flowchart LR A["🔧 CLI Flag"] -->|overrides| B["🌍 Environment Variable"] B -->|overrides| C["📁 Local Credentials"] C -->|overrides| D["🏠 Global Credentials"] style A fill:#6366f1,color:#fff,stroke:#4f46e5 style B fill:#8b5cf6,color:#fff,stroke:#7c3aed style C fill:#a78bfa,color:#fff,stroke:#8b5cf6 style D fill:#c4b5fd,color:#1e1b4b,stroke:#a78bfa ``` **Example:** If your saved credentials have `SKIP_BUILD_NUMBER_BUMP=true` but you pass `--no-skip-build-number-bump` on the CLI, the CLI flag wins and build numbers will be auto-incremented. Tip For CI/CD pipelines, environment variables are usually the most convenient. For local development, saved credentials (via `build credentials save`) avoid repeating flags every time. *** ## iOS Options [Section titled “iOS Options”](#ios-options) ### Code Signing [Section titled “Code Signing”](#code-signing) | CLI Flag | Env Variable | Credential Key | Default | Description | | ------------------------------------------------- | ------------------------------------- | ------------------------------------- | ------- | ------------------------------------------------------------------------- | | `--build-certificate-base64 ` | `BUILD_CERTIFICATE_BASE64` | `BUILD_CERTIFICATE_BASE64` | — | Base64-encoded `.p12` distribution certificate | | `--build-provision-profile-base64 ` | `BUILD_PROVISION_PROFILE_BASE64` | `BUILD_PROVISION_PROFILE_BASE64` | — | Base64-encoded `.mobileprovision` provisioning profile | | `--build-provision-profile-base64-prod ` | `BUILD_PROVISION_PROFILE_BASE64_PROD` | `BUILD_PROVISION_PROFILE_BASE64_PROD` | — | Production provisioning profile (optional, for App Store distribution) | | `--p12-password ` | `P12_PASSWORD` | `P12_PASSWORD` | — | Password for the `.p12` certificate (omit if certificate has no password) | ### App Store Connect Authentication [Section titled “App Store Connect Authentication”](#app-store-connect-authentication) | CLI Flag | Env Variable | Credential Key | Default | Description | | ---------------------------------- | --------------------------- | --------------------------- | ------- | ------------------------------------------------------------ | | `--apple-key-id ` | `APPLE_KEY_ID` | `APPLE_KEY_ID` | — | App Store Connect API Key ID | | `--apple-issuer-id ` | `APPLE_ISSUER_ID` | `APPLE_ISSUER_ID` | — | App Store Connect Issuer ID (UUID) | | `--apple-key-content ` | `APPLE_KEY_CONTENT` | `APPLE_KEY_CONTENT` | — | Base64-encoded App Store Connect API key (`.p8` file) | | `--apple-profile-name ` | `APPLE_PROFILE_NAME` | `APPLE_PROFILE_NAME` | — | Provisioning profile name as shown in Apple Developer portal | | `--app-store-connect-team-id ` | `APP_STORE_CONNECT_TEAM_ID` | `APP_STORE_CONNECT_TEAM_ID` | — | App Store Connect Team ID | Note **Alternative authentication:** You can also use Apple ID + app-specific password instead of API keys. Pass `--apple-id ` and `--apple-app-specific-password ` (env vars: `APPLE_ID`, `APPLE_APP_SPECIFIC_PASSWORD`). API keys are recommended for CI/CD. ### iOS Build Settings [Section titled “iOS Build Settings”](#ios-build-settings) | CLI Flag | Env Variable | Credential Key | Default | Description | | ----------------------- | ------------------ | ------------------ | ------- | --------------------------------------- | | `--ios-scheme ` | `CAPGO_IOS_SCHEME` | `CAPGO_IOS_SCHEME` | `App` | Xcode scheme to build | | `--ios-target ` | `CAPGO_IOS_TARGET` | `CAPGO_IOS_TARGET` | `App` | Xcode target for reading build settings | *** ## Android Options [Section titled “Android Options”](#android-options) ### Keystore Signing [Section titled “Keystore Signing”](#keystore-signing) | CLI Flag | Env Variable | Credential Key | Default | Description | | -------------------------------------- | ------------------------- | ------------------------- | ------- | --------------------------------------------------------------- | | `--android-keystore-file ` | `ANDROID_KEYSTORE_FILE` | `ANDROID_KEYSTORE_FILE` | — | Base64-encoded keystore file (`.keystore` or `.jks`) | | `--keystore-key-alias ` | `KEYSTORE_KEY_ALIAS` | `KEYSTORE_KEY_ALIAS` | `key0` | Keystore key alias | | `--keystore-key-password ` | `KEYSTORE_KEY_PASSWORD` | `KEYSTORE_KEY_PASSWORD` | — | Keystore key password (falls back to store password if not set) | | `--keystore-store-password ` | `KEYSTORE_STORE_PASSWORD` | `KEYSTORE_STORE_PASSWORD` | — | Keystore store password | ### Google Play Configuration [Section titled “Google Play Configuration”](#google-play-configuration) | CLI Flag | Env Variable | Credential Key | Default | Description | | --------------------------- | ------------------ | ------------------ | ------- | --------------------------------------------------- | | `--play-config-json ` | `PLAY_CONFIG_JSON` | `PLAY_CONFIG_JSON` | — | Base64-encoded Google Play service account JSON key | ### Android Build Settings [Section titled “Android Build Settings”](#android-build-settings) | Env Variable | Default | Description | | --------------------------- | ---------- | --------------------------------------------------------------------- | | `PLAY_STORE_TRACK` | `internal` | Google Play release track (`internal`, `alpha`, `beta`, `production`) | | `PLAY_STORE_RELEASE_STATUS` | `draft` | Release status on Google Play (`draft`, `completed`) | *** ## Build Control Options [Section titled “Build Control Options”](#build-control-options) These options work for both iOS and Android builds. ### Build Mode [Section titled “Build Mode”](#build-mode) | CLI Flag | Default | Description | | ----------------------- | --------- | ----------------------------------- | | `--platform ` | — | **Required.** `ios` or `android` | | `--build-mode ` | `release` | `debug` or `release` | | `--build-config ` | — | Additional JSON build configuration | | `--path ` | `.` | Project directory | | `--verbose` | `false` | Enable verbose build logging | ### Build Number Control [Section titled “Build Number Control”](#build-number-control) | CLI Flag | Env Variable | Credential Key | Default | Description | | ----------------------------- | ------------------------ | ------------------------ | ------- | ----------------------------------------------------------------- | | `--skip-build-number-bump` | `SKIP_BUILD_NUMBER_BUMP` | `SKIP_BUILD_NUMBER_BUMP` | `false` | Skip automatic build number / version code incrementing | | `--no-skip-build-number-bump` | — | — | — | Explicitly re-enable auto-increment (overrides saved credentials) | By default, Capgo Cloud Build automatically increments build numbers: * **iOS:** Fetches latest build number from App Store Connect, increments by 1 * **Android:** Fetches max `versionCode` from Google Play, increments by 1 When `--skip-build-number-bump` is set, the build uses whatever version is already in your project files (Xcode project or `build.gradle`). ### Output Upload [Section titled “Output Upload”](#output-upload) | CLI Flag | Env Variable | Credential Key | Default | Description | | ------------------------------- | -------------------------------- | -------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------ | | `--output-upload` | `BUILD_OUTPUT_UPLOAD_ENABLED` | `BUILD_OUTPUT_UPLOAD_ENABLED` | `false` | Upload build outputs (IPA/APK/AAB) to Capgo storage. When set via env var, use `BUILD_OUTPUT_UPLOAD_ENABLED=true`. | | `--no-output-upload` | `BUILD_OUTPUT_UPLOAD_ENABLED` | — | — | Disable output upload. When set via env var, use `BUILD_OUTPUT_UPLOAD_ENABLED=false`. | | `--output-retention ` | `BUILD_OUTPUT_RETENTION_SECONDS` | `BUILD_OUTPUT_RETENTION_SECONDS` | `1h` | How long download links remain active | **Retention format:** Use human-readable durations like `1h`, `6h`, `2d`, `7d`. Minimum is 1 hour, maximum is 7 days. When set via env var, use seconds (e.g., `3600` for 1 hour). ### Authentication [Section titled “Authentication”](#authentication) | CLI Flag | Env Variable | Default | Description | | -------------------- | ------------- | ------- | -------------------------------------------- | | `-a, --apikey ` | `CAPGO_TOKEN` | — | Capgo API key for authentication | | `--supa-host ` | — | — | Custom Supabase host (self-hosting only) | | `--supa-anon ` | — | — | Custom Supabase anon key (self-hosting only) | *** ## Environment Variable Quick Reference [Section titled “Environment Variable Quick Reference”](#environment-variable-quick-reference) Copy-paste ready for your CI/CD pipeline. All variables are optional — only set what you need. ### iOS [Section titled “iOS”](#ios) ```bash # Code signing (required for iOS builds) BUILD_CERTIFICATE_BASE64="" BUILD_PROVISION_PROFILE_BASE64="" P12_PASSWORD="" # App Store Connect (required for store submission) APPLE_KEY_ID="ABC1234567" APPLE_ISSUER_ID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" APPLE_KEY_CONTENT="" APPLE_PROFILE_NAME="My App Distribution Profile" APP_STORE_CONNECT_TEAM_ID="TEAM123456" # Optional iOS settings CAPGO_IOS_SCHEME="App" CAPGO_IOS_TARGET="App" ``` ### Android [Section titled “Android”](#android) ```bash # Keystore signing (required for Android builds) ANDROID_KEYSTORE_FILE="" KEYSTORE_KEY_ALIAS="my-key-alias" KEYSTORE_KEY_PASSWORD="" KEYSTORE_STORE_PASSWORD="" # Google Play (required for store submission) PLAY_CONFIG_JSON="" # Optional Android settings PLAY_STORE_TRACK="internal" PLAY_STORE_RELEASE_STATUS="draft" ``` ### Build Control [Section titled “Build Control”](#build-control) ```bash # Build behavior SKIP_BUILD_NUMBER_BUMP="true" # Skip auto-increment BUILD_OUTPUT_UPLOAD_ENABLED="true" # Upload IPA/APK/AAB BUILD_OUTPUT_RETENTION_SECONDS="3600" # 1 hour download link # Authentication CAPGO_TOKEN="your-api-key" ``` *** ## Credential Storage [Section titled “Credential Storage”](#credential-storage) ### Save Credentials Locally [Section titled “Save Credentials Locally”](#save-credentials-locally) Instead of passing flags or env vars every time, save credentials once: ```bash # Save iOS credentials bunx @capgo/cli build credentials save \ --platform ios \ --certificate ./dist_cert.p12 \ --provisioning-profile ./profile.mobileprovision \ --p12-password "cert-password" \ --apple-key ./AuthKey.p8 \ --apple-key-id ABC1234567 \ --apple-issuer-id xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx \ --apple-profile-name "My App Profile" \ --apple-team-id TEAM123456 # Save Android credentials bunx @capgo/cli build credentials save \ --platform android \ --keystore ./release.keystore \ --keystore-alias my-key \ --keystore-key-password "key-pass" \ --keystore-store-password "store-pass" \ --play-config ./play-service-account.json ``` ### Storage Locations [Section titled “Storage Locations”](#storage-locations) | Flag | Location | Use Case | | ----------- | ----------------------------------------- | --------------------------------------------------- | | *(default)* | `~/.capgo-credentials/credentials.json` | Global — shared across all projects on your machine | | `--local` | `.capgo-credentials.json` in project root | Per-project — overrides global when both exist | Credentials are keyed by **app ID** (e.g. `com.example.myapp`), so a single credentials file can store settings for multiple apps without conflicts. Each app’s credentials are further split by platform (`ios` / `android`). Caution Add `.capgo-credentials.json` to your `.gitignore` if using local credentials. Never commit credentials to version control. ### Manage Saved Credentials [Section titled “Manage Saved Credentials”](#manage-saved-credentials) ```bash # List saved credentials bunx @capgo/cli build credentials list # Update a specific option without re-entering everything bunx @capgo/cli build credentials update --skip-build-number-bump # Clear saved credentials bunx @capgo/cli build credentials clear --platform ios ``` *** ## Examples [Section titled “Examples”](#examples) ### GitHub Actions [Section titled “GitHub Actions”](#github-actions) ```yaml name: Build and Submit on: push: branches: [main] jobs: build-ios: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: oven-sh/setup-bun@v2 - run: bun install - run: bunx cap sync ios - run: bunx @capgo/cli build request --platform ios env: CAPGO_TOKEN: ${{ secrets.CAPGO_TOKEN }} BUILD_CERTIFICATE_BASE64: ${{ secrets.IOS_CERTIFICATE }} BUILD_PROVISION_PROFILE_BASE64: ${{ secrets.IOS_PROFILE }} P12_PASSWORD: ${{ secrets.P12_PASSWORD }} APPLE_KEY_ID: ${{ secrets.APPLE_KEY_ID }} APPLE_ISSUER_ID: ${{ secrets.APPLE_ISSUER_ID }} APPLE_KEY_CONTENT: ${{ secrets.APPLE_KEY_CONTENT }} APPLE_PROFILE_NAME: ${{ secrets.APPLE_PROFILE_NAME }} APP_STORE_CONNECT_TEAM_ID: ${{ secrets.APP_STORE_CONNECT_TEAM_ID }} build-android: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: oven-sh/setup-bun@v2 - run: bun install - run: bunx cap sync android - run: bunx @capgo/cli build request --platform android env: CAPGO_TOKEN: ${{ secrets.CAPGO_TOKEN }} ANDROID_KEYSTORE_FILE: ${{ secrets.ANDROID_KEYSTORE }} KEYSTORE_KEY_ALIAS: ${{ secrets.KEYSTORE_ALIAS }} KEYSTORE_KEY_PASSWORD: ${{ secrets.KEYSTORE_KEY_PASSWORD }} KEYSTORE_STORE_PASSWORD: ${{ secrets.KEYSTORE_STORE_PASSWORD }} PLAY_CONFIG_JSON: ${{ secrets.PLAY_CONFIG_JSON }} ``` ### Using CLI Flags Directly [Section titled “Using CLI Flags Directly”](#using-cli-flags-directly) ```bash # Build iOS with all options inline bunx @capgo/cli build request \ --platform ios \ --build-mode release \ --skip-build-number-bump \ --output-retention 6h \ --apikey YOUR_API_KEY # Build Android, skip version bump, no output upload bunx @capgo/cli build request \ --platform android \ --skip-build-number-bump \ --no-output-upload \ --apikey YOUR_API_KEY ``` ### Mixed Configuration [Section titled “Mixed Configuration”](#mixed-configuration) Combine saved credentials with CLI overrides: ```bash # Save base credentials once bunx @capgo/cli build credentials save --platform ios \ --certificate ./cert.p12 \ --provisioning-profile ./profile.mobileprovision \ --output-upload # Override specific options per-build bunx @capgo/cli build request --platform ios \ --skip-build-number-bump \ --output-retention 2d ``` The saved credentials provide signing details while CLI flags override build behavior for this specific run. # Managing Credentials > Save and manage build credentials locally for iOS and Android builds Manage your iOS and Android build credentials locally for convenient cloud builds. ## Overview [Section titled “Overview”](#overview) Capgo CLI allows you to save build credentials locally on your machine in the `.capgo-credentials` folder. When you run a build, these credentials are automatically used and sent securely to Capgo’s build servers. Need Help Getting Credentials? If you don’t have your certificates and credentials yet, check these comprehensive guides: **iOS:** * [How to Get iOS Certificates](/docs/cli/cloud-build/ios/#how-to-get-ios-certificates-and-provisioning-profiles) - Step-by-step guide * [iOS Certificates Guide](/docs/cloud/native-builds/certificates/ios/) - Detailed step-by-step tutorial * [Blog: Automatic iOS Builds](https://capgo.app/blog/automatic-capacitor-ios-build-github-action/) - Complete CI/CD setup **Android:** * [Creating a Keystore](/docs/cli/cloud-build/android/#creating-a-keystore) - Step-by-step guide * [Android Certificates Guide](/docs/cloud/native-builds/certificates/android/) - Detailed step-by-step tutorial * [Blog: Automatic Android Builds](https://capgo.app/blog/automatic-capacitor-android-build-github-action/) - Complete CI/CD setup Security Guarantee **Your credentials are NEVER stored permanently on Capgo servers:** * ✅ Used ONLY during the active build process * ✅ Automatically deleted after build completion * ✅ Maximum retention: 24 hours (even if build fails) * ✅ Apps are sent directly to App Store/Play Store - we store NOTHING * ✅ Transmitted securely over HTTPS ## Commands [Section titled “Commands”](#commands) ### Save Credentials [Section titled “Save Credentials”](#save-credentials) Store your build credentials locally for automatic use: ```bash npx @capgo/cli build credentials save --platform [options] ``` ### Update Credentials [Section titled “Update Credentials”](#update-credentials) Partially update existing credentials without re-providing everything: ```bash npx @capgo/cli build credentials update --platform [options] ``` The `update` command uses **additive merge** for provisioning profiles — new profiles are merged with existing ones. To replace the entire provisioning map instead, add `--overwrite-ios-provisioning-map`. Example — add an extension profile to existing credentials: ```bash npx @capgo/cli build credentials update \ --platform ios \ --ios-provisioning-profile "com.example.app.widget=./widget_profile.mobileprovision" ``` The update command accepts the same options as `save` but all are optional — only the fields you provide are updated. ### List Credentials [Section titled “List Credentials”](#list-credentials) View currently saved credentials (passwords are masked): ```bash npx @capgo/cli build credentials list # List credentials for a specific app npx @capgo/cli build credentials list --appId com.example.app ``` ### Clear Credentials [Section titled “Clear Credentials”](#clear-credentials) Remove saved credentials from your local machine: ```bash # Clear all credentials npx @capgo/cli build credentials clear # Clear credentials for a specific app + platform npx @capgo/cli build credentials clear --appId com.example.app --platform ios ``` ### Migrate Credentials [Section titled “Migrate Credentials”](#migrate-credentials) Convert legacy single-profile format to the new multi-target format: ```bash npx @capgo/cli build credentials migrate --platform ios ``` The migrate command detects old `BUILD_PROVISION_PROFILE_BASE64` credentials, converts them to `CAPGO_IOS_PROVISIONING_MAP`, and removes the legacy keys. See [Migration from Single Profile](/docs/cli/cloud-build/ios/#migration-from-single-profile) for details. ## Saving iOS Credentials [Section titled “Saving iOS Credentials”](#saving-ios-credentials) Note **Don’t have iOS certificates yet?** See the [iOS Builds guide](/docs/cli/cloud-build/ios/#how-to-get-ios-certificates-and-provisioning-profiles) for instructions on creating certificates and provisioning profiles. ### Complete Example [Section titled “Complete Example”](#complete-example) ```bash npx @capgo/cli build credentials save \ --platform ios \ --certificate ./cert.p12 \ --p12-password "YourP12Password" \ --ios-provisioning-profile "com.example.app=./profile.mobileprovision" \ --apple-key ./AuthKey_ABC1234567.p8 \ --apple-key-id "ABC1234567" \ --apple-issuer-id "00000000-0000-0000-0000-000000000000" \ --apple-team-id "TEAM123456" ``` ### iOS Options [Section titled “iOS Options”](#ios-options) | Option | Description | Required | | -------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- | | `--certificate ` | Path to .p12 certificate file | Yes (release) | | `--p12-password ` | Password for the .p12 certificate | Yes (release) | | `--ios-provisioning-profile ` | Provisioning profile mapping (`bundleId=path`). Repeatable for multi-target apps. If only one profile and no bundleId prefix, CLI auto-infers from the profile. | Yes (release) | | `--apple-key ` | Path to App Store Connect API .p8 key | See note¹ | | `--apple-key-id ` | App Store Connect API Key ID | See note¹ | | `--apple-issuer-id ` | App Store Connect API Issuer ID (UUID) | See note¹ | | `--apple-team-id ` | App Store Connect Team ID | Yes | | `--ios-distribution ` | Distribution mode: `app_store` (default) or `ad_hoc` | No | | `--output-upload` | Enable a time-limited Capgo download link for the build artifact | No (default: `false`) | | `--output-retention ` | How long to keep build outputs (e.g. `3600s`) | No (default: `3600s`) | | `--skip-build-number-bump` | Skip automatic build-number increment | No | ¹ App Store Connect API Key Requirements * **`app_store` mode** (default): All three API key options are **required** unless you pass `--output-upload` or `--skip-build-number-bump` (which bypass the need for API-driven submission). * **`ad_hoc` mode**: These options are **not required** — no App Store submission takes place. See [Ad-Hoc Distribution Mode](/docs/cli/cloud-build/ios/#ad-hoc-distribution-mode) for details. ### What Gets Stored [Section titled “What Gets Stored”](#what-gets-stored) When you save iOS credentials, the CLI: 1. Reads the certificate and provisioning profile files 2. Converts them to base64 encoding 3. Saves the credentials to the `.capgo-credentials` folder 4. Stores passwords and IDs as plain text (local files only) The stored file structure: ```json { "ios": { "BUILD_CERTIFICATE_BASE64": "...", "CAPGO_IOS_PROVISIONING_MAP": "{\"com.example.app\":{\"profile\":\"...\",\"name\":\"match AppStore com.example.app\"}}", "APPLE_KEY_CONTENT": "...", "P12_PASSWORD": "...", "APPLE_KEY_ID": "ABC1234567", "APPLE_ISSUER_ID": "...", "APP_STORE_CONNECT_TEAM_ID": "TEAM123456", "CAPGO_IOS_DISTRIBUTION": "app_store" } } ``` ## Saving Android Credentials [Section titled “Saving Android Credentials”](#saving-android-credentials) Note **Don’t have a keystore yet?** See the [Android Builds guide](/docs/cli/cloud-build/android/#creating-a-keystore) for instructions on creating a keystore and setting up Play Store credentials. ### Complete Example [Section titled “Complete Example”](#complete-example-1) ```bash npx @capgo/cli build credentials save \ --platform android \ --keystore ./release.keystore \ --keystore-alias "my-key-alias" \ --keystore-key-password "KeyPassword123" \ --keystore-store-password "StorePassword123" \ --play-config ./play-store-service-account.json ``` ### Android Options [Section titled “Android Options”](#android-options) | Option | Description | Required | | -------------------------------------- | --------------------------------------- | ---------------- | | `--keystore ` | Path to .keystore or .jks file | Yes (release) | | `--keystore-alias ` | Key alias in the keystore | Yes (release) | | `--keystore-key-password ` | Password for the key alias | Yes (release) | | `--keystore-store-password ` | Password for the keystore | Yes (release) | | `--play-config ` | Path to Play Store service account JSON | Yes (submission) | ### What Gets Stored [Section titled “What Gets Stored”](#what-gets-stored-1) When you save Android credentials, the CLI: 1. Reads the keystore and service account JSON files 2. Converts them to base64 encoding 3. Saves the credentials to the `.capgo-credentials` folder 4. Stores passwords and alias as plain text (local files only) The stored file structure: ```json { "android": { "ANDROID_KEYSTORE_FILE": "...", "PLAY_CONFIG_JSON": "...", "KEYSTORE_KEY_ALIAS": "my-key-alias", "KEYSTORE_KEY_PASSWORD": "...", "KEYSTORE_STORE_PASSWORD": "..." } } } ``` ## Using Saved Credentials [Section titled “Using Saved Credentials”](#using-saved-credentials) Once you’ve saved credentials, they’re automatically used when you build: ```bash # Credentials automatically loaded from .capgo-credentials folder npx @capgo/cli build com.example.app --platform ios ``` You can also override saved credentials using environment variables: ```bash # Environment variables take precedence over saved credentials BUILD_CERTIFICATE_BASE64="..." \ P12_PASSWORD="different-password" \ npx @capgo/cli build com.example.app --platform ios ``` **Precedence order:** 1. Environment variables (highest priority) 2. Saved credentials in `.capgo-credentials` folder 3. No credentials (lowest priority) ## Viewing Saved Credentials [Section titled “Viewing Saved Credentials”](#viewing-saved-credentials) List what credentials you have saved: ```bash npx @capgo/cli build credentials list ``` Example output: ```plaintext 📋 Saved Build Credentials: iOS Credentials: ✓ Certificate (base64) ✓ Provisioning Map (JSON) ✓ Apple Key Content (base64) ✓ P12 Password: ******** ✓ Apple Key ID: ABC1234567 ✓ Apple Issuer ID: 00000000-0000-0000-0000-000000000000 ✓ Team ID: TEAM123456 Android Credentials: ✓ Keystore (base64) ✓ Play Store Config (base64) ✓ Keystore Alias: my-key-alias ✓ Key Password: ******** ✓ Store Password: ******** Location: .capgo-credentials/ 🔒 These credentials are stored locally on your machine only. When building, they are sent to Capgo but NEVER stored there. They are auto-deleted after build completion. ``` ## Security Best Practices [Section titled “Security Best Practices”](#security-best-practices) ### Local Storage Security [Section titled “Local Storage Security”](#local-storage-security) 1. **File Permissions** ```bash # Ensure credentials folder is not readable by others chmod 700 .capgo-credentials chmod 600 .capgo-credentials/* ``` 2. **Never Commit Credentials** ```bash # Add to .gitignore echo ".capgo-credentials/" >> .gitignore ``` 3. **Separate Credentials** * Use different credentials for local development vs CI/CD * Rotate credentials regularly * Don’t share credentials between team members ### CI/CD Usage [Section titled “CI/CD Usage”](#cicd-usage) For CI/CD environments, **prefer environment variables** over saved credentials. #### Complete Environment Variables Reference [Section titled “Complete Environment Variables Reference”](#complete-environment-variables-reference) The CLI reads the following environment variables for credentials: **iOS Credentials:** | Variable | Description | Format | Required | | ---------------------------- | ---------------------------------------------------- | --------------------------- | ------------- | | `BUILD_CERTIFICATE_BASE64` | P12/PKCS12 certificate for code signing | Base64 | Yes (release) | | `CAPGO_IOS_PROVISIONING_MAP` | JSON map of bundle IDs to provisioning profile data | JSON string | Yes (release) | | `P12_PASSWORD` | Password for the P12 certificate | Plain text | Optional | | `APPLE_KEY_ID` | App Store Connect API Key ID | String (e.g., “ABC1234567”) | See note¹ | | `APPLE_ISSUER_ID` | App Store Connect API Issuer ID | UUID string | See note¹ | | `APPLE_KEY_CONTENT` | App Store Connect API key (.p8 file content) | Base64 | See note¹ | | `APP_STORE_CONNECT_TEAM_ID` | Apple Developer Team ID | String (e.g., “XXXXXXXXXX”) | Yes | | `CAPGO_IOS_DISTRIBUTION` | Distribution mode: `app_store` (default) or `ad_hoc` | String | No | **Android Credentials:** | Variable | Description | Format | Required | | ------------------------- | --------------------------------- | ---------- | ---------------- | | `ANDROID_KEYSTORE_FILE` | Keystore file for signing APK/AAB | Base64 | Yes (release) | | `KEYSTORE_KEY_ALIAS` | Key alias within the keystore | String | Yes (release) | | `KEYSTORE_KEY_PASSWORD` | Password for the key alias | Plain text | Yes\* | | `KEYSTORE_STORE_PASSWORD` | Password for the keystore file | Plain text | Yes\* | | `PLAY_CONFIG_JSON` | Google Play service account JSON | Base64 | Yes (submission) | \*If only one password is provided, it will be used for both `KEYSTORE_KEY_PASSWORD` and `KEYSTORE_STORE_PASSWORD`. #### GitHub Actions Example [Section titled “GitHub Actions Example”](#github-actions-example) .github/workflows/build.yml ```yaml name: Cloud Build on: push: branches: [main] jobs: build-ios: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: '20' - run: npm install - run: npx @capgo/cli build com.example.app --platform ios env: CAPGO_TOKEN: ${{ secrets.CAPGO_TOKEN }} BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }} CAPGO_IOS_PROVISIONING_MAP: ${{ secrets.CAPGO_IOS_PROVISIONING_MAP }} P12_PASSWORD: ${{ secrets.P12_PASSWORD }} APPLE_KEY_ID: ${{ secrets.APPLE_KEY_ID }} APPLE_ISSUER_ID: ${{ secrets.APPLE_ISSUER_ID }} APPLE_KEY_CONTENT: ${{ secrets.APPLE_KEY_CONTENT }} APP_STORE_CONNECT_TEAM_ID: ${{ secrets.APP_STORE_CONNECT_TEAM_ID }} build-android: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: '20' - run: npm install - run: npx @capgo/cli build com.example.app --platform android env: CAPGO_TOKEN: ${{ secrets.CAPGO_TOKEN }} ANDROID_KEYSTORE_FILE: ${{ secrets.ANDROID_KEYSTORE_FILE }} KEYSTORE_KEY_ALIAS: ${{ secrets.KEYSTORE_KEY_ALIAS }} KEYSTORE_KEY_PASSWORD: ${{ secrets.KEYSTORE_KEY_PASSWORD }} KEYSTORE_STORE_PASSWORD: ${{ secrets.KEYSTORE_STORE_PASSWORD }} PLAY_CONFIG_JSON: ${{ secrets.PLAY_CONFIG_JSON }} ``` #### Preparing Base64 Values [Section titled “Preparing Base64 Values”](#preparing-base64-values) To convert your credential files to base64 for CI/CD secrets: ```bash # iOS Certificate (.p12) base64 -i certificate.p12 | tr -d '\n' > certificate_base64.txt # iOS Provisioning Profiles — use the CLI to generate CAPGO_IOS_PROVISIONING_MAP: npx @capgo/cli build credentials save --platform ios \ --ios-provisioning-profile "com.example.app=./profile.mobileprovision" \ # ... other options # Then copy CAPGO_IOS_PROVISIONING_MAP from .capgo-credentials to your CI secrets # iOS App Store Connect Key (.p8) base64 -i AuthKey_XXXXXX.p8 | tr -d '\n' > apple_key_base64.txt # Android Keystore (.keystore or .jks) base64 -i release.keystore | tr -d '\n' > keystore_base64.txt # Google Play Service Account JSON base64 -i play-store-service-account.json | tr -d '\n' > play_config_base64.txt ``` Tip The `tr -d '\n'` removes newlines to create a single-line base64 string, which is easier to store as a CI/CD secret. #### Why Environment Variables Are More Secure [Section titled “Why Environment Variables Are More Secure”](#why-environment-variables-are-more-secure) This approach is more secure because: * Secrets are managed by your CI/CD platform * No credential files on runners * Easy rotation and access control * Audit trails for secret usage ### Credential Rotation [Section titled “Credential Rotation”](#credential-rotation) Regularly rotate your credentials: 1. **iOS**: Generate new certificates and API keys yearly 2. **Android**: Change keystore passwords annually 3. **After team changes**: Rotate when team members leave Update saved credentials: ```bash # Re-run save command with new credentials npx @capgo/cli build credentials save --platform ios --certificate ./new-cert.p12 ... ``` ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) ### ”No credentials found” [Section titled “”No credentials found””](#no-credentials-found) If the build says no credentials were found: 1. **Check if credentials are saved**: ```bash npx @capgo/cli build credentials list ``` 2. **Save credentials if missing**: ```bash npx @capgo/cli build credentials save --platform ios ... ``` 3. **Verify credentials folder exists**: ```bash ls -la .capgo-credentials/ ``` ### “Permission denied” when reading credentials [Section titled ““Permission denied” when reading credentials”](#permission-denied-when-reading-credentials) Fix file permissions: ```bash chmod 700 .capgo-credentials chmod 600 .capgo-credentials/* ``` ### Credentials not being used [Section titled “Credentials not being used”](#credentials-not-being-used) Check that the correct platform is specified: ```bash # Make sure --platform matches saved credentials npx @capgo/cli build com.example.app --platform ios # Uses ios credentials npx @capgo/cli build com.example.app --platform android # Uses android credentials ``` ### Clear and re-save credentials [Section titled “Clear and re-save credentials”](#clear-and-re-save-credentials) If credentials seem corrupted: ```bash # Clear all credentials npx @capgo/cli build credentials clear # Save again npx @capgo/cli build credentials save --platform ios ... ``` ## Migration from Environment Variables [Section titled “Migration from Environment Variables”](#migration-from-environment-variables) If you’re currently using environment variables, you can migrate to saved credentials: 1. **Extract your current environment variables** ```bash echo $BUILD_CERTIFICATE_BASE64 # Verify they exist ``` 2. **Decode base64 files back to original files** (if needed) ```bash echo "$BUILD_CERTIFICATE_BASE64" | base64 -d > cert.p12 echo "$BUILD_PROVISION_PROFILE_BASE64" | base64 -d > profile.mobileprovision ``` 3. **Save using the CLI** ```bash npx @capgo/cli build credentials save \ --platform ios \ --certificate ./cert.p12 \ --ios-provisioning-profile ./profile.mobileprovision \ --p12-password "$P12_PASSWORD" \ --apple-key-id "$APPLE_KEY_ID" \ --apple-issuer-id "$APPLE_ISSUER_ID" \ --apple-team-id "$APP_STORE_CONNECT_TEAM_ID" ``` If you have existing credentials saved in the old format (single `BUILD_PROVISION_PROFILE_BASE64`), run: ```bash npx @capgo/cli build credentials migrate --platform ios ``` This converts the legacy single-profile to a `CAPGO_IOS_PROVISIONING_MAP` and removes the old `BUILD_PROVISION_PROFILE_BASE64` and `APPLE_PROFILE_NAME` keys. 4. **Test the build** ```bash npx @capgo/cli build com.example.app --platform ios ``` 5. **Remove environment variables** (optional) ```bash unset BUILD_CERTIFICATE_BASE64 BUILD_PROVISION_PROFILE_BASE64 ``` ## File Location [Section titled “File Location”](#file-location) Credentials are stored in the `.capgo-credentials` folder: * **macOS/Linux**: `.capgo-credentials/` (in your project root or home directory) * **Windows**: `.capgo-credentials\` (in your project root or home directory) The folder is automatically created when you save credentials for the first time. ## Next Steps [Section titled “Next Steps”](#next-steps) * [Getting Started](/docs/cli/cloud-build/getting-started/) - Create your first build * [iOS Builds](/docs/cli/cloud-build/ios/) - iOS-specific build configuration * [Android Builds](/docs/cli/cloud-build/android/) - Android-specific build configuration * [Troubleshooting](/docs/cli/cloud-build/troubleshooting/) - Common issues and solutions ## Need Help? [Section titled “Need Help?”](#need-help) * 📚 [Troubleshooting guide](/docs/cli/cloud-build/troubleshooting/) * 💬 [Discord community](https://discord.com/invite/VnYRvBfgA6) * 📧 Email: # Getting Started > Create your first native build with Capgo Cloud Build Get started with Capgo Cloud Build and create your first iOS or Android native build in minutes. ## What You’ll Need [Section titled “What You’ll Need”](#what-youll-need) Before you begin, ensure you have: * A Capacitor app that builds successfully locally * Node.js 20 or higher installed * A Capgo account with an active subscription * Your app already registered in Capgo (run `npx @capgo/cli@latest app add` if not) * **Build credentials configured** (certificates, keystores) - see below ## Before Your First Build [Section titled “Before Your First Build”](#before-your-first-build) ⚠️ Setup Credentials First **Required before building:** You must configure your build credentials (certificates for iOS, keystores for Android). [Setup Credentials →](/docs/cli/cloud-build/credentials/) ## Quick Start [Section titled “Quick Start”](#quick-start) 1. **Setup Build Credentials** Before you can build, you need to save your credentials locally: **For iOS:** ```bash npx @capgo/cli build credentials save \ --platform ios \ --certificate ./cert.p12 \ --p12-password "password" \ --provisioning-profile ./profile.mobileprovision \ --apple-key ./AuthKey.p8 \ --apple-key-id "KEY123" \ --apple-issuer-id "issuer-uuid" \ --apple-team-id "team-id" ``` **For Android:** ```bash npx @capgo/cli build credentials save \ --platform android \ --keystore ./release.keystore \ --keystore-alias "my-key" \ --keystore-key-password "key-pass" \ --keystore-store-password "store-pass" ``` See the [full credentials guide](/docs/cli/cloud-build/credentials/) for details. 2. **Verify Local Build** First, ensure your app builds locally without errors: ```bash # Build your web assets npm run build # Sync with Capacitor npx cap sync # Test local build (optional but recommended) npx cap open ios # For iOS npx cap open android # For Android ``` 3. **Authenticate with Capgo** Set your Capgo API key (if not already configured): ```bash npx @capgo/cli@latest login ``` Or set the environment variable: ```bash export CAPGO_TOKEN=your_api_key_here ``` 4. **Run Your First Build** Start with an Android debug build (fastest to test): ```bash npx @capgo/cli@latest build com.example.app \ --platform android \ --build-mode debug ``` You’ll see real-time logs as your build progresses: ```plaintext ✔ Creating build job... ✔ Uploading project (15.2 MB)... ✔ Build started 📝 Build logs: → Installing dependencies... → Running Gradle build... → Signing APK... ✔ Build succeeded in 3m 42s ``` 5. **Check Build Status** The CLI will automatically poll and display the build status. Once complete, you’ll see: * Build time * Success/failure status * App submitted to App Store/Play Store (if credentials configured) ## Understanding the Build Process [Section titled “Understanding the Build Process”](#understanding-the-build-process) When you run the build command, here’s what happens: ``` flowchart LR A[Your Machine] -->|1. Zip Project| B[Local Temp] B -->|2. Upload| C[Capgo Cloud] C -->|3. Build| D[Build Server] D -->|4. Logs Stream| A D -->|5. Cleanup| E[Auto Delete] ``` 1. **Local Preparation** - Your project is zipped (excluding `node_modules` and dotfiles) 2. **Upload** - The zip is uploaded to secure cloud storage (Cloudflare R2) 3. **Build Execution** - Your app builds on dedicated infrastructure 4. **Log Streaming** - Real-time logs stream to your terminal via Server-Sent Events 5. **Automatic Cleanup** - Build artifacts are deleted (Android: instant, iOS: 24 hours) ## Your First Production Build [Section titled “Your First Production Build”](#your-first-production-build) Once you’ve verified the process works, create a production build: ### Android [Section titled “Android”](#android) ```bash npx @capgo/cli@latest build com.example.app \ --platform android \ --build-mode release ``` You’ll need to configure signing credentials first. See [Android Build Configuration](/docs/cli/cloud-build/android/). ### iOS [Section titled “iOS”](#ios) ```bash npx @capgo/cli@latest build com.example.app \ --platform ios \ --build-mode release ``` iOS builds require signing certificates and provisioning profiles. See [iOS Build Configuration](/docs/cli/cloud-build/ios/). ## What Gets Built [Section titled “What Gets Built”](#what-gets-built) Capgo Build only uploads the **minimum files needed** to compile your native app. Your full source code never leaves your machine. ### What Gets Uploaded [Section titled “What Gets Uploaded”](#what-gets-uploaded) | Included | Description | | ----------------------------------- | ---------------------------------------------------------------- | | `ios/` or `android/` | The native platform folder you’re building | | `package.json`, `package-lock.json` | Dependency manifest | | `capacitor.config.*` | Capacitor configuration | | `resources/` | App icons, splash screens | | Native plugin code | Only the `ios/` or `android/` subfolder of each Capacitor plugin | ### What’s NOT Uploaded [Section titled “What’s NOT Uploaded”](#whats-not-uploaded) | Excluded | Why | | -------------------------------------- | -------------------------------------------------------- | | `node_modules/` (most of it) | Only native plugin code is included, not JS dependencies | | `src/` | Your web source code stays local | | `dist/`, `www/`, `build/` (root level) | Already synced into the native folder via `cap sync` | | `.git/` | Version control history | | `.gradle/`, `.idea/`, `.swiftpm/` | Build caches and IDE settings | | `.env`, secrets | Never uploaded | Note Your built web assets (JS, CSS, HTML) **are** uploaded - but as part of the native folder. When you run `npx cap sync`, your web build is copied into `ios/App/App/public/` or `android/app/src/main/assets/public/`. That’s why running `cap sync` before building is required. ### Your Responsibilities [Section titled “Your Responsibilities”](#your-responsibilities) Before running `npx @capgo/cli build`: 1. **Build your web assets** - Run `npm run build` (or your framework’s build command) 2. **Sync to native** - Run `npx cap sync` to copy web assets into the native project 3. **Commit dependencies** - Ensure all native plugins are in `package.json` ### What Capgo Build Handles [Section titled “What Capgo Build Handles”](#what-capgo-build-handles) * Native iOS compilation (Xcode, Fastlane) * Native Android compilation (Gradle) * Code signing with your credentials * App store submission (if configured) ## Build Time & Costs [Section titled “Build Time & Costs”](#build-time--costs) Build time is measured from start to completion: * **Android**: Typically 3-5 minutes (1× billing multiplier) * **iOS**: Typically 5-10 minutes (2× billing multiplier due to Mac hardware costs) You only pay for actual build time used. No hidden fees. ## Common Use Cases [Section titled “Common Use Cases”](#common-use-cases) ### CI/CD Integration [Section titled “CI/CD Integration”](#cicd-integration) Add to your GitHub Actions workflow: ```yaml - name: Build native app env: CAPGO_TOKEN: ${{ secrets.CAPGO_TOKEN }} run: | npm run build npx cap sync npx @capgo/cli@latest build ${{ secrets.APP_ID }} \ --platform both \ --build-mode release ``` ### Local Development [Section titled “Local Development”](#local-development) Test builds locally before committing: ```bash # Quick debug build for testing npm run build && npx cap sync npx @capgo/cli@latest build com.example.app \ --platform android \ --build-mode debug ``` ### Multi-Platform Builds [Section titled “Multi-Platform Builds”](#multi-platform-builds) Build for both platforms by running two commands: ```bash # iOS build npx @capgo/cli@latest build com.example.app \ --platform ios \ --build-mode release # Android build npx @capgo/cli@latest build com.example.app \ --platform android \ --build-mode release ``` In CI/CD, you can run these in parallel jobs for faster builds. ## Next Steps [Section titled “Next Steps”](#next-steps) Now that you’ve created your first build: * [Configure iOS builds](/docs/cli/cloud-build/ios/) - Set up certificates and profiles * [Configure Android builds](/docs/cli/cloud-build/android/) - Set up keystores and Play Store * [Troubleshooting](/docs/cli/cloud-build/troubleshooting/) - Common issues and solutions * [CLI Reference](/docs/cli/reference/build/) - Complete command documentation ## Need Help? [Section titled “Need Help?”](#need-help) * Check the [troubleshooting guide](/docs/cli/cloud-build/troubleshooting/) * Join our [Discord community](https://discord.com/invite/VnYRvBfgA6) * Email support at # iOS Builds > Configure and build iOS apps with Capgo Cloud Build Build and submit iOS apps to TestFlight and the App Store using Capgo’s dedicated Mac infrastructure. ## What you will learn [Section titled “What you will learn”](#what-you-will-learn) * You will learn how to upload your app via Capgo Native build * You will learn how to configure the certificates for Capgo Native Build ## Prerequisites [Section titled “Prerequisites”](#prerequisites) * A Capgo account with an active subscription * Your app already registered in Capgo (run `npx @capgo/cli@latest app add` if not) * A Mac computer with Xcode installed (it’s possible to setup the build on a linux/windows machine, but it’s not yet documented) * Valid Apple Developer account ($99/year) (You must have admin or owner rights on the Apple Developer account) * Your app must be able to build successfully via Xcode * A Capacitor app * A configured icon for the app. Apps without an icon cannot be uploaded to the App Store. Note I will use the following app for the tutorial: ## The first manual build [Section titled “The first manual build”](#the-first-manual-build) Before we can start thinking about building the app with Capgo, we should first set it up, and do a first TestFlight build by hand. There are some advantages to doing a manual build first: * You will setup the distribution certificate on your local machine * You will create the App Store record if you haven’t done it yet * You will be able to figure out any issues with the build process linked to your app code Before we can begin, you must have the distribution certificate installed on your local machine. This is quite a bit complex, but I will explain it below. ### Setting up the distribution certificate [Section titled “Setting up the distribution certificate”](#setting-up-the-distribution-certificate) 1. Open Xcode 2. Click on `Xcode` -> `Settings...` Alternatively, you can use the shortcut `Cmd + ,` ![Xcode settings](/native-build-assets/xcode-settings.webp) 3. Go to `Accounts` ![Xcode Accounts tab](/native-build-assets/xcode-apple-accounts.webp) 4. Find the Apple Account that is added to the Apple Developer Account ![Xcode selected apple account](/native-build-assets/xcode-selected-account.webp) 5. Find the team that you will use to deploy the app ![Xcode find team](/native-build-assets/xcode-find-team.webp) 6. Click on the `Manage Certificates...` button ![Xcode manage certificates](/native-build-assets/xcode-manage-certificates.webp) 7. Make sure you can see the distribution certificate in the list ![Xcode distribution certificate](/native-build-assets/xcode-deployment-certificate.webp) 8. If you do not, you need to create a new certificate Note Apple limits the number of distribution certificates you can have to 3. If you have more than 3 certificates, you need to delete one in order to create a new one. I will not cover that in the tutorial. 1. Click on the `+` button and then on `Apple Distribution` ![Xcode add certificate](/native-build-assets/xcode-add-certificate.webp) 2. The certificate will be created automatically. You can see it in the list. Look at the previous step to confirm that you see it. Now that you have the distribution certificate installed, you can begin the build process. ### Manual build to TestFlight [Section titled “Manual build to TestFlight”](#manual-build-to-testflight) 1. Open the app in Xcode Run `npx cap open ios` to open the app in Xcode. 2. Find and click on the `archive` button In the Xcode toolbar, find and click on the `product` -> `archive` button. ![Xcode toolbar](/native-build-assets/xcode-toolbar.webp) 3. Wait for the build to complete 4. Click on the `Distribute App` button ![Xcode distribute button](/native-build-assets/xcode-distribue-app.webp) 5. Select `TestFlight Internal Only` as the distribution method and click on `Distribute` button ![Xcode TestFlight internal only distribution method](/native-build-assets/xcode-distribue-testflight-1.webp) 6. Configure the app record Fill in the following fields: 1. Name: The name of your app - visible in the App Store 2. SKU - the SKU of your app - this is used to identify your app in the App Store 3. The primary language - the primary language of your app Then, click on the `next` button ![Xcode TestFlight configure app record](/native-build-assets/xcode-configure-app-record-testflight.webp) 7. If the creation of the app record fails, try to close the window and try to archive the app again. 8. Wait for the upload to complete 9. If everything went well, you should see the following screen ![Xcode TestFlight upload complete](/native-build-assets/xcode-upload-complete-testflight.webp) 10. Click on the `Done` button You may instinctively think that all is good now and that you will be able to see your app in TestFlight now, but there are a few more things needed to be done before you can see your app in TestFlight: 1. You need to add yourself to TestFlight 2. You need to promise Apple that your app doesn’t use any non-standard (like a custom algorithm) encryption. If your app does use any non-standard encryption, I suggest reading the [Apple documentation](https://developer.apple.com/help/app-store-connect/manage-app-information/overview-of-export-compliance) on how to handle this. Let’s start with the first one: ### Adding yourself to TestFlight [Section titled “Adding yourself to TestFlight”](#adding-yourself-to-testflight) 1. Go to the [App Store Connect](https://appstoreconnect.apple.com/) page ![App Store Connect login page](/native-build-assets/appstore-connect-login.webp) 2. Sign in with your Apple Developer account 3. Select the team that you used when you created the app record. If you are only in one developer account, can skip this step. ![App Store Connect team selection](/native-build-assets/appstore-connect-select-team.webp) 4. Click on the `Apps` button ![App Store Connect apps button](/native-build-assets/appstore-connect-apps-button.webp) 5. Find the app you created in the previous step and click on it ![App Store Connect app selection](/native-build-assets/appstore-connect-select-app.webp) 6. Click on the `TestFlight` button ![App Store Connect testflight button](/native-build-assets/appstore-connect-testflight-button.webp) 7. Click on the `Internal Testers plus` button ![App Store Connect internal testers plus button](/native-build-assets/appstore-connect-testflight-internal-testing-plus.webp) 8. Create a new group I like to name the group “internal”. You can name it whatever you want. ![App Store Connect create new group](/native-build-assets/appstore-connect-testflight-internal-create-group.webp) 9. Click on `Invite testers` button ![App Store Connect invite testers button](/native-build-assets/appstore-connect-testflight-internal-invite-testers.webp) 10. Add yourself to the group Find yourself in the list and select the checkbox next to your name. (You may need to refresh the page to see yourself) Then, click on the `Add` button. ![App Store Connect invite tester checkbox](/native-build-assets/appstore-connect-testflight-internal-invite-tester-checkbox.webp) 11. Verify that you are added to the group Now, you should see yourself in the group. ![App Store Connect verify tester](/native-build-assets/appstore-connect-testflight-internal-verify-tester.webp) Congratulations 🎉 You have added yourself to TestFlight. Now, there is just one more thing you need to do before you can configure Capgo Native Build. ### Setting up the compliance information [Section titled “Setting up the compliance information”](#setting-up-the-compliance-information) You now need to promise Apple that your app doesn’t use any non-standard (like a custom algorithm) encryption. If your app does use any non-standard encryption, I suggest reading the [Apple documentation](https://developer.apple.com/help/app-store-connect/manage-app-information/overview-of-export-compliance) on how to handle this. There are two ways to do this: 1. You can do this by hand every time you build your app. 2. You can configure your plist file to automatically set this value to `false`. Let’s start with the first one: 1. Follow all the steps from the previous section to find the TestFlight section in App Store Connect 2. Click on `Builds -> iOS` ![App Store Connect builds iOS button](/native-build-assets/appstore-connect-builds-ios-button.webp) 3. Find the build with missing compliance information and click on `Manage` ![App Store Connect manage build](/native-build-assets/appstore-connect-manage-build.webp) 4. Select the option that best describes your app For me, this is `none`, but it might be different for you. After, click save ![App Store Connect save compliance information](/native-build-assets/appstore-connect-manage-build-compliance.webp) 5. Your app should now say `ready to test` ![App Store Connect ready to test](/native-build-assets/appstore-connect-ready-to-test.webp) As for the second one, here are the steps: 1. Open the `Info.plist` file 2. Add the following key: ```xml ITSAppUsesNonExemptEncryption ``` 3. Save the file Note [Here](https://developer.apple.com/documentation/security/complying-with-encryption-export-regulations) is the official Apple documentation on how to inform Apple of your compliance with encryption export regulations. ### Installing the TestFlight app and accepting the invitation [Section titled “Installing the TestFlight app and accepting the invitation”](#installing-the-testflight-app-and-accepting-the-invitation) Now, you are **ALMOST** ready to test your app in TestFlight. Before, you need to do the following things: 1. Download the [TestFlight app](https://apps.apple.com/us/app/testflight/id899247664) from the App Store on your iOS/iPadOS device 2. Accept the invitation to test your app I will skip the details of how to install the TestFlight app on your device. If you are not sure how to install an app, Google has some great guides on how to do it. As for accepting the invitation, you will receive an email from Apple with a link to accept the invitation. 1. Open the email from Apple with the link to accept the invitation 2. Click on `View in TestFlight` button ![TestFlight email button](/native-build-assets/testflight-email-button.webp) 3. Click on the `Install` button ![TestFlight install button](/native-build-assets/testflight-install-button.webp) 4. Install the app on your device If you have installed the app previously using Xcode, you may see the following screen. Please click on the `install` button. ![TestFlight install app](/native-build-assets/testflight-install-app.webp) 5. Wait for the app to install 6. Click on the `Open` button and click it [](/native-build-assets/testflight-open-app.mp4) Congratulations 🎉 You have accepted the invitation to test your app in TestFlight. Now, you can configure Capgo Native Build to build and submit your app to TestFlight. ## Configuring Capgo Native Build [Section titled “Configuring Capgo Native Build”](#configuring-capgo-native-build) There are a few things you need to configure in Capgo Native Build to be able to build and submit your app to TestFlight. Here is a list of the things you will pass to the Capgo CLI: | Parameter | Description | | ---------------------------- | ----------------------------------------------------------------------------------------------------- | | `--platform` | The platform to build for (`ios`) | | `--apple-team-id` | Your Apple Developer Team ID (found in [Apple Developer Portal](https://developer.apple.com/account)) | | `--apple-key` | Path to your App Store Connect API Key file (`.p8` file) | | `--apple-key-id` | The Key ID of your App Store Connect API Key | | `--apple-issuer-id` | Your App Store Connect Issuer ID | | `--certificate` | Path to your distribution certificate (`.p12` file) | | `--ios-provisioning-profile` | Provisioning profile mapping (`bundleId=path` or just path for single profile) | Example command: ```bash npx @capgo/cli@latest build credentials save \ --platform ios \ --apple-team-id YOUR_TEAM_ID \ --apple-key '/path/to/AuthKey_XXXXX.p8' \ --apple-key-id YOUR_KEY_ID \ --apple-issuer-id YOUR_ISSUER_ID \ --certificate '/path/to/certificate.p12' \ --ios-provisioning-profile '/path/to/profile.mobileprovision' ``` ### Team ID [Section titled “Team ID”](#team-id) Let’s start with the team ID. Finding it is quite easy. 1. Go to [Apple Developer Account](https://developer.apple.com/account/) and scroll down 2. Find the `Team ID` ![Team ID location in developer account](/native-build-assets/apple_dev_team_id.png) ### Apple key, Apple key ID and Apple issuer ID [Section titled “Apple key, Apple key ID and Apple issuer ID”](#apple-key-apple-key-id-and-apple-issuer-id) Now, let’s move on to the Apple key. 1. Go to [App Store Connect user and access page](https://appstoreconnect.apple.com/access/users/) Note For me the link sometimes doesn’t work. Reload the page if it doesn’t work for you. 2. Select the correct team in the dropdown 1. Click on your name in the top right corner 2. Click on the team you want to use ![Apple Developer keys team selection](/native-build-assets/apple-developer-select-team.webp) 3. Click on the `Integrations` button ![App Store Connect integrations button](/native-build-assets/appstore-connect-integrations-button.webp) 4. Find the `issuer` Caution This **IS TO BE SAVED** - you will need it later Click on the `copy` button to copy the issuer ![App Store Connect copy issuer](/native-build-assets/appstore-connect-copy-issuer.webp) 5. Click on the plus button ![App Store Connect add key button](/native-build-assets/appstore-connect-add-key-button.webp) 6. Set the name of the key and set the access to `App manager` and click on the `Generate` button ![App Store Connect add key name, set access to app manager and generate button](/native-build-assets/appstore-connect-add-key-name.webp) 7. Save the key ID Caution This **IS TO BE SAVED** - you will need it later ![App Store Connect copy key ID](/native-build-assets/appstore-connect-copy-key-id.webp) 8. Download the key Caution This **IS TO BE SAVED** - you will need it later Danger **NEVER SHARE THE KEY WITH ANYONE - USE IT ONLY IN THE CAPGO CLI** ![App Store Connect download key](/native-build-assets/appstore-connect-download-key.webp) ![App Store Connect download key warning](/native-build-assets/appstore-connect-download-key-warning.webp) Congratulations 🎉 You have created the Apple key, Apple key ID and Apple issuer ID. ### Certificate [Section titled “Certificate”](#certificate) Now, you are ready to export the certificate. As you remember, one of the first steps of this guide was setting up the distribution certificate. However, Apple in their infinite wisdom, decided that the way you export the certificate is quite different from the way you create them 🙃 Let’s get into setting it up: 1. Open Keychain Access 1. Click `Command + Space` to open the search bar 2. Search for `Keychain Access` 3. Click on the `Keychain Access` app [](/native-build-assets/open-keychain-macos.mp4) 2. Select the `login` category and click on the `My Certificates` button ![Keychain Access login category](/native-build-assets/keychain-access-login-category.webp) 3. Find your certificate in the list The certificate should be named `Apple Distribution: [Your Name/Company] (your team ID)` ![Keychain Access find certificate](/native-build-assets/keychain-access-find-certificate.webp) 4. Right-click on the certificate and select `Export` ![Keychain Access export certificate](/native-build-assets/keychain-access-export-certificate.webp) 5. Save the certificate as a `.p12` file Caution This **IS TO BE SAVED** - you will need it later 1. Make sure to select a good name for the certificate file 2. Make sure the file format is set to `Personal Information Exchange (.p12)` 3. Click on the `Save` button ![Keychain Access save certificate dialog](/native-build-assets/keychain-access-save-certificate.webp) 6. When asked for the password, you can either: * Skip the password (recommended for simplicity): Click `OK` without entering a password * Set a password: If you prefer to protect your certificate with a password, you can set one here. Password-protected `.p12` files are fully supported by the Capgo CLI - just provide the password using the `--p12-password` option when running configuration command. ![Keychain Access save certificate password dialog](/native-build-assets/keychain-access-save-certificate-password-dialog.webp) 7. When asked for the “login keychain password”, give the password you use to login to your Mac Give the password you use to login to your Mac. Then, click on the `Allow` button. ![Keychain Access save certificate login keychain password](/native-build-assets/keychain-access-save-certificate-login-keychain-password.webp) Congratulations 🎉 You have exported the certificate. ### Provisioning profile [Section titled “Provisioning profile”](#provisioning-profile) Now, you are ready to export the provisioning profile. I promise, this is the last thing you will need to get from Apple. 1. Go to [Apple Developer Profiles](https://developer.apple.com/account/resources/profiles/list) 2. Select the correct team in the dropdown 1. Click on your name in the top right corner 2. Click on the team you want to use ![Apple Developer keys team selection](/native-build-assets/apple-developer-select-team.webp) 3. Make sure you are on the correct page It should look like this, if it doesn’t click on `profiles` in the sidebar ![Apple Developer profiles page](/native-build-assets/apple-developer-profiles-page.webp) 4. Click on the `+` button ![Apple Developer add profile button](/native-build-assets/apple-developer-add-profile-button.webp) 5. Select the profile type Select `App Store Connect` and click on the `Continue` button ![Apple Developer select profile type](/native-build-assets/apple-developer-select-profile-type.webp) 6. Select the app you want to build Find your app in the dropdown and click on the `Continue` button ![Apple Developer select app](/native-build-assets/apple-developer-select-app.webp) 7. Select the correct distribution certificate Select the certificate you exported in the previous step and click on the `Continue` button ![Apple Developer select distribution certificate](/native-build-assets/apple-developer-select-deployment-certificate.webp) If you are unsure which certificate to select, come back to Keychain Access and find the certificate you exported. Then look at the expiration date. ![Apple Developer select distribution certificate expiration date](/native-build-assets/apple-developer-select-deployment-certificate-expiration-date.webp) 8. Name the profile Give the profile a name and click on the `Generate` button Tip The profile name is automatically extracted by the Capgo CLI — you don’t need to remember it. ![Apple Developer name profile](/native-build-assets/apple-developer-name-profile.webp) 9. Download the profile Click on the `Download` button to download the profile Caution This file **IS TO BE SAVED** - you will need it later ![Apple Developer download profile](/native-build-assets/apple-developer-name-provisioning-profile.webp) Congratulations 🎉 You have now got everything you need to configure Capgo Native Build. Caution Never commit the credentials you just created to your repository. Keep them safe! 🔒 ### Running the configuration command [Section titled “Running the configuration command”](#running-the-configuration-command) You have done it! You have now got everything you need to configure Capgo Native Build. The command you will need to run is: ```bash npx @capgo/cli@latest build credentials save \ --platform ios \ --apple-team-id UVTJ336J2D \ --apple-key ./capgo-tutorial/AuthKey_66FGQZB566.p8 \ --apple-key-id 66FGQZB566 \ --apple-issuer-id 0cd4db4a-5598-45b8-9d32-75cdf127d005 \ --certificate ./capgo-tutorial/capgo-build-tutorial-certificate.p12 \ --ios-provisioning-profile ./capgo-tutorial/capgo_native_build_tutorial.mobileprovision ``` Replace placeholder values Replace the placeholder values above with your actual credentials gathered in the previous steps: * `--apple-team-id`: Your Apple Team ID * `--apple-key`: Path to your downloaded `.p8` key file * `--apple-key-id`: Your Apple Key ID * `--apple-issuer-id`: Your Apple Issuer ID * `--certificate`: Path to your exported `.p12` certificate * `--ios-provisioning-profile`: Path to your downloaded `.mobileprovision` file (bundle ID auto-inferred for single profiles) If all went well, you will see the following output: ![Capgo CLI credentials save output](/native-build-assets/credentials-save.gif) ### CI/CD setup (GitHub Actions) [Section titled “CI/CD setup (GitHub Actions)”](#cicd-setup-github-actions) If you already completed [Team ID](#team-id), [Apple key, Apple key ID and Apple issuer ID](#apple-key-apple-key-id-and-apple-issuer-id), [Certificate](#certificate), and [Provisioning profile](#provisioning-profile), you already have everything needed for CI/CD. This section only covers how to pass those values as GitHub Actions secrets and environment variables. #### 1) Convert credential files to single-line base64 [Section titled “1) Convert credential files to single-line base64”](#1-convert-credential-files-to-single-line-base64) ```bash # Distribution certificate (.p12) base64 -i ./capgo-tutorial/capgo-build-tutorial-certificate.p12 | tr -d '\n' > certificate_base64.txt # Provisioning profile (.mobileprovision) base64 -i ./capgo-tutorial/capgo_native_build_tutorial.mobileprovision | tr -d '\n' > profile_base64.txt # App Store Connect API key (.p8) base64 -i ./capgo-tutorial/AuthKey_66FGQZB566.p8 | tr -d '\n' > apple_key_base64.txt ``` Tip GitHub secrets should be single-line values. `tr -d '\n'` removes line breaks from base64 output. #### 2) Create repository secrets [Section titled “2) Create repository secrets”](#2-create-repository-secrets) In `GitHub > Repository > Settings > Secrets and variables > Actions`, add: | Secret name | Value | | ---------------------------- | --------------------------------------------------------------------------------------------------------- | | `CAPGO_TOKEN` | Your Capgo API token | | `APP_STORE_CONNECT_TEAM_ID` | Team ID from [Team ID](#team-id) | | `APPLE_KEY_ID` | Key ID from [Apple key, Apple key ID and Apple issuer ID](#apple-key-apple-key-id-and-apple-issuer-id) | | `APPLE_ISSUER_ID` | Issuer ID from [Apple key, Apple key ID and Apple issuer ID](#apple-key-apple-key-id-and-apple-issuer-id) | | `BUILD_CERTIFICATE_BASE64` | Content of `certificate_base64.txt` | | `CAPGO_IOS_PROVISIONING_MAP` | Generated by CLI — copy from `.capgo-credentials` file | | `APPLE_KEY_CONTENT` | Content of `apple_key_base64.txt` | | `P12_PASSWORD` (optional) | Your `.p12` password if set during export | #### 3) Use env vars in your GitHub Actions workflow [Section titled “3) Use env vars in your GitHub Actions workflow”](#3-use-env-vars-in-your-github-actions-workflow) .github/workflows/ios-build.yml ```yaml name: iOS Cloud Build on: workflow_dispatch: push: branches: [main] jobs: ios-build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: oven-sh/setup-bun@v2 with: bun-version: latest - name: Request iOS build with Capgo run: bunx @capgo/cli@latest build request --platform ios env: CAPGO_TOKEN: ${{ secrets.CAPGO_TOKEN }} APP_STORE_CONNECT_TEAM_ID: ${{ secrets.APP_STORE_CONNECT_TEAM_ID }} APPLE_KEY_ID: ${{ secrets.APPLE_KEY_ID }} APPLE_ISSUER_ID: ${{ secrets.APPLE_ISSUER_ID }} BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }} CAPGO_IOS_PROVISIONING_MAP: ${{ secrets.CAPGO_IOS_PROVISIONING_MAP }} APPLE_KEY_CONTENT: ${{ secrets.APPLE_KEY_CONTENT }} P12_PASSWORD: ${{ secrets.P12_PASSWORD }} ``` Tip With this setup, CI sends credentials through environment variables only. You do not need to commit signing files or upload them as workflow artifacts. ### Running the build [Section titled “Running the build”](#running-the-build) Now, you are ready to run your first build. Run the following command to build your app: ```bash npx @capgo/cli@latest build request --platform ios ``` Congratulations 🎉 At this point, you have successfully built your app and it is ready to be submitted to the App Store. ## Ad-Hoc Distribution Mode [Section titled “Ad-Hoc Distribution Mode”](#ad-hoc-distribution-mode) By default, Capgo builds iOS apps for App Store distribution (TestFlight + App Store). If you need ad-hoc builds instead (for internal testing or CI artifact collection), you can use the `--ios-distribution` flag. Note Ad-hoc distribution is **not** the same as Enterprise (In-House) distribution. Enterprise distribution requires a separate Apple Developer Enterprise account and uses different provisioning profiles. Capgo currently supports `app_store` and `ad_hoc` modes only. ### When to use ad-hoc mode [Section titled “When to use ad-hoc mode”](#when-to-use-ad-hoc-mode) * You want to distribute IPAs directly to registered devices (no TestFlight) * You don’t have or don’t want to use an App Store Connect API key * You want to collect build artifacts via `--output-upload` without submitting to the App Store ### Requirements [Section titled “Requirements”](#requirements) Ad-hoc builds have **fewer requirements** than App Store builds: | Credential | Required? | | ------------------------------------------------ | --------- | | Distribution certificate (`.p12`) | Yes | | Ad-hoc provisioning profile (`.mobileprovision`) | Yes | | Team ID (`--apple-team-id`) | Yes | | App Store Connect API key (`.p8`) | **No** | | Apple Key ID / Issuer ID | **No** | Note In `ad_hoc` mode, `--apple-key`, `--apple-key-id`, and `--apple-issuer-id` are not supported and are ignored. This is why the two App Store Connect rows above do not show ad-hoc CLI flags; these options apply to `app_store` mode only. Caution Without an App Store Connect API key, build number auto-increment uses a timestamp-based fallback. To suppress the warning, pass `--skip-build-number-bump`. ### Creating an ad-hoc provisioning profile [Section titled “Creating an ad-hoc provisioning profile”](#creating-an-ad-hoc-provisioning-profile) Follow the same steps as [Provisioning profile](#provisioning-profile), but in step 5, select **Ad Hoc** instead of **App Store**: 1. Go to [Apple Developer Profiles](https://developer.apple.com/account/resources/profiles/list) 2. Click the `+` button 3. Select **Ad Hoc** and click Continue 4. Select your app and distribution certificate 5. Select the devices you want to register 6. Name and download the profile ### Saving ad-hoc credentials [Section titled “Saving ad-hoc credentials”](#saving-ad-hoc-credentials) ```bash npx @capgo/cli@latest build credentials save \ --platform ios \ --ios-distribution ad_hoc \ --apple-team-id YOUR_TEAM_ID \ --certificate './certificate.p12' \ --ios-provisioning-profile './adhoc_profile.mobileprovision' ``` No `--apple-key`, `--apple-key-id`, or `--apple-issuer-id` needed. ### Running an ad-hoc build [Section titled “Running an ad-hoc build”](#running-an-ad-hoc-build) ```bash npx @capgo/cli@latest build request \ --platform ios \ --ios-distribution ad_hoc ``` To collect the IPA as a build artifact, add `--output-upload`: ```bash npx @capgo/cli@latest build request \ --platform ios \ --ios-distribution ad_hoc \ --output-upload ``` ### CI/CD with ad-hoc builds [Section titled “CI/CD with ad-hoc builds”](#cicd-with-ad-hoc-builds) For GitHub Actions, you need fewer secrets than App Store builds: .github/workflows/ios-adhoc-build.yml ```yaml name: iOS Ad-Hoc Build on: workflow_dispatch: jobs: ios-adhoc: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: oven-sh/setup-bun@v2 with: bun-version: latest - name: Request iOS ad-hoc build run: bunx @capgo/cli@latest build request --platform ios --ios-distribution ad_hoc --output-upload env: CAPGO_TOKEN: ${{ secrets.CAPGO_TOKEN }} APP_STORE_CONNECT_TEAM_ID: ${{ secrets.APP_STORE_CONNECT_TEAM_ID }} BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }} CAPGO_IOS_PROVISIONING_MAP: ${{ secrets.CAPGO_IOS_PROVISIONING_MAP_ADHOC }} CAPGO_IOS_DISTRIBUTION: ad_hoc ``` Tip Notice that `APPLE_KEY_ID`, `APPLE_ISSUER_ID`, and `APPLE_KEY_CONTENT` are not needed for ad-hoc builds. ## Apps with Extensions (Multi-Target Signing) [Section titled “Apps with Extensions (Multi-Target Signing)”](#apps-with-extensions-multi-target-signing) If your app includes extensions (share extensions, widgets, notification service extensions, etc.), each extension target needs its own provisioning profile. Capgo supports this via the repeatable `--ios-provisioning-profile` flag. ### Example: App + Share Extension [Section titled “Example: App + Share Extension”](#example-app--share-extension) ```bash npx @capgo/cli@latest build credentials save \ --platform ios \ --apple-team-id YOUR_TEAM_ID \ --apple-key '/path/to/AuthKey_XXXXX.p8' \ --apple-key-id YOUR_KEY_ID \ --apple-issuer-id YOUR_ISSUER_ID \ --certificate '/path/to/certificate.p12' \ --ios-provisioning-profile "com.example.app=./app_profile.mobileprovision" \ --ios-provisioning-profile "com.example.app.share-extension=./share_ext_profile.mobileprovision" ``` Each `--ios-provisioning-profile` flag maps a bundle ID to its provisioning profile file. The CLI: 1. Reads each mobileprovision file 2. Auto-extracts the profile name from the embedded plist 3. Base64-encodes the file 4. Stores everything as a single `CAPGO_IOS_PROVISIONING_MAP` credential Tip You need one distribution certificate for all targets — only provisioning profiles differ per target. ### Migration from Single Profile [Section titled “Migration from Single Profile”](#migration-from-single-profile) If you previously used `BUILD_PROVISION_PROFILE_BASE64` (single profile), run: ```bash npx @capgo/cli build credentials migrate --platform ios ``` This converts your existing single-profile credentials to the new `CAPGO_IOS_PROVISIONING_MAP` format and removes the legacy keys (`BUILD_PROVISION_PROFILE_BASE64`, `APPLE_PROFILE_NAME`). After migration, add extension profiles with the `update` command (additive merge): ```bash npx @capgo/cli build credentials update \ --platform ios \ --ios-provisioning-profile "com.example.app.share-extension=./share_ext_profile.mobileprovision" ``` Tip Use `update` (not `save`) to add profiles to existing credentials. The `update` command merges new profiles with existing ones. Use `save` only when setting up all credentials from scratch. ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) ### Provisioning profile doesn’t include the XYZ capability. [Section titled “Provisioning profile doesn’t include the XYZ capability.”](#provisioning-profile-doesnt-include-the-xyz-capability) Sometimes, you might see the following error: ```plaintext Provisioning profile "YOUR_PROVISIONING_PROFILE_NAME" doesn't include the XYZ capability. (in target 'App' from project 'App')" ``` This happens because you have enabled a new capability after the provisioning profile was created. The old provisioning profile does not include the new capability yet. To fix this, you need to regenerate the provisioning profile. 1. Open [Apple Developer Portal](https://developer.apple.com/account/) 2. Select the correct team in the dropdown ![Apple Developer keys team selection](/native-build-assets/apple-developer-select-team-2.webp) 3. Click on the `Profiles` button ![Apple Developer profiles button](/native-build-assets/apple-developer-profiles-button.webp) 4. Find the provisioning profile you want to regenerate ![Apple Developer find profile](/native-build-assets/pple-developer-find-profile.webp) 5. Click on the `Edit` button ![Apple Developer edit profile](/native-build-assets/apple-developer-edit-profile.webp) 6. Click on the `Save` button ![Apple Developer save profile](/native-build-assets/apple-developer-save-profile.webp) 7. Click on the `Download` button ![Apple Developer download profile](/native-build-assets/apple-developer-download-profile.webp) 8. [Re-run the Capgo Native Build setup command](/docs/cli/cloud-build/ios/#running-the-configuration-command) with the newly downloaded profile. This should fix the issue. ### Other issues [Section titled “Other issues”](#other-issues) If for whatever reason you are having issues either with Capgo Native Build, configuring the credentials or building the app, please don’t hesitate to reach via our [support](https://support.capgo.app/). # Troubleshooting > Common issues and solutions for Capgo Cloud Build Solutions to common issues when building native apps with Capgo Cloud Build. ## Build Failures [Section titled “Build Failures”](#build-failures) ### ”Upload failed” or “Connection timeout” [Section titled “”Upload failed” or “Connection timeout””](#upload-failed-or-connection-timeout) **Symptoms:** * Build fails during project upload * Timeout errors after 60 seconds **Solutions:** 1. **Check your internet connection** ```bash # Test connection to Capgo curl -I https://api.capgo.app ``` 2. **Reduce project size** * Ensure `node_modules/` is not being uploaded (should be auto-excluded) * Check for large files in your project: ```bash find . -type f -size +10M ``` 3. **Check upload URL expiration** * Upload URLs expire after 1 hour * If you get an expired URL error, re-run the build command Tip Large projects (>200MB) may hit timeout limits. Contact support for enterprise options. ### ”Build timeout after 10 minutes” [Section titled “”Build timeout after 10 minutes””](#build-timeout-after-10-minutes) **Symptoms:** * Build exceeds maximum allowed time * Status shows `timeout` **Solutions:** 1. **Optimize dependencies** * Remove unused npm packages * Use `npm prune --production` before building 2. **Check for network issues in build** * Some dependencies may download large files during build * Consider pre-caching with a lock file 3. **Review native dependencies** ```bash # iOS - check Podfile for heavy dependencies cat ios/App/Podfile # Android - check build.gradle cat android/app/build.gradle ``` 4. **Contact support** * If your app legitimately needs more time * We can adjust limits for specific use cases ## Authentication Issues [Section titled “Authentication Issues”](#authentication-issues) ### ”API key invalid” or “Unauthorized” [Section titled “”API key invalid” or “Unauthorized””](#api-key-invalid-or-unauthorized) **Symptoms:** * Build fails immediately with authentication error * 401 or 403 errors **Solutions:** 1. **Verify API key is correct** ```bash # Test with a simple command npx @capgo/cli@latest app list ``` 2. **Check API key permissions** * Key must have `write` or `all` permissions * Check in Capgo dashboard under API Keys 3. **Ensure API key is being read** ```bash # Check environment variable echo $CAPGO_TOKEN # Or verify local .capgo file cat .capgo ``` 4. **Re-authenticate** ```bash npx @capgo/cli@latest login ``` ### ”App not found” or “No permission for this app” [Section titled “”App not found” or “No permission for this app””](#app-not-found-or-no-permission-for-this-app) **Symptoms:** * Authentication works but app-specific error **Solutions:** 1. **Verify app is registered** ```bash npx @capgo/cli@latest app list ``` 2. **Check app ID matches** * Verify `capacitor.config.json` appId * Ensure command uses correct app ID 3. **Verify organization access** * Check you’re in the correct organization * API key must have access to the app’s organization ## iOS Build Issues [Section titled “iOS Build Issues”](#ios-build-issues) ### ”Code signing failed” [Section titled “”Code signing failed””](#code-signing-failed) **Symptoms:** * Build fails during code signing phase * Xcode errors about certificates or profiles **Solutions:** 1. **Verify certificate type matches build type** * Development builds need Development certificates * App Store builds need Distribution certificates 2. **Check certificate and profile match** ```bash # Decode and inspect your certificate echo $BUILD_CERTIFICATE_BASE64 | base64 -d > cert.p12 openssl pkcs12 -in cert.p12 -nokeys -passin pass:$P12_PASSWORD | openssl x509 -noout -subject ``` 3. **Ensure provisioning profile is valid** * Check expiration date * Verify it includes your App ID * Confirm it includes the certificate 4. **Regenerate credentials** * Delete old certificate/profile * Create new ones in Apple Developer portal * Re-encode and update environment variables ### ”Provisioning profile doesn’t include signing certificate” [Section titled “”Provisioning profile doesn’t include signing certificate””](#provisioning-profile-doesnt-include-signing-certificate) **Symptoms:** * Xcode can’t find certificate in profile **Solutions:** 1. **Download latest profile from Apple** * Go to Apple Developer → Certificates, IDs & Profiles * Download provisioning profile * Ensure it includes your certificate 2. **Verify certificate is in profile** ```bash # Extract profile echo $BUILD_PROVISION_PROFILE_BASE64 | base64 -d > profile.mobileprovision # View profile contents security cms -D -i profile.mobileprovision ``` 3. **Recreate profile with correct certificate** * In Apple Developer portal, edit profile * Ensure your distribution certificate is selected * Download and re-encode ### ”App Store Connect authentication failed” [Section titled “”App Store Connect authentication failed””](#app-store-connect-authentication-failed) **Symptoms:** * Upload to TestFlight fails * API key errors **Solutions:** 1. **Verify API key credentials** * Check APPLE\_KEY\_ID (should be 10 characters) * Check APPLE\_ISSUER\_ID (should be UUID format) * Verify APPLE\_KEY\_CONTENT is correctly base64-encoded 2. **Test API key locally** ```bash # Decode key echo $APPLE_KEY_CONTENT | base64 -d > AuthKey.p8 # Test with fastlane (if installed) fastlane pilot list ``` 3. **Check API key permissions** * Key needs “Developer” role or higher * Verify in App Store Connect → Users and Access → Keys 4. **Ensure key is not revoked** * Check in App Store Connect * Generate new key if needed ### ”Pod install failed” [Section titled “”Pod install failed””](#pod-install-failed) **Symptoms:** * Build fails during CocoaPods installation * Podfile errors **Solutions:** 1. **Verify Podfile.lock is committed** ```bash git status ios/App/Podfile.lock ``` 2. **Test pod install locally** ```bash cd ios/App pod install ``` 3. **Check for incompatible pods** * Review Podfile for version conflicts * Ensure all pods support your iOS deployment target 4. **Clear pod cache** ```bash cd ios/App rm -rf Pods rm Podfile.lock pod install # Then commit new Podfile.lock ``` ## Android Build Issues [Section titled “Android Build Issues”](#android-build-issues) ### ”Keystore password incorrect” [Section titled “”Keystore password incorrect””](#keystore-password-incorrect) **Symptoms:** * Build fails during signing * Gradle errors about keystore **Solutions:** 1. **Verify keystore password** ```bash # Test keystore locally keytool -list -keystore my-release-key.keystore # Enter password when prompted ``` 2. **Check environment variables** ```bash # Ensure no extra spaces or special characters echo "$KEYSTORE_STORE_PASSWORD" | cat -A echo "$KEYSTORE_KEY_PASSWORD" | cat -A ``` 3. **Verify base64 encoding** ```bash # Decode and test echo $ANDROID_KEYSTORE_FILE | base64 -d > test.keystore keytool -list -keystore test.keystore ``` ### ”Key alias not found” [Section titled “”Key alias not found””](#key-alias-not-found) **Symptoms:** * Signing fails with alias error **Solutions:** 1. **List keystore aliases** ```bash keytool -list -keystore my-release-key.keystore ``` 2. **Verify alias matches exactly** * Alias is case-sensitive * Check for typos in KEYSTORE\_KEY\_ALIAS 3. **Use correct alias from keystore** ```bash # Update environment variable to match export KEYSTORE_KEY_ALIAS="the-exact-alias-name" ``` ### ”Gradle build failed” [Section titled “”Gradle build failed””](#gradle-build-failed) **Symptoms:** * Generic Gradle errors * Compilation or dependency issues **Solutions:** 1. **Test build locally first** ```bash cd android ./gradlew clean ./gradlew assembleRelease ``` 2. **Check for missing dependencies** * Review build.gradle files * Ensure all plugins are listed in dependencies 3. **Verify Gradle version compatibility** ```bash # Check gradle version cat android/gradle/wrapper/gradle-wrapper.properties ``` 4. **Clear Gradle cache** ```bash cd android ./gradlew clean rm -rf .gradle build ``` ### ”Play Store upload failed” [Section titled “”Play Store upload failed””](#play-store-upload-failed) **Symptoms:** * Build succeeds but upload fails * Service account errors **Solutions:** 1. **Verify service account JSON** ```bash # Decode and check format echo $PLAY_CONFIG_JSON | base64 -d | jq . ``` 2. **Check service account permissions** * Go to Play Console → Setup → API Access * Ensure service account has access to your app * Grant “Release to testing tracks” permission 3. **Verify app is set up in Play Console** * App must be created in Play Console first * At least one APK must be uploaded manually initially 4. **Check API is enabled** * Google Play Developer API must be enabled * Check in Google Cloud Console ## General Issues [Section titled “General Issues”](#general-issues) ### ”Job not found” or “Build status unavailable” [Section titled “”Job not found” or “Build status unavailable””](#job-not-found-or-build-status-unavailable) **Symptoms:** * Cannot check build status * Job ID errors **Solutions:** 1. **Wait a moment and retry** * Build jobs may take a few seconds to initialize 2. **Check job ID is correct** * Verify the job ID from the initial build response 3. **Check build hasn’t expired** * Build data is available for 24 hours ### ”Project sync failed” [Section titled “”Project sync failed””](#project-sync-failed) **Symptoms:** * Build fails before compilation starts * Missing files errors **Solutions:** 1. **Run Capacitor sync locally** ```bash npx cap sync ``` 2. **Ensure all native files are committed** ```bash git status ios/ android/ ``` 3. **Check for gitignored native files** * Review .gitignore * Ensure important config files aren’t ignored ### ”Build succeeded but I don’t see output” [Section titled “”Build succeeded but I don’t see output””](#build-succeeded-but-i-dont-see-output) **Symptoms:** * Build shows success but no download link **Solutions:** 1. **Check build configuration** * Artifact storage may not be configured * For public beta, contact support about artifact access 2. **For iOS TestFlight submission** * Check App Store Connect * Processing may take 5-30 minutes after upload 3. **For Android Play Store** * Check Play Console → Testing → Internal testing * Processing may take a few minutes ## CI/CD Specific Issues [Section titled “CI/CD Specific Issues”](#cicd-specific-issues) ### GitHub Actions: “Command not found” [Section titled “GitHub Actions: “Command not found””](#github-actions-command-not-found) **Symptoms:** * `npx @capgo/cli` fails in CI **Solutions:** 1. **Ensure Node.js is installed** ```yaml - uses: actions/setup-node@v6 with: node-version: '24' ``` 2. **Install CLI explicitly** ```yaml - run: npm install -g @capgo/cli ``` ### GitHub Actions: “Secrets not found” [Section titled “GitHub Actions: “Secrets not found””](#github-actions-secrets-not-found) **Symptoms:** * Environment variables empty in build **Solutions:** 1. **Verify secrets are set** * Go to repo Settings → Secrets and variables → Actions * Add all required secrets 2. **Use correct syntax** ```yaml env: CAPGO_TOKEN: ${{ secrets.CAPGO_TOKEN }} ``` 3. **Check secret names match** * Names are case-sensitive * No typos in secret references ## Getting More Help [Section titled “Getting More Help”](#getting-more-help) ### Enable Verbose Logging [Section titled “Enable Verbose Logging”](#enable-verbose-logging) ```bash # Add debug flag (when available) npx @capgo/cli@latest build com.example.app --verbose ``` ### Collect Build Information [Section titled “Collect Build Information”](#collect-build-information) When contacting support, include: 1. **Build command used** ```bash npx @capgo/cli@latest build com.example.app --platform ios ``` 2. **Error message** (full output) 3. **Job ID** (from build output) 4. **Build logs** (copy full terminal output) 5. **Environment info** ```bash node --version npm --version npx @capgo/cli --version ``` ### Contact Support [Section titled “Contact Support”](#contact-support) * **Discord**: [Join our community](https://discord.com/invite/VnYRvBfgA6) * **Email**: * **Documentation**: [Capgo Docs](/docs/) ### Known Limitations [Section titled “Known Limitations”](#known-limitations) Current limitations during public beta: * Maximum build time: 10 minutes * Maximum upload size: \~500MB * iOS builds require 24-hour Mac leases, build on Mac will enqueue to ensure optimal usage * Build artifact download may not be available These limitations may be adjusted based on feedback. ## Additional Resources [Section titled “Additional Resources”](#additional-resources) * [Getting Started](/docs/cli/cloud-build/getting-started/) - Initial setup guide * [iOS Builds](/docs/cli/cloud-build/ios/) - iOS-specific configuration * [Android Builds](/docs/cli/cloud-build/android/) - Android-specific configuration * [CLI Reference](/docs/cli/reference/build/) - Complete command documentation # Commands > Capgo CLI documentation, how to use it and what is used for ### Usage [Section titled “Usage”](#usage) All command should be run in your app folder with capacitor project ignited properly. [Capacitor Cross-platform native runtime for web apps ](https://capacitorjs.com/docs/getting-started/) ### **Init** [Section titled “Init”](#init) `npx @capgo/cli@latest init [apikey]` This method is here to onboard you step by step. It will add your app to Capgo. It will add the code to your app to validate the update. Likewise, it will build your app. Furthermore, it will upload your app to Capgo. And it will help you to check if the update works. ### **Login** [Section titled “Login”](#login) `npx @capgo/cli login [apikey]` This method is here to remember the `apikey` for you. Note use `--apikey=********` in any command to override it **Optionally you can give:** `--local` This will store your **apikey** in the local repo and git ignore it. ## **Doctor** [Section titled “Doctor”](#doctor) `npx @capgo/cli doctor` Command to check if you are up-to-date with Capgo packages. This command will also be useful for bug report. ## App [Section titled “App”](#app) ### **Add** [Section titled “Add”](#add) `npx @capgo/cli app add [appId]` `[appId]` your app ID the format `com.test.app` is explained [here](https://capacitorjs.com/docs/cli/commands/init/). > 💡 All option will be guessed in your config if not provided. Optionally, you can give: * `--icon [/path/to/my/icon]` to have a custom icon display in Capgo web app. * `--name [test]` to have a custom name in the list. * `--apikey [key]` API key to link to your account. * `--retention [retention]` retention period of app bundle in days, 0 by default = infinite. Example of `capacitor.config.json` for appId and AppName, the icon is guess in the resources folder ```json { "appId": "ee.forgr.capacitor_go", "appName": "Capgo", "webDir": "dist" } ``` ### **Set** [Section titled “Set”](#set) `npx @capgo/cli app set [appId]` `[appId]` is your app ID, the format is explained [here](https://capacitorjs.com/docs/cli/commands/init/). Optionally, you can give: * `--icon [/path/to/my/icon]` to have a custom icon display in Capgo web app. * `--name [test]` to have a custom name in the list. * `--retention [retention]` retention period of app bundle in days, 0 by default = infinite. * `--apikey [key]` API key to link to your account. ### **List** [Section titled “List”](#list) `npx @capgo/cli app list [appId]` `[appId]` your app ID the format `com.test.app` is explained [here](https://capacitorjs.com/docs/cli/commands/init/). Optionally, you can give: * `--apikey [key]` API key to link to your account. ### **Delete** [Section titled “Delete”](#delete) `npx @capgo/cli app delete [appId]` `[appId]` your app ID the format `com.test.app` is explained [here](https://capacitorjs.com/docs/cli/commands/init/). Optionally, you can give: * `--apikey [key]` API key to link to your account. * `--bundle` with the version number will only delete this version. ### Debug [Section titled “Debug”](#debug) `npx @capgo/cli app debug [appId]` `[appId]` your app ID the format `com.test.app` is explained [here](https://capacitorjs.com/docs/cli/commands/init/). Optionally, you can give: * `--apikey [key]` API key to link to your account. * `--device` with the specific device you want to debug ### Setting [Section titled “Setting”](#setting) `npx @capgo/cli app setting [path]` Edit the Capacitor config. `[path]` - path of the setting that you would like to change. For example, to change the `appId`, provide `appId`. If you wish to disable auto update in the `capacitor-updater` provide `plugins.CapacitorUpdater.autoUpdate` You MUST provide either `--string` or `--bool`! Options: * `--string ` - sets the setting to a string * `--bool ` - sets the setting to a boolean ## Bundle [Section titled “Bundle”](#bundle) ### Upload [Section titled “Upload”](#upload) `npx @capgo/cli bundle upload [appId]` `[appId]` is your app ID, the format is explained [here](https://capacitorjs.com/docs/cli/commands/init/). Optionally, you can give: * `--apikey ` API key to link to your account. * `--path ` Path of the folder to upload. * `--channel ` Channel to link to. * `--external ` Link to external URL instead of uploading to Capgo Cloud. * `--iv-session-key ` Set the IV and session key for bundle URL external. * `--s3-endpoint ` URL of S3 endpoint. Does not work with delta uploads or the external option. * `--s3-region ` Region for your S3 bucket. * `--s3-apikey ` API key for your S3 endpoint. * `--s3-apisecret ` API secret for your S3 endpoint. * `--s3-bucket-name ` Name for your AWS S3 bucket. * `--s3-port ` Port for your S3 endpoint. * `--no-s3-ssl` Disable SSL for S3 upload. * `--key ` Custom path for public signing key (v1 system). * `--key-data ` Public signing key (v1 system). * `--key-v2 ` Custom path for private signing key (v2 system). * `--key-data-v2 ` Private signing key (v2 system). * `--bundle-url` Prints bundle URL into stdout. * `--no-key` Ignore signing key and send clear update. * `--no-code-check` Ignore checking if notifyAppReady() is called in source code and index present in root folder. * `--display-iv-session` Show in the console the IV and session key used to encrypt the update. * `--bundle ` Bundle version number of the bundle to upload. * `--min-update-version ` Minimal version required to update to this version. Used only if the disable auto update is set to metadata in channel. * `--auto-min-update-version` Set the min update version based on native packages. * `--ignore-metadata-check` Ignores the metadata (node\_modules) check when uploading. * `--ignore-checksum-check` Ignores the checksum check when uploading. * `--timeout ` Timeout for the upload process in seconds. * `--delta` Uploads Delta (manifest) files alongside the full bundle. * `--delta-only` Uploads only Delta (manifest) updates, skipping the full bundle. * `--no-delta` Disables Delta (manifest) uploads (useful if `directUpdate` is enabled but you want a full bundle). * `--tus` Upload the bundle using tus protocol. * `--multipart` Uses multipart protocol to upload data to S3, Deprecated, use TUS instead. * `--encrypted-checksum ` An encrypted checksum (signature). Used only when uploading an external bundle. * `--package-json ` A path to package.json. Useful for monorepos. * `--auto-set-bundle` Set the bundle in capacitor.config.json. * `--node-modules ` A list of path to node\_modules. Useful for monorepos (comma separated ex: ../../node\_modules,./node\_modules) > ⭐️ External option helps to unlock 2 cases: corporate with privacy concern, don’t send the code to a third part and app bigger than 200 MB. With this setting, Capgo store only the link to the zip and sends the link to all apps. > 👀 Capgo cloud never looks at what is in the link (for external option), or in the code when stored. > 🔑 You can add a second layer of security by using encryption, then Capgo will not be able to look or modify anything, it becomes “trustless”. Example of `package.json` for version ```json { "version": "1.0.2" } ``` > ⛔ Version should be greater than “0.0.0”. > 💡 Don’t forget to update the version number each time you send one, version number cannot be overridden, or reused after deletion for security reason. ### **List** [Section titled “List”](#list-1) `npx @capgo/cli bundle list [appId]` `[appId]` your app ID the format `com.test.app` is explained [here](https://capacitorjs.com/docs/cli/commands/init/). Optionally, you can give: * `--apikey [key]` API key to link to your account. ### **Delete** [Section titled “Delete”](#delete-1) `npx @capgo/cli bundle delete [appId]` `[appId]` your app ID the format `com.test.app` is explained [here](https://capacitorjs.com/docs/cli/commands/init/). Optionally, you can give: * `--apikey [key]` API key to link to your account. * `--bundle` with the version number will only delete this version. ### Cleanup [Section titled “Cleanup”](#cleanup) in a SemVer range for a major version to Cloud `npx @capgo/cli bundle cleanup [appId] --bundle=[majorVersion] --keep=[numberToKeep]` `[appId]` your app ID the format `com.test.app` is explained [here](https://capacitorjs.com/docs/cli/commands/init/). Optionally, you can give: * `--apikey [key]` API key to link to your account. * `--bundle [majorVersion]` a version you wish to remove previous packages for, it will keep the last one + `numberToKeep`. * `--keep [numberToKeep]` the number of packages you wish to keep (default 4). For example: If you have 10 versions from 10.0.1 to 10.0.11, and you use `npx @capgo/cli cleanup [appId] --bundle=10.0.0` it will remove 10.0.1 to 10.0.6. 10.0.7 until 10.0.11 will be kept. If you have 20 versions in total, and you don’t provide a bundle number like this: `npx @capgo/cli cleanup [appId] --keep=2` It will remove 18 versions, and keep the last 2. > This command will ask for confirmation, it shows a table of what it will be keeping and removing. Note This command will ignore bundles which are currently in use in any channel. ### **Encrypt** [Section titled “Encrypt”](#encrypt) > **Warning**: This command is deprecated and will be removed in the next major release. Please use the new encryption system. `npx @capgo/cli bundle encrypt [path/to/zip]` This command is used when you use external source to store your code or for test purpose. Optionally, you can give: `--key [/path/to/my/private_key]` the path of your private key. `--key-data [privateKey]` the private key data, if you want to use inline. The command will print your `ivSessionKey`y and generate an encrypted zip, to use it with the upload command or decryt command. ### **Encrypt V2** [Section titled “Encrypt V2”](#encrypt-v2) `npx @capgo/cli bundle encrypt [path/to/zip] [checksum]` This command is used when you use external source to store your code or for test purpose. The checksum is the sha256 of the bundle (generated by —key-v2), it is used to verify the integrity of the file after decryption. It will be enncrypted with the private key and sent along with the bundle. In encryption v2 the checksum is upgraded to become a “signature” of the bundle. Optionally, you can give: `--key [/path/to/my/private_key]` the path of your private key. `--key-data [privateKey]` the private key data, if you want to use inline. `--json` to output info as json. The command will print your `ivSessionKey`y and generate an encrypted zip, to use it with the upload command or decryt command. ### **Decrypt** [Section titled “Decrypt”](#decrypt) `npx @capgo/cli bundle decrypt [path/to/zip] [ivSessionKey]` Optionally, you can give: `--key [/path/to/my/private_key]` the path of your private key. `--key-data [privateKey]` the private key data, if you want to use inline. This command is mainly used for test purpose, it will decrypt the zip and print the base64 decrypted session key in the console. ### **Decrypt V2** [Section titled “Decrypt V2”](#decrypt-v2) `npx @capgo/cli bundle decryptV2 [path/to/zip] [ivSessionKey]` Optionally, you can give: `--key [/path/to/my/private_key]` the path of your private key. `--key-data [privateKey]` the private key data, if you want to use inline. This command is mainly used for test purpose, it will decrypt the zip and print the base64 decrypted session key in the console. `--checksum [checksum]` the checksum of the file, it will verify the checksum after decryption. ### **Zip** [Section titled “Zip”](#zip) `npx @capgo/cli bundle zip [appId]` `[appId]` is your app ID, the format is explained [here](https://capacitorjs.com/docs/cli/commands/init/). Optionally, you can give: * `--path [/path/to/my/bundle]` to upload a specific folder. * `--bundle [1.0.0]` to set the bundle version number of the filename. * `--name [myapp]` to override the filename. * `--json` to output info as json. * `--no-code-check` to ignore the code check and send the bundle anyway. * `--key-v2` to use the new encryption system. This is required as new encryption system use better checksums to verify the integrity of the file. ### **Compatibility** [Section titled “Compatibility”](#compatibility) `npx @capgo/cli bundle compatibility [appId] -c [channelId]` `[appId]` is your app ID, the format is explained [here](https://capacitorjs.com/docs/cli/commands/init/). `[channelId]` the name of your new channel. Optionally, you can give: * `--apikey [key]` API key to link to your account. * `--text` use text instead of emojis in the table * `--channel [channel]` the channel to check the compatibility with. * `--package-json ` A path to package.json. Useful for monorepos * `--node-modules ` A list of path to node\_modules. Useful for monorepos (comma separated ex: ../../node\_modules,./node\_modules) ## Channel [Section titled “Channel”](#channel) ### **Add** [Section titled “Add”](#add-1) `npx @capgo/cli channel add [channelId] [appId]` `[channelId]` the name of your new channel. `[appId]` your app ID the format `com.test.app` is explained [here](https://capacitorjs.com/docs/cli/commands/init/). ### **Delete** [Section titled “Delete”](#delete-2) `npx @capgo/cli channel delete [channelId] [appId]` `[channelId]` the name of your channel you want to delete. `[appId]` your app ID the format `com.test.app` is explained [here](https://capacitorjs.com/docs/cli/commands/init/). ### **List** [Section titled “List”](#list-2) `npx @capgo/cli channel list [appId]` `[appId]` your app ID the format `com.test.app` is explained [here](https://capacitorjs.com/docs/cli/commands/init/). Optionally, you can give: * `--apikey [key]` API key to link to your account. ### **Set** [Section titled “Set”](#set-1) `npx @capgo/cli channel set [channelId] [appId]` `[appId]` is your app ID, the format is explained [here](https://capacitorjs.com/docs/cli/commands/init/). Optionally, you can give: * `--bundle [1.2.3]` your app bundle already sent to the cloud, to link it to a channel. * `--latest` get the bundle version from `package.json:version`, cannot be used with `--bundle`. * `--state [ normal | default ]` set the channel state, can be `normal` or `default`. One channel needs to be `default`. * `--downgrade` allows the channel to send downgrade version to devices. * `--no-downgrade` disallows the channel to send downgrade version to devices. * `--upgrade` allows the channel to send upgrade (major) version to devices. * `--no-upgrade` disallow the channel to send upgrade (major) version to devices. * `--ios` allows the channel to send version to iOS devices. * `--no-ios` disallows the channel to send version to iOS devices. * `--android` allows the channel to send version to android devices. * `--no-android` disallows the channel to send version to android devices. * `--self-assign` allows devices to self assign to this channel. * `--no-self-assign` disallows devices to self assign to this channel. * `--disable-auto-update STRATEGY` Disable auto update strategy for this channel. The possible options are: major, minor, metadata, none. * `--apikey [key]` API key to link to your account. ## Disable updates strategy [Section titled “Disable updates strategy”](#disable-updates-strategy) There are a few ways to handle disabling updates for too old versions.\ Capgo cannot update native code thus an update from a version with the old native code to a version with the updated native code should not be possible. There are a couple of ways to achieve that. First, the `major` strategy. It prevents an update from `0.0.0` -> `1.0.0`. The major is the highlighted number (**1**.0.0 and **0**.0.0).\ Second is the `minor` strategy. It prevents an update from `0.0.0` -> `1.1.0` or an update from `1.1.0` to `1.2.0`. **BE AWARE** this strategy does not prevent an update from `0.1.0` -> `1.1.0` Third, the `patch` strategy. It was added into capgo as a very strict mode. It’s not recommended to be used unless you fully understand how it works. In order for it to accept a update the following conditions must be meet: * The major is the same between the new and the old version * The minor is the same between the new and the old version * The patch of the new version if greater then the patch of the old version Here is an example of which scenarios the update is allowed or denied * 0.0.311 -> 0.0.314 ✅ * 0.0.0 -> 0.0.314 ✅ * 0.0.316 -> 0.0.314 ❌ * 0.1.312 -> 0.0.314 ❌ * 1.0.312 -> 0.0.314 ❌ Lastly the most complicated strategy. The `metadata` strategy.\ First you need to know that initially after you enable it the updates **WILL** fail as the channel is lacking the required metadata.\ If the channel is lacking metadata you will see a message like this: ![Cannot find metadata](/fail-metadata.webp) If you see something like this you know that you have to go to the current bundle for the failing channel and set the metadata.\ First, figure out what channel is failing. You can do that by looking at the `misconfigured` column ![Misconfigured table](/misconfigured-table.webp) Then go to the failing channel and click on `Bundle number`. This should take you to the bundle page. ![Locate failing channel](/fail-channel-show.webp) Once there fill the `Minimal update version` field. This should be a [semver](https://devhints.io/semver/).\ If the value you pass is not a semver you will get an error, but if everything goes correctly you should see something like this: ![Set min version](/set-min-update-version.webp) Now, you likely do not want to set this data manually every time you update. Fortunately, the CLI will prevent you from sending an update without this metadata ![CLI fail no metadata](/cli-fail-no-metadata.webp) To properly upload a bundle when using the `metadata` option you need to pass the `--min-update-version` with the valid semver. Something like this: ![CLI upload with metadata](/cli-upload-with-metadata.webp) The `--min-update-version` is not the ONLY way to do compatibility. There also exists the `--auto-min-update-version`. Here is how it works. First, it takes a look at the version currently uploaded to the channel. It checks compatibility same as `bundle compatibility` command would. Second, if the new version is 100% compatible it reuses the `min_update_version` from the latest version in the channel. If not, then it sets the `min_update_version` to the bundle number of the newly uploaded version. You will always get an information what is the `min_update_version` when using this option. It will look something like this: ![Min update version](/min_update_version_info.webp) If the new version is not compatible it should look something like this ![Min update version not compatible](/min_update_version_not_compatible.webp) ## End-to-End encryption (Trustless) [Section titled “End-to-End encryption (Trustless)”](#end-to-end-encryption-trustless) Capgo supports end-to-end encryption, this means that your bundle(code) is encrypted before sent to the cloud and decrypted on the device. For that, you need to generate an RSA key pair, you can use the following command to generate it. The encryption system is a combination of RSA and AES, the RSA key is used to encrypt the AES key, and the AES key is used to encrypt the file. See below for more information about the encryption system. ![How crypto works](/crypto_explained.webp) Encryption schema ### Create key for your app [Section titled “Create key for your app”](#create-key-for-your-app) `npx @capgo/cli key create` Optionally, you can give: `--force` to overwrite the existing key. This command will create for you a key pair in your app, and will ask you to save the private key in a safe place. It’s recommended to not git commit the private key, and to not share it with anyone. > After your local test, remove the key from the config file and add it on the CI step with `key save` ### Save key in your app config [Section titled “Save key in your app config”](#save-key-in-your-app-config) `npx @capgo/cli key save` Optionally, you can give: `--key [/path/to/my/public_key]` the path of your public key file. `--key-data [publicKey]` the public key data, if you want to use inline. This command is useful if you followed the recommendation and didn’t commit the key in your app config. ## Ci integration [Section titled “Ci integration”](#ci-integration) To automate your work, I recommend you make GitHub action do the job of pushing to our server [GitHub action tutorial](https://capgo.app/blog/automatic-build-and-release-with-github-actions/) ## Our demo app [Section titled “Our demo app”](#our-demo-app) [GitHub - Cap-go/demo-app](https://github.com/Cap-go/demo-app/) Don’t forget to configure CI env variable with your API key # CLI From 0.x to 1.x > How to upgrade from 0.x to 1.x, of the updater of Capgo, learn what are the breaking changes and how to handle them There are no significant changes in the CLI. The breaking change is mainly the rename of the argument `--version` to `--bundle` to avoid conflict, and follow the new naming everywhere. # Encryption > How to encrypt your data with encryption v2, secure your app and ensure only you can update your users with your updates This documentation explains how to migrate to the encryption v2 system. Learn more about the encryption v2 system in the [blog post](/blog/introducing-end-to-end-security-to-capacitor-updater-with-code-signing). ## 1. Create Key Pair [Section titled “1. Create Key Pair”](#1-create-key-pair) ```bash npx @capgo/cli key create ``` Store the private key securely. Never commit it to source control or share it with untrusted parties. This command: * Creates a new key pair in your app * Removes the old key from your Capacitor config * Keeps old key files for backward compatibility ## 2. Update Capacitor Config [Section titled “2. Update Capacitor Config”](#2-update-capacitor-config) When prompted “Do you want to setup encryption with the new channel in order to support old apps and facilitate the migration?”, select yes. This adds a new `defaultChannel` option to your Capacitor config. capacitor.config.ts ```ts import { CapacitorConfig } from '@capacitor/cli'; const config: CapacitorConfig = { appId: 'com.example.app', appName: 'Example App', plugins: { CapacitorUpdater: { // ... other options defaultChannel: 'encryption_v2' // New apps will use this channel } } }; export default config; ``` ## 3. Upload Bundle to New Channel [Section titled “3. Upload Bundle to New Channel”](#3-upload-bundle-to-new-channel) ```bash npx @capgo/cli bundle upload --channel encryption_v2 ``` ## 4. Enable Self-Assignment [Section titled “4. Enable Self-Assignment”](#4-enable-self-assignment) Caution Required for the `defaultChannel` option to work ```bash npx @capgo/cli channel set encryption_v2 --self-assign ``` ## 5. Upload to Old Channel [Section titled “5. Upload to Old Channel”](#5-upload-to-old-channel) ```bash npx @capgo/cli bundle upload --channel production ``` Tip Capacitor config is never uploaded to Capgo ## 6. Cleanup (After 3-4 Months) [Section titled “6. Cleanup (After 3-4 Months)”](#6-cleanup-after-3-4-months) Once all users have updated their apps: 1. Remove `defaultChannel` from your Capacitor config 2. Delete the old channel: ```bash npx @capgo/cli channel delete encryption_v2 ``` Note Apps using `encryption_v2` as default will switch to `production` channel after deletion # Overview > This document provides a comprehensive overview of the Capgo CLI, how it can be utilized to enhance your app development process by enabling seamless live updates Use Capgo’s Live Updates feature to update the JavaScript bundles of your app remotely, in real-time. Push JS updates directly to your users without going through the app store review process to instantly fix bugs and ship new features. Note Live Updates are limited to JavaScript bundle changes. If you need to update native code, such as adding or removing a plugin or changing native project configuration, you’ll need to submit a new native binary build to the app stores. ## How Live Updates Work [Section titled “How Live Updates Work”](#how-live-updates-work) Capgo’s Live Update system has two key components: 1. The Capgo SDK, which you install in your app. The SDK checks for available updates and downloads them in the background. 2. Channels, which let you target updates to specific groups of users. You can use channels to manage different release tracks, such as `Production`, `Staging`, and `Dev`. When you upload a new JS bundle to Capgo and assign it to a channel, the Capgo SDK in apps configured for that channel will detect the update and download it. The next time the app restarts, the new bundle will be loaded. ## Getting Started [Section titled “Getting Started”](#getting-started) To start using Live Updates, follow these steps: 1. Complete the [Capgo Quickstart](/docs/getting-started/quickstart) to set up your app in Capgo and install the Capgo SDK. 2. In your app code, call `CapacitorUpdater.notifyAppReady()` after your app has finished initializing. This tells the Capgo SDK that your app is ready to receive updates. 3. Build your JS bundle and upload it to Capgo: ```shell npm run build npx @capgo/cli@latest bundle upload --channel=production ``` 4. Open your app and wait for the update to download. You can check the status with: ```shell npx @capgo/cli@latest app debug ``` 5. Once the update is downloaded, close and reopen your app to load the new bundle. See the [Deploying Live Updates](/docs/getting-started/deploy) guide for more details. ## The Capgo CLI [Section titled “The Capgo CLI”](#the-capgo-cli) The Capgo CLI is a powerful tool that allows developers to interact with Capgo’s services from their own CI/CD pipelines. With the CLI, you have granular control over when builds are produced and deployed, enabling you to integrate Capgo into your existing enterprise workflows. ### What is the Capgo CLI for? [Section titled “What is the Capgo CLI for?”](#what-is-the-capgo-cli-for) The Capgo CLI is designed for developers and teams who need more control and flexibility in their live update workflows. By using the CLI in your CI/CD pipelines, you can: * Decide exactly when to build and deploy updates, rather than relying on Capgo’s built-in automation * Insert your own processes, such as code signing, QA testing, or manager approvals, between the build and deploy steps * Integrate Capgo into your existing DevOps tooling and workflows ### Authentication [Section titled “Authentication”](#authentication) To use the Capgo CLI, you’ll need to authenticate with your API key. You can generate an API key in your Capgo account settings. To log in and securely store your API key, run: ```shell npx @capgo/cli@latest login [API_KEY] ``` This command will then be saved for future use. You won’t need to provide your API key with each command after logging in. ### Key Differences from Other CLI Tools [Section titled “Key Differences from Other CLI Tools”](#key-differences-from-other-cli-tools) If you’re familiar with other live update CLI tools, there are a few key things to note about Capgo’s CLI: * Capgo uses a single CLI for both development and CI/CD use cases, as Capgo is focused solely on the live update feature set. * The Capgo CLI doesn’t require a separate installation step. It’s bundled with the `@capgo/cli` package and can be run directly using `npx`. * Capgo’s CLI is designed specifically for the live update workflow, so it may not include some features or commands found in more general-purpose CLI tools. ## Next Steps [Section titled “Next Steps”](#next-steps) [ Channels](/docs/live-updates/channels/) [Learn how to use channels to manage different release tracks and target updates to specific users.](/docs/live-updates/channels/) [ Rollbacks](/docs/live-updates/rollbacks/) [Discover how to roll back to a previous JS bundle version if an update causes issues.](/docs/live-updates/rollbacks/) [ Update Behavior](/docs/live-updates/update-behavior/) [Customize how and when updates are downloaded and applied in your app.](/docs/live-updates/update-behavior/) [ Fast Updates](/docs/live-updates/differentials/) [Learn how to use fast updates to speed up the update process.](/docs/live-updates/differentials/) # 👤 account > 👤 Manage your Capgo account details and retrieve information for support or collaboration. 👤 Manage your Capgo account details and retrieve information for support or collaboration. ### []()🔹 **Id** [Section titled “ 🔹 Id”](#--id) ```bash npx @capgo/cli@latest account id ``` 🪪 Retrieve your account ID, safe to share for collaboration or support purposes in Discord or other platforms. **Example:** ```bash npx @capgo/cli@latest account id ``` **Options:** | Param | Type | Description | | ------- | -------- | ------------------------------- | | **-a,** | `string` | API key to link to your account | # 📱 app > 📱 Manage your Capgo app settings and configurations in Capgo Cloud. 📱 Manage your Capgo app settings and configurations in Capgo Cloud. ### []()➕ **Add** [Section titled “ ➕ Add”](#--add) **Alias:** `a` ```bash npx @capgo/cli@latest app add ``` ➕ Add a new app to Capgo Cloud with a unique app ID in the format com.test.app. All options can be guessed from config if not provided. **Example:** ```bash npx @capgo/cli@latest app add com.example.app --name "My App" --icon ./icon.png ``` **Options:** | Param | Type | Description | | -------------- | -------- | ---------------------------------------------------------------- | | **-n,** | `string` | App name for display in Capgo Cloud | | **-i,** | `string` | App icon path for display in Capgo Cloud | | **-a,** | `string` | API key to link to your account | | **—supa-host** | `string` | Custom Supabase host URL (for self-hosting or Capgo development) | | **—supa-anon** | `string` | Custom Supabase anon key (for self-hosting) | ### []()🗑️ **Delete** [Section titled “ 🗑️ Delete”](#-️-delete) ```bash npx @capgo/cli@latest app delete ``` 🗑️ Delete an app from Capgo Cloud, optionally specifying a version to delete only that bundle. **Example:** ```bash npx @capgo/cli@latest app delete com.example.app ``` **Options:** | Param | Type | Description | | -------------- | -------- | ---------------------------------------------------------------- | | **-a,** | `string` | API key to link to your account | | **—supa-host** | `string` | Custom Supabase host URL (for self-hosting or Capgo development) | | **—supa-anon** | `string` | Custom Supabase anon key (for self-hosting) | ### []()📋 **List** [Section titled “ 📋 List”](#--list) **Alias:** `l` ```bash npx @capgo/cli@latest app list ``` 📋 List all apps registered under your account in Capgo Cloud. **Example:** ```bash npx @capgo/cli@latest app list ``` **Options:** | Param | Type | Description | | -------------- | -------- | ---------------------------------------------------------------- | | **-a,** | `string` | API key to link to your account | | **—supa-host** | `string` | Custom Supabase host URL (for self-hosting or Capgo development) | | **—supa-anon** | `string` | Custom Supabase anon key (for self-hosting) | ### []()🐞 **Debug** [Section titled “ 🐞 Debug”](#--debug) ```bash npx @capgo/cli@latest app debug ``` 🐞 Listen for live update events in Capgo Cloud to debug your app. Optionally target a specific device for detailed diagnostics. **Example:** ```bash npx @capgo/cli@latest app debug com.example.app --device DEVICE_ID ``` **Options:** | Param | Type | Description | | -------------- | -------- | ---------------------------------------------------------------- | | **-a,** | `string` | API key to link to your account | | **-d,** | `string` | The specific device ID to debug | | **—supa-host** | `string` | Custom Supabase host URL (for self-hosting or Capgo development) | | **—supa-anon** | `string` | Custom Supabase anon key (for self-hosting) | ### []()⚙️ **Setting** [Section titled “ ⚙️ Setting”](#-️-setting) ```bash npx @capgo/cli@latest app setting ``` ⚙️ Modify Capacitor configuration programmatically. Specify setting path (e.g., plugins.CapacitorUpdater.defaultChannel) with —string or —bool. **Example:** ```bash npx @capgo/cli@latest app setting plugins.CapacitorUpdater.defaultChannel --string "Production" ``` **Options:** | Param | Type | Description | | ----------- | -------- | ----------------------------------------------------------------------- | | **—bool** | `string` | A value for the setting to modify as a boolean, ex: —bool true | | **—string** | `string` | A value for the setting to modify as a string, ex: —string “Production” | ### []()⚙️ **Set** [Section titled “ ⚙️ Set”](#-️-set) **Alias:** `s` ```bash npx @capgo/cli@latest app set ``` ⚙️ Update settings for an existing app in Capgo Cloud, such as name, icon, or retention period for bundles. Retention of 0 means infinite storage. **Example:** ```bash npx @capgo/cli@latest app set com.example.app --name "Updated App" --retention 30 ``` **Options:** | Param | Type | Description | | -------------------- | -------- | ------------------------------------------------------------------------------------ | | **-n,** | `string` | App name for display in Capgo Cloud | | **-i,** | `string` | App icon path for display in Capgo Cloud | | **-a,** | `string` | API key to link to your account | | **-r,** | `string` | Days to keep old bundles (0 = infinite, default: 0) | | **—expose-metadata** | `string` | Expose bundle metadata (link and comment) to the plugin (true/false, default: false) | | **—supa-host** | `string` | Custom Supabase host URL (for self-hosting or Capgo development) | | **—supa-anon** | `string` | Custom Supabase anon key (for self-hosting) | # 🔹 build > 🏗️ Manage native iOS/Android builds through Capgo Cloud. 🏗️ Manage native iOS/Android builds through Capgo Cloud. ### []()🔹 **Request** [Section titled “ 🔹 Request”](#--request) ```bash npx @capgo/cli@latest build request ``` Request a native build from Capgo Cloud. This command will zip your project directory and upload it to Capgo for building. The build will be processed and sent directly to app stores. 🔒 SECURITY: Credentials are never stored on Capgo servers. They are auto-deleted after build completion. Build outputs may optionally be uploaded for time-limited download links. 📋 PREREQUISITE: Save credentials first with: `npx @capgo/cli build credentials save --appId --platform ` **Example:** ```bash npx @capgo/cli@latest build request com.example.app --platform ios --path . ``` **Options:** | Param | Type | Description | | -------------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------ | | **—path** | `string` | Path to the project directory to build (default: current directory) | | **—platform** | `string` | Target platform: ios or android (required) | | **—build-mode** | `string` | Build mode: debug or release (default: release) | | **—build-certificate-base64** | `string` | iOS: Base64-encoded .p12 certificate | | **—p12-password** | `string` | iOS: Certificate password (optional if cert has no password) | | **—apple-id** | `string` | iOS: Apple ID email | | **—apple-app-specific-password** | `string` | iOS: App-specific password | | **—apple-key-id** | `string` | iOS: App Store Connect API Key ID | | **—apple-issuer-id** | `string` | iOS: App Store Connect Issuer ID | | **—apple-key-content** | `string` | iOS: Base64-encoded App Store Connect API key (.p8) | | **—app-store-connect-team-id** | `string` | iOS: App Store Connect Team ID | | **—ios-scheme** | `string` | iOS: Xcode scheme to build (default: App) | | **—ios-target** | `string` | iOS: Xcode target for reading build settings (default: same as scheme) | | **—ios-distribution** | `string` | iOS: Distribution mode | | **—ios-provisioning-profile** | `string` | iOS: Provisioning profile path or bundleId=path mapping (repeatable) | | **—android-keystore-file** | `string` | Android: Base64-encoded keystore file | | **—keystore-key-alias** | `string` | Android: Keystore key alias | | **—keystore-key-password** | `string` | Android: Keystore key password | | **—keystore-store-password** | `string` | Android: Keystore store password | | **—play-config-json** | `string` | Android: Base64-encoded Google Play service account JSON | | **—output-upload** | `boolean` | Override output upload behavior for this build only (enable). Precedence: CLI > env > saved credentials | | **—no-output-upload** | `boolean` | Override output upload behavior for this build only (disable). Precedence: CLI > env > saved credentials | | **—output-retention** | `string` | Override output link TTL for this build only (1h to 7d). Examples: 1h, 6h, 2d. Precedence: CLI > env > saved credentials | | **—skip-build-number-bump** | `boolean` | Skip automatic build number/version code incrementing. Uses whatever version is already in the project files. | | **—no-skip-build-number-bump** | `boolean` | Override saved credentials to re-enable automatic build number incrementing for this build only. | | **-a,** | `string` | API key to link to your account | | **—supa-host** | `string` | Custom Supabase host URL (for self-hosting or Capgo development) | | **—supa-anon** | `string` | Custom Supabase anon key (for self-hosting) | | **—verbose** | `boolean` | Enable verbose output with detailed logging | ### []()🔹 **Credentials** [Section titled “ 🔹 Credentials”](#--credentials) ```bash npx @capgo/cli@latest build credentials ``` Manage build credentials stored locally on your machine. 🔒 SECURITY: * Credentials saved to \~/.capgo-credentials/credentials.json (global) or .capgo-credentials.json (local) * When building, sent to Capgo but NEVER stored permanently * Deleted from Capgo immediately after build * Build outputs may optionally be uploaded for time-limited download links 📚 DOCUMENTATION: iOS setup: Android setup: # 📦 bundle > 📦 Manage app bundles for deployment in Capgo Cloud, including upload, compatibility checks, and encryption. 📦 Manage app bundles for deployment in Capgo Cloud, including upload, compatibility checks, and encryption. ### []()⬆️ **Upload** [Section titled “ ⬆️ Upload”](#-️-upload) **Alias:** `u` ```bash npx @capgo/cli@latest bundle upload ``` ⬆️ Upload a new app bundle to Capgo Cloud for distribution. Version must be > 0.0.0 and unique. Deleted versions cannot be reused for security. External option: Store only a URL link (useful for apps >200MB or privacy requirements). Capgo never inspects external content. Add encryption for trustless security. **Example:** ```bash npx @capgo/cli@latest bundle upload com.example.app --path ./dist --channel production ``` **Options:** | Param | Type | Description | | ----------------------------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------- | | **-a,** | `string` | API key to link to your account | | **-p,** | `string` | Path of the folder to upload, if not provided it will use the webDir set in capacitor.config | | **-c,** | `string` | Channel to link to | | **-e,** | `string` | Link to external URL instead of upload to Capgo Cloud | | **—iv-session-key** | `string` | Set the IV and session key for bundle URL external | | **—s3-region** | `string` | Region for your S3 bucket | | **—s3-apikey** | `string` | API key for your S3 endpoint | | **—s3-apisecret** | `string` | API secret for your S3 endpoint | | **—s3-endpoint** | `string` | URL of S3 endpoint | | **—s3-bucket-name** | `string` | Name for your AWS S3 bucket | | **—s3-port** | `string` | Port for your S3 endpoint | | **—no-s3-ssl** | `boolean` | Disable SSL for S3 upload | | **—key-v2** | `string` | Custom path for private signing key (v2 system) | | **—key-data-v2** | `string` | Private signing key (v2 system) | | **—bundle-url** | `boolean` | Prints bundle URL into stdout | | **—no-key** | `boolean` | Ignore signing key and send clear update | | **—no-code-check** | `boolean` | Ignore checking if notifyAppReady() is called in source code and index present in root folder | | **—display-iv-session** | `boolean` | Show in the console the IV and session key used to encrypt the update | | **-b,** | `string` | Bundle version number of the bundle to upload | | **—link** | `string` | Link to external resource (e.g. GitHub release) | | **—comment** | `string` | Comment about this version, could be a release note, a commit hash, a commit message, etc. | | **—min-update-version** | `string` | Minimal version required to update to this version. Used only if the disable auto update is set to metadata in channel | | **—auto-min-update-version** | `boolean` | Set the min update version based on native packages | | **—ignore-metadata-check** | `boolean` | Ignores the metadata (node\_modules) check when uploading | | **—ignore-checksum-check** | `boolean` | Ignores the checksum check when uploading | | **—force-crc32-checksum** | `boolean` | Force CRC32 checksum for upload (override auto-detection) | | **—timeout** | `string` | Timeout for the upload process in seconds | | **—multipart** | `boolean` | \[DEPRECATED] Use —tus instead. Uses multipart protocol for S3 uploads | | **—zip** | `boolean` | Upload the bundle using zip to Capgo cloud (legacy) | | **—tus** | `boolean` | Upload the bundle using TUS to Capgo cloud | | **—tus-chunk-size** | `string` | Chunk size in bytes for TUS resumable uploads (default: auto) | | **—partial** | `boolean` | \[DEPRECATED] Use —delta instead. Upload incremental updates | | **—partial-only** | `boolean` | \[DEPRECATED] Use —delta-only instead. Upload only incremental updates, skip full bundle | | **—delta** | `boolean` | Upload delta updates (only changed files) for instant, super fast updates instead of big zip downloads | | **—delta-only** | `boolean` | Upload only delta updates without full bundle for maximum speed (useful for large apps) | | **—no-delta** | `boolean` | Disable delta updates even if Direct Update is enabled | | **—encrypted-checksum** | `string` | An encrypted checksum (signature). Used only when uploading an external bundle. | | **—auto-set-bundle** | `boolean` | Set the bundle in capacitor.config.json | | **—dry-upload** | `boolean` | Dry upload the bundle process, mean it will not upload the files but add the row in database (Used by Capgo for internal testing) | | **—package-json** | `string` | Paths to package.json files for monorepos (comma-separated) | | **—node-modules** | `string` | Paths to node\_modules directories for monorepos (comma-separated) | | **—encrypt-partial** | `boolean` | Encrypt delta update files (auto-enabled for updater > 6.14.4) | | **—delete-linked-bundle-on-upload** | `boolean` | Locates the currently linked bundle in the channel you are trying to upload to, and deletes it | | **—no-brotli-patterns** | `string` | Files to exclude from Brotli compression (comma-separated globs, e.g., “*.jpg,*.png”) | | **—disable-brotli** | `boolean` | Completely disable brotli compression even if updater version supports it | | **—version-exists-ok** | `boolean` | Exit successfully if bundle version already exists, useful for CI/CD workflows with monorepos | | **—self-assign** | `boolean` | Allow devices to auto-join this channel (updates channel setting) | | **—supa-host** | `string` | Custom Supabase host URL (for self-hosting or Capgo development) | | **—supa-anon** | `string` | Custom Supabase anon key (for self-hosting) | | **—verbose** | `boolean` | Enable verbose output with detailed logging | ### []()🧪 **Compatibility** [Section titled “ 🧪 Compatibility”](#--compatibility) ```bash npx @capgo/cli@latest bundle compatibility ``` 🧪 Check compatibility of a bundle with a specific channel in Capgo Cloud to ensure updates are safe. **Example:** ```bash npx @capgo/cli@latest bundle compatibility com.example.app --channel production ``` **Options:** | Param | Type | Description | | ----------------- | --------- | ------------------------------------------------------------------ | | **-a,** | `string` | API key to link to your account | | **-c,** | `string` | Channel to check the compatibility with | | **—text** | `boolean` | Output text instead of emojis | | **—package-json** | `string` | Paths to package.json files for monorepos (comma-separated) | | **—node-modules** | `string` | Paths to node\_modules directories for monorepos (comma-separated) | | **—supa-host** | `string` | Custom Supabase host URL (for self-hosting or Capgo development) | | **—supa-anon** | `string` | Custom Supabase anon key (for self-hosting) | ### []()🔹 **ReleaseType** [Section titled “ 🔹 ReleaseType”](#--releasetype) ```bash npx @capgo/cli@latest bundle releaseType ``` 🧭 Print “native” or “OTA” based on compatibility with a channel’s latest metadata. **Example:** ```bash npx @capgo/cli@latest bundle releaseType com.example.app --channel production ``` **Options:** | Param | Type | Description | | ----------------- | -------- | ------------------------------------------------------------------ | | **-a,** | `string` | API key to link to your account | | **-c,** | `string` | Channel to compare against | | **—package-json** | `string` | Paths to package.json files for monorepos (comma-separated) | | **—node-modules** | `string` | Paths to node\_modules directories for monorepos (comma-separated) | | **—supa-host** | `string` | Custom Supabase host URL (for self-hosting or Capgo development) | | **—supa-anon** | `string` | Custom Supabase anon key (for self-hosting) | ### []()🗑️ **Delete** [Section titled “ 🗑️ Delete”](#-️-delete) **Alias:** `d` ```bash npx @capgo/cli@latest bundle delete ``` 🗑️ Delete a specific bundle from Capgo Cloud, optionally targeting a single version. **Example:** ```bash npx @capgo/cli@latest bundle delete BUNDLE_ID com.example.app ``` **Options:** | Param | Type | Description | | -------------- | -------- | ---------------------------------------------------------------- | | **-a,** | `string` | API key to link to your account | | **—supa-host** | `string` | Custom Supabase host URL (for self-hosting or Capgo development) | | **—supa-anon** | `string` | Custom Supabase anon key (for self-hosting) | ### []()📋 **List** [Section titled “ 📋 List”](#--list) **Alias:** `l` ```bash npx @capgo/cli@latest bundle list ``` 📋 List all bundles uploaded for an app in Capgo Cloud. **Example:** ```bash npx @capgo/cli@latest bundle list com.example.app ``` **Options:** | Param | Type | Description | | -------------- | -------- | ---------------------------------------------------------------- | | **-a,** | `string` | API key to link to your account | | **—supa-host** | `string` | Custom Supabase host URL (for self-hosting or Capgo development) | | **—supa-anon** | `string` | Custom Supabase anon key (for self-hosting) | ### []()🧹 **Cleanup** [Section titled “ 🧹 Cleanup”](#--cleanup) **Alias:** `c` ```bash npx @capgo/cli@latest bundle cleanup ``` 🧹 Delete old bundles in Capgo Cloud, keeping specified number of recent versions. Bundles linked to channels are preserved unless —ignore-channel is used. **Example:** ```bash npx @capgo/cli@latest bundle cleanup com.example.app --bundle=1.0 --keep=3 ``` **Options:** | Param | Type | Description | | ------------------- | --------- | ------------------------------------------------------------------------- | | **-b,** | `string` | Bundle version number of the app to delete | | **-a,** | `string` | API key to link to your account | | **-k,** | `string` | Number of versions to keep | | **-f,** | `string` | Force removal | | **—ignore-channel** | `boolean` | Delete bundles even if linked to channels (WARNING: deletes channels too) | | **—supa-host** | `string` | Custom Supabase host URL (for self-hosting or Capgo development) | | **—supa-anon** | `string` | Custom Supabase anon key (for self-hosting) | ### []()🔒 **Encrypt** [Section titled “ 🔒 Encrypt”](#--encrypt) ```bash npx @capgo/cli@latest bundle encrypt ``` 🔒 Encrypt a zip bundle for secure external storage. Returns ivSessionKey for upload/decryption. Get checksum using ‘bundle zip —json’. **Example:** ```bash npx @capgo/cli@latest bundle encrypt ./myapp.zip CHECKSUM ``` **Options:** | Param | Type | Description | | ----------------- | -------- | ----------------------------------------------------------- | | **—key** | `string` | Custom path for private signing key | | **—key-data** | `string` | Private signing key | | **-j,** | `string` | Output in JSON | | **—package-json** | `string` | Paths to package.json files for monorepos (comma-separated) | ### []()🔓 **Decrypt** [Section titled “ 🔓 Decrypt”](#--decrypt) ```bash npx @capgo/cli@latest bundle decrypt ``` 🔓 Decrypt an encrypted bundle (mainly for testing). Prints base64 session key for verification. **Example:** ```bash npx @capgo/cli@latest bundle decrypt ./myapp_encrypted.zip CHECKSUM ``` **Options:** | Param | Type | Description | | ----------------- | -------- | ------------------------------------------------------------- | | **—key** | `string` | Custom path for private signing key | | **—key-data** | `string` | Private signing key | | **—checksum** | `string` | Checksum of the bundle, to verify the integrity of the bundle | | **—package-json** | `string` | Paths to package.json files for monorepos (comma-separated) | ### []()🔹 **Zip** [Section titled “ 🔹 Zip”](#--zip) ```bash npx @capgo/cli@latest bundle zip ``` 🗜️ Create a zip file of your app bundle. Returns checksum for use with encryption. Use —json for machine-readable output. **Example:** ```bash npx @capgo/cli@latest bundle zip com.example.app --path ./dist ``` **Options:** | Param | Type | Description | | ------------------ | --------- | --------------------------------------------------------------------------------------------- | | **-p,** | `string` | Path of the folder to upload, if not provided it will use the webDir set in capacitor.config | | **-b,** | `string` | Bundle version number to name the zip file | | **-n,** | `string` | Name of the zip file | | **-j,** | `string` | Output in JSON | | **—no-code-check** | `boolean` | Ignore checking if notifyAppReady() is called in source code and index present in root folder | | **—key-v2** | `boolean` | Use encryption v2 | | **—package-json** | `string` | Paths to package.json files for monorepos (comma-separated) | # 📢 channel > 📢 Manage distribution channels for app updates in Capgo Cloud, controlling how updates are delivered to devices. 📢 Manage distribution channels for app updates in Capgo Cloud, controlling how updates are delivered to devices. ### []()➕ **Add** [Section titled “ ➕ Add”](#--add) **Alias:** `a` ```bash npx @capgo/cli@latest channel add ``` ➕ Create a new channel for app distribution in Capgo Cloud to manage update delivery. **Example:** ```bash npx @capgo/cli@latest channel add production com.example.app --default ``` **Options:** | Param | Type | Description | | ---------------- | --------- | ---------------------------------------------------------------- | | **-d,** | `string` | Set the channel as default | | **—self-assign** | `boolean` | Allow device to self-assign to this channel | | **-a,** | `string` | API key to link to your account | | **—supa-host** | `string` | Custom Supabase host URL (for self-hosting or Capgo development) | | **—supa-anon** | `string` | Custom Supabase anon key (for self-hosting) | ### []()🗑️ **Delete** [Section titled “ 🗑️ Delete”](#-️-delete) **Alias:** `d` ```bash npx @capgo/cli@latest channel delete ``` 🗑️ Delete a channel from Capgo Cloud, optionally removing associated bundles to free up resources. **Example:** ```bash npx @capgo/cli@latest channel delete production com.example.app ``` **Options:** | Param | Type | Description | | ------------------------- | --------- | ---------------------------------------------------------------- | | **-a,** | `string` | API key to link to your account | | **—delete-bundle** | `boolean` | Delete the bundle associated with the channel | | **—success-if-not-found** | `boolean` | Success if the channel is not found | | **—supa-host** | `string` | Custom Supabase host URL (for self-hosting or Capgo development) | | **—supa-anon** | `string` | Custom Supabase anon key (for self-hosting) | ### []()📋 **List** [Section titled “ 📋 List”](#--list) **Alias:** `l` ```bash npx @capgo/cli@latest channel list ``` 📋 List all channels configured for an app in Capgo Cloud to review distribution settings. **Example:** ```bash npx @capgo/cli@latest channel list com.example.app ``` **Options:** | Param | Type | Description | | -------------- | -------- | ---------------------------------------------------------------- | | **-a,** | `string` | API key to link to your account | | **—supa-host** | `string` | Custom Supabase host URL (for self-hosting or Capgo development) | | **—supa-anon** | `string` | Custom Supabase anon key (for self-hosting) | ### []()📦 **CurrentBundle** [Section titled “ 📦 CurrentBundle”](#--currentbundle) ```bash npx @capgo/cli@latest channel currentBundle ``` 📦 Get the current bundle linked to a specific channel in Capgo Cloud for update tracking. **Example:** ```bash npx @capgo/cli@latest channel currentBundle production com.example.app ``` **Options:** | Param | Type | Description | | -------------- | --------- | ---------------------------------------------------------------- | | **-c,** | `string` | Channel to get the current bundle from | | **-a,** | `string` | API key to link to your account | | **—quiet** | `boolean` | Only print the bundle version | | **—supa-host** | `string` | Custom Supabase host URL (for self-hosting or Capgo development) | | **—supa-anon** | `string` | Custom Supabase anon key (for self-hosting) | ### []()⚙️ **Set** [Section titled “ ⚙️ Set”](#-️-set) **Alias:** `s` ```bash npx @capgo/cli@latest channel set ``` ⚙️ Configure settings for a channel, such as linking a bundle, setting update strategies (major, minor, metadata, patch, none), or device targeting (iOS, Android, dev, prod, emulator, device). One channel must be default. **Example:** ```bash npx @capgo/cli@latest channel set production com.example.app --bundle 1.0.0 --state default ``` **Options:** | Param | Type | Description | | -------------------------- | --------- | -------------------------------------------------------------------------- | | **-a,** | `string` | API key to link to your account | | **-b,** | `string` | Bundle version number of the file to set | | **-s,** | `string` | Set the state of the channel, default or normal | | **—latest-remote** | `boolean` | Get the latest bundle uploaded in capgo cloud and set it to the channel | | **—latest** | `boolean` | Get the latest version key in the package.json to set it to the channel | | **—downgrade** | `boolean` | Allow to downgrade to version under native one | | **—no-downgrade** | `boolean` | Disable downgrade to version under native one | | **—ios** | `boolean` | Allow sending update to iOS devices | | **—no-ios** | `boolean` | Disable sending update to iOS devices | | **—android** | `boolean` | Allow sending update to Android devices | | **—no-android** | `boolean` | Disable sending update to Android devices | | **—self-assign** | `boolean` | Allow device to self-assign to this channel | | **—no-self-assign** | `boolean` | Disable devices to self-assign to this channel | | **—disable-auto-update** | `string` | Block updates by type: major, minor, metadata, patch, or none (allows all) | | **—dev** | `boolean` | Allow sending update to development devices | | **—no-dev** | `boolean` | Disable sending update to development devices | | **—prod** | `boolean` | Allow sending update to production devices | | **—no-prod** | `boolean` | Disable sending update to production devices | | **—emulator** | `boolean` | Allow sending update to emulator devices | | **—no-emulator** | `boolean` | Disable sending update to emulator devices | | **—device** | `boolean` | Allow sending update to physical devices | | **—no-device** | `boolean` | Disable sending update to physical devices | | **—package-json** | `string` | Paths to package.json files for monorepos (comma-separated) | | **—ignore-metadata-check** | `boolean` | Ignore checking node\_modules compatibility if present in the bundle | | **—supa-host** | `string` | Custom Supabase host URL (for self-hosting or Capgo development) | | **—supa-anon** | `string` | Custom Supabase anon key (for self-hosting) | # 👨‍⚕️ doctor > 👨‍⚕️ Check if your Capgo app installation is up-to-date and gather information useful for bug reports. 👨‍⚕️ Check if your Capgo app installation is up-to-date and gather information useful for bug reports. ```bash npx @capgo/cli@latest doctor ``` This command helps diagnose issues with your setup. **Example:** ```bash npx @capgo/cli@latest doctor ``` ## []()Options [Section titled “ Options”](#-options) | Param | Type | Description | | ----------------- | -------- | ----------------------------------------------------------- | | **—package-json** | `string` | Paths to package.json files for monorepos (comma-separated) | # 🚀 init > 🚀 Initialize a new app in Capgo Cloud with step-by-step guidance. 🚀 Initialize a new app in Capgo Cloud with step-by-step guidance. **Alias:** `i` ```bash npx @capgo/cli@latest init ``` This includes adding code for updates, building, uploading your app, and verifying update functionality. **Example:** ```bash npx @capgo/cli@latest init YOUR_API_KEY com.example.app ``` ## []()Options [Section titled “ Options”](#-options) | Param | Type | Description | | -------------- | -------- | ---------------------------------------------------------------- | | **-n,** | `string` | App name for display in Capgo Cloud | | **-i,** | `string` | App icon path for display in Capgo Cloud | | **—supa-host** | `string` | Custom Supabase host URL (for self-hosting or Capgo development) | | **—supa-anon** | `string` | Custom Supabase anon key (for self-hosting) | # 🔐 key > 🔐 Manage encryption keys for secure bundle distribution in Capgo Cloud, supporting end-to-end encryption with RSA and AES combination. 🔐 Manage encryption keys for secure bundle distribution in Capgo Cloud, supporting end-to-end encryption with RSA and AES combination. ### []()🔹 **Save** [Section titled “ 🔹 Save”](#--save) ```bash npx @capgo/cli@latest key save ``` 💾 Save the public key in the Capacitor config, useful for CI environments. Recommended not to commit the key for security. **Example:** ```bash npx @capgo/cli@latest key save --key ./path/to/key.pub ``` **Options:** | Param | Type | Description | | ------------- | -------- | ------------------------------------ | | **-f,** | `string` | Force generate a new one | | **—key** | `string` | Key path to save in Capacitor config | | **—key-data** | `string` | Key data to save in Capacitor config | ### []()🔨 **Create** [Section titled “ 🔨 Create”](#--create) ```bash npx @capgo/cli@latest key create ``` 🔨 Create RSA key pair for end-to-end encryption. Creates .capgo\_key\_v2 (private) and .capgo\_key\_v2.pub (public) in project root. Public key is saved to capacitor.config for mobile app decryption. NEVER commit the private key - store it securely! **Example:** ```bash npx @capgo/cli@latest key create ``` **Options:** | Param | Type | Description | | ------- | -------- | ------------------------ | | **-f,** | `string` | Force generate a new one | ### []()🗑️ **Delete\_old** [Section titled “ 🗑️ Delete\_old”](#-️-delete_old) ```bash npx @capgo/cli@latest key delete_old ``` 🧹 Delete the old encryption key from the Capacitor config to ensure only the current key is used. **Example:** ```bash npx @capgo/cli@latest key delete_old ``` # 🔑 login > 🔑 Save your Capgo API key to your machine or local folder for easier access to Capgo Cloud services. 🔑 Save your Capgo API key to your machine or local folder for easier access to Capgo Cloud services. **Alias:** `l` ```bash npx @capgo/cli@latest login ``` Use —apikey=\*\*\*\*\*\*\*\* in any command to override it. **Example:** ```bash npx @capgo/cli@latest login YOUR_API_KEY ``` ## []()Options [Section titled “ Options”](#-options) | Param | Type | Description | | -------------- | --------- | ---------------------------------------------------------------- | | **—local** | `boolean` | Only save in local folder, git ignored for security. | | **—supa-host** | `string` | Custom Supabase host URL (for self-hosting or Capgo development) | | **—supa-anon** | `string` | Custom Supabase anon key (for self-hosting) | # 🔹 organisation > [DEPRECATED] Use "organization" instead. This command will be removed in a future version. \[DEPRECATED] Use “organization” instead. This command will be removed in a future version. ### []()📋 **List** [Section titled “ 📋 List”](#--list) **Alias:** `l` ```bash npx @capgo/cli@latest organisation list ``` \[DEPRECATED] Use “organization list” instead. **Options:** | Param | Type | Description | | -------------- | -------- | ---------------------------------------------------------------- | | **-a,** | `string` | API key to link to your account | | **—supa-host** | `string` | Custom Supabase host URL (for self-hosting or Capgo development) | | **—supa-anon** | `string` | Custom Supabase anon key (for self-hosting) | ### []()➕ **Add** [Section titled “ ➕ Add”](#--add) **Alias:** `a` ```bash npx @capgo/cli@latest organisation add ``` \[DEPRECATED] Use “organization add” instead. **Options:** | Param | Type | Description | | -------------- | -------- | ---------------------------------------------------------------- | | **-n,** | `string` | Organization name | | **-e,** | `string` | Management email for the organization | | **-a,** | `string` | API key to link to your account | | **—supa-host** | `string` | Custom Supabase host URL (for self-hosting or Capgo development) | | **—supa-anon** | `string` | Custom Supabase anon key (for self-hosting) | ### []()⚙️ **Set** [Section titled “ ⚙️ Set”](#-️-set) **Alias:** `s` ```bash npx @capgo/cli@latest organisation set ``` \[DEPRECATED] Use “organization set” instead. **Options:** | Param | Type | Description | | --------------------------------- | --------- | -------------------------------------------------------------------------- | | **-n,** | `string` | Organization name | | **-e,** | `string` | Management email for the organization | | **—enforce-2fa** | `boolean` | Enable 2FA enforcement for all organization members | | **—no-enforce-2fa** | `boolean` | Disable 2FA enforcement for organization | | **—password-policy** | `boolean` | Enable password policy enforcement for organization | | **—no-password-policy** | `boolean` | Disable password policy enforcement | | **—min-length** | `string` | Minimum password length (6-128, default: 10) | | **—require-uppercase** | `boolean` | Require uppercase letter in password | | **—no-require-uppercase** | `boolean` | Do not require uppercase letter | | **—require-number** | `boolean` | Require number in password | | **—no-require-number** | `boolean` | Do not require number | | **—require-special** | `boolean` | Require special character in password | | **—no-require-special** | `boolean` | Do not require special character | | **—require-apikey-expiration** | `boolean` | Require all API keys to have an expiration date | | **—no-require-apikey-expiration** | `boolean` | Do not require API key expiration | | **—max-apikey-expiration-days** | `string` | Maximum days before API key expiration (1-365, null for no limit) | | **—enforce-hashed-api-keys** | `boolean` | Enforce hashed/secure API keys (key value stored as hash, shown only once) | | **—no-enforce-hashed-api-keys** | `boolean` | Allow plain-text API keys | | **-a,** | `string` | API key to link to your account | | **—supa-host** | `string` | Custom Supabase host URL (for self-hosting or Capgo development) | | **—supa-anon** | `string` | Custom Supabase anon key (for self-hosting) | ### []()🗑️ **Delete** [Section titled “ 🗑️ Delete”](#-️-delete) **Alias:** `d` ```bash npx @capgo/cli@latest organisation delete ``` \[DEPRECATED] Use “organization delete” instead. **Options:** | Param | Type | Description | | -------------- | -------- | ---------------------------------------------------------------- | | **-a,** | `string` | API key to link to your account | | **—supa-host** | `string` | Custom Supabase host URL (for self-hosting or Capgo development) | | **—supa-anon** | `string` | Custom Supabase anon key (for self-hosting) | # 🔹 organization > 🏢 Manage your organizations in Capgo Cloud for team collaboration and app management. 🏢 Manage your organizations in Capgo Cloud for team collaboration and app management. ### []()📋 **List** [Section titled “ 📋 List”](#--list) **Alias:** `l` ```bash npx @capgo/cli@latest organization list ``` 📋 List all organizations you have access to in Capgo Cloud. **Example:** ```bash npx @capgo/cli@latest organization list ``` **Options:** | Param | Type | Description | | -------------- | -------- | ---------------------------------------------------------------- | | **-a,** | `string` | API key to link to your account | | **—supa-host** | `string` | Custom Supabase host URL (for self-hosting or Capgo development) | | **—supa-anon** | `string` | Custom Supabase anon key (for self-hosting) | ### []()➕ **Add** [Section titled “ ➕ Add”](#--add) **Alias:** `a` ```bash npx @capgo/cli@latest organization add ``` ➕ Create a new organization in Capgo Cloud for team collaboration. **Example:** ```bash npx @capgo/cli@latest organization add --name "My Company" --email admin@mycompany.com ``` **Options:** | Param | Type | Description | | -------------- | -------- | ---------------------------------------------------------------- | | **-n,** | `string` | Organization name | | **-e,** | `string` | Management email for the organization | | **-a,** | `string` | API key to link to your account | | **—supa-host** | `string` | Custom Supabase host URL (for self-hosting or Capgo development) | | **—supa-anon** | `string` | Custom Supabase anon key (for self-hosting) | ### []()🔹 **Members** [Section titled “ 🔹 Members”](#--members) **Alias:** `m` ```bash npx @capgo/cli@latest organization members ``` 👥 List organization members and their 2FA status. Shows all members of an organization with their roles and whether they have 2FA enabled. Useful before enabling 2FA enforcement to see which members will be affected. > ℹ️ Viewing 2FA status requires super\_admin rights in the organization. **Example:** ```bash npx @capgo/cli@latest organization members ORG_ID ``` **Options:** | Param | Type | Description | | -------------- | -------- | ---------------------------------------------------------------- | | **-a,** | `string` | API key to link to your account | | **—supa-host** | `string` | Custom Supabase host URL (for self-hosting or Capgo development) | | **—supa-anon** | `string` | Custom Supabase anon key (for self-hosting) | ### []()⚙️ **Set** [Section titled “ ⚙️ Set”](#-️-set) **Alias:** `s` ```bash npx @capgo/cli@latest organization set ``` ⚙️ Update organization settings including name, email, security policies, and enforcement options. Security settings require super\_admin role. **Example:** ```bash npx @capgo/cli@latest organization set ORG_ID --name "New Name" ``` **Options:** | Param | Type | Description | | --------------------------------- | --------- | -------------------------------------------------------------------------- | | **-n,** | `string` | Organization name | | **-e,** | `string` | Management email for the organization | | **—enforce-2fa** | `boolean` | Enable 2FA enforcement for all organization members | | **—no-enforce-2fa** | `boolean` | Disable 2FA enforcement for organization | | **—password-policy** | `boolean` | Enable password policy enforcement for organization | | **—no-password-policy** | `boolean` | Disable password policy enforcement | | **—min-length** | `string` | Minimum password length (6-128, default: 10) | | **—require-uppercase** | `boolean` | Require uppercase letter in password | | **—no-require-uppercase** | `boolean` | Do not require uppercase letter | | **—require-number** | `boolean` | Require number in password | | **—no-require-number** | `boolean` | Do not require number | | **—require-special** | `boolean` | Require special character in password | | **—no-require-special** | `boolean` | Do not require special character | | **—require-apikey-expiration** | `boolean` | Require all API keys to have an expiration date | | **—no-require-apikey-expiration** | `boolean` | Do not require API key expiration | | **—max-apikey-expiration-days** | `string` | Maximum days before API key expiration (1-365, null for no limit) | | **—enforce-hashed-api-keys** | `boolean` | Enforce hashed/secure API keys (key value stored as hash, shown only once) | | **—no-enforce-hashed-api-keys** | `boolean` | Allow plain-text API keys | | **-a,** | `string` | API key to link to your account | | **—supa-host** | `string` | Custom Supabase host URL (for self-hosting or Capgo development) | | **—supa-anon** | `string` | Custom Supabase anon key (for self-hosting) | ### []()🗑️ **Delete** [Section titled “ 🗑️ Delete”](#-️-delete) **Alias:** `d` ```bash npx @capgo/cli@latest organization delete ``` 🗑️ Delete an organization from Capgo Cloud. This action cannot be undone. Only organization owners can delete organizations. **Example:** ```bash npx @capgo/cli@latest organization delete ORG_ID ``` **Options:** | Param | Type | Description | | -------------- | -------- | ---------------------------------------------------------------- | | **-a,** | `string` | API key to link to your account | | **—supa-host** | `string` | Custom Supabase host URL (for self-hosting or Capgo development) | | **—supa-anon** | `string` | Custom Supabase anon key (for self-hosting) | # 🔹 probe > 🔎 Probe the Capgo updates endpoint to check if an update is available for your app. 🔎 Probe the Capgo updates endpoint to check if an update is available for your app. ```bash npx @capgo/cli@latest probe ``` Sends a single request to the updates endpoint using your project’s capacitor config and reports whether an update would be delivered, or explains why not. **Example:** ```bash npx @capgo/cli@latest probe --platform ios ``` ## []()Options [Section titled “ Options”](#-options) | Param | Type | Description | | ------------- | -------- | --------------------------------- | | **—platform** | `string` | Platform to probe: ios or android | # 🔹 star > ⭐ Star a Capgo GitHub repository to support the project. ⭐ Star a Capgo GitHub repository to support the project. ```bash npx @capgo/cli@latest star ``` If you do not pass a repository name, this defaults to capacitor-updater in the Cap-go org. # 🔹 star-all > ⭐ Star all Capgo GitHub repositories with a small random delay between each request. ⭐ Star all Capgo GitHub repositories with a small random delay between each request. ```bash npx @capgo/cli@latest star-all ``` If you do not pass repositories, this defaults to all Cap-go repositories whose name starts with `capacitor-`. ## []()Options [Section titled “ Options”](#-options) | Param | Type | Description | | ----------------- | -------- | ----------------------------------------------------------- | | **—min-delay-ms** | `string` | Minimum delay in ms between each star action (default: 20) | | **—max-delay-ms** | `string` | Maximum delay in ms between each star action (default: 180) | # Adding or Updating Plugins > Complete guide for contributors and agents on how to add new plugins or update existing plugins in the Capgo documentation. This guide explains how to add new Capacitor plugins to the Capgo website or update existing plugin documentation. This is useful for contributors, maintainers, and AI agents helping to maintain the documentation. ## Overview [Section titled “Overview”](#overview) When adding a new plugin to the Capgo ecosystem, you need to update several files and locations across the website to ensure the plugin appears correctly in all relevant places: 1. **Plugin List Configuration** - Add plugin metadata to the master list 2. **Plugin Index Page** - Add plugin to the categorized plugin listing page 3. **Sidebar Navigation** - Add plugin to the documentation sidebar 4. **Plugin Documentation** - Create overview and getting-started pages 5. **Plugin Tutorial** - Create a comprehensive tutorial ## File Locations [Section titled “File Locations”](#file-locations) ### Key Files to Update [Section titled “Key Files to Update”](#key-files-to-update) | File | Purpose | | ----------------------------------------------- | --------------------------------- | | `/src/config/plugins.ts` | Master plugin list with metadata | | `/src/content/docs/docs/plugins/index.mdx` | Plugin index page with categories | | `/astro.config.mjs` | Sidebar navigation configuration | | `/src/content/docs/docs/plugins/[plugin-name]/` | Plugin documentation directory | | `/src/content/plugins-tutorials/en/` | English tutorial files | ## Step-by-Step Guide [Section titled “Step-by-Step Guide”](#step-by-step-guide) 1. ### Add Plugin to Master List [Section titled “Add Plugin to Master List”](#add-plugin-to-master-list) Open `/src/config/plugins.ts` and add your plugin to the `actions` array: ```typescript // First, import an appropriate Heroicon import YourIconName from 'astro-heroicons/mini/IconName.astro' // Then add to the actions array { name: '@capgo/your-plugin-name', author: 'github.com/Cap-go', description: 'Brief description of what the plugin does', href: 'https://github.com/Cap-go/your-plugin-name/', title: 'Display Name', icon: YourIconName, } ``` **Available Icons**: Check `/node_modules/astro-heroicons/mini/` for available icons. 2. ### Add Plugin to Index Page [Section titled “Add Plugin to Index Page”](#add-plugin-to-index-page) Open `/src/content/docs/docs/plugins/index.mdx` and add your plugin under the appropriate category: ```mdx ``` **Categories**: * ⭐ Featured Plugins * 📱 Device & System Plugins * 🎥 Media & Camera Plugins * 🛠️ Utility Plugins * 🤖 AI & Advanced Media * 📍 Location & Background Services * 📞 Communication & Analytics * 🔐 Security & System * 📊 Android-Specific Features * 📥 Download & Navigation 3. ### Add to Sidebar Navigation [Section titled “Add to Sidebar Navigation”](#add-to-sidebar-navigation) Open `/astro.config.mjs` and add your plugin to the sidebar configuration (around line 540): ```javascript { label: 'Your Plugin Name', items: [ { label: 'Overview', link: '/docs/plugins/your-plugin-name/' }, { label: 'Getting started', link: '/docs/plugins/your-plugin-name/getting-started' }, ], collapsed: true, } ``` Plugins are listed alphabetically in the sidebar. 4. ### Create Plugin Documentation Directory [Section titled “Create Plugin Documentation Directory”](#create-plugin-documentation-directory) Create a new directory for your plugin documentation: ```bash mkdir -p /src/content/docs/docs/plugins/your-plugin-name/ ``` 5. ### Create Plugin Overview Page [Section titled “Create Plugin Overview Page”](#create-plugin-overview-page) Create `/src/content/docs/docs/plugins/your-plugin-name/index.mdx`: ```mdx --- title: "@capgo/your-plugin-name" description: Brief description of the plugin's purpose tableOfContents: false next: false prev: false sidebar: order: 1 label: "Introduction" hero: tagline: Detailed tagline explaining what the plugin does image: file: ~public/your-plugin-icon.svg actions: - text: Get started link: /docs/plugins/your-plugin-name/getting-started/ icon: right-arrow variant: primary - text: Github link: https://github.com/Cap-go/your-plugin-name/ icon: external variant: minimal --- import { Card, CardGrid } from '@astrojs/starlight/components'; Description of first key feature Description of second key feature Works on both iOS and Android 📱 Check the [Documentation](/docs/plugins/your-plugin-name/getting-started/) to master the plugin. ``` 6. ### Create Getting Started Guide [Section titled “Create Getting Started Guide”](#create-getting-started-guide) Create `/src/content/docs/docs/plugins/your-plugin-name/getting-started.mdx`: ```mdx --- title: Getting Started description: Learn how to install and use the plugin in your Capacitor app. sidebar: order: 2 --- import { Steps } from '@astrojs/starlight/components'; import { PackageManagers } from 'starlight-package-managers' 1. **Install the package** 2. **Sync with native projects** ## Configuration ### iOS Configuration [iOS-specific setup instructions] ### Android Configuration [Android-specific setup instructions] ## Usage [Basic usage examples] ## API Reference [Detailed API documentation] ## Complete Example [Full working example] ## Best Practices [Recommended practices and tips] ## Platform Notes [Platform-specific notes and limitations] ``` 7. ### Create Tutorial File [Section titled “Create Tutorial File”](#create-tutorial-file) Create `/src/content/plugins-tutorials/en/your-plugin-name.md`: ```markdown --- locale: en --- # Using @capgo/your-plugin-name Package The `@capgo/your-plugin-name` package [brief description]. In this tutorial, we will guide you through the installation, configuration, and usage of this package in your Ionic Capacitor app. ## Installation [Installation steps] ## Configuration [Configuration steps for iOS and Android] ## API Usage [Detailed API usage examples] ## Complete Example [Full working example] ## Best Practices [Tips and best practices] ## Troubleshooting [Common issues and solutions] ## Conclusion [Summary and links to additional resources] ``` ## Plugin Documentation Structure [Section titled “Plugin Documentation Structure”](#plugin-documentation-structure) ### Required Files [Section titled “Required Files”](#required-files) ```plaintext src/content/docs/docs/plugins/your-plugin-name/ ├── index.mdx # Overview page with hero and feature cards └── getting-started.mdx # Installation and usage guide src/content/plugins-tutorials/en/ └── your-plugin-name.md # Comprehensive tutorial ``` ### Optional Files [Section titled “Optional Files”](#optional-files) For complex plugins, you may add additional documentation pages: ```plaintext src/content/docs/docs/plugins/your-plugin-name/ ├── index.mdx ├── getting-started.mdx ├── api-reference.mdx # Detailed API documentation ├── examples.mdx # Additional examples ├── troubleshooting.mdx # Troubleshooting guide └── migrations.mdx # Migration guides ``` ## Content Guidelines [Section titled “Content Guidelines”](#content-guidelines) ### Writing Plugin Descriptions [Section titled “Writing Plugin Descriptions”](#writing-plugin-descriptions) * **Be Concise**: Keep descriptions under 100 characters * **Be Specific**: Explain what the plugin does, not what it is * **Use Action Words**: Start with verbs like “Control”, “Integrate”, “Enable” **Good Examples**: * “Control device flashlight and torch with simple on/off toggle” * “Integrate Crisp live chat and customer support into your app” * “Enable secure authentication using Face ID and Touch ID” **Bad Examples**: * “A plugin for flash” * “This is a Crisp plugin” * “Biometric plugin” ### Writing Documentation [Section titled “Writing Documentation”](#writing-documentation) 1. **Start with Installation**: Always begin with clear installation steps 2. **Provide Configuration**: Include platform-specific setup requirements 3. **Show Usage Examples**: Provide working code examples 4. **Include API Reference**: Document all methods and parameters 5. **Add Complete Examples**: Show real-world usage patterns 6. **List Best Practices**: Share tips for optimal usage 7. **Document Platform Differences**: Clarify iOS vs Android behavior 8. **Add Troubleshooting**: Address common issues ### Code Examples [Section titled “Code Examples”](#code-examples) * Use TypeScript for all code examples * Include imports at the top * Add comments explaining key steps * Show error handling * Demonstrate both basic and advanced usage ## Checklist [Section titled “Checklist”](#checklist) Use this checklist when adding a new plugin: * [ ] Added plugin to `/src/config/plugins.ts` * [ ] Selected appropriate icon from Heroicons * [ ] Added plugin to `/src/content/docs/docs/plugins/index.mdx` under correct category * [ ] Added sidebar entry in `/astro.config.mjs` * [ ] Created plugin documentation directory * [ ] Created `index.mdx` overview page * [ ] Created `getting-started.mdx` guide * [ ] Created tutorial in `/src/content/plugins-tutorials/en/` * [ ] Included installation instructions * [ ] Documented iOS configuration * [ ] Documented Android configuration * [ ] Provided usage examples * [ ] Added API reference * [ ] Included complete working example * [ ] Listed best practices * [ ] Added platform-specific notes * [ ] Tested all links work correctly ## Icon Reference [Section titled “Icon Reference”](#icon-reference) Common icons used for plugins (from `astro-heroicons/mini/`): | Icon | Use Case | | ------------------------ | ----------------------------------- | | `BoltIcon` | Flash, power, energy | | `CameraIcon` | Camera, photo, video | | `ChatBubbleLeftIcon` | Chat, messaging, communication | | `FingerPrintIcon` | Biometric, security, authentication | | `MapPinIcon` | Location, geolocation, maps | | `SpeakerWaveIcon` | Audio, sound, music | | `VideoCameraIcon` | Video, recording, streaming | | `CreditCardIcon` | Payments, purchases | | `PlayCircleIcon` | Media players, video players | | `SignalIcon` | Connectivity, network, beacon | | `RadioIcon` | Beacon, broadcast, wireless | | `ChatBubbleOvalLeftIcon` | Social media, WeChat | ## Updating Existing Plugins [Section titled “Updating Existing Plugins”](#updating-existing-plugins) When updating an existing plugin: 1. **Update version numbers** in documentation 2. **Add migration guides** if breaking changes exist 3. **Update API reference** with new methods 4. **Add new examples** for new features 5. **Update platform requirements** if changed 6. **Revise best practices** based on new features 7. **Keep tutorial current** with latest API ## Multi-language Support [Section titled “Multi-language Support”](#multi-language-support) The website supports multiple languages. After creating English documentation: 1. Run the translation script: ```bash bun run plugins:translate_all ``` 2. Review generated translations in: * `/src/content/plugins-tutorials/de/` (German) * `/src/content/plugins-tutorials/es/` (Spanish) * `/src/content/plugins-tutorials/fr/` (French) * `/src/content/plugins-tutorials/it/` (Italian) * `/src/content/plugins-tutorials/ja/` (Japanese) * `/src/content/plugins-tutorials/ko/` (Korean) * `/src/content/plugins-tutorials/id/` (Indonesian) ## Testing Your Changes [Section titled “Testing Your Changes”](#testing-your-changes) After adding or updating plugin documentation: 1. **Build the site locally**: ```bash npm run build ``` 2. **Check for errors**: * Verify all links work * Ensure images load correctly * Confirm code examples are valid * Test navigation works 3. **Preview the site**: ```bash npm run dev ``` 4. **Verify your plugin appears**: * Check plugin listing page * Verify sidebar navigation * Test all documentation pages * Confirm tutorial page works ## Common Pitfalls [Section titled “Common Pitfalls”](#common-pitfalls) Caution **Avoid these common mistakes:** 1. **Forgetting to sync**: Always run `npx cap sync` in examples 2. **Inconsistent naming**: Use the same plugin name everywhere 3. **Missing platform config**: Document both iOS and Android setup 4. **Broken links**: Use relative links and verify they work 5. **No error handling**: Always show try-catch in examples 6. **Missing imports**: Include all necessary imports in examples 7. **Unclear descriptions**: Be specific about what the plugin does ## Getting Help [Section titled “Getting Help”](#getting-help) If you need help adding or updating plugin documentation: * **Discord**: Join our [Discord community](https://discord.capgo.app) * **GitHub**: Open an issue on the [website repository](https://github.com/Cap-go/website) * **Email**: Contact the team at ## Examples [Section titled “Examples”](#examples) For reference, check these well-documented plugins: * **Updater**: `/src/content/docs/docs/plugins/updater/` (complex plugin with multiple pages) * **Flash**: `/src/content/docs/docs/plugins/flash/` (simple plugin, good starter example) * **Social Login**: `/src/content/docs/docs/plugins/social-login/` (plugin with sub-pages) ## Summary [Section titled “Summary”](#summary) Adding a plugin to the Capgo documentation involves: 1. Adding metadata to the master configuration 2. Adding the plugin to the categorized index page 3. Configuring sidebar navigation 4. Creating comprehensive documentation pages 5. Writing a detailed tutorial 6. Testing all changes locally By following this guide, you ensure that plugins are consistently documented and easily discoverable by users. # FAQ > Frequently asked questions about Capgo, how to solve the most common issue in Capgo or with the Updater, what is OTA and how to manage them If you have questions not answered here, please ask! Both filing an issue or asking on [Discord](https://discord.capgo.app) work. ### What is “code push”?[](https://capgo.app/docs/#what-is-code-push "Direct link to What is \"code push\"?") [Section titled “What is “code push”?”](#what-is-code-push) Code push, also referred to as “over-the-air updates” (OTA) is a cloud service enabling Capacitor developers to deploy updates to their apps in production. Capgo currently works on Android, iOS, and Electron. “Code Push” is a reference to the name of a deploy feature used by the React Native community from [Microsoft](https://appcenter.ms/) and [Expo](https://expo.dev/), neither of which support Capacitor. ### What is the difference between a bundle and a release?[](https://capgo.app/docs/faq/#what-is-the-difference-between-a-bundle-and-a-release "Direct link to What is the difference between a bundle and a release?") [Section titled “What is the difference between a bundle and a release?”](#what-is-the-difference-between-a-bundle-and-a-release) We use the term “release” to mean preparing a binary for the app stores. In order to later generate a bundle Capgo needs to know the exact binary that was shipped to the app stores. We use the term “bundle” to mean a patch that can be applied to a release to update it to new code. The `npx @capgo/cli@latest bundle upload` command is used to generate a bundle from your new local code which is then shipped to your users. ### What is the roadmap?[](https://capgo.app/docs/faq/#what-is-the-roadmap "Direct link to What is the roadmap?") [Section titled “What is the roadmap?”](#what-is-the-roadmap) Our project boards are also public and found at: [https://github.com/orgs/Cap-go/projects](https://github.com/orgs/Cap-go/projects/) Our team also operates in the public, so you can see what we’re working on at any time. We’re happy to answer any questions you have about our roadmap or priorities via Github issues or [Discord](https://discord.capgo.app). ### Can I use Capgo with my team?[](https://capgo.app/docs/faq/#can-i-use-capgo-with-my-team "Direct link to Can I use Capgo with my team?") [Section titled “Can I use Capgo with my team?”](#can-i-use-capgo-with-my-team) Yes! All plans support unlimited developers. We only limit app metrics (MAU, storage and bandwidth) to each organization. See [Teams](https://capgo.app/pricing/) for more information. ### Does Capgo store my source code?[](https://capgo.app/docs/faq/#does-capgo-store-my-source-code "Direct link to Does Capgo store my source code?") [Section titled “Does Capgo store my source code?”](#does-capgo-store-my-source-code) No. Capgo servers never see your source code. When you run `npx @capgo/cli@latest bundle upload`, Capgo stores a zip file of the minified/compiled code - the same code that a browser would receive, not your source code. For additional security, you have two options: * **End-to-End Encryption**: Encrypt your bundle before uploading, making it impossible for Capgo to read or modify the contents * **External URL Upload**: Store the bundle on your own server and only provide Capgo with the download link with the option `--external ` See also our privacy policy: [https://capgo.app/privacy](https://capgo.app/privacy/) ### Can I use Capgo from my CI system?[](https://capgo.app/docs/faq/#can-i-use-capgo-from-my-ci-system "Direct link to Can I use Capgo from my CI system?") [Section titled “Can I use Capgo from my CI system?”](#can-i-use-capgo-from-my-ci-system) Yes. Capgo is intended to be used from CI systems. We’ve published a guide for [Android and Github Actions](https://capgo.app/blog/automatic-capacitor-android-build-github-action/) and [iOS](https://capgo.app/blog/automatic-capacitor-ios-build-github-action/), and for [GitLab](https://capgo.app/blog/setup-ci-and-cd-gitlab/). Other CI systems should be similar. Please don’t hesitate to reach out over GitHub issues or Discord if you encounter any issues. ### How does this relate to Firebase Remote Config or Launch Darkly?[](https://capgo.app/docs/faq/#how-does-this-relate-to-firebase-remote-config-or-launch-darkly "Direct link to How does this relate to Firebase Remote Config or Launch Darkly?") [Section titled “How does this relate to Firebase Remote Config or Launch Darkly?”](#how-does-this-relate-to-firebase-remote-config-or-launch-darkly) Code push allows adding new code / replacing code on the device. Firebase Remote Config and Launch Darkly are both configuration systems. They allow you to change the configuration of your app without having to ship a new version. They are not intended to replace code. ### How big of a dependency footprint does this add?[](https://capgo.app/docs/faq/#how-big-of-a-dependency-footprint-does-this-add "Direct link to How big of a dependency footprint does this add?") [Section titled “How big of a dependency footprint does this add?”](#how-big-of-a-dependency-footprint-does-this-add) I haven’t measured recently, but I expect the code push library to add less than one megabyte to Capacitor apps. We know of ways we can make this smaller when that becomes a priority. If size is a blocker for you, please let us know! ### Does Capgo work on the iOS 18.4 Simulator?[](https://capgo.app/docs/faq/#does-capgo-work-on-the-ios-18-4-simulator "Direct link to Does Capgo work on the iOS 18.4 Simulator?") [Section titled “Does Capgo work on the iOS 18.4 Simulator?”](#does-capgo-work-on-the-ios-184-simulator) No. Due to an upstream issue affecting the iOS 18.4 Simulator, Capgo does not run reliably there. Please test on a real device or use a different iOS simulator version. See details in the React Native issue: [facebook/react-native#50510](https://github.com/facebook/react-native/issues/50510) ### Does code push work with large applications?[](https://capgo.app/docs/faq/#does-code-push-work-with-large-applications "Direct link to Does code push work with large applications?") [Section titled “Does code push work with large applications?”](#does-code-push-work-with-large-applications) Yes. There is no limit on the size of the application that can be updated with code push. As noted [below](https://capgo.app/docs/faq/#what-types-of-changes-does-capgo-code-push-support), Capgo can change any JS code in your application regardless of size. To note: A bigger size make it harder for users to download updates. We recommend keeping your app as small as possible. ### What can I use Capgo code push for?[](https://capgo.app/docs/faq/#what-can-i-use-capgo-code-push-for "Direct link to What can I use Capgo code push for?") [Section titled “What can I use Capgo code push for?”](#what-can-i-use-capgo-code-push-for) We’ve seen a variety of uses, including: * Emergency fixes to production apps. * Shipping bug fixes to users on older versions of your app. * Shipping constantly (e.g. every hour). Note that most app stores prohibit shipping code that changes the behavior of the app in a significant way. Please see [below](https://capgo.app/docs/faq/#how-does-this-relate-to-the-appplay-store-review-process-or-policies) for more information. ### What counts as a “MAU” for Capgo?[](https://capgo.app/docs/faq/#what-counts-as-a-mau-for-capgo "Direct link to What counts as a \"MAU\" for Capgo?") [Section titled “What counts as a “MAU” for Capgo?”](#what-counts-as-a-mau-for-capgo) A MAU is a “Monthly Active User”. In Capgo’s context, this actually refers to a Monthly Active Device. We count a MAU as any device that has contacted our servers in the last 30 days. We do not count devices that have not contacted our servers in the last 30 days. **Important**: Starting from plugin version **v5.10.0**, **v6.25.0** and **v7.25.0**, the deviceID now persists across app reinstalls. Before these versions, each app reinstall would generate a new deviceID and count as a new MAU. With the current versions: * DeviceID persists across app reinstalls (stored securely in Keychain on iOS and EncryptedSharedPreferences on Android) * Updating the app does not create a new Device ID * During development, if you’re using an older plugin version (< v5.10.0 / v6.25.0 / v7.25.0), each reinstall still creates a new MAU Note: TestFlight downloads and channel switches in Android may still generate new device registrations depending on your configuration. > We recommend after first setup, to disable dev devices and emulators to reduce the amount of duplicated devices. ### What can’t we use Capgo code push for?[](https://capgo.app/docs/faq/#what-cant-we-use-capgo-code-push-for "Direct link to What can't we use Capgo code push for?") [Section titled “What can’t we use Capgo code push for?”](#what-cant-we-use-capgo-code-push-for) As above, Capgo should not be used to violate app store polices. Please see [below](https://capgo.app/docs/faq/#does-capgo-comply-with-play-store-guidelines) for more information. Also Capgo does not support changing native code (e.g. Java/Kotlin on Android or Objective-C/Swift on iOS). The tool will warn you during an attempted update if you have changed native code. ### Can I update capacitor.config.ts changes via Capgo?[](https://capgo.app/docs/faq/#can-i-update-capacitorconfigts-changes-via-capgo "Direct link to Can I update capacitor.config.ts changes via Capgo?") [Section titled “Can I update capacitor.config.ts changes via Capgo?”](#can-i-update-capacitorconfigts-changes-via-capgo) No. Changes to `capacitor.config.ts` cannot be sent through Capgo live updates. The Capacitor configuration file is read at native build time and compiled into the native app binary. This means any changes to `capacitor.config.ts` (such as plugin configurations, app ID, server settings, or native plugin options) require a new native release through the App Store or Google Play. Capgo can only update web assets (HTML, CSS, JavaScript) that are loaded at runtime. If you need to change your Capacitor configuration, you must: 1. Update `capacitor.config.ts` locally 2. Rebuild your native app (`npx cap sync` followed by a native build) 3. Submit the new binary to the app stores ### Does Capgo submit to the stores for me?[](https://capgo.app/docs/faq/#does-capgo-submit-to-the-stores-for-me "Direct link to Does Capgo submit to the stores for me?") [Section titled “Does Capgo submit to the stores for me?”](#does-capgo-submit-to-the-stores-for-me) Capgo does not currently support submitting to the app stores on your behalf. We have plans to add this in the future, but for now you will need to continue to use your existing processes to submit to the app stores. You can use our [CI guide Android](https://capgo.app/blog/automatic-capacitor-android-build-github-action/) to automate this process and [CI guide iOS](https://capgo.app/blog/automatic-capacitor-ios-build-github-action/). ### What does Capgo store on disk and where?[](https://capgo.app/docs/faq/#what-does-capgo-store-on-disk-and-where "Direct link to What does Capgo store on disk and where?") [Section titled “What does Capgo store on disk and where?”](#what-does-capgo-store-on-disk-and-where) The Capgo updater (included in your application when you build your app) caches the latest downloaded bundle in the only directory that capacitor allow to load code. On Android, this is located in `/data/user/0/com.example.app/code_cache/capgo_updater` although the base of that path is provided by the Android system and can change dynamically at runtime. On iOS devices, data is stored under `Library/Application Support/capgo`. The Capgo command line tools (e.g. `npx @capgo/cli@latest bundle upload`) are installed on disk in npm caches, your logins are stored in your home directory in `~/.capgo`. ### How does this relate to Capacitor Hot Reload?[](https://capgo.app/docs/faq/#how-does-this-relate-to-capacitor-hot-reload "Direct link to How does this relate to Capacitor Hot Reload?") [Section titled “How does this relate to Capacitor Hot Reload?”](#how-does-this-relate-to-capacitor-hot-reload) Capacitor’s Hot reload is a development-time-only feature. Code push is for production. Hot reload is a feature of Capacitor that allows you to change code on the device during development. It requires building the Capacitor app with a proxy to connect to your local machine. Code push is a feature that allows you to change code on the device in production. We will use a variety of different techniques to make this possible depending on the platform. ### What types of changes does Capgo code push support?[](https://capgo.app/docs/faq/#what-types-of-changes-does-capgo-code-push-support "Direct link to What types of changes does Capgo code push support?") [Section titled “What types of changes does Capgo code push support?”](#what-types-of-changes-does-capgo-code-push-support) Capgo can change any JS code in your application. This includes app code and generated code. You can also update dependencies in `package.json` as long as they don’t require native code changes. We do not have plans to support changing native code (e.g. Java/Kotlin on Android or Objective-C/Swift on iOS), and the tool will warn you if it detects that you have changed native code as it will not be included in the bundle. ### Does this support Web?[](https://capgo.app/docs/faq/#does-this-support-web "Direct link to Does this support Web?") [Section titled “Does this support Web?”](#does-this-support-web) Code push isn’t needed for web as the web already works this way. When a user opens a web app it downloads the latest version from the server if needed. If you have a use case for code push with web, we’d love to know! ### Will this work on iOS, Android, Mac, Windows, Linux, etc?[](https://capgo.app/docs/faq/#will-this-work-on-ios-android-mac-windows-linux-etc "Direct link to Will this work on iOS, Android, Mac, Windows, Linux, etc?") [Section titled “Will this work on iOS, Android, Mac, Windows, Linux, etc?”](#will-this-work-on-ios-android-mac-windows-linux-etc) Yes. So far we’ve focused on Android, iOS, and Electron support, and code push is production-ready on all three. ### What OS versions does Capgo support?[](https://capgo.app/docs/faq/#what-os-versions-does-capgo-support "Direct link to What OS versions does Capgo support?") [Section titled “What OS versions does Capgo support?”](#what-os-versions-does-capgo-support) Capgo supports the same versions of Android that Capacitor supports. Capacitor currently supports Android API level 22+ and iOS 13.0+: [https://capacitorjs.com/docs/main/reference/support-policy](https://capacitorjs.com/docs/main/reference/support-policy/) ### What versions of Capacitor does Capgo support?[](https://capgo.app/docs/faq/#what-versions-of-capacitor-does-capgo-support "Direct link to What versions of Capacitor does Capgo support?") [Section titled “What versions of Capacitor does Capgo support?”](#what-versions-of-capacitor-does-capgo-support) Capgo currently supports only recent stable releases of Capacitor. We could support older versions of Capacitor as well, we just haven’t built out the infrastructure necessary to maintain such over time. We intend to support more versions of Capacitor in the future, including any version for our enterprise customers. [https://github.com/Cap-go/capgo/issues/1100](https://github.com/Cap-go/capgo/issues/1100/) Capgo tracks Capacitor stable and generally updates within a few hours of any stable release. Our system for doing these updates is automated takes a few minutes to run. We then do an extra manual verification step before publishing to our servers. ### How does this relate to the App/Play Store review process or policies?[](https://capgo.app/docs/faq/#how-does-this-relate-to-the-appplay-store-review-process-or-policies "Direct link to How does this relate to the App/Play Store review process or policies?") [Section titled “How does this relate to the App/Play Store review process or policies?”](#how-does-this-relate-to-the-appplay-store-review-process-or-policies) Developers are bound by their agreements with store providers when they choose to use those stores. Code push is designed to allow developers to update their apps and still comply with store policies on iOS, Android, and Electron delivery channels. Similar to the variety of commercial products available to do so with React Native (e.g. [Microsoft](https://appcenter.ms/), [Expo](https://expo.dev/)). Microsoft also publishes a guide on how their solution complies with the app stores: [https://github.com/microsoft/react-native-code-push#store-guideline-compliance](https://github.com/microsoft/react-native-code-push/#store-guideline-compliance) Code push is a widely used technique throughout the app stores. All of the large apps I’m aware of use code push. The major policy to be aware of is not to change the behavior of the app in a significant way. Please see [below](https://capgo.app/docs/faq/#does-capgo-comply-with-play-store-guidelines) for more information. ### Does Capgo comply with Play Store guidelines?[](https://capgo.app/docs/faq/#does-capgo-comply-with-play-store-guidelines "Direct link to Does Capgo comply with Play Store guidelines?") [Section titled “Does Capgo comply with Play Store guidelines?”](#does-capgo-comply-with-play-store-guidelines) Yes. The Play Store offers two restrictions relating to update tools. 1. Updates must use an interpreter or virtual machine (Capgo uses JavaScript in a WebView). [https://support.google.com/googleplay/android-developer/answer/9888379?hl=en](https://support.google.com/googleplay/android-developer/answer/9888379/?hl=en) ```plaintext An app distributed via Google Play may not modify, replace, or update itself using any method other than Google Play's update mechanism. Likewise, an app may not download executable code (such as dex, JAR, .so files) from a source other than Google Play. *This restriction does not apply to code that runs in a virtual machine or an interpreter* where either provides indirect access to Android APIs (such as JavaScript in a webview or browser). Apps or third-party code, like SDKs, with interpreted languages (JavaScript, Python, Lua, etc.) loaded at run time (for example, not packaged with the app) must not allow potential violations of Google Play policies. ``` 2. Changes to the app must not be deceptive (e.g. changing the purpose of the app via update). [https://support.google.com/googleplay/android-developer/answer/9888077](https://support.google.com/googleplay/android-developer/answer/9888077/) Please be clear with your users about what you are providing with your application and do not violate their expectations with significant behavioral changes through the use of Capgo. Capgo is designed to be compatible with the Play Store guidelines. However Capgo is a tool, and as with any tool, can be abused. Deliberately abusing Capgo to violate Play Store guidelines is in violation of the Capgo [Terms of Service](https://capgo.app/tos/) and can result in termination of your account. Finally, code push services are widely used in the industry (all of the large apps I’m aware of use them) and there are multiple other code push services publicly available (e.g. expo.dev & appcenter.ms). This is a well trodden path. Microsoft also publishes a guide on how their react native “codepush” library complies with the app stores: [https://github.com/microsoft/react-native-code-push#store-guideline-compliance](https://github.com/microsoft/react-native-code-push/#store-guideline-compliance) ### Does Capgo comply with App Store guidelines?[](https://capgo.app/docs/faq/#does-capgo-comply-with-app-store-guidelines "Direct link to Does Capgo comply with App Store guidelines?") [Section titled “Does Capgo comply with App Store guidelines?”](#does-capgo-comply-with-app-store-guidelines) Yes. Similar to the Play Store, the App Store offers both technical and policy restrictions. ```plaintext 3.2.2 ... interpreted code may be downloaded to an Application but only so long as such code: (a) does not change the primary purpose of the Application by providing features or functionality that are inconsistent with the intended and advertised purpose of the Application as submitted to the App Store, (b) does not create a store or storefront for other code or applications, and (c) does not bypass signing, sandbox, or other security features of the OS. ``` Capgo uses JavaScript in a WebView to comply with the interpreter-only restriction for updates on iOS. So long as your application is not engaging in deceptive behavior via updates (e.g. changing the purpose of the app via update), updating via Capgo (or any other code push solution) is standard industry practice and compliant with App Store guidelines. Deliberately abusing Capgo to violate App Store guidelines is in violation of the Capgo [Terms of Service](https://capgo.app/tos/) and can result in termination of your account. Microsoft also publishes a guide on how their react native “codepush” library complies with the app stores: [https://github.com/microsoft/react-native-code-push#store-guideline-compliance](https://github.com/microsoft/react-native-code-push/#store-guideline-compliance) ### Can I use Capgo in my country?[](https://capgo.app/docs/faq/#can-i-use-capgo-in-my-country "Direct link to Can I use Capgo in my country?") [Section titled “Can I use Capgo in my country?”](#can-i-use-capgo-in-my-country) We have not attempted to restrict access to Capgo from any country. We recognize that some countries have restrictions on what urls can be accessed from within the country. Capgo currently uses Cloudflare Cloud for hosting, including R2 Storage and Cloudflare workers. The following URLs are used by Capgo: * [https://api.capgo.app](https://api.capgo.app/) — used by the `npx @capgo/cli` command line tools to interact with the Capgo servers as well as the Capgo updater on users’ devices to check for updates. * [https://\*.r2.cloudflarestorage.com](https://*.r2.cloudflarestorage.com/) — used by the `npx @capgo/cli` command line tool to upload and download bundle If all of those URLs are accessible from your country, then Capgo should work. If your region requires blocking access to any of those URLs, please let us know and we can work with you to find a solution. Proxy servers are one option. ### Can I self-host Capgo?[](https://capgo.app/docs/faq/#can-i-self-host-capgo "Direct link to Can I self-host Capgo?") [Section titled “Can I self-host Capgo?”](#can-i-self-host-capgo) Yes, you can self-host Capgo. The guide is not yet written, but the code is open source and available at [https://github.com/cap-go/capgo](https://github.com/cap-go/capgo/) ### Does code push require the internet to work?[](https://capgo.app/docs/faq/#does-code-push-require-the-internet-to-work "Direct link to Does code push require the internet to work?") [Section titled “Does code push require the internet to work?”](#does-code-push-require-the-internet-to-work) Yes. One could imagine running a server to distribute the updates separately from the general internet, but some form of network connectivity is required to transport updates to the devices. ### How is Capgo affected by lack of network connectivity?[](https://capgo.app/docs/faq/#how-is-capgo-affected-by-lack-of-network-connectivity "Direct link to How is Capgo affected by lack of network connectivity?") [Section titled “How is Capgo affected by lack of network connectivity?”](#how-is-capgo-affected-by-lack-of-network-connectivity) Capgo updater (included in your application when you build your app with Capgo) is designed to be resilient to network connectivity issues. In the default update behavior, when the application launches it alerts the Capgo updater, which spawns a separate thread to make a network request to Capgo’s servers and ask for an update. We intentionally use a separate thread to avoid affecting blocking anything else the application might be doing. If the network request fails or times out, the updater will simply try to check again next time the application launches. Capgo command line tools (e.g. `npx @capgo/cli@latest bundle upload`) require network connectivity to function. If you are using Capgo to distribute your app, you should ensure that your CI system has network connectivity. ### What happens if a user doesn’t update for a long time and misses an update?[](https://capgo.app/docs/faq/#what-happens-if-a-user-doesnt-update-for-a-long-time-and-misses-an-update "Direct link to What happens if a user doesn't update for a long time and misses an update?") [Section titled “What happens if a user doesn’t update for a long time and misses an update?”](#what-happens-if-a-user-doesnt-update-for-a-long-time-and-misses-an-update) Our implementation always sends an update specifically tailored for the device that is requesting it updating the requestor always to the latest version available. Thus if a user doesn’t update for a while they will “miss” intermediate updates. The update server could be changed to support responding with either the next incremental version or the latest version depending on your application’s needs. Please let us know if alternative update behaviors are important to you. ### How does Capgo relate to Capacitor?[](https://capgo.app/docs/faq/#how-does-capgo-relate-to-capacitor "Direct link to How does Capgo relate to Capacitor?") [Section titled “How does Capgo relate to Capacitor?”](#how-does-capgo-relate-to-capacitor) Capgo is a plugin for Capacitor that adds code push. Capgo is not a replacement for Capacitor. You can continue to use the Capacitor tooling you already know and love. We track the latest stable release of Capacitor and update our code push plugin to work with it. ### When do updates happen?[](https://capgo.app/docs/faq/#when-do-updates-happen "Direct link to When do updates happen?") [Section titled “When do updates happen?”](#when-do-updates-happen) By default, the Capgo updater checks for updates on app startup. It runs on a background thread and does not block the UI thread. Any updates will be installed while the user is using the app and will be applied the next time the app is restarted. It is also possible to run the Capgo updater manually using the `@capgo/capacitor-updater` package, through which it is possible to trigger updates at any time, including via a push notification. The Capgo updater is designed such that when the network is not available, or the server is down or otherwise unreachable, the app will continue to run as normal. Should you ever choose to delete an update from our servers, all your clients will continue to run as normal. We have added the ability to rollback patches. The simplest thing is to simply attach a previous bundle to your channel to undo. ### Do I need to keep my app\_id secret?[](https://capgo.app/docs/faq/#do-i-need-to-keep-my-app_id-secret "Direct link to Do I need to keep my app_id secret?") [Section titled “Do I need to keep my app\_id secret?”](#do-i-need-to-keep-my-app_id-secret) No. The `app_id` is included in your app and is safe to be public. You can check it into version control (even publicly) and not worry about someone else accessing it. Someone who has your `app_id` can fetch the latest version of your app from Capgo servers, but they cannot push updates to your app or access any other aspect of your Capgo account. ### What information is sent to Capgo servers?[](https://capgo.app/docs/faq/#what-information-is-sent-to-capgo-servers "Direct link to What information is sent to Capgo servers?") [Section titled “What information is sent to Capgo servers?”](#what-information-is-sent-to-capgo-servers) Although Capgo connects to the network, it does not send any personally identifiable information. Including Capgo should not affect your declarations for the Play Store or App Store. Requests sent from the app to Capgo servers include: * app\_id (specified `capacitor.config.json`) * channel (optional in `capacitor.config.json`) * release\_version (versionName from AndroidManifest.xml or CFBundleShortVersionString from Info.plist or `capacitor.config.json` if set in [`CapacitorUpdater.version`](/docs/plugin/settings/#version) ) * version\_number (generated as part of `npx @capgo/cli@latest bundle upload`) * os\_version (e.g. ‘11.2.1’) * platform (e.g. ‘android’, needed to send down the right patch) That’s it. The code for this is in `updater/library/src/network.rs` * device\_id (generated on the device on first run, used to de-duplicate per-device installs and allow us to charge based on users installed to (e.g. monthly active users), rather than total patches or total patch installs) * custom\_id ( optional, set at runtime by the developer, used for you to link a device to a user in your system) ### What platforms does Capgo support?[](https://capgo.app/docs/faq/#what-platforms-does-capgo-support "Direct link to What platforms does Capgo support?") [Section titled “What platforms does Capgo support?”](#what-platforms-does-capgo-support) Currently, Capgo supports Android, iOS, and Electron. All are production-ready. Use of Capgo for iOS, Android, or Electron can be independent decisions. You can set your channel strategy for Android and an ipa built to the App Store, or Electron channels, as needed. Capgo can (relatively easily) be made to support desktop or embedded targets. If those are important to you, please let us know. ### How does Capgo interact with Play Testing Tracks or Apple TestFlight?[](https://capgo.app/docs/faq/#how-does-capgo-interact-with-play-testing-tracks-or-apple-testflight "Direct link to How does Capgo interact with Play Testing Tracks or Apple TestFlight?") [Section titled “How does Capgo interact with Play Testing Tracks or Apple TestFlight?”](#how-does-capgo-interact-with-play-testing-tracks-or-apple-testflight) Each of the app stores have separate mechanisms for distributing apps to limited groups of users (e.g. “internal testing”, “closed beta”, etc.). These are all mechanisms for segmenting your users into groups and distributing specific versions of your apps to each. Unfortunately, these not all of these mechanisms allow 3rd parties to detect when apps are installed in any specific Test Track or via TestFlight. Thus, we do not have reliable visibility into composition of these groups, and cannot reliably gate access to Capgo patches based on these groups. [https://stackoverflow.com/questions/53291007/can-an-android-application-identify-the-test-track-within-google-play](https://stackoverflow.com/questions/53291007/can-an-android-application-identify-the-test-track-within-google-play/) [https://stackoverflow.com/questions/26081543/how-to-tell-at-runtime-whether-an-ios-app-is-running-through-a-testflight-beta-i](https://stackoverflow.com/questions/26081543/how-to-tell-at-runtime-whether-an-ios-app-is-running-through-a-testflight-beta-i/) If you’d like to segment availability of Capgo bundle, there are 4 potential options: 1. Use separate channel for each group. This is the most straightforward approach, but requires you to manage multiple channels. You may already have a dev channels and prod channels with different availability. You can thus update your dev channels, verify it and then separately update your prod channels. We recommend using branches / tags in your version control to help keep track of the sources associated with each release. 2. Track your own set of opt-in users, disable automatic updates, and trigger updates only for certain users via the `@capgo/capacitor-updater` package. This works today, but requires you to manage your own opt-in list. 3. Capgo allow creare its own opt-in mechanism on a per-device basis (similar to Test Tracks or TestFlight, just platform agnostic). This allow your QA team to opt-in to bundle before they’re promoted to the general public. 4. Capgo have percentage based rollouts. This does not let you choose which devices to send to, but can help you roll out incrementally and roll-back on sight of any problems. ## Billing[](https://capgo.app/docs/faq/#billing "Direct link to Billing") [Section titled “Billing”](#billing) ### How do I upgrade or downgrade my plan?[](https://capgo.app/docs/faq/#how-do-i-upgrade-or-downgrade-my-plan "Direct link to How do I upgrade or downgrade my plan?") [Section titled “How do I upgrade or downgrade my plan?”](#how-do-i-upgrade-or-downgrade-my-plan) You can upgrade or downgrade your plan at any time in your dashboard: [https://console.capgo.app/settings/organization/plans](https://console.capgo.app/settings/organization/plans/) ### When does my billing period reset?[](https://capgo.app/docs/faq/#when-does-my-billing-period-reset "Direct link to When does my billing period reset?") [Section titled “When does my billing period reset?”](#when-does-my-billing-period-reset) Billing periods are reset automatically every month on the month you first subscribed to Capgo. For example, if you subscribed on the 15th of the month, your billing period will reset on the 15th of every month. ### How do I cancel my subscription?[](https://capgo.app/docs/faq/#how-do-i-cancel-my-subscription "Direct link to How do I cancel my subscription?") [Section titled “How do I cancel my subscription?”](#how-do-i-cancel-my-subscription) You can cancel your subscription at any time in your dashboard: [https://console.capgo.app/settings/organization/plans](https://console.capgo.app/settings/organization/plans/) ### Can I pay for a year in advance?[](https://capgo.app/docs/faq/#can-i-pay-for-a-year-in-advance "Direct link to Can I pay for a year in advance?") [Section titled “Can I pay for a year in advance?”](#can-i-pay-for-a-year-in-advance) Yes you can can at any time in your dashboard: [https://console.capgo.app/settings/organization/plans](https://console.capgo.app/settings/organization/plans/) ### Stats and analytics[](https://capgo.app/docs/faq/#stats-and-analytics "Direct link to Stats and analytics") [Section titled “Stats and analytics”](#stats-and-analytics) The stats in your dashboard are updated every midnight UTC. The stats are calculated based on the number of [MAU](https://capgo.app/docs/faq/#what-is-the-difference-between-a-bundle-and-a-release "Direct link to What is the difference between a bundle and a release?") that have been installed on your devices. ## How device ID is generated[](https://capgo.app/docs/faq/#how-device-id-is-generated "Direct link to How device ID is generated") [Section titled “How device ID is generated”](#how-device-id-is-generated) The device ID is generated on the device on first run, and is used to de-duplicate per-device installs and allow us to charge based on users installed to (e.g. monthly active users), rather than total patches or total patch installs. MAU is a better solution than number of installs to price Capgo, as it is more accurate and reflects the actual cost of Capgo per device. **DeviceID Persistence (Updated in v6.25.0 and v7.25.0)**: * **Current behavior**: The deviceID now persists across app reinstalls. It is stored securely in the device’s Keychain (iOS) or EncryptedSharedPreferences (Android), allowing us to track the same device even after uninstall/reinstall. * **Previous behavior** (before v6.25.0/v7.25.0): For privacy reasons related to Apple and Google store policies, the deviceID was reset on each app reinstall, making it impossible to track the same device across reinstalls. The privacy rules are enforced by Apple and Google, and Capgo’s implementation complies with their best practices for device identification. Device ID will not be listed in your device list until they get their first patch installed. ## Why my device number is different than my MAU?[](https://capgo.app/docs/faq/#why-my-device-number-is-different-than-my-mau "Direct link to Why my device number is different than my MAU?") [Section titled “Why my device number is different than my MAU?”](#why-my-device-number-is-different-than-my-mau) Currently, the device list is not updated as often as the MAU. The device list is updated only when a device installs an update. While the MAU is updated at every app launch. This is a current limitation of the platform. Our Analytics platform do not support raw updates so we use conventional database for Devices list. To limit the number of database queries, we do update row only on app update. This limitation will be removed in the future. ## How to have different update by platform?[](https://capgo.app/docs/faq/#how-to-have-different-update-by-platform "Direct link to How to have different update by platform?") [Section titled “How to have different update by platform?”](#how-to-have-different-update-by-platform) You can create a channel for each platform. and disable platform specific updates in each channel. On ios channel disable android updates and on android channel disable ios updates. Then upload a bundle to each channel to have different update for each platform. If you need to have the same update for both platform, you can link one bundle to multiple channels. No need to duplicate the bundle. # Tech support for Capgo > How to get tech support for Capgo and our updater, please follow this guide to get help when the doc and our articles are not enough ## Support by discord [Section titled “Support by discord”](#support-by-discord) Capgo has an official [discord server](https://discord.capgo.app). Getting tech support there is likely one of the fastest ways to get a response. Here is a crash course: Step 1 - go to the `questions` channel ![Ask on discord](/discord-questions.webp) Step 2 - create your thread ![Create a question on discord](/discord-newquestion.webp) Step 3 - Describe your problem and select the relevant tags ![Create a post on discord](/discord-new-post.webp) Step 4 - Share your secure account id (optional) This will allow the capgo staff to take a look at your account. Sharing this id is safe, as it was designed to be shared publicly. To share this, please go to [capgo’s settings](https://console.capgo.app/dashboard/settings/account/). There please click on `copy account id`. ![Share your id without leaking your info](/share-secure-id.webp) This will copy the secure account id to the clipboard. Please include that in your discord post. ## Support by email [Section titled “Support by email”](#support-by-email) This is the slowest way to get support. Please use the discord server first. If you need to contact us by email, please send an email to . # Add an App > Add an app to your Capgo account, and install the plugin in your app ## Requirements [Section titled “Requirements”](#requirements) Before getting started with Capgo, make sure you have: * A Capacitor app installed and configured. [Learn how to set up Capacitor](https://capacitorjs.com/docs/getting-started/) * Node.js 20 or later installed * One of the following development environments: * **macOS** with Xcode (for iOS development) and/or Android Studio (for Android development) * **Linux** with Android Studio (for Android development) * **Windows** with Android Studio (for Android development) ## Introduction to Capgo [Section titled “Introduction to Capgo”](#introduction-to-capgo) [Play](https://youtube.com/watch?v=NzXXKoyhTIo) ## Live updates are 3 step away [Section titled “Live updates are 3 step away”](#live-updates-are-3-step-away) ### Guided setup [Section titled “Guided setup”](#guided-setup) 1. Create your account at . ![signup screenshot](/signup.webp "signup screenshot") 2. Use the Init commands to get started ```bash npx @capgo/cli@latest init [APIKEY] ``` You will be presented with a series of questions. Provide the necessary answers to complete the automated setup. 3. Deploy a live update Tip By following these steps, you’ll be up and running in no time. If you need any further assistance during the process, our support team is [here to help](https://support.capgo.app). Happy onboarding! [Detailed Onboarding Guide ](/docs/getting-started/onboarding/)See the complete step-by-step guide for the CLI onboarding process [Deploy a live update ](/docs/getting-started/deploy/)Learn how to deploy a live update to your app ### Manual setup [Section titled “Manual setup”](#manual-setup) In case the init command doesn’t work for you, you can manually add an app. 1. Connect the CLI to your account: ```bash npx @capgo/cli@latest login [APIKEY] ``` 2. Add the app to your account with this command: ```bash npx @capgo/cli@latest app add [APP_NAME] ``` 3. Install the plugin in your app: ```bash npm i @capgo/capacitor-updater ``` 4. Configure the plugin in your `capacitor.config` ```json { "plugins": { CapacitorUpdater: { "appId": "Your appID", "autoUpdate": true, "version": "1.0.0" } } } ``` [See all option available](/docs/plugin/settings/). This information will be infer if not provided. 5. Call the init method as early as possible in your app: ```typescript import { CapacitorUpdater } from '@capgo/capacitor-updater'; CapacitorUpdater.notifyAppReady(); ``` 6. Deploy a live update Installing for older Capacitor versions The command above (step 3) installs the latest version (v8.x) for Capacitor 8. For older Capacitor versions, use the appropriate npm tag: ```bash # Capacitor 7 npm i @capgo/capacitor-updater@lts-v7 # Capacitor 6 npm i @capgo/capacitor-updater@lts-v6 # Capacitor 5 npm i @capgo/capacitor-updater@lts-v5 ``` Each plugin major version matches the Capacitor major version (v8 → Capacitor 8, v7 → Capacitor 7, v6 → Capacitor 6, v5 → Capacitor 5). Minor versions share the same feature set across all major versions (e.g., 5.34.0, 6.34.0, 7.34.0, and 8.34.0 all include the same features). # CI/CD Integration > Integrating Capgo into your CI/CD pipeline allows you to fully automate the process of building and deploying updates to your app. By leveraging the Capgo CLI and semantic-release, you can ensure consistent, reliable deployments and enable rapid iteration. Integrating Capgo into your CI/CD pipeline allows you to fully automate the process of building and deploying updates to your app. By leveraging the Capgo CLI and semantic-release, you can ensure consistent, reliable deployments and enable rapid iteration. ## Benefits of CI/CD Integration [Section titled “Benefits of CI/CD Integration”](#benefits-of-cicd-integration) * **Automation**: No more manual steps or room for human error. Your entire build, test, and deployment process can be automated from end to end. * **Consistency**: Every deployment follows the same set of steps, ensuring a predictable and repeatable process. This is especially valuable when you have multiple team members contributing code. * **Faster iterations**: With automated deployments, you can ship updates more frequently and with confidence. No more waiting for manual QA or release approvals. ## Capgo CLI [Section titled “Capgo CLI”](#capgo-cli) The Capgo CLI is the key to integrating Capgo into your CI/CD workflow. It provides commands for pushing new bundle versions, managing channels, and more. The most important command for CI/CD integration is `bundle upload`: ```shell npx @capgo/cli@latest bundle upload --channel Production --apikey YOUR_API_KEY ``` If you use encryption you should provide it from one of these ways: **Using a private key file path:** ```shell npx @capgo/cli@latest bundle upload --channel Production --apikey YOUR_API_KEY --key-v2 PRIVATE_KEY_PATH ``` **Using the private key content directly (recommended for CI/CD):** ```shell npx @capgo/cli@latest bundle upload --channel Production --apikey YOUR_API_KEY --key-data-v2 PRIVATE_KEY_CONTENT ``` **Using environment variables (best practice for CI/CD):** ```shell npx @capgo/cli@latest bundle upload --channel Production --apikey YOUR_API_KEY --key-data-v2 "$CAPGO_PRIVATE_KEY" ``` ### Setting up Environment Variables for Encryption [Section titled “Setting up Environment Variables for Encryption”](#setting-up-environment-variables-for-encryption) For CI/CD environments, it’s recommended to store your private key as an environment variable rather than a file. Here’s how to set it up: 1. **Get your private key content:** ```shell cat .capgo_key_v2 | pbcopy ``` This copies the key content to your clipboard. 2. **Add it to your CI/CD environment:** * **GitHub Actions**: Add `CAPGO_PRIVATE_KEY` to your repository secrets * **GitLab CI**: Add it as a masked variable in your project settings * **CircleCI**: Add it as an environment variable in your project settings * **Jenkins**: Add it as a secret text credential 3. **Use it in your pipeline:** ```yaml - run: npx @capgo/cli@latest bundle upload --channel=production --apikey=${{ secrets.CAPGO_API_KEY }} --key-data-v2 "${{ secrets.CAPGO_PRIVATE_KEY }}" ``` **Note**: The `--key-data-v2` flag allows you to pass the private key content directly as a string, making it perfect for environment variables in CI/CD pipelines where you don’t want to create temporary files. This command uploads the current web build to the specified channel. You’ll typically run this as the last step in your CI/CD pipeline, after your web build has completed successfully. ## Setting up Capgo in your CI/CD Pipeline [Section titled “Setting up Capgo in your CI/CD Pipeline”](#setting-up-capgo-in-your-cicd-pipeline) While the exact steps will vary depending on your CI/CD tool of choice, the general process for integrating Capgo looks like this: 1. **Generate an API key**: Log in to the Capgo dashboard and create a new API key. This key will be used to authenticate the CLI in your CI/CD environment. Keep it secret and never commit it to your repository! 2. **Configure the `bundle upload` command**: Add a step to your CI/CD configuration that runs the `bundle upload` command with the appropriate arguments: upload.yml ```yaml - run: npx @capgo/cli@latest bundle upload --channel=production --apikey=${{ secrets.CAPGO_API_KEY }} ``` \n Replace `Production` with the channel you want to deploy to, `${{ secrets.CAPGO_API_KEY }}` with the environment variable holding your API key, and add `--key-data-v2 "${{ secrets.CAPGO_PRIVATE_KEY }}"` if using encryption. 3. **Add the `upload` step after your web build**: Ensure that the `upload` step comes after your web build has completed successfully. This ensures you’re always deploying your latest code.\n Here’s an example configuration for GitHub Actions:\n upload.yml ```yaml name: Deploy to Capgo on: push: branches: [main] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - uses: actions/setup-node@v6 with: node-version: '24' - run: npm ci - run: npm run build - run: npm install -g @capgo/cli - run: npx @capgo/cli@latest bundle upload --channel=production --apikey=${{ secrets.CAPGO_API_KEY }} --key-data-v2 "${{ secrets.CAPGO_PRIVATE_KEY }}" ``` ## Version Management with Semantic-release [Section titled “Version Management with Semantic-release”](#version-management-with-semantic-release) The recommended way to handle versioning with Capgo is to set the version in your `capacitor.config.ts` file by importing it from `package.json`: ```ts import pkg from './package.json' const config: CapacitorConfig = { // ... other config plugins: { CapacitorUpdater: { version: pkg.version, } } } ``` This approach allows you to: 1. Use semantic-release (or any other tool) to update the `package.json` version 2. Build your app with the updated version automatically included 3. Upload the bundle with the correct version Your CI/CD workflow would look like this: ```yaml - run: npm ci - run: npx semantic-release # Updates package.json version - run: npm run build # Builds with new version from capacitor.config - run: npx @capgo/cli@latest bundle upload --channel=production --apikey=${{ secrets.CAPGO_API_KEY }} ``` Here’s a sample `.releaserc` configuration file for semantic-release: ```json { "branches": [ "main", { "name": "beta", "prerelease": true } ], "plugins": [ "@semantic-release/commit-analyzer", "@semantic-release/release-notes-generator", "@semantic-release/changelog", [ "@semantic-release/git", { "assets": ["CHANGELOG.md", "package.json"], "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}" } ] ] } ``` This configuration does the following: 1. Analyzes commit messages to determine the next version number, following the Conventional Commits spec. 2. Generates release notes based on the commits since the last release. 3. Updates the `CHANGELOG.md` file with the new release notes. 4. Updates the `package.json` version, which will be picked up by your capacitor.config. 5. Commits the updated `CHANGELOG.md`, `package.json`, and any other changed files back to the repository. Make sure to run semantic-release before building your app so that the updated version from `package.json` is included in your build through the capacitor.config. ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) If you encounter issues with your Capgo CI/CD integration, here are a few things to check: * **API key**: Ensure your API key is valid and has the necessary permissions. If using an environment variable, double check that it’s set correctly. * **CLI version**: Make sure you’re using the latest version of the Capgo CLI. Older versions may have compatibility issues or lack certain features. * **Build artifacts**: Confirm that your web build is generating the expected output files. The Capgo CLI needs a valid web build to create a bundle. * **Network connectivity**: Check that your CI/CD environment has network access to the Capgo servers. Firewall or proxy issues can sometimes interfere with the `upload` command. If you’re still having trouble, reach out to Capgo support for assistance. They can help troubleshoot any issues with your specific setup. ## Conclusion [Section titled “Conclusion”](#conclusion) Integrating Capgo into your CI/CD pipeline with proper version management can greatly streamline your development workflow. By automating your deployments and versioning through the capacitor.config approach, you can ship updates faster and with more confidence. The recommended approach of setting the version in your `capacitor.config.ts` file and using semantic-release to update `package.json` provides a robust and reliable deployment process that allows you to focus on building great features rather than worrying about manual release steps. For more details on the Capgo CLI commands and options, check out the [CLI reference](/docs/cli/overview). And for a deeper dive into semantic-release configuration, see the [semantic-release docs](https://github.com/semantic-release/semantic-release). Happy deploying! # Deploy a Live Update > Learn how to deploy a live update to your app using Capgo's Live Updates feature, enabling real-time UI and logic updates without app store resubmission. Use Capgo’s Live Updates feature to update the UI and business logic of your app remotely, in real-time. Push JS bundle updates directly to your users without going through the app store to instantly fix bugs and ship new features. This guide assumes you’ve completed the [Capgo Quickstart](/docs/getting-started/quickstart) and have already: 1. Installed the `@capgo/capacitor-updater` SDK in your Capacitor app 2. Configured your app ID and update channel in `capacitor.config.ts` 3. Added in your code the `CapacitorUpdater.notifyAppReady()` method If you haven’t done those steps yet, please go back and complete the quickstart first. [Add an app ](/docs/getting-started/add-an-app/)Add an app to your Capgo account, and install the plugin in your app ## Uploading a Bundle [Section titled “Uploading a Bundle”](#uploading-a-bundle) With the Capgo SDK installed and configured, you’re ready to upload your first live update bundle: 1. Build your web assets: ```shell npm run build ``` 2. Upload the bundle to Capgo: * Console ```shell npx @capgo/cli@latest bundle upload --channel=production ``` * Github Actions .github/workflows/build\_and\_deploy.yml ```yml name: Build source code and send to Capgo concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true on: push: branches: - main jobs: deploy_to_capgo: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v6 - uses: actions/setup-node@v6 with: node-version: '24' - name: Install dependencies run: npm install - name: Build run: npm run build - name: Deploy to Capgo run: npx @capgo/cli@latest bundle upload -a ${{ secrets.CAPGO_TOKEN }} --channel ${{ env.CHANNEL }} env: CAPGO_TOKEN: ${{ secrets.CAPGO_TOKEN }} ``` * Gitlab .gitlab-ci.yml ```yml stages: - build build: stage: build image: node:18 cache: - key: files: - package-lock.json paths: - .node_modules/ script: - npm install - npm run build - npx @capgo/cli@latest bundle upload -a $CAPGO_TOKEN --channel $CAPGO_CHANNEL artifacts: paths: - node_modules/ - dist/ only: - master ``` This will upload a new bundle version to the channel specified in the command. ### Troubleshooting Uploads [Section titled “Troubleshooting Uploads”](#troubleshooting-uploads) If your upload fails, double check: * Your app ID in `capacitor.config.ts` matches your app in the Capgo dashboard * You’re running the upload command from the root of your Capacitor project * Your web assets are built and up to date If you’re still having trouble, go to the [Troubleshooting](/docs/getting-started/troubleshooting/) section. ## Receiving an Update on a Device [Section titled “Receiving an Update on a Device”](#receiving-an-update-on-a-device) Once your bundle is uploaded, you can test the live update on a device: 1. Sync your app to the device: ```shell npx cap sync ios ``` 2. Open another terminal and run the following command to check the update status: ```shell npx @capgo/cli@latest app debug ``` 3. Run your app locally: ```shell npx cap run ios ``` Or open the iOS/Android project in Xcode/Android Studio and do a native run. 4. Keep the app open for about 30 seconds to allow the update to download in the background. 5. The logs will take a few seconds to update and show the update status. 6. Close and reopen the app. You should see your live update applied! Refer back to the [Capgo Quickstart](/docs/getting-started/quickstart#receiving-a-live-update-on-a-device) for more details on testing live updates. ## Next Steps [Section titled “Next Steps”](#next-steps) Congrats on deploying your first live update with Capgo! 🎉 To learn more, review the rest of the [Capgo Live Updates documentation](/docs/live-updates). Some key topics to check out next: * [Targeting Updates with Channels](/docs/live-updates/channels) * [Customizing Update Behavior](/docs/live-updates/update-behavior) * [Live Update Rollbacks](/docs/live-updates/rollbacks) # CLI Onboarding Guide > Complete step-by-step guide to onboard your app with Capgo using the interactive CLI ## Quick Overview [Section titled “Quick Overview”](#quick-overview) The Capgo CLI provides an interactive onboarding that sets up live updates for your Capacitor app. You’ll: 1. ✅ Register your app in Capgo 2. 🔌 Install and configure the updater plugin 3. 🚀 Deploy your first live update 4. 📱 Test the update on your device **Estimated time:** 10-20 minutes (varies based on your internet speed and build time) Tip The onboarding is fully resumable - exit anytime and continue later from where you left off. ## Starting the Onboarding [Section titled “Starting the Onboarding”](#starting-the-onboarding) Run the onboarding command with your API key: ```bash npx @capgo/cli@latest init [APIKEY] ``` You’ll see the welcome message: ```plaintext Capgo onboarding 🛫 ``` ## What Happens During Onboarding [Section titled “What Happens During Onboarding”](#what-happens-during-onboarding) The CLI will guide you through 13 interactive steps: **Setup Phase (Steps 1-6):** * Check your development environment (Xcode/Android Studio) * Add your app to Capgo and create a production channel * Install the `@capgo/capacitor-updater` plugin * Inject the required code into your app * Optionally enable end-to-end encryption * Choose a platform for testing (iOS or Android) **Testing Phase (Steps 7-12):** * Build your app and run it on a device/simulator * Make a visible code change (automatic or manual) * Upload the updated bundle to Capgo * See the live update appear on your device in real-time **Completion (Step 13):** * Your app is ready for live updates! 🎉 ## The 13-Step Onboarding Process [Section titled “The 13-Step Onboarding Process”](#the-13-step-onboarding-process) ### Step 1: Check Prerequisites [Section titled “Step 1: Check Prerequisites”](#step-1-check-prerequisites) The CLI checks your development environment to ensure you have the necessary tools installed. **What’s checked:** * **Xcode** (macOS only) - for iOS development * **Android SDK** - for Android development **Possible outcomes:** ✅ **Both environments found:** ```plaintext ✅ Xcode detected - iOS development ready ✅ Android SDK detected - Android development ready ``` ⚠️ **No environment found:** ```plaintext ⚠️ Xcode not found ⚠️ Android SDK not found ❌ No development environment detected 📱 To develop mobile apps with Capacitor, you need: • For iOS: Xcode (macOS only) - https://developer.apple.com/xcode/ • For Android: Android Studio - https://developer.android.com/studio ``` **Questions you may be asked:** Caution If no development environment is detected, you’ll be asked if you want to continue. It’s recommended to install at least one platform before proceeding. ### Step 2: Add Your App [Section titled “Step 2: Add Your App”](#step-2-add-your-app) The CLI will log you into Capgo and add your app to your account. ```plaintext (spinner) Running: npm @capgo/cli@latest login *** Login Done ✅ ❓ Add {appId} in Capgo? ``` **If your app ID is already taken:** The CLI will suggest alternatives: ```plaintext ❌ App ID "com.example.app" is already taken 💡 Here are some suggestions: 1. com.example.app2 2. com.example.app3 3. com.example.app.new 4. com.example.app.app ❓ What would you like to do? ``` You can choose a suggestion or enter a custom app ID. Note App IDs must follow reverse domain notation (e.g., `com.example.myapp`) ### Step 3: Create Production Channel [Section titled “Step 3: Create Production Channel”](#step-3-create-production-channel) Channels allow you to manage different update streams for your app. ```plaintext ❓ Create default channel production for {appId} in Capgo? ``` Tip **Don’t worry!** This is just for local testing during onboarding. Creating a “production” channel doesn’t mean your updates will go live to customers immediately. You have full control over when updates are deployed. Select **Yes** unless you have specific channel requirements. **If you select Yes:** ```plaintext (spinner) Running: npm @capgo/cli@latest channel add production {appId} --default Channel add Done ✅ (or "Channel already added ✅") ``` A production channel will be created and set as default. This is the recommended option for most users. **If you select No:** ```plaintext If you change your mind, run it for yourself with: "npm @capgo/cli@latest channel add production {appId} --default" ``` You’ll need to create and configure channels manually later. Alternatively, you can: * Set the channel in your `capacitor.config.ts` file * Use the JavaScript `setChannel()` method to dynamically set the channel * Configure channels later from the Capgo web console ### Step 4: Install Updater Plugin [Section titled “Step 4: Install Updater Plugin”](#step-4-install-updater-plugin) The CLI will install the `@capgo/capacitor-updater` plugin compatible with your Capacitor version. ```plaintext ❓ Automatic Install "@capgo/capacitor-updater" dependency in {appId}? ``` **Version compatibility:** * **Capacitor 5**: Installs `@capgo/capacitor-updater` v5 * **Capacitor 6**: Installs `@capgo/capacitor-updater` v6 * **Capacitor 7**: Installs `@capgo/capacitor-updater` v7 * **Capacitor 8+**: Installs latest version Caution Capgo only supports Capacitor v5 and above. If you’re using an older version, you’ll need to upgrade first. **Instant updates option:** After installation, you’ll be asked: ```plaintext ❓ Do you want to set instant updates in {appId}? Read more: https://capgo.app/docs/live-updates/update-behavior/#applying-updates-immediately ``` Tip **What are instant updates?** With instant updates enabled, your app applies updates immediately when backgrounded and reopened. This works seamlessly because Capgo can distribute updates worldwide in under 300ms. Without it (standard mode), updates download in the background and apply on the next app restart. Instant updates are great for faster iteration during development and critical bug fixes in production. **If you select Yes:** * Updates will be configured to apply immediately when the app is backgrounded and reopened * `directUpdate: 'always'` and `autoSplashscreen: true` will be added to your config * Your `capacitor.config.ts` will be updated automatically * **Delta updates** will be automatically enabled - this sends only the files that changed between updates instead of the full bundle, making updates much faster **If you select No:** * Updates will use standard behavior (download in background, apply on next restart) * You can always enable instant updates later by modifying your `capacitor.config.ts` ### Step 5: Add Integration Code [Section titled “Step 5: Add Integration Code”](#step-5-add-integration-code) The CLI will automatically inject the required code into your main application file. ```plaintext ❓ Automatic Add "CapacitorUpdater.notifyAppReady()" code and import in {appId}? ``` **What gets added:** ```typescript import { CapacitorUpdater } from '@capgo/capacitor-updater' CapacitorUpdater.notifyAppReady() ``` **Project type detection:** * **Nuxt.js**: Creates `plugins/capacitorUpdater.client.ts` * **Other frameworks**: Adds to your main entry file Tip **If auto-injection fails**, you can add the code manually to your main application file: **For Nuxt.js:** Create `plugins/capacitorUpdater.client.ts`: ```typescript import { CapacitorUpdater } from '@capgo/capacitor-updater' export default defineNuxtPlugin(() => { CapacitorUpdater.notifyAppReady() }) ``` **For other frameworks:** Add to your main entry file (e.g., `main.ts`, `index.js`, `App.tsx`): ```typescript import { CapacitorUpdater } from '@capgo/capacitor-updater' CapacitorUpdater.notifyAppReady() ``` Place this code after your imports and before your app initialization. For more details, see the [Add an App guide](/docs/getting-started/add-an-app/). ### Step 6: Setup Encryption (Optional) [Section titled “Step 6: Setup Encryption (Optional)”](#step-6-setup-encryption-optional) End-to-end encryption adds an extra security layer for your updates. ```plaintext 🔐 End-to-end encryption ✅ Use this for: Banking, healthcare, or apps with legal encryption requirements ⚠️ Note: Makes debugging harder - skip if you don't need it ❓ Enable end-to-end encryption for {appId} updates? ``` Note Encryption is only available for Capacitor v6 and above. If you enable encryption, the CLI will: 1. Generate encryption keys 2. Offer to sync your Capacitor configuration ### Step 7: Select Platform [Section titled “Step 7: Select Platform”](#step-7-select-platform) Choose which platform to test with during onboarding. ```plaintext 📱 Platform selection for onboarding This is just for testing during onboarding - your app will work on all platforms ❓ Which platform do you want to test with during this onboarding? Options: - iOS - Android ``` Tip This only affects the onboarding process. Your final app will support all platforms. ### Step 8: Build Your Project [Section titled “Step 8: Build Your Project”](#step-8-build-your-project) The CLI will build your app and sync it with Capacitor. ```plaintext ❓ Automatic build {appId} with "npm run build"? ``` **What happens:** 1. Detects your project type 2. Runs your build script 3. Executes `npx cap sync {platform}` **If build script is missing:** You’ll be asked if you want to skip the build or add a build script to your `package.json`. ### Step 9: Run on Device [Section titled “Step 9: Run on Device”](#step-9-run-on-device) Test the initial version of your app on a device or simulator. ```plaintext ❓ Run {appId} on {PLATFORM} device now to test the initial version? ``` If you select **Yes**: ```plaintext (spinner) Running: npx cap run {platform} (device picker appears) App started ✅ 📱 Your app should now be running on your {platform} device with Capgo integrated 🔄 This is your baseline version - we'll create an update next ``` ### Step 10: Make a Test Change [Section titled “Step 10: Make a Test Change”](#step-10-make-a-test-change) Now it’s time to test Capgo’s update system by making a visible change. ```plaintext 🎯 Now let's test Capgo by making a visible change and deploying an update! ❓ How would you like to test the update? Options: - Auto: Let Capgo CLI make a visible change for you - Manual: I'll make changes myself ``` **Auto mode:** The CLI will automatically modify your files to add a visible test banner or change. **Manual mode:** You make your own changes (e.g., change text, colors, or add elements). **Version handling:** ```plaintext ❓ How do you want to handle the version for this update? Options: - Auto: Bump patch version ({currentVersion} → {nextVersion}) - Manual: I'll provide the version number ``` **Build with changes:** ```plaintext ❓ Build {appId} with changes before uploading? ``` Tip If you need to build manually in another terminal, select “No” and build yourself, then continue. ### Step 11: Upload Bundle [Section titled “Step 11: Upload Bundle”](#step-11-upload-bundle) Upload your updated app bundle to Capgo. ```plaintext ❓ Upload the updated {appId} bundle (v{version}) to Capgo? ``` The CLI runs: ```bash npx @capgo/cli@latest bundle upload ``` Tip **Delta updates with Direct Update:** If you enabled instant updates (Direct Update) in Step 4, the CLI will automatically ask if you want to enable delta updates. Delta updates send only the files that changed between versions instead of the entire bundle. Since usually only a few files change between updates, this makes downloads much faster. Select **Yes** for the best experience with instant updates. **Delta updates prompt (if Direct Update is enabled):** ```plaintext 💡 Direct Update (instant updates) is enabled in your config Delta updates send only changed files instead of the full bundle ❓ Enable delta updates for this upload? (Recommended with Direct Update) ``` Caution For monorepos, you may need to provide additional paths to your `package.json` and `node_modules`. **Success:** ```plaintext ✅ Update v{version} uploaded successfully! 🎉 Your updated bundle is now available on Capgo ``` ### Step 12: Test Update on Device [Section titled “Step 12: Test Update on Device”](#step-12-test-update-on-device) Time to see the update in action! ```plaintext 🧪 Time to test the Capgo update system! 📱 Go to your device where the app is running ``` **For instant updates:** ```plaintext 🔄 IMPORTANT: Background your app (swipe up/press home button) and then reopen it ⏱️ The update should be downloaded and applied automatically ``` **For standard updates:** ```plaintext 📱 With standard updates, you will need to: 1. Background the app (swipe up/press home button) to start download 2. Wait a few seconds for download to complete 3. Background and foreground again to see the update ``` **Monitor logs:** ```plaintext ❓ Monitor Capgo logs to verify the update worked? ``` If you select **Yes**, you’ll see live logs from your device showing the update process. ### Step 13: Completion [Section titled “Step 13: Completion”](#step-13-completion) ```plaintext Welcome onboard ✈️! ``` Congratulations! You’ve successfully set up Capgo live updates for your app. ## What You’ve Accomplished [Section titled “What You’ve Accomplished”](#what-youve-accomplished) After completing the onboarding, you have: ✅ App Registered Your app is registered in Capgo with a production channel ✅ Plugin Installed The Capacitor Updater plugin is installed and configured ✅ Code Integrated Integration code is added to your app ✅ Update Tested You’ve successfully deployed and received a live update ## Daily Workflow [Section titled “Daily Workflow”](#daily-workflow) For subsequent updates, use: ```bash npm run build npx @capgo/cli@latest bundle upload --channel=production ``` For more deployment options, see [Deploy a Live Update](/docs/getting-started/deploy/). ## Resuming Onboarding [Section titled “Resuming Onboarding”](#resuming-onboarding) If you exit the onboarding process, you can resume anytime: ```bash npx @capgo/cli@latest init [APIKEY] ``` You’ll see: ```plaintext You have already got to the step {stepNumber}/13 in the previous session ❓ Would you like to continue from where you left off? ``` Tip Progress is saved locally, so you can safely exit and resume the onboarding process. ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) ### No Development Environment [Section titled “No Development Environment”](#no-development-environment) **Problem:** Neither Xcode nor Android SDK is detected. **Solution:** * **For iOS**: Install [Xcode](https://developer.apple.com/xcode/) (macOS only) * **For Android**: Install [Android Studio](https://developer.android.com/studio) ### App ID Already Taken [Section titled “App ID Already Taken”](#app-id-already-taken) **Problem:** Your app ID is already registered. **Solution:** Choose one of the suggested alternatives or enter a custom app ID in reverse domain notation. ### Build Script Missing [Section titled “Build Script Missing”](#build-script-missing) **Problem:** No build script found in `package.json`. **Solution:** Add a build script to your `package.json`: ```json { "scripts": { "build": "your-build-command" } } ``` ### Auto-Injection Failed [Section titled “Auto-Injection Failed”](#auto-injection-failed) **Problem:** CLI cannot automatically inject the integration code. **Solution:** Add the code manually to your main file: ```typescript import { CapacitorUpdater } from '@capgo/capacitor-updater' CapacitorUpdater.notifyAppReady() ``` ### Capacitor Version Too Old [Section titled “Capacitor Version Too Old”](#capacitor-version-too-old) **Problem:** Your Capacitor version is below v5. **Solution:** Upgrade Capacitor to v5 or higher: * [Upgrading to Capacitor 5](https://capacitorjs.com/docs/updating/5-0) * [Upgrading to Capacitor 6](https://capacitorjs.com/docs/updating/6-0) * [Upgrading to Capacitor 7](https://capacitorjs.com/docs/updating/7-0) ## Next Steps [Section titled “Next Steps”](#next-steps) Now that you’ve completed onboarding, explore these topics: [ Deploy Updates](/docs/getting-started/deploy/) [Learn how to deploy updates from the Capgo dashboard](/docs/getting-started/deploy/) [ Update Types](/docs/live-updates/update-types/) [Reference of all OTA update types: apply timing, delay conditions, version blocking, and delivery](/docs/live-updates/update-types/) [ CI/CD Integration](/docs/getting-started/cicd-integration/) [Automate your update deployments with CI/CD](/docs/getting-started/cicd-integration/) [ Channels](/docs/live-updates/channels/) [Manage multiple update streams with channels](/docs/live-updates/channels/) [ Encryption](/docs/live-updates/encryption/) [Secure your updates with end-to-end encryption](/docs/live-updates/encryption/) [ Update Behavior](/docs/live-updates/update-behavior/) [Customize when and how updates are applied (direct, delta, etc.)](/docs/live-updates/update-behavior/) ## Getting Help [Section titled “Getting Help”](#getting-help) If you encounter issues during onboarding: * Check the [Troubleshooting Guide](/docs/getting-started/troubleshooting/) * Join the [Discord Community](https://discord.capgo.app) * Review the [FAQ](/docs/faq/) * Contact [Support](/docs/getting-help/) # Overview > Get started with Capgo by learning the key concepts and steps to integrate and deploy live updates to your app. The quickstart tutorial will walk you through the key concepts of Capgo! Concepts that will be explored include: 1. Adding an app to your Capgo account 2. Integrating Capgo with your CI/CD 3. Triggering bundle upload on Capgo by pushing commits 4. Configuring and customizing the Capgo bundle publishing 5. Setting up your app to enable live updates via Capgo 6. Deploying live updates to your app from Capgo Simply follow the guide step-by-step, or navigate directly to the documentation for the component that interests you. [ Start the Tutorial](/docs/getting-started/add-an-app/) [Follow the quickstart tutorial and get up and running with Capgo in no time!](/docs/getting-started/add-an-app/) [ CLI Onboarding Guide](/docs/getting-started/onboarding/) [Complete step-by-step guide for the interactive CLI onboarding process.](/docs/getting-started/onboarding/) [ Ship updates](/docs/getting-started/deploy/) [Ship updates to your app from the Capgo dashboard.](/docs/getting-started/deploy/) [ Automate updates](/docs/getting-started/cicd-integration/) [Integrate Capgo with your CI/CD and trigger bundle uploads on Capgo by pushing commits.](/docs/getting-started/cicd-integration/) [ Trouble Shooting](/docs/getting-started/troubleshooting/) [Common issues and how to solve them.](/docs/getting-started/troubleshooting/) [ Wrap Up](/docs/getting-started/wrapping-up/) [Wrap up the tutorial and get a quick overview of what you’ve learned.](/docs/getting-started/wrapping-up/) Tip The Over-the-Air (OTA) update feature is applicable only for modifications made to HTML, CSS, and JavaScript files. If you make any changes to the native code, such as updates to Capacitor plugins, it is mandatory to resubmit the application to the app store for approval. ## Join Discord Community [Section titled “Join Discord Community”](#join-discord-community) [Join the Capgo Discord Server!](https://discord.capgo.app) ## Maintenance [Section titled “Maintenance”](#maintenance) | Plugin version | Capacitor compatibility | Maintained | | ------------------ | ----------------------- | -------------------------------------------------------- | | v7.\*.\* (≥7.25.0) | v7.\*.\* | ✅ Fully supported | | v6.\*.\* (≥6.25.0) | v6.\*.\* | ✅ Fully supported | | v5.\*.\* (≥5.10.0) | v5.\*.\* | ✅ Fully supported | | v5.\*.\* (<5.10.0) | v5.\*.\* | ⚠️ Deprecated | | v4.\*.\* | v4.\*.\* | ❌ No longer supported | | v3.\*.\* | v3.\*.\* | ❌ No longer supported | | >= 8 | v4.\*.\* | ⚠️ Deprecated due to versioning issues in our CI process | ## Store Guideline Compliance [Section titled “Store Guideline Compliance”](#store-guideline-compliance) Android Google Play and iOS App Store have corresponding guidelines that have rules you should be aware of before integrating the Capacitor-updater solution within your application. ### Google play [Section titled “Google play”](#google-play) Third paragraph of [Device and Network Abuse](https://support.google.com/googleplay/android-developer/answer/9888379/?hl=en) topic describe that updating source code by any method other than Google Play’s update mechanism is restricted. But this restriction does not apply to updating javascript bundles. > This restriction does not apply to code that runs in a virtual machine and has limited access to Android APIs (such as JavaScript in a webview or browser). That fully allows Capacitor-updater as it updates just the JS bundles and won’t update native code. ### App Store [Section titled “App Store”](#app-store) Paragraph **3.3.2**, since back in 2015’s [Apple Developer Program License Agreement](https://developer.apple.com/programs/ios/information/) fully allows performing over-the-air updates of JavaScript and assets - and in its latest version (20170605) [downloadable here](https://developer.apple.com/terms/) this ruling is even broader: > Interpreted code may be downloaded to an Application but only so long as such code: (a) does not change the primary purpose of the Application by providing features or functionality that are inconsistent with the intended and advertised purpose of the Application as submitted to the App Store, (b) does not create a store or storefront for other code or applications, and (c) does not bypass signing, sandbox, or other security features of the OS. Capacitor Updater allows you to follow these rules in full compliance so long as the update you push does not significantly deviate your product from its original App Store approved intent. To further remain in compliance with Apple’s guidelines we suggest that App Store-distributed apps do not enable the `Force update` scenario, since in the [App Store Review Guidelines](https://developer.apple.com/app-store/review/guidelines/) state that: > Apps must not force users to rate the app, review the app, download other apps, or other similar actions in order to access functionality, content, or use of the app. This is not a problem for the default behavior of background update, since it won’t force the user to apply the new version until next time they close the app, but at least you should be aware of that role if you decide to show it. ## Open source [Section titled “Open source”](#open-source) The plugin is under the LGPL-3.0 License and the back-end is AGPL-3.0 License. > 💡 LGPL-3.0 means if someone modifies the code of the plugin, it’s mandatory to publish it, in open-source with the same licensing. If you use the code without modification, that doesn’t concern you. See the issue below for more details check the link 👇 [Licensing? ](https://github.com/Cap-go/capacitor-updater/issues/7) [Try GPTS Capgo to Get help instead of reading the docs ](https://chat.openai.com/g/g-3dMwHbF2w-capgo-doc-gpt) > You can include it in your app without worrying ## Final Notes [Section titled “Final Notes”](#final-notes) If you self-host and find this tool useful, please consider supporting my work by becoming a [GitHub sponsor](https://github.com/sponsors/riderx/). I made a bet to open-source all the code I built here instead of paywalling it. By opening it up instead of fighting and hiding, I believe we can make the world a better place. To make this possible, it’s necessary for all of us to do our part, including you 🥹. If Capgo cloud doesn’t meet your needs, you can back a bootstrapped Maker [here](https://github.com/sponsors/riderx/) on your own terms. ## Simple Maths [Section titled “Simple Maths”](#simple-maths) The price of the basic plan: $14\*12 = $168 a year. While average dev/hour = $60. That means that 3 hours wasted of dev time on self-host allows you to pay for a whole year, if you spent more than 3 hours you’re losing money ^^ # Troubleshooting > Resolve common issues encountered while using Capgo with detailed troubleshooting steps and advanced options for upload and debugging. Here are some common issues you might encounter while using Capgo and how to resolve them. 🚀 Need Expert Help? Stuck with a complex issue? Our expert team is here to help! Get personalized support, code reviews, and custom solutions tailored to your specific needs. [Get Professional Support](/consulting/) ### Upload failures [Section titled “Upload failures”](#upload-failures) If your bundle upload fails, double check: * Your app ID in `capacitor.config.ts` matches your app in the Capgo dashboard * You’re running the upload command from the root of your Capacitor project * Your web assets are built and up to date #### Advanced upload options [Section titled “Advanced upload options”](#advanced-upload-options) The Capgo CLI provides some additional flags to help with common upload issues: * `--tus`: Uses the [tus resumable upload protocol](https://tus.io/) for more reliable uploads of large bundles or on poor network connections. If your bundle is over 10MB or you’re on a spotty connection, consider using `--tus`: ```shell npx @capgo/cli@latest bundle upload --tus ``` * `--package-json` and `--node-modules`: Tells Capgo where to find your root `package.json` and `node_modules` if your app uses a non-standard structure like a monorepo or npm workspace. Pass the path to the root `package.json` and the `--node_modules` path: ```shell npx @capgo/cli@latest bundle upload --package-json=path/to/package.json --node_modules=path/to/node_modules ``` Capgo needs this information to correctly bundle your app’s dependencies. You can combine these flags with other options like `--channel` as needed. See the [Capgo CLI docs](/docs/cli/overview/) for full details on the available upload options. If you’re still having trouble with uploads, reach out to [Capgo support](https://support.capgo.app) for further assistance. ### Debugging Updates [Section titled “Debugging Updates”](#debugging-updates) If you’re encountering issues with live updates, the Capgo debug command is a helpful tool for troubleshooting. To use it: 1. Run the following command in your project directory: ```shell npx @capgo/cli@latest app debug ``` 2. Launch your app on a device or emulator and perform the action that should trigger an update (e.g. reopening the app after uploading a new bundle). 3. Watch the output of the debug command. It will log information about the update process, including: * When the app checks for an update * If an update is found and what version it is * Download and installation progress for the update * Any errors that occur during the update process 4. Use the debug logs to identify where the issue is occurring. For example: * If no update is found, double check that your bundle was uploaded successfully and the app is configured to use the correct channel. * If the update downloads but doesn’t install, make sure you’ve called `CapacitorUpdater.notifyAppReady()` and that the app was fully closed and reopened. * If you see an error message, look up that specific error in the Capgo docs or reach out to support for help. The debug command is especially useful for identifying issues with the update download and installation process. If the logs show the expected update version was found but not ultimately applied, focus your troubleshooting on the steps after the download. ### Debugging with Native Logs [Section titled “Debugging with Native Logs”](#debugging-with-native-logs) In addition to the Capgo debug command, the native logs on Android, iOS, and Electron can provide valuable troubleshooting information, especially for issues on the native side of the update process. #### Android Logs [Section titled “Android Logs”](#android-logs) To access the Android logs: 1. Connect your device or start your emulator 2. Open Android Studio and select “View > Tool Windows > Logcat” 3. In the Logcat window, filter the logs to just your app’s process by selecting it from the dropdown at the top 4. Look for any lines that include `Capgo` to find the SDK logs Alternatively, you can use the `adb logcat` command and grep for `Capgo` to filter the logs. The Capgo SDK will log key events during the update process, such as: * When an update check is initiated * If an update is found and what version it is * When the update download starts and completes * When the update installation is triggered * Any errors that occur during the native update steps Common Android-specific issues you might see in the logs include: * Network connectivity problems preventing the update download * File permissions errors when saving or reading the update bundle * Out of storage space for the update bundle * Failure to restart the app after the update is installed #### iOS Logs [Section titled “iOS Logs”](#ios-logs) To access the iOS logs: 1. Connect your device or start your simulator 2. Open Xcode and go to “Window > Devices and Simulators” 3. Select your device and click on “Open Console” 4. In the console output, look for any lines that include `Capgo` to find the SDK logs You can also use the `log stream` command in the terminal and grep for `Capgo` to filter the logs. Similar to Android, the Capgo SDK will log key iOS-side events: * Update check initiation and result * Download start, progress, and completion * Installation trigger and result * Any errors during the native update process iOS-specific issues you might identify in the logs include: * SSL certificate problems when downloading the update * App transport security blocking the update download * Insufficient storage space for the update bundle * Failure to properly extract or apply the update bundle #### Electron Logs [Section titled “Electron Logs”](#electron-logs) For Electron apps, check both the main process and renderer process output: 1. Run the Electron app from your terminal using your normal launch command (for example `bun run electron:dev` or `bun run electron:serve`) and watch the terminal output for startup, update checks, and network errors. 2. Open DevTools in the renderer window (View → Toggle Developer Tools) and inspect console logs and failed network requests while reproducing the update flow. 3. For packaged apps, check OS log tools for crashes or startup failures: * **macOS**: open `Console.app` and filter on your app name * **Windows**: open **Event Viewer** → **Windows Logs** → **Application** * **Linux**: use your desktop log viewer or `journalctl` for your app process When debugging updates, compare messages from both main-process and renderer-process logs to separate Electron bootstrap issues from Capgo update lifecycle issues. Across platforms, the native logs provide a lower-level view into the update process, with more details on the native implementation. They are especially useful for identifying issues that occur outside of the Capgo JavaScript layer. When troubleshooting a tricky live update problem, it’s a good idea to capture both the Capgo debug logs and the native logs for a comprehensive picture of what’s happening. The two logs together will give you the best chance of identifying and resolving the issue. ### Updates not applying [Section titled “Updates not applying”](#updates-not-applying) If you’ve uploaded a bundle but aren’t seeing the changes on your device: * Make sure you’ve called `CapacitorUpdater.notifyAppReady()` in your app code as shown in the [quickstart](/docs/getting-started/quickstart) * Check that your device is connected to the internet and the Capgo debug logs show the update was downloaded * Try fully closing and reopening the app, as updates are only applied on a fresh launch * Look for any errors in the native logs that might indicate a problem applying the update Refer to the [deploying live updates](/docs/getting-started/deploy) guide for more details on the update process. If you’re still stuck, use the `npx @capgo/cli@latest app debug` command and native logs to get more visibility into what’s happening. ### Common update failure codes [Section titled “Common update failure codes”](#common-update-failure-codes) If your logs show backend errors such as `disable_auto_update_to_major`, `semver_error`, or `cannot_update_via_private_channel`, use the dedicated guide: * [Common Update Problems](/docs/plugins/updater/commonproblems/) It explains what each common code means, why it happens, and how to fix it. ## SDK Installation [Section titled “SDK Installation”](#sdk-installation) If you’re having trouble installing the Capgo SDK, make sure: * Your app is using a supported version of Capacitor (4.0 or newer) * You’ve followed the [quickstart](/docs/getting-started/quickstart) steps in order, including syncing your app after installing the SDK ## CI/CD Integration [Section titled “CI/CD Integration”](#cicd-integration) For issues with triggering Capgo uploads from your CI/CD pipeline: * Double check your Capgo authentication token is set up correctly * Make sure you’re running the upload command after your web assets are built * Check that the upload command is using the correct channel name for your target environment See the [CI/CD integration](/docs/getting-started/cicd-integration/) docs for more troubleshooting tips. You can also use the `npx @capgo/cli@latest app debug` command to confirm if your CI/CD-triggered updates are being received by the app. # Wrapping up > Wrap up your Capgo journey with a concise overview of key concepts and next steps, ensuring a solid foundation for future exploration and mastery. Now that you have completed the quickstart guide, you should have a basic understanding of the key concepts of Capgo! The key concepts you have learned in this guide are: 1. Adding an app to your Capgo account 2. Integrating Capgo with your CI/CD pipeline 3. Triggering bundle uploads to Capgo on new commits 4. Configuring your app to enable live updates with the Capgo SDK 5. Deploying live updates to your app from the Capgo dashboard But there’s still more to learn about Capgo! Continue exploring the docs or check out some of these key topics: [ CI/CD Integration](/docs/getting-started/cicd-integration/) [Already have a CI/CD pipeline? Learn how to incorporate Capgo into your existing workflow.](/docs/getting-started/cicd-integration/) [ Live Updates](/docs/live-updates/) [Dive deeper into Capgo’s live update features and best practices.](/docs/live-updates/) [ FAQ](/docs/faq/) [Find answers to common questions about Capgo.](/docs/faq/) [ Troubleshooting](/docs/getting-started/troubleshooting/) [Get help with common issues that can come up while using Capgo.](/docs/getting-started/troubleshooting/) # How to > A comprehensive guide to Capgo, offering detailed tutorials, insightful tips, and advanced techniques to enhance your effective usage of the platform [How version works in Capgo ](https://capgo.app/blog/how-version-work-in-capgo/)capgo.app [How to release major version in Capgo ](https://capgo.app/blog/how-to-release-major-version-in-capgo/)capgo.app [How to send specific update to one user or a group ](https://capgo.app/blog/how-to-send-specific-version-to-users/)capgo.app ## CI / CD [Section titled “CI / CD”](#ci--cd) [Automatic build and release with GitHub Actions ](https://capgo.app/blog/automatic-build-and-release-with-github-actions/)capgo.app [Manage development and production build with GitHub Actions ](https://capgo.app/blog/automatic-build-and-release-with-github-actions/)capgo.app ## Contributing [Section titled “Contributing”](#contributing) [Contributing to Capgo open source ](https://github.com/Cap-go/capgo/blob/main/CONTRIBUTING.md)github.com # Overview > Discover how Capgo's Live Updates enable seamless JavaScript bundle updates, allowing you to push changes directly to users without app store delays. Use Capgo’s Live Updates feature to update the JavaScript bundles of your app remotely, in real-time. Push JS updates directly to your users on iOS, Android, and Electron without going through store-level review cycles to fix bugs and ship new features faster. Note Live Updates are limited to JavaScript bundle changes. If you need to update native code, such as adding or removing a plugin or changing native project configuration, you’ll need to submit a new binary build through the usual platform distribution process. ## How Live Updates Work [Section titled “How Live Updates Work”](#how-live-updates-work) Capgo’s Live Update system has two key components: 1. The Capgo SDK, which you install in your app. The SDK checks for available updates and downloads them in the background. 2. Channels, which let you target updates to specific groups of users. You can use channels to manage different release tracks, such as `Production`, `Staging`, and `Dev`. When you upload a new JS bundle to Capgo and assign it to a channel, the Capgo SDK in apps configured for that channel will detect the update and download it. The next time the app restarts, the new bundle will be loaded. ## Why Capgo Logs Matter (marketing view) [Section titled “Why Capgo Logs Matter (marketing view)”](#why-capgo-logs-matter-marketing-view) * **Instant x-ray of every rollout**: Per-device timelines show checks, downloads, installs, policy blocks, and rollbacks, so you know exactly what happened—no guesswork or “it works on my phone” debates. * **Faster incident response**: Alert-like codes (e.g., rate limits, checksum fails, notifyAppReady misses) surface before users start flooding support, letting you ship a fix or rollback in minutes. * **Channel policy proof**: Logs verify that guardrails (block majors, disable emulators/dev builds, platform limits) are actively protecting production. * **Revenue & reputation protection**: See when updates stall on poor networks or hit plan limits, so you can intervene before conversions, sessions, or reviews drop. * **Single source of truth**: Product, QA, and Support share the same cloud log stream—no digging through Xcode/Android Studio or DM’ing engineers for native logs. ## Getting Started [Section titled “Getting Started”](#getting-started) To start using Live Updates, follow these steps: 1. Complete the [Capgo Quickstart](/docs/getting-started/quickstart) to set up your app in Capgo and install the Capgo SDK. 2. In your app code, call `CapacitorUpdater.notifyAppReady()` after your app has finished initializing. This tells the Capgo SDK that your app is ready to receive updates. 3. Build your JS bundle and upload it to Capgo: ```shell npm run build npx @capgo/cli@latest bundle upload --channel=production ``` 4. Open your app and wait for the update to download. You can check the status with: ```shell npx @capgo/cli@latest app debug ``` 5. Once the update is downloaded, close and reopen your app to load the new bundle. See the [Deploying Live Updates](/docs/getting-started/deploy) guide for more details. ## Next Steps [Section titled “Next Steps”](#next-steps) [ Update Types](/docs/live-updates/update-types/) [Reference of all OTA update types: apply timing, delay conditions, version blocking, and delivery.](/docs/live-updates/update-types/) [ Channels](/docs/live-updates/channels/) [Learn how to use channels to manage different release tracks and target updates to specific users.](/docs/live-updates/channels/) [ Rollbacks](/docs/live-updates/rollbacks/) [Discover how to roll back to a previous JS bundle version if an update causes issues.](/docs/live-updates/rollbacks/) [ Update Behavior](/docs/live-updates/update-behavior/) [Customize how and when updates are downloaded and applied in your app.](/docs/live-updates/update-behavior/) [ Fast Updates](/docs/live-updates/differentials/) [Learn how to use fast updates to speed up the update process.](/docs/live-updates/differentials/) # Breaking Changes > How to handle breaking changes with versioned channels This documentation explains how to handle breaking changes in your app using versioned channels. This approach allows you to maintain different versions of your app while ensuring users receive compatible updates. ## Example Scenario [Section titled “Example Scenario”](#example-scenario) Let’s say you have: * App version 1.2.3 (old version) - uses production channel * App version 2.0.0 (new version with breaking changes) - uses v2 channel * Live update 1.2.4 (compatible with 1.2.3) * Live update 2.0.1 (compatible with 2.0.0) ## Strategy: Always Use defaultChannel for Major Versions [Section titled “Strategy: Always Use defaultChannel for Major Versions”](#strategy-always-use-defaultchannel-for-major-versions) **Recommended approach:** Set a `defaultChannel` for every major version. This ensures you can always push updates to specific user groups without relying on dynamic channel assignment. ```ts // Version 1.x releases defaultChannel: 'v1' // Version 2.x releases defaultChannel: 'v2' // Version 3.x releases (future) defaultChannel: 'v3' ``` Tip **Benefits of this approach:** * **Always have control** over which users receive updates * **No dynamic channel switching** needed in your app code * **Clear separation** between different app versions * **Flexibility** to push updates to any specific version group ## 1. Create Channel for New Version [Section titled “1. Create Channel for New Version”](#1-create-channel-for-new-version) ```bash # Create channel for version 2.x npx @capgo/cli channel create v2 ``` ## 2. Update Capacitor Config for Version 2.0.0 [Section titled “2. Update Capacitor Config for Version 2.0.0”](#2-update-capacitor-config-for-version-200) Update your Capacitor config before building version 2.0.0 for the app store: capacitor.config.ts ```ts import { CapacitorConfig } from '@capacitor/cli'; const config: CapacitorConfig = { appId: 'com.example.app', appName: 'Example App', plugins: { CapacitorUpdater: { // ... other options defaultChannel: 'v2' // All 2.0.0 users will use v2 channel } } }; export default config; ``` Note **For version 1.x:** If you didn’t set a `defaultChannel` initially, version 1.x users are on the `production` channel. For future major versions, always set a specific channel like `v3`, `v4`, etc. ## 3. Manage Separate Code Branches [Section titled “3. Manage Separate Code Branches”](#3-manage-separate-code-branches) Create separate git branches to maintain compatibility between app versions: ```bash # Create and maintain a branch for version 1.x updates git checkout -b v1-maintenance git push origin v1-maintenance # Your main branch continues with version 2.x development git checkout main ``` **Critical:** Never push JavaScript bundles to older apps that expect native code/APIs they don’t have. Always build updates from the appropriate branch: * **v1-maintenance branch**: For updates to 1.x apps (production channel) * **main branch**: For updates to 2.x apps (v2 channel) ## 4. Upload Bundles to Respective Channels [Section titled “4. Upload Bundles to Respective Channels”](#4-upload-bundles-to-respective-channels) ```bash # For 1.x updates: Build from v1-maintenance branch git checkout v1-maintenance # Make your 1.x compatible changes here npx @capgo/cli bundle upload --channel production # For 2.x updates: Build from main branch git checkout main # Make your 2.x changes here npx @capgo/cli bundle upload --channel v2 ``` ## 5. Enable Self-Assignment [Section titled “5. Enable Self-Assignment”](#5-enable-self-assignment) ```bash # Allow apps to self-assign to v2 channel npx @capgo/cli channel set v2 --self-assign ``` ## 6. Deploy to App Store [Section titled “6. Deploy to App Store”](#6-deploy-to-app-store) Build and deploy version 2.0.0 to the app store. All users who download this version (whether new users or existing users upgrading) will automatically use the v2 channel because it’s configured in the app bundle. Note **No code changes needed!** Since `defaultChannel: 'v2'` is bundled with the app store version, all users downloading version 2.0.0 will automatically use the correct channel. ## Scaling to Future Versions [Section titled “Scaling to Future Versions”](#scaling-to-future-versions) When you release version 3.0.0 with more breaking changes: ```bash # Create channel for version 3.x npx @capgo/cli channel create v3 ``` ```ts // capacitor.config.ts for version 3.0.0 const config: CapacitorConfig = { // ... plugins: { CapacitorUpdater: { defaultChannel: 'v3' // Version 3.x users } } }; ``` Now you can push updates to any version: * `production` channel → Version 1.x users * `v2` channel → Version 2.x users * `v3` channel → Version 3.x users ## 7. Cleanup (After Migration) [Section titled “7. Cleanup (After Migration)”](#7-cleanup-after-migration) Once all users have migrated to version 2.x (count 3-4 months): 1. Remove `defaultChannel` from your Capacitor config 2. Delete the v2 channel: ```bash npx @capgo/cli channel delete v2 ``` 3. Delete the v1-maintenance branch: ```bash git branch -d v1-maintenance git push origin --delete v1-maintenance ``` Tip This approach ensures users only receive updates compatible with their app version Always test updates thoroughly in each channel before deployment Note You can safely delete the v2 channel in Capgo even if some users still have the channel override. They will automatically receive updates from the production channel instead. ## Maintaining Version 1.x Updates [Section titled “Maintaining Version 1.x Updates”](#maintaining-version-1x-updates) To send updates compatible with version 1.x: 1. Switch to the v1-maintenance branch: ```bash git checkout v1-maintenance ``` 2. Make your changes and commit: ```bash # Make 1.x compatible changes git add . git commit -m "Fix for v1.x" git push origin v1-maintenance ``` 3. Build and upload to production channel: ```bash npx @capgo/cli bundle upload --channel production ``` Tip Keep your v1-maintenance branch up to date with bug fixes that are compatible with version 1.x, but never merge breaking changes from main # Channels > Learn how to manage and configure Live Update channels in Capgo, enabling seamless app updates by directing specific JS bundle builds to devices configured for those channels. A Live Update channel points to a specific JS bundle build of your app that will be shared with any devices configured to listen to that channel for updates. When you [install the Capgo Live Updates SDK](/docs/getting-started/quickstart/) in your app, any native binary configured to that channel will check for available updates whenever the app is launched. You can change the build a channel points to at any time and can also roll back to previous builds if needed. ## How a device picks a channel (precedence) [Section titled “How a device picks a channel (precedence)”](#how-a-device-picks-a-channel-precedence) When a device checks for an update, Capgo decides which channel to use in this strict order (highest priority first): 1. **Forced device mapping (Dashboard)** – Manually pin a specific device ID to a channel. Use for urgent debugging or controlled testing with a single real user. This always wins. 2. **Cloud override (per‑device) via Dashboard or API** – Created when you change the device’s channel in the dashboard or via API. Use for QA users switching between feature / PR channels or to reproduce a user issue. Reinstalling the binary does not clear it; deleting the device entry does. Instant Channel Switching with setChannel() **Starting from plugin version 5.34.0, 6.34.0, 7.34.0, or 8.0.0** (depending on your major version), `setChannel()` works differently: it contacts the backend to **validate** that the channel is allowed (checking if self-assignment is enabled for that channel), then stores the channel **locally on the device** as `defaultChannel`. This means the new channel takes effect **instantly** for the next update check—no waiting for replication. Previously, `setChannel()` saved the channel override to the backend database (like Dashboard or API changes), and devices had to wait for data replication (up to 2 minutes) before the new channel was recognized. The new behavior only reads from the backend (for validation) and stores locally, making channel switches instant. **Note:** Even if a channel becomes disallowed after being set locally, the backend will still validate the channel during update checks, so security is maintained. **Important:** When channel changes are made via the Dashboard or API, there is still a replication lag of up to 2 minutes before all edge servers reflect the change. For instant channel switching, use `setChannel()` from your app code—it validates with the backend, then sets the channel locally for immediate effect. 3. **Capacitor config `defaultChannel` (test build default)** – If present in `capacitor.config.*` and no force/override exists, the app starts on this channel (e.g. `beta`, `qa`, `pr-123`). Intended for TestFlight / internal builds so testers land on a pre‑release channel automatically. Production builds typically leave this unset. 4. **Cloud Default Channel (primary path \~99% of users)** – If you mark a default channel in the dashboard, all normal end‑users (no force, no override, no config defaultChannel) attach here. Change it to roll out or roll back instantly—no new binary. If you have platform-specific defaults (for example, one iOS-only, one Android-only, one Electron-only), each device lands on the default matching its platform. Leaving the cloud default unset is allowed; in that case the device must match on steps 1–3 to receive updates. Best practice: * Treat 1–3 as exception / testing layers; when you set a cloud default, real users should flow into it. If you choose not to set one, be deliberate about how users attach (typically via `defaultChannel` in config or per-device overrides). * Only configure `defaultChannel` in binaries you explicitly ship to testers. Leaving it unset keeps production logic centralized in the dashboard. * Use `setChannel()` sparingly in production—mainly for QA or targeted diagnostics. If a channel is disabled for the platform (iOS/Android/Electron toggles) when it would otherwise be chosen, the selection process skips it and continues down the list. > Summary: Force > Override > Config `defaultChannel` > Cloud Default. ## Default Channel Behavior [Section titled “Default Channel Behavior”](#default-channel-behavior) Setting a cloud default is optional, but it usually serves as the catch-all path for new devices. Without one, only devices that match on forced mappings, overrides, or a `defaultChannel` in the Capacitor config will receive updates. When you do choose to mark defaults, keep these patterns in mind: * **Single default (most common)** – If a channel has iOS, Android, and Electron enabled, it becomes the lone default; any device without overrides will attach here. * **Platform-specific defaults** – If you split channels by platform (for example, `ios-production` with only iOS enabled, `android-production` with only Android enabled, and `electron-production` with only Electron enabled), mark each one as the default for its platform. iOS devices go to the iOS default, Android devices go to the Android default, and Electron apps go to the Electron default. Remember that the cloud default and `defaultChannel` in `capacitor.config.*` both occupy the same decision layer. If you set a cloud default, you don’t need to duplicate the value in your Capacitor config—leave `defaultChannel` empty for production builds. Reserve `defaultChannel` for binaries you intentionally ship to testers or QA when you want them to start on a non-production channel even if the cloud default is different. You can change defaults at any time in the dashboard. When you swap a default, new devices obey the new routing immediately and existing devices follow the normal precedence rules the next time they check in. ## Setting up a Channel [Section titled “Setting up a Channel”](#setting-up-a-channel) During onboarding you create the first channel (most teams name it “Production”), but nothing is locked—you can rename or delete any channel at any time. To add additional channels later: 1. Go to the “Channels” section of the Capgo dashboard 2. Click the “New Channel” button 3. Enter a name for the channel and click “Create” Channel names can be anything you’d like. A common strategy is to match channels to your development stages, such as: * `Development` - for testing live updates on local devices or emulators * `QA` - for your QA team to verify updates before wider release * `Staging` - for final testing in a production-like environment * `Production` - for the version of your app that end users receive from the app stores ## Configuring the Channel in Your App [Section titled “Configuring the Channel in Your App”](#configuring-the-channel-in-your-app) With your channels created, you need to configure your app to listen to the appropriate channel. In this example, we’ll use the `Development` channel. Open your `capacitor.config.ts` (or `capacitor.config.json`) file. Under the `plugins` section, optionally set `defaultChannel` for **test builds** (internal / QA). For production builds, prefer omitting it so devices use the Cloud Default unless explicitly overridden. ```ts import { CapacitorConfig } from '@capacitor/cli'; const config: CapacitorConfig = { plugins: { CapacitorUpdater: { // For a QA/TestFlight build – testers start on the Development channel automatically. defaultChannel: 'Development', // Production builds usually omit this so users attach to the Cloud Default channel. }, }, }; ``` Next, build your web app and run `npx cap sync` to copy the updated config file to your iOS, Android, and Electron projects. If you skip this sync step, your native projects will continue to use whichever channel they were previously configured for. Caution Channel selection order: Force > Override (`setChannel` / dashboard) > Config `defaultChannel` > Cloud Default. Use `defaultChannel` only in test/internal builds; leave it out for production so users follow the Cloud Default (when set) instead of duplicating the routing in native config. You can still force (pin) a device or apply an override later—those immediately supersede the config value. > Channel names are case sensitive. ## Channel Options and Strategies [Section titled “Channel Options and Strategies”](#channel-options-and-strategies) Channels have several options that control who can receive updates and how updates are delivered. The most important ones are below. You can configure these from the web app, the CLI, or the Public API. * Default channel: Optionally mark the channel or platform-specific channels that new devices attach to. See “Default Channel Behavior” for routing scenarios. * Platform filters: Enable or disable delivery to `iOS`, `Android`, or `Electron` devices per channel. * Disable auto downgrade under native: Prevents sending an update when the device’s native app version is newer than the channel’s bundle (for example, device on 1.2.3 while channel has 1.2.2). * Allow development builds: Permit updates to development builds (useful for testing). * Allow emulator devices: Permit updates to emulators/simulators (useful for testing). * Allow device self‑assignment: Lets the app switch to this channel at runtime using `setChannel`. If disabled, `setChannel` will fail for this channel. ### Disable Auto Update strategies [Section titled “Disable Auto Update strategies”](#disable-auto-update-strategies) Use this to restrict which kinds of updates the channel will automatically deliver. Options: * major: Block cross‑major updates (0.0.0 → 1.0.0). Minor and patch updates still allowed. * minor: Block cross‑minor updates (e.g., 1.1.0 → 1.2.0) and majors. Patch updates still allowed. Note: does not block 0.1.0 → 1.1.0. * patch: Very strict. Allows only increasing patch versions within the same major and minor. Examples: 0.0.311 → 0.0.314 ✅, 0.1.312 → 0.0.314 ❌, 1.0.312 → 0.0.314 ❌. * metadata: Require a minimum update version metadata on each bundle. Configure via CLI using `--min-update-version` or `--auto-min-update-version`. If missing, the channel is marked misconfigured and updates will be rejected until set. * none: Allow all updates according to semver compatibility. Learn more details and examples in Disable updates strategy at /docs/cli/commands/#disable-updates-strategy. Example (CLI): ```bash # Block major updates on the Production channel npx @capgo/cli@latest channel set production com.example.app \ --disable-auto-update major # Allow devices to self-assign to the Beta channel npx @capgo/cli@latest channel set beta com.example.app --self-assign ``` ### Using setChannel() from Your App [Section titled “Using setChannel() from Your App”](#using-setchannel-from-your-app) The `setChannel()` method allows your app to programmatically switch channels at runtime. This is particularly useful for: * QA/debug menus where testers can switch between channels * Beta program opt-in flows * Feature flag implementations * A/B testing scenarios ```typescript import { CapacitorUpdater } from '@capgo/capacitor-updater'; // Switch to the beta channel await CapacitorUpdater.setChannel({ channel: 'beta' }); // Optionally trigger an immediate update check after switching await CapacitorUpdater.setChannel({ channel: 'beta', triggerAutoUpdate: true }); ``` How setChannel() Works (v5.34.0+ / v6.34.0+ / v7.34.0+ / v8.0.0+) When `setChannel()` is called: 1. **Backend validation (read-only)**: A request is sent to the Capgo backend to validate the channel is allowed (checking self-assignment permissions) 2. **Local storage update**: If validation passes, the channel is saved to the device’s local storage as `defaultChannel` 3. **Instant effect**: The next update check uses the new channel immediately (no waiting for replication) **Why this matters:** In older versions, `setChannel()` saved the channel override to the backend database (same as Dashboard or API changes). Devices had to wait for backend replication (up to 2 minutes) before the channel change took effect. Now, `setChannel()` only reads from the backend (for validation) and stores locally, making channel switches instant. **Security note:** Even if a channel’s permissions change after being set locally (e.g., self-assignment is disabled), the backend will still validate the channel during update checks, ensuring security is maintained. **Comparison of channel change methods:** | Method | Effect Time | Persisted Where | Use Case | | -------------------------- | ----------- | ------------------- | -------------------------------------------- | | `setChannel()` from plugin | **Instant** | Device only (local) | User-initiated channel switching in-app | | Dashboard device override | Up to 2 min | Backend database | Admin-initiated changes for specific devices | | API channel assignment | Up to 2 min | Backend database | Automated backend integrations | For the best user experience when building channel-switching UIs, always use the plugin’s `setChannel()` method. Minimum versions for local-only channel switching: **5.34.0**, **6.34.0**, **7.34.0**, or **8.0.0** (depending on your major version). Each minor version number corresponds to the same feature set across all major versions (e.g., X.34.0 includes the same features whether X is 5, 6, 7, or 8). See [plugin installation](/docs/getting-started/add-an-app/) for version tags. ## Assigning a Bundle to a Channel [Section titled “Assigning a Bundle to a Channel”](#assigning-a-bundle-to-a-channel) To deploy a live update, you need to upload a new JS bundle build and assign it to a channel. You can do this in one step with the Capgo CLI: ```shell npx @capgo/cli@latest bundle upload --channel=Development ``` This will upload your built web assets and set the new bundle as the active build for the `Development` channel. Any apps configured to listen to that channel will receive the update the next time they check for one. You can also assign builds to channels from the “Bundles” section of the Capgo dashboard. Click the menu icon next to a build and select “Assign to Channel” to choose the channel for that build. ## Bundle Versioning and Channels [Section titled “Bundle Versioning and Channels”](#bundle-versioning-and-channels) It’s important to note that bundles in Capgo are global to your app, not specific to individual channels. The same bundle can be assigned to multiple channels. When versioning your bundles, we recommend using semantic versioning [semver](https://semver.org/) with pre-release identifiers for channel-specific builds. For example, a beta release might be versioned as `1.2.3-beta.1`. This approach has several benefits: * It clearly communicates the relationship between builds. `1.2.3-beta.1` is obviously a pre-release of `1.2.3`. * It allows for reusing version numbers across channels, reducing confusion. * It enables clear rollback paths. If you need to roll back from `1.2.3`, you know `1.2.2` is the previous stable release. Here’s an example of how you might align your bundle versions with a typical channel setup: * `Development` channel: `1.2.3-dev.1`, `1.2.3-dev.2`, etc. * `QA` channel: `1.2.3-qa.1`, `1.2.3-qa.2`, etc. * `Staging` channel: `1.2.3-rc.1`, `1.2.3-rc.2`, etc. * `Production` channel: `1.2.3`, `1.2.4`, etc. Using semver with pre-release identifiers is a recommended approach, but not strictly required. The key is to find a versioning scheme that clearly communicates the relationships between your builds and aligns with your team’s development process. ## Rolling Back a Live Update [Section titled “Rolling Back a Live Update”](#rolling-back-a-live-update) If you deploy a live update that introduces a bug or otherwise needs to be reverted, you can easily roll back to a previous build. From the “Channels” section of the dashboard: 1. Click the name of the channel you want to roll back 2. Find the build you want to revert to and click the crown icon ![Rollback build](/select_bundle.webp) 3. Confirm the action The selected build will immediately become the active build for that channel again. Apps will receive the rolled back version the next time they check for an update. ## Automating Deployments [Section titled “Automating Deployments”](#automating-deployments) For more advanced workflows, you can automate your live update deployments as part of your CI/CD pipeline. By integrating Capgo into your build process, you can automatically upload new bundles and assign them to channels whenever you push to certain branches or create new releases. Check out the [CI/CD Integration](/docs/getting-started/cicd-integration/) docs to learn more about automating Capgo live updates. ## Deploying to a Device [Section titled “Deploying to a Device”](#deploying-to-a-device) Now that you understand channels, you’re ready to start deploying live updates to real devices. The basic process is: 1. Install the Capgo SDK in your app 2. Configure the app to listen to your desired channel 3. Upload a build and assign it to that channel 4. Launch the app and wait for the update! For a more detailed walkthrough, see the [Deploying Live Updates](/docs/getting-started/deploy/) guide. Happy updating! ## Advanced Channel Usage: User Segmentation [Section titled “Advanced Channel Usage: User Segmentation”](#advanced-channel-usage-user-segmentation) Channels can be used for more than just development stages. They’re a powerful tool for user segmentation, enabling features like: * Feature flags for different user tiers * A/B testing * Gradual feature rollouts * Beta testing programs Learn how to implement these advanced use cases in our guide: [How to Segment Users by Plan and Channels for Feature Flags and A/B Testing](/blog/how-to-segment-users-by-plan-and-channels/). # Using Capgo in China > Learn how to configure Capgo Live Updates to work in China by using regional OST URLs for optimal performance and reliability. If you’re deploying your app to users in China, you’ll need to configure Capgo to use regional OST (Object Storage Technology) URLs to ensure reliable and fast updates. ## Why Use China-Specific URLs? [Section titled “Why Use China-Specific URLs?”](#why-use-china-specific-urls) Due to network infrastructure and regulations in China (the Great Firewall), direct connections to international servers can be slow or unreliable. Capgo provides dedicated OST URLs with data located in Hong Kong to minimize latency and ensure your users receive updates as quickly and reliably as possible. ## Configuration [Section titled “Configuration”](#configuration) To configure Capgo for China, you need to set three specific URLs in your Capacitor configuration file. These URLs point to Capgo’s Hong Kong-based infrastructure. 1. Open your `capacitor.config.ts` file 2. Add the following configuration to the `CapacitorUpdater` plugin section: ```typescript import { CapacitorConfig } from '@capacitor/cli'; const config: CapacitorConfig = { plugins: { CapacitorUpdater: { autoUpdate: true, updateUrl: 'https://updater.capgo.com.cn/updates', statsUrl: 'https://updater.capgo.com.cn/stats', channelUrl: 'https://updater.capgo.com.cn/channel_self', }, }, }; export default config; ``` 3. Rebuild your app to apply the changes: ```shell npm run build npx cap sync ``` ## Configuration Details [Section titled “Configuration Details”](#configuration-details) Here’s what each URL does: * **updateUrl**: `https://updater.capgo.com.cn/updates` - Used to check for and download available updates for your app * **statsUrl**: `https://updater.capgo.com.cn/stats` - Used to report analytics and usage statistics back to Capgo * **channelUrl**: `https://updater.capgo.com.cn/channel_self` - Used to retrieve channel configuration and determine which updates to apply Tip All three URLs must be configured together to ensure full functionality of the Capgo updater in China. ## Recommended Settings for China [Section titled “Recommended Settings for China”](#recommended-settings-for-china) Due to network performance limitations caused by the Great Firewall of China, we have specific recommendations for apps deployed in mainland China: ### Disable Direct Updates [Section titled “Disable Direct Updates”](#disable-direct-updates) We **strongly recommend disabling `directUpdate`** for apps in China. Network connectivity in China is less performant than in other regions, and direct updates (which apply immediately) can lead to a poor user experience if downloads are interrupted or slow. Instead, use the default update behavior where updates download in the background and apply when the app backgrounds or restarts. This provides a more reliable experience for your users. ```typescript const config: CapacitorConfig = { plugins: { CapacitorUpdater: { autoUpdate: true, directUpdate: false, // Recommended for China updateUrl: 'https://updater.capgo.com.cn/updates', statsUrl: 'https://updater.capgo.com.cn/stats', channelUrl: 'https://updater.capgo.com.cn/channel_self', }, }, }; ``` Caution While our Hong Kong-based infrastructure helps minimize latency and improve reliability, network performance to mainland China can still be affected by the Great Firewall. Disabling `directUpdate` helps ensure updates complete successfully without disrupting the user experience. ## Complete Configuration Example [Section titled “Complete Configuration Example”](#complete-configuration-example) Here’s a complete example with recommended settings for apps deployed in China: ```typescript import { CapacitorConfig } from '@capacitor/cli'; const config: CapacitorConfig = { appId: 'com.example.app', appName: 'My App', webDir: 'dist', plugins: { CapacitorUpdater: { autoUpdate: true, directUpdate: false, // Recommended: disable for better reliability in China updateUrl: 'https://updater.capgo.com.cn/updates', statsUrl: 'https://updater.capgo.com.cn/stats', channelUrl: 'https://updater.capgo.com.cn/channel_self', }, }, }; export default config; ``` ## Testing Your Configuration [Section titled “Testing Your Configuration”](#testing-your-configuration) After configuring the China-specific URLs, you can verify that updates are working correctly: 1. Upload a new bundle to Capgo: ```shell npx @capgo/cli@latest bundle upload --channel=production ``` 2. Install your app on a test device in China 3. Monitor the update process: ```shell npx @capgo/cli@latest app debug ``` 4. Check that updates are being downloaded from the China OST URLs Note The update behavior and timing remain the same as with standard Capgo configuration. See the [Update Behavior](/docs/live-updates/update-behavior/) documentation for details on how and when updates are applied. ## Multi-Region Deployment [Section titled “Multi-Region Deployment”](#multi-region-deployment) If your app serves users both inside and outside China, you can use the Chinese domain configuration for all users worldwide. The `updater.capgo.com.cn` domain is resolved globally thanks to Alibaba DNS infrastructure, making it accessible both inside China and everywhere else in the world. ### Using Chinese Domains Globally [Section titled “Using Chinese Domains Globally”](#using-chinese-domains-globally) The Chinese domain URLs work seamlessly for multi-region apps: ```typescript const config: CapacitorConfig = { plugins: { CapacitorUpdater: { autoUpdate: true, directUpdate: false, // Recommended for China users updateUrl: 'https://updater.capgo.com.cn/updates', statsUrl: 'https://updater.capgo.com.cn/stats', channelUrl: 'https://updater.capgo.com.cn/channel_self', }, }, }; ``` This single configuration will work for: * Users in mainland China (using Hong Kong-based infrastructure) * Users outside China (accessing the same infrastructure via Alibaba DNS) **Performance Considerations:** While the `.cn` domain is resolved globally through Alibaba DNS and works everywhere, it’s slightly less performant for users outside China compared to the standard domain (`api.capgo.app`), which is resolved directly by Cloudflare where our backend is hosted. However, DNS resolution is fast, so the performance difference is minimal and won’t significantly impact the user experience. Tip Using the `.cn` domain for all users simplifies your deployment and ensures consistent update behavior across all regions. You don’t need separate builds or environment-based configurations. The small performance trade-off outside China is typically worth the simplified deployment. ### Alternative: Region-Specific Configurations [Section titled “Alternative: Region-Specific Configurations”](#alternative-region-specific-configurations) If you prefer to optimize differently for each region, you can also consider: * Building separate app variants with different configurations * Using environment-based configuration to dynamically set the URLs * Creating different release channels for different regions If you need assistance with multi-region deployment strategies, please contact us at or join our [Discord community](https://discord.capgo.app) for help. ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) If you experience issues with updates in China: 1. **Verify your configuration** - Double-check that all three URLs are correctly set in your `capacitor.config.ts` 2. **Check network connectivity** - Ensure your device can reach the `updater.capgo.com.cn` domain 3. **Review logs** - Use `npx @capgo/cli@latest app debug` to check for error messages 4. **Test updates** - Try uploading a new bundle and monitoring the download process 5. **Contact support** - If issues persist, reach out to us at or join our [Discord community](https://discord.capgo.app) for assistance Caution Make sure to use the `.cn` domain (`updater.capgo.com.cn`) and not the standard international domain when configuring for China. ## Next Steps [Section titled “Next Steps”](#next-steps) * Learn about [Update Behavior](/docs/live-updates/update-behavior/) to customize when updates are applied * Explore [Channels](/docs/live-updates/channels/) to manage different release tracks * Review [Encryption](/docs/live-updates/encryption/) to secure your updates # Compliance > Learn about Capgo's privacy practices, data collection, security compliance, and how we protect your users' information during live updates. Capgo is designed with privacy, security, and compliance in mind. This document explains what data is collected, how it’s used, and what measures are in place to protect your users’ privacy and ensure regulatory compliance when using Capgo’s live update service. ## Data Collection Overview [Section titled “Data Collection Overview”](#data-collection-overview) Capgo collects minimal data necessary to provide the live update service effectively. The data collection is focused on operational requirements rather than user tracking or analytics. ### What Data is Collected [Section titled “What Data is Collected”](#what-data-is-collected) Capgo collects only the data that is necessary to provide the live updates feature. When your app checks for updates or downloads new bundles, the following information is collected: * **App ID**: A unique identifier for your app that is used to associate the app with the correct account * **App Version Code**: The version code of the app that is used to determine which updates are compatible with the app * **App Version Name**: The version name of the app that is used for display purposes * **Platform**: The platform (iOS, Android, Electron) of the app that is used to determine which updates are compatible with the app * **Device ID**: A unique identifier for the device that is used to deliver updates to a specific device and for billing purposes. This identifier is a random string that is created when the app is started for the first time. **Starting from plugin version v5.10.0, v6.25.0 and v7.25.0**, the device ID now persists across app reinstalls (stored securely in Keychain on iOS, EncryptedSharedPreferences on Android, and Electron secure storage) to provide better device tracking while maintaining compliance with app store guidelines. Prior to these versions, the device ID was reset with every app installation * **Bundle ID**: The unique identifier for the bundle that is currently installed on the device * **Channel Name**: The name of the channel that is selected to receive updates * **OS Version**: The version of the operating system that is used to determine which updates are compatible with the device * **Plugin Version**: The version of the @capgo/capacitor-updater plugin that is used to deliver updates to the device **Additional Technical Data:** * Update check timestamps * Download success/failure status * Bundle installation status * Rollback events and reasons * IP address (for geolocation and CDN optimization) Note You can verify the data that is collected by inspecting the source code of the @capgo/capacitor-updater plugin, which is open-source and available on [GitHub](https://github.com/Cap-go/capacitor-updater). Capgo does not collect personally identifiable information (PII) such as names, email addresses, phone numbers, or persistent device identifiers that can be used to track individual users across apps. ### What Data is NOT Collected [Section titled “What Data is NOT Collected”](#what-data-is-not-collected) Capgo explicitly does not collect: * Personal user information or credentials * App usage analytics or user behavior data * Content from your app or user-generated data * Location data beyond general geographic region * Persistent device identifiers for tracking * Biometric or sensitive personal data ## Data Usage and Purpose [Section titled “Data Usage and Purpose”](#data-usage-and-purpose) The data collected by Capgo is used exclusively for: ### Service Operation [Section titled “Service Operation”](#service-operation) * Determining which updates are available for specific app versions * Optimizing content delivery through geographic CDN selection * Ensuring compatibility between updates and device capabilities * Managing update rollouts and channel assignments ### Service Improvement [Section titled “Service Improvement”](#service-improvement) * Monitoring update success rates and identifying issues * Optimizing download performance and reliability * Improving the overall update delivery system * Debugging and troubleshooting update failures ### Security and Integrity [Section titled “Security and Integrity”](#security-and-integrity) * Preventing abuse and ensuring service availability * Validating update authenticity and integrity * Protecting against malicious or corrupted updates * Maintaining service security and stability ## Data Storage and Retention [Section titled “Data Storage and Retention”](#data-storage-and-retention) ### Storage Location [Section titled “Storage Location”](#storage-location) * Update bundles and metadata are stored on secure cloud infrastructure * Data is distributed across multiple geographic regions for performance * All data transmission is encrypted using industry-standard protocols (HTTPS/TLS) ### Data Retention [Section titled “Data Retention”](#data-retention) * Update check logs are retained for operational purposes (typically 30-90 days) * Bundle files are retained as long as they’re assigned to active channels * Aggregated, non-personal metrics may be retained longer for service improvement * Personal data, if any, is deleted according to applicable data protection laws ### Data Security [Section titled “Data Security”](#data-security) * All data is encrypted in transit and at rest * Access to data is restricted to authorized personnel only * Regular security audits and monitoring are performed * Industry-standard security practices are followed * **SOC 2 Certification**: Capgo is currently SOC 2 Type II certified, ensuring the highest standards of security, availability, and confidentiality. View our compliance status at [trust.capgo.app](https://trust.capgo.app) * **Continuous Code Auditing**: Every commit is automatically audited by [SonarCloud](https://sonarcloud.io/summary/overall?id=Cap-go_capacitor-updater\&branch=main) for the [plugin](https://sonarcloud.io/summary/overall?id=Cap-go_capgo\&branch=main) and [backend](https://sonarcloud.io/summary/overall?id=Cap-go_capgo\&branch=main), ensuring code quality, security vulnerabilities detection, and maintainability * **Vulnerability Scanning**: Additional security scanning is performed by [Snyk](https://snyk.io/test/github/Cap-go/capgo) to detect and remediate security vulnerabilities in dependencies * **Infrastructure Security**: Our hosting infrastructure is continuously monitored and verified through [hosting security checks](https://hosting-checker.net/websites/api.capgo.app) * **AI-Powered Code Review**: Every pull request is reviewed by CodeRabbit AI to catch potential issues, security concerns, and maintain code quality standards ## Privacy Controls [Section titled “Privacy Controls”](#privacy-controls) ### For App Developers [Section titled “For App Developers”](#for-app-developers) As a Capgo user, you have control over: * **Channel Management**: Control which updates are distributed to which users * **Data Minimization**: Configure what device information is shared * **Geographic Controls**: Manage where your updates are distributed * **Retention Settings**: Control how long update data is retained ### For End Users [Section titled “For End Users”](#for-end-users) Your app users benefit from: * **Minimal Data Collection**: Only essential data for update delivery is collected * **No Tracking**: No cross-app or persistent user tracking * **Transparency**: This privacy policy explains exactly what data is collected * **Security**: All data transmission is encrypted and secure ## Compliance and Legal [Section titled “Compliance and Legal”](#compliance-and-legal) ### Data Protection Regulations [Section titled “Data Protection Regulations”](#data-protection-regulations) Capgo is designed to comply with major data protection regulations including: * **GDPR** (General Data Protection Regulation) * **CCPA** (California Consumer Privacy Act) * **COPPA** (Children’s Online Privacy Protection Act) * Other applicable regional privacy laws ### App Store Compliance [Section titled “App Store Compliance”](#app-store-compliance) Capgo strictly adheres to app store guidelines and policies: * **Apple App Store**: Complies with [App Store Review Guidelines](https://developer.apple.com/app-store/review/guidelines/) section 3.3.2, ensuring that live updates only modify the app’s behavior in ways that are consistent with the submitted app * **Google Play Store**: Follows [Google Play Developer Policy](https://play.google.com/about/developer-content-policy/) requirements for dynamic code loading and app updates * **Content Restrictions**: Live updates cannot introduce functionality that wasn’t present in the original app submission or violate platform-specific content policies * **Security Requirements**: All updates maintain the same security posture and permissions as the original app ### Your Responsibilities [Section titled “Your Responsibilities”](#your-responsibilities) As an app developer using Capgo, you should: * Include appropriate privacy disclosures in your app’s privacy policy * Inform users about the use of live update services * Ensure compliance with applicable laws in your jurisdiction * Implement appropriate consent mechanisms if required Tip **No Additional Privacy Implementation Required**: Capgo is designed to be privacy-compliant by default. You don’t need to implement any additional privacy controls or data handling mechanisms in your app code. The minimal data collection and privacy-by-design approach means that integrating Capgo typically doesn’t require changes to your existing privacy declarations or policies. ## Privacy by Design [Section titled “Privacy by Design”](#privacy-by-design) Capgo follows privacy-by-design principles: ### Data Minimization [Section titled “Data Minimization”](#data-minimization) * Only collect data that is absolutely necessary for service operation * Avoid collecting personal or sensitive information * Use aggregated and anonymized data where possible ### Purpose Limitation [Section titled “Purpose Limitation”](#purpose-limitation) * Use collected data only for the stated purposes * Do not repurpose data for unrelated activities * Maintain clear boundaries on data usage ### Transparency [Section titled “Transparency”](#transparency) * Provide clear information about data collection and usage * Make privacy practices easily accessible and understandable * Regularly update privacy documentation ## Contact and Questions [Section titled “Contact and Questions”](#contact-and-questions) If you have questions about Capgo’s privacy practices or need to report a privacy concern: * Review our full Privacy Policy at [capgo.app/privacy](https://capgo.app/privacy) * View our security and compliance status at [capgo.app/trust](https://capgo.app/trust) * Contact our privacy team through the support channels * Report any privacy-related issues through our security contact Tip Remember to update your own app’s privacy policy to reflect the use of Capgo’s live update service and any data collection that may occur as part of the update process. ## Best Practices for Privacy [Section titled “Best Practices for Privacy”](#best-practices-for-privacy) When implementing Capgo in your app: 1. **Be Transparent**: Inform users about the live update functionality 2. **Minimize Data**: Only enable data collection features you actually need 3. **Secure Implementation**: Follow security best practices in your integration 4. **Regular Reviews**: Periodically review your privacy practices and update policies 5. **User Control**: Consider providing users with options to control update behavior By following these practices and understanding Capgo’s privacy approach, you can provide your users with a secure, privacy-respecting live update experience. # Custom Storage > Learn how to use custom storage solutions with Capgo Live Updates, including external URLs, S3 integration, and bundle encryption for secure deployments. Capgo supports custom storage solutions for your app bundles, allowing you to host your updates on your own infrastructure or third-party storage services. This is particularly useful for organizations with specific security requirements, compliance needs, or existing storage infrastructure. ## Overview [Section titled “Overview”](#overview) Custom storage in Capgo works by uploading your bundle to an external location and providing Capgo with the URL to access it. The Capgo SDK will then download updates directly from your custom storage location instead of Capgo’s default cloud storage. Tip Custom storage is ideal for: * Organizations with strict data residency requirements * Teams with existing CDN or storage infrastructure * Applications requiring additional security layers * Cost optimization for large bundle sizes ## External URL Upload [Section titled “External URL Upload”](#external-url-upload) The simplest way to use custom storage is by uploading your bundle to any publicly accessible URL and providing that URL to Capgo. ### Basic External URL Upload [Section titled “Basic External URL Upload”](#basic-external-url-upload) ```shell npx @capgo/cli@latest bundle upload --external https://your-domain.com/bundles/v1.2.3.zip ``` This command tells Capgo to reference the bundle at the specified URL instead of uploading it to Capgo’s cloud storage. ### With Encryption [Section titled “With Encryption”](#with-encryption) For secure external storage, you can encrypt your bundle and provide the decryption keys: ```shell npx @capgo/cli@latest bundle upload --external https://your-domain.com/bundles/v1.2.3.zip --iv-session-key YOUR_IV_SESSION_KEY ``` ## S3 Integration [Section titled “S3 Integration”](#s3-integration) Capgo provides built-in support for Amazon S3 and S3-compatible storage services. The CLI can automatically upload your bundle to S3 and configure Capgo to use the S3 URL. ### S3 Upload Options [Section titled “S3 Upload Options”](#s3-upload-options) ```shell npx @capgo/cli@latest bundle upload \ --s3-region us-east-1 \ --s3-apikey YOUR_ACCESS_KEY \ --s3-apisecret YOUR_SECRET_KEY \ --s3-bucket-name your-bucket-name ``` ### Complete S3 Configuration [Section titled “Complete S3 Configuration”](#complete-s3-configuration) For S3-compatible services or custom endpoints: ```shell npx @capgo/cli@latest bundle upload \ --s3-region us-east-1 \ --s3-apikey YOUR_ACCESS_KEY \ --s3-apisecret YOUR_SECRET_KEY \ --s3-endpoint https://s3.your-provider.com \ --s3-bucket-name your-bucket-name \ --s3-port 443 \ --no-s3-ssl # Only if your endpoint doesn't support SSL ``` ### S3 Configuration Parameters [Section titled “S3 Configuration Parameters”](#s3-configuration-parameters) | Parameter | Description | Required | | ------------------ | ----------------------------- | -------- | | `--s3-region` | AWS region for your S3 bucket | Yes | | `--s3-apikey` | S3 access key ID | Yes | | `--s3-apisecret` | S3 secret access key | Yes | | `--s3-bucket-name` | Name of your S3 bucket | Yes | | `--s3-endpoint` | Custom S3 endpoint URL | No | | `--s3-port` | Port for S3 endpoint | No | | `--no-s3-ssl` | Disable SSL for S3 upload | No | ## Bundle Preparation and Encryption [Section titled “Bundle Preparation and Encryption”](#bundle-preparation-and-encryption) When using custom storage, especially with encryption, you need to prepare your bundles properly. This involves creating a zip file and optionally encrypting it. ### Step 1: Create a Zip Bundle [Section titled “Step 1: Create a Zip Bundle”](#step-1-create-a-zip-bundle) First, create a zip file of your app bundle: ```shell npx @capgo/cli@latest bundle zip com.example.app --path ./dist ``` The zip command will return the checksum of the zip file. You can use this checksum to encrypt the zip file if needed. Use the `--json` option to get structured output including the checksum. #### Zip Command Options [Section titled “Zip Command Options”](#zip-command-options) ```shell npx @capgo/cli@latest bundle zip [appId] \ --path ./dist \ --bundle 1.2.3 \ --name myapp-v1.2.3 \ --json \ --no-code-check \ --key-v2 \ --package-json ../../package.json,./package.json ``` | Option | Description | | ----------------- | -------------------------------------------------------------------- | | `--path` | Path to the folder to zip (defaults to webDir from capacitor.config) | | `--bundle` | Bundle version number to name the zip file | | `--name` | Custom name for the zip file | | `--json` | Output results in JSON format (includes checksum) | | `--no-code-check` | Skip checking for notifyAppReady() call and index file | | `--key-v2` | Use encryption v2 | | `--package-json` | Paths to package.json files for monorepos (comma separated) | ### Step 2: Encrypt the Bundle (Optional) [Section titled “Step 2: Encrypt the Bundle (Optional)”](#step-2-encrypt-the-bundle-optional) For enhanced security, encrypt your zip bundle before uploading: ```shell # Using default local key npx @capgo/cli@latest bundle encrypt ./myapp.zip CHECKSUM # Using custom key file npx @capgo/cli@latest bundle encrypt ./myapp.zip CHECKSUM --key ./path/to/.capgo_key_v2 # Using key data directly npx @capgo/cli@latest bundle encrypt ./myapp.zip CHECKSUM --key-data "PRIVATE_KEY_CONTENT" ``` The `CHECKSUM` parameter is required and should be the checksum of your zip file. You can get the checksum from the zip command output (use `--json` option for structured output). By default, the encrypt command will use your local private signing key. You can specify a custom key using the `--key` or `--key-data` options. The encrypt command will return the `ivSessionKey` needed for upload or decryption. #### Encryption Command Options [Section titled “Encryption Command Options”](#encryption-command-options) | Option | Description | | ------------ | ------------------------------------------------------------------------- | | `zipPath` | Path to the zip file to encrypt (required) | | `checksum` | Checksum of the zip file (required) - get it from zip command | | `--key` | Custom path for private signing key (optional, uses local key by default) | | `--key-data` | Private signing key data directly (optional) | | `--json` | Output results in JSON format | Caution The encrypt command will output an `ivSessionKey` that you’ll need to provide when uploading with the `--iv-session-key` option. ## Complete Workflow Examples [Section titled “Complete Workflow Examples”](#complete-workflow-examples) ### Example 1: External URL with Encryption [Section titled “Example 1: External URL with Encryption”](#example-1-external-url-with-encryption) 1. **Build your app:** ```shell npm run build ``` 2. **Create a zip bundle:** ```shell npx @capgo/cli@latest bundle zip com.example.app --path ./dist --bundle 1.2.3 ``` Note the checksum returned by this command. 3. **Encrypt the bundle:** ```shell npx @capgo/cli@latest bundle encrypt ./com.example.app-1.2.3.zip CHECKSUM_FROM_STEP_2 ``` Note the `ivSessionKey` from the output. 4. **Upload to your storage:** Upload the encrypted zip file to your hosting service. 5. **Register with Capgo:** ```shell npx @capgo/cli@latest bundle upload \ --external https://your-cdn.com/bundles/com.example.app-1.2.3.zip \ --iv-session-key IV_SESSION_KEY_FROM_STEP_3 ``` ### Example 2: Direct S3 Upload [Section titled “Example 2: Direct S3 Upload”](#example-2-direct-s3-upload) 1. **Build your app:** ```shell npm run build ``` 2. **Upload directly to S3:** ```shell npx @capgo/cli@latest bundle upload \ --s3-region us-west-2 \ --s3-apikey YOUR_ACCESS_KEY \ --s3-apisecret YOUR_SECRET_KEY \ --s3-bucket-name your-app-bundles \ --channel Production ``` ### Example 3: S3 with Encryption [Section titled “Example 3: S3 with Encryption”](#example-3-s3-with-encryption) 1. **Build and zip:** ```shell npm run build npx @capgo/cli@latest bundle zip com.example.app --path ./dist --key-v2 ``` 2. **Encrypt the bundle:** ```shell npx @capgo/cli@latest bundle encrypt ./com.example.app.zip CHECKSUM ``` 3. **Upload to S3 with encryption:** ```shell npx @capgo/cli@latest bundle upload \ --s3-region us-west-2 \ --s3-apikey YOUR_ACCESS_KEY \ --s3-apisecret YOUR_SECRET_KEY \ --s3-bucket-name your-app-bundles \ --iv-session-key IV_SESSION_KEY_FROM_STEP_2 \ --channel Production ``` ## Security Considerations [Section titled “Security Considerations”](#security-considerations) When using custom storage, consider these security best practices: ### Access Control [Section titled “Access Control”](#access-control) * Ensure your storage URLs are accessible to your app users but not publicly discoverable * Use signed URLs or token-based authentication when possible * Implement proper CORS headers for web-based apps ### Encryption [Section titled “Encryption”](#encryption) * Always encrypt sensitive bundles using the Capgo encryption tools * Store encryption keys securely and rotate them regularly * Use HTTPS for all bundle URLs (required for mobile and Electron apps) ### Monitoring [Section titled “Monitoring”](#monitoring) * Monitor access logs to detect unusual download patterns * Set up alerts for failed bundle downloads * Regularly audit your storage permissions ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) ### Common Issues [Section titled “Common Issues”](#common-issues) **Bundle not downloading:** * Verify the URL is publicly accessible and uses HTTPS (required for mobile and Electron apps) * Check CORS headers for web apps * Ensure the bundle format is correct **Encryption errors:** * Verify the `ivSessionKey` matches the encrypted bundle * Check that the bundle was encrypted with the correct key * Ensure encryption v2 is used for new bundles **S3 upload failures:** * Verify your S3 credentials and permissions * Check bucket policies and CORS configuration * Ensure the specified region is correct ### Debug Commands [Section titled “Debug Commands”](#debug-commands) Check bundle status: ```shell npx @capgo/cli@latest app debug ``` Verify bundle integrity: ```shell npx @capgo/cli@latest bundle list ``` ## Next Steps [Section titled “Next Steps”](#next-steps) * Learn about [Channels](/docs/live-updates/channels/) to manage different deployment environments * Explore [Update Behavior](/docs/live-updates/update-behavior/) to customize how updates are applied * Set up [CI/CD Integration](/docs/getting-started/cicd-integration/) to automate your custom storage workflow # Delta updates > Learn how Capgo's Delta (manifest) updates optimize data transfer by only sending changed files, enhancing performance on slower networks. Capgo’s Live Update system can deliver updates faster and more efficiently by only sending the changed files, rather than the entire JS bundle. This is especially beneficial for users on slower or metered network connections, as it minimizes the amount of data that needs to be downloaded. A second benefit is when the app have large assets who change rarely, like images or videos, compare to zipped JS files it will be downloaded only once. ## How Delta (Manifest) Updates Work [Section titled “How Delta (Manifest) Updates Work”](#how-delta-manifest-updates-work) Delta (manifest) updates in Capgo are handled by the Capgo plugin installed in your app. When you upload a new version of your app using the `--delta` flag, Capgo does the following: 1. Each file in your build is uploaded individually 2. Checksums are generated for each file 3. A new json manifest is created, listing all files and their checksums 4. This manifest is uploaded to the Capgo database When a device running your app checks for an update, the Capgo plugin receives the new manifest from the server. It compares this manifest to the one it currently has, identifying which files have changed based on the checksums and file paths. The plugin then downloads only the changed files, rather than the entire JS bundle. It reconstructs the new version of the app by combining these downloaded files with the unchanged files it already has. Manifest In case of Delta (manifest) updates, the device stores all downloaded files in a common cache. Capgo never cleans it, but the OS can at any time. ## Enabling Delta (Manifest) Updates [Section titled “Enabling Delta (Manifest) Updates”](#enabling-delta-manifest-updates) To enable Delta (manifest) updates for your Capgo app, simply use the `--delta` flag when uploading a new version: ```shell npx @capgo/cli@latest bundle upload --delta ``` If `directUpdate` is enabled in your `capacitor.config`, the CLI detects it. In non-interactive environments it sends Delta (manifest) updates automatically, and in interactive environments it prompts you to confirm before uploading. Use `--no-delta` to force a full bundle upload. ## Enforcing Delta (Manifest) Updates [Section titled “Enforcing Delta (Manifest) Updates”](#enforcing-delta-manifest-updates) If you want to ensure that all uploads are Delta (manifest) updates and prevent any accidental full bundle uploads, you can use the `--delta-only` flag: ```shell npx @capgo/cli@latest bundle upload --delta-only ``` When `--delta-only` is used, Capgo will only upload individual files and generate a manifest. Any device that does not support Delta (manifest) updates will not be able to download the update. You might want to use `--delta-only` if: * You always want to use Delta (manifest) updates and never want to allow full bundle uploads * You’re setting up a CI/CD pipeline and want to ensure all automated uploads are Delta (manifest) * Your app is large and bandwidth is constrained, so you need to minimize upload/download sizes If you need to do a full bundle upload while `--delta-only` is set, simply run the upload command without `--delta-only`. This will override the setting for that single upload, allowing you to push a complete bundle when needed. ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) If Delta (manifest) updates don’t seem to be working (i.e. devices are always downloading the full JS bundle even for small changes), double check that: * You’re using the `--delta` flag every time you upload a new version * If using `--delta-only`, make sure you haven’t accidentally omitted the `--delta` flag * Your device is running the latest version of the Capgo plugin * Your device has a stable network connection and can reach the Capgo servers You can also use the Capgo webapp to check the details of your last upload: 1. Go to the [webapp](https://app.capgo.io) 2. Click on your app 3. Click on the bundles number of the stats bar. 4. Select the last bundle 5. Check the `Partial` field ![bundle type](/bundle_type.webp) If you continue to have trouble, please reach out to Capgo support for further assistance. They can check the server logs to confirm that your Delta (manifest) uploads are being processed correctly and that devices are receiving the updated manifests. That’s it! The `--delta` flag tells Capgo to perform the individual file uploads and manifest generation needed for Delta (manifest) updates. Note that you need to use `--delta` every time you upload a new version that you want to be delivered as a Delta (manifest) update. If you omit the flag, Capgo will upload the entire JS bundle as a single file, and devices will download the whole bundle even if only a small part has changed. # Encryption > Learn how Capgo's end-to-end encryption secures your app bundles during transmission and storage, protecting your code and user data. Capgo provides robust end-to-end encryption for your app bundles, ensuring that your JavaScript code and assets are protected during transmission and storage. This encryption system is designed to give you complete control over your app’s security while maintaining the convenience of live updates. ## Overview [Section titled “Overview”](#overview) Capgo’s encryption system uses industry-standard cryptographic methods to protect your bundles from unauthorized access. When encryption is enabled, your bundles are encrypted before leaving your development environment and remain encrypted until they’re decrypted by your app on the user’s device. **True End-to-End Encryption**: Unlike other OTA update platforms that only sign updates (leaving the code publicly readable), Capgo provides true end-to-end encryption. This means only your users can decrypt your updates - no one else, including Capgo itself. Your bundle content remains completely private and unreadable throughout the entire delivery process. Tip Encryption is particularly important for: * Apps handling sensitive data or business logic * Enterprise applications with compliance requirements * Apps deployed in regulated industries * Organizations with strict security policies ## How Encryption Works [Section titled “How Encryption Works”](#how-encryption-works) Capgo uses a hybrid encryption approach that combines RSA and AES encryption for optimal security and performance: ![Capgo Encryption Flow](/encryption_flow.webp) ### 1. Key Generation [Section titled “1. Key Generation”](#1-key-generation) * **Private Key**: Generated and stored securely in your development environment (used for encryption) * **Public Key**: Derived from your private key and stored in your app’s Capacitor config (used for decryption) * **Session Keys**: Random AES keys generated for each bundle upload ### 2. Encryption Process [Section titled “2. Encryption Process”](#2-encryption-process) 1. A random AES session key is generated for each bundle upload 2. Your bundle is encrypted using the AES session key 3. The bundle checksum is calculated 4. Both the AES session key and checksum are encrypted together using your RSA private key (creating the “signature”) 5. The encrypted bundle and encrypted signature are stored The checksum is encrypted alongside the AES key to prevent tampering. Since only your RSA private key can create this signature, and only the corresponding public key can decrypt it, this ensures that both the AES session key and the expected checksum are authentic and haven’t been modified by an attacker. ### 3. Decryption Process [Section titled “3. Decryption Process”](#3-decryption-process) 1. Your app downloads the encrypted bundle and encrypted signature 2. The Capgo SDK uses your RSA public key (stored in the app) to decrypt the signature 3. This reveals the AES session key and the original checksum 4. The AES session key is used to decrypt the bundle 5. A checksum of the decrypted bundle is calculated and compared with the original checksum for integrity verification This process ensures that even if an attacker intercepts the encrypted bundle, they cannot modify the AES session key or provide a fake checksum, because they would need your private key to create a valid signature that the public key can decrypt. Tip RSA cannot encrypt large amounts of data efficiently, so AES is used for the actual bundle encryption while RSA secures the AES key and provides integrity verification through checksum signing. ## Capgo vs Other Platforms [Section titled “Capgo vs Other Platforms”](#capgo-vs-other-platforms) | Feature | Capgo | Other OTA Platforms | | ------------------- | ------------------------------------------------ | ----------------------------- | | **Bundle Content** | Fully encrypted (unreadable) | Publicly readable | | **Security Method** | True end-to-end encryption | Code signing only | | **Privacy Level** | Zero-knowledge (even Capgo can’t read your code) | Platform can access your code | | **Protection** | Content + integrity + authenticity | Integrity + authenticity only | **Why This Matters:** * **Code signing** only verifies that updates haven’t been tampered with and come from the right source * **End-to-end encryption** ensures that your actual code content remains private and unreadable during transmission and storage * With Capgo’s true end-to-end encryption, only your users can decrypt updates - no one else, including Capgo itself ## Encryption Methods [Section titled “Encryption Methods”](#encryption-methods) Capgo uses Encryption V2 as the standard encryption method: ### Encryption V2 (Current Standard) [Section titled “Encryption V2 (Current Standard)”](#encryption-v2-current-standard) * Uses RSA-4096 for enhanced security * AES-256-GCM for authenticated encryption * Provides integrity verification * Better performance and security ### Encryption V1 (Deprecated) [Section titled “Encryption V1 (Deprecated)”](#encryption-v1-deprecated) * Uses RSA-2048 for key encryption * AES-256-CBC for bundle encryption * **No longer available in the current CLI** * Legacy apps using V1 must migrate to V2 Danger Encryption V1 is no longer supported in the current Capgo CLI. If you’re using V1 encryption, you must migrate to V2. See the [migration guide](/docs/upgrade/encryption-v1-to-v2/) for detailed instructions. ## Setting Up Encryption [Section titled “Setting Up Encryption”](#setting-up-encryption) ### Step 1: Generate Encryption Keys [Section titled “Step 1: Generate Encryption Keys”](#step-1-generate-encryption-keys) First, generate your encryption keys using the Capgo CLI: ```shell # Generate new encryption keys (creates files in current directory) npx @capgo/cli@latest key create ``` This creates: * `.capgo_key_v2`: Your private key (keep this secure!) * `.capgo_key_v2.pub`: Your public key (used by your app) These files are created in the current directory where you run the command. Caution **Important Storage Notes:** * **Private Key (`.capgo_key_v2`)**: Never commit this to version control. This file should be kept secure and used only for encryption during bundle uploads. * **Public Key (`.capgo_key_v2.pub`)**: This is safe to commit to version control as it’s a backup of your public key. * **File Location**: Keys are created in the current directory where you run the `key create` command. * **Public Key in Config**: You must run `key save` to store the public key in your Capacitor config for the mobile app to use. For production use, store the private key securely (environment variables, key management services) and remove it from your local project after setup. ### Step 2: Save Your Public Key to Capacitor Config (Required) [Section titled “Step 2: Save Your Public Key to Capacitor Config (Required)”](#step-2-save-your-public-key-to-capacitor-config-required) You **must** save your public key to the Capacitor config so your mobile app can decrypt bundles: ```shell # Save public key from file to Capacitor config (required) npx @capgo/cli@latest key save --key ./.capgo_key_v2.pub # Or save public key data directly npx @capgo/cli@latest key save --key-data "$CAPGO_PUBLIC_KEY" ``` ### Step 3: Sync Capacitor Platform (Required) [Section titled “Step 3: Sync Capacitor Platform (Required)”](#step-3-sync-capacitor-platform-required) After saving the public key, you **must** sync the Capacitor platform to copy the updated config to the native layer: ```shell # Sync the platform to copy config to native npx cap sync ``` Caution **Required Steps**: 1. The `key save` command stores the public key in your Capacitor config 2. `npx cap sync` copies this config to the native layer where the mobile app can access it 3. Without both steps, your app won’t be able to decrypt encrypted updates ## Encrypting Bundles [Section titled “Encrypting Bundles”](#encrypting-bundles) ### Method 1: Encrypt During Upload [Section titled “Method 1: Encrypt During Upload”](#method-1-encrypt-during-upload) The simplest way is to encrypt during the upload process: ```shell # Upload with automatic encryption npx @capgo/cli@latest bundle upload --key-v2 # For external storage, you must encrypt first (see Manual Encryption Workflow below) ``` ### Method 2: Manual Encryption Workflow [Section titled “Method 2: Manual Encryption Workflow”](#method-2-manual-encryption-workflow) For more control, you can manually encrypt bundles: 1. **Create a zip bundle:** ```shell npx @capgo/cli@latest bundle zip com.example.app --path ./dist --key-v2 ``` 2. **Encrypt the bundle:** ```shell npx @capgo/cli@latest bundle encrypt ./com.example.app.zip CHECKSUM_FROM_STEP_1 ``` 3. **Upload to your storage (e.g., S3) and register with Capgo:** ```shell # First upload the encrypted bundle to your storage (e.g., AWS S3) aws s3 cp ./encrypted-bundle.zip s3://your-bucket/encrypted-bundle.zip # Then register with Capgo using the external URL npx @capgo/cli@latest bundle upload --external https://your-storage.com/encrypted-bundle.zip --iv-session-key IV_SESSION_KEY_FROM_STEP_2 ``` ## Key Management [Section titled “Key Management”](#key-management) ### Storing Keys Securely [Section titled “Storing Keys Securely”](#storing-keys-securely) **Private Key Options:** 1. **File-based (local development):** ```shell # Key stored as .capgo_key_v2 file in project root npx @capgo/cli@latest bundle upload --key-v2 ``` 2. **Environment variable (CI/CD):** ```shell # Store in environment variable for CI export CAPGO_PRIVATE_KEY="$(cat .capgo_key_v2)" npx @capgo/cli@latest bundle upload --key-data-v2 "$CAPGO_PRIVATE_KEY" ``` **Public Key Setup (Required):** ```shell # Must save public key to Capacitor config for mobile app npx @capgo/cli@latest key save --key ./.capgo_key_v2.pub ``` **Production Environment:** * Store private keys in secure key management services (AWS KMS, Azure Key Vault, etc.) * Use CI/CD secret management for private keys * Never commit private keys to version control **Key Usage:** * **Private Key**: Used by CLI for encryption during bundle upload (keep secure) * **Public Key**: Stored in app configuration for decryption on device (safe to commit) ### Key Rotation [Section titled “Key Rotation”](#key-rotation) Regularly rotate your encryption keys for enhanced security: 1. **Generate new keys:** ```shell # Navigate to desired directory first, then create keys mkdir ./new-keys && cd ./new-keys npx @capgo/cli@latest key create ``` 2. **Save the new public key to Capacitor config:** ```shell npx @capgo/cli@latest key save --key ./new-keys/.capgo_key_v2.pub ``` 3. **Update your app configuration** with the new public key 4. **Deploy the updated app** before uploading encrypted bundles with the new key ## Security Best Practices [Section titled “Security Best Practices”](#security-best-practices) ### Key Security [Section titled “Key Security”](#key-security) * **Never share private keys** between environments or team members * **Use different keys** for different environments (dev, staging, production) * **Rotate keys regularly** (recommended: every 6-12 months) * **Store keys securely** using proper key management systems ### Bundle Security [Section titled “Bundle Security”](#bundle-security) * **Always verify** bundle integrity after decryption * **Monitor** for unusual download patterns or failures * **Use HTTPS** for all bundle URLs (required for mobile apps) * **Implement** proper error handling for decryption failures ### Access Control [Section titled “Access Control”](#access-control) * **Limit access** to encryption keys to authorized personnel only * **Use role-based access** for key management operations * **Audit** key usage and access regularly * **Implement** proper backup and recovery procedures ## Troubleshooting Encryption [Section titled “Troubleshooting Encryption”](#troubleshooting-encryption) ### Common Issues [Section titled “Common Issues”](#common-issues) **Decryption failures:** * Verify the private key matches the public key used for encryption * Check that the `ivSessionKey` is correct * Ensure you’re using Encryption V2 (V1 is no longer supported) **Key-related errors:** * Confirm the private key format is correct (PEM format) * Verify the key hasn’t been corrupted during storage/transfer * Check that the key has proper permissions in your app configuration **Performance issues:** * Large bundles may take longer to encrypt/decrypt * Consider using Delta (manifest) updates to reduce bundle sizes * Monitor device performance during decryption ### Debug Commands [Section titled “Debug Commands”](#debug-commands) Check encryption status: ```shell npx @capgo/cli@latest app debug ``` Test encryption/decryption workflow: ```shell # Test the complete workflow: zip → encrypt → decrypt → unzip npx @capgo/cli@latest bundle zip com.example.app --key-v2 npx @capgo/cli@latest bundle encrypt ./com.example.app.zip CHECKSUM --json npx @capgo/cli@latest bundle decrypt ./encrypted-bundle.zip IV_SESSION_KEY ``` ## Compliance and Standards [Section titled “Compliance and Standards”](#compliance-and-standards) Capgo’s encryption implementation follows industry standards: * **AES-256**: FIPS 140-2 approved encryption algorithm * **RSA-4096**: Strong asymmetric encryption for key protection * **GCM Mode**: Provides both confidentiality and authenticity * **Secure Random**: Cryptographically secure random number generation This makes Capgo suitable for applications requiring compliance with: * GDPR (General Data Protection Regulation) * HIPAA (Health Insurance Portability and Accountability Act) * SOC 2 (Service Organization Control 2) * ISO 27001 (Information Security Management) ## Performance Considerations [Section titled “Performance Considerations”](#performance-considerations) ### Encryption Overhead [Section titled “Encryption Overhead”](#encryption-overhead) * **Bundle size**: Encrypted bundles are slightly larger (\~1-2% overhead) * **Processing time**: Encryption/decryption adds minimal latency * **Memory usage**: Temporary increase during encryption/decryption operations ### Optimization Tips [Section titled “Optimization Tips”](#optimization-tips) * Use Delta (manifest) updates to minimize encrypted data transfer * Optimize your bundle size by converting images to WebP format * Minimize JavaScript and CSS files before bundling * Remove unused dependencies and code * Monitor device performance on older/slower devices ## Next Steps [Section titled “Next Steps”](#next-steps) * Learn about [Custom Storage](/docs/live-updates/custom-storage/) to use encryption with your own infrastructure * Explore [Channels](/docs/live-updates/channels/) to manage encrypted bundles across environments * Set up [CI/CD Integration](/docs/getting-started/cicd-integration/) to automate encrypted deployments # Features > Complete reference of all Capgo Live Update capabilities, from core update system to advanced deployment controls, analytics, and team collaboration features. This page provides a comprehensive overview of all features available in Capgo Live Updates. Each feature includes a brief description and links to detailed documentation. ## Core Update System [Section titled “Core Update System”](#core-update-system) ### Over-the-Air (OTA) Updates [Section titled “Over-the-Air (OTA) Updates”](#over-the-air-ota-updates) Deploy JavaScript, HTML, CSS, and asset updates directly to users without app store approval. Updates are downloaded in the background and applied on next app restart. **Key capabilities:** * Background downloads * Automatic installation * No user interruption * Cross-platform support (iOS, Android, Electron) [Learn more about update behavior →](/docs/live-updates/update-behavior/) *** ### Delta Updates (Differential Updates) [Section titled “Delta Updates (Differential Updates)”](#delta-updates-differential-updates) Only download files that have changed between versions, reducing bandwidth usage by up to 95% and speeding up update delivery. **Key capabilities:** * Automatic file-level diffing * Checksum-based verification * Manifest comparison * Intelligent fallback to full updates when needed [Learn more about delta updates →](/docs/live-updates/differentials/) *** ### Automatic Rollback [Section titled “Automatic Rollback”](#automatic-rollback) If an update fails to load or causes crashes, the system automatically reverts to the last known working version. **Key capabilities:** * Crash detection * Timeout detection * Automatic reversion * No user intervention required [Learn more about rollbacks →](/docs/live-updates/rollbacks/) *** ### Checksum Validation & Fallback [Section titled “Checksum Validation & Fallback”](#checksum-validation--fallback) Verifies bundle integrity via checksums and automatically falls back to the last known working version if corruption is detected. **Key capabilities:** * Checksum validation on download * Corruption detection * Automatic fallback to last working bundle * Manual recovery tools available *** ### Breaking Update Detection [Section titled “Breaking Update Detection”](#breaking-update-detection) Prevents incompatible updates from being applied to devices running older native code versions. **Key capabilities:** * Native version compatibility checking * Plugin dependency validation * Automatic blocking of incompatible updates * Clear error messaging [Learn more about version targeting →](/docs/live-updates/version-targeting/) *** ## Deployment Control [Section titled “Deployment Control”](#deployment-control) ### Channel System [Section titled “Channel System”](#channel-system) Organize and manage updates across different environments and user segments with flexible channel configurations. **Key capabilities:** * Unlimited custom channels (production, staging, beta, etc.) * Per-channel bundle assignments * Channel-specific targeting rules * Device self-assignment * Channel override per device [Learn more about channels →](/docs/live-updates/channels/) *** ### Device Targeting [Section titled “Device Targeting”](#device-targeting) Target specific devices, versions, or user segments for phased rollouts and controlled deployments. **Key capabilities:** * Version-based targeting * Device-specific overrides * Platform filtering (iOS, Android, Electron) * Custom metadata filtering * Emulator/dev build blocking *** ### Channel Policies [Section titled “Channel Policies”](#channel-policies) Configure rules and restrictions for how updates are delivered on each channel. **Key capabilities:** * Disable auto-updates * Block major version updates * Disable updates on emulators * Disable updates in development builds * Platform-specific policies (iOS-only, Android-only, Electron-only) [Learn more about channel policies →](/docs/live-updates/channels/#channel-policies) *** ## Developer Tools [Section titled “Developer Tools”](#developer-tools) ### Bundle Preview [Section titled “Bundle Preview”](#bundle-preview) Preview bundles in a live web environment before deploying to devices, accessible from the web dashboard. **Location:** Web Dashboard → App → Bundle → Preview tab *** ### Live Debugging [Section titled “Live Debugging”](#live-debugging) Real-time monitoring of update events for specific devices via CLI, showing check, download, install, and error events. **Usage:** ```bash npx @capgo/cli app debug [appId] ``` **Shows:** * Update checks * Download progress * Installation status * Error messages * Policy blocks *** ### Bundle Manifest Viewer [Section titled “Bundle Manifest Viewer”](#bundle-manifest-viewer) Inspect the complete manifest of any bundle including file list, checksums, and metadata. **Location:** Web Dashboard → App → Bundle → Manifest tab **Shows:** * File list with checksums * Bundle metadata * Native version compatibility * Plugin dependencies *** ### Native Plugin Dependencies [Section titled “Native Plugin Dependencies”](#native-plugin-dependencies) View all native Capacitor plugins included in each bundle to track dependency changes across versions. **Location:** Web Dashboard → App → Bundle → Dependencies tab **Shows:** * Plugin names and versions * Dependency additions/removals * Compatibility warnings *** ### CLI Integration [Section titled “CLI Integration”](#cli-integration) Comprehensive command-line interface for automated deployments and CI/CD integration. **Key commands:** * `bundle upload` - Upload new bundles * `bundle list` - List all bundles * `bundle delete` - Delete bundles * `bundle cleanup` - Clean up old bundles * `channel set` - Configure channels * `app debug` - Live debugging [View full CLI reference →](/docs/cli/reference/) *** ### Bundle Encryption [Section titled “Bundle Encryption”](#bundle-encryption) End-to-end encryption for bundles with AES-256 encryption, protecting your code in transit and at rest. **Key capabilities:** * RSA key pair generation * AES-256 bundle encryption * Code signature verification * Encryption key management [Learn more about encryption →](/docs/live-updates/encryption/) *** ### Bundle Cleanup & Retention [Section titled “Bundle Cleanup & Retention”](#bundle-cleanup--retention) Automatically clean up old bundles based on retention policies to manage storage usage. **Key capabilities:** * Configurable retention count * Automatic cleanup via CLI * Scheduled cleanup jobs * Storage usage tracking **Usage:** ```bash npx @capgo/cli bundle cleanup --keep=10 ``` *** ## Analytics & Monitoring [Section titled “Analytics & Monitoring”](#analytics--monitoring) ### Update Statistics [Section titled “Update Statistics”](#update-statistics) Track update adoption rates, success rates, and deployment progress across your user base. **Metrics available:** * Download success rate * Installation success rate * Error rates by type * Update adoption over time * Version distribution **Location:** Web Dashboard → App → Statistics *** ### Device Logs [Section titled “Device Logs”](#device-logs) Per-device event logs showing complete update lifecycle from check to installation. **Event types:** * Update checks * Download start/complete/fail * Install start/complete/fail * Rollback events * Policy blocks **Location:** * Web Dashboard → App → Device → Logs * Web Dashboard → App → Logs (all devices) [Learn more about logs →](/docs/webapp/logs/) *** ### Bundle Usage Analytics [Section titled “Bundle Usage Analytics”](#bundle-usage-analytics) Detailed analytics on which bundles are active, download counts, and storage usage. **Metrics:** * Active installations per bundle * Download counts * Storage usage per bundle * Bandwidth usage *** ### Channel Statistics [Section titled “Channel Statistics”](#channel-statistics) Track performance and adoption metrics per channel. **Metrics:** * Devices per channel * Update success rates per channel * Deployment history * Error rates by channel **Location:** Web Dashboard → App → Channel → Statistics *** ### Deployment History [Section titled “Deployment History”](#deployment-history) Complete audit trail of all bundle deployments, channel assignments, and configuration changes. **Tracked events:** * Bundle uploads * Channel assignments * Policy changes * Device overrides **Location:** Web Dashboard → App → Channel → History *** ## Security & Compliance [Section titled “Security & Compliance”](#security--compliance) ### End-to-End Encryption [Section titled “End-to-End Encryption”](#end-to-end-encryption) Encrypt bundles at rest and in transit with industry-standard AES-256 encryption. [Learn more about encryption →](/docs/live-updates/encryption/) *** ### Code Signing [Section titled “Code Signing”](#code-signing) Verify bundle integrity with cryptographic signatures to prevent tampering. *** ### SOC 2 Type II Compliance [Section titled “SOC 2 Type II Compliance”](#soc-2-type-ii-compliance) Infrastructure and processes certified to SOC 2 Type II standards for enterprise security. *** ### App Store Compliance [Section titled “App Store Compliance”](#app-store-compliance) Fully compliant with Apple App Store and Google Play Store policies for OTA updates. [Learn more about compliance →](/docs/live-updates/compliance/) *** ### 2FA Enforcement (Organization-level) [Section titled “2FA Enforcement (Organization-level)”](#2fa-enforcement-organization-level) Require two-factor authentication for all organization members to access the dashboard and API. **Location:** Web Dashboard → Organization → Security [Learn more about 2FA →](/docs/webapp/2fa-enforcement/) *** ### Encrypted Bundles Enforcement [Section titled “Encrypted Bundles Enforcement”](#encrypted-bundles-enforcement) Require all bundles to be encrypted at the organization level. **Location:** Web Dashboard → Organization → Security *** ## Team Collaboration [Section titled “Team Collaboration”](#team-collaboration) ### Role-Based Access Control (RBAC) [Section titled “Role-Based Access Control (RBAC)”](#role-based-access-control-rbac) Granular permissions for organization and app-level access control. **Organization roles:** * `super_admin` - Full access * `admin` - Admin access * `read` - Read-only access * `upload` - Upload-only access **App roles:** * `app_developer` - Full app access * `app_uploader` - Upload bundles only * `app_reader` - Read-only access **Location:** * Web Dashboard → Organization → Members * Web Dashboard → App → Access [Learn more about RBAC →](/docs/webapp/organization-system/#roles-and-permissions) *** ### Audit Logs [Section titled “Audit Logs”](#audit-logs) Complete audit trail of all organization and app activities for compliance and security. **Logged events:** * User actions (login, logout, permission changes) * Bundle operations (upload, delete, assign) * Channel operations (create, update, delete) * Organization changes (settings, members) **Location:** Web Dashboard → Organization → Audit Logs *** ### Webhooks [Section titled “Webhooks”](#webhooks) Receive real-time notifications about events in your apps via HTTP webhooks. **Supported events:** * `apps` - App created/updated/deleted * `app_versions` - Bundle uploaded/deleted * `channels` - Channel created/updated/deleted * `org_users` - Member added/removed * `orgs` - Organization updated **Features:** * Custom webhook URLs * Event filtering * Delivery logs * Retry mechanism * Test functionality **Location:** Web Dashboard → Organization → Webhooks *** ### Multi-User Collaboration [Section titled “Multi-User Collaboration”](#multi-user-collaboration) Invite team members to your organization with specific roles and permissions. **Features:** * Email invitations * Role assignment * Member management * Access revocation **Location:** Web Dashboard → Organization → Members *** ### API Key Management [Section titled “API Key Management”](#api-key-management) Create, manage, and revoke API keys with optional expiration dates and hashed storage. **Key capabilities:** * Per-app or per-organization keys * Optional expiration dates * Hashed storage (irreversible) * Key rotation support **Location:** Web Dashboard → API Keys [Learn more about API keys →](/docs/public-api/#authentication) *** ### Password Policies [Section titled “Password Policies”](#password-policies) Organization-level password requirements to enforce security standards. **Configurable policies:** * Minimum length * Require uppercase * Require numbers * Require special characters **Location:** Web Dashboard → Organization → Security *** ## Platform Support [Section titled “Platform Support”](#platform-support) ### Multi-Platform Support [Section titled “Multi-Platform Support”](#multi-platform-support) Support for iOS, Android, and Electron apps with a single SDK. **Supported platforms:** * iOS (Capacitor 5, 6, 7, 8) * Android (Capacitor 5, 6, 7, 8) * Electron (NEW in 2025) *** ### Long-Term Support [Section titled “Long-Term Support”](#long-term-support) Continued support for older Capacitor versions to maintain compatibility with legacy apps. **Currently supported:** * Capacitor 8 (latest) * Capacitor 7 * Capacitor 6 * Capacitor 5 *** ### Custom Storage Backends [Section titled “Custom Storage Backends”](#custom-storage-backends) Use your own storage infrastructure (S3, R2, etc.) instead of Capgo’s default storage. [Learn more about custom storage →](/docs/live-updates/custom-storage/) *** ### China Configuration [Section titled “China Configuration”](#china-configuration) Special configuration for apps distributed in mainland China to comply with local regulations. [Learn more about China configuration →](/docs/live-updates/china-configuration/) *** ## Advanced Features [Section titled “Advanced Features”](#advanced-features) ### Custom Update Behavior [Section titled “Custom Update Behavior”](#custom-update-behavior) Configure when and how updates are checked and applied via the SDK. **Configurable options:** * Check interval (`periodCheckDelay` - minimum 600 seconds) * Direct update timing (`directUpdate` - atInstall, onLaunch, always) * Auto-update enable/disable (`autoUpdate`) * Network requirements (Android only - via WorkManager) [Learn more about update behavior →](/docs/live-updates/update-behavior/) *** ### Update Types [Section titled “Update Types”](#update-types) Different update types for different use cases, from instant updates to user-controlled installations. **Available types:** * Background updates (default) * Immediate updates * User-prompted updates * Conditional updates [Learn more about update types →](/docs/live-updates/update-types/) *** ### Credit System [Section titled “Credit System”](#credit-system) Usage-based billing with credits for bandwidth, storage, and other resources. **Features:** * Credit usage tracking * Usage alerts * Top-up via Stripe * Credit ledger **Location:** Web Dashboard → Organization → Credits *** ## Getting Started [Section titled “Getting Started”](#getting-started) Ready to start using these features? Follow our [Quickstart Guide](/docs/getting-started/quickstart/) to set up your first app with Capgo Live Updates. ## Need Help? [Section titled “Need Help?”](#need-help) * [Join our Discord](https://discord.capgo.app) for community support * [Check the FAQ](/docs/faq/) for common questions * [Browse API documentation](/docs/public-api/) for API integration * [Contact support](https://capgo.app/consulting/) for enterprise assistance # CI/CD Integrations > Integrate Capgo Live Updates with your favorite CI/CD platform for automated deployment workflows. Automate your Capgo Live Updates deployment process by integrating with popular CI/CD platforms. These integrations allow you to automatically deploy app updates whenever you push code changes, test feature branches, and manage multiple deployment environments. ## Available Integrations [Section titled “Available Integrations”](#available-integrations) Choose your CI/CD platform to get started with automated deployments: [Azure DevOps ](/docs/live-updates/integrations/azure-devops/)Integrate with Azure DevOps Pipelines for automated builds, testing, and deployment workflows. [GitLab CI/CD ](/docs/live-updates/integrations/gitlab-ci/)Set up GitLab CI/CD pipelines to automatically deploy your app updates with comprehensive environment management. [GitHub Actions ](/docs/live-updates/integrations/github-actions/)Use GitHub Actions for powerful automation with multi-channel deployments and environment protection. [Bitbucket Pipelines ](/docs/live-updates/integrations/bitbucket-pipeline/)Deploy with Bitbucket Pipelines using simple or advanced configurations for multiple environments. ## What You’ll Get [Section titled “What You’ll Get”](#what-youll-get) All integration guides include: * **Simple Setup**: Basic configuration to get started quickly * **Advanced Workflows**: Multi-environment deployments with staging and production * **Feature Branch Testing**: Automatic deployment of feature branches to test channels * **Security Best Practices**: Secure secret management and environment protection * **Monitoring**: Notifications and logging for deployment status Tip **New to CI/CD?** Start with the simple configuration for your platform, then gradually add more advanced features like multi-channel deployments and automated testing as your needs grow. ## Common Features [Section titled “Common Features”](#common-features) Each integration supports: * **Automated Builds**: Trigger deployments on code changes * **Multi-Channel Support**: Deploy to different channels (development, staging, production) * **Pull Request/Merge Request Testing**: Test changes in isolated environments * **Encryption Support**: Secure deployments with Capgo’s encryption feature * **Environment Protection**: Manual approvals and restricted access for production * **Notifications**: Slack, email, and other notification integrations ## Prerequisites [Section titled “Prerequisites”](#prerequisites) Before setting up any integration, ensure you have: * A Capgo account with an app configured * Your app’s source code in a Git repository * A Capgo API token from [console.capgo.app/apikeys](https://console.capgo.app/apikeys) * Node.js and npm/yarn configured in your project ## Related Documentation [Section titled “Related Documentation”](#related-documentation) * [Channels](/docs/live-updates/channels/) - Learn how to manage different deployment environments * [Encryption](/docs/live-updates/encryption/) - Secure your deployments with end-to-end encryption * [Update Behavior](/docs/live-updates/update-behavior/) - Customize how updates are applied to your apps Choose your CI/CD platform above to start automating your Capgo deployments! # Azure DevOps Integration > Learn how to integrate Capgo Live Updates with Azure DevOps Pipelines for automated deployment of your app updates. Integrate Capgo Live Updates with Azure DevOps Pipelines to automatically deploy your app updates whenever you push code changes. This guide covers setting up automated builds, testing, and deployment workflows. ## Prerequisites [Section titled “Prerequisites”](#prerequisites) Before setting up Azure DevOps integration, ensure you have: * An Azure DevOps organization and project * A Capgo account with an app configured * Your app’s source code in an Azure Repos Git repository * Node.js and npm/yarn configured in your project ## Setting Up Azure DevOps Pipeline [Section titled “Setting Up Azure DevOps Pipeline”](#setting-up-azure-devops-pipeline) ### Step 1: Create Pipeline Variables [Section titled “Step 1: Create Pipeline Variables”](#step-1-create-pipeline-variables) First, set up the necessary variables in your Azure DevOps project: 1. Navigate to your Azure DevOps project 2. Go to **Pipelines** → **Library** → **Variable groups** 3. Create a new variable group named `Capgo-Variables` 4. Add the following variables: | Variable Name | Value | Secure | | ------------- | -------------------- | ------ | | `CAPGO_TOKEN` | Your Capgo API token | ✅ Yes | Tip Get your Capgo API token from [console.capgo.app/apikeys](https://console.capgo.app/apikeys). Your app ID is already configured in your `capacitor.config.ts` file. ## Simple [Section titled “Simple”](#simple) Basic configuration that deploys to production on every push to the main branch: ```yaml # Simple Azure DevOps Pipeline for Capgo Live Updates trigger: branches: include: - main variables: - group: Capgo-Variables jobs: - job: BuildAndDeploy displayName: 'Build and Deploy to Capgo' pool: vmImage: 'ubuntu-latest' steps: - task: NodeTool@0 displayName: 'Setup Node.js' inputs: versionSpec: '22.x' - script: | npm ci npm run test npm run build displayName: 'Install, test and build' - script: | npm install -g @capgo/cli npx @capgo/cli bundle upload --apikey $(CAPGO_TOKEN) --channel production displayName: 'Deploy to Capgo' ``` ## Advanced [Section titled “Advanced”](#advanced) ### Feature Branch Deployments [Section titled “Feature Branch Deployments”](#feature-branch-deployments) Deploy feature branches to test channels for review and testing: ```yaml # Feature branch deployment trigger: branches: include: - feature/* variables: - group: Capgo-Variables jobs: - job: DeployFeature displayName: 'Deploy Feature Branch' pool: vmImage: 'ubuntu-latest' condition: startsWith(variables['Build.SourceBranch'], 'refs/heads/feature/') steps: - task: NodeTool@0 inputs: versionSpec: '22.x' - script: | npm ci npm run test npm run build displayName: 'Install, test and build' - script: | BRANCH_NAME=$(echo "$(Build.SourceBranchName)" | sed 's/[^a-zA-Z0-9-]/-/g') CHANNEL_NAME="feature-$BRANCH_NAME" npm install -g @capgo/cli npx @capgo/cli channel create $CHANNEL_NAME --apikey $(CAPGO_TOKEN) || true npx @capgo/cli bundle upload --apikey $(CAPGO_TOKEN) --channel $CHANNEL_NAME displayName: 'Deploy to Feature Channel' ``` Tip **Testing with Channels**: After deploying to a feature channel, you can test the update in your app by configuring it to use that specific channel. Learn more about [configuring channels in your app](/docs/live-updates/channels/#configuring-the-channel-in-your-app). ### Using Encryption [Section titled “Using Encryption”](#using-encryption) If you’re using [Capgo’s encryption feature](/docs/live-updates/encryption/), you’ll need to store your private key securely in your CI/CD environment. After [setting up encryption keys](/docs/live-updates/encryption/#setting-up-encryption) locally, add your private key to Azure DevOps variables: ```shell # Display your private key content (copy this output) cat .capgo_key_v2 ``` Add this content as `CAPGO_PRIVATE_KEY` in your Azure DevOps variable group (mark as secret), then use it in pipelines: ```yaml # Deploy with encryption - script: | npm install -g @capgo/cli npx @capgo/cli bundle upload --apikey $(CAPGO_TOKEN) --key-data-v2 "$(CAPGO_PRIVATE_KEY)" --channel production displayName: 'Deploy to Capgo with Encryption' ``` Caution **Security Best Practices:** * Never commit the `.capgo_key_v2` file to version control * Store the private key only in secure CI/CD secret management * Use different keys for different environments ### Multi-Channel Configuration [Section titled “Multi-Channel Configuration”](#multi-channel-configuration) For comprehensive information about setting up and managing multiple deployment channels, see the [Channels documentation](/docs/live-updates/channels/). Complete configuration with multiple environments and pull request deployments: ```yaml # Advanced Azure DevOps Pipeline with Multiple Channels trigger: branches: include: - main - develop pr: branches: include: - main - develop variables: - group: Capgo-Variables stages: # Build stage - stage: Build jobs: - job: BuildApp pool: vmImage: 'ubuntu-latest' steps: - task: NodeTool@0 inputs: versionSpec: '22.x' - script: | npm ci npm run test npm run build displayName: 'Install, test and build' - task: PublishBuildArtifacts@1 inputs: pathToPublish: 'dist' artifactName: 'app-build' # Deploy to development - stage: DeployDev condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/develop')) jobs: - deployment: DeployDevelopment environment: development pool: vmImage: 'ubuntu-latest' strategy: runOnce: deploy: steps: - task: NodeTool@0 inputs: versionSpec: '22.x' - task: DownloadBuildArtifacts@0 inputs: artifactName: 'app-build' downloadPath: '$(Pipeline.Workspace)' - script: | npm install -g @capgo/cli npx @capgo/cli bundle upload --apikey $(CAPGO_TOKEN) --channel development --path $(Pipeline.Workspace)/app-build displayName: 'Deploy to Development' # Deploy PR to test channel - stage: DeployPR condition: and(succeeded(), eq(variables['Build.Reason'], 'PullRequest')) jobs: - job: DeployPRChannel pool: vmImage: 'ubuntu-latest' steps: - task: NodeTool@0 inputs: versionSpec: '22.x' - task: DownloadBuildArtifacts@0 inputs: artifactName: 'app-build' downloadPath: '$(Pipeline.Workspace)' - script: | CHANNEL_NAME="pr-$(System.PullRequest.PullRequestNumber)" npm install -g @capgo/cli npx @capgo/cli channel create $CHANNEL_NAME --apikey $(CAPGO_TOKEN) || true npx @capgo/cli bundle upload --apikey $(CAPGO_TOKEN) --channel $CHANNEL_NAME --path $(Pipeline.Workspace)/app-build displayName: 'Deploy to PR Channel' # Deploy to production - stage: DeployProd condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main')) jobs: - deployment: DeployProduction environment: production pool: vmImage: 'ubuntu-latest' strategy: runOnce: deploy: steps: - task: NodeTool@0 inputs: versionSpec: '22.x' - task: DownloadBuildArtifacts@0 inputs: artifactName: 'app-build' downloadPath: '$(Pipeline.Workspace)' - script: | npm install -g @capgo/cli npx @capgo/cli bundle upload --apikey $(CAPGO_TOKEN) --channel production --path $(Pipeline.Workspace)/app-build displayName: 'Deploy to Production' ``` ### Multi-Environment Deployment [Section titled “Multi-Environment Deployment”](#multi-environment-deployment) For complex scenarios with multiple environments: ```yaml # Extended pipeline with multiple environments parameters: - name: deployEnvironment displayName: 'Deploy Environment' type: string default: 'staging' values: - staging - production variables: - group: Capgo-Variables - name: channelName ${{ if eq(parameters.deployEnvironment, 'production') }}: value: 'production' ${{ else }}: value: 'staging' stages: # Build stage - stage: Build jobs: - job: BuildApp pool: vmImage: 'ubuntu-latest' steps: - task: NodeTool@0 inputs: versionSpec: '22.x' - script: | npm ci npm run test npm run build displayName: 'Install, test and build' - task: PublishBuildArtifacts@1 inputs: pathToPublish: 'dist' artifactName: 'app-build' - stage: DeployStaging displayName: 'Deploy to Staging' dependsOn: Build condition: and(succeeded(), eq('${{ parameters.deployEnvironment }}', 'staging')) jobs: - deployment: DeployStaging displayName: 'Deploy to Staging Channel' pool: vmImage: 'ubuntu-latest' environment: 'staging' strategy: runOnce: deploy: steps: - template: deploy-steps.yml parameters: channel: 'staging' - stage: DeployProduction displayName: 'Deploy to Production' dependsOn: Build condition: and(succeeded(), eq('${{ parameters.deployEnvironment }}', 'production')) jobs: - deployment: DeployProduction displayName: 'Deploy to Production Channel' pool: vmImage: 'ubuntu-latest' environment: 'production' strategy: runOnce: deploy: steps: - template: deploy-steps.yml parameters: channel: 'production' ``` ### Deployment Template (deploy-steps.yml) [Section titled “Deployment Template (deploy-steps.yml)”](#deployment-template-deploy-stepsyml) Create a reusable template file `deploy-steps.yml`: deploy-steps.yml ```yaml parameters: - name: channel type: string steps: - task: NodeTool@0 displayName: 'Install Node.js' inputs: versionSpec: '22.x' - task: DownloadBuildArtifacts@0 displayName: 'Download build artifacts' inputs: artifactName: 'app-build' downloadPath: '$(System.ArtifactsDirectory)' - script: | npm install -g @capgo/cli displayName: 'Install Capgo CLI' - script: | npx @capgo/cli bundle upload \ --apikey $(CAPGO_TOKEN) \ --channel ${{ parameters.channel }} \ --path $(System.ArtifactsDirectory)/app-build displayName: 'Upload to Capgo (${{ parameters.channel }})' ``` ### Branch-Based Deployment Strategy [Section titled “Branch-Based Deployment Strategy”](#branch-based-deployment-strategy) Configure different deployment strategies based on Git branches: ```yaml trigger: branches: include: - main - develop - feature/* variables: - group: Capgo-Variables - name: targetChannel ${{ if eq(variables['Build.SourceBranch'], 'refs/heads/main') }}: value: 'production' ${{ elseif eq(variables['Build.SourceBranch'], 'refs/heads/develop') }}: value: 'staging' ${{ else }}: value: 'development' stages: - stage: Build jobs: - job: BuildApp pool: vmImage: 'ubuntu-latest' steps: - task: NodeTool@0 inputs: versionSpec: '22.x' - script: | npm ci npm run test npm run build displayName: 'Install, test and build' - task: PublishBuildArtifacts@1 inputs: pathToPublish: 'dist' artifactName: 'app-build' - stage: Deploy displayName: 'Deploy to $(targetChannel)' dependsOn: Build condition: succeeded() jobs: - deployment: DeployJob displayName: 'Deploy to $(targetChannel) Channel' pool: vmImage: 'ubuntu-latest' environment: '$(targetChannel)' strategy: runOnce: deploy: steps: - template: deploy-steps.yml parameters: channel: '$(targetChannel)' ``` ## Security Best Practices [Section titled “Security Best Practices”](#security-best-practices) ### Secure Variable Management [Section titled “Secure Variable Management”](#secure-variable-management) 1. **Use Variable Groups**: Store sensitive data in Azure DevOps variable groups 2. **Mark as Secret**: Always mark API tokens and keys as secret variables 3. **Scope Access**: Limit variable group access to specific pipelines and users 4. **Rotate Keys**: Regularly rotate your Capgo API tokens ## Monitoring and Notifications [Section titled “Monitoring and Notifications”](#monitoring-and-notifications) ### Teams Integration [Section titled “Teams Integration”](#teams-integration) Add Microsoft Teams notifications to your pipeline: ```yaml - task: ms-teams-deploy-card@1.4.1 displayName: 'Notify Teams on Success' condition: succeeded() inputs: webhookUri: '$(TEAMS_WEBHOOK_URL)' title: 'Capgo Deployment Successful' text: 'App deployed to $(targetChannel) channel' themeColor: '00FF00' - task: ms-teams-deploy-card@1.4.1 displayName: 'Notify Teams on Failure' condition: failed() inputs: webhookUri: '$(TEAMS_WEBHOOK_URL)' title: 'Capgo Deployment Failed' text: 'Deployment to $(targetChannel) failed' themeColor: 'FF0000' ``` ### Email Notifications [Section titled “Email Notifications”](#email-notifications) Configure email notifications for deployment status: ```yaml - task: EmailReport@1 displayName: 'Send Email Report' condition: always() inputs: sendMailConditionConfig: 'Always' subject: 'Capgo Deployment Report - $(Build.BuildNumber)' to: 'team@yourcompany.com' body: | Deployment Status: $(Agent.JobStatus) Channel: $(targetChannel) Build: $(Build.BuildNumber) Commit: $(Build.SourceVersion) ``` ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) ### Common Issues [Section titled “Common Issues”](#common-issues) **Pipeline fails with “Capgo CLI not found”:** ```yaml # Ensure global installation - script: | npm install -g @capgo/cli which capgo || echo "Capgo CLI not found in PATH" displayName: 'Install and verify Capgo CLI' ``` **Authentication errors:** ```yaml # Verify token is correctly set - script: | echo "Token length: ${#CAPGO_TOKEN}" if [ -z "$CAPGO_TOKEN" ]; then echo "CAPGO_TOKEN is not set" exit 1 fi displayName: 'Verify Capgo token' env: CAPGO_TOKEN: $(CAPGO_TOKEN) ``` **Build artifacts not found:** ```yaml # List available artifacts for debugging - script: | ls -la $(System.ArtifactsDirectory) find $(System.ArtifactsDirectory) -name "*.js" -o -name "*.html" displayName: 'Debug artifacts' ``` ### Debug Pipeline [Section titled “Debug Pipeline”](#debug-pipeline) Add debugging steps to troubleshoot issues: ```yaml - script: | echo "Build.SourceBranch: $(Build.SourceBranch)" echo "Build.BuildNumber: $(Build.BuildNumber)" echo "Target Channel: $(targetChannel)" displayName: 'Debug Pipeline Variables' - script: | npx @capgo/cli app debug --apikey $(CAPGO_TOKEN) displayName: 'Debug Capgo App Status' ``` ## Next Steps [Section titled “Next Steps”](#next-steps) * Learn about [Channels](/docs/live-updates/channels/) to manage different deployment environments * Explore [Custom Storage](/docs/live-updates/custom-storage/) for advanced deployment scenarios * Set up [Encryption](/docs/live-updates/encryption/) for secure deployments * Configure [Update Behavior](/docs/live-updates/update-behavior/) to customize how updates are applied With Azure DevOps integration, you can automate your Capgo deployments and ensure consistent, reliable updates to your mobile app users. # Bitbucket Pipelines Integration > Learn how to integrate Capgo Live Updates with Bitbucket Pipelines for automated deployment of your app updates. Integrate Capgo Live Updates with Bitbucket Pipelines to automatically deploy your app updates whenever you push code changes. This guide covers setting up automated builds, testing, and deployment workflows. ## Prerequisites [Section titled “Prerequisites”](#prerequisites) Before setting up Bitbucket Pipelines integration, ensure you have: * A Bitbucket account with a repository * A Capgo account with an app configured * Node.js and npm/yarn configured in your project ## Setting Up Bitbucket Pipelines [Section titled “Setting Up Bitbucket Pipelines”](#setting-up-bitbucket-pipelines) ### Step 1: Configure Repository Variables [Section titled “Step 1: Configure Repository Variables”](#step-1-configure-repository-variables) First, set up the necessary variables in your Bitbucket repository: 1. Navigate to your Bitbucket repository 2. Go to **Repository settings** → **Pipelines** → **Repository variables** 3. Add the following variables: | Variable Name | Value | Secured | | ------------- | -------------------- | ------- | | `CAPGO_TOKEN` | Your Capgo API token | ✅ Yes | Tip Get your Capgo API token from [console.capgo.app/apikeys](https://console.capgo.app/apikeys). Your app ID is already configured in your `capacitor.config.ts` file. ## Simple [Section titled “Simple”](#simple) Basic configuration that deploys to production on every push to the main branch: ```yaml # bitbucket-pipelines.yml - Simple Configuration image: node:22 pipelines: branches: main: - step: name: Build and Deploy to Production script: - npm ci - npm run test - npm run build - npm install -g @capgo/cli - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel production artifacts: - dist/** ``` ## Advanced [Section titled “Advanced”](#advanced) ### Feature Branch Deployments [Section titled “Feature Branch Deployments”](#feature-branch-deployments) Deploy feature branches to test channels for review and testing: ```yaml # Feature branch deployment pipelines: branches: feature/*: - step: name: Deploy Feature Branch script: - npm ci - npm run test - npm run build - BRANCH_NAME=$(echo $BITBUCKET_BRANCH | sed 's/[^a-zA-Z0-9-]/-/g') - CHANNEL_NAME="feature-$BRANCH_NAME" - npm install -g @capgo/cli - npx @capgo/cli channel create $CHANNEL_NAME --apikey $CAPGO_TOKEN || true - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel $CHANNEL_NAME artifacts: - dist/** ``` Tip **Testing with Channels**: After deploying to a feature channel, you can test the update in your app by configuring it to use that specific channel. Learn more about [configuring channels in your app](/docs/live-updates/channels/#configuring-the-channel-in-your-app). ### Using Encryption [Section titled “Using Encryption”](#using-encryption) If you’re using [Capgo’s encryption feature](/docs/live-updates/encryption/), you’ll need to store your private key securely in your CI/CD environment. After [setting up encryption keys](/docs/live-updates/encryption/#setting-up-encryption) locally, add your private key to Bitbucket variables: ```shell # Display your private key content (copy this output) cat .capgo_key_v2 ``` Add this content as `CAPGO_PRIVATE_KEY` in your Bitbucket repository variables (mark as secured), then use it in pipelines: ```yaml # Deploy with encryption - step: name: Deploy to Capgo with Encryption script: - npm install -g @capgo/cli - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --key-data-v2 "$CAPGO_PRIVATE_KEY" --channel production ``` Caution **Security Best Practices:** * Never commit the `.capgo_key_v2` file to version control * Store the private key only in secure CI/CD secret management * Use different keys for different environments ### Multi-Channel Configuration [Section titled “Multi-Channel Configuration”](#multi-channel-configuration) For comprehensive information about setting up and managing multiple deployment channels, see the [Channels documentation](/docs/live-updates/channels/). Complete configuration with multiple environments and pull request deployments: ```yaml # bitbucket-pipelines.yml - Advanced Multi-Channel Configuration image: node:22 definitions: steps: - step: &build-step name: Build Application script: - npm ci - npm run test - npm run build artifacts: - dist/** - step: &deploy-step name: Deploy to Capgo script: - npm install -g @capgo/cli - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel $CHANNEL_NAME pipelines: branches: main: - step: <<: *build-step - step: <<: *deploy-step name: Deploy to Production deployment: production trigger: manual script: - export CHANNEL_NAME=production - npm install -g @capgo/cli - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel $CHANNEL_NAME develop: - step: <<: *build-step - step: <<: *deploy-step name: Deploy to Development deployment: development script: - export CHANNEL_NAME=development - npm install -g @capgo/cli - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel $CHANNEL_NAME pull-requests: '**': - step: <<: *build-step - step: name: Deploy PR to Test Channel script: - CHANNEL_NAME="pr-$BITBUCKET_PR_ID" - npm install -g @capgo/cli - npx @capgo/cli channel create $CHANNEL_NAME --apikey $CAPGO_TOKEN || true - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel $CHANNEL_NAME artifacts: - dist/** ``` ### Multi-Environment Pipeline [Section titled “Multi-Environment Pipeline”](#multi-environment-pipeline) For complex deployment scenarios with staging and production environments: ```yaml # Multi-environment pipeline image: node:22 pipelines: branches: main: - step: name: Build script: - npm ci - npm run test - npm run build artifacts: - dist/** - step: name: Deploy to Staging deployment: staging script: - npm install -g @capgo/cli - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel staging - step: name: Deploy to Production deployment: production trigger: manual script: - npm install -g @capgo/cli - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel production develop: - step: name: Build and Deploy to Development script: - npm ci - npm run test - npm run build - npm install -g @capgo/cli - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel development artifacts: - dist/** ``` ### Branch-Based Deployment Strategy [Section titled “Branch-Based Deployment Strategy”](#branch-based-deployment-strategy) Automatically deploy different branches to appropriate channels: ```yaml # Dynamic channel deployment image: node:22 definitions: scripts: - script: &determine-channel | if [ "$BITBUCKET_BRANCH" = "main" ]; then export CHANNEL_NAME="production" elif [ "$BITBUCKET_BRANCH" = "develop" ]; then export CHANNEL_NAME="staging" else export CHANNEL_NAME="development" fi echo "Deploying to channel: $CHANNEL_NAME" pipelines: default: - step: name: Build and Deploy script: - npm ci - npm run test - npm run build - *determine-channel - npm install -g @capgo/cli - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel $CHANNEL_NAME artifacts: - dist/** ``` ### Parallel Pipeline Execution [Section titled “Parallel Pipeline Execution”](#parallel-pipeline-execution) Optimize build times with parallel steps: ```yaml # Parallel execution pipeline image: node:22 pipelines: branches: main: - parallel: - step: name: Run Tests script: - npm ci - npm run test - step: name: Lint Code script: - npm ci - npm run lint - step: name: Build Application script: - npm ci - npm run build artifacts: - dist/** - step: name: Deploy to Production deployment: production script: - npm install -g @capgo/cli - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel production ``` ## Security Best Practices [Section titled “Security Best Practices”](#security-best-practices) ### Repository Variables [Section titled “Repository Variables”](#repository-variables) 1. **Secured Variables**: Always mark API tokens as secured 2. **Environment Variables**: Use deployment-specific variables when needed 3. **Access Control**: Limit repository access to authorized team members 4. **Token Rotation**: Regularly rotate your Capgo API tokens ### Deployment Environments [Section titled “Deployment Environments”](#deployment-environments) Configure deployment environments for better security: ```yaml # Deployment with environment restrictions pipelines: branches: main: - step: name: Deploy to Production deployment: production trigger: manual script: - npm install -g @capgo/cli - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel production ``` ## Monitoring and Notifications [Section titled “Monitoring and Notifications”](#monitoring-and-notifications) ### Slack Integration [Section titled “Slack Integration”](#slack-integration) Add Slack notifications to your pipeline: ```yaml # Pipeline with Slack notifications pipelines: branches: main: - step: name: Build and Deploy script: - npm ci - npm run test - npm run build - npm install -g @capgo/cli - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel production after-script: - | if [ $BITBUCKET_EXIT_CODE -eq 0 ]; then curl -X POST -H 'Content-type: application/json' \ --data '{"text":"✅ Capgo deployment successful for '$BITBUCKET_BRANCH'"}' \ $SLACK_WEBHOOK_URL else curl -X POST -H 'Content-type: application/json' \ --data '{"text":"❌ Capgo deployment failed for '$BITBUCKET_BRANCH'"}' \ $SLACK_WEBHOOK_URL fi ``` ### Email Notifications [Section titled “Email Notifications”](#email-notifications) Configure email notifications through Bitbucket’s built-in features or using external services: ```yaml # Email notification step - step: name: Send Notification script: - | curl -X POST \ -H "Content-Type: application/json" \ -d '{ "to": "team@yourcompany.com", "subject": "Capgo Deployment Status", "body": "Deployment of '$BITBUCKET_BRANCH' completed with status: '$BITBUCKET_EXIT_CODE'" }' \ $EMAIL_SERVICE_URL condition: result: [successful, failed] ``` ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) ### Common Issues [Section titled “Common Issues”](#common-issues) **Pipeline fails with “Capgo CLI not found”:** ```yaml # Debug CLI installation - step: name: Debug CLI script: - npm install -g @capgo/cli - which capgo || echo "Capgo CLI not found" - npx @capgo/cli --version ``` **Authentication errors:** ```yaml # Verify token configuration - step: name: Debug Auth script: - | if [ -z "$CAPGO_TOKEN" ]; then echo "CAPGO_TOKEN is not set" exit 1 fi echo "Token length: ${#CAPGO_TOKEN}" ``` **Build artifacts not found:** ```yaml # List build outputs - step: name: Debug Build script: - ls -la dist/ - find dist/ -type f -name "*.js" -o -name "*.html" ``` ### Debug Pipeline [Section titled “Debug Pipeline”](#debug-pipeline) Add debugging information to troubleshoot issues: ```yaml # Debug pipeline pipelines: branches: main: - step: name: Debug Information script: - echo "Branch: $BITBUCKET_BRANCH" - echo "Commit: $BITBUCKET_COMMIT" - echo "Build: $BITBUCKET_BUILD_NUMBER" - env | grep BITBUCKET_ | sort - step: name: Build and Deploy script: - npm ci - npm run test - npm run build - npm install -g @capgo/cli - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel production ``` ### Pipeline Validation [Section titled “Pipeline Validation”](#pipeline-validation) Enable pipeline validation to catch configuration errors: ```yaml # Enable pipeline validation options: docker: true size: 2x pipelines: branches: main: - step: name: Validate Pipeline script: - echo "Pipeline validation successful" - step: name: Build and Deploy script: # ... deployment steps ``` ## Next Steps [Section titled “Next Steps”](#next-steps) * Learn about [Channels](/docs/live-updates/channels/) to manage different deployment environments * Explore [Custom Storage](/docs/live-updates/custom-storage/) for advanced deployment scenarios * Set up [Encryption](/docs/live-updates/encryption/) for secure deployments * Configure [Update Behavior](/docs/live-updates/update-behavior/) to customize how updates are applied With Bitbucket Pipelines integration, you can automate your Capgo deployments and ensure consistent, reliable updates to your mobile app users. # GitHub Actions Integration > Learn how to integrate Capgo Live Updates with GitHub Actions for automated deployment of your app updates. Integrate Capgo Live Updates with GitHub Actions to automatically deploy your app updates whenever you push code changes. This guide covers setting up automated builds, testing, and deployment workflows using GitHub’s powerful CI/CD platform. ## Prerequisites [Section titled “Prerequisites”](#prerequisites) Before setting up GitHub Actions integration, ensure you have: * A GitHub repository with your app’s source code * A Capgo account with an app configured * Node.js and npm/yarn configured in your project * GitHub Actions enabled for your repository ## Setting Up GitHub Secrets [Section titled “Setting Up GitHub Secrets”](#setting-up-github-secrets) ### Step 1: Configure Repository Secrets [Section titled “Step 1: Configure Repository Secrets”](#step-1-configure-repository-secrets) Set up the necessary secrets in your GitHub repository: 1. Navigate to your GitHub repository 2. Go to **Settings** → **Secrets and variables** → **Actions** 3. Click **New repository secret** and add the following: | Secret Name | Value | | ------------- | -------------------- | | `CAPGO_TOKEN` | Your Capgo API token | Tip Get your Capgo API token from [console.capgo.app/apikeys](https://console.capgo.app/apikeys). Your app ID is already configured in your `capacitor.config.ts` file. ## Simple Production Deployment [Section titled “Simple Production Deployment”](#simple-production-deployment) Start with this basic configuration that deploys to production on every push to the main branch: ```yaml # Simple GitHub Actions Workflow for Capgo Live Updates name: Deploy to Capgo on: push: branches: - main jobs: deploy: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v6 - name: Setup Node.js uses: actions/setup-node@v6 with: node-version: '24' cache: 'npm' - name: Install, test and build run: | npm ci npm run test npm run build - name: Deploy to Capgo run: | npm install -g @capgo/cli npx @capgo/cli bundle upload --channel production env: CAPGO_TOKEN: ${{ secrets.CAPGO_TOKEN }} # For encrypted uploads, add: --key-data-v2 "${{ secrets.CAPGO_PRIVATE_KEY }}" ``` ## Advanced Multi-Channel Configuration [Section titled “Advanced Multi-Channel Configuration”](#advanced-multi-channel-configuration) ### Feature Branch Deployments [Section titled “Feature Branch Deployments”](#feature-branch-deployments) Deploy feature branches to temporary channels for testing: ```yaml # Feature branch deployment name: Deploy Feature Branch to Capgo on: push: branches: - 'feature/**' jobs: deploy-feature: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - uses: actions/setup-node@v6 with: node-version: '24' cache: 'npm' - run: | npm ci npm run test npm run build - name: Deploy to feature channel run: | CHANNEL_NAME=$(echo "${{ github.ref_name }}" | sed 's/[^a-zA-Z0-9]/-/g' | tr '[:upper:]' '[:lower:]') npm install -g @capgo/cli npx @capgo/cli channel create $CHANNEL_NAME --apikey ${{ secrets.CAPGO_TOKEN }} || true npx @capgo/cli bundle upload --apikey ${{ secrets.CAPGO_TOKEN }} --channel $CHANNEL_NAME ``` ### Using Encryption [Section titled “Using Encryption”](#using-encryption) If you’re using [Capgo’s encryption feature](/docs/live-updates/encryption/), you’ll need to store your private key securely in your CI/CD environment. After [setting up encryption keys](/docs/live-updates/encryption/#setting-up-encryption) locally, add your private key to GitHub secrets: ```shell # Display your private key content (copy this output) cat .capgo_key_v2 ``` Add this content as `CAPGO_PRIVATE_KEY` in your GitHub repository secrets, then use it in workflows: ```yaml # Deploy with encryption - name: Deploy to Capgo with Encryption run: | npm install -g @capgo/cli npx @capgo/cli bundle upload --apikey ${{ secrets.CAPGO_TOKEN }} --key-data-v2 "${{ secrets.CAPGO_PRIVATE_KEY }}" --channel production ``` Caution **Security Best Practices:** * Never commit the `.capgo_key_v2` file to version control * Store the private key only in secure CI/CD secret management * Use different keys for different environments ### Multi-Channel Configuration [Section titled “Multi-Channel Configuration”](#multi-channel-configuration) For comprehensive information about setting up and managing multiple deployment channels, see the [Channels documentation](/docs/live-updates/channels/). Complete workflow with development, pull requests, and production deployments: ```yaml # Complete multi-environment workflow name: Deploy to Capgo on: push: branches: [main, develop] pull_request: branches: [main, develop] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - uses: actions/setup-node@v6 with: node-version: '24' cache: 'npm' - run: | npm ci npm run test npm run build - uses: actions/upload-artifact@v4 with: name: dist path: dist/ deploy-development: if: github.ref == 'refs/heads/develop' needs: build runs-on: ubuntu-latest environment: development steps: - uses: actions/setup-node@v6 with: node-version: '24' - uses: actions/download-artifact@v4 with: name: dist path: dist/ - run: | npm install -g @capgo/cli npx @capgo/cli bundle upload --apikey ${{ secrets.CAPGO_TOKEN }} --channel development deploy-pr: if: github.event_name == 'pull_request' needs: build runs-on: ubuntu-latest steps: - uses: actions/setup-node@v6 with: node-version: '24' - uses: actions/download-artifact@v4 with: name: dist path: dist/ - name: Deploy to PR channel run: | CHANNEL_NAME="pr-${{ github.event.number }}" npm install -g @capgo/cli npx @capgo/cli channel create $CHANNEL_NAME --apikey ${{ secrets.CAPGO_TOKEN }} || true npx @capgo/cli bundle upload --apikey ${{ secrets.CAPGO_TOKEN }} --channel $CHANNEL_NAME - name: Comment PR uses: actions/github-script@v7 with: script: | github.rest.issues.createComment({ issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo, body: `🚀 This PR has been deployed to Capgo channel: \`pr-${{ github.event.number }}\`\n\nTo test this update in your app, configure it to use this channel. [Learn how to configure channels →](/docs/live-updates/channels/#configuring-the-channel-in-your-app)` }) deploy-production: if: github.ref == 'refs/heads/main' needs: build runs-on: ubuntu-latest environment: production steps: - uses: actions/setup-node@v6 with: node-version: '24' - uses: actions/download-artifact@v4 with: name: dist path: dist/ - run: | npm install -g @capgo/cli npx @capgo/cli bundle upload --apikey ${{ secrets.CAPGO_TOKEN }} --channel production ``` Tip **Testing with Channels**: After deploying to a PR or development channel, you can test the update in your app by configuring it to use that specific channel. Learn more about [configuring channels in your app](/docs/live-updates/channels/#configuring-the-channel-in-your-app). ### Cleanup Feature Channels [Section titled “Cleanup Feature Channels”](#cleanup-feature-channels) Automatically clean up feature channels when branches are deleted: ```yaml name: Cleanup Feature Channels on: delete: jobs: cleanup: runs-on: ubuntu-latest if: github.event.ref_type == 'branch' && startsWith(github.event.ref, 'feature/') steps: - uses: actions/setup-node@v6 with: node-version: '24' - name: Delete Capgo channel run: | CHANNEL_NAME=$(echo "${{ github.event.ref }}" | sed 's/[^a-zA-Z0-9]/-/g' | tr '[:upper:]' '[:lower:]') npm install -g @capgo/cli npx @capgo/cli channel delete $CHANNEL_NAME --apikey ${{ secrets.CAPGO_TOKEN }} || true ``` ## Security and Best Practices [Section titled “Security and Best Practices”](#security-and-best-practices) ### Environment Protection Rules [Section titled “Environment Protection Rules”](#environment-protection-rules) Set up environment protection rules in GitHub: 1. Go to **Settings** → **Environments** in your repository 2. Create environments: `development`, `staging`, `production` 3. For production environment, add: * **Required reviewers**: Add team members who must approve deployments * **Wait timer**: Add a delay before deployment (optional) * **Deployment branches**: Restrict to `main` branch only ### Secure Secrets Management [Section titled “Secure Secrets Management”](#secure-secrets-management) Use environment-specific secrets: ```yaml # Use different secrets per environment deploy-production: environment: production steps: - name: Deploy to Production run: | npx @capgo/cli bundle upload \ --apikey ${{ secrets.CAPGO_PROD_TOKEN }} \ --app ${{ secrets.CAPGO_PROD_APP_ID }} \ --channel production ``` ## Monitoring and Notifications [Section titled “Monitoring and Notifications”](#monitoring-and-notifications) ### Slack Integration [Section titled “Slack Integration”](#slack-integration) Add Slack notifications to your workflow: ```yaml name: Deploy with Notifications jobs: deploy: runs-on: ubuntu-latest steps: # ... deployment steps - name: Notify Slack on Success if: success() uses: 8398a7/action-slack@v3 with: status: success text: '✅ Capgo deployment successful!' fields: repo,message,commit,author,action,eventName,ref,workflow env: SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} - name: Notify Slack on Failure if: failure() uses: 8398a7/action-slack@v3 with: status: failure text: '❌ Capgo deployment failed!' fields: repo,message,commit,author,action,eventName,ref,workflow env: SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} ``` ### Discord Integration [Section titled “Discord Integration”](#discord-integration) Send notifications to Discord: ```yaml - name: Discord notification if: always() uses: Ilshidur/action-discord@master with: args: | Capgo deployment ${{ job.status }}! App: ${{ secrets.CAPGO_APP_ID }} Channel: ${{ github.ref_name }} Commit: ${{ github.sha }} env: DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }} ``` ### Email Notifications [Section titled “Email Notifications”](#email-notifications) Configure email notifications: ```yaml - name: Send email notification if: failure() uses: dawidd6/action-send-mail@v3 with: server_address: smtp.gmail.com server_port: 465 username: ${{ secrets.EMAIL_USERNAME }} password: ${{ secrets.EMAIL_PASSWORD }} subject: 'Capgo Deployment Failed - ${{ github.repository }}' to: team@yourcompany.com from: ci-cd@yourcompany.com body: | Deployment failed for ${{ github.repository }} Branch: ${{ github.ref_name }} Commit: ${{ github.sha }} Workflow: ${{ github.workflow }} ``` ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) ### Debug Workflow [Section titled “Debug Workflow”](#debug-workflow) Add debugging steps to troubleshoot issues: ```yaml - name: Debug environment run: | echo "Node version: $(node --version)" echo "NPM version: $(npm --version)" echo "Working directory: $(pwd)" echo "Files in dist/: $(ls -la dist/ || echo 'No dist directory')" echo "Environment variables:" env | grep -E "(GITHUB_|CAPGO_)" | sort - name: Test Capgo CLI run: | npx @capgo/cli --version npx @capgo/cli app debug --apikey ${{ secrets.CAPGO_TOKEN }} --app ${{ secrets.CAPGO_APP_ID }} ``` ### Common Issues and Solutions [Section titled “Common Issues and Solutions”](#common-issues-and-solutions) **Workflow fails with “CAPGO\_TOKEN not found”:** ```yaml - name: Verify secrets run: | if [ -z "${{ secrets.CAPGO_TOKEN }}" ]; then echo "ERROR: CAPGO_TOKEN secret is not set" exit 1 fi echo "CAPGO_TOKEN is set (length: ${#CAPGO_TOKEN})" env: CAPGO_TOKEN: ${{ secrets.CAPGO_TOKEN }} ``` **Build artifacts not found:** ```yaml - name: Debug artifacts run: | echo "Checking for build artifacts..." ls -la dist/ || echo "No dist directory found" find . -name "*.js" -o -name "*.html" | head -10 ``` **Network connectivity issues:** ```yaml - name: Test connectivity run: | ping -c 3 api.capgo.io || echo "Ping failed" curl -I https://api.capgo.io/health || echo "Health check failed" ``` ## Reusable Workflows [Section titled “Reusable Workflows”](#reusable-workflows) Create reusable workflows for consistency across projects: .github/workflows/reusable-capgo-deploy.yml ```yaml name: Reusable Capgo Deploy on: workflow_call: inputs: environment: required: true type: string channel: required: true type: string secrets: CAPGO_TOKEN: required: true CAPGO_APP_ID: required: true jobs: deploy: runs-on: ubuntu-latest environment: ${{ inputs.environment }} steps: - uses: actions/checkout@v6 - name: Setup Node.js uses: actions/setup-node@v6 with: node-version: '24' cache: 'npm' - name: Install and build run: | npm ci npm run build - name: Deploy to Capgo run: | npm install -g @capgo/cli npx @capgo/cli bundle upload \ --apikey ${{ secrets.CAPGO_TOKEN }} \ --app ${{ secrets.CAPGO_APP_ID }} \ --channel ${{ inputs.channel }} ``` Use the reusable workflow: .github/workflows/deploy.yml ```yaml name: Deploy App on: push: branches: [main, develop] jobs: deploy-dev: if: github.ref == 'refs/heads/develop' uses: ./.github/workflows/reusable-capgo-deploy.yml with: environment: development channel: development secrets: CAPGO_TOKEN: ${{ secrets.CAPGO_TOKEN }} CAPGO_APP_ID: ${{ secrets.CAPGO_APP_ID }} deploy-prod: if: github.ref == 'refs/heads/main' uses: ./.github/workflows/reusable-capgo-deploy.yml with: environment: production channel: production secrets: CAPGO_TOKEN: ${{ secrets.CAPGO_TOKEN }} CAPGO_APP_ID: ${{ secrets.CAPGO_APP_ID }} ``` ## Next Steps [Section titled “Next Steps”](#next-steps) * Learn about [Channels](/docs/live-updates/channels/) to manage different deployment environments * Explore [Custom Storage](/docs/live-updates/custom-storage/) for advanced deployment scenarios * Set up [Encryption](/docs/live-updates/encryption/) for secure deployments * Configure [Update Behavior](/docs/live-updates/update-behavior/) to customize how updates are applied With GitHub Actions integration, you can leverage GitHub’s powerful CI/CD platform to create sophisticated deployment workflows with built-in security, monitoring, and collaboration features for your Capgo Live Updates. # GitLab CI/CD Integration > Learn how to integrate Capgo Live Updates with GitLab CI/CD for automated deployment of your app updates. Integrate Capgo Live Updates with GitLab CI/CD to automatically deploy your app updates whenever you push code changes. This guide covers setting up automated builds, testing, and deployment workflows. ## Prerequisites [Section titled “Prerequisites”](#prerequisites) Before setting up GitLab CI/CD integration, ensure you have: * A GitLab account with a project repository * A Capgo account with an app configured * Node.js and npm/yarn configured in your project ## Setting Up GitLab CI/CD [Section titled “Setting Up GitLab CI/CD”](#setting-up-gitlab-cicd) ### Step 1: Configure Environment Variables [Section titled “Step 1: Configure Environment Variables”](#step-1-configure-environment-variables) First, set up the necessary variables in your GitLab project: 1. Navigate to your GitLab project 2. Go to **Settings** → **CI/CD** → **Variables** 3. Add the following variables: | Variable Name | Value | Protected | Masked | | ------------- | -------------------- | --------- | ------ | | `CAPGO_TOKEN` | Your Capgo API token | ✅ Yes | ✅ Yes | Tip Get your Capgo API token from [console.capgo.app/apikeys](https://console.capgo.app/apikeys). Your app ID is already configured in your `capacitor.config.ts` file. ## Simple [Section titled “Simple”](#simple) Basic configuration that deploys to production on every push to the main branch: ```yaml # .gitlab-ci.yml - Simple Configuration image: node:22 stages: - build - deploy variables: npm_config_cache: "$CI_PROJECT_DIR/.npm" build: stage: build script: - npm ci - npm run test - npm run build artifacts: paths: - dist/ expire_in: 1 hour only: - main deploy_production: stage: deploy script: - npm install -g @capgo/cli - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel production # For encrypted uploads, add: --key-data-v2 "$CAPGO_PRIVATE_KEY" dependencies: - build only: - main ``` ## Advanced [Section titled “Advanced”](#advanced) ### Feature Branch Deployments [Section titled “Feature Branch Deployments”](#feature-branch-deployments) Deploy feature branches to test channels for review and testing: ```yaml # Feature branch deployment deploy_feature: stage: deploy script: - npm install -g @capgo/cli - CHANNEL_NAME="feature-$(echo $CI_COMMIT_REF_NAME | sed 's/[^a-zA-Z0-9-]/-/g')" - npx @capgo/cli channel create $CHANNEL_NAME --apikey $CAPGO_TOKEN || true - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel $CHANNEL_NAME dependencies: - build only: - /^feature\/.*$/ environment: name: feature/$CI_COMMIT_REF_NAME url: https://your-app.com/channels/$CHANNEL_NAME ``` Tip **Testing with Channels**: After deploying to a feature channel, you can test the update in your app by configuring it to use that specific channel. Learn more about [configuring channels in your app](/docs/live-updates/channels/#configuring-the-channel-in-your-app). ### Using Encryption [Section titled “Using Encryption”](#using-encryption) If you’re using [Capgo’s encryption feature](/docs/live-updates/encryption/), you’ll need to store your private key securely in your CI/CD environment. After [setting up encryption keys](/docs/live-updates/encryption/#setting-up-encryption) locally, add your private key to GitLab variables: ```shell # Display your private key content (copy this output) cat .capgo_key_v2 ``` Add this content as `CAPGO_PRIVATE_KEY` in your GitLab project variables (mark as protected and masked), then use it in pipelines: ```yaml # Deploy with encryption deploy_production: script: - npm install -g @capgo/cli - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --key-data-v2 "$CAPGO_PRIVATE_KEY" --channel production ``` Caution **Security Best Practices:** * Never commit the `.capgo_key_v2` file to version control * Store the private key only in secure CI/CD secret management * Use different keys for different environments ### Multi-Channel Configuration [Section titled “Multi-Channel Configuration”](#multi-channel-configuration) For comprehensive information about setting up and managing multiple deployment channels, see the [Channels documentation](/docs/live-updates/channels/). Complete configuration with multiple environments and merge request deployments: ```yaml # .gitlab-ci.yml - Advanced Multi-Channel Configuration image: node:22 stages: - build - deploy variables: npm_config_cache: "$CI_PROJECT_DIR/.npm" # Build stage build: stage: build script: - npm ci - npm run test - npm run build artifacts: paths: - dist/ expire_in: 24 hours # Deploy to development channel deploy_development: stage: deploy script: - npm install -g @capgo/cli - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel development dependencies: - build only: - develop environment: name: development # Deploy merge requests to test channels deploy_mr: stage: deploy script: - npm install -g @capgo/cli - CHANNEL_NAME="mr-$CI_MERGE_REQUEST_IID" - npx @capgo/cli channel create $CHANNEL_NAME --apikey $CAPGO_TOKEN || true - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel $CHANNEL_NAME dependencies: - build only: - merge_requests environment: name: review/$CI_MERGE_REQUEST_IID url: https://your-app.com/channels/mr-$CI_MERGE_REQUEST_IID on_stop: cleanup_mr # Cleanup MR channels when MR is closed cleanup_mr: stage: deploy script: - npm install -g @capgo/cli - npx @capgo/cli channel delete mr-$CI_MERGE_REQUEST_IID --apikey $CAPGO_TOKEN || true when: manual environment: name: review/$CI_MERGE_REQUEST_IID action: stop only: - merge_requests # Deploy to staging deploy_staging: stage: deploy script: - npm install -g @capgo/cli - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel staging dependencies: - build only: - develop environment: name: staging # Deploy to production deploy_production: stage: deploy script: - npm install -g @capgo/cli - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel production dependencies: - build only: - main environment: name: production ``` ### Multi-Environment with Manual Approval [Section titled “Multi-Environment with Manual Approval”](#multi-environment-with-manual-approval) For production deployments requiring manual approval: ```yaml deploy_production: stage: deploy script: - npm install -g @capgo/cli - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel production dependencies: - build only: - main when: manual environment: name: production ``` ### Branch-Based Deployment Strategy [Section titled “Branch-Based Deployment Strategy”](#branch-based-deployment-strategy) Deploy different branches to appropriate channels automatically: ```yaml # Dynamic channel deployment based on branch deploy: stage: deploy script: - npm install -g @capgo/cli - | if [ "$CI_COMMIT_REF_NAME" = "main" ]; then CHANNEL="production" elif [ "$CI_COMMIT_REF_NAME" = "develop" ]; then CHANNEL="staging" else CHANNEL="development" fi - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel $CHANNEL dependencies: - build environment: name: $CHANNEL ``` ## Security Best Practices [Section titled “Security Best Practices”](#security-best-practices) ### Protected Variables [Section titled “Protected Variables”](#protected-variables) 1. **Mark Sensitive Variables**: Always mark API tokens as protected and masked 2. **Branch Protection**: Use protected variables for production deployments 3. **Access Control**: Limit variable access to maintainers only 4. **Regular Rotation**: Rotate API tokens regularly ### Secure Pipeline Configuration [Section titled “Secure Pipeline Configuration”](#secure-pipeline-configuration) ```yaml # Use protected variables for production deploy_production: stage: deploy script: - npm install -g @capgo/cli - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel production only: refs: - main variables: - $CI_COMMIT_REF_PROTECTED == "true" ``` ## Monitoring and Notifications [Section titled “Monitoring and Notifications”](#monitoring-and-notifications) ### Slack Integration [Section titled “Slack Integration”](#slack-integration) Add Slack notifications to your pipeline: ```yaml notify_success: stage: .post image: alpine:latest before_script: - apk add --no-cache curl script: - | curl -X POST -H 'Content-type: application/json' \ --data '{"text":"✅ Capgo deployment successful for '"$CI_COMMIT_REF_NAME"'"}' \ $SLACK_WEBHOOK_URL when: on_success notify_failure: stage: .post image: alpine:latest before_script: - apk add --no-cache curl script: - | curl -X POST -H 'Content-type: application/json' \ --data '{"text":"❌ Capgo deployment failed for '"$CI_COMMIT_REF_NAME"'"}' \ $SLACK_WEBHOOK_URL when: on_failure ``` ### Email Notifications [Section titled “Email Notifications”](#email-notifications) Configure email notifications in your GitLab project settings or use the API: ```yaml notify_email: stage: .post script: - | curl --request POST \ --header "PRIVATE-TOKEN: $GITLAB_API_TOKEN" \ --form "to=team@yourcompany.com" \ --form "subject=Capgo Deployment Status" \ --form "body=Deployment of $CI_COMMIT_REF_NAME completed with status: $CI_JOB_STATUS" \ "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/emails" when: always ``` ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) ### Common Issues [Section titled “Common Issues”](#common-issues) **Pipeline fails with “Capgo CLI not found”:** ```yaml # Debug CLI installation debug_cli: script: - npm install -g @capgo/cli - which capgo || echo "Capgo CLI not found" - npx @capgo/cli --version ``` **Authentication errors:** ```yaml # Verify token configuration debug_auth: script: - | if [ -z "$CAPGO_TOKEN" ]; then echo "CAPGO_TOKEN is not set" exit 1 fi echo "Token length: ${#CAPGO_TOKEN}" ``` **Build artifacts not found:** ```yaml # List build outputs debug_build: script: - ls -la dist/ - find dist/ -type f -name "*.js" -o -name "*.html" ``` ### Debug Pipeline [Section titled “Debug Pipeline”](#debug-pipeline) Add debugging information to troubleshoot issues: ```yaml debug: stage: build script: - echo "Branch: $CI_COMMIT_REF_NAME" - echo "Commit: $CI_COMMIT_SHA" - echo "Build: $CI_PIPELINE_ID" - env | grep CI_ | sort only: - branches ``` ## Next Steps [Section titled “Next Steps”](#next-steps) * Learn about [Channels](/docs/live-updates/channels/) to manage different deployment environments * Explore [Custom Storage](/docs/live-updates/custom-storage/) for advanced deployment scenarios * Set up [Encryption](/docs/live-updates/encryption/) for secure deployments * Configure [Update Behavior](/docs/live-updates/update-behavior/) to customize how updates are applied With GitLab CI/CD integration, you can automate your Capgo deployments and ensure consistent, reliable updates to your mobile app users. # Rollbacks > Learn how to manage rollbacks in Capgo, allowing you to revert to previous app versions seamlessly when needed. While Capgo’s live updates allow you to quickly deliver improvements and fixes to your users, there may be situations where you need to roll back to a previous version of your app. Perhaps a new update introduced an unexpected critical issue, or maybe you want to revert a specific change while you work on a fix. Capgo provides several ways to manage a channel’s builds and control the version of your app that users receive, including both manual rollback options and automatic safety mechanisms. ## Automatic Rollback Protection [Section titled “Automatic Rollback Protection”](#automatic-rollback-protection) Capgo includes a built-in safety mechanism to protect your users from broken updates. If a JavaScript error occurs before the `notifyAppReady()` method is called, the plugin will automatically roll back to the previous working version. ### How Automatic Rollback Works [Section titled “How Automatic Rollback Works”](#how-automatic-rollback-works) When a new update is downloaded and applied, Capgo expects your app to call `notifyAppReady()` within a configurable timeframe to confirm that the update loaded successfully. This method signals that: * The JavaScript bundle loaded without critical errors * Your app’s core functionality is working * The update is safe to keep If `notifyAppReady()` is not called due to a JavaScript crash or critical error, Capgo will: 1. Detect that the update failed to initialize properly 2. Automatically revert to the previous working bundle 3. Mark the problematic update as failed to prevent it from being applied again Tip Make sure to call `notifyAppReady()` in your app’s initialization code after your core components have loaded successfully. This ensures the automatic rollback protection works as intended. ```javascript import { CapacitorUpdater } from '@capgo/capacitor-updater' // Call this after your app has successfully initialized await CapacitorUpdater.notifyAppReady() ``` This automatic protection helps ensure that even if you accidentally push a broken update, your users won’t be stuck with a non-functional app. ### Configuring the Timeout [Section titled “Configuring the Timeout”](#configuring-the-timeout) You can configure how long Capgo waits for `notifyAppReady()` to be called by setting the `appReadyTimeout` in your Capacitor configuration: ```json { "plugins": { "CapacitorUpdater": { "appReadyTimeout": 10000 } } } ``` The `appReadyTimeout` value is specified in milliseconds. The default timeout is typically 10 seconds, but you can adjust this based on your app’s initialization requirements. If your app takes longer to load due to complex initialization processes, you may want to increase this value. ## Rolling Back to a Previous Bundle [Section titled “Rolling Back to a Previous Bundle”](#rolling-back-to-a-previous-bundle) Every time you upload a new build and assign it to a channel, Capgo keeps a history of those builds. If you need to revert a specific update, you can select one of these previous builds to redeploy to the channel. ![Rollback UI interface](/rollback_ui.webp) The primary way to roll back is through the rollback interface, which is located in the 4th tab (History) when viewing a channel in the Capgo Dashboard. This tab provides a comprehensive view of all available builds for the channel, allowing you to easily select and revert to any previous version. To roll back using the History tab: 1. Log in to the [Capgo Dashboard](https://app.capgo.io). 2. Navigate to the “Channels” section. 3. Click the name of the channel you want to roll back. 4. Go to the 4th tab (History) in the channel view. 5. Find the build you want to revert to in the build history. 6. Select that build to make it the active build for the channel. 7. Confirm that you want to roll back to this build. ### Alternative Method: Using the Crown Icon [Section titled “Alternative Method: Using the Crown Icon”](#alternative-method-using-the-crown-icon) As a second way, you can also roll back directly from the first tab by clicking the crown icon next to any build in the channel’s build history: 1. In the first tab of the channel view, find the build you want to revert to. 2. Click the crown icon next to that build to make it the active build for the channel. ![Channel management options](/select_bundle.webp) 3. Confirm that you want to roll back to this build. Note Rolling back to a previous build only affects the selected channel. If you have multiple channels (e.g. Production, Staging, etc.), you’ll need to repeat the rollback process for each affected channel. After rolling back, devices configured to listen to the updated channel will receive the previous build the next time they check for an update. The rolled-back build will be treated as a new update, so the usual update flow and conditions apply. ## Unlinking a Channel [Section titled “Unlinking a Channel”](#unlinking-a-channel) If you want to temporarily halt updates on a channel while you investigate an issue, you can unlink the channel from its current build. To unlink a channel: 1. Navigate to the channel in the Capgo Dashboard. 2. Click the “Unlink” button next to the current build. 3. Confirm that you want to unlink the channel. Once a channel is unlinked, it will not distribute any new updates. Devices configured to that channel will stay on their current build until the channel is linked to a build again. This is useful if you’ve identified a problem with an update but aren’t yet sure which build you want to roll back to. Unlinking the channel gives you time to investigate without pushing out further updates. ## Forcing the Built-In Bundle [Section titled “Forcing the Built-In Bundle”](#forcing-the-built-in-bundle) In more severe situations, you may want to revert all devices on a channel back to the web build that was originally packaged with your app’s native binary. This is known as the “built-in bundle”. To force the built-in bundle on a channel: 1. Navigate to the channel in the Capgo Dashboard. 2. Click the “Built-in Bundle” button. 3. Confirm that you want to force the built-in bundle. When you force the built-in bundle, all devices configured to that channel will revert back to the original packaged web build on their next update check. This happens regardless of what build they’re currently on. This is a more aggressive rollback option than reverting to a specific previous build, as it discards all live updates released since the app was last published to the app stores. Caution Be cautious when forcing the built-in bundle, as it will affect all devices on the channel. Make sure you’ve considered the impact and have a plan to move forward before taking this action. ## Monitoring and Responding to Issues [Section titled “Monitoring and Responding to Issues”](#monitoring-and-responding-to-issues) To catch issues quickly and minimize the impact of problematic updates, it’s important to have a plan for monitoring your releases and responding to problems. Some strategies include: * Monitoring crash reports and user feedback immediately after releasing an update * Using phased rollouts or a staged channel system to test updates on a smaller group before wide release * Having a clear decision process for when to roll back, unlink, or force the built-in bundle, and who has the authority to do so * Communicating to users about the issue and the resolution, if appropriate By combining careful monitoring with the ability to quickly manage problematic updates, you can deliver a continuously improving app experience while minimizing disruptions for your users. # Update Behavior > Explore the comprehensive update behavior of Capgo, designed to deliver seamless updates to your app users without interrupting their experience. When you release an update to your Capgo app, you probably want your users to receive that update as soon as possible. But you also don’t want to disrupt their experience by forcing them to wait for a download or restart the app in the middle of a session. Capgo’s update behavior is designed to strike a balance between delivering updates quickly and minimizing disruption to your users. ## Default Update Flow [Section titled “Default Update Flow”](#default-update-flow) By default, here’s how Capgo handles app updates: 1. On app launch, the Capgo plugin checks to see if a new update is available. 2. If an update is found, it’s downloaded in the background while the user continues using the current version of the app. 3. Once the download completes, Capgo waits for the user to either background the app or kill it entirely. 4. When the user next launches the app, they’ll be running the updated version. This flow ensures that users are always running the latest version of your app, without ever being interrupted by update prompts or forced to wait for downloads. Tip Capgo also checks for updates when the app resumes from the background, so users will receive updates even if they don’t fully quit the app. ## Why This Approach? [Section titled “Why This Approach?”](#why-this-approach) Applying updates on a background or kill event has a few key benefits for user experience: * Users aren’t interrupted by update prompts or forced to wait for downloads in the middle of a session. * Updates are applied seamlessly in between sessions, so the experience of launching the app is always fresh. * You can deliver updates frequently without worrying about disrupting active users. The main downside is that if a user backgrounds and quickly resumes your app, they may lose any unsaved state since the update was applied in between those actions. To mitigate this, we recommend: * Saving state frequently and restoring it gracefully when the app resumes. * Avoiding very frequent updates that modify large parts of the app state. * Considering customizing the update behavior for sensitive flows (see below). ## Customizing When Updates Are Applied [Section titled “Customizing When Updates Are Applied”](#customizing-when-updates-are-applied) In some cases, you may want more control over exactly when an update is applied. For example, you might want to ensure a user completes an in-progress flow before updating, or coordinate an app update with a server-side change. Capgo provides a `setDelay` function that lets you specify conditions that must be met before an update is installed: ```typescript import { CapacitorUpdater } from '@capgo/capacitor-updater'; await CapacitorUpdater.setMultiDelay({ delayConditions: [ { kind: 'date', value: '2023-06-01T00:00:00.000Z', }, { kind: 'background', value: '60000', }, ], }); ``` This example would delay installing an update until after June 1, 2023 AND the app has been backgrounded for at least 60 seconds. The available delay conditions are: * `date`: Wait until after a specific date/time to apply the update. * `background`: Wait a minimum duration after the app is backgrounded to apply the update. * `nativeVersion`: Wait for a native binary with a minimum version to be installed before applying the update. * `kill`: Wait until the next app kill event to apply the update. You can mix and match these conditions to precisely control when an update is installed. Danger Note that the `kill` condition currently triggers the update after the first kill event, not the next background event like the other conditions. This inconsistency will be fixed in a future release. ## Applying Updates Immediately [Section titled “Applying Updates Immediately”](#applying-updates-immediately) For critical updates or apps with very simple state, you may want to apply an update as soon as it’s downloaded, without waiting for a background or kill event. Capgo supports this via the `directUpdate` configuration option. Recommended: Use Delta (Manifest) Updates with Direct Update When using `directUpdate`, we **strongly recommend** enabling [Delta (manifest) Updates](/docs/live-updates/differentials/) to minimize download times and improve the user experience. Delta (manifest) updates only download changed files instead of the entire bundle, which is especially important when updates are applied immediately while users are actively using your app. **Why this matters for Direct Updates:** * **Faster updates**: Smaller downloads mean updates complete quickly, reducing the time users see loading screens * **Better mobile experience**: Users on cellular networks or slower connections won’t face long wait times * **Lower bandwidth usage**: Only changed files are downloaded, saving data for both you and your users When `directUpdate` is enabled in your `capacitor.config`, the CLI detects it. In non-interactive environments it sends Delta (manifest) updates automatically, and in interactive environments it prompts you to confirm before uploading. Use `--no-delta` to force a full bundle upload. To enable Delta (manifest) updates, simply use the `--delta` flag when uploading bundles: ```shell npx @capgo/cli@latest bundle upload --delta ``` Learn more in the [Delta (manifest) Updates documentation](/docs/live-updates/differentials/). `directUpdate` is set in your `capacitor.config.ts` file, not in JavaScript code. It supports three values: * `false` (default): Never do direct updates (use default behavior: download at start, set when backgrounded) * `'atInstall'`: Direct update only when app is installed, updated from store, otherwise act as directUpdate = false * `'onLaunch'`: Direct update only on app installed, updated from store or after app kill, otherwise act as directUpdate = false * `'always'`: Direct update in all previous cases (app installed, updated from store, after app kill or app resume), never act as directUpdate = false * `true` (deprecated): Same as `'always'` for backward compatibility ```typescript import { CapacitorConfig } from '@capacitor/cli'; const config: CapacitorConfig = { plugins: { CapacitorUpdater: { autoUpdate: true, directUpdate: 'always', // or 'atInstall' for updates only on app install/update autoSplashscreen: true, // NEW: Automatically handle splashscreen keepUrlPathAfterReload: true, }, SplashScreen: { launchAutoHide: false, // Still required when using directUpdate }, }, }; export default config; ``` Note **Important**: `directUpdate` only applies updates when the app actually checks for them. By default, this happens only at app startup or when resuming from background. Note that `periodCheckDelay` is not compatible with `directUpdate`. With `directUpdate` enabled, Capgo will immediately apply an update as soon as the download completes during an update check, even if the user is actively using the app. Without periodic checking enabled, this means updates will only be applied when the app starts or resumes from background. Note that because `directUpdate` is a native configuration, it requires some additional handling in your JavaScript code. Caution When using `directUpdate`, you must set `launchAutoHide: false` in the SplashScreen configuration (as shown above) to prevent the splash screen from hiding automatically. This ensures you have full control over when the splash screen is hidden after the update process completes. ## Automatic Splashscreen Handling [Section titled “Automatic Splashscreen Handling”](#automatic-splashscreen-handling) To make `directUpdate` easier to use, Capgo provides an `autoSplashscreen` option that automatically handles hiding the splashscreen for you (available since version 7.6.0): ```typescript const config: CapacitorConfig = { plugins: { CapacitorUpdater: { autoUpdate: true, directUpdate: 'always', // or 'atInstall' autoSplashscreen: true, // Automatically hide splashscreen keepUrlPathAfterReload: true, }, SplashScreen: { launchAutoHide: false, }, }, }; ``` When `autoSplashscreen` is enabled: * The plugin automatically hides the splashscreen when an update is applied * The plugin automatically hides the splashscreen when no update is needed * You don’t need to manually listen for `appReady` events or call `SplashScreen.hide()` ### Manual Splashscreen Handling [Section titled “Manual Splashscreen Handling”](#manual-splashscreen-handling) If you prefer manual control or need custom logic, you can disable `autoSplashscreen` and handle it yourself: ```js import { CapacitorUpdater } from '@capgo/capacitor-updater'; import { SplashScreen } from '@capacitor/splash-screen'; CapacitorUpdater.addListener('appReady', () => { // Hide splash screen SplashScreen.hide(); }); CapacitorUpdater.notifyAppReady(); ``` The `appReady` event fires once the app has finished initializing and applying any pending updates. This is the point at which it’s safe to show your app’s UI, as it ensures the user will see the latest version. In addition to handling the `appReady` event, we recommend setting the `keepUrlPathAfterReload` configuration option to `true` when using `directUpdate`. This preserves the current URL path when the app is reloaded due to an update, helping maintain the user’s location in the app and reducing disorientation. If you don’t handle the `appReady` event and set `keepUrlPathAfterReload` when using `directUpdate`, the user may briefly see a stale version of the app, be taken back to the initial route, or see a flicker as the update is applied. Using `directUpdate` can be useful for delivering critical bug fixes or security patches, but it comes with some tradeoffs: * The user may see a brief flicker or loading state as the update is applied if you don’t properly handle the splashscreen (either with `autoSplashscreen` or manual `appReady` event handling). * If the update modifies the app state or UI, the user may see a disruptive change in the middle of a session. * The user’s location in the app may be lost if `keepUrlPathAfterReload` is not set, potentially disorienting them. * You’ll need to carefully handle saving and restoring state to ensure a smooth transition. If you do enable `directUpdate`, we recommend: * Using `autoSplashscreen: true` for the simplest setup, or manually handling the `appReady` event if you need custom logic. * Setting `keepUrlPathAfterReload` to `true` to preserve the user’s location in the app. * Saving and restoring the app state as needed to avoid losing user progress. * Thoroughly testing your app’s update behavior to ensure there are no jarring transitions, lost state, or disorienting location changes. In most cases, the default update behavior provides the best balance of delivering updates quickly and minimizing disruption. But for apps with specific needs, Capgo provides the flexibility to customize when and how updates are applied. # Update Types > A comprehensive reference of all OTA update types Capgo provides: apply timing, delay conditions, version blocking, and delivery methods. Capgo supports several types of over-the-air (OTA) updates. This page lists and explains all of them so you can choose the right combination for your app. ## Apply Timing [Section titled “Apply Timing”](#apply-timing) Controls **when** an update is applied after it is downloaded. | Type | Description | Use Case | | ----------------------------- | ------------------------------------------------------------------------ | ----------------------------------------------------- | | **Default** | Download in background, apply when user backgrounds or kills the app | Most apps; minimal disruption | | **directUpdate: `atInstall`** | Apply immediately only on fresh install or app store update | New users get latest; existing users use default flow | | **directUpdate: `onLaunch`** | Apply immediately on install, store update, or after app kill | Balance between freshness and session stability | | **directUpdate: `always`** | Apply immediately whenever an update is downloaded (including on resume) | Critical fixes, apps with simple state | Configure in `capacitor.config.ts`: ```typescript plugins: { CapacitorUpdater: { directUpdate: false, // default // or: 'atInstall' | 'onLaunch' | 'always' } } ``` Tip For full details and splashscreen handling, see [Update Behavior](/docs/live-updates/update-behavior/). ## Delay Conditions [Section titled “Delay Conditions”](#delay-conditions) Conditions that must be met **before** an update is installed. Use `setMultiDelay` to combine them (all conditions must be satisfied). | Condition | Description | Example | | ----------------- | ------------------------------------------------------ | ----------------------------------------- | | **date** | Wait until after a specific date/time | Coordinate with server-side release | | **background** | Wait a minimum duration (ms) after app is backgrounded | Avoid applying during quick app switches | | **nativeVersion** | Require a minimum native binary version | Block updates on incompatible native code | | **kill** | Wait until the next app kill event | Apply only on full restart | ```typescript import { CapacitorUpdater } from '@capgo/capacitor-updater'; await CapacitorUpdater.setMultiDelay({ delayConditions: [ { kind: 'date', value: '2023-06-01T00:00:00.000Z' }, { kind: 'background', value: '60000' }, ], }); ``` Danger The `kill` condition triggers after the first kill event, not the next background like the others. This will be fixed in a future release. ## Version Blocking (Channel Policy) [Section titled “Version Blocking (Channel Policy)”](#version-blocking-channel-policy) Controls which **semver updates** a channel will auto-deliver. Set via `--disable-auto-update` on channels. | Strategy | Blocks | Allows | Use Case | | ------------ | ------------------------------------ | -------------------------------------------- | ------------------------------------------------- | | **none** | Nothing | All updates | Default; full auto-update | | **major** | 0.0.0 → 1.0.0 | Same major (e.g. 1.x → 1.y) | Prevent breaking changes from reaching old native | | **minor** | 0.0.0 → 1.1.0, 1.1.0 → 1.2.0 | Same minor (e.g. 1.2.x → 1.2.y) | Stricter control within major | | **patch** | Any change except patch bump | Only 0.0.311 → 0.0.314 | Very strict; patch-only updates | | **metadata** | Updates without `min_update_version` | Updates with explicit compatibility metadata | Custom compatibility rules per bundle | ```bash npx @capgo/cli channel set production --disable-auto-update major ``` Caution `patch` and `metadata` require careful setup. See [CLI commands](/docs/cli/commands/#disable-updates-strategy) and [Version Targeting](/docs/live-updates/version-targeting/) for details. ## Delivery Types [Section titled “Delivery Types”](#delivery-types) How the **bundle is transferred** to the device. | Type | Description | When to Use | | -------------------- | --------------------------------- | ---------------------------------------------------------- | | **Full bundle** | Entire JS bundle is downloaded | First install, large changes, or when delta is unavailable | | **Delta (manifest)** | Only changed files are downloaded | Most updates; faster and bandwidth-friendly | ```bash # Full bundle (default) npx @capgo/cli bundle upload --channel production # Delta updates npx @capgo/cli bundle upload --channel production --delta ``` Tip When using `directUpdate`, enable [Delta updates](/docs/live-updates/differentials/) to minimize download time and improve UX. ## Quick Reference [Section titled “Quick Reference”](#quick-reference) | Category | Types | | -------------------- | --------------------------------------------- | | **Apply timing** | Default, `atInstall`, `onLaunch`, `always` | | **Delay conditions** | `date`, `background`, `nativeVersion`, `kill` | | **Version blocking** | `none`, `major`, `minor`, `patch`, `metadata` | | **Delivery** | Full bundle, Delta (manifest) | ## Related [Section titled “Related”](#related) * [Update Behavior](/docs/live-updates/update-behavior/) — Configure apply timing and delays * [Version Targeting](/docs/live-updates/version-targeting/) — Channel-based version routing * [Delta (manifest) Updates](/docs/live-updates/differentials/) — Enable partial downloads * [Channels](/docs/live-updates/channels/) — Channel configuration and precedence # Version Targeting > Automatically deliver compatible updates to users based on their native app version This guide explains how to automatically deliver the latest compatible bundle to users based on their native app version, **similar to Ionic AppFlow’s approach**. This ensures simplified update management and faster rollouts while preventing compatibility issues. Migrating from Ionic AppFlow? If you’re coming from Ionic AppFlow, this guide is especially important for you. AppFlow automatically matched updates to native versions, and Capgo provides the same capability with even more control and flexibility. See the [AppFlow Migration Guide](/docs/upgrade/from-appflow-to-capgo) for step-by-step migration instructions. ## Overview [Section titled “Overview”](#overview) Capgo’s version targeting system allows you to: * **Automatically deliver compatible updates** to users based on their native app version * **Prevent breaking changes** from reaching incompatible app versions * **Manage multiple app versions** simultaneously without complex logic * **Seamlessly roll out updates** to specific user segments ### Why Version Targeting Matters (Especially for AppFlow Users) [Section titled “Why Version Targeting Matters (Especially for AppFlow Users)”](#why-version-targeting-matters-especially-for-appflow-users) If you’re familiar with **Ionic AppFlow**, you know how critical it is to ensure users receive only compatible updates. AppFlow automatically matched live update bundles to native app versions, preventing incompatible JavaScript from being delivered to older native code. **Capgo provides the same safety guarantees**, with additional features: * More granular control over version matching * Multiple strategies (channels, semver, native constraints) * Better visibility into version distribution * API and CLI control alongside dashboard management This approach is particularly useful when: * You have users on different major versions of your app (e.g., v1.x, v2.x, v3.x) * You need to maintain backward compatibility while rolling out breaking changes * You want to prevent newer bundles from breaking older native code * You’re migrating users gradually from one version to another * **You’re migrating from AppFlow** and want to maintain the same update safety ## How It Works [Section titled “How It Works”](#how-it-works) Capgo uses a multi-layered approach to match users with compatible updates: 1. **Native Version Constraints**: Prevent bundles from being delivered to incompatible native versions 2. **Channel-Based Routing**: Route different app versions to different update channels 3. **Semantic Versioning Controls**: Automatically block updates across major/minor/patch boundaries 4. **Device-Level Overrides**: Target specific devices or user groups ### Version Matching Flow [Section titled “Version Matching Flow”](#version-matching-flow) ```mermaid graph TD A[User Opens App] --> B{Check Device Override} B -->|Override Set| C[Use Override Channel] B -->|No Override| D{Check defaultChannel in App} D -->|Has defaultChannel| E[Use App's defaultChannel] D -->|No defaultChannel| F[Use Cloud Default Channel] C --> G{Check Version Constraints} E --> G F --> G G -->|Compatible| H[Deliver Update] G -->|Incompatible| I[Skip Update] ``` ## Strategy 1: Channel-Based Version Routing [Section titled “Strategy 1: Channel-Based Version Routing”](#strategy-1-channel-based-version-routing) This is the **recommended approach** for managing breaking changes and major version updates. It’s similar to AppFlow’s delivery model. ### Example Scenario [Section titled “Example Scenario”](#example-scenario) * **App v1.x** (100,000 users) → `production` channel * **App v2.x** (50,000 users with breaking changes) → `v2` channel * **App v3.x** (10,000 beta users) → `v3` channel ### Implementation [Section titled “Implementation”](#implementation) #### Step 1: Configure Channels for Each Major Version [Section titled “Step 1: Configure Channels for Each Major Version”](#step-1-configure-channels-for-each-major-version) ```typescript // capacitor.config.ts for version 1.x builds import { CapacitorConfig } from '@capacitor/cli'; const config: CapacitorConfig = { appId: 'com.example.app', appName: 'Example App', plugins: { CapacitorUpdater: { autoUpdate: true, defaultChannel: 'production', // or omit for default } } }; export default config; ``` ```typescript // capacitor.config.ts for version 2.x builds const config: CapacitorConfig = { appId: 'com.example.app', appName: 'Example App', plugins: { CapacitorUpdater: { autoUpdate: true, defaultChannel: 'v2', // Routes v2 users automatically } } }; ``` ```typescript // capacitor.config.ts for version 3.x builds const config: CapacitorConfig = { appId: 'com.example.app', appName: 'Example App', plugins: { CapacitorUpdater: { autoUpdate: true, defaultChannel: 'v3', // Routes v3 users automatically } } }; ``` #### Step 2: Create Channels [Section titled “Step 2: Create Channels”](#step-2-create-channels) ```bash # Create channels for each major version npx @capgo/cli channel create production npx @capgo/cli channel create v2 npx @capgo/cli channel create v3 # Enable self-assignment so apps can switch channels npx @capgo/cli channel set production --self-assign npx @capgo/cli channel set v2 --self-assign npx @capgo/cli channel set v3 --self-assign ``` #### Step 3: Upload Version-Specific Bundles [Section titled “Step 3: Upload Version-Specific Bundles”](#step-3-upload-version-specific-bundles) ```bash # For v1.x users (from v1-maintenance branch) git checkout v1-maintenance npm run build npx @capgo/cli bundle upload --channel production # For v2.x users (from v2-maintenance or main branch) git checkout main npm run build npx @capgo/cli bundle upload --channel v2 # For v3.x users (from beta/v3 branch) git checkout beta npm run build npx @capgo/cli bundle upload --channel v3 ``` Automatic Routing When users open the app, they automatically connect to their designated channel based on the `defaultChannel` in their installed app bundle. No JavaScript code changes required! ### Benefits [Section titled “Benefits”](#benefits) * **Zero code changes** - Channel routing happens automatically * **Clear separation** - Each version has its own update pipeline * **Flexible targeting** - Push updates to specific version groups * **Safe rollouts** - Breaking changes never reach incompatible versions ## Strategy 2: Semantic Versioning Controls [Section titled “Strategy 2: Semantic Versioning Controls”](#strategy-2-semantic-versioning-controls) Use Capgo’s built-in semantic versioning controls to prevent updates across version boundaries. ### Disable Auto-Update Across Major Versions [Section titled “Disable Auto-Update Across Major Versions”](#disable-auto-update-across-major-versions) ```bash # Create a channel that blocks major version updates npx @capgo/cli channel create stable --disable-auto-update major ``` This configuration means: * Users on app version **1.2.3** will receive updates up to **1.9.9** * Users will **NOT** receive version **2.0.0** automatically * Prevents breaking changes from reaching older native code ### Granular Control Options [Section titled “Granular Control Options”](#granular-control-options) ```bash # Block minor version updates (1.2.x won't get 1.3.0) npx @capgo/cli channel set stable --disable-auto-update minor # Block patch updates (1.2.3 won't get 1.2.4) npx @capgo/cli channel set stable --disable-auto-update patch # Allow all updates npx @capgo/cli channel set stable --disable-auto-update none ``` Semantic Versioning Required This strategy only works if you follow semantic versioning (semver) for your app versions. Ensure your version numbers follow the `MAJOR.MINOR.PATCH` format. ## Strategy 3: Native Version Constraints [Section titled “Strategy 3: Native Version Constraints”](#strategy-3-native-version-constraints) Specify minimum native version requirements for bundles to prevent delivery to incompatible devices. ### Using nativeVersion Delay Condition [Section titled “Using nativeVersion Delay Condition”](#using-nativeversion-delay-condition) When uploading a bundle, you can specify a minimum native version: ```bash # This bundle requires native version 2.0.0 or higher npx @capgo/cli bundle upload \ --channel production \ --native-version "2.0.0" ``` How It Works Devices on native version 1.x will NOT receive this bundle. Only devices on 2.0.0+ will get it. This is perfect for updates that require new native APIs or plugins. ### Use Cases [Section titled “Use Cases”](#use-cases) 1. **New Native Plugin Required** ```bash # Bundle needs Camera plugin added in v2.0.0 npx @capgo/cli bundle upload --native-version "2.0.0" ``` 2. **Breaking Native API Changes** ```bash # Bundle uses new Capacitor 6 APIs npx @capgo/cli bundle upload --native-version "3.0.0" ``` 3. **Gradual Migration** ```bash # Test bundle only on latest native version npx @capgo/cli bundle upload \ --channel beta \ --native-version "2.5.0" ``` ## Strategy 4: Auto-Downgrade Prevention [Section titled “Strategy 4: Auto-Downgrade Prevention”](#strategy-4-auto-downgrade-prevention) Prevent users from receiving bundles older than their current native version. ### Enable in Channel Settings [Section titled “Enable in Channel Settings”](#enable-in-channel-settings) In the Capgo dashboard: 1. Go to **Channels** → Select your channel 2. Enable **“Disable auto downgrade under native”** 3. Save changes Or via CLI: ```bash npx @capgo/cli channel set production --disable-downgrade ``` ### Example [Section titled “Example”](#example) * User’s device: Native version **1.2.5** * Channel bundle: Version **1.2.3** * **Result**: Update is blocked (would be a downgrade) This is useful when: * Users manually installed a newer version from the app store * You need to ensure users always have the latest security patches * You want to prevent regression bugs ## Strategy 5: Device-Level Targeting [Section titled “Strategy 5: Device-Level Targeting”](#strategy-5-device-level-targeting) Override channel assignment for specific devices or user groups. ### Force Specific Version for Testing [Section titled “Force Specific Version for Testing”](#force-specific-version-for-testing) ```typescript import { CapacitorUpdater } from '@capgo/capacitor-updater' // Force beta testers to use v3 channel async function assignBetaTesters() { const deviceId = await CapacitorUpdater.getDeviceId() // Check if user is beta tester if (isBetaTester(userId)) { await CapacitorUpdater.setChannel({ channel: 'v3' }) } } ``` ### Dashboard Device Override [Section titled “Dashboard Device Override”](#dashboard-device-override) In the Capgo dashboard: 1. Go to **Devices** → Find device 2. Click **Set Channel** or **Set Version** 3. Override with specific channel or bundle version 4. Device will receive updates from overridden source Testing Updates Use device overrides to test updates on your own device before rolling out to all users. ## Complete AppFlow-Style Workflow [Section titled “Complete AppFlow-Style Workflow”](#complete-appflow-style-workflow) Here’s a complete example combining all strategies: ### 1. Initial Setup (App v1.0.0) [Section titled “1. Initial Setup (App v1.0.0)”](#1-initial-setup-app-v100) ```bash # Create production channel with semver controls npx @capgo/cli channel create production \ --disable-auto-update major \ --disable-downgrade ``` capacitor.config.ts ```typescript const config: CapacitorConfig = { plugins: { CapacitorUpdater: { autoUpdate: true, defaultChannel: 'production', } } }; ``` ### 2. Release Breaking Change (App v2.0.0) [Section titled “2. Release Breaking Change (App v2.0.0)”](#2-release-breaking-change-app-v200) ```bash # Create v2 channel for new version npx @capgo/cli channel create v2 \ --disable-auto-update major \ --disable-downgrade \ --self-assign # Create git branch for v1 maintenance git checkout -b v1-maintenance git push origin v1-maintenance ``` ```typescript // capacitor.config.ts for v2.0.0 const config: CapacitorConfig = { plugins: { CapacitorUpdater: { autoUpdate: true, defaultChannel: 'v2', // New users get v2 channel } } }; ``` ### 3. Push Updates to Both Versions [Section titled “3. Push Updates to Both Versions”](#3-push-updates-to-both-versions) ```bash # Update v1.x users (bug fix) git checkout v1-maintenance # Make changes npx @capgo/cli bundle upload \ --channel production \ --native-version "1.0.0" # Update v2.x users (new feature) git checkout main # Make changes npx @capgo/cli bundle upload \ --channel v2 \ --native-version "2.0.0" ``` ### 4. Monitor Version Distribution [Section titled “4. Monitor Version Distribution”](#4-monitor-version-distribution) Use the Capgo dashboard to track: * How many users are on v1 vs v2 * Bundle adoption rates per version * Errors or crashes per version ### 5. Deprecate Old Version [Section titled “5. Deprecate Old Version”](#5-deprecate-old-version) Once v1 usage drops below threshold: ```bash # Stop uploading to production channel # Optional: Delete v1 maintenance branch git branch -d v1-maintenance # Move all remaining users to default # (They'll need to update via app store) ``` ## Channel Precedence [Section titled “Channel Precedence”](#channel-precedence) When multiple channel configurations exist, Capgo uses this precedence order: 1. **Device Override** (Dashboard or API) - Highest priority 2. **Cloud Override** via `setChannel()` call 3. **defaultChannel** in capacitor.config.ts 4. **Default Channel** (Cloud setting) - Lowest priority Precedence Example If a user’s app has `defaultChannel: 'v2'` but you override their device to `'beta'` in the dashboard, they’ll receive updates from the `'beta'` channel. ## Best Practices [Section titled “Best Practices”](#best-practices) ### 1. Always Set defaultChannel for Major Versions [Section titled “1. Always Set defaultChannel for Major Versions”](#1-always-set-defaultchannel-for-major-versions) ```typescript // ✅ Good: Each major version has explicit channel // v1.x → production // v2.x → v2 // v3.x → v3 // ❌ Bad: Relying on dynamic channel switching // All versions → production, switch manually ``` ### 2. Use Semantic Versioning [Section titled “2. Use Semantic Versioning”](#2-use-semantic-versioning) ```bash # ✅ Good 1.0.0 → 1.0.1 → 1.1.0 → 2.0.0 # ❌ Bad 1.0 → 1.1 → 2 → 2.5 ``` ### 3. Maintain Separate Branches [Section titled “3. Maintain Separate Branches”](#3-maintain-separate-branches) ```bash # ✅ Good: Separate branches per major version main (v3.x) v2-maintenance (v2.x) v1-maintenance (v1.x) # ❌ Bad: Single branch for all versions ``` ### 4. Test Before Rollout [Section titled “4. Test Before Rollout”](#4-test-before-rollout) ```bash # Test on beta channel first npx @capgo/cli bundle upload --channel beta # Monitor for issues, then promote to production npx @capgo/cli bundle upload --channel production ``` ### 5. Monitor Version Distribution [Section titled “5. Monitor Version Distribution”](#5-monitor-version-distribution) Regularly check your dashboard: * Are users upgrading to newer native versions? * Are old versions still getting high traffic? * Should you deprecate old channels? ## Comparison with Ionic AppFlow [Section titled “Comparison with Ionic AppFlow”](#comparison-with-ionic-appflow) For teams migrating from **Ionic AppFlow**, here’s how Capgo’s version targeting compares: | Feature | Ionic AppFlow | Capgo | | ------------------------------ | ----------------------------------------- | --------------------------------------------------------- | | **Version-based routing** | Automatic based on native version | Automatic via `defaultChannel` + multiple strategies | | **Semantic versioning** | Basic support | Advanced with `--disable-auto-update` (major/minor/patch) | | **Native version constraints** | Manual configuration in AppFlow dashboard | Built-in `--native-version` flag in CLI | | **Channel management** | Web UI + CLI | Web UI + CLI + API | | **Device overrides** | Limited device-level control | Full control via Dashboard/API | | **Auto-downgrade prevention** | Yes | Yes via `--disable-downgrade` | | **Multi-version maintenance** | Manual branch/channel management | Automated with channel precedence | | **Self-hosting** | No | Yes (full control) | | **Version analytics** | Basic | Detailed per-version metrics | AppFlow Parity and Beyond Capgo provides **all the version targeting capabilities** that AppFlow offered, plus additional control mechanisms. If you relied on AppFlow’s automatic version matching, you’ll find Capgo equally safe with more flexibility. ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) ### Users Not Receiving Updates [Section titled “Users Not Receiving Updates”](#users-not-receiving-updates) Check the following: 1. **Channel Assignment**: Verify device is on correct channel ```typescript const channel = await CapacitorUpdater.getChannel() console.log('Current channel:', channel) ``` 2. **Version Constraints**: Check if bundle has native version requirements * Dashboard → Bundles → Check “Native Version” column 3. **Semver Settings**: Verify channel’s `disable-auto-update` setting ```bash npx @capgo/cli channel list ``` 4. **Device Override**: Check if device has manual override * Dashboard → Devices → Search for device → Check channel/version ### Bundle Delivered to Wrong Version [Section titled “Bundle Delivered to Wrong Version”](#bundle-delivered-to-wrong-version) 1. **Review defaultChannel**: Ensure correct channel in `capacitor.config.ts` 2. **Check Bundle Upload**: Verify bundle was uploaded to intended channel 3. **Inspect Native Version**: Confirm `--native-version` flag was used correctly ### Breaking Changes Affecting Old Versions [Section titled “Breaking Changes Affecting Old Versions”](#breaking-changes-affecting-old-versions) 1. **Immediate Fix**: Override affected devices to safe bundle * Dashboard → Devices → Bulk select → Set Version 2. **Long-term Fix**: Create versioned channels and maintain separate branches 3. **Prevention**: Always test updates on representative devices before rollout ## Migration from Ionic AppFlow [Section titled “Migration from Ionic AppFlow”](#migration-from-ionic-appflow) If you’re migrating from **Ionic AppFlow**, version targeting works very similarly in Capgo, with improved flexibility: ### Concept Mapping [Section titled “Concept Mapping”](#concept-mapping) | AppFlow Concept | Capgo Equivalent | Notes | | ------------------------------ | ----------------------------------------------- | --------------------------------- | | **Deploy Channel** | Capgo Channel | Same concept, more powerful | | **Native Version Lock** | `--native-version` flag | More granular control | | **Channel Priority** | Channel precedence (override → cloud → default) | More transparent precedence | | **Deployment Target** | Channel + semver controls | Multiple strategies available | | **Production Channel** | `production` channel (or any name) | Flexible naming | | **Git-based deployment** | CLI bundle upload from branch | Same workflow | | **Automatic version matching** | `defaultChannel` + version constraints | Enhanced with multiple strategies | ### Key Differences for AppFlow Users [Section titled “Key Differences for AppFlow Users”](#key-differences-for-appflow-users) 1. **More Control**: Capgo gives you multiple strategies (channels, semver, native version) that can be combined 2. **Better Visibility**: Dashboard shows version distribution and compatibility issues 3. **API Access**: Full programmatic control over version targeting 4. **Self-Hosting**: Option to run your own update server with same version logic ### Migration Steps [Section titled “Migration Steps”](#migration-steps) 1. **Map your AppFlow channels** to Capgo channels (usually 1:1) 2. **Set `defaultChannel`** in `capacitor.config.ts` for each major version 3. **Configure semver rules** if you want automatic blocking at version boundaries 4. **Upload version-specific bundles** using `--native-version` flag 5. **Monitor version distribution** in Capgo dashboard Complete Migration Guide For complete migration instructions including SDK replacement and API mapping, see the [AppFlow to Capgo Migration Guide](/docs/upgrade/from-appflow-to-capgo). ## Advanced Patterns [Section titled “Advanced Patterns”](#advanced-patterns) ### Gradual Rollout by Version [Section titled “Gradual Rollout by Version”](#gradual-rollout-by-version) ```typescript // Gradually migrate v1 users to v2 async function migrateUsers() { const deviceId = await CapacitorUpdater.getDeviceId() const rolloutPercentage = 10 // Start with 10% // Hash device ID to get deterministic percentage const hash = hashCode(deviceId) % 100 if (hash < rolloutPercentage) { // User is in rollout group - migrate to v2 await CapacitorUpdater.setChannel({ channel: 'v2' }) } } ``` ### Feature Flags by Version [Section titled “Feature Flags by Version”](#feature-flags-by-version) ```typescript // Enable features based on native version async function checkFeatureAvailability() { const info = await CapacitorUpdater.getDeviceId() const nativeVersion = info.nativeVersion if (compareVersions(nativeVersion, '2.0.0') >= 0) { // Enable features requiring v2.0.0+ enableNewCameraFeature() } } ``` ### A/B Testing Across Versions [Section titled “A/B Testing Across Versions”](#ab-testing-across-versions) ```typescript // Run A/B tests within same native version async function assignABTest() { const nativeVersion = await getNativeVersion() if (nativeVersion.startsWith('2.')) { // Only A/B test on v2 users const variant = Math.random() < 0.5 ? 'v2-test-a' : 'v2-test-b' await CapacitorUpdater.setChannel({ channel: variant }) } } ``` ## Summary [Section titled “Summary”](#summary) Capgo provides multiple strategies for version-specific update delivery: 1. **Channel-Based Routing**: Automatic version separation via `defaultChannel` 2. **Semantic Versioning**: Prevent updates across major/minor/patch boundaries 3. **Native Version Constraints**: Require minimum native version for bundles 4. **Auto-Downgrade Prevention**: Never deliver older bundles to newer native versions 5. **Device Overrides**: Manual control for testing and targeting By combining these strategies, you can achieve AppFlow-style automatic update delivery with even more flexibility and control. Choose the approach that best fits your app’s versioning and deployment workflow. For more details on specific features: * [Breaking Changes Guide](/docs/live-updates/breaking-changes) - Detailed channel versioning strategy * [Channel Management](/docs/live-updates/channels) - Complete channel configuration reference * [Update Behavior](/docs/live-updates/update-behavior) - Native version delays and conditions # Functions and settings > All available method and settings of the plugin # Updater Plugin Config [Section titled “Updater Plugin Config”](#updater-plugin-config) CapacitorUpdater can be configured with these options: | Prop | Type | Description | Default | Since | | ------------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------ | ------------ | | **`appReadyTimeout`** | `number` | Configure the number of milliseconds the native plugin should wait before considering an update ‘failed’. Only available for Android and iOS. | `10000 // (10 seconds)` | | | **`responseTimeout`** | `number` | Configure the number of seconds the native plugin should wait before considering API timeout. Only available for Android and iOS. | `20 // (20 second)` | | | **`autoDeleteFailed`** | `boolean` | Configure whether the plugin should use automatically delete failed bundles. Only available for Android and iOS. | `true` | | | **`autoDeletePrevious`** | `boolean` | Configure whether the plugin should use automatically delete previous bundles after a successful update. Only available for Android and iOS. | `true` | | | **`autoUpdate`** | `boolean` | Configure whether the plugin should use Auto Update via an update server. Only available for Android and iOS. | `true` | | | **`resetWhenUpdate`** | `boolean` | Automatically delete previous downloaded bundles when a newer native app bundle is installed to the device. Setting this to false can broke the auto update flow if the user download from the store a native app bundle that is older than the current downloaded bundle. Upload will be prevented by channel setting downgrade\_under\_native. Only available for Android and iOS. | `true` | | | **`updateUrl`** | `string` | Configure the URL / endpoint to which update checks are sent. Only available for Android and iOS. | `https://plugin.capgo.app/updates` | | | **`channelUrl`** | `string` | Configure the URL / endpoint for channel operations. Only available for Android and iOS. | `https://plugin.capgo.app/channel_self` | | | **`statsUrl`** | `string` | Configure the URL / endpoint to which update statistics are sent. Only available for Android and iOS. Set to "" to disable stats reporting. | `https://plugin.capgo.app/stats` | | | **`publicKey`** | `string` | Configure the public key for end to end live update encryption Version 2 Only available for Android and iOS. | `undefined` | 6.2.0 | | **`version`** | `string` | Configure the current version of the app. This will be used for the first update request. If not set, the plugin will get the version from the native code. Only available for Android and iOS. | `undefined` | 4.17.48 | | **`directUpdate`** | \`boolean | ’always' | 'atInstall' | 'onLaunch’\` | | **`autoSplashscreen`** | `boolean` | Automatically handle splashscreen hiding when using directUpdate. When enabled, the plugin will automatically hide the splashscreen after updates are applied or when no update is needed. This removes the need to manually listen for appReady events and call SplashScreen.hide(). Only works when directUpdate is set to “atInstall”, “always”, “onLaunch”, or true. Requires the @capacitor/splash-screen plugin to be installed and configured with launchAutoHide: false. Requires autoUpdate and directUpdate to be enabled. Only available for Android and iOS. | `false` | 7.6.0 | | **`autoSplashscreenLoader`** | `boolean` | Display a native loading indicator on top of the splashscreen while automatic direct updates are running. Only takes effect when {@link autoSplashscreen} is enabled. Requires the @capacitor/splash-screen plugin to be installed and configured with launchAutoHide: false. Only available for Android and iOS. | `false` | 7.19.0 | | **`autoSplashscreenTimeout`** | `number` | Automatically hide the splashscreen after the specified number of milliseconds when using automatic direct updates. If the timeout elapses, the update continues to download in the background while the splashscreen is dismissed. Set to `0` (zero) to disable the timeout. When the timeout fires, the direct update flow is skipped and the downloaded bundle is installed on the next background/launch. Requires {@link autoSplashscreen} to be enabled. Only available for Android and iOS. | `10000 // (10 seconds)` | 7.19.0 | | **`periodCheckDelay`** | `number` | Configure the delay period for period update check. the unit is in seconds. Only available for Android and iOS. Cannot be less than 600 seconds (10 minutes). | `0 (disabled)` | | | **`localS3`** | `boolean` | Configure the CLI to use a local server for testing or self-hosted update server. | `undefined` | 4.17.48 | | **`localHost`** | `string` | Configure the CLI to use a local server for testing or self-hosted update server. | `undefined` | 4.17.48 | | **`localWebHost`** | `string` | Configure the CLI to use a local server for testing or self-hosted update server. | `undefined` | 4.17.48 | | **`localSupa`** | `string` | Configure the CLI to use a local server for testing or self-hosted update server. | `undefined` | 4.17.48 | | **`localSupaAnon`** | `string` | Configure the CLI to use a local server for testing. | `undefined` | 4.17.48 | | **`localApi`** | `string` | Configure the CLI to use a local api for testing. | `undefined` | 6.3.3 | | **`localApiFiles`** | `string` | Configure the CLI to use a local file api for testing. | `undefined` | 6.3.3 | | **`allowModifyUrl`** | `boolean` | Allow the plugin to modify the updateUrl, statsUrl and channelUrl dynamically from the JavaScript side. | `false` | 5.4.0 | | **`allowModifyAppId`** | `boolean` | Allow the plugin to modify the appId dynamically from the JavaScript side. | `false` | 7.14.0 | | **`allowManualBundleError`** | `boolean` | Allow marking bundles as errored from JavaScript while using manual update flows. When enabled, {@link CapacitorUpdaterPlugin.setBundleError} can change a bundle status to `error`. | `false` | 7.20.0 | | **`persistCustomId`** | `boolean` | Persist the customId set through {@link CapacitorUpdaterPlugin.setCustomId} across app restarts. Only available for Android and iOS. | `false (will be true by default in a future major release v8.x.x)` | 7.17.3 | | **`persistModifyUrl`** | `boolean` | Persist the updateUrl, statsUrl and channelUrl set through {@link CapacitorUpdaterPlugin.setUpdateUrl}, {@link CapacitorUpdaterPlugin.setStatsUrl} and {@link CapacitorUpdaterPlugin.setChannelUrl} across app restarts. Only available for Android and iOS. | `false` | 7.20.0 | | **`allowSetDefaultChannel`** | `boolean` | Allow or disallow the {@link CapacitorUpdaterPlugin.setChannel} method to modify the defaultChannel. When set to `false`, calling `setChannel()` will return an error with code `disabled_by_config`. | `true` | 7.34.0 | | **`defaultChannel`** | `string` | Set the default channel for the app in the config. Case sensitive. This will setting will override the default channel set in the cloud, but will still respect overrides made in the cloud. This requires the channel to allow devices to self dissociate/associate in the channel settings. | `undefined` | 5.5.0 | | **`appId`** | `string` | Configure the app id for the app in the config. | `undefined` | 6.0.0 | | **`keepUrlPathAfterReload`** | `boolean` | Configure the plugin to keep the URL path after a reload. WARNING: When a reload is triggered, ‘window\.history’ will be cleared. | `false` | 6.8.0 | | **`disableJSLogging`** | `boolean` | Disable the JavaScript logging of the plugin. if true, the plugin will not log to the JavaScript console. only the native log will be done | `false` | 7.3.0 | | **`osLogging`** | `boolean` | Enable OS-level logging. When enabled, logs are written to the system log which can be inspected in production builds. - **iOS**: Uses os\_log instead of Swift.print, logs accessible via Console.app or Instruments - **Android**: Logs to Logcat (android.util.Log) When set to false, system logging is disabled on both platforms (only JavaScript console logging will occur if enabled). This is useful for debugging production apps (App Store/TestFlight builds on iOS, or production APKs on Android). | `true` | 8.42.0 | | **`shakeMenu`** | `boolean` | Enable shake gesture to show update menu for debugging/testing purposes | `false` | 7.5.0 | | **`allowShakeChannelSelector`** | `boolean` | Enable the shake gesture to show a channel selector menu for switching between update channels. When enabled AND `shakeMenu` is true, the shake gesture shows a channel selector instead of the default debug menu (Go Home/Reload/Close). After selecting a channel, the app automatically checks for updates and downloads if available. Only works if channels have `allow_self_set` enabled on the backend. Only available for Android and iOS. | `false` | 8.43.0 | ## API Reference [Section titled “API Reference”](#api-reference) * [`notifyAppReady`](#notifyappready) * [`setUpdateUrl`](#setupdateurl) * [`setStatsUrl`](#setstatsurl) * [`setChannelUrl`](#setchannelurl) * [`download`](#download) * [`next`](#next) * [`set`](#set) * [`delete`](#delete) * [`setBundleError`](#setbundleerror) * [`list`](#list) * [`reset`](#reset) * [`current`](#current) * [`reload`](#reload) * [`setMultiDelay`](#setmultidelay) * [`cancelDelay`](#canceldelay) * [`getLatest`](#getlatest) * [`setChannel`](#setchannel) * [`unsetChannel`](#unsetchannel) * [`getChannel`](#getchannel) * [`listChannels`](#listchannels) * [`setCustomId`](#setcustomid) * [`getBuiltinVersion`](#getbuiltinversion) * [`getDeviceId`](#getdeviceid) * [`getPluginVersion`](#getpluginversion) * [`isAutoUpdateEnabled`](#isautoupdateenabled) * [`removeAllListeners`](#removealllisteners) * [`addListener('download')`](#addlistenerdownload-) * [`addListener('noNeedUpdate')`](#addlistenernoneedupdate-) * [`addListener('updateAvailable')`](#addlistenerupdateavailable-) * [`addListener('downloadComplete')`](#addlistenerdownloadcomplete-) * [`addListener('breakingAvailable')`](#addlistenerbreakingavailable-) * [`addListener('majorAvailable')`](#addlistenermajoravailable-) * [`addListener('updateFailed')`](#addlistenerupdatefailed-) * [`addListener('downloadFailed')`](#addlistenerdownloadfailed-) * [`addListener('appReloaded')`](#addlistenerappreloaded-) * [`addListener('appReady')`](#addlistenerappready-) * [`addListener('channelPrivate')`](#addlistenerchannelprivate-) * [`addListener('onFlexibleUpdateStateChange')`](#addlisteneronflexibleupdatestatechange-) * [`isAutoUpdateAvailable`](#isautoupdateavailable) * [`getNextBundle`](#getnextbundle) * [`getFailedUpdate`](#getfailedupdate) * [`setShakeMenu`](#setshakemenu) * [`isShakeMenuEnabled`](#isshakemenuenabled) * [`setShakeChannelSelector`](#setshakechannelselector) * [`isShakeChannelSelectorEnabled`](#isshakechannelselectorenabled) * [`getAppId`](#getappid) * [`setAppId`](#setappid) * [`getAppUpdateInfo`](#getappupdateinfo) * [`openAppStore`](#openappstore) * [`performImmediateUpdate`](#performimmediateupdate) * [`startFlexibleUpdate`](#startflexibleupdate) * [`completeFlexibleUpdate`](#completeflexibleupdate) ### notifyAppReady [Section titled “notifyAppReady”](#notifyappready) ```typescript notifyAppReady() => Promise ``` Notify the native layer that JavaScript initialized successfully. **CRITICAL: You must call this method on every app launch to prevent automatic rollback.** This is a simple notification to confirm that your bundle’s JavaScript loaded and executed. The native web server successfully served the bundle files and your JS runtime started. That’s all it checks - nothing more complex. **What triggers rollback:** * NOT calling this method within the timeout (default: 10 seconds) * Complete JavaScript failure (bundle won’t load at all) **What does NOT trigger rollback:** * Runtime errors after initialization (API failures, crashes, etc.) * Network request failures * Application logic errors **IMPORTANT: Call this BEFORE any network requests.** Don’t wait for APIs, data loading, or async operations. Call it as soon as your JavaScript bundle starts executing to confirm the bundle itself is valid. Best practices: * Call immediately in your app entry point (main.js, app component mount, etc.) * Don’t put it after network calls or heavy initialization * Don’t wrap it in try/catch with conditions * Adjust {@link PluginsConfig.CapacitorUpdater.appReadyTimeout} if you need more time **Returns** `Promise` — Always resolves successfully with current bundle info. This method never fails. *** ### setUpdateUrl [Section titled “setUpdateUrl”](#setupdateurl) ```typescript setUpdateUrl(options: UpdateUrl) => Promise ``` Set the update URL for the app dynamically at runtime. This overrides the {@link PluginsConfig.CapacitorUpdater.updateUrl} config value. Requires {@link PluginsConfig.CapacitorUpdater.allowModifyUrl} to be set to `true`. Use {@link PluginsConfig.CapacitorUpdater.persistModifyUrl} to persist this value across app restarts. Otherwise, the URL will reset to the config value on next app launch. **Parameters** | Name | Type | Description | | --------- | ----------- | ------------------------------------------------- | | `options` | `UpdateUrl` | Contains the URL to use for checking for updates. | **Returns** `Promise` — Resolves when the URL is successfully updated. **Since:** 5.4.0 **Throws:** {Error} If `allowModifyUrl` is false or if the operation fails. *** ### setStatsUrl [Section titled “setStatsUrl”](#setstatsurl) ```typescript setStatsUrl(options: StatsUrl) => Promise ``` Set the statistics URL for the app dynamically at runtime. This overrides the {@link PluginsConfig.CapacitorUpdater.statsUrl} config value. Requires {@link PluginsConfig.CapacitorUpdater.allowModifyUrl} to be set to `true`. Pass an empty string to disable statistics gathering entirely. Use {@link PluginsConfig.CapacitorUpdater.persistModifyUrl} to persist this value across app restarts. **Parameters** | Name | Type | Description | | --------- | ---------- | ------------------------------------------------------------------------------ | | `options` | `StatsUrl` | Contains the URL to use for sending statistics, or an empty string to disable. | **Returns** `Promise` — Resolves when the URL is successfully updated. **Since:** 5.4.0 **Throws:** {Error} If `allowModifyUrl` is false or if the operation fails. *** ### setChannelUrl [Section titled “setChannelUrl”](#setchannelurl) ```typescript setChannelUrl(options: ChannelUrl) => Promise ``` Set the channel URL for the app dynamically at runtime. This overrides the {@link PluginsConfig.CapacitorUpdater.channelUrl} config value. Requires {@link PluginsConfig.CapacitorUpdater.allowModifyUrl} to be set to `true`. Use {@link PluginsConfig.CapacitorUpdater.persistModifyUrl} to persist this value across app restarts. Otherwise, the URL will reset to the config value on next app launch. **Parameters** | Name | Type | Description | | --------- | ------------ | ----------------------------------------------- | | `options` | `ChannelUrl` | Contains the URL to use for channel operations. | **Returns** `Promise` — Resolves when the URL is successfully updated. **Since:** 5.4.0 **Throws:** {Error} If `allowModifyUrl` is false or if the operation fails. *** ### download [Section titled “download”](#download) ```typescript download(options: DownloadOptions) => Promise ``` Download a new bundle from the provided URL for later installation. The downloaded bundle is stored locally but not activated. To use it: * Call {@link next} to set it for installation on next app backgrounding/restart * Call {@link set} to activate it immediately (destroys current JavaScript context) The URL should point to a zip file containing either: * Your app files directly in the zip root, or * A single folder containing all your app files The bundle must include an `index.html` file at the root level. For encrypted bundles, provide the `sessionKey` and `checksum` parameters. For multi-file delta updates, provide the `manifest` array. **Parameters** | Name | Type | Description | | --------- | ----------------- | ------------------------------------------------------------- | | `options` | `DownloadOptions` | The {@link DownloadOptions} for downloading a new bundle zip. | **Returns** `Promise` — The {@link BundleInfo} for the downloaded bundle. **Throws:** {Error} If the download fails or the bundle is invalid. **Example** ```ts const bundle = await CapacitorUpdater.download({ url: `https://example.com/versions/${version}/dist.zip`, version: version }); // Bundle is downloaded but not active yet await CapacitorUpdater.next({ id: bundle.id }); // Will activate on next background ``` *** ### next [Section titled “next”](#next) ```typescript next(options: BundleId) => Promise ``` Set the next bundle to be activated when the app backgrounds or restarts. This is the recommended way to apply updates as it doesn’t interrupt the user’s current session. The bundle will be activated when: * The app is backgrounded (user switches away), or * The app is killed and relaunched, or * {@link reload} is called manually Unlike {@link set}, this method does NOT destroy the current JavaScript context immediately. Your app continues running normally until one of the above events occurs. Use {@link setMultiDelay} to add additional conditions before the update is applied. **Parameters** | Name | Type | Description | | --------- | ---------- | ------------------------------------------------------------------------------------------------- | | `options` | `BundleId` | Contains the ID of the bundle to set as next. Use {@link BundleInfo.id} from a downloaded bundle. | **Returns** `Promise` — The {@link BundleInfo} for the specified bundle. **Throws:** {Error} When there is no index.html file inside the bundle folder or the bundle doesn’t exist. *** ### set [Section titled “set”](#set) ```typescript set(options: BundleId) => Promise ``` Set the current bundle and immediately reloads the app. **IMPORTANT: This is a terminal operation that destroys the current JavaScript context.** When you call this method: * The entire JavaScript context is immediately destroyed * The app reloads from a different folder with different files * NO code after this call will execute * NO promises will resolve * NO callbacks will fire * Event listeners registered after this call are unreliable and may never fire The reload happens automatically - you don’t need to do anything else. If you need to preserve state like the current URL path, use the {@link PluginsConfig.CapacitorUpdater.keepUrlPathAfterReload} config option. For other state preservation needs, save your data before calling this method (e.g., to localStorage). **Do not** try to execute additional logic after calling `set()` - it won’t work as expected. **Parameters** | Name | Type | Description | | --------- | ---------- | ------------------------------------------------------------------------- | | `options` | `BundleId` | A {@link BundleId} object containing the new bundle id to set as current. | **Returns** `Promise` — A promise that will never resolve because the JavaScript context is destroyed. **Throws:** {Error} When there is no index.html file inside the bundle folder. *** ### delete [Section titled “delete”](#delete) ```typescript delete(options: BundleId) => Promise ``` Delete a bundle from local storage to free up disk space. You cannot delete: * The currently active bundle * The `builtin` bundle (the version shipped with your app) * The bundle set as `next` (call {@link next} with a different bundle first) Use {@link list} to get all available bundle IDs. **Note:** The bundle ID is NOT the same as the version name. Use the `id` field from {@link BundleInfo}, not the `version` field. **Parameters** | Name | Type | Description | | --------- | ---------- | ------------------------------------------------------------- | | `options` | `BundleId` | A {@link BundleId} object containing the bundle ID to delete. | **Returns** `Promise` — Resolves when the bundle is successfully deleted. **Throws:** {Error} If the bundle is currently in use or doesn’t exist. *** ### setBundleError [Section titled “setBundleError”](#setbundleerror) ```typescript setBundleError(options: BundleId) => Promise ``` Manually mark a bundle as failed/errored in manual update mode. This is useful when you detect that a bundle has critical issues and want to prevent it from being used again. The bundle status will be changed to `error` and the plugin will avoid using this bundle in the future. **Requirements:** * {@link PluginsConfig.CapacitorUpdater.allowManualBundleError} must be set to `true` * Only works in manual update mode (when autoUpdate is disabled) Common use case: After downloading and testing a bundle, you discover it has critical bugs and want to mark it as failed so it won’t be retried. **Parameters** | Name | Type | Description | | --------- | ---------- | ---------------------------------------------------------------------- | | `options` | `BundleId` | A {@link BundleId} object containing the bundle ID to mark as errored. | **Returns** `Promise` — The updated {@link BundleInfo} with status set to `error`. **Since:** 7.20.0 **Throws:** {Error} When the bundle does not exist or `allowManualBundleError` is false. *** ### list [Section titled “list”](#list) ```typescript list(options?: ListOptions | undefined) => Promise ``` Get all locally downloaded bundles stored in your app. This returns all bundles that have been downloaded and are available locally, including: * The currently active bundle * The `builtin` bundle (shipped with your app) * Any downloaded bundles waiting to be activated * Failed bundles (with `error` status) Use this to: * Check available disk space by counting bundles * Delete old bundles with {@link delete} * Monitor bundle download status **Parameters** | Name | Type | Description | | --------- | ------------- | ----------- | | `options` | \`ListOptions | undefined\` | **Returns** `Promise` — A promise containing the array of {@link BundleInfo} objects. **Throws:** {Error} If the operation fails. *** ### reset [Section titled “reset”](#reset) ```typescript reset(options?: ResetOptions | undefined) => Promise ``` Reset the app to a known good bundle. This method helps recover from problematic updates by reverting to either: * The `builtin` bundle (the original version shipped with your app to App Store/Play Store) * The last successfully loaded bundle (most recent bundle that worked correctly) **IMPORTANT: This triggers an immediate app reload, destroying the current JavaScript context.** See {@link set} for details on the implications of this operation. Use cases: * Emergency recovery when an update causes critical issues * Testing rollback functionality * Providing users a “reset to factory” option **Parameters** | Name | Type | Description | | --------- | -------------- | ----------- | | `options` | \`ResetOptions | undefined\` | **Returns** `Promise` — A promise that may never resolve because the app will be reloaded. **Throws:** {Error} If the reset operation fails. *** ### current [Section titled “current”](#current) ```typescript current() => Promise ``` Get information about the currently active bundle. Returns: * `bundle`: The currently active bundle information * `native`: The version of the builtin bundle (the original app version from App/Play Store) If no updates have been applied, `bundle.id` will be `"builtin"`, indicating the app is running the original version shipped with the native app. Use this to: * Display the current version to users * Check if an update is currently active * Compare against available updates * Log the active bundle for debugging **Returns** `Promise` — A promise with the current bundle and native version info. **Throws:** {Error} If the operation fails. *** ### reload [Section titled “reload”](#reload) ```typescript reload() => Promise ``` Manually reload the app to apply a pending update. This triggers the same reload behavior that happens automatically when the app backgrounds. If you’ve called {@link next} to queue an update, calling `reload()` will apply it immediately. **IMPORTANT: This destroys the current JavaScript context immediately.** See {@link set} for details on the implications of this operation. Common use cases: * Applying an update immediately after download instead of waiting for backgrounding * Providing a “Restart now” button to users after an update is ready * Testing update flows during development If no update is pending (no call to {@link next}), this simply reloads the current bundle. **Returns** `Promise` — A promise that may never resolve because the app will be reloaded. **Throws:** {Error} If the reload operation fails. *** ### setMultiDelay [Section titled “setMultiDelay”](#setmultidelay) ```typescript setMultiDelay(options: MultiDelayConditions) => Promise ``` Configure conditions that must be met before a pending update is applied. After calling {@link next} to queue an update, use this method to control when it gets applied. The update will only be installed after ALL specified conditions are satisfied. Available condition types: * `background`: Wait for the app to be backgrounded. Optionally specify duration in milliseconds. * `kill`: Wait for the app to be killed and relaunched (**Note:** Current behavior triggers update immediately on kill, not on next background. This will be fixed in v8.) * `date`: Wait until a specific date/time (ISO 8601 format) * `nativeVersion`: Wait until the native app is updated to a specific version Condition value formats: * `background`: Number in milliseconds (e.g., `"300000"` for 5 minutes), or omit for immediate * `kill`: No value needed * `date`: ISO 8601 date string (e.g., `"2025-12-31T23:59:59Z"`) * `nativeVersion`: Version string (e.g., `"2.0.0"`) **Parameters** | Name | Type | Description | | --------- | ---------------------- | -------------------------------------------------------------- | | `options` | `MultiDelayConditions` | Contains the {@link MultiDelayConditions} array of conditions. | **Returns** `Promise` — Resolves when the delay conditions are set. **Since:** 4.3.0 **Throws:** {Error} If the operation fails or conditions are invalid. **Example** ```ts // Update after user kills app OR after 5 minutes in background await CapacitorUpdater.setMultiDelay({ delayConditions: [ { kind: 'kill' }, { kind: 'background', value: '300000' } ] }); ``` **Example** ```ts // Update after a specific date await CapacitorUpdater.setMultiDelay({ delayConditions: [{ kind: 'date', value: '2025-12-31T23:59:59Z' }] }); ``` **Example** ```ts // Default behavior: update on next background await CapacitorUpdater.setMultiDelay({ delayConditions: [{ kind: 'background' }] }); ``` *** ### cancelDelay [Section titled “cancelDelay”](#canceldelay) ```typescript cancelDelay() => Promise ``` Cancel all delay conditions and apply the pending update immediately. If you’ve set delay conditions with {@link setMultiDelay}, this method clears them and triggers the pending update to be applied on the next app background or restart. This is useful when: * User manually requests to update now (e.g., clicks “Update now” button) * Your app detects it’s a good time to update (e.g., user finished critical task) * You want to override a time-based delay early **Returns** `Promise` — Resolves when the delay conditions are cleared. **Since:** 4.0.0 **Throws:** {Error} If the operation fails. *** ### getLatest [Section titled “getLatest”](#getlatest) ```typescript getLatest(options?: GetLatestOptions | undefined) => Promise ``` Check the update server for the latest available bundle version. This queries your configured update URL (or Capgo backend) to see if a newer bundle is available for download. It does NOT download the bundle automatically. The response includes: * `version`: The latest available version identifier * `url`: Download URL for the bundle (if available) * `breaking`: Whether this update is marked as incompatible (requires native app update) * `message`: Optional message from the server * `manifest`: File list for delta updates (if using multi-file downloads) After receiving the latest version info, you can: 1. Compare it with your current version 2. Download it using {@link download} 3. Apply it using {@link next} or {@link set} **Important: Error handling for “no new version available”** When the device’s current version matches the latest version on the server (i.e., the device is already up-to-date), the server returns a 200 response with `error: "no_new_version_available"` and `message: "No new version available"`. **This causes `getLatest()` to throw an error**, even though this is a normal, expected condition. You should catch this specific error to handle it gracefully: ```typescript try { const latest = await CapacitorUpdater.getLatest(); // New version is available, proceed with download } catch (error) { if (error.message === 'No new version available') { // Device is already on the latest version - this is normal console.log('Already up to date'); } else { // Actual error occurred console.error('Failed to check for updates:', error); } } ``` In this scenario, the server: * Logs the request with a “No new version available” message * Sends a “noNew” stat action to track that the device checked for updates but was already current (done on the backend) **Parameters** | Name | Type | Description | | --------- | ------------------ | ----------- | | `options` | \`GetLatestOptions | undefined\` | **Returns** `Promise` — Information about the latest available bundle version. **Since:** 4.0.0 **Throws:** {Error} Always throws when no new version is available (`error: "no_new_version_available"`), or when the request fails. *** ### setChannel [Section titled “setChannel”](#setchannel) ```typescript setChannel(options: SetChannelOptions) => Promise ``` Assign this device to a specific update channel at runtime. Channels allow you to distribute different bundle versions to different groups of users (e.g., “production”, “beta”, “staging”). This method switches the device to a new channel. **Requirements:** * The target channel must allow self-assignment (configured in your Capgo dashboard or backend) * The backend may accept or reject the request based on channel settings **When to use:** * After the app is ready and the user has interacted (e.g., opted into beta program) * To implement in-app channel switching (beta toggle, tester access, etc.) * For user-driven channel changes **When NOT to use:** * At app boot/initialization - use {@link PluginsConfig.CapacitorUpdater.defaultChannel} config instead * Before user interaction **Important: Listen for the `channelPrivate` event** When a user attempts to set a channel that doesn’t allow device self-assignment, the method will throw an error AND fire a {@link addListener}(‘channelPrivate’) event. You should listen to this event to provide appropriate feedback to users: ```typescript CapacitorUpdater.addListener('channelPrivate', (data) => { console.warn(`Cannot access channel "${data.channel}": ${data.message}`); // Show user-friendly message }); ``` This sends a request to the Capgo backend linking your device ID to the specified channel. **Parameters** | Name | Type | Description | | --------- | ------------------- | ------------------------------------------------------------------------------------------- | | `options` | `SetChannelOptions` | The {@link SetChannelOptions} containing the channel name and optional auto-update trigger. | **Returns** `Promise` — Channel operation result with status and optional error/message. **Since:** 4.7.0 **Throws:** {Error} If the channel doesn’t exist or doesn’t allow self-assignment. *** ### unsetChannel [Section titled “unsetChannel”](#unsetchannel) ```typescript unsetChannel(options: UnsetChannelOptions) => Promise ``` Remove the device’s channel assignment and return to the default channel. This unlinks the device from any specifically assigned channel, causing it to fall back to: * The {@link PluginsConfig.CapacitorUpdater.defaultChannel} if configured, or * Your backend’s default channel for this app Use this when: * Users opt out of beta/testing programs * You want to reset a device to standard update distribution * Testing channel switching behavior **Parameters** | Name | Type | Description | | --------- | --------------------- | ----------- | | `options` | `UnsetChannelOptions` | | **Returns** `Promise` — Resolves when the channel is successfully unset. **Since:** 4.7.0 **Throws:** {Error} If the operation fails. *** ### getChannel [Section titled “getChannel”](#getchannel) ```typescript getChannel() => Promise ``` Get the current channel assigned to this device. Returns information about: * `channel`: The currently assigned channel name (if any) * `allowSet`: Whether the channel allows self-assignment * `status`: Operation status * `error`/`message`: Additional information (if applicable) Use this to: * Display current channel to users (e.g., “You’re on the Beta channel”) * Check if a device is on a specific channel before showing features * Verify channel assignment after calling {@link setChannel} **Returns** `Promise` — The current channel information. **Since:** 4.8.0 **Throws:** {Error} If the operation fails. *** ### listChannels [Section titled “listChannels”](#listchannels) ```typescript listChannels() => Promise ``` Get a list of all channels available for this device to self-assign to. Only returns channels where `allow_self_set` is `true`. These are channels that users can switch to using {@link setChannel} without backend administrator intervention. Each channel includes: * `id`: Unique channel identifier * `name`: Human-readable channel name * `public`: Whether the channel is publicly visible * `allow_self_set`: Always `true` in results (filtered to only self-assignable channels) Use this to: * Build a channel selector UI for users (e.g., “Join Beta” button) * Show available testing/preview channels * Implement channel discovery features **Returns** `Promise` — List of channels the device can self-assign to. **Since:** 7.5.0 **Throws:** {Error} If the operation fails or the request to the backend fails. *** ### setCustomId [Section titled “setCustomId”](#setcustomid) ```typescript setCustomId(options: SetCustomIdOptions) => Promise ``` Set a custom identifier for this device. This allows you to identify devices by your own custom ID (user ID, account ID, etc.) instead of or in addition to the device’s unique hardware ID. The custom ID is sent to your update server and can be used for: * Targeting specific users for updates * Analytics and user tracking * Debugging and support (correlating devices with users) * A/B testing or feature flagging **Persistence:** * When {@link PluginsConfig.CapacitorUpdater.persistCustomId} is `true`, the ID persists across app restarts * When `false`, the ID is only kept for the current session **Clearing the custom ID:** * Pass an empty string `""` to remove any stored custom ID **Parameters** | Name | Type | Description | | --------- | -------------------- | ----------------------------------------------------------------------- | | `options` | `SetCustomIdOptions` | The {@link SetCustomIdOptions} containing the custom identifier string. | **Returns** `Promise` — Resolves immediately (synchronous operation). **Since:** 4.9.0 **Throws:** {Error} If the operation fails. *** ### getBuiltinVersion [Section titled “getBuiltinVersion”](#getbuiltinversion) ```typescript getBuiltinVersion() => Promise ``` Get the builtin bundle version (the original version shipped with your native app). This returns the version of the bundle that was included when the app was installed from the App Store or Play Store. This is NOT the currently active bundle version - use {@link current} for that. Returns: * The {@link PluginsConfig.CapacitorUpdater.version} config value if set, or * The native app version from platform configs (package.json, Info.plist, build.gradle) Use this to: * Display the “factory” version to users * Compare against downloaded bundle versions * Determine if any updates have been applied * Debugging version mismatches **Returns** `Promise` — The builtin bundle version string. **Since:** 5.2.0 *** ### getDeviceId [Section titled “getDeviceId”](#getdeviceid) ```typescript getDeviceId() => Promise ``` Get the unique, privacy-friendly identifier for this device. This ID is used to identify the device when communicating with update servers. It’s automatically generated and stored securely by the plugin. **Privacy & Security characteristics:** * Generated as a UUID (not based on hardware identifiers) * Stored securely in platform-specific secure storage * Android: Android Keystore (persists across app reinstalls on API 23+) * iOS: Keychain with `kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly` * Not synced to cloud (iOS) * Follows Apple and Google privacy best practices * Users can clear it via system settings (Android) or keychain access (iOS) **Persistence:** The device ID persists across app reinstalls to maintain consistent device identity for update tracking and analytics. Use this to: * Debug update delivery issues (check what ID the server sees) * Implement device-specific features * Correlate server logs with specific devices **Returns** `Promise` — The unique device identifier string. **Throws:** {Error} If the operation fails. *** ### getPluginVersion [Section titled “getPluginVersion”](#getpluginversion) ```typescript getPluginVersion() => Promise ``` Get the version of the Capacitor Updater plugin installed in your app. This returns the version of the native plugin code (Android/iOS), which is sent to the update server with each request. This is NOT your app version or bundle version. Use this to: * Debug plugin-specific issues (when reporting bugs) * Verify plugin installation and version * Check compatibility with backend features * Display in debug/about screens **Returns** `Promise` — The Capacitor Updater plugin version string. **Throws:** {Error} If the operation fails. *** ### isAutoUpdateEnabled [Section titled “isAutoUpdateEnabled”](#isautoupdateenabled) ```typescript isAutoUpdateEnabled() => Promise ``` Check if automatic updates are currently enabled. Returns `true` if {@link PluginsConfig.CapacitorUpdater.autoUpdate} is enabled, meaning the plugin will automatically check for, download, and apply updates. Returns `false` if in manual mode, where you control the update flow using {@link getLatest}, {@link download}, {@link next}, and {@link set}. Use this to: * Determine which update flow your app is using * Show/hide manual update UI based on mode * Debug update behavior **Returns** `Promise` — `true` if auto-update is enabled, `false` if in manual mode. **Throws:** {Error} If the operation fails. *** ### removeAllListeners [Section titled “removeAllListeners”](#removealllisteners) ```typescript removeAllListeners() => Promise ``` Remove all event listeners registered for this plugin. This unregisters all listeners added via {@link addListener} for all event types: * `download` * `noNeedUpdate` * `updateAvailable` * `downloadComplete` * `downloadFailed` * `breakingAvailable` / `majorAvailable` * `updateFailed` * `appReloaded` * `appReady` Use this during cleanup (e.g., when unmounting components or closing screens) to prevent memory leaks from lingering event listeners. **Returns** `Promise` — Resolves when all listeners are removed. **Since:** 1.0.0 *** ### addListener(‘download’) [Section titled “addListener(‘download’)”](#addlistenerdownload) ```typescript addListener(eventName: 'download', listenerFunc: (state: DownloadEvent) => void) => Promise ``` Listen for bundle download event in the App. Fires once a download has started, during downloading and when finished. This will return you all download percent during the download **Parameters** | Name | Type | Description | | -------------- | -------------------------------- | ----------- | | `eventName` | `'download'` | | | `listenerFunc` | `(state: DownloadEvent) => void` | | **Returns** `Promise` **Since:** 2.0.11 *** ### addListener(‘noNeedUpdate’) [Section titled “addListener(‘noNeedUpdate’)”](#addlistenernoneedupdate) ```typescript addListener(eventName: 'noNeedUpdate', listenerFunc: (state: NoNeedEvent) => void) => Promise ``` Listen for no need to update event, useful when you want force check every time the app is launched **Parameters** | Name | Type | Description | | -------------- | ------------------------------ | ----------- | | `eventName` | `'noNeedUpdate'` | | | `listenerFunc` | `(state: NoNeedEvent) => void` | | **Returns** `Promise` **Since:** 4.0.0 *** ### addListener(‘updateAvailable’) [Section titled “addListener(‘updateAvailable’)”](#addlistenerupdateavailable) ```typescript addListener(eventName: 'updateAvailable', listenerFunc: (state: UpdateAvailableEvent) => void) => Promise ``` Listen for available update event, useful when you want to force check every time the app is launched **Parameters** | Name | Type | Description | | -------------- | --------------------------------------- | ----------- | | `eventName` | `'updateAvailable'` | | | `listenerFunc` | `(state: UpdateAvailableEvent) => void` | | **Returns** `Promise` **Since:** 4.0.0 *** ### addListener(‘downloadComplete’) [Section titled “addListener(‘downloadComplete’)”](#addlistenerdownloadcomplete) ```typescript addListener(eventName: 'downloadComplete', listenerFunc: (state: DownloadCompleteEvent) => void) => Promise ``` Listen for downloadComplete events. **Parameters** | Name | Type | Description | | -------------- | ---------------------------------------- | ----------- | | `eventName` | `'downloadComplete'` | | | `listenerFunc` | `(state: DownloadCompleteEvent) => void` | | **Returns** `Promise` **Since:** 4.0.0 *** ### addListener(‘breakingAvailable’) [Section titled “addListener(‘breakingAvailable’)”](#addlistenerbreakingavailable) ```typescript addListener(eventName: 'breakingAvailable', listenerFunc: (state: BreakingAvailableEvent) => void) => Promise ``` Listen for breaking update events when the backend flags an update as incompatible with the current app. Emits the same payload as the legacy `majorAvailable` listener. **Parameters** | Name | Type | Description | | -------------- | -------------------------------------- | ----------- | | `eventName` | `'breakingAvailable'` | | | `listenerFunc` | `(state: MajorAvailableEvent) => void` | | **Returns** `Promise` **Since:** 7.22.0 *** ### addListener(‘majorAvailable’) [Section titled “addListener(‘majorAvailable’)”](#addlistenermajoravailable) ```typescript addListener(eventName: 'majorAvailable', listenerFunc: (state: MajorAvailableEvent) => void) => Promise ``` Listen for Major update event in the App, let you know when major update is blocked by setting disableAutoUpdateBreaking **Parameters** | Name | Type | Description | | -------------- | -------------------------------------- | ----------- | | `eventName` | `'majorAvailable'` | | | `listenerFunc` | `(state: MajorAvailableEvent) => void` | | **Returns** `Promise` **Since:** 2.3.0 *** ### addListener(‘updateFailed’) [Section titled “addListener(‘updateFailed’)”](#addlistenerupdatefailed) ```typescript addListener(eventName: 'updateFailed', listenerFunc: (state: UpdateFailedEvent) => void) => Promise ``` Listen for update fail event in the App, let you know when update has fail to install at next app start **Parameters** | Name | Type | Description | | -------------- | ------------------------------------ | ----------- | | `eventName` | `'updateFailed'` | | | `listenerFunc` | `(state: UpdateFailedEvent) => void` | | **Returns** `Promise` **Since:** 2.3.0 *** ### addListener(‘downloadFailed’) [Section titled “addListener(‘downloadFailed’)”](#addlistenerdownloadfailed) ```typescript addListener(eventName: 'downloadFailed', listenerFunc: (state: DownloadFailedEvent) => void) => Promise ``` Listen for download fail event in the App, let you know when a bundle download has failed **Parameters** | Name | Type | Description | | -------------- | -------------------------------------- | ----------- | | `eventName` | `'downloadFailed'` | | | `listenerFunc` | `(state: DownloadFailedEvent) => void` | | **Returns** `Promise` **Since:** 4.0.0 *** ### addListener(‘appReloaded’) [Section titled “addListener(‘appReloaded’)”](#addlistenerappreloaded) ```typescript addListener(eventName: 'appReloaded', listenerFunc: () => void) => Promise ``` Listen for reload event in the App, let you know when reload has happened **Parameters** | Name | Type | Description | | -------------- | --------------- | ----------- | | `eventName` | `'appReloaded'` | | | `listenerFunc` | `() => void` | | **Returns** `Promise` **Since:** 4.3.0 *** ### addListener(‘appReady’) [Section titled “addListener(‘appReady’)”](#addlistenerappready) ```typescript addListener(eventName: 'appReady', listenerFunc: (state: AppReadyEvent) => void) => Promise ``` Listen for app ready event in the App, let you know when app is ready to use, this event is retain till consumed. **Parameters** | Name | Type | Description | | -------------- | -------------------------------- | ----------- | | `eventName` | `'appReady'` | | | `listenerFunc` | `(state: AppReadyEvent) => void` | | **Returns** `Promise` **Since:** 5.1.0 *** ### addListener(‘channelPrivate’) [Section titled “addListener(‘channelPrivate’)”](#addlistenerchannelprivate) ```typescript addListener(eventName: 'channelPrivate', listenerFunc: (state: ChannelPrivateEvent) => void) => Promise ``` Listen for channel private event, fired when attempting to set a channel that doesn’t allow device self-assignment. This event is useful for: * Informing users they don’t have permission to switch to a specific channel * Implementing custom error handling for channel restrictions * Logging unauthorized channel access attempts **Parameters** | Name | Type | Description | | -------------- | -------------------------------------- | ----------- | | `eventName` | `'channelPrivate'` | | | `listenerFunc` | `(state: ChannelPrivateEvent) => void` | | **Returns** `Promise` **Since:** 7.34.0 *** ### addListener(‘onFlexibleUpdateStateChange’) [Section titled “addListener(‘onFlexibleUpdateStateChange’)”](#addlisteneronflexibleupdatestatechange) ```typescript addListener(eventName: 'onFlexibleUpdateStateChange', listenerFunc: (state: FlexibleUpdateState) => void) => Promise ``` Listen for flexible update state changes on Android. This event fires during the flexible update download process, providing: * Download progress (bytes downloaded / total bytes) * Installation status changes **Install status values:** * `UNKNOWN` (0): Unknown status * `PENDING` (1): Download pending * `DOWNLOADING` (2): Download in progress * `INSTALLING` (3): Installing the update * `INSTALLED` (4): Update installed (app restart needed) * `FAILED` (5): Update failed * `CANCELED` (6): Update was canceled * `DOWNLOADED` (11): Download complete, ready to install When status is `DOWNLOADED`, you should prompt the user and call {@link completeFlexibleUpdate} to finish the installation. **Parameters** | Name | Type | Description | | -------------- | -------------------------------------- | ----------- | | `eventName` | `'onFlexibleUpdateStateChange'` | | | `listenerFunc` | `(state: FlexibleUpdateState) => void` | | **Returns** `Promise` **Since:** 8.0.0 *** ### isAutoUpdateAvailable [Section titled “isAutoUpdateAvailable”](#isautoupdateavailable) ```typescript isAutoUpdateAvailable() => Promise ``` Check if the auto-update feature is available (not disabled by custom server configuration). Returns `false` when a custom `updateUrl` is configured, as this typically indicates you’re using a self-hosted update server that may not support all auto-update features. Returns `true` when using the default Capgo backend or when the feature is available. This is different from {@link isAutoUpdateEnabled}: * `isAutoUpdateEnabled()`: Checks if auto-update MODE is turned on/off * `isAutoUpdateAvailable()`: Checks if auto-update is SUPPORTED with your current configuration **Returns** `Promise` — `false` when custom updateUrl is set, `true` otherwise. **Throws:** {Error} If the operation fails. *** ### getNextBundle [Section titled “getNextBundle”](#getnextbundle) ```typescript getNextBundle() => Promise ``` Get information about the bundle queued to be activated on next reload. Returns: * {@link BundleInfo} object if a bundle has been queued via {@link next} * `null` if no update is pending This is useful to: * Check if an update is waiting to be applied * Display “Update pending” status to users * Show version info of the queued update * Decide whether to show a “Restart to update” prompt The queued bundle will be activated when: * The app is backgrounded (default behavior) * The app is killed and restarted * {@link reload} is called manually * Delay conditions set by {@link setMultiDelay} are met **Returns** `Promise` — The pending bundle info, or `null` if none is queued. **Since:** 6.8.0 **Throws:** {Error} If the operation fails. *** ### getFailedUpdate [Section titled “getFailedUpdate”](#getfailedupdate) ```typescript getFailedUpdate() => Promise ``` Retrieve information about the most recent bundle that failed to load. When a bundle fails to load (e.g., JavaScript errors prevent initialization, missing files), the plugin automatically rolls back and stores information about the failure. This method retrieves that failure information. **IMPORTANT: The stored value is cleared after being retrieved once.** Calling this method multiple times will only return the failure info on the first call, then `null` on subsequent calls until another failure occurs. Returns: * {@link UpdateFailedEvent} with bundle info if a failure was recorded * `null` if no failure has occurred or if it was already retrieved Use this to: * Show users why an update failed * Log failure information for debugging * Implement custom error handling/reporting * Display rollback notifications **Returns** `Promise` — The failed update info (cleared after first retrieval), or `null`. **Since:** 7.22.0 **Throws:** {Error} If the operation fails. *** ### setShakeMenu [Section titled “setShakeMenu”](#setshakemenu) ```typescript setShakeMenu(options: SetShakeMenuOptions) => Promise ``` Enable or disable the shake gesture menu for debugging and testing. When enabled, users can shake their device to open a debug menu that shows: * Current bundle information * Available bundles * Options to switch bundles manually * Update status This is useful during development and testing to: * Quickly test different bundle versions * Debug update flows * Switch between production and test bundles * Verify bundle installations **Important:** Disable this in production builds or only enable for internal testers. Can also be configured via {@link PluginsConfig.CapacitorUpdater.shakeMenu}. **Parameters** | Name | Type | Description | | --------- | --------------------- | ----------- | | `options` | `SetShakeMenuOptions` | | **Returns** `Promise` — Resolves when the setting is applied. **Since:** 7.5.0 **Throws:** {Error} If the operation fails. *** ### isShakeMenuEnabled [Section titled “isShakeMenuEnabled”](#isshakemenuenabled) ```typescript isShakeMenuEnabled() => Promise ``` Check if the shake gesture debug menu is currently enabled. Returns the current state of the shake menu feature that can be toggled via {@link setShakeMenu} or configured via {@link PluginsConfig.CapacitorUpdater.shakeMenu}. Use this to: * Check if debug features are enabled * Show/hide debug settings UI * Verify configuration during testing **Returns** `Promise` — Object with `enabled: true` or `enabled: false`. **Since:** 7.5.0 **Throws:** {Error} If the operation fails. *** ### setShakeChannelSelector [Section titled “setShakeChannelSelector”](#setshakechannelselector) ```typescript setShakeChannelSelector(options: SetShakeChannelSelectorOptions) => Promise ``` Enable or disable the shake channel selector at runtime. When enabled AND shakeMenu is true, shaking the device shows a channel selector instead of the debug menu. This allows users to switch between update channels by shaking their device. After selecting a channel, the app automatically checks for updates and downloads if available. Can also be configured via {@link PluginsConfig.CapacitorUpdater.allowShakeChannelSelector}. **Parameters** | Name | Type | Description | | --------- | -------------------------------- | ----------- | | `options` | `SetShakeChannelSelectorOptions` | | **Returns** `Promise` — Resolves when the setting is applied. **Since:** 8.43.0 **Throws:** {Error} If the operation fails. *** ### isShakeChannelSelectorEnabled [Section titled “isShakeChannelSelectorEnabled”](#isshakechannelselectorenabled) ```typescript isShakeChannelSelectorEnabled() => Promise ``` Check if the shake channel selector is currently enabled. Returns the current state of the shake channel selector feature that can be toggled via {@link setShakeChannelSelector} or configured via {@link PluginsConfig.CapacitorUpdater.allowShakeChannelSelector}. **Returns** `Promise` — Object with `enabled: true` or `enabled: false`. **Since:** 8.43.0 **Throws:** {Error} If the operation fails. *** ### getAppId [Section titled “getAppId”](#getappid) ```typescript getAppId() => Promise ``` Get the currently configured App ID used for update server communication. Returns the App ID that identifies this app to the update server. This can be: * The value set via {@link setAppId}, or * The {@link PluginsConfig.CapacitorUpdater.appId} config value, or * The default app identifier from your native app configuration Use this to: * Verify which App ID is being used for updates * Debug update delivery issues * Display app configuration in debug screens * Confirm App ID after calling {@link setAppId} **Returns** `Promise` — Object containing the current `appId` string. **Since:** 7.14.0 **Throws:** {Error} If the operation fails. *** ### setAppId [Section titled “setAppId”](#setappid) ```typescript setAppId(options: SetAppIdOptions) => Promise ``` Dynamically change the App ID used for update server communication. This overrides the App ID used to identify your app to the update server, allowing you to switch between different app configurations at runtime (e.g., production vs staging app IDs, or multi-tenant configurations). **Requirements:** * {@link PluginsConfig.CapacitorUpdater.allowModifyAppId} must be set to `true` **Important considerations:** * Changing the App ID will affect which updates this device receives * The new App ID must exist on your update server * This is primarily for advanced use cases (multi-tenancy, environment switching) * Most apps should use the config-based {@link PluginsConfig.CapacitorUpdater.appId} instead **Parameters** | Name | Type | Description | | --------- | ----------------- | ----------- | | `options` | `SetAppIdOptions` | | **Returns** `Promise` — Resolves when the App ID is successfully changed. **Since:** 7.14.0 **Throws:** {Error} If `allowModifyAppId` is false or the operation fails. *** ### getAppUpdateInfo [Section titled “getAppUpdateInfo”](#getappupdateinfo) ```typescript getAppUpdateInfo(options?: GetAppUpdateInfoOptions | undefined) => Promise ``` Get information about the app’s availability in the App Store or Play Store. This method checks the native app stores to see if a newer version of the app is available for download. This is different from Capgo’s OTA updates - this checks for native app updates that require going through the app stores. **Platform differences:** * **Android**: Uses Play Store’s In-App Updates API for accurate update information * **iOS**: Queries the App Store lookup API (requires country code for accurate results) **Returns information about:** * Current installed version * Available version in the store (if any) * Whether an update is available * Update priority (Android only) * Whether immediate/flexible updates are allowed (Android only) Use this to: * Check if users need to update from the app store * Show “Update Available” prompts for native updates * Implement version gating (require minimum native version) * Combine with Capgo OTA updates for a complete update strategy **Parameters** | Name | Type | Description | | --------- | ------------------------- | ----------- | | `options` | \`GetAppUpdateInfoOptions | undefined\` | **Returns** `Promise` — Information about the current and available app versions. **Since:** 8.0.0 **Throws:** {Error} If the operation fails or store information is unavailable. *** ### openAppStore [Section titled “openAppStore”](#openappstore) ```typescript openAppStore(options?: OpenAppStoreOptions | undefined) => Promise ``` Open the app’s page in the App Store or Play Store. This navigates the user to your app’s store listing where they can manually update the app. Use this as a fallback when in-app updates are not available or when the user needs to update on iOS. **Platform behavior:** * **Android**: Opens Play Store to the app’s page * **iOS**: Opens App Store to the app’s page **Customization options:** * `appId`: Specify a custom App Store ID (iOS) - useful for opening a different app’s page * `packageName`: Specify a custom package name (Android) - useful for opening a different app’s page **Parameters** | Name | Type | Description | | --------- | --------------------- | ----------- | | `options` | \`OpenAppStoreOptions | undefined\` | **Returns** `Promise` — Resolves when the store is opened. **Since:** 8.0.0 **Throws:** {Error} If the store cannot be opened. *** ### performImmediateUpdate [Section titled “performImmediateUpdate”](#performimmediateupdate) ```typescript performImmediateUpdate() => Promise ``` Perform an immediate in-app update on Android. This triggers Google Play’s immediate update flow, which: 1. Shows a full-screen update UI 2. Downloads and installs the update 3. Restarts the app automatically The user cannot continue using the app until the update is complete. This is ideal for critical updates that must be installed immediately. **Requirements:** * Android only (throws error on iOS) * An update must be available (check with {@link getAppUpdateInfo} first) * The update must allow immediate updates (`immediateUpdateAllowed: true`) **User experience:** * Full-screen blocking UI * Progress shown during download * App automatically restarts after installation **Returns** `Promise` — Result indicating success, cancellation, or failure. **Since:** 8.0.0 **Throws:** {Error} If not on Android, no update is available, or immediate updates not allowed. *** ### startFlexibleUpdate [Section titled “startFlexibleUpdate”](#startflexibleupdate) ```typescript startFlexibleUpdate() => Promise ``` Start a flexible in-app update on Android. This triggers Google Play’s flexible update flow, which: 1. Downloads the update in the background 2. Allows the user to continue using the app 3. Notifies when download is complete 4. Requires calling {@link completeFlexibleUpdate} to install Monitor the download progress using the `onFlexibleUpdateStateChange` listener. **Requirements:** * Android only (throws error on iOS) * An update must be available (check with {@link getAppUpdateInfo} first) * The update must allow flexible updates (`flexibleUpdateAllowed: true`) **Typical flow:** 1. Call `startFlexibleUpdate()` to begin download 2. Listen to `onFlexibleUpdateStateChange` for progress 3. When status is `DOWNLOADED`, prompt user to restart 4. Call `completeFlexibleUpdate()` to install and restart **Returns** `Promise` — Result indicating the update was started, cancelled, or failed. **Since:** 8.0.0 **Throws:** {Error} If not on Android, no update is available, or flexible updates not allowed. *** ### completeFlexibleUpdate [Section titled “completeFlexibleUpdate”](#completeflexibleupdate) ```typescript completeFlexibleUpdate() => Promise ``` Complete a flexible in-app update on Android. After a flexible update has been downloaded (status `DOWNLOADED` in `onFlexibleUpdateStateChange`), call this method to install the update and restart the app. **Important:** This will immediately restart the app. Make sure to: * Save any user data before calling * Prompt the user before restarting * Only call when the download status is `DOWNLOADED` **Returns** `Promise` — Resolves when the update installation begins (app will restart). **Since:** 8.0.0 **Throws:** {Error} If not on Android or no downloaded update is pending. *** # Capacitor Plugins by Capgo > Explore our comprehensive collection of Capacitor plugins to extend your app's native capabilities with powerful features. Welcome to the Capgo Capacitor Plugins collection! We maintain a suite of high-quality, well-documented plugins to help you build amazing native experiences in your Capacitor apps. ## ⭐ Capgo Cloud - Live Updates [Section titled “⭐ Capgo Cloud - Live Updates”](#-capgo-cloud---live-updates) [Capacitor Updater ](/docs/plugins/updater/)The core plugin powering Capgo Cloud - deliver instant updates to your Capacitor apps without app store delays. Push fixes and features directly to your users. The Updater plugin is the foundation of Capgo Cloud, enabling you to: * 🚀 Deploy updates instantly without app store reviews * 📱 Update your app’s JavaScript, HTML, CSS, and assets * 🎯 Target specific user segments with channels * 📊 Monitor update success with built-in analytics * 🔒 Secure updates with encryption and code signing ## 🚀 Featured Plugins [Section titled “🚀 Featured Plugins”](#-featured-plugins) [Social Login ](/docs/plugins/social-login/)Seamless authentication with Google, Apple, and Facebook. One plugin for all major social providers. [Native Purchases ](/docs/plugins/capacitor-native-purchases/)Implement in-app purchases and subscriptions with a unified API for iOS and Android. [Camera Preview ](/docs/plugins/capacitor-camera-preview/)Display real-time camera feed in your app with full control over capture and settings. [Data Storage SQLite ](/docs/plugins/data-storage-sqlite/)Fast and secure key-value storage powered by SQLite with optional encryption. ## 📱 Device & System Plugins [Section titled “📱 Device & System Plugins”](#-device--system-plugins) [Native Market ](/docs/plugins/capacitor-native-market/)Link to app stores for ratings, reviews, and app updates. [Native Biometric ](/docs/plugins/native-biometric/)Access biometric authentication APIs for secure app access. [Shake ](/docs/plugins/shake/)Detect shake gestures for interactive features and debug menus. [Navigation Bar ](/docs/plugins/navigation-bar/)Customize Android navigation bar color to match your app theme. [Home Indicator ](/docs/plugins/capacitor-home-indicator/)Control iOS home indicator visibility for immersive experiences. [NFC ](/docs/plugins/nfc/)Read and write NFC tags with native support for iOS and Android. [Barometer ](/docs/plugins/barometer/)Access device barometer for atmospheric pressure and altitude readings. [Accelerometer ](/docs/plugins/accelerometer/)Read device accelerometer for motion detection and orientation tracking. ## 🎥 Media & Camera Plugins [Section titled “🎥 Media & Camera Plugins”](#-media--camera-plugins) [Screen Recorder ](/docs/plugins/screen-recorder/)Record your device screen for tutorials, demos, or user feedback. [IVS Player ](/docs/plugins/capacitor-ivs-player/)Amazon IVS integration for ultra-low latency live streaming. [Native Audio ](/docs/plugins/capacitor-native-audio/)High-performance native audio engine for games and apps. [Mute ](/docs/plugins/mute/)Detect if the device mute switch is enabled or disabled. [Video Player ](/docs/plugins/video-player/)Native video playback with subtitles, fullscreen, and comprehensive controls. [YouTube Player ](/docs/plugins/youtube-player/)Embed YouTube videos with full player API control and event handling. [FFmpeg ](/docs/plugins/ffmpeg/)Video encoding and processing powered by FFmpeg for compression and conversion. [Volume Buttons ](/docs/plugins/volume-buttons/)Listen to hardware volume button presses for custom controls. [Audio Recorder ](/docs/plugins/audio-recorder/)Record audio on iOS, Android, and Web with simple controls. ## 🛠️ Utility Plugins [Section titled “🛠️ Utility Plugins”](#️-utility-plugins) [Uploader ](/docs/plugins/uploader/)Background file upload with progress tracking and resumable uploads. [Flash ](/docs/plugins/flash/)Control device flashlight/torch for utility and camera features. [Native Geocoder ](/docs/plugins/nativegeocoder/)Forward and reverse geocoding using native platform APIs. [InAppBrowser ](/docs/plugins/inappbrowser/)Open web content in a customizable in-app browser. [Crisp ](/docs/plugins/crisp/)Integrate Crisp chat support directly into your mobile app. [Live Reload ](/docs/plugins/live-reload/)Connect to your dev server for instant hot reloading during development. [Contacts ](/docs/plugins/contacts/)Access and manage device contacts with read and write capabilities. ## 🤖 AI & Advanced Media [Section titled “🤖 AI & Advanced Media”](#-ai--advanced-media) [LLM ](/docs/plugins/llm/)Run Large Language Models locally with Apple Intelligence support. [StreamCall ](/docs/plugins/streamcall/)Enable high-quality video streaming and calling capabilities. [JW Player ](/docs/plugins/jw-player/)Professional video player with advanced streaming features. [Ricoh 360 Camera ](/docs/plugins/ricoh360-camera/)Control and capture from Ricoh 360 degree cameras. ## 📍 Location & Background Services [Section titled “📍 Location & Background Services”](#-location--background-services) [Background Geolocation ](/docs/plugins/background-geolocation/)Track device location in background with battery optimization. [Alarm ](/docs/plugins/alarm/)Schedule native alarms and notifications even when app is closed. [iBeacon ](/docs/plugins/ibeacon/)Proximity detection and beacon region monitoring for location-based features. ## 📞 Communication & Analytics [Section titled “📞 Communication & Analytics”](#-communication--analytics) [Twilio Voice ](/docs/plugins/twilio-voice/)Make and receive phone calls using Twilio Voice API. [Intercom ](/docs/plugins/intercom/)Add Intercom support for in-app messaging, help center, and customer service workflows. [GTM ](/docs/plugins/gtm/)Google Tag Manager integration for analytics and tracking. [AppInsights ](/docs/plugins/appinsights/)Application insights and analytics using the Apptopia AppInsights SDK. [WeChat ](/docs/plugins/wechat/)WeChat SDK integration for authentication, sharing, payments, and mini-programs. ## 🔐 Security & System [Section titled “🔐 Security & System”](#-security--system) [Is Root ](/docs/plugins/is-root/)Detect if device is rooted or jailbroken for security. [App Attest ](/docs/plugins/app-attest/)Cross-platform device attestation using Apple App Attest and Google Play Integrity Standard. [Persona ](/docs/plugins/persona/)Launch Persona identity verification inquiries with native SDK support on iOS and Android. [Persistent Account ](/docs/plugins/persistent-account/)Maintain user accounts across app reinstalls. [Autofill Save Password ](/docs/plugins/autofill-save-password/)Enable password autofill and save functionality. [App Tracking Transparency ](/docs/plugins/app-tracking-transparency/)Request and check iOS App Tracking Transparency permission for IDFA access. [Age Range ](/docs/plugins/age-range/)Cross-platform age range detection using Google Play Age Signals and Apple DeclaredAgeRange. ## 📊 Android-Specific Features [Section titled “📊 Android-Specific Features”](#-android-specific-features) [Android Kiosk ](/docs/plugins/android-kiosk/)Lock Android devices into kiosk mode with launcher functionality and hardware key control. [Android Usage Stats ](/docs/plugins/android-usagestatsmanager/)Access Android app usage statistics and screen time data. [Android Inline Install ](/docs/plugins/android-inline-install/)Enable inline app installation on Android devices. [Age Signals ](/docs/plugins/age-signals/)Access Google Play Age Signals to detect supervised accounts and verified users. [WebView Version Checker ](/docs/plugins/webview-version-checker/)Check if the active Android WebView provider is outdated and show a native update prompt. ## 📥 Download & Navigation [Section titled “📥 Download & Navigation”](#-download--navigation) [Downloader ](/docs/plugins/downloader/)Background file downloads with progress tracking. [Launch Navigator ](/docs/plugins/launch-navigator/)Launch navigation apps with addresses and coordinates. ## 🎯 Why Choose Capgo Plugins? [Section titled “🎯 Why Choose Capgo Plugins?”](#-why-choose-capgo-plugins) **Well Maintained**: Regular updates and compatibility with latest Capacitor versions **Comprehensive Docs**: Detailed documentation with examples for every plugin **Easy Integration**: Simple APIs that follow Capacitor best practices **TypeScript Support**: Full TypeScript definitions for better development experience ## 🚀 Getting Started [Section titled “🚀 Getting Started”](#-getting-started) Each plugin follows a similar installation pattern: 1. **Install the plugin** using your preferred package manager 2. **Sync your project** with `npx cap sync` 3. **Configure platform-specific settings** if needed 4. **Start using the plugin** in your code Visit each plugin’s documentation for detailed setup instructions and API references. ## 💡 Need Help? [Section titled “💡 Need Help?”](#-need-help) * 📖 Check the individual plugin documentation * 💬 Join our [Discord community](https://discord.capgo.app) * 🐛 Report issues on the plugin’s GitHub repository * 📧 Contact support for enterprise inquiries ## 🛠️ Need a Custom Plugin? [Section titled “🛠️ Need a Custom Plugin?”](#️-need-a-custom-plugin) Can’t find the exact functionality you need? We can help! [Custom Plugin Development ](/consulting/)Our team specializes in building custom Capacitor plugins tailored to your specific requirements. From complex native integrations to unique device features, we've got you covered. Whether you need: * 🔧 A completely new plugin built from scratch * 🔄 Modifications to existing plugins * 🤝 Expert consulting on native development * 📱 Platform-specific implementations [Get in touch with our team](/consulting/) to discuss your custom plugin needs. ## 🤝 Contributing [Section titled “🤝 Contributing”](#-contributing) We welcome contributions! Each plugin is open source and available on GitHub. Feel free to: * Report bugs and request features * Submit pull requests * Improve documentation * Share your use cases *** **Built with ❤️ by the Capgo team** # @capgo/capacitor-accelerometer > Access device motion and acceleration data from accelerometer sensors 3-Axis Acceleration Measure acceleration forces in X, Y, and Z axes 📊 Motion Detection Detect device shakes, tilts, and movements 📱 Real-time Updates Continuous monitoring of acceleration data ⚡ Comprehensive Documentation Check the [Documentation](/docs/plugins/accelerometer/getting-started/) to master the plugin in just a few minutes. # Getting Started with Accelerometer > Learn how to integrate accelerometer sensing into your Capacitor app This guide will walk you through integrating the Capacitor Accelerometer plugin into your application. ## Installation [Section titled “Installation”](#installation) Install the plugin using npm: ```bash npm install @capgo/capacitor-accelerometer npx cap sync ``` ## Platform Configuration [Section titled “Platform Configuration”](#platform-configuration) ### iOS [Section titled “iOS”](#ios) No additional configuration required. The accelerometer is always available. ### Android [Section titled “Android”](#android) No additional configuration required. The accelerometer is always available. ### Web [Section titled “Web”](#web) The plugin uses the DeviceMotion API. Requires HTTPS in production. ## Basic Usage [Section titled “Basic Usage”](#basic-usage) ### Import the Plugin [Section titled “Import the Plugin”](#import-the-plugin) ```typescript import { Accelerometer } from '@capgo/capacitor-accelerometer'; ``` ### Start Monitoring [Section titled “Start Monitoring”](#start-monitoring) ```typescript const startAccelerometer = async () => { await Accelerometer.start({ interval: 100 // Update interval in milliseconds }); console.log('Accelerometer started'); }; ``` ### Listen to Acceleration Events [Section titled “Listen to Acceleration Events”](#listen-to-acceleration-events) ```typescript Accelerometer.addListener('accelerationChange', (data) => { console.log('X:', data.x); console.log('Y:', data.y); console.log('Z:', data.z); console.log('Timestamp:', data.timestamp); }); ``` ### Get Current Reading [Section titled “Get Current Reading”](#get-current-reading) ```typescript const getCurrentAcceleration = async () => { const reading = await Accelerometer.getCurrentAcceleration(); console.log('Current acceleration:', reading); }; ``` ### Stop Monitoring [Section titled “Stop Monitoring”](#stop-monitoring) ```typescript const stopAccelerometer = async () => { await Accelerometer.stop(); console.log('Accelerometer stopped'); }; ``` ## Complete Example [Section titled “Complete Example”](#complete-example) Here’s a complete example with shake detection: ```typescript import { Accelerometer } from '@capgo/capacitor-accelerometer'; class AccelerometerService { private listener: any; private lastX = 0; private lastY = 0; private lastZ = 0; private shakeThreshold = 15; async initialize() { await Accelerometer.start({ interval: 100 }); this.listener = Accelerometer.addListener('accelerationChange', (data) => { this.handleAcceleration(data); }); } handleAcceleration(data: any) { // Calculate delta const deltaX = Math.abs(data.x - this.lastX); const deltaY = Math.abs(data.y - this.lastY); const deltaZ = Math.abs(data.z - this.lastZ); // Check for shake if (deltaX > this.shakeThreshold || deltaY > this.shakeThreshold || deltaZ > this.shakeThreshold) { this.onShake(); } // Update last values this.lastX = data.x; this.lastY = data.y; this.lastZ = data.z; // Update UI this.updateDisplay(data); } onShake() { console.log('Device shaken!'); // Trigger shake action } updateDisplay(data: any) { console.log(`X: ${data.x.toFixed(2)} m/s²`); console.log(`Y: ${data.y.toFixed(2)} m/s²`); console.log(`Z: ${data.z.toFixed(2)} m/s²`); // Calculate magnitude const magnitude = Math.sqrt( data.x * data.x + data.y * data.y + data.z * data.z ); console.log(`Magnitude: ${magnitude.toFixed(2)} m/s²`); } async cleanup() { if (this.listener) { this.listener.remove(); } await Accelerometer.stop(); } } // Usage const accelService = new AccelerometerService(); accelService.initialize(); // Cleanup when done // accelService.cleanup(); ``` ## Understanding Readings [Section titled “Understanding Readings”](#understanding-readings) ### Acceleration Axes [Section titled “Acceleration Axes”](#acceleration-axes) * **X-axis**: Left (-) to Right (+) * **Y-axis**: Bottom (-) to Top (+) * **Z-axis**: Back (-) to Front (+) ### Gravity [Section titled “Gravity”](#gravity) * Device at rest shows \~9.8 m/s² on one axis (gravity) * Moving device shows acceleration in addition to gravity ### Units [Section titled “Units”](#units) * Measured in meters per second squared (m/s²) * Gravity = 9.8 m/s² ## Common Use Cases [Section titled “Common Use Cases”](#common-use-cases) ### Shake Detection [Section titled “Shake Detection”](#shake-detection) ```typescript class ShakeDetector { private lastUpdate = 0; private lastX = 0; private lastY = 0; private lastZ = 0; detectShake(x: number, y: number, z: number): boolean { const currentTime = Date.now(); if (currentTime - this.lastUpdate > 100) { const deltaX = Math.abs(x - this.lastX); const deltaY = Math.abs(y - this.lastY); const deltaZ = Math.abs(z - this.lastZ); this.lastUpdate = currentTime; this.lastX = x; this.lastY = y; this.lastZ = z; return deltaX + deltaY + deltaZ > 15; } return false; } } ``` ### Tilt Detection [Section titled “Tilt Detection”](#tilt-detection) ```typescript class TiltDetector { getTiltAngles(x: number, y: number, z: number) { const roll = Math.atan2(y, z) * (180 / Math.PI); const pitch = Math.atan2(-x, Math.sqrt(y * y + z * z)) * (180 / Math.PI); return { roll, pitch }; } isDeviceFlat(z: number): boolean { return Math.abs(z - 9.8) < 1.0; } isDeviceUpright(y: number): boolean { return Math.abs(y - 9.8) < 2.0; } } ``` ### Step Counter [Section titled “Step Counter”](#step-counter) ```typescript class StepCounter { private steps = 0; private lastMagnitude = 0; private threshold = 11; processAcceleration(x: number, y: number, z: number) { const magnitude = Math.sqrt(x * x + y * y + z * z); if (magnitude > this.threshold && this.lastMagnitude < this.threshold) { this.steps++; console.log('Steps:', this.steps); } this.lastMagnitude = magnitude; } } ``` ## Best Practices [Section titled “Best Practices”](#best-practices) 1. **Choose Appropriate Intervals**: Balance responsiveness and battery life * Gaming: 16-50ms * Fitness: 100-200ms * General: 200-500ms 2. **Remove Listeners**: Always clean up when done 3. **Filter Noise**: Use moving averages for smoother data 4. **Consider Battery**: High-frequency polling drains battery 5. **Test on Real Devices**: Simulators don’t provide accurate data ## Performance Tips [Section titled “Performance Tips”](#performance-tips) ### Debouncing [Section titled “Debouncing”](#debouncing) ```typescript class AccelerometerDebouncer { private timeout: any; debounce(callback: Function, delay: number) { return (...args: any[]) => { clearTimeout(this.timeout); this.timeout = setTimeout(() => callback(...args), delay); }; } } ``` ### Data Smoothing [Section titled “Data Smoothing”](#data-smoothing) ```typescript class AccelerometerFilter { private alpha = 0.8; private filteredX = 0; private filteredY = 0; private filteredZ = 0; filter(x: number, y: number, z: number) { this.filteredX = this.alpha * x + (1 - this.alpha) * this.filteredX; this.filteredY = this.alpha * y + (1 - this.alpha) * this.filteredY; this.filteredZ = this.alpha * z + (1 - this.alpha) * this.filteredZ; return { x: this.filteredX, y: this.filteredY, z: this.filteredZ }; } } ``` ## Next Steps [Section titled “Next Steps”](#next-steps) * Explore the [API Reference](https://github.com/Cap-go/capacitor-accelerometer#api) for complete method documentation * Check out the [example app](https://github.com/Cap-go/capacitor-accelerometer/tree/main/example) for advanced usage * See the [tutorial](/plugins/capacitor-accelerometer) for complete implementation examples # @capgo/capacitor-admob > Monetize your Capacitor app with Google AdMob banners, interstitials, and rewarded ads managed entirely from JavaScript. Capgo’s AdMob plugin delivers a first-class bridge to the Google Mobile Ads SDK so you can monetize native experiences without sacrificing Capacitor simplicity. Flexible ad formats Serve banner, interstitial, rewarded, and app open ads with a unified API. Request configuration Fine-tune ad requests with child-directed flags, content ratings, and extra network parameters. Lifecycle management Create, load, show, hide, and destroy ads on demand while tracking load state. Privacy-ready Handle ATT prompts and monitor tracking authorization status to stay compliant. Use the getting started guide to wire up your AdMob app IDs, configure consent, and manage ad inventory directly from your Capacitor project. # Getting Started > Install and configure the Capgo AdMob plugin to serve ads inside your Capacitor application. 1. **Install the plugin** * npm ```sh npm i @capgo/capacitor-admob ``` * pnpm ```sh pnpm add @capgo/capacitor-admob ``` * yarn ```sh yarn add @capgo/capacitor-admob ``` * bun ```sh bun add @capgo/capacitor-admob ``` 2. **Sync native projects** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Initialize the SDK [Section titled “Initialize the SDK”](#initialize-the-sdk) ```typescript import { AdMob, MaxAdContentRating } from '@capgo/capacitor-admob'; import { Capacitor } from '@capacitor/core'; // Start the Mobile Ads SDK once in your application bootstrap await AdMob.start(); // Optional: configure global request settings await AdMob.configure({ appMuted: false, appVolume: 1, }); // iOS: request tracking authorization const platform = Capacitor.getPlatform(); if (platform === 'ios') { await AdMob.requestTrackingAuthorization(); } await AdMob.configRequest({ maxAdContentRating: MaxAdContentRating.T, tagForChildDirectedTreatment: false, tagForUnderAgeOfConsent: false, }); ``` ## Android configuration [Section titled “Android configuration”](#android-configuration) > android/app/src/main/AndroidManifest.xml ```xml ... ``` \## iOS configuration > ios/App/App/Info.plist ```xml ... GADApplicationIdentifier ca-app-pub-000000000000000~0000000000 NSUserTrackingUsageDescription Your data will be used to deliver personalized ads and help us keep YourAppName free to play. ``` ## Show a banner ad [Section titled “Show a banner ad”](#show-a-banner-ad) ```typescript import { BannerAd } from '@capgo/capacitor-admob'; const banner = new BannerAd({ adUnitId: 'ca-app-pub-xxxxxxxxxxxxxxxx/banner', position: 'bottom', }); await banner.show(); ``` ## Interstitial or rewarded ads [Section titled “Interstitial or rewarded ads”](#interstitial-or-rewarded-ads) ```typescript import { InterstitialAd, RewardedAd } from '@capgo/capacitor-admob'; const interstitial = new InterstitialAd({ adUnitId: 'ca-app-pub-xxxxxxxxxxxxxxxx/interstitial', }); await interstitial.load(); await interstitial.show(); const rewarded = new RewardedAd({ adUnitId: 'ca-app-pub-xxxxxxxxxxxxxxxx/rewarded', }); await rewarded.load(); await rewarded.show(); ``` ## Listen for ad events [Section titled “Listen for ad events”](#listen-for-ad-events) ```typescript import { AdMob } from '@capgo/capacitor-admob'; const handle = await AdMob.addListener('adImpression', (event) => { console.log('Ad impression', event); }); // Later when cleaning up await handle.remove(); ``` ## Platform notes [Section titled “Platform notes”](#platform-notes) * **iOS**: Add your AdMob app ID to `Info.plist` under the `GADApplicationIdentifier` key and include any SKAdNetwork IDs you rely on. * **Android**: Declare your AdMob app ID in `AndroidManifest.xml` by adding `com.google.android.gms.ads.APPLICATION_ID` inside the `` tag. * **Consent & Privacy**: Use `requestTrackingAuthorization()` on iOS 14+ and `configRequest()` child-directed flags to stay compliant with regional privacy rules. * **iOS**: On `npx cap sync` got this error `The 'Pods-App' target has transitive dependencies that include statically linked binaries: (Google-Mobile-Ads-SDK and GoogleUserMessagingPlatform)` solved by editing `ios/App/Podfile` on line 4 adding `use_frameworks! :linkage => :static` # @capgo/capacitor-age-range > Detect user age ranges across platforms with a unified API. Uses Google Play Age Signals on Android and Apple DeclaredAgeRange on iOS for COPPA/KOSA compliance. Cross-Platform Single API wrapping Google Play Age Signals (Android) and Apple DeclaredAgeRange (iOS) Privacy-Preserving No birth dates collected — only age ranges are returned, respecting user privacy Regulatory Compliance Meet COPPA, KOSA, and regional age-appropriate design requirements Comprehensive Documentation Check the [Documentation](/docs/plugins/age-range/getting-started/) to master the plugin in just a few minutes. # Getting Started > Learn how to install and use the Age Range plugin for cross-platform age detection in your Capacitor app. 1. **Install the package** * npm ```sh npm i @capgo/capacitor-age-range ``` * pnpm ```sh pnpm add @capgo/capacitor-age-range ``` * yarn ```sh yarn add @capgo/capacitor-age-range ``` * bun ```sh bun add @capgo/capacitor-age-range ``` 2. **Sync with native projects** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## iOS Setup [Section titled “iOS Setup”](#ios-setup) ### Requirements [Section titled “Requirements”](#requirements) * iOS 15+ (plugin compiles and runs, returns `NOT_AVAILABLE` on older versions) * iOS 26+ for actual age range functionality (DeclaredAgeRange framework) * Xcode 26+ ### Entitlement [Section titled “Entitlement”](#entitlement) Add the `com.apple.developer.declared-age-range` entitlement to your app: 1. In Xcode, select your target → **Signing & Capabilities** 2. Click **+ Capability** → search for **Declared Age Range** 3. Enable it Or add manually to your `*.entitlements` file: ```xml com.apple.developer.declared-age-range ``` ### How it works on iOS [Section titled “How it works on iOS”](#how-it-works-on-ios) `requestAgeRange()` presents a system dialog where the user (or their guardian via Family Sharing) declares their age range. The `ageGates` option controls the age boundaries shown (default: `[13, 16, 18]`). ## Android Setup [Section titled “Android Setup”](#android-setup) ### Requirements [Section titled “Requirements”](#requirements-1) * Android API 24+ (minSdk) * Google Play Store installed and up to date ### How it works on Android [Section titled “How it works on Android”](#how-it-works-on-android) The plugin queries **Google Play Age Signals API** in the background — no user prompt is shown. The Play Store determines the user’s age verification status from their Google account. No additional permissions or manifest changes are needed. ## Usage [Section titled “Usage”](#usage) Import the plugin and request the user’s age range: ```typescript import { AgeRange } from '@capgo/capacitor-age-range'; const result = await AgeRange.requestAgeRange(); switch (result.status) { case 'SHARING': console.log('Age range:', result.ageLower, '-', result.ageUpper); console.log('Source:', result.declarationSource); break; case 'DECLINED_SHARING': console.log('User declined to share age'); break; case 'NOT_AVAILABLE': console.log('Age range API not available on this device'); break; case 'ERROR': console.log('Error requesting age range'); break; } ``` ## API Reference [Section titled “API Reference”](#api-reference) ### requestAgeRange(options?) [Section titled “requestAgeRange(options?)”](#requestagerangeoptions) Requests the user’s age range from the platform. ```typescript const result = await AgeRange.requestAgeRange({ ageGates: [13, 16, 18] // iOS only, ignored on Android }); ``` #### Options [Section titled “Options”](#options) | Option | Type | Default | Description | | ---------- | ---------- | -------------- | ------------------------------------------------------------- | | `ageGates` | `number[]` | `[13, 16, 18]` | Age thresholds for the iOS system dialog. Ignored on Android. | #### Result [Section titled “Result”](#result) | Property | Type | Description | | ------------------------ | --------- | -------------------------------------------------------------- | | `status` | `string` | `SHARING`, `DECLINED_SHARING`, `NOT_AVAILABLE`, or `ERROR` | | `ageLower` | `number?` | Lower bound of the age range | | `ageUpper` | `number?` | Upper bound (absent for highest bracket, e.g. 18+) | | `declarationSource` | `string?` | `SELF_DECLARED`, `GUARDIAN_DECLARED`, `VERIFIED`, or `UNKNOWN` | | `androidUserStatus` | `string?` | Android-only. Google Play user status | | `mostRecentApprovalDate` | `string?` | Android-only. Date of most recent guardian approval | | `installId` | `string?` | Android-only. Supervised install identifier | ### getPluginVersion() [Section titled “getPluginVersion()”](#getpluginversion) Returns the native plugin version. ```typescript const { version } = await AgeRange.getPluginVersion(); console.log('Plugin version:', version); ``` ## Complete Example [Section titled “Complete Example”](#complete-example) ```typescript import { AgeRange } from '@capgo/capacitor-age-range'; export class AgeVerificationService { async checkAgeRange(): Promise<{ isAdult: boolean; ageRange?: string }> { const result = await AgeRange.requestAgeRange({ ageGates: [13, 16, 18] }); if (result.status === 'SHARING') { const isAdult = (result.ageLower ?? 0) >= 18; const upper = result.ageUpper ? `-${result.ageUpper}` : '+'; const ageRange = `${result.ageLower}${upper}`; return { isAdult, ageRange }; } // User declined or API not available return { isAdult: false }; } async isAgeRangeAvailable(): Promise { const result = await AgeRange.requestAgeRange(); return result.status !== 'NOT_AVAILABLE'; } } ``` ## Platform Response Mapping [Section titled “Platform Response Mapping”](#platform-response-mapping) ### Android (Google Play Age Signals) [Section titled “Android (Google Play Age Signals)”](#android-google-play-age-signals) | Android UserStatus | → status | → declarationSource | | ----------------------------- | ----------------- | ------------------- | | VERIFIED | SHARING | VERIFIED | | SUPERVISED | SHARING | GUARDIAN\_DECLARED | | SUPERVISED\_APPROVAL\_PENDING | SHARING | GUARDIAN\_DECLARED | | SUPERVISED\_APPROVAL\_DENIED | SHARING | GUARDIAN\_DECLARED | | UNKNOWN / EMPTY | DECLINED\_SHARING | — | ### iOS (DeclaredAgeRange) [Section titled “iOS (DeclaredAgeRange)”](#ios-declaredagerange) | iOS Response | → status | → declarationSource | | --------------------------- | ----------------- | ------------------- | | .sharing (selfDeclared) | SHARING | SELF\_DECLARED | | .sharing (guardianDeclared) | SHARING | GUARDIAN\_DECLARED | | .declinedSharing | DECLINED\_SHARING | — | ## Platform Notes [Section titled “Platform Notes”](#platform-notes) ### iOS [Section titled “iOS”](#ios) * Requires iOS 26+ for the DeclaredAgeRange framework * On older iOS versions, returns `NOT_AVAILABLE` * Requires `com.apple.developer.declared-age-range` entitlement * Shows a system dialog to the user ### Android [Section titled “Android”](#android) * Uses Google Play Age Signals API (background check, no dialog) * Requires Google Play Store to be installed and up to date * Returns detailed error codes if Play Store is unavailable ### Web [Section titled “Web”](#web) * Not supported. Throws `'AgeRange does not have web implementation'` ## Best Practices [Section titled “Best Practices”](#best-practices) 1. **Handle all statuses** Always check the `status` field before reading age data. The `ageLower` and `ageUpper` fields are only present when `status` is `SHARING`. 2. **Provide fallbacks** When the API returns `NOT_AVAILABLE` or `ERROR`, implement a fallback (e.g., self-declaration form or feature gating). 3. **Use appropriate age gates** Choose age gates that match your app’s requirements. Common values: `[13]` for COPPA, `[13, 18]` for adult content, `[13, 16, 18]` for EU Digital Services Act. # @capgo/capacitor-android-age-signals > Access Google Play Age Signals to detect supervised accounts, verify user ages, and comply with child safety regulations in your Android app. Supervised Accounts Detect when users have supervised Google accounts managed by guardians 👨‍👧‍👦 Age Verification Identify verified users who are 18+ with Google’s age verification 🔐 Compliance Comply with child safety regulations like COPPA and GDPR 📋 Guardian Approvals Track pending approvals and guardian consent status ✅ Android Only Designed specifically for Android with Google Play Services 📱 Simple API Single method call to retrieve all age signals 🚀 # Getting Started > Learn how to install and use the Age Signals plugin to detect supervised accounts and verified users in your Android app. Caution This plugin is **Android-only** and requires Google Play Services. It will not work on iOS, web, or Android devices without Google Play Store. 1. **Install the package** * npm ```sh npm i @capgo/capacitor-android-age-signals ``` * pnpm ```sh pnpm add @capgo/capacitor-android-age-signals ``` * yarn ```sh yarn add @capgo/capacitor-android-age-signals ``` * bun ```sh bun add @capgo/capacitor-android-age-signals ``` 2. **Sync with Android project** * npm ```sh npx cap sync android ``` * pnpm ```sh pnpm cap sync android ``` * yarn ```sh yarn cap sync android ``` * bun ```sh bunx cap sync android ``` ## Prerequisites [Section titled “Prerequisites”](#prerequisites) * Android device with Google Play Services * Minimum Android API level 21 (Android 5.0) * Google Play Store installed on the device ## Usage [Section titled “Usage”](#usage) ### Check Age Signals [Section titled “Check Age Signals”](#check-age-signals) ```typescript import { AgeSignals, UserStatus } from '@capgo/capacitor-android-age-signals'; try { const result = await AgeSignals.checkAgeSignals(); console.log('User status:', result.userStatus); switch (result.userStatus) { case UserStatus.Verified: console.log('User is 18+ and verified by Google'); // Allow access to age-restricted content break; case UserStatus.Supervised: console.log(`Supervised user aged ${result.ageLower}-${result.ageUpper}`); // Apply age-appropriate restrictions break; case UserStatus.SupervisedApprovalPending: console.log('Waiting for guardian approval'); console.log('Pending since:', result.mostRecentApprovalDate); // Inform user to wait for guardian approval break; case UserStatus.SupervisedApprovalDenied: console.log('Guardian denied access'); console.log('Last approval:', result.mostRecentApprovalDate); // Block access or show alternative content break; case UserStatus.Unknown: console.log('User status unknown - prompt to verify in Play Store'); // Guide user to verify their age in Google Play break; case UserStatus.Empty: console.log('No age signal available'); // Handle as unverified user break; } } catch (error) { console.error('Failed to check age signals:', error); } ``` ### Handle Supervised Users [Section titled “Handle Supervised Users”](#handle-supervised-users) ```typescript async function handleSupervisedUser() { const result = await AgeSignals.checkAgeSignals(); if (result.userStatus === UserStatus.Supervised) { const age = result.ageLower; if (age < 13) { // Apply COPPA restrictions console.log('User is under 13 - COPPA applies'); disableDataCollection(); disableSocialFeatures(); requireParentalConsent(); } else if (age < 18) { // Apply teen restrictions console.log('User is 13-17 - Teen restrictions apply'); enableModeratedSocialFeatures(); restrictAds(); } // Track install ID for revocation notifications console.log('Install ID:', result.installId); saveInstallId(result.installId); } } ``` ### Implement Age Gate [Section titled “Implement Age Gate”](#implement-age-gate) ```typescript async function ageGate() { const result = await AgeSignals.checkAgeSignals(); // Allow verified 18+ users if (result.userStatus === UserStatus.Verified) { return true; } // Check supervised user age if (result.userStatus === UserStatus.Supervised) { return result.ageUpper >= 18; } // Block users with pending or denied approvals if ( result.userStatus === UserStatus.SupervisedApprovalPending || result.userStatus === UserStatus.SupervisedApprovalDenied ) { return false; } // For unknown/empty, implement fallback age verification return await showAgeVerificationDialog(); } ``` ### Monitor Approval Status [Section titled “Monitor Approval Status”](#monitor-approval-status) ```typescript async function checkApprovalStatus() { const result = await AgeSignals.checkAgeSignals(); if (result.userStatus === UserStatus.SupervisedApprovalPending) { console.log('Guardian approval pending'); console.log('Age range:', result.ageLower, '-', result.ageUpper); console.log('Most recent approval:', result.mostRecentApprovalDate); // Show message to user showMessage( 'Your guardian needs to approve this app. ' + 'We notified them on ' + result.mostRecentApprovalDate ); } else if (result.userStatus === UserStatus.SupervisedApprovalDenied) { console.log('Guardian denied approval'); console.log('Last approved on:', result.mostRecentApprovalDate); // Show blocked message showMessage( 'Your guardian did not approve this app. ' + 'Contact them for more information.' ); } } ``` ## API Reference [Section titled “API Reference”](#api-reference) ### checkAgeSignals() [Section titled “checkAgeSignals()”](#checkagesignals) Request the current Play Age Signals for the active user. ```typescript const result = await AgeSignals.checkAgeSignals(); ``` **Returns:** ```typescript interface CheckAgeSignalsResult { userStatus: UserStatus; ageLower?: number; ageUpper?: number; mostRecentApprovalDate?: string; installId?: string; } ``` ### UserStatus Enum [Section titled “UserStatus Enum”](#userstatus-enum) ```typescript enum UserStatus { Verified = 'VERIFIED', // 18+ verified Supervised = 'SUPERVISED', // Supervised account SupervisedApprovalPending = 'SUPERVISED_APPROVAL_PENDING', SupervisedApprovalDenied = 'SUPERVISED_APPROVAL_DENIED', Unknown = 'UNKNOWN', // Unknown status Empty = 'EMPTY' // No signal } ``` ## Result Fields [Section titled “Result Fields”](#result-fields) ### userStatus [Section titled “userStatus”](#userstatus) The user’s verification status as reported by Google Play. * **Verified**: User is over 18 and age-verified by Google * **Supervised**: User has a supervised Google Account * **SupervisedApprovalPending**: Pending guardian approval for changes * **SupervisedApprovalDenied**: Guardian denied app access * **Unknown**: User should verify status in Play Store * **Empty**: All other users (default state) ### ageLower [Section titled “ageLower”](#agelower) Inclusive lower bound of the supervised user’s age range. Only present when `userStatus` is: * `SUPERVISED` * `SUPERVISED_APPROVAL_PENDING` * `SUPERVISED_APPROVAL_DENIED` ### ageUpper [Section titled “ageUpper”](#ageupper) Inclusive upper bound of the supervised user’s age range. Only present when: * `userStatus` is one of the supervised statuses * User’s age is reported as less than 18 ### mostRecentApprovalDate [Section titled “mostRecentApprovalDate”](#mostrecentapprovaldate) Date string for the most recent significant change that received guardian approval. Only present when `userStatus` is: * `SUPERVISED_APPROVAL_PENDING` * `SUPERVISED_APPROVAL_DENIED` Format: ISO 8601 date string ### installId [Section titled “installId”](#installid) Identifier assigned to supervised installs in Google Play. Used for revocation notifications when guardian revokes app approval. Only present when `userStatus` is: * `SUPERVISED` * `SUPERVISED_APPROVAL_PENDING` * `SUPERVISED_APPROVAL_DENIED` ## Use Cases [Section titled “Use Cases”](#use-cases) ### 1. COPPA Compliance [Section titled “1. COPPA Compliance”](#1-coppa-compliance) ```typescript async function applyCoppaRestrictions() { const result = await AgeSignals.checkAgeSignals(); if (result.userStatus === UserStatus.Supervised && result.ageLower < 13) { // Disable data collection disableAnalytics(); disableAdvertising(); // Disable social features hideChatFeatures(); disableUserProfiles(); // Require verifiable parental consent await requestParentalConsent(result.installId); } } ``` ### 2. Age-Appropriate Content [Section titled “2. Age-Appropriate Content”](#2-age-appropriate-content) ```typescript async function filterContent() { const result = await AgeSignals.checkAgeSignals(); let contentRating; if (result.userStatus === UserStatus.Verified) { contentRating = 'MATURE'; } else if (result.userStatus === UserStatus.Supervised) { if (result.ageUpper < 13) { contentRating = 'EVERYONE'; } else if (result.ageUpper < 18) { contentRating = 'TEEN'; } else { contentRating = 'MATURE'; } } else { contentRating = 'TEEN'; // Default safe rating } loadContentForRating(contentRating); } ``` ### 3. Guardian Dashboard [Section titled “3. Guardian Dashboard”](#3-guardian-dashboard) ```typescript async function getGuardianInfo() { const result = await AgeSignals.checkAgeSignals(); if ( result.userStatus === UserStatus.Supervised || result.userStatus === UserStatus.SupervisedApprovalPending || result.userStatus === UserStatus.SupervisedApprovalDenied ) { return { isSupervised: true, ageRange: `${result.ageLower}-${result.ageUpper}`, approvalStatus: result.userStatus, lastApprovalDate: result.mostRecentApprovalDate, installId: result.installId, }; } return { isSupervised: false }; } ``` ## Complete Example [Section titled “Complete Example”](#complete-example) ```typescript import { AgeSignals, UserStatus } from '@capgo/capacitor-android-age-signals'; export class AgeVerificationService { async verifyAge(): Promise<{ allowed: boolean; reason: string; restrictions: string[]; }> { try { const result = await AgeSignals.checkAgeSignals(); switch (result.userStatus) { case UserStatus.Verified: return { allowed: true, reason: 'User is verified 18+', restrictions: [], }; case UserStatus.Supervised: return this.handleSupervised(result); case UserStatus.SupervisedApprovalPending: return { allowed: false, reason: 'Waiting for guardian approval', restrictions: ['Guardian approval required'], }; case UserStatus.SupervisedApprovalDenied: return { allowed: false, reason: 'Guardian denied access', restrictions: ['Access denied by guardian'], }; case UserStatus.Unknown: return { allowed: false, reason: 'Age verification required', restrictions: ['Verify age in Google Play'], }; case UserStatus.Empty: default: return { allowed: false, reason: 'No age signal available', restrictions: ['Age verification needed'], }; } } catch (error) { console.error('Age verification failed:', error); return { allowed: false, reason: 'Verification error', restrictions: ['Try again later'], }; } } private handleSupervised(result: any) { const age = result.ageLower; const restrictions: string[] = []; if (age < 13) { restrictions.push('No data collection (COPPA)'); restrictions.push('No social features'); restrictions.push('Parental consent required'); return { allowed: false, reason: `User is under 13 (${result.ageLower}-${result.ageUpper})`, restrictions, }; } else if (age < 18) { restrictions.push('Age-appropriate content only'); restrictions.push('Moderated social features'); return { allowed: true, reason: `Teen user (${result.ageLower}-${result.ageUpper})`, restrictions, }; } else { return { allowed: true, reason: `Adult supervised user (${result.ageLower}+)`, restrictions: [], }; } } async saveInstallId(installId: string) { // Store install ID for revocation handling localStorage.setItem('ageSignalsInstallId', installId); } async checkRevocation() { const result = await AgeSignals.checkAgeSignals(); const storedId = localStorage.getItem('ageSignalsInstallId'); if (result.installId && storedId && result.installId !== storedId) { // Install ID changed - likely revoked and reinstalled console.log('App was revoked and reinstalled'); return true; } return false; } } ``` ## Best Practices [Section titled “Best Practices”](#best-practices) 1. **Check on App Start**: Verify age signals when the app launches 2. **Cache Results**: Cache results but refresh periodically 3. **Handle All States**: Implement logic for all UserStatus values 4. **Respect Denials**: Don’t allow access when guardian denies approval 5. **Store Install ID**: Track install ID for revocation detection 6. **Fallback Logic**: Have fallback age verification for Unknown/Empty states 7. **Privacy First**: Don’t collect unnecessary data from supervised users ## Compliance Guidelines [Section titled “Compliance Guidelines”](#compliance-guidelines) ### COPPA (Children’s Online Privacy Protection Act) [Section titled “COPPA (Children’s Online Privacy Protection Act)”](#coppa-childrens-online-privacy-protection-act) For users under 13: * Obtain verifiable parental consent * Limit data collection * Disable behavioral advertising * Provide parental controls ### GDPR (General Data Protection Regulation) [Section titled “GDPR (General Data Protection Regulation)”](#gdpr-general-data-protection-regulation) For supervised users: * Process data lawfully * Provide guardian consent mechanisms * Allow data access and deletion * Implement privacy by design ## Platform Notes [Section titled “Platform Notes”](#platform-notes) ### Android [Section titled “Android”](#android) * Requires Google Play Services * Minimum API level 21 (Android 5.0+) * Only works on devices with Play Store * Signals may not be available in all regions * Results can change if guardian modifies settings ### iOS / Web [Section titled “iOS / Web”](#ios--web) * **Not supported** - This is an Android-only plugin * Will throw an error if called on unsupported platforms ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) ### No Signal Returned (Empty Status) [Section titled “No Signal Returned (Empty Status)”](#no-signal-returned-empty-status) This is normal for: * Users outside supported regions * Devices without Google Play Services * Users who haven’t set up Family Link * New accounts without verification ### Unknown Status [Section titled “Unknown Status”](#unknown-status) User should: 1. Open Google Play Store 2. Go to Settings → Family 3. Complete age verification process ### Permission Issues [Section titled “Permission Issues”](#permission-issues) Ensure: * Google Play Services is updated * App has correct package name in Play Console * Testing on real device (not emulator without Play Services) ## Resources [Section titled “Resources”](#resources) * [Google Play Age Signals Documentation](https://developers.google.com/android/reference/com/google/android/gms/agesignals) * [Family Link Developer Guide](https://developers.google.com/families) * [COPPA Compliance Guide](https://www.ftc.gov/business-guidance/resources/complying-coppa-frequently-asked-questions) # @capgo/capacitor-alarm > Manage native alarms on iOS and Android directly from your Capacitor app. Native Integration Uses native AlarmKit on iOS and AlarmClock intents on Android 📱 Simple API Easy-to-use methods to create and manage alarms ⚡ Permission Handling Built-in support for requesting necessary permissions 🔒 Comprehensive Documentation Check the [Documentation](/docs/plugins/alarm/getting-started/) to master the plugin in just a few minutes. # Getting Started > Learn how to install and use the Capacitor Alarm plugin to manage native alarms on iOS and Android. ## Installation [Section titled “Installation”](#installation) * npm ```bash npm install @capgo/capacitor-alarm npx cap sync ``` * yarn ```bash yarn add @capgo/capacitor-alarm npx cap sync ``` * pnpm ```bash pnpm add @capgo/capacitor-alarm npx cap sync ``` * bun ```bash bun add @capgo/capacitor-alarm npx cap sync ``` ## Requirements [Section titled “Requirements”](#requirements) * **iOS**: iOS 26+ only. This plugin relies on `AlarmKit` APIs and will report unsupported on earlier versions or when the framework is unavailable. * **Android**: Uses `AlarmClock` intents; behavior depends on the default Clock app and OEM policies. Note: This plugin only exposes native alarm actions (create/open). It does not implement any custom in-app alarm scheduling/CRUD. ## API [Section titled “API”](#api) ### createAlarm(…) [Section titled “createAlarm(…)”](#createalarm) ```typescript createAlarm(options: NativeAlarmCreateOptions) => Promise ``` Create a native OS alarm using the platform clock app. On Android this uses the Alarm Clock intent; on iOS this use AlarmKit if available (iOS 26+). | Param | Type | | ------------- | -------------------------- | | **`options`** | `NativeAlarmCreateOptions` | **Returns:** `Promise` ### openAlarms() [Section titled “openAlarms()”](#openalarms) ```typescript openAlarms() => Promise ``` Open the platform’s native alarm list UI, if available. **Returns:** `Promise` ### getOSInfo() [Section titled “getOSInfo()”](#getosinfo) ```typescript getOSInfo() => Promise ``` Get information about the OS and capabilities. **Returns:** `Promise` ### requestPermissions(…) [Section titled “requestPermissions(…)”](#requestpermissions) ```typescript requestPermissions(options?: { exactAlarm?: boolean | undefined; } | undefined) => Promise ``` Request relevant permissions for alarm usage on the platform. On Android, may route to settings for exact alarms. | Param | Type | | ------------- | --------------------------- | | **`options`** | `{ exactAlarm?: boolean; }` | **Returns:** `Promise` ## Interfaces [Section titled “Interfaces”](#interfaces) ### NativeActionResult [Section titled “NativeActionResult”](#nativeactionresult) | Prop | Type | | ------------- | --------- | | **`success`** | `boolean` | | **`message`** | `string` | ### NativeAlarmCreateOptions [Section titled “NativeAlarmCreateOptions”](#nativealarmcreateoptions) Options for creating a native OS alarm via the platform clock app. | Prop | Type | Description | | ------------- | --------- | -------------------------------------------- | | **`hour`** | `number` | Hour of day in 24h format (0-23) | | **`minute`** | `number` | Minute of hour (0-59) | | **`label`** | `string` | Optional label for the alarm | | **`skipUi`** | `boolean` | Android only: attempt to skip UI if possible | | **`vibrate`** | `boolean` | Android only: set alarm to vibrate | ### OSInfo [Section titled “OSInfo”](#osinfo) Returned info about current OS and capabilities. | Prop | Type | Description | | ------------------------------------ | --------- | ----------------------------------------------------------- | | **`platform`** | `string` | ’ios’ \| ‘android’ \| ‘web’ | | **`version`** | `string` | OS version string | | **`supportsNativeAlarms`** | `boolean` | Whether the platform exposes a native alarm app integration | | **`supportsScheduledNotifications`** | `boolean` | Whether scheduling local notifications is supported | | **`canScheduleExactAlarms`** | `boolean` | Android only: whether exact alarms are allowed | ### PermissionResult [Section titled “PermissionResult”](#permissionresult) Result of a permissions request. | Prop | Type | Description | | ------------- | ------------------------- | ---------------------------------- | | **`granted`** | `boolean` | Overall grant for requested scope | | **`details`** | `Record` | Optional details by permission key | # @capgo/capacitor-android-inline-install > Enable seamless in-app installation and updates for Android apps with inline installation capabilities and user-friendly flows. ## Overview [Section titled “Overview”](#overview) The Capacitor Android Inline Install plugin enables triggering Google Play’s Inline Install overlay for Android applications. This plugin provides a seamless in-app installation experience using Google’s Premium Growth Tools, allowing users to install apps without leaving your application. Google Play overlay Trigger native Google Play install overlay 📱 Seamless experience Install apps without leaving your app 🔄 Fallback support Automatic fallback to full Play Store page ❤️ Campaign tracking Support for referrer tracking and analytics 📊 ## Documentation [Section titled “Documentation”](#documentation) Check the [complete documentation](/docs/plugins/android-inline-install/getting-started/) for detailed implementation guides and advanced integration patterns. # Getting Started > Installation and usage guide for @capgo/capacitor-android-inline-install ## Installation [Section titled “Installation”](#installation) * npm ```bash npm install @capgo/capacitor-android-inline-install npx cap sync ``` * yarn ```bash yarn add @capgo/capacitor-android-inline-install npx cap sync ``` * pnpm ```bash pnpm add @capgo/capacitor-android-inline-install npx cap sync ``` * bun ```bash bun add @capgo/capacitor-android-inline-install npx cap sync ``` ## Usage Example [Section titled “Usage Example”](#usage-example) ```typescript import { AndroidInlineInstall } from '@capgo/capacitor-android-inline-install'; // Basic inline install await AndroidInlineInstall.startInlineInstall({ id: 'com.example.targetapp' }); // Advanced install with tracking await AndroidInlineInstall.startInlineInstall({ id: 'com.example.targetapp', referrer: 'campaign=my-campaign&source=app', overlay: true, fallback: true }); // Handle the installation flow try { await AndroidInlineInstall.startInlineInstall({ id: 'com.spotify.music', referrer: 'utm_source=myapp&utm_campaign=music_promotion' }); console.log('Install overlay triggered successfully'); } catch (error) { console.error('Install failed:', error); } ``` ## Core API Methods [Section titled “Core API Methods”](#core-api-methods) ### Installation Management [Section titled “Installation Management”](#installation-management) * `startInlineInstall(options)` - Trigger Google Play inline install overlay for specified app ## Configuration Options [Section titled “Configuration Options”](#configuration-options) ```typescript interface InlineInstallOptions { id: string; // Target app package name (required) referrer?: string; // Tracking campaign string (optional) callerId?: string; // Caller app package name (defaults to current app) overlay?: boolean; // Enable/disable Play overlay (default: true) fallback?: boolean; // Use full store page if overlay fails (default: true) } ``` ## Google Play Requirements [Section titled “Google Play Requirements”](#google-play-requirements) ### Premium Growth Tools Eligibility [Section titled “Premium Growth Tools Eligibility”](#premium-growth-tools-eligibility) Your app must qualify for Google Play’s Premium Growth Tools to use inline install: * Apps with significant user engagement * Good Play Store ratings and reviews * Compliance with Google Play policies ### Target App Requirements [Section titled “Target App Requirements”](#target-app-requirements) * Target app must be available on Google Play Store * Target app must support inline installation * User must be signed into Google Play Store ## Behavior and Fallbacks [Section titled “Behavior and Fallbacks”](#behavior-and-fallbacks) ### Overlay Mode (default) [Section titled “Overlay Mode (default)”](#overlay-mode-default) 1. Attempts to open Google Play overlay within your app 2. If overlay is unavailable, falls back to full Play Store page 3. If Play Store is not available, shows error ### Full Store Mode [Section titled “Full Store Mode”](#full-store-mode) * Directly opens the full Google Play Store page * Bypasses overlay attempt entirely ## Platform Support [Section titled “Platform Support”](#platform-support) * **Android**: Full support with Google Play Services * **iOS**: Not supported (Android-specific feature) * **Web**: Not supported (native Android feature) ## Implementation Details [Section titled “Implementation Details”](#implementation-details) The plugin uses Android intents to communicate with Google Play Store: * Intent action for inline install overlay * Fallback to standard Play Store intent * Automatic handling of Play Services availability ## Use Cases [Section titled “Use Cases”](#use-cases) * **App discovery**: Promote related apps within your ecosystem * **Cross-promotion**: Drive installs for partner applications * **Feature unlocking**: Install additional modules or extensions * **Companion apps**: Install supporting applications seamlessly ## Best Practices [Section titled “Best Practices”](#best-practices) * Always provide fallback options for users without Play Services * Test with different device configurations and Play Store versions * Use referrer tracking to measure conversion rates * Handle installation errors gracefully * Respect user choice if they decline installation ## Limitations [Section titled “Limitations”](#limitations) * Android-only functionality * Requires Google Play Services * Only works with apps qualifying for Premium Growth Tools * Subject to Google Play Store policies and availability # @capgo/capacitor-android-kiosk > Lock your Android device into kiosk mode with full control over hardware buttons and launcher functionality for your Capacitor apps. Kiosk Mode Hide system UI and enter immersive fullscreen mode Launcher Integration Set your app as the device launcher/home app Hardware Key Control Block or allow specific hardware buttons (back, home, recent, volume, power) Status Detection Check if kiosk mode is active or if app is set as launcher Android 6.0+ Supports Android API 23 through Android 15 (API 35) Comprehensive Documentation Check the [Documentation](/docs/plugins/android-kiosk/getting-started/) to integrate kiosk mode in minutes. ## Platform Support [Section titled “Platform Support”](#platform-support) This plugin is **Android-only**. For iOS kiosk mode functionality, please use the device’s built-in [Guided Access](https://support.apple.com/en-us/HT202612) feature. # Getting Started > Learn how to install and use the Android Kiosk plugin to lock your Android device into kiosk mode in your Capacitor app. 1. **Install the package** * npm ```sh npm i @capgo/capacitor-android-kiosk ``` * pnpm ```sh pnpm add @capgo/capacitor-android-kiosk ``` * yarn ```sh yarn add @capgo/capacitor-android-kiosk ``` * bun ```sh bun add @capgo/capacitor-android-kiosk ``` 2. **Sync with native projects** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Platform Support [Section titled “Platform Support”](#platform-support) This plugin is **Android-only**. For iOS kiosk mode functionality, please use the device’s built-in [Guided Access](https://support.apple.com/en-us/HT202612) feature. ## Features [Section titled “Features”](#features) * **Kiosk Mode**: Hide system UI and enter immersive fullscreen mode * **Launcher Integration**: Set your app as the device launcher/home app * **Hardware Key Control**: Block or allow specific hardware buttons * **Status Detection**: Check if kiosk mode is active or if app is set as launcher * **Android 6.0+**: Supports Android API 23 through Android 15 (API 35) ## Basic Usage [Section titled “Basic Usage”](#basic-usage) ### Enter and Exit Kiosk Mode [Section titled “Enter and Exit Kiosk Mode”](#enter-and-exit-kiosk-mode) ```typescript import { CapacitorAndroidKiosk } from '@capgo/capacitor-android-kiosk'; // Enter kiosk mode await CapacitorAndroidKiosk.enterKioskMode(); // Exit kiosk mode await CapacitorAndroidKiosk.exitKioskMode(); // Check if in kiosk mode const { isInKioskMode } = await CapacitorAndroidKiosk.isInKioskMode(); console.log('Kiosk mode active:', isInKioskMode); ``` ### Launcher Functionality [Section titled “Launcher Functionality”](#launcher-functionality) For full kiosk mode functionality, you need to set your app as the device launcher: ```typescript // Open home screen settings for user to select your app as launcher await CapacitorAndroidKiosk.setAsLauncher(); // Check if app is set as launcher const { isLauncher } = await CapacitorAndroidKiosk.isSetAsLauncher(); console.log('App is launcher:', isLauncher); ``` ### Hardware Key Control [Section titled “Hardware Key Control”](#hardware-key-control) Control which hardware buttons are allowed to function in kiosk mode: ```typescript // Allow only volume keys await CapacitorAndroidKiosk.setAllowedKeys({ volumeUp: true, volumeDown: true, back: false, home: false, recent: false }); // Block all keys (default) await CapacitorAndroidKiosk.setAllowedKeys({}); ``` ## Complete Example [Section titled “Complete Example”](#complete-example) ```typescript async function setupKioskMode() { try { // Check if already set as launcher const { isLauncher } = await CapacitorAndroidKiosk.isSetAsLauncher(); if (!isLauncher) { // Prompt user to set as launcher await CapacitorAndroidKiosk.setAsLauncher(); alert('Please select this app as your Home app'); return; } // Configure allowed keys await CapacitorAndroidKiosk.setAllowedKeys({ volumeUp: true, volumeDown: true, back: false, home: false, recent: false, power: false }); // Enter kiosk mode await CapacitorAndroidKiosk.enterKioskMode(); console.log('Kiosk mode activated'); } catch (error) { console.error('Failed to setup kiosk mode:', error); } } ``` ## API Reference [Section titled “API Reference”](#api-reference) ### isInKioskMode() [Section titled “isInKioskMode()”](#isinkioskmode) Checks if the app is currently running in kiosk mode. ```typescript const { isInKioskMode } = await CapacitorAndroidKiosk.isInKioskMode(); ``` **Returns:** * `isInKioskMode` (boolean): Whether kiosk mode is currently active *** ### isSetAsLauncher() [Section titled “isSetAsLauncher()”](#issetaslauncher) Checks if the app is set as the device launcher (home app). ```typescript const { isLauncher } = await CapacitorAndroidKiosk.isSetAsLauncher(); ``` **Returns:** * `isLauncher` (boolean): Whether the app is set as the device launcher *** ### enterKioskMode() [Section titled “enterKioskMode()”](#enterkioskmode) Enters kiosk mode, hiding system UI and blocking hardware buttons. The app must be set as the device launcher for this to work effectively. ```typescript await CapacitorAndroidKiosk.enterKioskMode(); ``` *** ### exitKioskMode() [Section titled “exitKioskMode()”](#exitkioskmode) Exits kiosk mode, restoring normal system UI and hardware button functionality. ```typescript await CapacitorAndroidKiosk.exitKioskMode(); ``` *** ### setAsLauncher() [Section titled “setAsLauncher()”](#setaslauncher) Opens the device’s home screen settings to allow user to set this app as the launcher. This is required for full kiosk mode functionality. ```typescript await CapacitorAndroidKiosk.setAsLauncher(); ``` *** ### setAllowedKeys(options) [Section titled “setAllowedKeys(options)”](#setallowedkeysoptions) Sets which hardware keys are allowed to function in kiosk mode. By default, all hardware keys are blocked in kiosk mode. ```typescript await CapacitorAndroidKiosk.setAllowedKeys({ volumeUp: true, volumeDown: true, back: false, home: false, recent: false, power: false, camera: false, menu: false }); ``` **Parameters:** * `volumeUp` (boolean, optional): Allow volume up button (default: false) * `volumeDown` (boolean, optional): Allow volume down button (default: false) * `back` (boolean, optional): Allow back button (default: false) * `home` (boolean, optional): Allow home button (default: false) * `recent` (boolean, optional): Allow recent apps button (default: false) * `power` (boolean, optional): Allow power button (default: false) * `camera` (boolean, optional): Allow camera button if present (default: false) * `menu` (boolean, optional): Allow menu button if present (default: false) *** ### getPluginVersion() [Section titled “getPluginVersion()”](#getpluginversion) Get the native Capacitor plugin version. ```typescript const { version } = await CapacitorAndroidKiosk.getPluginVersion(); console.log('Plugin version:', version); ``` **Returns:** * `version` (string): The plugin version number ## Android Configuration [Section titled “Android Configuration”](#android-configuration) ### 1. MainActivity Setup [Section titled “1. MainActivity Setup”](#1-mainactivity-setup) To enable full hardware key blocking, you need to override `dispatchKeyEvent` in your `MainActivity.java`: ```java import android.view.KeyEvent; import ee.forgr.plugin.android_kiosk.CapacitorAndroidKioskPlugin; public class MainActivity extends BridgeActivity { @Override public boolean dispatchKeyEvent(KeyEvent event) { // Get the kiosk plugin CapacitorAndroidKioskPlugin kioskPlugin = (CapacitorAndroidKioskPlugin) this.getBridge().getPlugin("CapacitorAndroidKiosk").getInstance(); if (kioskPlugin != null && kioskPlugin.shouldBlockKey(event.getKeyCode())) { return true; // Block the key } return super.dispatchKeyEvent(event); } @Override public void onBackPressed() { // Don't call super.onBackPressed() to disable back button // Or call the plugin's handleOnBackPressed } } ``` ### 2. AndroidManifest.xml [Section titled “2. AndroidManifest.xml”](#2-androidmanifestxml) Add launcher intent filter to make your app selectable as a launcher: ```xml ``` ## Important Notes [Section titled “Important Notes”](#important-notes) 1. **Launcher Requirement**: For full kiosk mode functionality (blocking home button, preventing task switching), your app must be set as the device launcher. 2. **Testing**: When testing, you can exit kiosk mode programmatically or by setting another app as the launcher. 3. **Android Versions**: The plugin uses modern Android APIs for Android 11+ and falls back to older methods for compatibility with Android 6.0+. 4. **Security**: This plugin is designed for legitimate kiosk applications. Ensure you provide users with a way to exit kiosk mode. 5. **Battery**: Kiosk mode keeps the screen on. Consider implementing your own screen timeout or brightness management. ## iOS Alternative [Section titled “iOS Alternative”](#ios-alternative) For iOS devices, use the built-in [Guided Access](https://support.apple.com/en-us/HT202612) feature: 1. Go to Settings > Accessibility > Guided Access 2. Turn on Guided Access 3. Set a passcode 4. Open your app 5. Triple-click the side button 6. Adjust settings and start Guided Access ## Best Practices [Section titled “Best Practices”](#best-practices) 1. **Check launcher status first** ```typescript const { isLauncher } = await CapacitorAndroidKiosk.isSetAsLauncher(); if (!isLauncher) { // Prompt user to set as launcher first await CapacitorAndroidKiosk.setAsLauncher(); } ``` 2. **Provide exit mechanism** ```typescript // Allow specific key combination to exit // Or implement a secret gesture/pattern async function exitKioskWithConfirmation() { const confirmed = confirm('Exit kiosk mode?'); if (confirmed) { await CapacitorAndroidKiosk.exitKioskMode(); } } ``` 3. **Handle app lifecycle** ```typescript // Re-enter kiosk mode when app resumes window.addEventListener('resume', async () => { const { isInKioskMode } = await CapacitorAndroidKiosk.isInKioskMode(); if (!isInKioskMode) { await CapacitorAndroidKiosk.enterKioskMode(); } }); ``` 4. **Error handling** ```typescript try { await CapacitorAndroidKiosk.enterKioskMode(); } catch (error) { console.error('Failed to enter kiosk mode:', error); // Notify user and provide alternative } ``` # @capgo/capacitor-android-usagestatsmanager > Access Android usage statistics to monitor app usage, screen time, and user behavior analytics with system-level data. ## Overview [Section titled “Overview”](#overview) The Capacitor Android Usage Stats Manager plugin exposes Android’s UsageStatsManager SDK to Capacitor applications, enabling access to detailed app usage statistics and device usage data. This plugin allows developers to track app usage patterns, screen time, and user behavior analytics on Android devices. Usage statistics Access Android UsageStatsManager SDK data 📱 App monitoring Track individual app usage time and frequency 🕐 Permission management Handle usage stats permissions seamlessly 🛡️ Package information Query installed package details and metadata 📦 ## Installation [Section titled “Installation”](#installation) ```bash npm install @capgo/capacitor-android-usagestatsmanager npx cap sync ``` ## Required Permissions [Section titled “Required Permissions”](#required-permissions) Add these permissions to your `android/app/src/main/AndroidManifest.xml`: ```xml ``` ## Core API Methods [Section titled “Core API Methods”](#core-api-methods) ### Usage Statistics [Section titled “Usage Statistics”](#usage-statistics) * `queryAndAggregateUsageStats(options)` - Retrieve detailed usage statistics for installed apps * `isUsageStatsPermissionGranted()` - Check if usage stats permission is granted * `openUsageStatsSettings()` - Open system settings for usage stats permission ### Package Information [Section titled “Package Information”](#package-information) * `queryAllPackages()` - Get information about all installed packages ## Usage Example [Section titled “Usage Example”](#usage-example) ```typescript import { AndroidUsageStatsManager } from '@capgo/capacitor-android-usagestatsmanager'; // Check if permission is granted const permissionResult = await AndroidUsageStatsManager.isUsageStatsPermissionGranted(); if (!permissionResult.granted) { // Open settings to grant permission await AndroidUsageStatsManager.openUsageStatsSettings(); return; } // Query usage statistics const statsOptions = { intervalType: 0, // INTERVAL_DAILY startTime: Date.now() - (7 * 24 * 60 * 60 * 1000), // 7 days ago endTime: Date.now() }; const usageStats = await AndroidUsageStatsManager.queryAndAggregateUsageStats(statsOptions); console.log('Usage statistics:', usageStats); // Get all installed packages const packages = await AndroidUsageStatsManager.queryAllPackages(); console.log('Installed packages:', packages); ``` ## Permission Handling [Section titled “Permission Handling”](#permission-handling) The plugin requires special permissions that cannot be granted through normal runtime permission requests: 1. **PACKAGE\_USAGE\_STATS**: Allows access to usage statistics 2. **QUERY\_ALL\_PACKAGES**: Required for package information (Android 11+) Users must manually grant these permissions through system settings. Use `openUsageStatsSettings()` to direct users to the appropriate settings page. ## Data Types [Section titled “Data Types”](#data-types) ### Usage Statistics [Section titled “Usage Statistics”](#usage-statistics-1) * App usage time and frequency * First and last time used * Total time in foreground * Launch count ### Package Information [Section titled “Package Information”](#package-information-1) * Package name and version * Installation time * App labels and icons * System vs user apps ## Use Cases [Section titled “Use Cases”](#use-cases) * **Digital wellbeing apps**: Monitor screen time and app usage * **Parental controls**: Track children’s device usage * **Productivity apps**: Analyze work patterns and focus time * **Analytics**: Understand user behavior and app engagement ## Android Compatibility [Section titled “Android Compatibility”](#android-compatibility) * **Minimum Android version**: API level 21 (Android 5.0) * **Advanced features**: API level 29+ (Android 10+) for enhanced statistics * **Package queries**: API level 30+ (Android 11+) requires QUERY\_ALL\_PACKAGES ## Security Considerations [Section titled “Security Considerations”](#security-considerations) * Usage stats permission is sensitive and requires user consent * Consider user privacy when collecting usage data * Implement proper data handling and storage practices * Follow Google Play policies for usage data collection ## Documentation [Section titled “Documentation”](#documentation) Check the [complete documentation](/docs/plugins/android-usagestatsmanager/getting-started/) for detailed implementation guides and advanced usage patterns. # Getting Started > Learn how to install and use the Android Usage Stats Manager plugin to access app usage statistics and device usage data. ## Installation [Section titled “Installation”](#installation) * npm ```bash npm install @capgo/capacitor-android-usagestatsmanager npx cap sync ``` * yarn ```bash yarn add @capgo/capacitor-android-usagestatsmanager npx cap sync ``` * pnpm ```bash pnpm add @capgo/capacitor-android-usagestatsmanager npx cap sync ``` * bun ```bash bun add @capgo/capacitor-android-usagestatsmanager npx cap sync ``` ## Platform Configuration [Section titled “Platform Configuration”](#platform-configuration) ### Android [Section titled “Android”](#android) Add the required permissions to your `android/app/src/main/AndroidManifest.xml`: ```xml ``` ## Usage Example [Section titled “Usage Example”](#usage-example) ```typescript import { AndroidUsageStatsManager } from '@capgo/capacitor-android-usagestatsmanager'; // Check if permission is granted const permissionResult = await AndroidUsageStatsManager.isUsageStatsPermissionGranted(); if (!permissionResult.granted) { // Open settings to grant permission await AndroidUsageStatsManager.openUsageStatsSettings(); return; } // Query usage statistics for the last 7 days const statsOptions = { intervalType: 0, // INTERVAL_DAILY startTime: Date.now() - (7 * 24 * 60 * 60 * 1000), // 7 days ago endTime: Date.now() }; const usageStats = await AndroidUsageStatsManager.queryAndAggregateUsageStats(statsOptions); console.log('Usage statistics:', usageStats); // Get all installed packages const packages = await AndroidUsageStatsManager.queryAllPackages(); console.log('Installed packages:', packages); ``` ## API Reference [Section titled “API Reference”](#api-reference) ### isUsageStatsPermissionGranted() [Section titled “isUsageStatsPermissionGranted()”](#isusagestatspermissiongranted) ```typescript isUsageStatsPermissionGranted() => Promise<{ granted: boolean }> ``` Check if the PACKAGE\_USAGE\_STATS permission is granted. **Returns:** `Promise<{ granted: boolean }>` ### openUsageStatsSettings() [Section titled “openUsageStatsSettings()”](#openusagestatssettings) ```typescript openUsageStatsSettings() => Promise ``` Open the system settings page for granting usage stats permission. ### queryAndAggregateUsageStats(options) [Section titled “queryAndAggregateUsageStats(options)”](#queryandaggregateusagestatsoptions) ```typescript queryAndAggregateUsageStats(options: UsageStatsOptions) => Promise ``` Query detailed usage statistics for installed apps. | Param | Type | | ------------- | ------------------- | | **`options`** | `UsageStatsOptions` | **Returns:** `Promise` ### queryAllPackages() [Section titled “queryAllPackages()”](#queryallpackages) ```typescript queryAllPackages() => Promise ``` Get information about all installed packages on the device. **Returns:** `Promise` ## Interfaces [Section titled “Interfaces”](#interfaces) ### UsageStatsOptions [Section titled “UsageStatsOptions”](#usagestatsoptions) | Prop | Type | Description | | ------------------ | -------- | -------------------------------------------------- | | **`intervalType`** | `number` | Interval type (0=DAILY, 1=WEEKLY, 2=MONTHLY, etc.) | | **`startTime`** | `number` | Start time in milliseconds | | **`endTime`** | `number` | End time in milliseconds | ### UsageStatsResult [Section titled “UsageStatsResult”](#usagestatsresult) Contains usage statistics data for each app: * App usage time and frequency * First and last time used * Total time in foreground * Launch count ### PackagesResult [Section titled “PackagesResult”](#packagesresult) Contains information about installed packages: * Package name and version * Installation time * App labels and icons * System vs user apps ## Permission Handling [Section titled “Permission Handling”](#permission-handling) The plugin requires special permissions that cannot be granted through normal runtime permission requests: 1. **PACKAGE\_USAGE\_STATS**: Allows access to usage statistics 2. **QUERY\_ALL\_PACKAGES**: Required for package information (Android 11+) Users must manually grant these permissions through system settings. Use `openUsageStatsSettings()` to direct users to the appropriate settings page. ## Best Practices [Section titled “Best Practices”](#best-practices) * Always check permission status before querying usage statistics * Handle permission denial gracefully with user-friendly messages * Consider user privacy when collecting usage data * Implement proper data handling and storage practices * Follow Google Play policies for usage data collection ## Use Cases [Section titled “Use Cases”](#use-cases) * **Digital wellbeing apps**: Monitor screen time and app usage * **Parental controls**: Track children’s device usage * **Productivity apps**: Analyze work patterns and focus time * **Analytics**: Understand user behavior and app engagement # @capgo/capacitor-app-attest > One JavaScript API for iOS and Android attestation with native security systems only. Unified JavaScript API Use the same methods on both platforms: `prepare()`, `createAttestation()`, and `createAssertion()`. Native security systems iOS uses Apple App Attest, Android uses Google Play Integrity Standard API. Backend-ready output Normalized response shape with `platform`, `format`, `keyId`, and `token` for server verification. Platform guides Follow [Getting Started](/docs/plugins/app-attest/getting-started/), then configure [iOS](/docs/plugins/app-attest/ios/) and [Android](/docs/plugins/app-attest/android/). # Android Setup & Backend Verification > Configure Google Play Integrity Standard API on Android and verify integrity tokens on your backend. ## Android native system used [Section titled “Android native system used”](#android-native-system-used) On Android, this plugin uses **Google Play Integrity Standard API**: * `prepareIntegrityToken` during `prepare()` * `requestStandardIntegrityToken` for `createAttestation()` and `createAssertion()` ## Requirements [Section titled “Requirements”](#requirements) * Android app distributed through Google Play ecosystem * Google Play services available on device * Play Integrity API enabled for your app * Google Cloud project number configured ## Google setup [Section titled “Google setup”](#google-setup) 1. Enable **Play Integrity API** in your Google Cloud project. 2. Open Play Console and configure Play Integrity access for your app. 3. Provide `cloudProjectNumber` to the plugin. ## Capacitor config [Section titled “Capacitor config”](#capacitor-config) capacitor.config.ts ```ts plugins: { AppAttest: { cloudProjectNumber: '123456789012', }, } ``` You can also pass `cloudProjectNumber` per call in method options. ## Client flow [Section titled “Client flow”](#client-flow) ```typescript import { AppAttest } from '@capgo/capacitor-app-attest'; const { keyId } = await AppAttest.prepare({ cloudProjectNumber: '123456789012', }); const attestation = await AppAttest.createAttestation({ keyId, challenge: 'backend-registration-challenge', }); const assertion = await AppAttest.createAssertion({ keyId, payload: 'backend-request-payload', }); ``` `token` is a Play Integrity token and must be decoded server-side. ## Backend workflow (Android) [Section titled “Backend workflow (Android)”](#backend-workflow-android) ### Registration (`createAttestation`) [Section titled “Registration (createAttestation)”](#registration-createattestation) 1. Backend creates one-time `challenge`. 2. App calls `createAttestation({ keyId, challenge })`. 3. Backend calls Google `decodeIntegrityToken` API. 4. Backend verifies at minimum: * `requestDetails.requestHash === base64url(SHA256(challenge))` * `appIntegrity.packageName` equals your Android application id * `appIntegrity.certificateSha256Digest` contains your release signing cert digest * integrity verdicts match your security policy ### Request protection (`createAssertion`) [Section titled “Request protection (createAssertion)”](#request-protection-createassertion) 1. Backend creates one-time `payload`. 2. App calls `createAssertion({ keyId, payload })`. 3. Backend decodes token and checks `requestHash === base64url(SHA256(payload))`. 4. Enforce replay prevention (single-use + TTL) and integrity verdict policy. ## Android schema [Section titled “Android schema”](#android-schema) ```mermaid sequenceDiagram participant App as Android App participant Plugin as AppAttest plugin participant PlaySDK as Play Integrity SDK participant BE as Backend participant Google as decodeIntegrityToken API App->>Plugin: prepare(cloudProjectNumber) Plugin->>PlaySDK: prepareIntegrityToken() PlaySDK-->>Plugin: provider handle (keyId) BE->>App: one-time challenge App->>Plugin: createAttestation(keyId, challenge) Plugin->>PlaySDK: requestStandardIntegrityToken(requestHash) PlaySDK-->>Plugin: integrity token Plugin-->>App: token + platform + format + keyId App->>BE: token + challenge + keyId BE->>Google: decodeIntegrityToken(token) Google-->>BE: decoded payload BE->>BE: verify requestHash + app identity + verdicts BE->>App: one-time payload App->>Plugin: createAssertion(keyId, payload) Plugin->>PlaySDK: requestStandardIntegrityToken(requestHash) PlaySDK-->>Plugin: integrity token App->>BE: token + payload + keyId BE->>Google: decodeIntegrityToken(token) Google-->>BE: decoded payload BE->>BE: verify requestHash + replay policy ``` ## Minimal backend payload contract [Section titled “Minimal backend payload contract”](#minimal-backend-payload-contract) Registration: ```json { "platform": "android", "format": "google-play-integrity-standard", "keyId": "string", "challenge": "string", "token": "string" } ``` Assertion: ```json { "platform": "android", "format": "google-play-integrity-standard", "keyId": "string", "payload": "string", "token": "string" } ``` # Getting Started > Learn how to install and use App Attest with a unified API for iOS and Android attestation. 1. **Install the package** * npm ```sh npm i @capgo/capacitor-app-attest ``` * pnpm ```sh pnpm add @capgo/capacitor-app-attest ``` * yarn ```sh yarn add @capgo/capacitor-app-attest ``` * bun ```sh bun add @capgo/capacitor-app-attest ``` 2. **Sync native projects** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` 3. **Configure platform requirements** * Complete [iOS setup](/docs/plugins/app-attest/ios/) for App Attest capability and backend verification flow. * Complete [Android setup](/docs/plugins/app-attest/android/) for Play Integrity Standard and backend verification flow. ## Why use this plugin [Section titled “Why use this plugin”](#why-use-this-plugin) This plugin provides one cross-platform API while keeping native platform security: * iOS: Apple App Attest (`DeviceCheck`) * Android: Google Play Integrity Standard API * No custom client-side crypto scheme * Normalized outputs for backend checks ## Usage [Section titled “Usage”](#usage) ```typescript import { AppAttest } from '@capgo/capacitor-app-attest'; const support = await AppAttest.isSupported(); if (!support.isSupported) { throw new Error(`Attestation not supported on ${support.platform}`); } const prepared = await AppAttest.prepare(); const registration = await AppAttest.createAttestation({ keyId: prepared.keyId, challenge: 'backend-one-time-registration-challenge', }); const assertion = await AppAttest.createAssertion({ keyId: prepared.keyId, payload: 'backend-one-time-request-payload', }); console.log(registration.platform, registration.format, registration.token); console.log(assertion.platform, assertion.format, assertion.token); ``` ## Unified response shape [Section titled “Unified response shape”](#unified-response-shape) `createAttestation()` and `createAssertion()` return the same key fields on iOS and Android: | Field | Type | Description | | ---------- | ----------------------------- | ------------------------------------------------------ | | `platform` | `'ios' \| 'android' \| 'web'` | Native platform that produced the token | | `format` | `AttestationFormat` | `apple-app-attest` or `google-play-integrity-standard` | | `keyId` | `string` | Key/provider handle used for attestation | | `token` | `string` | Token to verify on your backend | ## Backend requirement [Section titled “Backend requirement”](#backend-requirement) Attestation is only useful when verified server-side. * Never trust client-only success. * Require one-time challenge/payload values from your backend. * Verify `token`, app identity, and replay protections in backend logic. Use the platform-specific backend guides: * [iOS setup and backend verification](/docs/plugins/app-attest/ios/) * [Android setup and backend verification](/docs/plugins/app-attest/android/) # iOS Setup & Backend Verification > Configure Apple App Attest on iOS and verify attestation/assertion payloads on your backend. ## iOS native system used [Section titled “iOS native system used”](#ios-native-system-used) On iOS, this plugin uses **Apple App Attest** from the `DeviceCheck` framework. ## Requirements [Section titled “Requirements”](#requirements) * iOS 14+ * Physical device recommended for real validation flows * Xcode target with App Attest capability enabled ## Xcode setup [Section titled “Xcode setup”](#xcode-setup) 1. Open your iOS app target in Xcode. 2. Go to **Signing & Capabilities**. 3. Click **+ Capability** and add **App Attest**. No custom iOS permissions are required in `Info.plist` for App Attest itself. ## Client flow [Section titled “Client flow”](#client-flow) ```typescript import { AppAttest } from '@capgo/capacitor-app-attest'; const { keyId } = await AppAttest.prepare(); const attestation = await AppAttest.createAttestation({ keyId, challenge: 'backend-registration-challenge', }); const assertion = await AppAttest.createAssertion({ keyId, payload: 'backend-request-payload', }); ``` Send `attestation.token` and `assertion.token` to your backend. Do not validate them in the app. ## Backend workflow (iOS) [Section titled “Backend workflow (iOS)”](#backend-workflow-ios) ### Registration (`createAttestation`) [Section titled “Registration (createAttestation)”](#registration-createattestation) 1. Backend creates one-time `challenge`. 2. App calls `createAttestation({ keyId, challenge })`. 3. Backend verifies App Attest attestation: * certificate chain is valid and anchored to Apple App Attest * app identity matches your app (`bundleId`, team) * `clientDataHash` matches `SHA256(challenge)` 4. Store device key state (`keyId`, public key, and verifier metadata). ### Request protection (`createAssertion`) [Section titled “Request protection (createAssertion)”](#request-protection-createassertion) 1. Backend creates one-time `payload` (or canonical request hash input). 2. App calls `createAssertion({ keyId, payload })`. 3. Backend verifies assertion signature with previously stored key material. 4. Enforce replay protection and nonce TTL checks. ## iOS schema [Section titled “iOS schema”](#ios-schema) ```mermaid sequenceDiagram participant App as iOS App participant Plugin as AppAttest plugin participant Apple as Apple App Attest participant BE as Backend BE->>App: one-time challenge App->>Plugin: prepare() Plugin->>Apple: generateKey() Apple-->>Plugin: keyId App->>Plugin: createAttestation(keyId, challenge) Plugin->>Apple: attestKey(keyId, SHA256(challenge)) Apple-->>Plugin: attestation token Plugin-->>App: token + platform + format + keyId App->>BE: token + challenge + keyId BE->>BE: verify Apple attestation rules BE->>App: one-time payload App->>Plugin: createAssertion(keyId, payload) Plugin->>Apple: generateAssertion(keyId, SHA256(payload)) Apple-->>Plugin: assertion token Plugin-->>App: token + platform + format + keyId App->>BE: token + payload + keyId BE->>BE: verify signature + replay policy ``` ## Minimal backend payload contract [Section titled “Minimal backend payload contract”](#minimal-backend-payload-contract) Registration: ```json { "platform": "ios", "format": "apple-app-attest", "keyId": "string", "challenge": "string", "token": "string" } ``` Assertion: ```json { "platform": "ios", "format": "apple-app-attest", "keyId": "string", "payload": "string", "token": "string" } ``` # @capgo/capacitor-app-tracking-transparency > Handle Apple's App Tracking Transparency requirements with this lightweight Capacitor plugin. Request user authorization to access app-related data for tracking. iOS 14+ Compliance Handle Apple’s App Tracking Transparency requirements easily Cross-platform Returns `authorized` on Android/Web for transparent cross-platform usage Lightweight Minimal footprint with no dependencies Comprehensive Documentation Check the [Documentation](/docs/plugins/app-tracking-transparency/getting-started/) to master the plugin in just a few minutes. # Getting Started > Learn how to install and use the App Tracking Transparency plugin to handle iOS tracking permissions in your Capacitor app. 1. **Install the package** * npm ```sh npm i @capgo/capacitor-app-tracking-transparency ``` * pnpm ```sh pnpm add @capgo/capacitor-app-tracking-transparency ``` * yarn ```sh yarn add @capgo/capacitor-app-tracking-transparency ``` * bun ```sh bun add @capgo/capacitor-app-tracking-transparency ``` 2. **Sync with native projects** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## iOS Setup [Section titled “iOS Setup”](#ios-setup) Add the `NSUserTrackingUsageDescription` key to your `Info.plist` file: ```xml NSUserTrackingUsageDescription This identifier will be used to deliver personalized ads to you. ``` The string should explain why you need tracking permission. This message will be displayed to users when requesting permission. ## Usage [Section titled “Usage”](#usage) Import the plugin and use its methods to handle tracking authorization: ```typescript import { AppTrackingTransparency } from '@capgo/capacitor-app-tracking-transparency'; // Check current tracking status const checkStatus = async () => { const { status } = await AppTrackingTransparency.getStatus(); console.log('Tracking status:', status); }; // Request tracking permission const requestPermission = async () => { const { status } = await AppTrackingTransparency.requestPermission(); console.log('User chose:', status); }; ``` ## API Reference [Section titled “API Reference”](#api-reference) ### getStatus() [Section titled “getStatus()”](#getstatus) Gets the current tracking authorization status without prompting the user. ```typescript const { status } = await AppTrackingTransparency.getStatus(); // Returns: 'authorized' | 'denied' | 'notDetermined' | 'restricted' ``` ### requestPermission() [Section titled “requestPermission()”](#requestpermission) Requests user authorization to access app-related data for tracking. Displays the native iOS tracking permission dialog. ```typescript const { status } = await AppTrackingTransparency.requestPermission(); // Returns: 'authorized' | 'denied' | 'notDetermined' | 'restricted' ``` Note This method will only show the dialog once. Subsequent calls return the stored authorization status without showing the dialog. ## Complete Example [Section titled “Complete Example”](#complete-example) ```typescript import { AppTrackingTransparency } from '@capgo/capacitor-app-tracking-transparency'; export class TrackingService { async requestTrackingIfNeeded(): Promise { // Check current status first const { status } = await AppTrackingTransparency.getStatus(); if (status === 'notDetermined') { // User hasn't been asked yet, show the dialog const result = await AppTrackingTransparency.requestPermission(); return result.status === 'authorized'; } return status === 'authorized'; } async isTrackingAuthorized(): Promise { const { status } = await AppTrackingTransparency.getStatus(); return status === 'authorized'; } } ``` ## Status Values [Section titled “Status Values”](#status-values) | Status | Description | | --------------- | ------------------------------------------------ | | `authorized` | User has authorized tracking | | `denied` | User has denied tracking | | `notDetermined` | User hasn’t been asked yet | | `restricted` | Tracking is restricted (e.g., parental controls) | ## Platform Notes [Section titled “Platform Notes”](#platform-notes) ### iOS [Section titled “iOS”](#ios) * Requires iOS 14.0+ * Uses Apple’s `ATTrackingManager` framework * The permission dialog is shown only once per app install ### Android [Section titled “Android”](#android) * Returns `authorized` status automatically * No permission dialog is shown (ATT is iOS-only) ### Web [Section titled “Web”](#web) * Returns `authorized` status for development purposes ## Best Practices [Section titled “Best Practices”](#best-practices) 1. **Request at the right time** Don’t request permission on app launch. Wait until the user reaches a feature that benefits from tracking, and explain the value first. 2. **Check before requesting** Always check `getStatus()` before calling `requestPermission()` to avoid unnecessary API calls. 3. **Handle all statuses** ```typescript const { status } = await AppTrackingTransparency.getStatus(); switch (status) { case 'authorized': // Enable personalized features break; case 'denied': case 'restricted': // Use non-personalized alternatives break; case 'notDetermined': // Consider requesting permission break; } ``` # @capgo/capacitor-appinsights > Track app usage, user behavior, and performance metrics with AppInsights integration for your Capacitor apps. Analytics Tracking Track user behavior and app usage with AppInsights User Identification Set and manage user IDs for personalized tracking SDK State Management Monitor SDK initialization and permission status Comprehensive Documentation Check the [Documentation](/docs/plugins/appinsights/getting-started/) to integrate analytics in minutes. # Getting Started > Learn how to install and use the AppInsights plugin for application analytics in your Capacitor app. 1. **Install the package** * npm ```sh npm i @capgo/capacitor-appinsights ``` * pnpm ```sh pnpm add @capgo/capacitor-appinsights ``` * yarn ```sh yarn add @capgo/capacitor-appinsights ``` * bun ```sh bun add @capgo/capacitor-appinsights ``` 2. **Sync with native projects** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Usage [Section titled “Usage”](#usage) Import the plugin and initialize it with your AppInsights credentials: ```typescript import { CapacitorAppinsights } from '@capgo/capacitor-appinsights'; // Initialize the SDK const initializeAnalytics = async () => { await CapacitorAppinsights.init({ partnerId: 'your-partner-id', partnerKey: 'your-partner-key' }); }; // Set user ID for tracking const setUser = async (userId: string) => { await CapacitorAppinsights.setUserId({ userId }); }; // Get SDK state const checkSDKState = async () => { const state = await CapacitorAppinsights.getState(); console.log('SDK State:', state); }; // Get plugin version const checkVersion = async () => { const { version } = await CapacitorAppinsights.getPluginVersion(); console.log('Plugin version:', version); }; ``` ## API Reference [Section titled “API Reference”](#api-reference) ### init(options) [Section titled “init(options)”](#initoptions) Initialize the AppInsights SDK with your credentials. ```typescript await CapacitorAppinsights.init({ partnerId: 'your-partner-id', partnerKey: 'your-partner-key' }); ``` **Parameters:** * `partnerId` (string): Your AppInsights partner ID * `partnerKey` (string): Your AppInsights partner key ### setUserId(options) [Section titled “setUserId(options)”](#setuseridoptions) Set or update the user ID after initialization. ```typescript await CapacitorAppinsights.setUserId({ userId: 'user-123' }); ``` **Parameters:** * `userId` (string): The user ID to set for tracking ### getState() [Section titled “getState()”](#getstate) Get the current state of the SDK. ```typescript const state = await CapacitorAppinsights.getState(); console.log(state); // { // initCompleted: true, // jobScheduled: true, // permissionAcquired: true // } ``` **Returns:** * `initCompleted` (boolean): Whether SDK initialization is complete * `jobScheduled` (boolean): Whether data collection job is scheduled * `permissionAcquired` (boolean): Whether necessary permissions are acquired ### getPluginVersion() [Section titled “getPluginVersion()”](#getpluginversion) Get the native Capacitor plugin version. ```typescript const { version } = await CapacitorAppinsights.getPluginVersion(); console.log('Version:', version); ``` ## Complete Example [Section titled “Complete Example”](#complete-example) ```typescript import { CapacitorAppinsights } from '@capgo/capacitor-appinsights'; export class AnalyticsService { private initialized = false; async initialize(partnerId: string, partnerKey: string) { try { await CapacitorAppinsights.init({ partnerId, partnerKey }); const state = await CapacitorAppinsights.getState(); this.initialized = state.initCompleted; console.log('AppInsights initialized:', state); } catch (error) { console.error('Failed to initialize AppInsights:', error); } } async identifyUser(userId: string) { if (!this.initialized) { throw new Error('AppInsights not initialized'); } await CapacitorAppinsights.setUserId({ userId }); console.log('User identified:', userId); } async getSDKStatus() { const state = await CapacitorAppinsights.getState(); return { ready: state.initCompleted && state.permissionAcquired, tracking: state.jobScheduled, state }; } } ``` ## Best Practices [Section titled “Best Practices”](#best-practices) 1. **Initialize early** Initialize the SDK as soon as your app starts to ensure accurate tracking: ```typescript // In your app initialization await CapacitorAppinsights.init({ partnerId: process.env.APPINSIGHTS_PARTNER_ID, partnerKey: process.env.APPINSIGHTS_PARTNER_KEY }); ``` 2. **Set user ID on login** Identify users after authentication: ```typescript async function onUserLogin(user) { await CapacitorAppinsights.setUserId({ userId: user.id }); } ``` 3. **Check SDK state** Verify SDK is ready before critical operations: ```typescript const state = await CapacitorAppinsights.getState(); if (!state.initCompleted) { console.warn('AppInsights not ready'); } ``` 4. **Handle errors gracefully** ```typescript try { await CapacitorAppinsights.init(config); } catch (error) { console.error('Analytics initialization failed:', error); // Continue app operation even if analytics fails } ``` ## Platform Notes [Section titled “Platform Notes”](#platform-notes) ### iOS [Section titled “iOS”](#ios) * Requires iOS 10.0+ * Ensure you have the necessary permissions in Info.plist for tracking ### Android [Section titled “Android”](#android) * Requires Android 5.0 (API 21)+ * May require usage stats permission depending on tracking features ### Web [Section titled “Web”](#web) * Not supported on web platform # @capgo/capacitor-audio-recorder > High-quality audio recording for your Capacitor applications Cross-Platform Record audio on iOS, Android, and Web with a unified API 🌐 Multiple Formats Support for AAC, MP3, and WAV encoding 🎵 Configurable Quality Control sample rate, bit rate, and channels for optimal quality 🎚️ Comprehensive Documentation Check the [Documentation](/docs/plugins/audio-recorder/getting-started/) to master the plugin in just a few minutes. # Getting Started with Audio Recorder > Learn how to integrate audio recording into your Capacitor app This guide will walk you through integrating the Capacitor Audio Recorder plugin into your application. ## Installation [Section titled “Installation”](#installation) Install the plugin using npm: ```bash npm install @capgo/capacitor-audio-recorder npx cap sync ``` ## iOS Configuration [Section titled “iOS Configuration”](#ios-configuration) Add the following to your `Info.plist`: ```xml NSMicrophoneUsageDescription This app needs access to the microphone to record audio ``` ## Android Configuration [Section titled “Android Configuration”](#android-configuration) Add the following permissions to your `AndroidManifest.xml`: ```xml ``` ## Web Configuration [Section titled “Web Configuration”](#web-configuration) The plugin uses the MediaRecorder API. Requires HTTPS in production. ## Basic Usage [Section titled “Basic Usage”](#basic-usage) ### Import the Plugin [Section titled “Import the Plugin”](#import-the-plugin) ```typescript import { AudioRecorder } from '@capgo/capacitor-audio-recorder'; ``` ### Request Permissions [Section titled “Request Permissions”](#request-permissions) ```typescript const requestPermission = async () => { const permission = await AudioRecorder.requestPermissions(); console.log('Permission status:', permission.recordAudio); }; ``` ### Start Recording [Section titled “Start Recording”](#start-recording) ```typescript const startRecording = async () => { await AudioRecorder.startRecording(); console.log('Recording started'); }; ``` ### Stop Recording [Section titled “Stop Recording”](#stop-recording) ```typescript const stopRecording = async () => { const result = await AudioRecorder.stopRecording(); console.log('Recording path:', result.filePath); console.log('Duration:', result.duration); }; ``` ### Pause/Resume Recording [Section titled “Pause/Resume Recording”](#pauseresume-recording) ```typescript const pauseRecording = async () => { await AudioRecorder.pauseRecording(); console.log('Recording paused'); }; const resumeRecording = async () => { await AudioRecorder.resumeRecording(); console.log('Recording resumed'); }; ``` ### Get Status [Section titled “Get Status”](#get-status) ```typescript const getStatus = async () => { const status = await AudioRecorder.getStatus(); console.log('Is recording:', status.isRecording); console.log('Duration:', status.currentTime); }; ``` ## Complete Example [Section titled “Complete Example”](#complete-example) Here’s a complete voice recorder implementation: ```typescript import { AudioRecorder } from '@capgo/capacitor-audio-recorder'; class VoiceRecorder { private isRecording = false; private isPaused = false; private recordingPath: string | null = null; async initialize() { const permission = await AudioRecorder.checkPermissions(); if (permission.recordAudio !== 'granted') { const requested = await AudioRecorder.requestPermissions(); if (requested.recordAudio !== 'granted') { throw new Error('Microphone permission denied'); } } } async startRecording() { try { await AudioRecorder.startRecording(); this.isRecording = true; this.isPaused = false; console.log('Recording started'); } catch (error) { console.error('Failed to start recording:', error); throw error; } } async pauseRecording() { if (!this.isRecording || this.isPaused) { return; } try { await AudioRecorder.pauseRecording(); this.isPaused = true; console.log('Recording paused'); } catch (error) { console.error('Failed to pause recording:', error); throw error; } } async resumeRecording() { if (!this.isRecording || !this.isPaused) { return; } try { await AudioRecorder.resumeRecording(); this.isPaused = false; console.log('Recording resumed'); } catch (error) { console.error('Failed to resume recording:', error); throw error; } } async stopRecording() { if (!this.isRecording) { return null; } try { const result = await AudioRecorder.stopRecording(); this.isRecording = false; this.isPaused = false; this.recordingPath = result.filePath; console.log('Recording stopped'); console.log('File path:', result.filePath); console.log('Duration:', result.duration, 'seconds'); return result; } catch (error) { console.error('Failed to stop recording:', error); throw error; } } async getCurrentStatus() { const status = await AudioRecorder.getStatus(); return { isRecording: status.isRecording, duration: status.currentTime, isPaused: this.isPaused }; } formatDuration(seconds: number): string { const mins = Math.floor(seconds / 60); const secs = Math.floor(seconds % 60); return `${mins}:${secs.toString().padStart(2, '0')}`; } getRecordingPath(): string | null { return this.recordingPath; } } // Usage const recorder = new VoiceRecorder(); // Initialize await recorder.initialize(); // Start recording await recorder.startRecording(); // Pause await recorder.pauseRecording(); // Resume await recorder.resumeRecording(); // Stop and get result const result = await recorder.stopRecording(); console.log('Recording saved:', result?.filePath); ``` ## Advanced Configuration [Section titled “Advanced Configuration”](#advanced-configuration) ### Configure Audio Quality [Section titled “Configure Audio Quality”](#configure-audio-quality) ```typescript const startHighQualityRecording = async () => { await AudioRecorder.startRecording({ format: 'aac', // aac, mp3, or wav sampleRate: 44100, // 44100 Hz (CD quality) channels: 2, // Stereo bitRate: 320000 // 320 kbps }); }; ``` ### Configure for Voice [Section titled “Configure for Voice”](#configure-for-voice) ```typescript const startVoiceRecording = async () => { await AudioRecorder.startRecording({ format: 'aac', sampleRate: 16000, // 16 kHz (voice optimized) channels: 1, // Mono bitRate: 64000 // 64 kbps }); }; ``` ## UI Integration Example [Section titled “UI Integration Example”](#ui-integration-example) ```typescript class AudioRecorderUI { private recorder: VoiceRecorder; private updateInterval: any; constructor() { this.recorder = new VoiceRecorder(); } async init() { await this.recorder.initialize(); this.setupEventListeners(); } setupEventListeners() { const recordButton = document.getElementById('record-btn'); const pauseButton = document.getElementById('pause-btn'); const stopButton = document.getElementById('stop-btn'); recordButton?.addEventListener('click', () => this.handleRecord()); pauseButton?.addEventListener('click', () => this.handlePause()); stopButton?.addEventListener('click', () => this.handleStop()); } async handleRecord() { await this.recorder.startRecording(); this.startDurationUpdate(); this.updateUI('recording'); } async handlePause() { const status = await this.recorder.getCurrentStatus(); if (status.isPaused) { await this.recorder.resumeRecording(); this.updateUI('recording'); } else { await this.recorder.pauseRecording(); this.updateUI('paused'); } } async handleStop() { const result = await this.recorder.stopRecording(); this.stopDurationUpdate(); this.updateUI('stopped'); if (result) { this.showRecordingResult(result); } } startDurationUpdate() { this.updateInterval = setInterval(async () => { const status = await this.recorder.getCurrentStatus(); this.updateDurationDisplay(status.duration); }, 100); } stopDurationUpdate() { if (this.updateInterval) { clearInterval(this.updateInterval); } } updateDurationDisplay(duration: number) { const display = document.getElementById('duration'); if (display) { display.textContent = this.recorder.formatDuration(duration); } } updateUI(state: 'recording' | 'paused' | 'stopped') { // Update button states, colors, etc. console.log('UI state:', state); } showRecordingResult(result: any) { console.log('Recording complete:', result); // Show playback UI, save options, etc. } } // Initialize UI const ui = new AudioRecorderUI(); ui.init(); ``` ## Best Practices [Section titled “Best Practices”](#best-practices) 1. **Request Permissions Early**: Check permissions before showing recording UI 2. **Handle Interruptions**: Phone calls, alarms can interrupt recording 3. **Manage Storage**: Clean up old recordings to save space 4. **Provide Feedback**: Show recording status, duration, and waveform 5. **Test on Devices**: Simulators/emulators have limited audio support ## Common Issues [Section titled “Common Issues”](#common-issues) ### Permission Denied [Section titled “Permission Denied”](#permission-denied) ```typescript const handlePermissionDenied = async () => { const permission = await AudioRecorder.checkPermissions(); if (permission.recordAudio === 'denied') { alert('Microphone permission is required to record audio. Please enable it in Settings.'); } }; ``` ### Recording Interrupted [Section titled “Recording Interrupted”](#recording-interrupted) ```typescript // Handle app going to background document.addEventListener('pause', async () => { const status = await recorder.getCurrentStatus(); if (status.isRecording) { await recorder.pauseRecording(); // Notify user } }); ``` ### Storage Management [Section titled “Storage Management”](#storage-management) ```typescript const cleanupOldRecordings = async () => { // Implement cleanup logic // Delete recordings older than X days // Or keep only last N recordings }; ``` ## Next Steps [Section titled “Next Steps”](#next-steps) * Explore the [API Reference](https://github.com/Cap-go/capacitor-audio-recorder#api) for complete method documentation * Check out the [example app](https://github.com/Cap-go/capacitor-audio-recorder/tree/main/example) for advanced usage * See the [tutorial](/plugins/capacitor-audio-recorder) for complete implementation examples # @capgo/capacitor-plugin-audiosession > Listen for audio route changes, react to interruptions, and reroute playback to the speaker without leaving Capacitor. Capgo’s AudioSession plugin gives you fine-grained control over the iOS AVAudioSession layer so you can gracefully handle headphones, Bluetooth devices, and unexpected interruptions. Route awareness Detect when users plug or unplug headsets, AirPods, Bluetooth speakers, and more. Speaker override Force playback through the built-in speaker when you need hands-free audio. Interruption handling React to interruption events such as phone calls or Siri to pause and resume audio safely. Type safety Strong TypeScript definitions make it easy to build resilient audio experiences. This plugin targets iOS where AVAudioSession control is required. On other platforms the calls resolve without effect, letting you keep one shared code path. # Getting Started > Configure the AudioSession plugin to react to iOS audio route changes and interruptions. > ℹ️ **Platform support:** The audio session API is available on iOS. Calls on Android and the web resolve without effect so you can keep shared logic. 1. **Install the package** * npm ```sh npm i @capgo/capacitor-plugin-audiosession ``` * pnpm ```sh pnpm add @capgo/capacitor-plugin-audiosession ``` * yarn ```sh yarn add @capgo/capacitor-plugin-audiosession ``` * bun ```sh bun add @capgo/capacitor-plugin-audiosession ``` 2. **Sync iOS platform** * npm ```sh npx cap sync ios ``` * pnpm ```sh pnpm cap sync ios ``` * yarn ```sh yarn cap sync ios ``` * bun ```sh bunx cap sync ios ``` ## Inspect available outputs [Section titled “Inspect available outputs”](#inspect-available-outputs) ```typescript import { AudioSession, AudioSessionPorts } from '@capgo/capacitor-plugin-audiosession'; const outputs = await AudioSession.currentOutputs(); if (outputs.includes(AudioSessionPorts.BLUETOOTH_A2DP)) { console.log('Bluetooth speaker connected'); } ``` ## Override to speaker mode [Section titled “Override to speaker mode”](#override-to-speaker-mode) ```typescript import { AudioSession, OutputOverrideType } from '@capgo/capacitor-plugin-audiosession'; await AudioSession.overrideOutput(OutputOverrideType.speaker); // Restore system routing when you are done await AudioSession.overrideOutput(OutputOverrideType.default); ``` ## Listen for route and interruption events [Section titled “Listen for route and interruption events”](#listen-for-route-and-interruption-events) ```typescript import { AudioSession, RouteChangeReasons, InterruptionTypes } from '@capgo/capacitor-plugin-audiosession'; const routeListener = await AudioSession.addListener('routeChanged', (reason) => { if (reason === RouteChangeReasons.NEW_DEVICE_AVAILABLE) { console.log('User connected a new audio route'); } }); const interruptionListener = await AudioSession.addListener('interruption', (type) => { if (type === InterruptionTypes.BEGAN) { // Pause playback while we are interrupted } else { // Resume playback } }); // Later during cleanup await routeListener.remove(); await interruptionListener.remove(); ``` ## Entitlements [Section titled “Entitlements”](#entitlements) * **iOS**: Add any required background audio modes (e.g. `audio`, `voice`) inside `ios/App/App/Info.plist` if your app plays audio in the background. * **Other platforms**: No additional setup is necessary; methods will resolve with empty results. # @capgo/capacitor-autofill-save-password > Enable password autofill and credential management with system integration for seamless authentication experiences. ## Overview [Section titled “Overview”](#overview) The Capacitor Autofill Save Password plugin provides password saving and autofill functionality for Capacitor applications. This plugin integrates with system-level credential management to offer seamless authentication experiences with secure password storage and retrieval. Password saving Save credentials to system keychain securely 🔐 Autofill integration System-level password autofill support 🗝️ Cross-platform iOS support with Android development in progress 📱 Domain association Associated domains for seamless authentication ❤️ ## Installation [Section titled “Installation”](#installation) ```bash npm install @capgo/capacitor-autofill-save-password npx cap sync ``` ## Platform Support [Section titled “Platform Support”](#platform-support) * **iOS**: Full support (works with iOS 18.3 and older versions) * **Android**: Work in progress ## iOS Configuration [Section titled “iOS Configuration”](#ios-configuration) ### 1. Associated Domains Setup [Section titled “1. Associated Domains Setup”](#1-associated-domains-setup) Configure associated domains in your Apple Developer account and add them to your `App.entitlements` file: ```xml com.apple.developer.associated-domains webcredentials:yourdomain.com webcredentials:www.yourdomain.com ``` ### 2. Web Credential Configuration [Section titled “2. Web Credential Configuration”](#2-web-credential-configuration) Add a `.well-known/apple-app-site-association` file to your website: ```json { "webcredentials": { "apps": [ "TEAMID.com.yourcompany.yourapp" ] } } ``` ## Core API Methods [Section titled “Core API Methods”](#core-api-methods) ### Password Management [Section titled “Password Management”](#password-management) * `promptDialog(options)` - Save password to system keychain * `readPassword()` - Read saved password from keychain ## Usage Example [Section titled “Usage Example”](#usage-example) ```typescript import { SavePassword } from '@capgo/capacitor-autofill-save-password'; // Save password after successful login async function login(username: string, password: string) { try { // Perform your login logic here const loginSuccess = await performLogin(username, password); if (loginSuccess) { // Prompt user to save password await SavePassword.promptDialog({ username: username, password: password, url: 'https://yourdomain.com' // iOS only }); console.log('Password saved successfully'); } } catch (error) { console.error('Failed to save password:', error); } } // Read saved password for autofill async function loadSavedCredentials() { try { const credentials = await SavePassword.readPassword(); if (credentials.username && credentials.password) { console.log('Found saved credentials'); // Pre-fill login form return { username: credentials.username, password: credentials.password }; } } catch (error) { console.error('Failed to read saved password:', error); } return null; } ``` ## Android Configuration (Work in Progress) [Section titled “Android Configuration (Work in Progress)”](#android-configuration-work-in-progress) For future Android support, the following configuration will be required: ### 1. Google Services Plugin [Section titled “1. Google Services Plugin”](#1-google-services-plugin) Add to `android/app/build.gradle`: ```kotlin apply plugin: 'com.google.gms.google-services' ``` ### 2. Domain Configuration [Section titled “2. Domain Configuration”](#2-domain-configuration) Add to `android/app/src/main/res/values/strings.xml`: ```xml [{ "relation": ["delegate_permission/common.handle_all_urls"], "target": { "namespace": "web", "site": "https://yourdomain.com" } }] ``` ### 3. Google Services JSON [Section titled “3. Google Services JSON”](#3-google-services-json) Add `google-services.json` to your Android project. ## API Reference [Section titled “API Reference”](#api-reference) ### promptDialog(options) [Section titled “promptDialog(options)”](#promptdialogoptions) Saves user credentials to the system keychain. ```typescript interface SavePasswordOptions { username: string; password: string; url?: string; // iOS only - associated domain URL } ``` ### readPassword() [Section titled “readPassword()”](#readpassword) Retrieves saved credentials from the system keychain. ```typescript interface SavedCredentials { username: string; password: string; } ``` ## Integration with Login Flow [Section titled “Integration with Login Flow”](#integration-with-login-flow) ```typescript import { SavePassword } from '@capgo/capacitor-autofill-save-password'; class AuthService { async login(username: string, password: string) { try { // Authenticate with your backend const response = await fetch('/api/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username, password }) }); if (response.ok) { // Offer to save credentials await this.offerToSavePassword(username, password); return true; } } catch (error) { console.error('Login failed:', error); } return false; } private async offerToSavePassword(username: string, password: string) { try { await SavePassword.promptDialog({ username, password, url: 'https://yourdomain.com' }); } catch (error) { // User declined to save or error occurred console.log('Password not saved:', error); } } async loadSavedCredentials() { try { return await SavePassword.readPassword(); } catch (error) { console.log('No saved credentials found'); return null; } } } ``` ## Best Practices [Section titled “Best Practices”](#best-practices) * Only prompt to save passwords after successful authentication * Handle cases where users decline to save passwords gracefully * Implement proper error handling for keychain access failures * Use associated domains for seamless web-app credential sharing * Test autofill functionality across different iOS versions ## Security Considerations [Section titled “Security Considerations”](#security-considerations) * Credentials are stored in the system keychain with appropriate security flags * Associated domains ensure credentials are only accessible to authorized apps * Follow platform security guidelines for credential management * Consider implementing additional security measures for sensitive applications ## Limitations [Section titled “Limitations”](#limitations) * Android support is currently under development * iOS requires proper associated domains configuration * Autofill behavior may vary across different iOS versions * Requires user consent for saving and accessing credentials ## Documentation [Section titled “Documentation”](#documentation) Check the [complete documentation](/docs/plugins/autofill-save-password/getting-started/) for detailed implementation guides and advanced integration patterns. # Getting Started > Learn how to install and configure the Autofill Save Password plugin for seamless password management in your Capacitor app. ## Installation [Section titled “Installation”](#installation) * npm ```bash npm install @capgo/capacitor-autofill-save-password npx cap sync ``` * yarn ```bash yarn add @capgo/capacitor-autofill-save-password npx cap sync ``` * pnpm ```bash pnpm add @capgo/capacitor-autofill-save-password npx cap sync ``` * bun ```bash bun add @capgo/capacitor-autofill-save-password npx cap sync ``` ## Platform Support [Section titled “Platform Support”](#platform-support) * **iOS**: Full support (iOS 18.3 and older versions) * **Android**: Work in progress ## Platform Configuration [Section titled “Platform Configuration”](#platform-configuration) ### iOS [Section titled “iOS”](#ios) #### 1. Associated Domains Setup [Section titled “1. Associated Domains Setup”](#1-associated-domains-setup) Configure associated domains in your Apple Developer account and add them to your `App.entitlements` file: ```xml com.apple.developer.associated-domains webcredentials:yourdomain.com webcredentials:www.yourdomain.com ``` #### 2. Web Credential Configuration [Section titled “2. Web Credential Configuration”](#2-web-credential-configuration) Add a `.well-known/apple-app-site-association` file to your website: ```json { "webcredentials": { "apps": [ "TEAMID.com.yourcompany.yourapp" ] } } ``` ### Android (Future Support) [Section titled “Android (Future Support)”](#android-future-support) #### 1. Google Services Plugin [Section titled “1. Google Services Plugin”](#1-google-services-plugin) Add to `android/app/build.gradle`: ```kotlin apply plugin: 'com.google.gms.google-services' ``` #### 2. Domain Configuration [Section titled “2. Domain Configuration”](#2-domain-configuration) Add to `android/app/src/main/res/values/strings.xml`: ```xml [{ "relation": ["delegate_permission/common.handle_all_urls"], "target": { "namespace": "web", "site": "https://yourdomain.com" } }] ``` ## Usage Example [Section titled “Usage Example”](#usage-example) ```typescript import { SavePassword } from '@capgo/capacitor-autofill-save-password'; // Save password after successful login async function login(username: string, password: string) { try { // Perform your login logic here const loginSuccess = await performLogin(username, password); if (loginSuccess) { // Prompt user to save password await SavePassword.promptDialog({ username: username, password: password, url: 'https://yourdomain.com' // iOS only }); console.log('Password saved successfully'); } } catch (error) { console.error('Failed to save password:', error); } } // Read saved password for autofill async function loadSavedCredentials() { try { const credentials = await SavePassword.readPassword(); if (credentials.username && credentials.password) { console.log('Found saved credentials'); // Pre-fill login form return { username: credentials.username, password: credentials.password }; } } catch (error) { console.error('Failed to read saved password:', error); } return null; } ``` ## API Reference [Section titled “API Reference”](#api-reference) ### promptDialog(options) [Section titled “promptDialog(options)”](#promptdialogoptions) ```typescript promptDialog(options: SavePasswordOptions) => Promise ``` Saves user credentials to the system keychain. | Param | Type | | ------------- | --------------------- | | **`options`** | `SavePasswordOptions` | ### readPassword() [Section titled “readPassword()”](#readpassword) ```typescript readPassword() => Promise ``` Retrieves saved credentials from the system keychain. **Returns:** `Promise` ## Interfaces [Section titled “Interfaces”](#interfaces) ### SavePasswordOptions [Section titled “SavePasswordOptions”](#savepasswordoptions) | Prop | Type | Description | | -------------- | -------- | ------------------------------------------- | | **`username`** | `string` | The username to save | | **`password`** | `string` | The password to save | | **`url`** | `string` | iOS only - associated domain URL (optional) | ### SavedCredentials [Section titled “SavedCredentials”](#savedcredentials) | Prop | Type | Description | | -------------- | -------- | ------------------ | | **`username`** | `string` | The saved username | | **`password`** | `string` | The saved password | ## Integration with Login Flow [Section titled “Integration with Login Flow”](#integration-with-login-flow) ```typescript import { SavePassword } from '@capgo/capacitor-autofill-save-password'; class AuthService { async login(username: string, password: string) { try { // Authenticate with your backend const response = await fetch('/api/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username, password }) }); if (response.ok) { // Offer to save credentials await this.offerToSavePassword(username, password); return true; } } catch (error) { console.error('Login failed:', error); } return false; } private async offerToSavePassword(username: string, password: string) { try { await SavePassword.promptDialog({ username, password, url: 'https://yourdomain.com' }); } catch (error) { // User declined to save or error occurred console.log('Password not saved:', error); } } async loadSavedCredentials() { try { return await SavePassword.readPassword(); } catch (error) { console.log('No saved credentials found'); return null; } } } ``` ## Best Practices [Section titled “Best Practices”](#best-practices) * Only prompt to save passwords after successful authentication * Handle cases where users decline to save passwords gracefully * Implement proper error handling for keychain access failures * Use associated domains for seamless web-app credential sharing * Test autofill functionality across different iOS versions ## Security Considerations [Section titled “Security Considerations”](#security-considerations) * Credentials are stored in the system keychain with appropriate security flags * Associated domains ensure credentials are only accessible to authorized apps * Follow platform security guidelines for credential management * Consider implementing additional security measures for sensitive applications # Background Geolocation > Receive accurate geolocation updates even while the app is backgrounded. Accurate Tracking Prioritizes accuracy for use cases like hiking and navigation 🎯 Background Updates Continues to receive location updates even when the app is in the background 🏃 Battery Efficient Optimized to balance accuracy and battery consumption 🔋 Getting Started Check the [Getting Started Guide](/docs/plugins/background-geolocation/getting-started/) to install and configure the plugin. ## Plugin comparison [Section titled “Plugin comparison”](#plugin-comparison) A short comparison between the three main background-geolocation plugins commonly used in Capacitor apps. | Plugin | Accuracy | Background | HTTP Upload | Pricing | | --------------------------------------------------------- | ------------ | ---------- | ---------------------------------------- | ------- | | `@capacitor-community/background-geolocation` (Community) | Not accurate | Yes | No | Free | | `@capgo/background-geolocation` (this plugin) | Accurate | Yes | No | Free | | Transistorsoft (original) | Accurate | Yes | Yes — built-in HTTP uploader to your API | Paid | **Notes:** * The Community plugin is lightweight and continues to work in the background, but it is known to be less accurate than the options below. * This Capgo plugin is optimized to provide accurate location fixes and reliable background operation without requiring a paid license. * Transistorsoft offers a complete commercial solution that also includes built-in HTTP upload capabilities for sending location data to your backend. # Getting Started > Learn how to install and use the Background Geolocation plugin to track device location in the background. ## Installation [Section titled “Installation”](#installation) This plugin supports Capacitor v7: | Capacitor | Plugin | | --------- | ------ | | v7 | v7 | * npm ```bash npm install @capgo/background-geolocation npx cap update ``` * yarn ```bash yarn add @capgo/background-geolocation npx cap update ``` * pnpm ```bash pnpm add @capgo/background-geolocation npx cap update ``` * bun ```bash bun add @capgo/background-geolocation npx cap update ``` ## Platform Setup [Section titled “Platform Setup”](#platform-setup) ### iOS [Section titled “iOS”](#ios) Add the following keys to `Info.plist.`: ```xml ... NSLocationWhenInUseUsageDescription We need to track your location NSLocationAlwaysAndWhenInUseUsageDescription We need to track your location while your device is locked. UIBackgroundModes location ... ``` ### Android [Section titled “Android”](#android) Set the the `android.useLegacyBridge` option to `true` in your Capacitor configuration. This prevents location updates halting after 5 minutes in the background. See and . On Android 13+, the app needs the `POST_NOTIFICATIONS` runtime permission to show the persistent notification informing the user that their location is being used in the background. This runtime permission is requested after the location permission is granted. If your app forwards location updates to a server in real time, be aware that after 5 minutes in the background Android will throttle HTTP requests initiated from the WebView. The solution is to use a native HTTP plugin such as [CapacitorHttp](https://capacitorjs.com/docs/apis/http). See . Configuration specific to Android can be made in `strings.xml`: ```xml Background Tracking drawable/ic_tracking yellow ``` ## Usage [Section titled “Usage”](#usage) ```javascript import { BackgroundGeolocation } from "@capgo/background-geolocation"; BackgroundGeolocation.start( { backgroundMessage: "Cancel to prevent battery drain.", backgroundTitle: "Tracking You.", requestPermissions: true, stale: false, distanceFilter: 50 }, (location, error) => { if (error) { if (error.code === "NOT_AUTHORIZED") { if (window.confirm( "This app needs your location, " + "but does not have permission.\n\n" + "Open settings now?" )) { // It can be useful to direct the user to their device's // settings when location permissions have been denied. The // plugin provides the 'openSettings' method to do exactly // this. BackgroundGeolocation.openSettings(); } } return console.error(error); } return console.log(location); } ).then(() => { // When location updates are no longer needed, the plugin should be stopped by calling BackgroundGeolocation.stop(); }); // Set a planned route to get a notification sound when a new location arrives and it's not on the route: BackgroundGeolocation.setPlannedRoute({soundFile: "assets/myFile.mp3", route: [[1,2], [3,4]], distance: 30 }); // If you just want the current location, try something like this. The longer // the timeout, the more accurate the guess will be. I wouldn't go below about 100ms. function guessLocation(callback, timeout) { let last_location; BackgroundGeolocation.start( { requestPermissions: false, stale: true }, (location) => { last_location = location || undefined; } ).then(() => { setTimeout(() => { callback(last_location); BackgroundGeolocation.stop(); }, timeout); }); } ``` ## API [Section titled “API”](#api) ### start(…) [Section titled “start(…)”](#start) ```typescript start(options: StartOptions, callback: (position?: Location | undefined, error?: CallbackError | undefined) => void) => Promise ``` To start listening for changes in the device’s location, call this method. A Promise is returned to indicate that it finished the call. The callback will be called every time a new location is available, or if there was an error when calling this method. Don’t rely on promise rejection for this. | Param | Type | Description | | -------------- | ------------------------------------------------------ | --------------------------------------------------------------------------------- | | **`options`** | `StartOptions` | The configuration options | | **`callback`** | `(position?: Location, error?: CallbackError) => void` | The callback function invoked when a new location is available or an error occurs | ### stop() [Section titled “stop()”](#stop) ```typescript stop() => Promise ``` Stops location updates. ### openSettings() [Section titled “openSettings()”](#opensettings) ```typescript openSettings() => Promise ``` Opens the device’s location settings page. Useful for directing users to enable location services or adjust permissions. ### setPlannedRoute(…) [Section titled “setPlannedRoute(…)”](#setplannedroute) ```typescript setPlannedRoute(options: SetPlannedRouteOptions) => Promise ``` Plays a sound file when the user deviates from the planned route. This should be used to play a sound (in the background too, only for native). | Param | Type | Description | | ------------- | ------------------------ | -------------------------------------------------------- | | **`options`** | `SetPlannedRouteOptions` | The options for setting the planned route and sound file | ## Interfaces [Section titled “Interfaces”](#interfaces) ### StartOptions [Section titled “StartOptions”](#startoptions) The options for configuring for location updates. | Prop | Type | Description | Default | | ------------------------ | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------- | | **`backgroundMessage`** | `string` | If the “backgroundMessage” option is defined, the plugin will provide location updates whether the app is in the background or the foreground. If it is not defined, location updates are only guaranteed in the foreground. This is true on both platforms. On Android, a notification must be shown to continue receiving location updates in the background. This option specifies the text of that notification. | | | **`backgroundTitle`** | `string` | The title of the notification mentioned above. | `"Using your location"` | | **`requestPermissions`** | `boolean` | Whether permissions should be requested from the user automatically, if they are not already granted. | `true` | | **`stale`** | `boolean` | If “true”, stale locations may be delivered while the device obtains a GPS fix. You are responsible for checking the “time” property. If “false”, locations are guaranteed to be up to date. | `false` | | **`distanceFilter`** | `number` | The distance in meters that the device must move before a new location update is triggered. This is used to filter out small movements and reduce the number of updates. | `0` | ### Location [Section titled “Location”](#location) Represents a geographical location with various attributes. Contains all the standard location properties returned by GPS/network providers. | Prop | Type | Description | | ---------------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------- | | **`latitude`** | `number` | Latitude in degrees. Range: -90.0 to +90.0 | | **`longitude`** | `number` | Longitude in degrees. Range: -180.0 to +180.0 | | **`accuracy`** | `number` | Radius of horizontal uncertainty in metres, with 68% confidence. Lower values indicate more accurate location. | | **`altitude`** | `number \| null` | Metres above sea level (or null if not available). | | **`altitudeAccuracy`** | `number \| null` | Vertical uncertainty in metres, with 68% confidence (or null if not available). | | **`simulated`** | `boolean` | `true` if the location was simulated by software, rather than GPS. Useful for detecting mock locations in development or testing. | | **`bearing`** | `number \| null` | Deviation from true north in degrees (or null if not available). Range: 0.0 to 360.0 | | **`speed`** | `number \| null` | Speed in metres per second (or null if not available). | | **`time`** | `number \| null` | Time the location was produced, in milliseconds since the unix epoch. Use this to check if a location is stale when using stale: true. | ### CallbackError [Section titled “CallbackError”](#callbackerror) Error object that may be passed to the location start callback. Extends the standard Error with optional error codes. | Prop | Type | Description | | ---------- | -------- | ----------------------------------------------------- | | **`code`** | `string` | Optional error code for more specific error handling. | ### SetPlannedRouteOptions [Section titled “SetPlannedRouteOptions”](#setplannedrouteoptions) | Prop | Type | Description | Default | | --------------- | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | | **`soundFile`** | `string` | The name of the sound file to play. Must be a valid sound relative path in the app’s public folder to work for both web and native platforms. There’s no need to include the public folder in the path. | | | **`route`** | `[number, number][]` | The planned route as an array of longitude and latitude pairs. Each pair represents a point on the route. This is used to define a route that the user can follow. The route is used to play a sound when the user deviates from it. | | | **`distance`** | `number` | The distance in meters that the user must deviate from the planned route to trigger the sound. This is used to determine how far off the route the user can be before the sound is played. If not specified, a default value of 50 meters is used. | `50` | # @capgo/capacitor-barometer > Access barometric pressure and altitude data from device sensors for weather and fitness applications. Pressure Monitoring Real-time atmospheric pressure readings 🌡️ Altitude Calculation Derive relative altitude from pressure changes 📈 Event Listeners Subscribe to pressure change events 🔔 Cross-Platform Works on iOS and Android devices with barometer sensors 📱 Getting Started Check the [Getting Started Guide](/docs/plugins/barometer/getting-started/) to install and configure the plugin. # Getting Started with Barometer > Learn how to integrate barometric pressure sensing into your Capacitor app This guide will walk you through integrating the Capacitor Barometer plugin into your application. ## Installation [Section titled “Installation”](#installation) Install the plugin using npm: ```bash npm install @capgo/capacitor-barometer npx cap sync ``` ## iOS Configuration [Section titled “iOS Configuration”](#ios-configuration) Add the following to your `Info.plist`: ```xml NSMotionUsageDescription This app uses the barometer to measure atmospheric pressure ``` ## Android Configuration [Section titled “Android Configuration”](#android-configuration) No additional configuration required. The plugin automatically requests necessary permissions. ## Basic Usage [Section titled “Basic Usage”](#basic-usage) ### Import the Plugin [Section titled “Import the Plugin”](#import-the-plugin) ```typescript import { Barometer } from '@capgo/capacitor-barometer'; ``` ### Check Sensor Availability [Section titled “Check Sensor Availability”](#check-sensor-availability) ```typescript const checkBarometer = async () => { const { available } = await Barometer.isAvailable(); console.log('Barometer available:', available); }; ``` ### Start Monitoring Pressure [Section titled “Start Monitoring Pressure”](#start-monitoring-pressure) ```typescript const startMonitoring = async () => { await Barometer.start({ interval: 1000 // Update interval in milliseconds }); console.log('Barometer monitoring started'); }; ``` ### Listen to Pressure Events [Section titled “Listen to Pressure Events”](#listen-to-pressure-events) ```typescript Barometer.addListener('pressureChange', (data) => { console.log('Pressure (hPa):', data.pressure); console.log('Relative altitude (m):', data.relativeAltitude); console.log('Timestamp:', data.timestamp); }); ``` ### Get Current Reading [Section titled “Get Current Reading”](#get-current-reading) ```typescript const getCurrentPressure = async () => { const reading = await Barometer.getCurrentReading(); console.log('Current pressure:', reading.pressure, 'hPa'); console.log('Relative altitude:', reading.relativeAltitude, 'm'); }; ``` ### Stop Monitoring [Section titled “Stop Monitoring”](#stop-monitoring) ```typescript const stopMonitoring = async () => { await Barometer.stop(); console.log('Barometer monitoring stopped'); }; ``` ## Complete Example [Section titled “Complete Example”](#complete-example) Here’s a complete example showing barometer integration: ```typescript import { Barometer } from '@capgo/capacitor-barometer'; class BarometerService { private listener: any; async initialize() { // Check availability const { available } = await Barometer.isAvailable(); if (!available) { console.error('Barometer not available on this device'); return; } // Start monitoring await Barometer.start({ interval: 1000 }); // Add listener this.listener = Barometer.addListener('pressureChange', (data) => { this.handlePressureChange(data); }); } handlePressureChange(data: any) { console.log('Pressure:', data.pressure, 'hPa'); console.log('Altitude:', data.relativeAltitude, 'm'); // Update UI or process data this.updateDisplay(data); } updateDisplay(data: any) { // Convert hPa to other units const inHg = (data.pressure * 0.02953).toFixed(2); const mmHg = (data.pressure * 0.750062).toFixed(1); console.log(`Pressure: ${data.pressure.toFixed(1)} hPa`); console.log(` ${inHg} inHg`); console.log(` ${mmHg} mmHg`); console.log(`Altitude: ${data.relativeAltitude.toFixed(1)} m`); } async cleanup() { // Remove listener if (this.listener) { this.listener.remove(); } // Stop monitoring await Barometer.stop(); } } // Usage const barometerService = new BarometerService(); barometerService.initialize(); // Cleanup when done // barometerService.cleanup(); ``` ## Understanding Readings [Section titled “Understanding Readings”](#understanding-readings) ### Atmospheric Pressure [Section titled “Atmospheric Pressure”](#atmospheric-pressure) * Measured in hectopascals (hPa) or millibars (mb) * Standard sea-level pressure: 1013.25 hPa * Higher altitude = lower pressure * Weather systems affect pressure readings ### Relative Altitude [Section titled “Relative Altitude”](#relative-altitude) * Calculated from pressure changes * Relative to starting reference point * Accuracy: ±10-30 meters * Works best for short-term tracking ## Best Practices [Section titled “Best Practices”](#best-practices) 1. **Check Availability**: Always verify sensor availability before use 2. **Reasonable Intervals**: Don’t poll too frequently (1000ms is good) 3. **Remove Listeners**: Clean up listeners when not needed 4. **Calibration**: Use known altitude for accurate readings 5. **Battery Impact**: Monitor battery usage with continuous sensing ## Common Issues [Section titled “Common Issues”](#common-issues) ### Sensor Not Available [Section titled “Sensor Not Available”](#sensor-not-available) ```typescript try { const { available } = await Barometer.isAvailable(); if (!available) { // Fallback to GPS altitude or manual input showAlternativeMethod(); } } catch (error) { console.error('Barometer check failed:', error); } ``` ### Noisy Data [Section titled “Noisy Data”](#noisy-data) ```typescript class PressureFilter { private readings: number[] = []; private maxReadings = 5; addReading(pressure: number): number { this.readings.push(pressure); if (this.readings.length > this.maxReadings) { this.readings.shift(); } // Return moving average const sum = this.readings.reduce((a, b) => a + b, 0); return sum / this.readings.length; } } ``` ## Next Steps [Section titled “Next Steps”](#next-steps) * Explore the [API Reference](https://github.com/Cap-go/capacitor-barometer#api) for complete method documentation * Check out the [example app](https://github.com/Cap-go/capacitor-barometer/tree/main/example) for advanced usage * See the [tutorial](/plugins/capacitor-barometer) for complete implementation examples # @capgo/capacitor-bluetooth-low-energy > Full-featured Bluetooth Low Energy plugin for scanning, connecting, and communicating with BLE peripherals on iOS, Android, and Web. Full BLE Support Scan, connect, read, write, and receive notifications from BLE devices Peripheral Mode Act as a BLE server and advertise services (Android/iOS) Cross-platform Works on iOS, Android, and Web (Chrome Web Bluetooth API) Background Support Foreground service for Android, background modes for iOS Service Discovery Automatically discover services, characteristics, and descriptors Comprehensive Documentation Check the [Documentation](/docs/plugins/bluetooth-low-energy/getting-started/) to master the plugin in just a few minutes. # Getting Started > Learn how to install and use the Bluetooth Low Energy plugin to communicate with BLE devices in your Capacitor app. 1. **Install the package** * npm ```sh npm i @capgo/capacitor-bluetooth-low-energy ``` * pnpm ```sh pnpm add @capgo/capacitor-bluetooth-low-energy ``` * yarn ```sh yarn add @capgo/capacitor-bluetooth-low-energy ``` * bun ```sh bun add @capgo/capacitor-bluetooth-low-energy ``` 2. **Sync with native projects** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## iOS Setup [Section titled “iOS Setup”](#ios-setup) Add the following to your `Info.plist`: ```xml NSBluetoothAlwaysUsageDescription This app uses Bluetooth to communicate with BLE devices. NSBluetoothPeripheralUsageDescription This app uses Bluetooth to communicate with BLE devices. ``` For background BLE support, add: ```xml UIBackgroundModes bluetooth-central bluetooth-peripheral ``` ## Android Setup [Section titled “Android Setup”](#android-setup) Works out of the box. The plugin automatically adds required permissions. For Android 12+, request runtime permissions: ```typescript await BluetoothLowEnergy.requestPermissions(); ``` ## Web Setup [Section titled “Web Setup”](#web-setup) Works in Chrome and Chromium-based browsers using the Web Bluetooth API. Requires HTTPS and user interaction to scan for devices. ## Basic Usage [Section titled “Basic Usage”](#basic-usage) ```typescript import { BluetoothLowEnergy } from '@capgo/capacitor-bluetooth-low-energy'; // Initialize the plugin await BluetoothLowEnergy.initialize(); // Request permissions (Android 12+) await BluetoothLowEnergy.requestPermissions(); // Check if Bluetooth is available and enabled const { available } = await BluetoothLowEnergy.isAvailable(); const { enabled } = await BluetoothLowEnergy.isEnabled(); if (!available || !enabled) { console.log('Bluetooth is not available or not enabled'); return; } // Start scanning for devices BluetoothLowEnergy.addListener('deviceScanned', (event) => { console.log('Found device:', event.device.name, event.device.deviceId); }); await BluetoothLowEnergy.startScan({ services: ['180d'], // Filter by Heart Rate service UUID timeout: 10000, // 10 seconds }); ``` ## Connecting to a Device [Section titled “Connecting to a Device”](#connecting-to-a-device) ```typescript // Connect to a device await BluetoothLowEnergy.connect({ deviceId: 'AA:BB:CC:DD:EE:FF', }); // Listen for connection events BluetoothLowEnergy.addListener('deviceConnected', (event) => { console.log('Connected to:', event.deviceId); }); BluetoothLowEnergy.addListener('deviceDisconnected', (event) => { console.log('Disconnected from:', event.deviceId); }); // Discover services await BluetoothLowEnergy.discoverServices({ deviceId: 'AA:BB:CC:DD:EE:FF', }); // Get discovered services const { services } = await BluetoothLowEnergy.getServices({ deviceId: 'AA:BB:CC:DD:EE:FF', }); console.log('Services:', services); ``` ## Reading and Writing Characteristics [Section titled “Reading and Writing Characteristics”](#reading-and-writing-characteristics) ```typescript // Read a characteristic const { value } = await BluetoothLowEnergy.readCharacteristic({ deviceId: 'AA:BB:CC:DD:EE:FF', service: '180d', characteristic: '2a37', }); console.log('Value:', value); // Array of bytes // Write to a characteristic await BluetoothLowEnergy.writeCharacteristic({ deviceId: 'AA:BB:CC:DD:EE:FF', service: '180d', characteristic: '2a39', value: [0x01], // Array of bytes type: 'withResponse', }); ``` ## Subscribing to Notifications [Section titled “Subscribing to Notifications”](#subscribing-to-notifications) ```typescript // Listen for characteristic changes BluetoothLowEnergy.addListener('characteristicChanged', (event) => { console.log('Characteristic changed:', event.characteristic); console.log('New value:', event.value); }); // Start notifications await BluetoothLowEnergy.startCharacteristicNotifications({ deviceId: 'AA:BB:CC:DD:EE:FF', service: '180d', characteristic: '2a37', }); // Stop notifications when done await BluetoothLowEnergy.stopCharacteristicNotifications({ deviceId: 'AA:BB:CC:DD:EE:FF', service: '180d', characteristic: '2a37', }); ``` ## Complete Example - Heart Rate Monitor [Section titled “Complete Example - Heart Rate Monitor”](#complete-example---heart-rate-monitor) ```typescript import { BluetoothLowEnergy } from '@capgo/capacitor-bluetooth-low-energy'; const HEART_RATE_SERVICE = '180d'; const HEART_RATE_MEASUREMENT = '2a37'; export class HeartRateMonitor { private deviceId: string | null = null; async init() { await BluetoothLowEnergy.initialize(); const { available } = await BluetoothLowEnergy.isAvailable(); if (!available) { throw new Error('Bluetooth not available'); } await BluetoothLowEnergy.requestPermissions(); } async scan(): Promise { const devices: string[] = []; BluetoothLowEnergy.addListener('deviceScanned', (event) => { if (event.device.name && !devices.includes(event.device.deviceId)) { devices.push(event.device.deviceId); console.log('Found:', event.device.name); } }); await BluetoothLowEnergy.startScan({ services: [HEART_RATE_SERVICE], timeout: 5000, }); return devices; } async connect(deviceId: string) { this.deviceId = deviceId; await BluetoothLowEnergy.connect({ deviceId }); await BluetoothLowEnergy.discoverServices({ deviceId }); } async startMonitoring(callback: (heartRate: number) => void) { if (!this.deviceId) throw new Error('Not connected'); BluetoothLowEnergy.addListener('characteristicChanged', (event) => { if (event.characteristic === HEART_RATE_MEASUREMENT) { // Parse heart rate from BLE Heart Rate Measurement format const flags = event.value[0]; const is16Bit = (flags & 0x01) !== 0; const heartRate = is16Bit ? (event.value[2] << 8) | event.value[1] : event.value[1]; callback(heartRate); } }); await BluetoothLowEnergy.startCharacteristicNotifications({ deviceId: this.deviceId, service: HEART_RATE_SERVICE, characteristic: HEART_RATE_MEASUREMENT, }); } async disconnect() { if (this.deviceId) { await BluetoothLowEnergy.disconnect({ deviceId: this.deviceId }); this.deviceId = null; } } } ``` ## API Reference [Section titled “API Reference”](#api-reference) ### Core Methods [Section titled “Core Methods”](#core-methods) | Method | Description | | ---------------------- | ------------------------------------------------------- | | `initialize()` | Initialize the BLE plugin (required before other calls) | | `isAvailable()` | Check if Bluetooth is available on the device | | `isEnabled()` | Check if Bluetooth is enabled | | `checkPermissions()` | Check current permission status | | `requestPermissions()` | Request Bluetooth permissions | ### Scanning [Section titled “Scanning”](#scanning) | Method | Description | | --------------------- | ------------------------------ | | `startScan(options?)` | Start scanning for BLE devices | | `stopScan()` | Stop scanning | ### Connection [Section titled “Connection”](#connection) | Method | Description | | ----------------------- | ---------------------------------------- | | `connect(options)` | Connect to a BLE device | | `disconnect(options)` | Disconnect from a device | | `getConnectedDevices()` | Get list of connected devices | | `createBond(options)` | Create a bond (Android only) | | `isBonded(options)` | Check if device is bonded (Android only) | ### Service Discovery [Section titled “Service Discovery”](#service-discovery) | Method | Description | | --------------------------- | ------------------------------------- | | `discoverServices(options)` | Discover services on connected device | | `getServices(options)` | Get discovered services | ### Characteristics [Section titled “Characteristics”](#characteristics) | Method | Description | | ------------------------------------------- | ------------------------------ | | `readCharacteristic(options)` | Read a characteristic value | | `writeCharacteristic(options)` | Write to a characteristic | | `startCharacteristicNotifications(options)` | Subscribe to notifications | | `stopCharacteristicNotifications(options)` | Unsubscribe from notifications | ### Descriptors [Section titled “Descriptors”](#descriptors) | Method | Description | | -------------------------- | ----------------------- | | `readDescriptor(options)` | Read a descriptor value | | `writeDescriptor(options)` | Write to a descriptor | ### Advanced [Section titled “Advanced”](#advanced) | Method | Description | | ------------------------------------ | ---------------------------------------- | | `readRssi(options)` | Read signal strength of connected device | | `requestMtu(options)` | Request MTU size change (Android) | | `requestConnectionPriority(options)` | Request connection priority (Android) | | `startAdvertising(options)` | Start advertising as peripheral | | `stopAdvertising()` | Stop advertising | | `startForegroundService(options)` | Start foreground service (Android) | | `stopForegroundService()` | Stop foreground service (Android) | ### Events [Section titled “Events”](#events) | Event | Description | | ----------------------- | ---------------------------------------------- | | `deviceScanned` | Emitted when a device is found during scanning | | `deviceConnected` | Emitted when connected to a device | | `deviceDisconnected` | Emitted when disconnected from a device | | `characteristicChanged` | Emitted when a characteristic value changes | ## Best Practices [Section titled “Best Practices”](#best-practices) 1. **Always initialize first** ```typescript await BluetoothLowEnergy.initialize(); ``` 2. **Check permissions before scanning** ```typescript const { bluetooth } = await BluetoothLowEnergy.checkPermissions(); if (bluetooth !== 'granted') { await BluetoothLowEnergy.requestPermissions(); } ``` 3. **Handle disconnections gracefully** ```typescript BluetoothLowEnergy.addListener('deviceDisconnected', async (event) => { // Attempt reconnection or notify user }); ``` 4. **Clean up listeners when done** ```typescript await BluetoothLowEnergy.removeAllListeners(); ``` 5. **Use foreground service for long-running operations (Android)** ```typescript await BluetoothLowEnergy.startForegroundService({ title: 'BLE Active', body: 'Connected to device', smallIcon: 'ic_notification', }); ``` ## Platform Notes [Section titled “Platform Notes”](#platform-notes) ### iOS [Section titled “iOS”](#ios) * Requires iOS 10.0+ * Uses CoreBluetooth framework * Supports background modes with proper entitlements * Device IDs are UUIDs (not MAC addresses) ### Android [Section titled “Android”](#android) * Requires Android 5.0 (API 21)+ * Android 12+ requires BLUETOOTH\_SCAN and BLUETOOTH\_CONNECT permissions * Device IDs are MAC addresses * Location services may be required for scanning ### Web [Section titled “Web”](#web) * Requires Chrome/Chromium with Web Bluetooth API * Must be served over HTTPS * Requires user interaction to initiate scanning * Limited compared to native implementations # @capgo/capacitor-brightness > Adjust screen brightness levels programmatically with support for both app-specific and system-wide brightness control. Requires Capacitor 8+. Simple API Get and set brightness with intuitive method calls Cross-platform Works on iOS, Android, and Web platforms System-wide Control Android supports system-wide brightness settings Automatic Mode Toggle between automatic and manual brightness modes on Android Comprehensive Documentation Check the [Documentation](/docs/plugins/brightness/getting-started/) to master the plugin in just a few minutes. # Getting Started > Learn how to install and use the Brightness plugin to control screen brightness in your Capacitor app. 1. **Install the package** * npm ```sh npm i @capgo/capacitor-brightness ``` * pnpm ```sh pnpm add @capgo/capacitor-brightness ``` * yarn ```sh yarn add @capgo/capacitor-brightness ``` * bun ```sh bun add @capgo/capacitor-brightness ``` 2. **Sync with native projects** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Android Configuration [Section titled “Android Configuration”](#android-configuration) To modify system-wide brightness on Android, add the WRITE\_SETTINGS permission to your `AndroidManifest.xml`: ```xml ``` ## Usage [Section titled “Usage”](#usage) Import the plugin and use its methods to control brightness: ```typescript import { CapgoBrightness } from '@capgo/capacitor-brightness'; // Get current brightness const getBrightness = async () => { const { brightness } = await CapgoBrightness.getBrightness(); console.log('Current brightness:', brightness); }; // Set brightness (0 to 1) const setBrightness = async (level: number) => { await CapgoBrightness.setBrightness({ brightness: level }); }; // Check if API is available const checkAvailable = async () => { const { available } = await CapgoBrightness.isAvailable(); console.log('Brightness API available:', available); }; ``` ## API Reference [Section titled “API Reference”](#api-reference) ### getBrightness() [Section titled “getBrightness()”](#getbrightness) Get the current brightness level of the device’s main screen. ```typescript const { brightness } = await CapgoBrightness.getBrightness(); // Returns: { brightness: number } (0 to 1) ``` ### setBrightness(options) [Section titled “setBrightness(options)”](#setbrightnessoptions) Set the brightness level of the device’s main screen. ```typescript interface SetBrightnessOptions { brightness: number; // 0.0 to 1.0 } await CapgoBrightness.setBrightness({ brightness: 0.75 }); ``` ### getSystemBrightness() (Android only) [Section titled “getSystemBrightness() (Android only)”](#getsystembrightness-android-only) Get the system-wide screen brightness. ```typescript const { brightness } = await CapgoBrightness.getSystemBrightness(); ``` ### setSystemBrightness(options) (Android only) [Section titled “setSystemBrightness(options) (Android only)”](#setsystembrightnessoptions-android-only) Set the system-wide screen brightness. Requires WRITE\_SETTINGS permission. ```typescript await CapgoBrightness.setSystemBrightness({ brightness: 0.5 }); ``` ### getSystemBrightnessMode() (Android only) [Section titled “getSystemBrightnessMode() (Android only)”](#getsystembrightnessmode-android-only) Get the current system brightness mode (automatic or manual). ```typescript import { BrightnessMode } from '@capgo/capacitor-brightness'; const { mode } = await CapgoBrightness.getSystemBrightnessMode(); if (mode === BrightnessMode.AUTOMATIC) { console.log('Auto-brightness is enabled'); } ``` ### setSystemBrightnessMode(options) (Android only) [Section titled “setSystemBrightnessMode(options) (Android only)”](#setsystembrightnessmodeoptions-android-only) Set the system brightness mode. ```typescript import { BrightnessMode } from '@capgo/capacitor-brightness'; // Enable automatic brightness await CapgoBrightness.setSystemBrightnessMode({ mode: BrightnessMode.AUTOMATIC }); // Enable manual brightness await CapgoBrightness.setSystemBrightnessMode({ mode: BrightnessMode.MANUAL }); ``` ### isUsingSystemBrightness() (Android only) [Section titled “isUsingSystemBrightness() (Android only)”](#isusingsystembrightness-android-only) Check if the current activity is using system-wide brightness. ```typescript const { isUsing } = await CapgoBrightness.isUsingSystemBrightness(); ``` ### restoreSystemBrightness() (Android only) [Section titled “restoreSystemBrightness() (Android only)”](#restoresystembrightness-android-only) Reset brightness to use system-wide value. ```typescript await CapgoBrightness.restoreSystemBrightness(); ``` ### isAvailable() [Section titled “isAvailable()”](#isavailable) Check if the Brightness API is available. ```typescript const { available } = await CapgoBrightness.isAvailable(); ``` ### checkPermissions() [Section titled “checkPermissions()”](#checkpermissions) Check permissions for system brightness access. ```typescript const status = await CapgoBrightness.checkPermissions(); // Returns: { brightness: 'granted' | 'denied' | 'prompt' | 'prompt-with-rationale' } ``` ### requestPermissions() [Section titled “requestPermissions()”](#requestpermissions) Request permissions for system brightness. Opens system settings on Android. ```typescript const status = await CapgoBrightness.requestPermissions(); ``` ## Complete Example [Section titled “Complete Example”](#complete-example) ```typescript import { CapgoBrightness, BrightnessMode } from '@capgo/capacitor-brightness'; export class BrightnessService { private originalBrightness: number | null = null; async init() { const { available } = await CapgoBrightness.isAvailable(); if (!available) { throw new Error('Brightness API not available'); } // Store original brightness const { brightness } = await CapgoBrightness.getBrightness(); this.originalBrightness = brightness; } async setMaxBrightness() { await CapgoBrightness.setBrightness({ brightness: 1.0 }); } async setMinBrightness() { await CapgoBrightness.setBrightness({ brightness: 0.0 }); } async restore() { if (this.originalBrightness !== null) { await CapgoBrightness.setBrightness({ brightness: this.originalBrightness }); } } // Android-specific: Enable auto-brightness async enableAutoBrightness() { const status = await CapgoBrightness.checkPermissions(); if (status.brightness !== 'granted') { await CapgoBrightness.requestPermissions(); } await CapgoBrightness.setSystemBrightnessMode({ mode: BrightnessMode.AUTOMATIC }); } } ``` ## Best Practices [Section titled “Best Practices”](#best-practices) 1. **Check availability first** ```typescript const { available } = await CapgoBrightness.isAvailable(); if (!available) { // Show fallback UI or disable brightness features } ``` 2. **Restore brightness on exit** Always restore the original brightness when your app goes to background or when the feature is no longer needed. 3. **Request permissions on Android** For system-wide brightness control, request WRITE\_SETTINGS permission before calling system methods. 4. **Handle errors gracefully** ```typescript try { await CapgoBrightness.setBrightness({ brightness: 0.8 }); } catch (error) { console.error('Failed to set brightness:', error); } ``` ## Platform Notes [Section titled “Platform Notes”](#platform-notes) ### iOS [Section titled “iOS”](#ios) * Brightness changes persist until the device is locked * Only app-specific brightness is supported * Uses `UIScreen.main.brightness` API ### Android [Section titled “Android”](#android) * App-specific brightness only applies to the current activity * System-wide brightness requires WRITE\_SETTINGS permission * Supports automatic brightness mode toggle * Uses WindowManager for app brightness, Settings.System for system brightness ### Web [Section titled “Web”](#web) * Limited support depending on browser capabilities * May require user interaction for some browsers ## Requirements [Section titled “Requirements”](#requirements) * **Capacitor 8.0.0** or higher # @capgo/camera-preview > Display camera preview in your app with full control over camera settings, capture photos and videos directly from the preview. Real-time preview Display live camera feed directly in your app UI 🚀 Full control Control flash, zoom, focus, and switch between cameras 📸 Capture capabilities Take photos and record videos from the preview 🎥 Comprehensive Documentation Check the [Documentation](/docs/plugins/camera-preview/getting-started/) to master the plugin in just a few minutes. # Getting Started > Learn how to install and use the Camera Preview plugin to display real-time camera feed and capture photos/videos in your Capacitor app. 1. **Install the package** * npm ```sh npm i @capgo/camera-preview ``` * pnpm ```sh pnpm add @capgo/camera-preview ``` * yarn ```sh yarn add @capgo/camera-preview ``` * bun ```sh bun add @capgo/camera-preview ``` 2. **Sync with native projects** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` 3. **Configure permissions** ### iOS [Section titled “iOS”](#ios) Add camera usage description to your `Info.plist`: ```xml NSCameraUsageDescription To take photos and videos NSMicrophoneUsageDescription To record audio with videos NSPhotoLibraryUsageDescription To save photos and videos ``` ### Android [Section titled “Android”](#android) Add camera permissions to your `AndroidManifest.xml`: ```xml ``` ## Usage [Section titled “Usage”](#usage) Import the plugin and use its methods to control the camera: ```typescript import { CameraPreview } from '@capgo/camera-preview'; import { CameraPreviewOptions, CameraPreviewPictureOptions } from '@capgo/camera-preview'; // Start camera preview const startCamera = async () => { const cameraPreviewOptions: CameraPreviewOptions = { position: 'rear', height: 1920, width: 1080, quality: 50, toBack: false, paddingBottom: 0, rotateWhenOrientationChanged: true, x: 0, y: 0 }; await CameraPreview.start(cameraPreviewOptions); }; // Stop camera preview const stopCamera = async () => { await CameraPreview.stop(); }; // Take a picture const takePicture = async () => { const pictureOptions: CameraPreviewPictureOptions = { quality: 90, width: 1920, height: 1080 }; const result = await CameraPreview.capture(pictureOptions); const base64PictureData = result.value; // Use the base64 image data console.log(base64PictureData); }; // Switch camera const flipCamera = async () => { await CameraPreview.flip(); }; // Control flash const setFlashMode = async (mode: 'on' | 'off' | 'auto' | 'torch') => { await CameraPreview.setFlashMode({ flashMode: mode }); }; // Start recording video const startRecording = async () => { await CameraPreview.startRecordVideo({ storeToFile: true, fileName: 'myvideo' }); }; // Stop recording const stopRecording = async () => { const result = await CameraPreview.stopRecordVideo(); console.log('Video path:', result.videoFilePath); }; ``` ## API Reference [Section titled “API Reference”](#api-reference) ### start(options) [Section titled “start(options)”](#startoptions) Starts the camera preview. ```typescript interface CameraPreviewOptions { position?: 'rear' | 'front'; height?: number; width?: number; x?: number; y?: number; toBack?: boolean; paddingBottom?: number; rotateWhenOrientationChanged?: boolean; storeToFile?: boolean; disableExifHeaderStripping?: boolean; enableHighResolution?: boolean; disableAudio?: boolean; lockAndroidOrientation?: boolean; enableOpacity?: boolean; enableZoom?: boolean; } ``` ### stop() [Section titled “stop()”](#stop) Stops the camera preview. ### capture(options) [Section titled “capture(options)”](#captureoptions) Takes a picture and returns it as base64 encoded string. ```typescript interface CameraPreviewPictureOptions { height?: number; width?: number; quality?: number; } ``` ### flip() [Section titled “flip()”](#flip) Switches between front and rear camera. ### setFlashMode(options) [Section titled “setFlashMode(options)”](#setflashmodeoptions) Sets the flash mode. ```typescript interface CameraPreviewFlashMode { flashMode: 'off' | 'on' | 'auto' | 'torch'; } ``` ### startRecordVideo(options) [Section titled “startRecordVideo(options)”](#startrecordvideooptions) Starts video recording. ```typescript interface CameraPreviewVideoOptions { storeToFile?: boolean; fileName?: string; width?: number; height?: number; quality?: number; withFlash?: boolean; } ``` ### stopRecordVideo() [Section titled “stopRecordVideo()”](#stoprecordvideo) Stops video recording and returns the video file path. ## Advanced Features [Section titled “Advanced Features”](#advanced-features) ### Focus Control [Section titled “Focus Control”](#focus-control) ```typescript // Set focus await CameraPreview.setFocusCoordinate({ x: 100, y: 100 }); // Get supported focus modes const focusModes = await CameraPreview.getFocusModes(); // Set focus mode await CameraPreview.setFocusMode({ focusMode: 'continuous-picture' }); ``` ### Zoom Control [Section titled “Zoom Control”](#zoom-control) ```typescript // Get max zoom const maxZoom = await CameraPreview.getMaxZoom(); // Set zoom await CameraPreview.setZoom({ zoom: 2.0 }); // Get current zoom const currentZoom = await CameraPreview.getZoom(); ``` ### Exposure Control [Section titled “Exposure Control”](#exposure-control) ```typescript // Get exposure compensation range const range = await CameraPreview.getExposureCompensationRange(); // Set exposure compensation await CameraPreview.setExposureCompensation({ exposureCompensation: 0.5 }); ``` ## Best Practices [Section titled “Best Practices”](#best-practices) 1. **Request permissions first** ```typescript import { Camera } from '@capacitor/camera'; const requestPermissions = async () => { const permissions = await Camera.requestPermissions(); if (permissions.camera === 'granted') { // Start camera preview } }; ``` 2. **Handle orientation changes** Set `rotateWhenOrientationChanged: true` to automatically adjust the preview. 3. **Optimize performance** * Use appropriate resolution settings * Stop the preview when not in use * Disable features you don’t need 4. **Error handling** ```typescript try { await CameraPreview.start(options); } catch (error) { console.error('Failed to start camera:', error); } ``` ## Platform Notes [Section titled “Platform Notes”](#platform-notes) ### iOS [Section titled “iOS”](#ios-1) * Requires iOS 10.0+ * Uses AVFoundation framework * Hardware acceleration supported ### Android [Section titled “Android”](#android-1) * Requires Android 5.0 (API 21)+ * Uses Camera2 API * Supports various camera features based on device capabilities # Capacitor+ > Capacitor+ is an automated fork of Capacitor that merges community PRs faster, with security-reviewed releases. Tip **Why Capacitor+?** Great PRs sit unmerged in the official Capacitor repository for months. Capacitor+ actively merges these community contributions so you don’t have to wait. Community PRs Merged Faster Bug fixes and features stuck in upstream? We merge them so you can benefit immediately. Auto-Synced Daily Every change from official Capacitor is automatically pulled, tested, and verified. Security Reviewed Every release is analyzed by AI for security vulnerabilities, breaking changes, and stability risks. Drop-in Replacement Same API as official Capacitor - just change the package scope and you’re done. ## Philosophy [Section titled “Philosophy”](#philosophy) The Ionic team maintains Capacitor with their own priorities and release schedule. Community contributions can wait months or even years to be merged. Capacitor+ takes a different approach: 1. **Merge PRs from Forks** - Valuable PRs stuck in the upstream queue are actively merged 2. **Continuous Sync** - Every upstream change is automatically pulled and tested 3. **Rapid Releases** - Changes are published to npm as soon as they pass CI 4. **Community-First** - Your contributions matter and get prioritized 5. **Transparency** - All automation is open source and visible ## Packages [Section titled “Packages”](#packages) | Package | npm | | ------------------------- | --------------------------------------------------------------------------------------------------------------------- | | `@capacitor-plus/core` | [![npm](https://img.shields.io/npm/v/@capacitor-plus/core)](https://www.npmjs.com/package/@capacitor-plus/core) | | `@capacitor-plus/cli` | [![npm](https://img.shields.io/npm/v/@capacitor-plus/cli)](https://www.npmjs.com/package/@capacitor-plus/cli) | | `@capacitor-plus/android` | [![npm](https://img.shields.io/npm/v/@capacitor-plus/android)](https://www.npmjs.com/package/@capacitor-plus/android) | | `@capacitor-plus/ios` | [![npm](https://img.shields.io/npm/v/@capacitor-plus/ios)](https://www.npmjs.com/package/@capacitor-plus/ios) | # Getting Started > Learn how to install Capacitor+ as a drop-in replacement for official Capacitor packages. ## New Project Installation [Section titled “New Project Installation”](#new-project-installation) 1. **Install core packages** ```bash npm install @capacitor-plus/core @capacitor-plus/cli ``` 2. **Add platform packages** ```bash npm install @capacitor-plus/android # for Android npm install @capacitor-plus/ios # for iOS ``` 3. **Initialize Capacitor** * npm ```sh npx cap init ``` * pnpm ```sh pnpm cap init ``` * yarn ```sh yarn cap init ``` * bun ```sh bunx cap init ``` 4. **Add platforms** * npm ```sh npx cap add android ``` * pnpm ```sh pnpm cap add android ``` * yarn ```sh yarn cap add android ``` * bun ```sh bunx cap add android ``` - npm ```sh npx cap add ios ``` - pnpm ```sh pnpm cap add ios ``` - yarn ```sh yarn cap add ios ``` - bun ```sh bunx cap add ios ``` ## Migrating from Official Capacitor [Section titled “Migrating from Official Capacitor”](#migrating-from-official-capacitor) If you have an existing Capacitor project, migrating to Capacitor+ is simple: 1. **Remove official packages** ```bash npm uninstall @capacitor/core @capacitor/cli @capacitor/android @capacitor/ios ``` 2. **Install Capacitor+ packages** ```bash npm install @capacitor-plus/core @capacitor-plus/cli npm install @capacitor-plus/android # if using Android npm install @capacitor-plus/ios # if using iOS ``` 3. **Sync your project** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` Note No code changes required! Capacitor+ has the same API as official Capacitor. Your imports and code remain exactly the same. ## Usage [Section titled “Usage”](#usage) Since Capacitor+ is API-compatible, your existing code works without changes: ```typescript import { Capacitor } from '@capacitor/core'; import { registerPlugin } from '@capacitor/core'; // Check platform const platform = Capacitor.getPlatform(); console.log('Running on:', platform); // Check if native if (Capacitor.isNativePlatform()) { console.log('Running on native platform'); } // Register a custom plugin const MyPlugin = registerPlugin('MyPlugin'); ``` ### With Official Capacitor Plugins [Section titled “With Official Capacitor Plugins”](#with-official-capacitor-plugins) All official Capacitor plugins work seamlessly: ```typescript import { Camera, CameraResultType } from '@capacitor/camera'; import { Geolocation } from '@capacitor/geolocation'; import { Storage } from '@capacitor/preferences'; // Camera const photo = await Camera.getPhoto({ quality: 90, resultType: CameraResultType.Uri }); // Geolocation const position = await Geolocation.getCurrentPosition(); // Storage await Storage.set({ key: 'name', value: 'John' }); ``` ### With Capgo Plugins [Section titled “With Capgo Plugins”](#with-capgo-plugins) Capgo plugins work perfectly with Capacitor+: ```typescript import { CapacitorUpdater } from '@capgo/capacitor-updater'; import { ScreenOrientation } from '@capgo/capacitor-screen-orientation'; import { CapacitorFlash } from '@capgo/capacitor-flash'; // Live updates await CapacitorUpdater.notifyAppReady(); // Screen orientation await ScreenOrientation.lock({ orientation: 'portrait' }); // Flashlight await CapacitorFlash.toggle(); ``` ## How the Sync Works [Section titled “How the Sync Works”](#how-the-sync-works) ```plaintext ┌─────────────────────┐ ┌──────────────────┐ ┌──────────────────┐ ┌─────────────────┐ │ ionic-team/ │ │ CI/CD │ │ Claude Code │ │ npm publish │ │ capacitor │────▶│ Pipeline │────▶│ Security Review │────▶│ @capacitor-plus│ │ (upstream) │ │ (daily sync) │ │ (AI analysis) │ │ packages │ └─────────────────────┘ └──────────────────┘ └──────────────────┘ └─────────────────┘ ``` 1. **Daily Sync**: GitHub Actions fetch latest changes from `ionic-team/capacitor` 2. **PR Creation**: Changes are proposed as pull requests to the `plus` branch 3. **CI Validation**: Full test suite runs (lint, unit tests, iOS build, Android build) 4. **Security Review**: AI-powered analysis checks for vulnerabilities and breaking changes 5. **Auto-Merge**: Only if CI passes AND security review approves 6. **Auto-Publish**: New version published to npm under `@capacitor-plus/*` ## Security Review Details [Section titled “Security Review Details”](#security-review-details) Every upstream sync is analyzed for: | Check | What It Catches | | -------------------- | ---------------------------------------------------------------------- | | **Security** | Command injection, XSS, path traversal, hardcoded secrets | | **Breaking Changes** | Removed/renamed APIs, changed signatures, config changes | | **Stability** | Null dereferences, unhandled exceptions, race conditions, memory leaks | | **Data Safety** | Data loss scenarios, privacy violations, insecure storage | | **Code Integrity** | Obfuscated code, suspicious network calls, backdoors | Caution If any issues are detected, the PR is flagged for manual review and will NOT be auto-merged. This ensures you always get stable, secure releases. ## Submitting Your PR [Section titled “Submitting Your PR”](#submitting-your-pr) Have a PR stuck in the official Capacitor repo? Get it merged in Capacitor+: 1. **Open an issue** in the [Capacitor+ repo](https://github.com/Cap-go/capacitor-plus/issues) linking to your upstream PR 2. **Or submit directly** as a PR to the `plus` branch 3. The team will review, run CI, and merge if it passes This way you and others can benefit from your work immediately without waiting for the upstream release cycle. ## FAQ [Section titled “FAQ”](#faq) ### Is this production-ready? [Section titled “Is this production-ready?”](#is-this-production-ready) Yes. Capacitor+ is used in production apps. Every release passes the same test suite as official Capacitor, plus additional security analysis. ### Will my official plugins still work? [Section titled “Will my official plugins still work?”](#will-my-official-plugins-still-work) Yes. All `@capacitor/*` plugins work with Capacitor+ out of the box. ### What if upstream releases a breaking change? [Section titled “What if upstream releases a breaking change?”](#what-if-upstream-releases-a-breaking-change) The AI security review flags breaking changes for manual review. You’ll see the changes documented before they’re merged. ### How do I report issues? [Section titled “How do I report issues?”](#how-do-i-report-issues) File issues on the [Capacitor+ GitHub repo](https://github.com/Cap-go/capacitor-plus/issues). For issues that also affect official Capacitor, we’ll help coordinate upstream. ### Can I contribute? [Section titled “Can I contribute?”](#can-i-contribute) Absolutely! PRs are welcome. You can submit fixes directly or request that specific upstream PRs be merged. # @capgo/capacitor-compass > Access device compass sensor for heading data with real-time updates on iOS and Android. Real-time Heading Get continuous compass heading updates in degrees (0-360) Cross-platform Works on both iOS and Android devices with native sensor access Permission Handling Built-in permission management for location services (iOS) Event-driven Subscribe to heading changes with efficient event listeners # Getting Started > Learn how to install and use the Compass plugin to read device compass heading in your Capacitor app. 1. **Install the package** * npm ```sh npm i @capgo/capacitor-compass ``` * pnpm ```sh pnpm add @capgo/capacitor-compass ``` * yarn ```sh yarn add @capgo/capacitor-compass ``` * bun ```sh bun add @capgo/capacitor-compass ``` 2. **Sync with native projects** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## iOS Setup [Section titled “iOS Setup”](#ios-setup) On iOS, compass access requires location permission. Add the following to your `Info.plist`: ```xml NSLocationWhenInUseUsageDescription We need location permission to access the compass ``` ## Android Setup [Section titled “Android Setup”](#android-setup) No additional setup required. The plugin uses the device’s magnetometer and accelerometer sensors. ## Usage [Section titled “Usage”](#usage) Import the plugin and use its methods to read compass heading: ```typescript import { CapgoCompass } from '@capgo/capacitor-compass'; // Get current heading once const getCurrentHeading = async () => { const { value } = await CapgoCompass.getCurrentHeading(); console.log('Current heading:', value, 'degrees'); }; // Start continuous heading updates const startCompass = async () => { // Start listening for updates await CapgoCompass.startListening(); // Add listener for heading changes const handle = await CapgoCompass.addListener('headingChange', (event) => { console.log('Heading:', event.value, 'degrees'); }); // Later, to stop listening: // await CapgoCompass.stopListening(); // await handle.remove(); }; // Check permissions const checkPermission = async () => { const status = await CapgoCompass.checkPermissions(); console.log('Permission status:', status.compass); }; // Request permissions const requestPermission = async () => { const status = await CapgoCompass.requestPermissions(); if (status.compass === 'granted') { console.log('Compass access granted'); } }; ``` ## API Reference [Section titled “API Reference”](#api-reference) ### getCurrentHeading() [Section titled “getCurrentHeading()”](#getcurrentheading) Get the current compass heading in degrees. ```typescript const result = await CapgoCompass.getCurrentHeading(); // Returns: { value: number } - heading in degrees (0-360) ``` ### startListening() [Section titled “startListening()”](#startlistening) Start listening for compass heading changes. Must be called before heading events are emitted. ```typescript await CapgoCompass.startListening(); ``` ### stopListening() [Section titled “stopListening()”](#stoplistening) Stop listening for compass heading changes. ```typescript await CapgoCompass.stopListening(); ``` ### addListener(‘headingChange’, callback) [Section titled “addListener(‘headingChange’, callback)”](#addlistenerheadingchange-callback) Add a listener for heading change events. ```typescript const handle = await CapgoCompass.addListener('headingChange', (event) => { console.log('Heading:', event.value); }); // Remove listener when done await handle.remove(); ``` ### removeAllListeners() [Section titled “removeAllListeners()”](#removealllisteners) Remove all registered listeners. ```typescript await CapgoCompass.removeAllListeners(); ``` ### checkPermissions() [Section titled “checkPermissions()”](#checkpermissions) Check current permission status. ```typescript const status = await CapgoCompass.checkPermissions(); // Returns: { compass: 'prompt' | 'granted' | 'denied' } ``` ### requestPermissions() [Section titled “requestPermissions()”](#requestpermissions) Request permission to access compass data. ```typescript const status = await CapgoCompass.requestPermissions(); ``` ### getPluginVersion() [Section titled “getPluginVersion()”](#getpluginversion) Get the native plugin version. ```typescript const { version } = await CapgoCompass.getPluginVersion(); ``` ## Complete Example [Section titled “Complete Example”](#complete-example) ```typescript import { CapgoCompass } from '@capgo/capacitor-compass'; export class CompassService { private listenerHandle: any = null; async init() { // Check and request permissions const status = await CapgoCompass.checkPermissions(); if (status.compass !== 'granted') { const result = await CapgoCompass.requestPermissions(); if (result.compass !== 'granted') { throw new Error('Compass permission denied'); } } } async startTracking(onHeadingChange: (heading: number) => void) { // Start listening for updates await CapgoCompass.startListening(); // Add event listener this.listenerHandle = await CapgoCompass.addListener( 'headingChange', (event) => { onHeadingChange(event.value); } ); } async stopTracking() { if (this.listenerHandle) { await this.listenerHandle.remove(); this.listenerHandle = null; } await CapgoCompass.stopListening(); } async getHeading(): Promise { const { value } = await CapgoCompass.getCurrentHeading(); return value; } getCardinalDirection(heading: number): string { const directions = ['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW']; const index = Math.round(heading / 45) % 8; return directions[index]; } } ``` ## Platform Notes [Section titled “Platform Notes”](#platform-notes) ### iOS [Section titled “iOS”](#ios) * Requires iOS 10.0+ * Uses Core Location for heading data * Requires location permission (NSLocationWhenInUseUsageDescription) * Heading updates are continuous when listening is active ### Android [Section titled “Android”](#android) * Requires Android 6.0 (API 23)+ * Uses accelerometer and magnetometer sensors * No special permissions required for compass sensors * Heading is calculated from sensor fusion ### Web [Section titled “Web”](#web) * Not supported on web platform * Methods will throw errors when called # @capgo/capacitor-contacts > Access, search, and manage device contacts in your Capacitor app with full CRUD operations and permission handling. Read Contacts Access device contact list with full details 📇 Search Functionality Find contacts by name, email, or phone 🔍 Create & Update Add new contacts or modify existing ones ✏️ Delete Contacts Remove contacts from device safely 🗑️ Permission Handling Handle contact permissions properly with Privacy manifest compliance 🔒 Getting Started Check the [Getting Started Guide](/docs/plugins/contacts/getting-started/) to install and configure the plugin. # Getting Started with Contacts > Learn how to integrate contact management into your Capacitor app This guide will walk you through integrating the Capacitor Contacts plugin into your application. ## Installation [Section titled “Installation”](#installation) Install the plugin using npm: ```bash npm install @capgo/capacitor-contacts npx cap sync ``` ## iOS Configuration [Section titled “iOS Configuration”](#ios-configuration) Add the following to your `Info.plist`: ```xml NSContactsUsageDescription This app needs access to contacts to let you select recipients ``` ## Android Configuration [Section titled “Android Configuration”](#android-configuration) Add the following permissions to your `AndroidManifest.xml`: ```xml ``` ## Basic Usage [Section titled “Basic Usage”](#basic-usage) ### Import the Plugin [Section titled “Import the Plugin”](#import-the-plugin) ```typescript import { Contacts } from '@capgo/capacitor-contacts'; ``` ### Request Permissions [Section titled “Request Permissions”](#request-permissions) ```typescript const requestPermissions = async () => { const permission = await Contacts.requestPermissions(); console.log('Permission status:', permission.contacts); }; ``` ### Get All Contacts [Section titled “Get All Contacts”](#get-all-contacts) ```typescript const getAllContacts = async () => { const result = await Contacts.getContacts({ projection: { name: true, phones: true, emails: true, image: true } }); console.log('Contacts:', result.contacts); }; ``` ### Search Contacts [Section titled “Search Contacts”](#search-contacts) ```typescript const searchContacts = async (query: string) => { const result = await Contacts.getContacts({ projection: { name: true, phones: true, emails: true }, query: query }); console.log('Search results:', result.contacts); }; ``` ### Create Contact [Section titled “Create Contact”](#create-contact) ```typescript const createContact = async () => { const newContact = { name: { given: 'John', family: 'Doe' }, phones: [{ type: 'mobile', number: '+1234567890' }], emails: [{ type: 'work', address: 'john.doe@example.com' }] }; const result = await Contacts.createContact(newContact); console.log('Contact created:', result); }; ``` ### Update Contact [Section titled “Update Contact”](#update-contact) ```typescript const updateContact = async (contactId: string) => { const updates = { contactId: contactId, name: { given: 'Jane', family: 'Doe' } }; await Contacts.updateContact(updates); console.log('Contact updated'); }; ``` ### Delete Contact [Section titled “Delete Contact”](#delete-contact) ```typescript const deleteContact = async (contactId: string) => { await Contacts.deleteContact({ contactId }); console.log('Contact deleted'); }; ``` ## Complete Example [Section titled “Complete Example”](#complete-example) Here’s a complete example with a contact service: ```typescript import { Contacts } from '@capgo/capacitor-contacts'; interface Contact { contactId: string; name: { display?: string; given?: string; family?: string; }; phones?: Array<{ type: string; number: string }>; emails?: Array<{ type: string; address: string }>; image?: { base64String: string }; } class ContactsService { async checkPermissions(): Promise { const permission = await Contacts.checkPermissions(); if (permission.contacts === 'granted') { return true; } const requested = await Contacts.requestPermissions(); return requested.contacts === 'granted'; } async getAllContacts(): Promise { const hasPermission = await this.checkPermissions(); if (!hasPermission) { throw new Error('Contacts permission denied'); } const result = await Contacts.getContacts({ projection: { name: true, phones: true, emails: true, image: true } }); return result.contacts; } async searchContacts(query: string): Promise { if (!query || query.length < 2) { return []; } const result = await Contacts.getContacts({ projection: { name: true, phones: true, emails: true }, query: query }); return result.contacts; } async getContactById(contactId: string): Promise { const result = await Contacts.getContacts({ projection: { name: true, phones: true, emails: true, image: true, organization: true, birthday: true, note: true, urls: true, postalAddresses: true }, contactId: contactId }); return result.contacts.length > 0 ? result.contacts[0] : null; } async createContact(contact: Partial): Promise { const hasPermission = await this.checkPermissions(); if (!hasPermission) { throw new Error('Contacts permission denied'); } const result = await Contacts.createContact(contact); return result.contactId; } async updateContact(contactId: string, updates: Partial): Promise { await Contacts.updateContact({ contactId, ...updates }); } async deleteContact(contactId: string): Promise { await Contacts.deleteContact({ contactId }); } formatPhoneNumber(phone: string): string { // Remove non-numeric characters const cleaned = phone.replace(/\D/g, ''); // Format as (XXX) XXX-XXXX if (cleaned.length === 10) { return `(${cleaned.slice(0, 3)}) ${cleaned.slice(3, 6)}-${cleaned.slice(6)}`; } return phone; } getContactInitials(contact: Contact): string { const given = contact.name?.given || ''; const family = contact.name?.family || ''; if (given && family) { return `${given[0]}${family[0]}`.toUpperCase(); } const display = contact.name?.display || ''; const parts = display.split(' '); if (parts.length >= 2) { return `${parts[0][0]}${parts[1][0]}`.toUpperCase(); } return display.slice(0, 2).toUpperCase(); } } // Usage const contactsService = new ContactsService(); // Get all contacts const contacts = await contactsService.getAllContacts(); console.log('Contacts:', contacts); // Search contacts const results = await contactsService.searchContacts('john'); console.log('Search results:', results); // Create contact const newContactId = await contactsService.createContact({ name: { given: 'Jane', family: 'Smith' }, phones: [{ type: 'mobile', number: '+1234567890' }] }); // Update contact await contactsService.updateContact(newContactId, { emails: [{ type: 'work', address: 'jane@example.com' }] }); ``` ## Understanding Projection [Section titled “Understanding Projection”](#understanding-projection) The `projection` parameter controls which fields to fetch: ```typescript const result = await Contacts.getContacts({ projection: { name: true, // Contact name phones: true, // Phone numbers emails: true, // Email addresses image: true, // Contact photo organization: true, // Company/organization birthday: true, // Birth date note: true, // Notes urls: true, // Websites postalAddresses: true // Physical addresses } }); ``` **Tip**: Only request fields you need to improve performance. ## Contact Picker UI [Section titled “Contact Picker UI”](#contact-picker-ui) Many apps prefer using the native contact picker: ```typescript const pickContact = async () => { try { const result = await Contacts.pickContact({ projection: { name: true, phones: true, emails: true } }); if (result.contacts.length > 0) { const contact = result.contacts[0]; console.log('Selected contact:', contact); } } catch (error) { console.error('Contact picker cancelled or failed:', error); } }; ``` ## Best Practices [Section titled “Best Practices”](#best-practices) 1. **Request Minimal Permissions**: Only request contacts permission when needed 2. **Use Projection**: Only fetch fields you actually use 3. **Handle Denials**: Provide fallback when permissions denied 4. **Cache Wisely**: Contacts can change, don’t cache too long 5. **Respect Privacy**: Be transparent about contact usage 6. **Async Operations**: All contact operations are async ## Common Issues [Section titled “Common Issues”](#common-issues) ### Permission Denied [Section titled “Permission Denied”](#permission-denied) ```typescript const handlePermissionDenied = async () => { const permission = await Contacts.checkPermissions(); if (permission.contacts === 'denied') { // Show dialog explaining why permission is needed showPermissionDialog(); // Direct user to settings // On iOS: Settings > [App] > Contacts // On Android: Settings > Apps > [App] > Permissions } }; ``` ### Large Contact Lists [Section titled “Large Contact Lists”](#large-contact-lists) ```typescript const loadContactsInBatches = async () => { // Get count first (lightweight) const projection = { name: true }; // Minimal projection // Implement pagination if needed const allContacts = await Contacts.getContacts({ projection }); // Process in chunks const chunkSize = 100; for (let i = 0; i < allContacts.contacts.length; i += chunkSize) { const chunk = allContacts.contacts.slice(i, i + chunkSize); await processContactChunk(chunk); } }; ``` ## Next Steps [Section titled “Next Steps”](#next-steps) * Explore the [API Reference](https://github.com/Cap-go/capacitor-contacts#api) for complete method documentation * Check out the [example app](https://github.com/Cap-go/capacitor-contacts/tree/main/example) for advanced usage * See the [tutorial](/plugins/capacitor-contacts) for complete implementation examples # @capgo/capacitor-crisp > Integrate Crisp native chat SDK into your Capacitor app for seamless in-app customer support and real-time messaging. Native performance Native SDK integration for smooth chat experience 🚀 Real-time messaging Live chat with customers directly in your app 💬 Rich features Support for user data, events, and custom styling ✨ Comprehensive Documentation Check the [Documentation](/docs/plugins/crisp/getting-started/) to master the plugin in just a few minutes. # Getting Started > Learn how to install and use the Crisp chat SDK plugin for in-app customer support in your Capacitor app. 1. **Install the package** * npm ```sh npm i @capgo/capacitor-crisp ``` * pnpm ```sh pnpm add @capgo/capacitor-crisp ``` * yarn ```sh yarn add @capgo/capacitor-crisp ``` * bun ```sh bun add @capgo/capacitor-crisp ``` 2. **Sync with native projects** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` 3. **Get your Crisp Website ID** * Sign up at [crisp.chat](https://crisp.chat) * Create a website/project * Find your Website ID in Settings > Setup Instructions ## Basic Usage [Section titled “Basic Usage”](#basic-usage) Import the plugin and configure it with your Website ID: ```typescript import { CapacitorCrisp } from '@capgo/capacitor-crisp'; // Configure Crisp with your website ID const configureCrisp = async () => { await CapacitorCrisp.configure({ websiteID: 'YOUR_WEBSITE_ID' }); }; // Open the chat const openChat = async () => { await CapacitorCrisp.openMessenger(); }; // Set user information const setUserInfo = async () => { await CapacitorCrisp.setUser({ email: 'user@example.com', nickname: 'John Doe', phone: '+1234567890', avatar: 'https://example.com/avatar.jpg' }); }; // Reset user session (logout) const logout = async () => { await CapacitorCrisp.resetChatSession(); }; ``` ## API Reference [Section titled “API Reference”](#api-reference) ### configure(options) [Section titled “configure(options)”](#configureoptions) Configure Crisp with your website ID and optional settings. ```typescript interface ConfigureOptions { websiteID: string; locale?: string; // e.g., 'en', 'fr', 'es' tokenID?: string; // For authenticated sessions } await CapacitorCrisp.configure({ websiteID: 'YOUR_WEBSITE_ID', locale: 'en' }); ``` ### openMessenger() [Section titled “openMessenger()”](#openmessenger) Opens the Crisp chat interface. ```typescript await CapacitorCrisp.openMessenger(); ``` ### setUser(options) [Section titled “setUser(options)”](#setuseroptions) Sets user information for the chat session. ```typescript interface UserOptions { email?: string; nickname?: string; phone?: string; avatar?: string; } await CapacitorCrisp.setUser({ email: 'user@example.com', nickname: 'John Doe' }); ``` ### setUserCompany(options) [Section titled “setUserCompany(options)”](#setusercompanyoptions) Sets company information for business users. ```typescript interface CompanyOptions { name: string; url?: string; description?: string; employment?: { title?: string; role?: string; }; geolocation?: { city?: string; country?: string; }; } await CapacitorCrisp.setUserCompany({ name: 'Acme Corp', url: 'https://acme.com', employment: { title: 'CEO', role: 'Leadership' } }); ``` ### pushEvent(options) [Section titled “pushEvent(options)”](#pusheventoptions) Send custom events to track user actions. ```typescript interface EventOptions { name: string; color?: 'red' | 'orange' | 'yellow' | 'green' | 'blue' | 'purple' | 'pink' | 'brown' | 'grey' | 'black'; data?: { [key: string]: any }; } await CapacitorCrisp.pushEvent({ name: 'checkout_completed', color: 'green', data: { price: 99.99, currency: 'USD' } }); ``` ### setSessionSegment(segment) [Section titled “setSessionSegment(segment)”](#setsessionsegmentsegment) Set a segment to categorize the chat session. ```typescript await CapacitorCrisp.setSessionSegment('premium_customer'); ``` ### resetChatSession() [Section titled “resetChatSession()”](#resetchatsession) Reset the current chat session (useful for logout). ```typescript await CapacitorCrisp.resetChatSession(); ``` ## Advanced Features [Section titled “Advanced Features”](#advanced-features) ### Custom User Data [Section titled “Custom User Data”](#custom-user-data) ```typescript // Set multiple custom data fields await CapacitorCrisp.setSessionData({ key: 'plan', value: 'premium' }); await CapacitorCrisp.setSessionData({ key: 'signup_date', value: '2024-01-15' }); ``` ### Message Preset [Section titled “Message Preset”](#message-preset) Set a pre-filled message in the chat input: ```typescript await CapacitorCrisp.setMessageText( "Hello, I need help with my order #12345" ); ``` ### Chat Availability [Section titled “Chat Availability”](#chat-availability) Control when the chat widget is available: ```typescript // Hide chat temporarily await CapacitorCrisp.setTokenID('user_token_12345'); ``` ## Complete Example [Section titled “Complete Example”](#complete-example) ```typescript import { CapacitorCrisp } from '@capgo/capacitor-crisp'; export class ChatService { async initialize() { // Configure Crisp await CapacitorCrisp.configure({ websiteID: 'YOUR_WEBSITE_ID', locale: 'en' }); } async loginUser(user: any) { // Set user information await CapacitorCrisp.setUser({ email: user.email, nickname: user.name, phone: user.phone, avatar: user.avatarUrl }); // Set custom data await CapacitorCrisp.setSessionData({ key: 'user_id', value: user.id }); await CapacitorCrisp.setSessionData({ key: 'account_type', value: user.accountType }); // Set segment if (user.isPremium) { await CapacitorCrisp.setSessionSegment('premium'); } // Track login event await CapacitorCrisp.pushEvent({ name: 'user_login', color: 'blue', data: { method: 'email' } }); } async openSupport(context?: string) { if (context) { await CapacitorCrisp.setMessageText( `I need help with: ${context}` ); } await CapacitorCrisp.openMessenger(); } async logout() { await CapacitorCrisp.resetChatSession(); } } ``` ## Styling and Customization [Section titled “Styling and Customization”](#styling-and-customization) Crisp automatically adapts to your app’s theme, but you can customize it further through the Crisp dashboard: 1. Go to your Crisp dashboard 2. Navigate to Settings > Website Settings > Chatbox & Email Settings 3. Customize colors, position, and behavior ## Best Practices [Section titled “Best Practices”](#best-practices) 1. **Initialize early** Configure Crisp during app initialization for immediate availability: ```typescript import { App } from '@capacitor/app'; App.addListener('appStateChange', async ({ isActive }) => { if (isActive) { await CapacitorCrisp.configure({ websiteID: 'YOUR_WEBSITE_ID' }); } }); ``` 2. **Set user context** Always provide user information when available for better support: ```typescript if (user.isAuthenticated) { await CapacitorCrisp.setUser({ email: user.email, nickname: user.name }); } ``` 3. **Track important events** Use events to provide context to support agents: ```typescript await CapacitorCrisp.pushEvent({ name: 'error_occurred', color: 'red', data: { error: error.message, screen: 'checkout' } }); ``` 4. **Handle logout properly** Always reset the session when users log out: ```typescript async logout() { await CapacitorCrisp.resetChatSession(); // Your other logout logic } ``` ## Platform Notes [Section titled “Platform Notes”](#platform-notes) ### iOS [Section titled “iOS”](#ios) * Requires iOS 10.0+ * Uses native Crisp iOS SDK * Supports push notifications (configure in Crisp dashboard) ### Android [Section titled “Android”](#android) * Requires Android 5.0 (API 21)+ * Uses native Crisp Android SDK * Material Design compliant ### Web [Section titled “Web”](#web) * Falls back to Crisp JavaScript SDK * Full feature parity with native platforms # @capgo/capacitor-data-storage-sqlite > Fast and reliable SQLite-based key-value storage for your Capacitor apps with encryption support. SQLite powered Fast and reliable data storage using SQLite 💾 Encryption support Optional encryption for sensitive data 🔐 Key-value simplicity Simple key-value API with powerful features 🗂️ Comprehensive Documentation Check the [Documentation](/docs/plugins/data-storage-sqlite/getting-started/) to master the plugin in just a few minutes. # Getting Started > Learn how to install and configure the Capacitor Data Storage SQLite plugin for fast key-value storage with optional encryption. 1. **Install the package** * npm ```sh npm i @capgo/capacitor-data-storage-sqlite ``` * pnpm ```sh pnpm add @capgo/capacitor-data-storage-sqlite ``` * yarn ```sh yarn add @capgo/capacitor-data-storage-sqlite ``` * bun ```sh bun add @capgo/capacitor-data-storage-sqlite ``` 2. **Sync with native projects** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` 3. **Configure the plugin** **Basic Storage Example:** ```typescript import { CapacitorDataStorageSqlite } from '@capgo/capacitor-data-storage-sqlite'; // Open a storage database await CapacitorDataStorageSqlite.openStore({ database: 'myapp_storage' }); // Store data await CapacitorDataStorageSqlite.set({ key: 'user_preferences', value: JSON.stringify({ theme: 'dark' }) }); ``` **Encrypted Storage Example:** ```typescript // Open encrypted storage await CapacitorDataStorageSqlite.openStore({ database: 'secure_storage', encrypted: true, mode: 'encryption' }); // Store sensitive data await CapacitorDataStorageSqlite.set({ key: 'api_token', value: 'secret_token_value' }); ``` * iOS No additional setup required for iOS. * Android No additional setup required for Android. 4. **Basic operations** ```typescript import { CapacitorDataStorageSqlite } from '@capgo/capacitor-data-storage-sqlite'; // Set a value await CapacitorDataStorageSqlite.set({ key: 'username', value: 'john_doe' }); // Get a value const { value } = await CapacitorDataStorageSqlite.get({ key: 'username' }); console.log('Username:', value); // "john_doe" // Remove a value await CapacitorDataStorageSqlite.remove({ key: 'username' }); // Clear all data await CapacitorDataStorageSqlite.clear(); // Check if key exists const { result } = await CapacitorDataStorageSqlite.iskey({ key: 'username' }); console.log('Key exists:', result); // true or false // Get all keys const { keys } = await CapacitorDataStorageSqlite.keys(); console.log('All keys:', keys); // Get all values const { values } = await CapacitorDataStorageSqlite.values(); console.log('All values:', values); ``` 5. **Advanced usage** ```typescript import { CapacitorDataStorageSqlite } from '@capgo/capacitor-data-storage-sqlite'; export class StorageService { private dbName = 'app_storage'; private isEncrypted = false; async initialize(encrypted = false) { this.isEncrypted = encrypted; // Open storage with options await CapacitorDataStorageSqlite.openStore({ database: this.dbName, encrypted: encrypted, mode: encrypted ? 'encryption' : 'no-encryption', version: 1 }); } // Generic storage methods async setObject(key: string, data: T): Promise { const value = JSON.stringify(data); await CapacitorDataStorageSqlite.set({ key, value }); } async getObject(key: string): Promise { try { const { value } = await CapacitorDataStorageSqlite.get({ key }); return value ? JSON.parse(value) : null; } catch (error) { console.error('Error getting object:', error); return null; } } // Batch operations async setMultiple(items: Record): Promise { for (const [key, value] of Object.entries(items)) { await CapacitorDataStorageSqlite.set({ key, value: typeof value === 'string' ? value : JSON.stringify(value) }); } } async getMultiple(keys: string[]): Promise> { const results: Record = {}; for (const key of keys) { try { const { value } = await CapacitorDataStorageSqlite.get({ key }); results[key] = value; } catch (error) { results[key] = null; } } return results; } // Table management async getTables(): Promise { const { tables } = await CapacitorDataStorageSqlite.tables(); return tables; } async deleteTable(table: string): Promise { await CapacitorDataStorageSqlite.deleteTable({ table }); } // Import/Export functionality async exportToJson(): Promise { const { keys } = await CapacitorDataStorageSqlite.keys(); const { values } = await CapacitorDataStorageSqlite.values(); return keys.map((key, index) => ({ key, value: values[index] })); } async importFromJson(data: Array<{ key: string; value: string }>): Promise { // Clear existing data await CapacitorDataStorageSqlite.clear(); // Import new data for (const item of data) { await CapacitorDataStorageSqlite.set({ key: item.key, value: item.value }); } } // Filtering and searching async keysStartingWith(prefix: string): Promise { const { keys } = await CapacitorDataStorageSqlite.keys(); return keys.filter(key => key.startsWith(prefix)); } async filterByPrefix(prefix: string): Promise> { const { keys } = await CapacitorDataStorageSqlite.keys(); const { values } = await CapacitorDataStorageSqlite.values(); const filtered: Array<{ key: string; value: string }> = []; keys.forEach((key, index) => { if (key.startsWith(prefix)) { filtered.push({ key, value: values[index] }); } }); return filtered; } // Close database when done async close(): Promise { await CapacitorDataStorageSqlite.closeStore({ database: this.dbName }); } } // Usage example const storage = new StorageService(); await storage.initialize(true); // Use encryption // Store user data await storage.setObject('user_profile', { id: 123, name: 'John Doe', email: 'john@example.com' }); // Retrieve user data const profile = await storage.getObject('user_profile'); console.log('User profile:', profile); ``` ## API Reference [Section titled “API Reference”](#api-reference) ### Methods [Section titled “Methods”](#methods) #### `openStore(options: OpenStoreOptions)` [Section titled “openStore(options: OpenStoreOptions)”](#openstoreoptions-openstoreoptions) Open a storage database. **Parameters:** * `options.database`: string - Database name * `options.encrypted`: boolean - Enable encryption * `options.mode`: string - ‘encryption’ or ‘no-encryption’ * `options.version`: number - Database version #### `closeStore(options: CloseStoreOptions)` [Section titled “closeStore(options: CloseStoreOptions)”](#closestoreoptions-closestoreoptions) Close the storage database. #### `set(options: SetOptions)` [Section titled “set(options: SetOptions)”](#setoptions-setoptions) Store a key-value pair. **Parameters:** * `options.key`: string - Storage key * `options.value`: string - Value to store #### `get(options: GetOptions)` [Section titled “get(options: GetOptions)”](#getoptions-getoptions) Retrieve a value by key. **Returns:** `Promise<{ value: string }>` #### `remove(options: RemoveOptions)` [Section titled “remove(options: RemoveOptions)”](#removeoptions-removeoptions) Remove a key-value pair. #### `clear()` [Section titled “clear()”](#clear) Clear all data from storage. #### `iskey(options: IskeyOptions)` [Section titled “iskey(options: IskeyOptions)”](#iskeyoptions-iskeyoptions) Check if a key exists. **Returns:** `Promise<{ result: boolean }>` #### `keys()` [Section titled “keys()”](#keys) Get all storage keys. **Returns:** `Promise<{ keys: string[] }>` #### `values()` [Section titled “values()”](#values) Get all storage values. **Returns:** `Promise<{ values: string[] }>` #### `tables()` [Section titled “tables()”](#tables) Get all table names. **Returns:** `Promise<{ tables: string[] }>` #### `deleteTable(options: DeleteTableOptions)` [Section titled “deleteTable(options: DeleteTableOptions)”](#deletetableoptions-deletetableoptions) Delete a specific table. ### Interfaces [Section titled “Interfaces”](#interfaces) ```typescript interface OpenStoreOptions { database: string; encrypted?: boolean; mode?: string; version?: number; } interface SetOptions { key: string; value: string; } interface GetOptions { key: string; } interface RemoveOptions { key: string; } ``` ## Platform Notes [Section titled “Platform Notes”](#platform-notes) ### iOS [Section titled “iOS”](#ios) * Uses SQLite3 with optional SQLCipher for encryption * Data persists across app updates * Supports iOS 11.0+ ### Android [Section titled “Android”](#android) * Uses SQLite with optional SQLCipher * Data persists across app updates * Supports Android 5.0 (API 21)+ ## Common Use Cases [Section titled “Common Use Cases”](#common-use-cases) 1. **User Preferences**: Store app settings and preferences 2. **Cache Management**: Cache API responses and data 3. **Offline Storage**: Store data for offline access 4. **Session Management**: Manage user sessions securely 5. **Token Storage**: Securely store authentication tokens ## Best Practices [Section titled “Best Practices”](#best-practices) 1. **Use Encryption for Sensitive Data** ```typescript // For sensitive data like tokens await openStore({ database: 'secure_db', encrypted: true, mode: 'encryption' }); ``` 2. **Organize Keys with Prefixes** ```typescript // Use prefixes for organization await set({ key: 'user:123:profile', value: userData }); await set({ key: 'cache:api:users', value: apiData }); ``` 3. **Handle Large Data Carefully** ```typescript // For large objects, consider compression const compressed = compress(largeData); await set({ key: 'large_data', value: compressed }); ``` 4. **Regular Cleanup** ```typescript // Remove expired cache entries const keys = await keys(); for (const key of keys.keys) { if (key.startsWith('cache:') && isExpired(key)) { await remove({ key }); } } ``` ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) **Database not opening:** * Check database name is valid (alphanumeric, underscores) * Ensure no special characters in database name * Verify encryption mode matches existing database **Data not persisting:** * Ensure `openStore` is called before operations * Check for any errors in console * Verify key names are strings **Performance issues:** * Avoid storing very large values * Use batch operations when possible * Consider using multiple databases for different data types ## Related Documentation [Section titled “Related Documentation”](#related-documentation) * [Migrate from Ionic Secure Storage](/docs/upgrade/from-ionic-secure-storage/) * [Ionic enterprise plugins migration solution](/solutions/ionic-enterprise-plugins/) # @capgo/capacitor-document-scanner > Launch a polished document scanning experience with automatic cropping, multi-page capture, and configurable output formats. The Document Scanner plugin gives your Capacitor app a native-quality scanning flow that works on both iOS and Android devices. Automatic detection Detect page edges, crop automatically, and let users adjust before saving. Multi-page capture Capture one or many pages in a single scan session with adjustable limits. Custom output Return either file paths or base64 strings at the image quality you choose. Unified API One TypeScript interface spans both iOS and Android implementations. Use the getting started guide to enable camera permissions, configure quality settings, and export scanned files to your storage workflow. # Getting Started > Add native-quality document scanning with edge detection and adjustable output formats. 1. **Install the plugin** * npm ```sh npm i @capgo/capacitor-document-scanner ``` * pnpm ```sh pnpm add @capgo/capacitor-document-scanner ``` * yarn ```sh yarn add @capgo/capacitor-document-scanner ``` * bun ```sh bun add @capgo/capacitor-document-scanner ``` 2. **Sync native platforms** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Trigger a scan [Section titled “Trigger a scan”](#trigger-a-scan) ```typescript import { DocumentScanner, ResponseType, ScanDocumentResponseStatus, } from '@capgo/capacitor-document-scanner'; const result = await DocumentScanner.scanDocument({ croppedImageQuality: 90, letUserAdjustCrop: true, maxNumDocuments: 10, responseType: ResponseType.ImageFilePath, }); if (result.status === ScanDocumentResponseStatus.Success) { console.log('Scanned files:', result.scannedImages); } else { console.log('Scan cancelled by user'); } ``` ## Return base64 data [Section titled “Return base64 data”](#return-base64-data) ```typescript const result = await DocumentScanner.scanDocument({ responseType: ResponseType.Base64, }); const [firstPage] = result.scannedImages ?? []; if (firstPage) { const dataUrl = `data:image/jpeg;base64,${firstPage}`; // Display preview or upload to server } ``` ## Platform requirements [Section titled “Platform requirements”](#platform-requirements) * **iOS**: Add `NSCameraUsageDescription` to `ios/App/App/Info.plist` explaining how you use the camera. * **Android**: Confirm that `CAMERA` permission is declared (Capacitor adds it automatically) and adjust `croppedImageQuality`, `letUserAdjustCrop`, and `maxNumDocuments` to match your UI. * **Storage**: When using `ResponseType.ImageFilePath`, move or copy the scanned files to your desired storage location before the app session ends. # @capgo/capacitor-downloader > Download files with background support, progress tracking, pause/resume capabilities, and efficient download management. ## Overview [Section titled “Overview”](#overview) The Capacitor Downloader plugin provides powerful file download capabilities with support for background downloads, progress tracking, and comprehensive download management. This plugin enables robust file downloading with pause/resume functionality and detailed progress monitoring. > ⚠️ **Work in Progress**: This plugin is currently under development and not yet ready for production use. Background downloads Continue downloads when app is backgrounded 📥 Progress tracking Real-time download progress and status monitoring 📊 Download control Pause, resume, and stop downloads dynamically ⏯️ Network options Configure network preferences and priorities 📶 ## Development Status [Section titled “Development Status”](#development-status) This plugin is inspired by react-native-background-downloader and is currently being developed. Features may change as development progresses. # Getting Started > Installation and usage guide for @capgo/capacitor-downloader ## Installation [Section titled “Installation”](#installation) * npm ```bash npm install @capgo/capacitor-downloader npx cap sync ``` * yarn ```bash yarn add @capgo/capacitor-downloader npx cap sync ``` * pnpm ```bash pnpm add @capgo/capacitor-downloader npx cap sync ``` * bun ```bash bun add @capgo/capacitor-downloader npx cap sync ``` ## Usage Example [Section titled “Usage Example”](#usage-example) ```typescript import { CapacitorDownloader } from '@capgo/capacitor-downloader'; // Start a download const downloadId = 'my-download-001'; await CapacitorDownloader.download({ id: downloadId, url: 'https://example.com/large-file.zip', destination: '/downloads/large-file.zip', headers: { 'Authorization': 'Bearer token123' }, network: 'wifi-only', priority: 'high' }); // Listen for progress updates CapacitorDownloader.addListener('downloadProgress', (data) => { console.log(`Download ${data.id}: ${data.progress}% complete`); console.log(`Downloaded: ${data.bytesDownloaded}/${data.totalBytes} bytes`); }); // Handle completion CapacitorDownloader.addListener('downloadCompleted', (data) => { console.log(`Download completed: ${data.id}`); console.log(`File saved to: ${data.path}`); }); // Handle errors CapacitorDownloader.addListener('downloadFailed', (error) => { console.error(`Download failed: ${error.id}`, error.message); }); // Pause a download await CapacitorDownloader.pause(downloadId); // Resume the download await CapacitorDownloader.resume(downloadId); // Check download status const status = await CapacitorDownloader.checkStatus(downloadId); console.log('Download status:', status); // Stop the download await CapacitorDownloader.stop(downloadId); ``` ## Core API Methods [Section titled “Core API Methods”](#core-api-methods) ### Download Management [Section titled “Download Management”](#download-management) * `download(options)` - Start a new file download * `pause(id)` - Pause an ongoing download * `resume(id)` - Resume a paused download * `stop(id)` - Stop and cancel a download * `checkStatus(id)` - Get current download status ### File Operations [Section titled “File Operations”](#file-operations) * `getFileInfo(path)` - Retrieve file information and metadata ## Download Configuration [Section titled “Download Configuration”](#download-configuration) ```typescript interface DownloadOptions { id: string; // Unique download identifier url: string; // Download source URL destination: string; // Local save path headers?: Record; // Custom HTTP headers network?: 'cellular' | 'wifi-only'; // Network restrictions priority?: 'high' | 'normal' | 'low'; // Download priority } ``` ## Event Listeners [Section titled “Event Listeners”](#event-listeners) The plugin provides comprehensive event handling: * `downloadProgress` - Track real-time download progress * `downloadCompleted` - Handle successful download completion * `downloadFailed` - Handle download errors and failures ## Network Configuration [Section titled “Network Configuration”](#network-configuration) ### WiFi-Only Downloads [Section titled “WiFi-Only Downloads”](#wifi-only-downloads) ```typescript await CapacitorDownloader.download({ id: 'large-file', url: 'https://example.com/video.mp4', destination: '/downloads/video.mp4', network: 'wifi-only' // Restricts to WiFi networks only }); ``` ### Priority Management [Section titled “Priority Management”](#priority-management) ```typescript // High priority download await CapacitorDownloader.download({ id: 'urgent-update', url: 'https://example.com/update.zip', destination: '/downloads/update.zip', priority: 'high' }); ``` ## Download States [Section titled “Download States”](#download-states) Downloads can be in various states: * **Pending**: Queued for download * **Running**: Currently downloading * **Paused**: Temporarily stopped * **Completed**: Successfully finished * **Failed**: Encountered an error * **Stopped**: Manually cancelled ## File Information [Section titled “File Information”](#file-information) ```typescript // Get file details const fileInfo = await CapacitorDownloader.getFileInfo('/downloads/my-file.pdf'); console.log('File size:', fileInfo.size); console.log('Last modified:', fileInfo.lastModified); console.log('MIME type:', fileInfo.mimeType); ``` ## Best Practices [Section titled “Best Practices”](#best-practices) * Use unique download IDs to avoid conflicts * Implement proper error handling for network failures * Consider storage space before starting large downloads * Use WiFi-only mode for large files to preserve mobile data * Clean up completed downloads to manage storage # @capgo/electron-updater > Push instant updates to your Electron desktop apps without rebuilding binaries. Same reliable live update system now for desktop. Tip New to Capgo? The Electron Updater brings the same powerful live update capabilities from mobile to desktop. If you’re already using `@capgo/capacitor-updater`, you’ll feel right at home. ## Why Electron Updater? [Section titled “Why Electron Updater?”](#why-electron-updater) Electron’s built-in auto-updater requires shipping a full new binary for every update. This plugin solves that by enabling JavaScript/HTML/CSS updates without rebuilding. Instant Updates Push JavaScript, HTML, and CSS updates directly to users without rebuilding the entire app binary. Delta Updates Only download changed files, making updates ultra-fast and bandwidth-efficient. Rollback Protection Automatic rollback if an update fails, keeping your app stable for users. End-to-End Encryption Secure update delivery with RSA session keys and AES bundle encryption. ## Key Features [Section titled “Key Features”](#key-features) * **Live Updates** - Push updates instantly without app store delays * **Auto-Update** - Automatic update checking and installation * **Rollback Protection** - Automatic rollback if `notifyAppReady()` isn’t called * **Bundle Management** - Full control over downloaded bundles * **Channel System** - Deploy to different user groups (production, beta, staging) * **Delay Conditions** - Control when updates are applied (background, kill, date, native version) * **Debug Menu** - Built-in debug tools accessible via `Ctrl+Shift+D` / `Cmd+Shift+D` * **Statistics Reporting** - Track update success rates and user versions ## Feature Parity with Capacitor Updater [Section titled “Feature Parity with Capacitor Updater”](#feature-parity-with-capacitor-updater) The Electron Updater maintains 100% API compatibility with `@capgo/capacitor-updater`. The same methods, events, and configuration options work across both platforms: | Feature | Capacitor | Electron | | --------------------- | ------------ | ----------------- | | Live Updates | Yes | Yes | | Channels | Yes | Yes | | Rollback Protection | Yes | Yes | | End-to-End Encryption | Yes | Yes | | Statistics | Yes | Yes | | Delay Conditions | Yes | Yes | | Debug Menu | Shake device | Keyboard shortcut | ## Installation [Section titled “Installation”](#installation) ```bash npm install @capgo/electron-updater ``` ## Quick Links [Section titled “Quick Links”](#quick-links) [Getting Started ](/docs/plugins/electron-updater/getting-started/)Complete setup guide for adding live updates to your Electron app. [API Reference ](/docs/plugins/electron-updater/api/)All available methods, events, and configuration options. [Capacitor Updater Docs ](/docs/plugins/updater/)The mobile counterpart - same API, same concepts, for Capacitor apps. ## Documentation [Section titled “Documentation”](#documentation) * **[Getting Started](/docs/plugins/electron-updater/getting-started/)** - Setup and configuration guide * **[API Reference](/docs/plugins/electron-updater/api/)** - All methods, events, and options * **[Migration from Capacitor](/docs/plugins/electron-updater/migration/)** - Moving from Capacitor to Electron ## Community [Section titled “Community”](#community) Join the [Discord](https://discord.gg/VnYRvBfgA6) to get help and connect with other developers. # Electron Updater API Reference > Complete API reference for @capgo/electron-updater - all methods, events, and configuration options. This page documents all available methods, events, and configuration options for the Electron Updater. ## Core Methods [Section titled “Core Methods”](#core-methods) ### notifyAppReady() [Section titled “notifyAppReady()”](#notifyappready) **Must be called on every app launch.** Confirms the bundle loaded successfully and prevents automatic rollback. ```typescript await updater.notifyAppReady(); ``` Caution If not called within `appReadyTimeout` (default 10 seconds), the update is considered failed and the app rolls back to the previous version. ### download(options) [Section titled “download(options)”](#downloadoptions) Download a bundle from a URL. ```typescript const bundle = await updater.download({ url: 'https://example.com/bundle.zip', version: '1.0.1', checksum: 'sha256-hash', // Optional but recommended sessionKey: '...', // For encrypted bundles }); ``` **Parameters:** | Option | Type | Required | Description | | ------------ | ------ | -------- | --------------------------------- | | `url` | string | Yes | URL to download the bundle from | | `version` | string | Yes | Version identifier for the bundle | | `checksum` | string | No | SHA256 checksum for verification | | `sessionKey` | string | No | Session key for encrypted bundles | **Returns:** `BundleInfo` object with `id`, `version`, `status` ### next(options) [Section titled “next(options)”](#nextoptions) Queue a bundle to be loaded on next app restart. ```typescript await updater.next({ id: 'bundle-id' }); ``` **Parameters:** | Option | Type | Required | Description | | ------ | ------ | -------- | ------------------ | | `id` | string | Yes | Bundle ID to queue | ### set(options) [Section titled “set(options)”](#setoptions) Immediately switch to a bundle and reload the app. ```typescript await updater.set({ id: 'bundle-id' }); ``` **Parameters:** | Option | Type | Required | Description | | ------ | ------ | -------- | --------------------- | | `id` | string | Yes | Bundle ID to activate | ### reload() [Section titled “reload()”](#reload) Manually reload the app with the current bundle. ```typescript await updater.reload(); ``` ### delete(options) [Section titled “delete(options)”](#deleteoptions) Delete a bundle from storage. ```typescript await updater.delete({ id: 'bundle-id' }); ``` **Parameters:** | Option | Type | Required | Description | | ------ | ------ | -------- | ------------------- | | `id` | string | Yes | Bundle ID to delete | ### reset(options) [Section titled “reset(options)”](#resetoptions) Reset to builtin version or last successful bundle. ```typescript // Reset to builtin await updater.reset({ toLastSuccessful: false }); // Reset to last successful bundle await updater.reset({ toLastSuccessful: true }); ``` **Parameters:** | Option | Type | Required | Description | | ------------------ | ------- | -------- | ----------------------------------------------------------- | | `toLastSuccessful` | boolean | No | If true, reset to last successful bundle instead of builtin | ## Bundle Information [Section titled “Bundle Information”](#bundle-information) ### current() [Section titled “current()”](#current) Get information about the current bundle and native version. ```typescript const info = await updater.current(); // { bundle: { id, version, status }, native: '1.0.0' } ``` ### list(options) [Section titled “list(options)”](#listoptions) List all downloaded bundles. ```typescript const bundles = await updater.list(); // [{ id, version, status, downloaded, checksum }, ...] ``` ### getNextBundle() [Section titled “getNextBundle()”](#getnextbundle) Get the bundle queued for next restart. ```typescript const next = await updater.getNextBundle(); // { id, version, status } or null ``` ### getFailedUpdate() [Section titled “getFailedUpdate()”](#getfailedupdate) Get information about the last failed update (useful for debugging rollbacks). ```typescript const failed = await updater.getFailedUpdate(); // { id, version, reason } or null ``` ### getBuiltinVersion() [Section titled “getBuiltinVersion()”](#getbuiltinversion) Get the version shipped with the app binary. ```typescript const version = await updater.getBuiltinVersion(); // '1.0.0' ``` ## Update Checking [Section titled “Update Checking”](#update-checking) ### getLatest(options) [Section titled “getLatest(options)”](#getlatestoptions) Check the server for the latest available version. ```typescript const latest = await updater.getLatest(); if (latest.url && !latest.error) { // Update available console.log('New version:', latest.version); console.log('Download URL:', latest.url); } else if (latest.error) { console.error('Error checking updates:', latest.error); } ``` **Returns:** | Property | Type | Description | | ------------ | ------ | --------------------------------- | | `url` | string | Download URL (empty if no update) | | `version` | string | Available version | | `checksum` | string | SHA256 checksum | | `sessionKey` | string | Encryption session key | | `error` | string | Error message if check failed | | `message` | string | Server message | ## Channel Management [Section titled “Channel Management”](#channel-management) ### setChannel(options) [Section titled “setChannel(options)”](#setchanneloptions) Assign the device to a specific channel. ```typescript await updater.setChannel({ channel: 'beta' }); ``` ### unsetChannel(options) [Section titled “unsetChannel(options)”](#unsetchanneloptions) Remove channel assignment and use default. ```typescript await updater.unsetChannel(); ``` ### getChannel() [Section titled “getChannel()”](#getchannel) Get the current channel assignment. ```typescript const channel = await updater.getChannel(); // { channel: 'production', status: 'set' } ``` ### listChannels() [Section titled “listChannels()”](#listchannels) List all available channels for this app. ```typescript const channels = await updater.listChannels(); // ['production', 'beta', 'staging'] ``` ## Delay Conditions [Section titled “Delay Conditions”](#delay-conditions) Control when downloaded updates are applied. ### setMultiDelay(options) [Section titled “setMultiDelay(options)”](#setmultidelayoptions) Set conditions that must be met before an update is applied. ```typescript // Wait for app to be backgrounded await updater.setMultiDelay({ delayConditions: [{ kind: 'background' }] }); // Wait until specific date await updater.setMultiDelay({ delayConditions: [{ kind: 'date', value: '2024-12-25T00:00:00Z' }] }); // Wait for app to be killed and restarted await updater.setMultiDelay({ delayConditions: [{ kind: 'kill' }] }); // Multiple conditions (all must be met) await updater.setMultiDelay({ delayConditions: [ { kind: 'background' }, { kind: 'date', value: '2024-12-25T00:00:00Z' } ] }); ``` **Delay Condition Types:** | Kind | Value | Description | | --------------- | ---------------------- | --------------------------------------- | | `background` | Optional duration (ms) | Wait for app to be backgrounded | | `kill` | - | Wait for app to be killed and restarted | | `date` | ISO date string | Wait until specific date/time | | `nativeVersion` | Version string | Wait for native app update | ### cancelDelay() [Section titled “cancelDelay()”](#canceldelay) Clear all delay conditions and apply update immediately on next check. ```typescript await updater.cancelDelay(); ``` ## Device Identification [Section titled “Device Identification”](#device-identification) ### getDeviceId() [Section titled “getDeviceId()”](#getdeviceid) Get the unique device identifier. ```typescript const deviceId = await updater.getDeviceId(); // 'uuid-xxxx-xxxx-xxxx' ``` ### setCustomId(options) [Section titled “setCustomId(options)”](#setcustomidoptions) Set a custom identifier for the device (useful for analytics). ```typescript await updater.setCustomId({ customId: 'user-123' }); ``` ## Configuration [Section titled “Configuration”](#configuration) ### setUpdateUrl(options) [Section titled “setUpdateUrl(options)”](#setupdateurloptions) Change the update server URL at runtime. ```typescript await updater.setUpdateUrl({ url: 'https://my-server.com/updates' }); ``` Note Requires `allowModifyUrl: true` in the constructor options. ### setStatsUrl(options) [Section titled “setStatsUrl(options)”](#setstatsurloptions) Change the statistics reporting URL. ```typescript await updater.setStatsUrl({ url: 'https://my-server.com/stats' }); ``` ### setChannelUrl(options) [Section titled “setChannelUrl(options)”](#setchannelurloptions) Change the channel management URL. ```typescript await updater.setChannelUrl({ url: 'https://my-server.com/channel' }); ``` ### setAppId(options) [Section titled “setAppId(options)”](#setappidoptions) Change the App ID at runtime. ```typescript await updater.setAppId({ appId: 'com.example.newapp' }); ``` Note Requires `allowModifyAppId: true` in the constructor options. ### getAppId() [Section titled “getAppId()”](#getappid) Get the current App ID. ```typescript const appId = await updater.getAppId(); ``` ## Debug [Section titled “Debug”](#debug) ### setDebugMenu(options) [Section titled “setDebugMenu(options)”](#setdebugmenuoptions) Enable or disable the debug menu. ```typescript await updater.setDebugMenu({ enabled: true }); ``` ### isDebugMenuEnabled() [Section titled “isDebugMenuEnabled()”](#isdebugmenuenabled) Check if the debug menu is enabled. ```typescript const enabled = await updater.isDebugMenuEnabled(); ``` ## Events [Section titled “Events”](#events) Listen to update events using `addListener`: ```typescript updater.addListener('eventName', (event) => { // Handle event }); ``` ### Available Events [Section titled “Available Events”](#available-events) | Event | Payload | Description | | ------------------- | --------------------- | ------------------------------------------------------ | | `download` | `{ percent, status }` | Download progress updates | | `updateAvailable` | `{ bundle }` | New update available | | `noNeedUpdate` | `{ message }` | Already up-to-date | | `downloadComplete` | `{ bundle }` | Download finished successfully | | `downloadFailed` | `{ bundle, error }` | Download failed | | `breakingAvailable` | `{ bundle }` | Incompatible update available (requires native update) | | `updateFailed` | `{ bundle, reason }` | Update installation failed | | `appReloaded` | `{}` | App was reloaded | | `appReady` | `{}` | `notifyAppReady()` was called | ### Example: Full Event Handling [Section titled “Example: Full Event Handling”](#example-full-event-handling) ```typescript // Progress tracking updater.addListener('download', (event) => { updateProgressBar(event.percent); }); // Update available notification updater.addListener('updateAvailable', (event) => { showNotification(`Update ${event.bundle.version} available!`); }); // Handle completion updater.addListener('downloadComplete', async (event) => { // Queue for next restart await updater.next({ id: event.bundle.id }); showNotification('Update will apply on next restart'); }); // Handle failures updater.addListener('updateFailed', (event) => { console.error('Update failed:', event.reason); reportError(event); }); ``` ## Constructor Options [Section titled “Constructor Options”](#constructor-options) Full configuration options for `ElectronUpdater`: ```typescript const updater = new ElectronUpdater({ // Required appId: 'com.example.app', // Version override version: '1.0.0', // Override builtin version detection // Server URLs updateUrl: 'https://plugin.capgo.app/updates', channelUrl: 'https://plugin.capgo.app/channel_self', statsUrl: 'https://plugin.capgo.app/stats', // Behavior autoUpdate: true, // Enable automatic update checks appReadyTimeout: 10000, // Milliseconds before rollback (default: 10000) autoDeleteFailed: true, // Auto-delete failed bundles autoDeletePrevious: true, // Auto-delete old bundles resetWhenUpdate: true, // Reset to builtin on native update // Channels defaultChannel: 'production', // Direct Update Mode directUpdate: false, // 'atInstall' | 'onLaunch' | 'always' | false // Security publicKey: '...', // RSA public key for E2E encryption // Dynamic Configuration allowModifyUrl: false, // Allow runtime URL changes allowModifyAppId: false, // Allow runtime App ID changes persistCustomId: false, // Persist custom ID across updates persistModifyUrl: false, // Persist URL changes // Debug debugMenu: false, // Enable debug menu (Ctrl+Shift+D) disableJSLogging: false, // Disable console logs // Periodic Updates periodCheckDelay: 0, // Seconds between auto-checks (0 = disabled, min 600) }); ``` # Getting Started with Electron Updater > Complete setup guide for adding Capgo live updates to your Electron application. This guide walks you through setting up `@capgo/electron-updater` in your Electron application to enable live JavaScript/HTML/CSS updates. Tip Already using Capgo with Capacitor? The Electron Updater uses the same Capgo Cloud backend, so you can manage both mobile and desktop apps from the same dashboard. ## Prerequisites [Section titled “Prerequisites”](#prerequisites) * Electron 20.0.0 or higher * Node.js 18 or higher * A Capgo account (sign up at [capgo.app](https://capgo.app)) ## Installation [Section titled “Installation”](#installation) 1. Install the package: ```bash npm install @capgo/electron-updater ``` 2. Get your App ID from the Capgo dashboard. If you haven’t created an app yet, run: ```bash npx @capgo/cli@latest init ``` ## Setup [Section titled “Setup”](#setup) The Electron Updater requires setup in three places: main process, preload script, and renderer process. ### Main Process [Section titled “Main Process”](#main-process) main.ts ```typescript import { app, BrowserWindow } from 'electron'; import * as path from 'path'; import { ElectronUpdater, setupIPCHandlers, setupEventForwarding, } from '@capgo/electron-updater'; // Create updater instance with your Capgo App ID const updater = new ElectronUpdater({ appId: 'YOUR_CAPGO_APP_ID', // e.g., 'com.example.myapp' autoUpdate: true, }); app.whenReady().then(async () => { const mainWindow = new BrowserWindow({ width: 1200, height: 800, webPreferences: { preload: path.join(__dirname, 'preload.js'), contextIsolation: true, }, }); // Initialize updater with window and builtin path const builtinPath = path.join(__dirname, 'www/index.html'); await updater.initialize(mainWindow, builtinPath); // Setup IPC communication between main and renderer setupIPCHandlers(updater); setupEventForwarding(updater, mainWindow); // Load the current bundle (either builtin or downloaded update) await mainWindow.loadFile(updater.getCurrentBundlePath()); }); app.on('window-all-closed', () => { if (process.platform !== 'darwin') { app.quit(); } }); ``` ### Preload Script [Section titled “Preload Script”](#preload-script) preload.ts ```typescript import { exposeUpdaterAPI } from '@capgo/electron-updater/preload'; // Expose the updater API to the renderer process exposeUpdaterAPI(); ``` ### Renderer Process [Section titled “Renderer Process”](#renderer-process) ```typescript // renderer.ts (or in your app's entry point) import { requireUpdater } from '@capgo/electron-updater/renderer'; const updater = requireUpdater(); // CRITICAL: Call this on every app launch! // This confirms the bundle loaded successfully and prevents rollback await updater.notifyAppReady(); console.log('App ready, current bundle:', await updater.current()); ``` Caution You **must** call `notifyAppReady()` within 10 seconds of app launch (configurable via `appReadyTimeout`). If not called, the updater assumes the update failed and rolls back to the previous version. ## Checking for Updates [Section titled “Checking for Updates”](#checking-for-updates) With `autoUpdate: true`, the updater automatically checks for updates. You can also trigger manual checks: ```typescript // Check for updates manually const latest = await updater.getLatest(); if (latest.url && !latest.error) { console.log('Update available:', latest.version); // Download the update const bundle = await updater.download({ url: latest.url, version: latest.version, checksum: latest.checksum, }); console.log('Downloaded bundle:', bundle.id); // Option 1: Queue for next restart await updater.next({ id: bundle.id }); // Option 2: Apply immediately and reload // await updater.set({ id: bundle.id }); } ``` ## Listening to Events [Section titled “Listening to Events”](#listening-to-events) Track update progress and status with events: ```typescript // Download progress updater.addListener('download', (event) => { console.log(`Download progress: ${event.percent}%`); }); // Update available updater.addListener('updateAvailable', (event) => { console.log('New version available:', event.bundle.version); }); // Download completed updater.addListener('downloadComplete', (event) => { console.log('Download finished:', event.bundle.id); }); // Update failed updater.addListener('updateFailed', (event) => { console.error('Update failed:', event.bundle.version); }); ``` ## Deploying Updates [Section titled “Deploying Updates”](#deploying-updates) Use the Capgo CLI to upload updates: ```bash # Build your app npm run build # Upload to Capgo npx @capgo/cli@latest bundle upload --channel=production ``` Your Electron app will automatically detect and download the new bundle on next check. ## Debug Menu [Section titled “Debug Menu”](#debug-menu) Enable the debug menu during development: ```typescript const updater = new ElectronUpdater({ appId: 'YOUR_CAPGO_APP_ID', debugMenu: true, // Enable debug menu }); ``` Press `Ctrl+Shift+D` (or `Cmd+Shift+D` on Mac) to open the debug menu and: * View current bundle information * Switch between available bundles * Reset to builtin version * View device and channel info ## Configuration Options [Section titled “Configuration Options”](#configuration-options) ```typescript const updater = new ElectronUpdater({ // Required appId: 'com.example.app', // Server URLs (defaults to Capgo Cloud) updateUrl: 'https://plugin.capgo.app/updates', channelUrl: 'https://plugin.capgo.app/channel_self', statsUrl: 'https://plugin.capgo.app/stats', // Behavior autoUpdate: true, // Enable auto-updates appReadyTimeout: 10000, // MS before rollback (default: 10s) autoDeleteFailed: true, // Delete failed bundles autoDeletePrevious: true, // Delete old bundles after successful update // Channels defaultChannel: 'production', // Security publicKey: '...', // For end-to-end encryption // Debug debugMenu: false, // Enable debug menu disableJSLogging: false, // Disable console logs // Periodic Updates periodCheckDelay: 0, // Seconds between checks (0 = disabled, min 600) }); ``` ## Next Steps [Section titled “Next Steps”](#next-steps) * [API Reference](/docs/plugins/electron-updater/api/) - Explore all available methods * [Channels](/docs/live-updates/channels/) - Learn about deployment channels * [Rollbacks](/docs/live-updates/rollbacks/) - Understand rollback protection # @capgo/capacitor-env > Deliver tenant-specific settings without new builds by reading secure configuration values directly from Capacitor. Capgo Env lets you store secrets and per-build configuration inside `capacitor.config.*` and resolve them at runtime through a simple API. Runtime configuration Read values from native config files without shipping them in your JavaScript bundle. Per build overrides Create multiple app flavors or tenants by providing different config files. Typesafe access Fetch keys with TypeScript support and centralize fallback logic. Secure by default Keep API keys and secrets out of Git while still shipping them with native binaries. Follow the getting started guide to declare keys in your Capacitor config and fetch them safely across platforms. # Getting Started > Configure per-build environment values and fetch them at runtime with the Env plugin. 1. **Install the package** * npm ```sh npm i @capgo/capacitor-env ``` * pnpm ```sh pnpm add @capgo/capacitor-env ``` * yarn ```sh yarn add @capgo/capacitor-env ``` * bun ```sh bun add @capgo/capacitor-env ``` 2. **Sync native platforms** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Declare configuration values [Section titled “Declare configuration values”](#declare-configuration-values) Add keys to the Capacitor config so they are baked into your native builds. You can create multiple config variants (`capacitor.config.prod.ts`, `capacitor.config.dev.ts`, etc.) to swap values per environment. capacitor.config.ts ```ts import { CapacitorConfig } from '@capacitor/cli'; const config: CapacitorConfig = { appId: 'com.example.app', appName: 'Example', webDir: 'dist', plugins: { Env: { API_URL: 'https://api.example.com', PUBLIC_KEY: 'pk_live_123', }, }, }; export default config; ``` On native platforms the values are stored inside the generated configuration files (`ios/App/App/capacitor.config.json` and `android/app/src/main/assets/capacitor.config.json`). Update those files per flavor if you need tenant-specific values. ## Read values in code [Section titled “Read values in code”](#read-values-in-code) ```typescript import { Env } from '@capgo/capacitor-env'; const apiUrl = await Env.getKey({ key: 'API_URL' }).then((result) => result.value); if (!apiUrl) { throw new Error('Missing API_URL configuration'); } ``` ## Provide fallbacks [Section titled “Provide fallbacks”](#provide-fallbacks) ```typescript const loadConfig = async () => { const { value: endpoint } = await Env.getKey({ key: 'API_URL' }); return endpoint || 'https://staging.example.com'; }; ``` ## Tips [Section titled “Tips”](#tips) * Use different `capacitor.config` files per environment and point the CLI to the right one with `npx cap run ios --configuration=prod`. * Combine with Capgo updater channels to ship tenant-specific values without publishing new binaries. * Keep secrets out of source control by substituting them during your CI build before `npx cap sync`. # @capgo/capacitor-fast-sql > Ultra-fast native SQLite with custom protocol for sync systems and large datasets, delivering up to 25x faster performance. Custom HTTP Protocol Bypasses Capacitor’s bridge for faster large-result and batch workloads. Transaction support Supports `transaction()`, explicit begin/commit/rollback, and isolation levels. Batch + BLOB support Execute multiple statements in one request and store binary data. Key-value convenience Use `KeyValueStore` for simple mobile-first key/value persistence. Connection management Track open databases, disconnect explicitly, or close all in one call. Getting Started Check the getting started guide to install and configure the plugin. # Getting Started with Fast SQL > Install and configure the **@capgo/capacitor-fast-sql** plugin for high-performance native SQLite database access 1. **Install the package** * npm ```sh npm i @capgo/capacitor-fast-sql ``` * pnpm ```sh pnpm add @capgo/capacitor-fast-sql ``` * yarn ```sh yarn add @capgo/capacitor-fast-sql ``` * bun ```sh bun add @capgo/capacitor-fast-sql ``` 2. **Sync with native projects** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` 3. **Configure platforms** ### iOS [Section titled “iOS”](#ios) Allow local network access in `Info.plist`: ios/App/App/Info.plist ```xml NSAppTransportSecurity NSAllowsLocalNetworking ``` ### Android [Section titled “Android”](#android) Add cleartext exception for localhost traffic: android/app/src/main/AndroidManifest.xml ```xml ... ``` ### Web [Section titled “Web”](#web) Install sql.js for web storage fallback: ```bash npm install sql.js ``` ## Why Fast SQL? [Section titled “Why Fast SQL?”](#why-fast-sql) Fast SQL avoids heavy bridge serialization by using a local HTTP transport to native SQLite, which is much faster for large result sets and sync-style writes. ## Basic Usage [Section titled “Basic Usage”](#basic-usage) ```typescript import { FastSQL, IsolationLevel, KeyValueStore } from '@capgo/capacitor-fast-sql'; const db = await FastSQL.connect({ database: 'myapp', encrypted: false, readOnly: false, }); await db.execute(` CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, email TEXT UNIQUE, created_at INTEGER DEFAULT (strftime('%s', 'now')) ) `); await db.run('INSERT INTO users (name, email) VALUES (?, ?)', ['John', 'john@example.com']); const users = await db.query('SELECT * FROM users'); ``` ## Core connection methods [Section titled “Core connection methods”](#core-connection-methods) * `execute(statement, params?)` -> returns full SQL result * `query(statement, params?)` -> returns `rows` * `run(statement, params?)` -> returns `{ rowsAffected, insertId }` * `executeBatch(operations)` -> returns an array of results ```typescript import { type SQLBatchOperation } from '@capgo/capacitor-fast-sql'; const rows = await db.query('SELECT * FROM users WHERE email LIKE ?', ['john%']); const batch: SQLBatchOperation[] = [ { statement: 'INSERT INTO users (name, email) VALUES (?, ?)', params: ['Alice', 'alice@acme.dev'] }, { statement: 'INSERT INTO users (name, email) VALUES (?, ?)', params: ['Bob', 'bob@acme.dev'] }, ]; await db.executeBatch(batch); ``` ## Transactions [Section titled “Transactions”](#transactions) ### callback helper [Section titled “callback helper”](#callback-helper) ```typescript await db.transaction(async (tx) => { await tx.run('UPDATE accounts SET balance = balance - 100 WHERE id = ?', [1]); await tx.run('UPDATE accounts SET balance = balance + 100 WHERE id = ?', [2]); }); ``` ### explicit control [Section titled “explicit control”](#explicit-control) ```typescript import { IsolationLevel } from '@capgo/capacitor-fast-sql'; await db.beginTransaction(IsolationLevel.Serializable); try { await db.run('UPDATE wallets SET points = points - 1 WHERE user_id = ?', [42]); await db.commit(); } catch (error) { await db.rollback(); throw error; } ``` Supported isolation levels: * `ReadUncommitted` * `ReadCommitted` * `RepeatableRead` * `Serializable` ## Binary data (BLOB) [Section titled “Binary data (BLOB)”](#binary-data-blob) ```typescript const imageData = new Uint8Array([0xFF, 0xD8, 0xFF, 0xE0]); await db.run('INSERT INTO assets (name, data) VALUES (?, ?)', ['avatar', imageData]); const rows = await db.query('SELECT data FROM assets WHERE name = ?', ['avatar']); const retrieved = rows[0].data; // Uint8Array ``` ## Encryption and read-only modes [Section titled “Encryption and read-only modes”](#encryption-and-read-only-modes) ```typescript const secureDb = await FastSQL.connect({ database: 'secure_db', encrypted: true, encryptionKey: 'replace-with-a-strong-key', }); const readOnlyDb = await FastSQL.connect({ database: 'public_db', readOnly: true, }); ``` On Android, encrypted mode uses SQLCipher; include dependency in app `build.gradle`. ## KeyValueStore [Section titled “KeyValueStore”](#keyvaluestore) `KeyValueStore` is a convenience wrapper for mobile key/value data. ```typescript import { KeyValueStore } from '@capgo/capacitor-fast-sql'; const kv = await KeyValueStore.open({ database: 'kv', store: 'session', encrypted: true, encryptionKey: 'super-secret-key', }); await kv.set('session', { token: 'abc', expiresAt: Date.now() + 3600_000 }); const session = await kv.get('session'); await kv.has('session'); await kv.keys(); await kv.remove('session'); await kv.clear(); await kv.close(); ``` ## Connection lifecycle [Section titled “Connection lifecycle”](#connection-lifecycle) ```typescript await FastSQL.disconnect('myapp'); await FastSQL.disconnectAll(); const openDatabases = FastSQL.getOpenDatabases(); const same = FastSQL.getConnection('myapp'); ``` ## Error handling [Section titled “Error handling”](#error-handling) ```typescript try { await FastSQL.connect({ database: 'myapp' }); await db.query('SELECT * FROM unknown_table'); } catch (error) { console.error('Fast SQL error:', error); } ``` ## Common SQL Patterns [Section titled “Common SQL Patterns”](#common-sql-patterns) ### Check if Table Exists [Section titled “Check if Table Exists”](#check-if-table-exists) ```typescript const result = await db.query( "SELECT name FROM sqlite_master WHERE type='table' AND name=?", ['users'] ); const tableExists = result.length > 0; ``` ### Get Table Schema [Section titled “Get Table Schema”](#get-table-schema) ```typescript const schema = await db.query('PRAGMA table_info(users)'); console.log('Columns:', schema); ``` ### Count Rows [Section titled “Count Rows”](#count-rows) ```typescript const result = await db.query('SELECT COUNT(*) as count FROM users'); const count = result[0].count; ``` ### Pagination [Section titled “Pagination”](#pagination) ```typescript const pageSize = 20; const page = 1; const offset = (page - 1) * pageSize; const users = await db.query( 'SELECT * FROM users ORDER BY created_at DESC LIMIT ? OFFSET ?', [pageSize, offset] ); ``` ## Performance Tips [Section titled “Performance Tips”](#performance-tips) 1. **Use Transactions** for multiple operations - significantly faster than individual commits 2. **Use Batch Operations** for bulk inserts - more efficient than loops 3. **Create Indexes** on frequently queried columns 4. **Use Prepared Statements** with parameters (?) - prevents SQL injection and improves performance 5. **Use HTTP Protocol Directly** for very large result sets 6. **Close Connections** when not in use to free resources ## Next Steps [Section titled “Next Steps”](#next-steps) Check out the [complete tutorial](/plugins/capacitor-fast-sql) for advanced patterns including: * Database service architecture * Migration systems * Sync engines * Complex queries and joins * Performance optimization ## Related Documentation [Section titled “Related Documentation”](#related-documentation) * [Migrate from Ionic Secure Storage](/docs/upgrade/from-ionic-secure-storage/) * [Ionic enterprise plugins migration solution](/solutions/ionic-enterprise-plugins/) # @capgo/capacitor-ffmpeg > Re-encode videos with custom resolution and bitrate using the powerful FFmpeg library in your Capacitor apps. Video Re-encoding Convert and compress videos with FFmpeg Custom Resolution Resize videos to any width and height Bitrate Control Control video quality and file size Comprehensive Documentation Check the [Documentation](/docs/plugins/ffmpeg/getting-started/) to start processing videos. # Getting Started > Learn how to install and use the FFmpeg plugin for video processing in your Capacitor app. 1. **Install the package** * npm ```sh npm i @capgo/capacitor-ffmpeg ``` * pnpm ```sh pnpm add @capgo/capacitor-ffmpeg ``` * yarn ```sh yarn add @capgo/capacitor-ffmpeg ``` * bun ```sh bun add @capgo/capacitor-ffmpeg ``` 2. **Sync with native projects** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Usage [Section titled “Usage”](#usage) Import the plugin and use it to re-encode videos: ```typescript import { CapacitorFFmpeg } from '@capgo/capacitor-ffmpeg'; // Re-encode a video with custom settings const processVideo = async () => { await CapacitorFFmpeg.reencodeVideo({ inputPath: '/path/to/input/video.mp4', outputPath: '/path/to/output/video.mp4', width: 1280, height: 720, bitrate: 2000000 // Optional: 2 Mbps }); }; // Get plugin version const checkVersion = async () => { const { version } = await CapacitorFFmpeg.getPluginVersion(); console.log('FFmpeg plugin version:', version); }; ``` ## API Reference [Section titled “API Reference”](#api-reference) ### reencodeVideo(options) [Section titled “reencodeVideo(options)”](#reencodevideooptions) Re-encode a video file with specified dimensions and bitrate. ```typescript await CapacitorFFmpeg.reencodeVideo({ inputPath: '/path/to/input.mp4', outputPath: '/path/to/output.mp4', width: 1920, height: 1080, bitrate: 5000000 // Optional: 5 Mbps }); ``` **Parameters:** * `inputPath` (string): Full path to the input video file * `outputPath` (string): Full path where the output video will be saved * `width` (number): Target width in pixels * `height` (number): Target height in pixels * `bitrate` (number, optional): Target bitrate in bits per second ### getPluginVersion() [Section titled “getPluginVersion()”](#getpluginversion) Get the native Capacitor plugin version. ```typescript const { version } = await CapacitorFFmpeg.getPluginVersion(); ``` ## Complete Example [Section titled “Complete Example”](#complete-example) ```typescript import { CapacitorFFmpeg } from '@capgo/capacitor-ffmpeg'; import { Filesystem, Directory } from '@capacitor/filesystem'; export class VideoProcessor { /** * Compress a video to reduce file size */ async compressVideo(inputPath: string, quality: 'low' | 'medium' | 'high') { const qualitySettings = { low: { width: 640, height: 360, bitrate: 500000 }, medium: { width: 1280, height: 720, bitrate: 2000000 }, high: { width: 1920, height: 1080, bitrate: 5000000 } }; const settings = qualitySettings[quality]; const outputPath = inputPath.replace('.mp4', `_${quality}.mp4`); try { await CapacitorFFmpeg.reencodeVideo({ inputPath, outputPath, width: settings.width, height: settings.height, bitrate: settings.bitrate }); console.log(`Video compressed to ${quality} quality:`, outputPath); return outputPath; } catch (error) { console.error('Video compression failed:', error); throw error; } } /** * Resize video to specific dimensions */ async resizeVideo( inputPath: string, width: number, height: number ): Promise { const outputPath = inputPath.replace('.mp4', '_resized.mp4'); await CapacitorFFmpeg.reencodeVideo({ inputPath, outputPath, width, height }); return outputPath; } /** * Create a thumbnail-quality version of a video */ async createThumbnailVideo(inputPath: string): Promise { return this.compressVideo(inputPath, 'low'); } /** * Batch process multiple videos */ async processMultipleVideos( videoPaths: string[], width: number, height: number, bitrate?: number ): Promise { const outputPaths: string[] = []; for (const inputPath of videoPaths) { const outputPath = inputPath.replace('.mp4', '_processed.mp4'); try { await CapacitorFFmpeg.reencodeVideo({ inputPath, outputPath, width, height, bitrate }); outputPaths.push(outputPath); } catch (error) { console.error(`Failed to process ${inputPath}:`, error); } } return outputPaths; } } ``` ## Best Practices [Section titled “Best Practices”](#best-practices) 1. **Use appropriate bitrates** Choose bitrate based on resolution and use case: ```typescript // Mobile sharing (low bandwidth) const lowQuality = { width: 640, height: 360, bitrate: 500000 }; // Standard quality const standardQuality = { width: 1280, height: 720, bitrate: 2000000 }; // High quality const highQuality = { width: 1920, height: 1080, bitrate: 5000000 }; ``` 2. **Maintain aspect ratio** Calculate dimensions to preserve aspect ratio: ```typescript function calculateDimensions(originalWidth: number, originalHeight: number, targetWidth: number) { const aspectRatio = originalWidth / originalHeight; return { width: targetWidth, height: Math.round(targetWidth / aspectRatio) }; } ``` 3. **Handle file paths correctly** Use Capacitor Filesystem for cross-platform path handling: ```typescript import { Filesystem, Directory } from '@capacitor/filesystem'; const inputPath = await Filesystem.getUri({ directory: Directory.Documents, path: 'input.mp4' }); ``` 4. **Show progress to users** Video processing can be slow - inform users: ```typescript async function processWithProgress(inputPath: string) { // Show loading indicator showLoading('Processing video...'); try { await CapacitorFFmpeg.reencodeVideo({ inputPath, outputPath: '/path/to/output.mp4', width: 1280, height: 720 }); showSuccess('Video processed successfully!'); } catch (error) { showError('Failed to process video'); } finally { hideLoading(); } } ``` 5. **Clean up temporary files** Remove intermediate files to save storage: ```typescript async function processAndCleanup(inputPath: string) { const outputPath = inputPath.replace('.mp4', '_output.mp4'); await CapacitorFFmpeg.reencodeVideo({ inputPath, outputPath, width: 1280, height: 720 }); // Remove original if no longer needed await Filesystem.deleteFile({ path: inputPath }); return outputPath; } ``` ## Platform Notes [Section titled “Platform Notes”](#platform-notes) ### iOS [Section titled “iOS”](#ios) * Requires iOS 11.0+ * Large video processing may require background task permissions * Uses native iOS VideoToolbox for hardware acceleration ### Android [Section titled “Android”](#android) * Requires Android 5.0 (API 21)+ * Hardware acceleration varies by device * May require WRITE\_EXTERNAL\_STORAGE permission for file access ### Web [Section titled “Web”](#web) * Not supported on web platform ## Performance Tips [Section titled “Performance Tips”](#performance-tips) 1. **Lower resolution for faster processing**: Smaller dimensions = faster encoding 2. **Use hardware acceleration**: Let the native platform optimize encoding 3. **Process in background**: Don’t block UI during video processing 4. **Monitor memory usage**: Large videos can consume significant memory 5. **Test on real devices**: Emulators may not reflect actual performance # @capgo/capacitor-file > Complete file system operations with read, write, copy, move, and directory management for iOS and Android. Complete File Operations Read, write, append, copy, move, and delete files with ease Directory Management Create, list, and remove directories with recursive support Multiple Encodings Support for UTF-8, ASCII, UTF-16, and binary (base64) data Cross-platform Works on iOS and Android with consistent API across platforms # @capgo/capacitor-file-compressor > Compress images efficiently with support for PNG, JPEG, and WebP formats on iOS, Android, and Web. Multiple Formats Support for JPEG and WebP compression across platforms 🖼️ Quality Control Adjustable compression quality from 0.0 to 1.0 for perfect balance ⚙️ Smart Resizing Automatic aspect ratio preservation during resize 📐 Zero Backend All compression happens on the device - no server needed 🚀 Cross-platform Consistent API across iOS, Android, and Web platforms 📱 Comprehensive Documentation Check the [Documentation](/docs/plugins/file-compressor/getting-started/) to master the plugin in just a few minutes. # Getting Started > Learn how to install and use the File Compressor plugin to compress images in your Capacitor app. 1. **Install the package** * npm ```sh npm i @capgo/capacitor-file-compressor ``` * pnpm ```sh pnpm add @capgo/capacitor-file-compressor ``` * yarn ```sh yarn add @capgo/capacitor-file-compressor ``` * bun ```sh bun add @capgo/capacitor-file-compressor ``` 2. **Sync with native projects** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Platform Support [Section titled “Platform Support”](#platform-support) | Platform | Supported Formats | Notes | | -------- | ----------------- | ------------------------------- | | iOS | JPEG | Only JPEG compression supported | | Android | JPEG, WebP | Both formats fully supported | | Web | JPEG, WebP | Canvas API-based compression | Note: EXIF metadata is removed during compression on all platforms. ## Usage [Section titled “Usage”](#usage) Import the plugin and compress images: ```typescript import { FileCompressor } from '@capgo/capacitor-file-compressor'; // Compress an image const compressImage = async () => { const result = await FileCompressor.compressImage({ source: 'file:///path/to/image.jpg', quality: 0.8, width: 1920, height: 1080 }); console.log('Compressed image path:', result.path); console.log('Original size:', result.originalSize); console.log('Compressed size:', result.size); }; ``` ## API Reference [Section titled “API Reference”](#api-reference) ### compressImage(options) [Section titled “compressImage(options)”](#compressimageoptions) Compresses an image file with specified dimensions and quality settings. ```typescript interface CompressImageOptions { source: string; // Path to the image file quality?: number; // 0.0 to 1.0 (default: 0.8) width?: number; // Target width in pixels height?: number; // Target height in pixels format?: 'jpeg' | '.webp'; // Output format } interface CompressImageResult { path: string; // Path to compressed image size: number; // Compressed file size in bytes originalSize: number; // Original file size in bytes } const result = await FileCompressor.compressImage(options); ``` **Important Notes:** * EXIF metadata is removed during compression on all platforms * Aspect ratio is automatically maintained if only one dimension is provided * Compressed files are saved to temporary directories on native platforms ### getPluginVersion() [Section titled “getPluginVersion()”](#getpluginversion) Get the native plugin version. ```typescript const { version } = await FileCompressor.getPluginVersion(); console.log('Plugin version:', version); ``` ## Complete Example [Section titled “Complete Example”](#complete-example) ```typescript import { FileCompressor } from '@capgo/capacitor-file-compressor'; import { Camera } from '@capacitor/camera'; export class ImageCompressionService { async captureAndCompress() { try { // Take a photo const photo = await Camera.getPhoto({ quality: 100, allowEditing: false, resultType: 'uri' }); if (!photo.path) { throw new Error('No image path'); } // Compress the photo const compressed = await FileCompressor.compressImage({ source: photo.path, quality: 0.7, width: 1920, height: 1080, format: 'jpeg' }); console.log(`Compression ratio: ${ ((1 - compressed.size / compressed.originalSize) * 100).toFixed(1) }%`); return compressed.path; } catch (error) { console.error('Compression failed:', error); throw error; } } async batchCompress(imagePaths: string[]) { const results = []; for (const path of imagePaths) { try { const result = await FileCompressor.compressImage({ source: path, quality: 0.8, width: 1280 }); results.push(result); } catch (error) { console.error(`Failed to compress ${path}:`, error); } } return results; } } ``` ## Best Practices [Section titled “Best Practices”](#best-practices) 1. **Quality Settings**: Start with 0.8 quality for a good balance between file size and image quality 2. **Resize Dimensions**: Only specify dimensions when needed - aspect ratio is preserved automatically 3. **Format Selection**: Use JPEG for photos and WebP for better compression (Android/Web only) 4. **Error Handling**: Always wrap compression calls in try-catch blocks 5. **Cleanup**: Remember to clean up temporary files after use ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) ### Common Issues [Section titled “Common Issues”](#common-issues) **Image not compressing**: Ensure the source path is valid and accessible **Out of memory**: Reduce target dimensions or compress images one at a time **Format not supported**: Check platform support table above # @capgo/capacitor-file-picker > A comprehensive file picker plugin with native support for picking files, images, videos, and directories on iOS and Android. Multi-File Support Pick files, images, videos, or directories with a single API Rich Metadata Get file size, dimensions, duration, and more for all picked files HEIC Conversion Convert HEIC images to JPEG on iOS for better compatibility Cross-Platform Works on iOS, Android, and Web with platform-specific optimizations # Getting Started > Learn how to install and use the File Picker plugin to select files, images, videos, and directories in your Capacitor app. 1. **Install the package** * npm ```sh npm i @capgo/capacitor-file-picker ``` * pnpm ```sh pnpm add @capgo/capacitor-file-picker ``` * yarn ```sh yarn add @capgo/capacitor-file-picker ``` * bun ```sh bun add @capgo/capacitor-file-picker ``` 2. **Sync with native projects** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Requirements [Section titled “Requirements”](#requirements) * iOS: iOS 15.0+ * Android: API 24+ (Android 7.0+) * Web: Modern browsers with File API support ## Basic Usage [Section titled “Basic Usage”](#basic-usage) ### Pick Files [Section titled “Pick Files”](#pick-files) ```typescript import { FilePicker } from '@capgo/capacitor-file-picker'; // Pick any files const pickFiles = async () => { const result = await FilePicker.pickFiles({ limit: 5 // Max 5 files }); for (const file of result.files) { console.log('File name:', file.name); console.log('File path:', file.path); console.log('MIME type:', file.mimeType); console.log('Size:', file.size); } }; // Pick specific file types const pickPDFs = async () => { const result = await FilePicker.pickFiles({ types: ['application/pdf'], limit: 1 }); return result.files[0]; }; ``` ### Pick Images [Section titled “Pick Images”](#pick-images) ```typescript import { FilePicker } from '@capgo/capacitor-file-picker'; const pickImages = async () => { const result = await FilePicker.pickImages({ limit: 10, ordered: true // Show selection order badges (iOS 15+) }); for (const image of result.files) { console.log('Image:', image.name); console.log('Dimensions:', image.width, 'x', image.height); console.log('Path:', image.path); } }; ``` ### Pick Videos [Section titled “Pick Videos”](#pick-videos) ```typescript import { FilePicker } from '@capgo/capacitor-file-picker'; const pickVideos = async () => { const result = await FilePicker.pickVideos({ limit: 3, skipTranscoding: true // Skip video transcoding on iOS }); for (const video of result.files) { console.log('Video:', video.name); console.log('Duration:', video.duration, 'seconds'); console.log('Dimensions:', video.width, 'x', video.height); } }; ``` ### Pick Media (Images + Videos) [Section titled “Pick Media (Images + Videos)”](#pick-media-images--videos) ```typescript import { FilePicker } from '@capgo/capacitor-file-picker'; const pickMedia = async () => { const result = await FilePicker.pickMedia({ limit: 0 // Unlimited selection }); return result.files; }; ``` ### Pick Directory [Section titled “Pick Directory”](#pick-directory) ```typescript import { FilePicker } from '@capgo/capacitor-file-picker'; const pickDirectory = async () => { const result = await FilePicker.pickDirectory(); console.log('Selected directory:', result.path); return result.path; }; ``` ## API Reference [Section titled “API Reference”](#api-reference) ### pickFiles(options?) [Section titled “pickFiles(options?)”](#pickfilesoptions) Pick one or more files from the device. ```typescript interface PickFilesOptions { types?: string[]; // MIME types or extensions: ['image/*'], ['application/pdf'] limit?: number; // Max files (0 = unlimited) readData?: boolean; // Return base64 data } const result = await FilePicker.pickFiles(options); // Returns: { files: PickedFile[] } ``` ### pickImages(options?) [Section titled “pickImages(options?)”](#pickimagesoptions) Pick images from the gallery. Android/iOS only. ```typescript interface PickMediaOptions { limit?: number; // Max files (0 = unlimited) readData?: boolean; // Return base64 data skipTranscoding?: boolean; // iOS: Skip transcoding ordered?: boolean; // iOS 15+: Show selection order } const result = await FilePicker.pickImages(options); ``` ### pickVideos(options?) [Section titled “pickVideos(options?)”](#pickvideosoptions) Pick videos from the gallery. Android/iOS only. ```typescript const result = await FilePicker.pickVideos(options); ``` ### pickMedia(options?) [Section titled “pickMedia(options?)”](#pickmediaoptions) Pick images or videos from the gallery. Android/iOS only. ```typescript const result = await FilePicker.pickMedia(options); ``` ### pickDirectory() [Section titled “pickDirectory()”](#pickdirectory) Pick a directory. Android/iOS only. ```typescript const result = await FilePicker.pickDirectory(); // Returns: { path: string } ``` ### convertHeicToJpeg(options) [Section titled “convertHeicToJpeg(options)”](#convertheictojpegoptions) Convert a HEIC image to JPEG. iOS only. ```typescript interface ConvertHeicToJpegOptions { path: string; // Path to HEIC file quality?: number; // 0.0 - 1.0 (default: 0.9) } const result = await FilePicker.convertHeicToJpeg({ path: '/path/to/image.heic', quality: 0.8 }); // Returns: { path: string } - Path to converted JPEG ``` ### copyFile(options) [Section titled “copyFile(options)”](#copyfileoptions) Copy a file to a new location. ```typescript interface CopyFileOptions { from: string; // Source path to: string; // Destination path overwrite?: boolean; // Overwrite if exists (default: false) } await FilePicker.copyFile({ from: '/path/to/source.jpg', to: '/path/to/destination.jpg', overwrite: true }); ``` ### checkPermissions() / requestPermissions() [Section titled “checkPermissions() / requestPermissions()”](#checkpermissions--requestpermissions) Check or request file access permissions. Android only. ```typescript const status = await FilePicker.checkPermissions(); // Returns: { readExternalStorage: 'granted' | 'denied' | 'prompt' } const newStatus = await FilePicker.requestPermissions(); ``` ## PickedFile Interface [Section titled “PickedFile Interface”](#pickedfile-interface) ```typescript interface PickedFile { name: string; // File name path: string; // File path mimeType: string; // MIME type size: number; // Size in bytes data?: string; // Base64 data (if readData was true) blob?: Blob; // Blob instance (Web only) width?: number; // Width in pixels (images/videos) height?: number; // Height in pixels (images/videos) duration?: number; // Duration in seconds (videos) modifiedAt?: number; // Last modified timestamp } ``` ## Complete Example [Section titled “Complete Example”](#complete-example) ```typescript import { FilePicker } from '@capgo/capacitor-file-picker'; import { Capacitor } from '@capacitor/core'; export class FilePickerService { async pickProfileImage(): Promise { try { const result = await FilePicker.pickImages({ limit: 1 }); if (result.files.length === 0) { return null; } const file = result.files[0]; // Convert HEIC to JPEG on iOS if needed if (Capacitor.getPlatform() === 'ios' && file.mimeType === 'image/heic') { const converted = await FilePicker.convertHeicToJpeg({ path: file.path, quality: 0.9 }); return converted.path; } return file.path; } catch (error) { console.error('Failed to pick image:', error); return null; } } async pickDocuments(): Promise { const result = await FilePicker.pickFiles({ types: [ 'application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' ], limit: 10 }); return result.files; } async pickAndReadFile(): Promise<{ name: string; data: string } | null> { const result = await FilePicker.pickFiles({ limit: 1, readData: true }); if (result.files.length === 0) return null; const file = result.files[0]; return { name: file.name, data: file.data! // Base64 encoded data }; } async getVideoForUpload(): Promise<{ path: string; duration: number; dimensions: { width: number; height: number }; } | null> { const result = await FilePicker.pickVideos({ limit: 1, skipTranscoding: false // Ensure compatible format }); if (result.files.length === 0) return null; const video = result.files[0]; return { path: video.path, duration: video.duration || 0, dimensions: { width: video.width || 0, height: video.height || 0 } }; } } ``` ## Platform Notes [Section titled “Platform Notes”](#platform-notes) ### iOS [Section titled “iOS”](#ios) * Requires iOS 15.0+ * Uses `PHPickerViewController` for images/videos * Uses `UIDocumentPickerViewController` for files * HEIC images can be converted to JPEG using `convertHeicToJpeg()` * `ordered` option shows selection order badges (iOS 15+) * `skipTranscoding` prevents automatic video format conversion ### Android [Section titled “Android”](#android) * Requires API 24+ (Android 7.0+) * Uses `Intent.ACTION_OPEN_DOCUMENT` for file picking * Uses `Intent.ACTION_PICK` with MediaStore for media * Permissions are automatically requested when needed * Check permissions with `checkPermissions()` before picking ### Web [Section titled “Web”](#web) * Uses native `` element * Returns `Blob` objects in the `blob` property * `pickDirectory()` is not supported on web * HEIC conversion is not supported on web * Some metadata (dimensions, duration) may not be available # Getting Started > Learn how to install and use the File plugin for file system operations in your Capacitor app. 1. **Install the package** * npm ```sh npm i @capgo/capacitor-file ``` * pnpm ```sh pnpm add @capgo/capacitor-file ``` * yarn ```sh yarn add @capgo/capacitor-file ``` * bun ```sh bun add @capgo/capacitor-file ``` 2. **Sync with native projects** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Usage [Section titled “Usage”](#usage) Import the plugin and use its methods for file operations: ```typescript import { CapacitorFile, Directory, Encoding } from '@capgo/capacitor-file'; // Write a text file const writeFile = async () => { const result = await CapacitorFile.writeFile({ path: 'my-file.txt', directory: Directory.Documents, data: 'Hello, World!', encoding: Encoding.UTF8, recursive: true, // Create parent directories if needed }); console.log('File written to:', result.uri); }; // Read a text file const readFile = async () => { const result = await CapacitorFile.readFile({ path: 'my-file.txt', directory: Directory.Documents, encoding: Encoding.UTF8, }); console.log('File contents:', result.data); }; // Check if file exists const checkExists = async () => { const result = await CapacitorFile.exists({ path: 'my-file.txt', directory: Directory.Documents, }); console.log('File exists:', result.exists); }; // Delete a file const deleteFile = async () => { await CapacitorFile.deleteFile({ path: 'my-file.txt', directory: Directory.Documents, }); }; ``` ## Directory Locations [Section titled “Directory Locations”](#directory-locations) The plugin provides several directory constants: ```typescript import { Directory } from '@capgo/capacitor-file'; // Available directories Directory.Documents // User-visible documents (backed up) Directory.Data // Private app data storage Directory.Library // App support files (iOS) / files (Android) Directory.Cache // Temporary cache (may be cleared by OS) Directory.External // External storage (Android only) Directory.Application // Read-only app bundle ``` ## Encoding Options [Section titled “Encoding Options”](#encoding-options) ```typescript import { Encoding } from '@capgo/capacitor-file'; Encoding.UTF8 // UTF-8 text encoding Encoding.ASCII // ASCII text encoding Encoding.UTF16 // UTF-16 text encoding // Omit encoding for binary data (returns base64) ``` ## API Reference [Section titled “API Reference”](#api-reference) ### writeFile(options) [Section titled “writeFile(options)”](#writefileoptions) Write data to a file. ```typescript const result = await CapacitorFile.writeFile({ path: 'folder/file.txt', directory: Directory.Documents, data: 'File content', encoding: Encoding.UTF8, recursive: true, // Create directories if needed append: false, // Overwrite existing file position: 0, // Byte position for random access writes }); // Returns: { uri: string } ``` ### readFile(options) [Section titled “readFile(options)”](#readfileoptions) Read a file’s contents. ```typescript const result = await CapacitorFile.readFile({ path: 'file.txt', directory: Directory.Documents, encoding: Encoding.UTF8, offset: 0, // Start reading from byte offset length: 100, // Read only this many bytes }); // Returns: { data: string } ``` ### appendFile(options) [Section titled “appendFile(options)”](#appendfileoptions) Append data to a file. ```typescript await CapacitorFile.appendFile({ path: 'file.txt', directory: Directory.Documents, data: '\nNew line', encoding: Encoding.UTF8, }); ``` ### deleteFile(options) [Section titled “deleteFile(options)”](#deletefileoptions) Delete a file. ```typescript await CapacitorFile.deleteFile({ path: 'file.txt', directory: Directory.Documents, }); ``` ### mkdir(options) [Section titled “mkdir(options)”](#mkdiroptions) Create a directory. ```typescript await CapacitorFile.mkdir({ path: 'my-folder/sub-folder', directory: Directory.Documents, recursive: true, // Create parent directories }); ``` ### rmdir(options) [Section titled “rmdir(options)”](#rmdiroptions) Delete a directory. ```typescript await CapacitorFile.rmdir({ path: 'my-folder', directory: Directory.Documents, recursive: true, // Delete contents recursively }); ``` ### readdir(options) [Section titled “readdir(options)”](#readdiroptions) List directory contents. ```typescript const result = await CapacitorFile.readdir({ path: '', // Empty for root of directory directory: Directory.Documents, }); // Returns: { entries: Entry[] } // Each entry has: { name, isFile, isDirectory, fullPath, nativeURL } ``` ### stat(options) [Section titled “stat(options)”](#statoptions) Get file or directory metadata. ```typescript const result = await CapacitorFile.stat({ path: 'file.txt', directory: Directory.Documents, }); // Returns: { type, size, mtime, ctime, uri } ``` ### exists(options) [Section titled “exists(options)”](#existsoptions) Check if a file or directory exists. ```typescript const result = await CapacitorFile.exists({ path: 'file.txt', directory: Directory.Documents, }); // Returns: { exists: boolean, type?: 'file' | 'directory' } ``` ### copy(options) [Section titled “copy(options)”](#copyoptions) Copy a file or directory. ```typescript const result = await CapacitorFile.copy({ from: 'source.txt', to: 'destination.txt', directory: Directory.Documents, toDirectory: Directory.Documents, }); // Returns: { uri: string } ``` ### rename(options) / move(options) [Section titled “rename(options) / move(options)”](#renameoptions--moveoptions) Rename or move a file or directory. ```typescript await CapacitorFile.rename({ from: 'old-name.txt', to: 'new-name.txt', directory: Directory.Documents, toDirectory: Directory.Documents, }); ``` ### truncate(options) [Section titled “truncate(options)”](#truncateoptions) Truncate a file to a specific size. ```typescript await CapacitorFile.truncate({ path: 'file.txt', directory: Directory.Documents, size: 100, // Truncate to 100 bytes }); ``` ### getUri(options) [Section titled “getUri(options)”](#geturioptions) Get the native URI for a file. ```typescript const result = await CapacitorFile.getUri({ path: 'file.txt', directory: Directory.Documents, }); // Returns: { uri: string } ``` ### getDirectories() [Section titled “getDirectories()”](#getdirectories) Get all available directory paths. ```typescript const dirs = await CapacitorFile.getDirectories(); // Returns paths for: documents, data, cache, external, etc. ``` ### getFreeDiskSpace() [Section titled “getFreeDiskSpace()”](#getfreediskspace) Get available disk space. ```typescript const result = await CapacitorFile.getFreeDiskSpace(); console.log('Free space:', result.free, 'bytes'); ``` ### checkPermissions() / requestPermissions() [Section titled “checkPermissions() / requestPermissions()”](#checkpermissions--requestpermissions) Handle storage permissions (Android). ```typescript const status = await CapacitorFile.checkPermissions(); if (status.publicStorage !== 'granted') { await CapacitorFile.requestPermissions(); } ``` ## Complete Example [Section titled “Complete Example”](#complete-example) ```typescript import { CapacitorFile, Directory, Encoding } from '@capgo/capacitor-file'; export class FileService { async saveJson(filename: string, data: object): Promise { await CapacitorFile.writeFile({ path: filename, directory: Directory.Documents, data: JSON.stringify(data, null, 2), encoding: Encoding.UTF8, recursive: true, }); } async loadJson(filename: string): Promise { try { const { exists } = await CapacitorFile.exists({ path: filename, directory: Directory.Documents, }); if (!exists) return null; const result = await CapacitorFile.readFile({ path: filename, directory: Directory.Documents, encoding: Encoding.UTF8, }); return JSON.parse(result.data) as T; } catch (error) { console.error('Failed to load JSON:', error); return null; } } async listFiles(folder: string = ''): Promise { const result = await CapacitorFile.readdir({ path: folder, directory: Directory.Documents, }); return result.entries .filter(entry => entry.isFile) .map(entry => entry.name); } async deleteAll(folder: string): Promise { const { exists } = await CapacitorFile.exists({ path: folder, directory: Directory.Documents, }); if (exists) { await CapacitorFile.rmdir({ path: folder, directory: Directory.Documents, recursive: true, }); } } async copyToBackup(filename: string): Promise { const timestamp = Date.now(); const backupName = `backup/${timestamp}-${filename}`; const result = await CapacitorFile.copy({ from: filename, to: backupName, directory: Directory.Documents, toDirectory: Directory.Documents, }); return result.uri; } } ``` ## Platform Notes [Section titled “Platform Notes”](#platform-notes) ### iOS [Section titled “iOS”](#ios) * Requires iOS 13.0+ * Documents directory is visible in Files app * Library directory is for app support files * Cache may be cleared when device storage is low ### Android [Section titled “Android”](#android) * Requires Android 6.0 (API 23)+ * External storage requires runtime permissions on older Android versions * Documents directory maps to app’s files directory * Use External directory for shared storage access ### Web [Section titled “Web”](#web) * Not supported on web platform # @capgo/capacitor-flash > Switch your device's flashlight/torch on and off programmatically with this lightweight Capacitor plugin. Simple API Turn flashlight on/off with just one method call 🚀 Cross-platform Works on iOS, Android, and Web platforms 📱 Lightweight Minimal footprint with no dependencies ⚡ Comprehensive Documentation Check the [Documentation](/docs/plugins/flash/getting-started/) to master the plugin in just a few minutes. # Getting Started > Learn how to install and use the Flash plugin to control your device's flashlight in your Capacitor app. 1. **Install the package** * npm ```sh npm i @capgo/capacitor-flash ``` * pnpm ```sh pnpm add @capgo/capacitor-flash ``` * yarn ```sh yarn add @capgo/capacitor-flash ``` * bun ```sh bun add @capgo/capacitor-flash ``` 2. **Sync with native projects** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Usage [Section titled “Usage”](#usage) Import the plugin and use its methods to control the flashlight: ```typescript import { CapacitorFlash } from '@capgo/capacitor-flash'; // Check if flashlight is available const checkFlashlight = async () => { const { value } = await CapacitorFlash.isAvailable(); console.log('Flashlight available:', value); }; // Turn on flashlight const turnOn = async () => { await CapacitorFlash.switchOn(); }; // Turn off flashlight const turnOff = async () => { await CapacitorFlash.switchOff(); }; // Check if flashlight is on const checkStatus = async () => { const { value } = await CapacitorFlash.isSwitchedOn(); console.log('Flashlight is on:', value); }; // Toggle flashlight const toggle = async () => { await CapacitorFlash.toggle(); }; ``` ## API Reference [Section titled “API Reference”](#api-reference) ### isAvailable() [Section titled “isAvailable()”](#isavailable) Checks if flashlight is available on the device. ```typescript const result = await CapacitorFlash.isAvailable(); // Returns: { value: boolean } ``` ### switchOn(options?) [Section titled “switchOn(options?)”](#switchonoptions) Turns the flashlight on. ```typescript interface SwitchOnOptions { intensity?: number; // 0.0 to 1.0 (iOS only) } await CapacitorFlash.switchOn({ intensity: 0.5 }); ``` ### switchOff() [Section titled “switchOff()”](#switchoff) Turns the flashlight off. ```typescript await CapacitorFlash.switchOff(); ``` ### isSwitchedOn() [Section titled “isSwitchedOn()”](#isswitchedon) Checks if the flashlight is currently on. ```typescript const result = await CapacitorFlash.isSwitchedOn(); // Returns: { value: boolean } ``` ### toggle() [Section titled “toggle()”](#toggle) Toggles the flashlight on/off. ```typescript await CapacitorFlash.toggle(); ``` ## Complete Example [Section titled “Complete Example”](#complete-example) ```typescript import { CapacitorFlash } from '@capgo/capacitor-flash'; export class FlashlightService { private isOn = false; async init() { const { value } = await CapacitorFlash.isAvailable(); if (!value) { throw new Error('Flashlight not available on this device'); } } async toggle() { if (this.isOn) { await CapacitorFlash.switchOff(); } else { await CapacitorFlash.switchOn(); } this.isOn = !this.isOn; } async turnOn(intensity?: number) { await CapacitorFlash.switchOn({ intensity }); this.isOn = true; } async turnOff() { await CapacitorFlash.switchOff(); this.isOn = false; } async strobe(intervalMs: number = 100, duration: number = 3000) { const endTime = Date.now() + duration; while (Date.now() < endTime) { await CapacitorFlash.toggle(); await new Promise(resolve => setTimeout(resolve, intervalMs)); } // Ensure flashlight is off after strobe await CapacitorFlash.switchOff(); this.isOn = false; } } ``` ## Best Practices [Section titled “Best Practices”](#best-practices) 1. **Check availability first** Always verify flashlight availability before using it: ```typescript const { value } = await CapacitorFlash.isAvailable(); if (!value) { // Show alternative UI or disable flashlight features } ``` 2. **Handle errors gracefully** ```typescript try { await CapacitorFlash.switchOn(); } catch (error) { console.error('Failed to turn on flashlight:', error); } ``` 3. **Turn off when not needed** Always turn off the flashlight when your app goes to background or when the feature is no longer needed to save battery. 4. **Avoid rapid toggling** Implement debouncing to prevent rapid on/off switching which could damage the LED or drain battery. ## Platform Notes [Section titled “Platform Notes”](#platform-notes) ### iOS [Section titled “iOS”](#ios) * Requires iOS 10.0+ * Uses `AVCaptureDevice` torch mode * Supports intensity control (0.0 to 1.0) ### Android [Section titled “Android”](#android) * Requires Android 6.0 (API 23)+ * Uses Camera2 API * Intensity control not supported (uses default brightness) ### Web [Section titled “Web”](#web) * Supported on browsers with MediaDevices API and torch capability * Uses the camera stream to access the device’s flashlight * Requires user permission to access the camera * Works best on mobile browsers (Chrome on Android) * Desktop browsers typically return `isAvailable: false` (no torch hardware) * Intensity control not supported on web # @capgo/capacitor-gtm > Integrate Google Tag Manager into your mobile applications with the official SDK for comprehensive analytics tracking. Official SDK Uses official Google Tag Manager SDK for iOS and Android 🏆 Event Tracking Push events to dataLayer with custom parameters 📊 User Properties Set and manage user properties for segmentation 👤 Container Management Initialize and manage GTM containers with ease 📦 DataLayer Access Get values from container configuration 🔍 Getting Started Check the [Getting Started Guide](/docs/plugins/gtm/getting-started/) to install and configure the plugin. # Getting Started > Learn how to install and integrate Google Tag Manager into your Capacitor app using the official SDK. ## Installation [Section titled “Installation”](#installation) * npm ```bash npm install @capgo/capacitor-gtm npx cap sync ``` * yarn ```bash yarn add @capgo/capacitor-gtm npx cap sync ``` * pnpm ```bash pnpm add @capgo/capacitor-gtm npx cap sync ``` * bun ```bash bun add @capgo/capacitor-gtm npx cap sync ``` ## Platform Configuration [Section titled “Platform Configuration”](#platform-configuration) ### iOS [Section titled “iOS”](#ios) 1. Download your GTM container configuration file from Google Tag Manager console 2. Add the container JSON file to your iOS project in Xcode 3. The file should be named something like `GTM-XXXXXX.json` ### Android [Section titled “Android”](#android) 1. Download your GTM container configuration file from Google Tag Manager console 2. Add the container JSON file to `android/app/src/main/assets/` 3. The file should be named something like `GTM-XXXXXX.json` ## Usage Example [Section titled “Usage Example”](#usage-example) ```typescript import { CapacitorGTM } from '@capgo/capacitor-gtm'; // Initialize GTM with your container ID await CapacitorGTM.init({ containerId: 'GTM-XXXXXX' }); // Push events to dataLayer await CapacitorGTM.pushEvent({ event: 'screen_view', parameters: { screen_name: 'Home', screen_class: 'HomeScreen' } }); // Push custom events with parameters await CapacitorGTM.pushEvent({ event: 'purchase', parameters: { transaction_id: 'T12345', value: 25.00, currency: 'USD', items: [ { item_id: 'SKU_12345', item_name: 'Product Name', quantity: 1, price: 25.00 } ] } }); // Set user properties await CapacitorGTM.setUserProperty({ key: 'user_type', value: 'premium' }); // Get values from dataLayer const result = await CapacitorGTM.getVariable({ key: 'config_value' }); console.log('Config value:', result.value); ``` ## API Reference [Section titled “API Reference”](#api-reference) ### init(options) [Section titled “init(options)”](#initoptions) ```typescript init(options: { containerId: string }) => Promise ``` Initialize Google Tag Manager with your container ID. | Param | Type | | ------------- | ------------------------- | | **`options`** | `{ containerId: string }` | ### pushEvent(options) [Section titled “pushEvent(options)”](#pusheventoptions) ```typescript pushEvent(options: GTMEvent) => Promise ``` Push an event to the dataLayer with optional parameters. | Param | Type | | ------------- | ---------- | | **`options`** | `GTMEvent` | ### setUserProperty(options) [Section titled “setUserProperty(options)”](#setuserpropertyoptions) ```typescript setUserProperty(options: { key: string; value: string }) => Promise ``` Set a user property for segmentation and targeting. | Param | Type | | ------------- | -------------------------------- | | **`options`** | `{ key: string; value: string }` | ### getVariable(options) [Section titled “getVariable(options)”](#getvariableoptions) ```typescript getVariable(options: { key: string }) => Promise<{ value: any }> ``` Get a value from the GTM container configuration. | Param | Type | | ------------- | ----------------- | | **`options`** | `{ key: string }` | **Returns:** `Promise<{ value: any }>` ## Interfaces [Section titled “Interfaces”](#interfaces) ### GTMEvent [Section titled “GTMEvent”](#gtmevent) | Prop | Type | Description | | ---------------- | --------------------- | ------------------------------------ | | **`event`** | `string` | The event name | | **`parameters`** | `Record` | Optional event parameters (optional) | ## Common Event Examples [Section titled “Common Event Examples”](#common-event-examples) ### Screen Views [Section titled “Screen Views”](#screen-views) ```typescript await CapacitorGTM.pushEvent({ event: 'screen_view', parameters: { screen_name: 'Product Details', screen_class: 'ProductScreen' } }); ``` ### User Actions [Section titled “User Actions”](#user-actions) ```typescript await CapacitorGTM.pushEvent({ event: 'button_click', parameters: { button_name: 'add_to_cart', product_id: 'SKU_12345' } }); ``` ### E-commerce Events [Section titled “E-commerce Events”](#e-commerce-events) ```typescript await CapacitorGTM.pushEvent({ event: 'begin_checkout', parameters: { currency: 'USD', value: 75.00, items: [ { item_id: 'SKU_12345', item_name: 'Product 1', quantity: 2, price: 25.00 }, { item_id: 'SKU_67890', item_name: 'Product 2', quantity: 1, price: 25.00 } ] } }); ``` ## Best Practices [Section titled “Best Practices”](#best-practices) * Initialize GTM early in your app lifecycle * Use consistent event naming conventions * Include relevant parameters with each event * Test your GTM configuration in preview mode * Monitor data in Google Tag Manager console * Use user properties for meaningful segmentation ## Debugging [Section titled “Debugging”](#debugging) Enable debug mode in Google Tag Manager console to test your implementation: 1. Open GTM console 2. Go to Preview mode 3. Connect to your app using the container ID 4. Verify events are being tracked correctly ## Use Cases [Section titled “Use Cases”](#use-cases) * Track user interactions and behavior * Monitor app performance metrics * Collect e-commerce data * Manage third-party tags without code changes * Implement A/B testing and personalization * Integrate with Google Analytics and other services # @capgo/capacitor-health > Read and write fitness metrics across iOS and Android while keeping one consistent TypeScript contract. The Capgo Health plugin unifies HealthKit and Health Connect access so you can track activity, biometric metrics, and body measurements with one Capacitor integration. Cross-platform health data Support Apple HealthKit and Android Health Connect out of the box. Granular permissions Request separate read/write scopes and react to authorization changes. Consistent units Work with normalized data types like `steps`, `heartRate`, and `weight`. Real-time writes Save samples back to the user’s health store when they log activity. Follow the getting started guide to enable platform capabilities, gather consent, and start syncing metrics. # Getting Started > Enable HealthKit and Health Connect access with the Capgo Health plugin. 1. **Install the plugin** * npm ```sh npm i @capgo/capacitor-health ``` * pnpm ```sh pnpm add @capgo/capacitor-health ``` * yarn ```sh yarn add @capgo/capacitor-health ``` * bun ```sh bun add @capgo/capacitor-health ``` 2. **Sync native projects** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## iOS configuration [Section titled “iOS configuration”](#ios-configuration) * Open `ios/App/App.xcworkspace` in Xcode and enable the **HealthKit** capability. * Provide usage descriptions in `Info.plist`: ```xml NSHealthShareUsageDescription This app reads your health metrics to personalize your experience. NSHealthUpdateUsageDescription This app writes health data that you explicitly record. ``` ## Android configuration [Section titled “Android configuration”](#android-configuration) Health data is powered by [Health Connect](https://developer.android.com/health-and-fitness/guides/health-connect). Make sure: * `minSdkVersion` is **26 or higher** (Capacitor 5 already satisfies this). * Users have Health Connect installed (Android 14+ bundles it; earlier versions require a Play Store download). * You guide users through the runtime permission UI when prompting for access. ## Check availability and request access [Section titled “Check availability and request access”](#check-availability-and-request-access) ```typescript import { Health } from '@capgo/capacitor-health'; const { available, reason } = await Health.isAvailable(); if (!available) { console.warn('Health APIs unavailable:', reason); return; } await Health.requestAuthorization({ read: ['steps', 'heartRate', 'weight'], write: ['weight'], }); ``` ## Read samples [Section titled “Read samples”](#read-samples) ```typescript const { samples } = await Health.readSamples({ dataType: 'steps', startDate: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString(), endDate: new Date().toISOString(), limit: 200, }); samples.forEach((sample) => { console.log(sample.value, sample.startDate, sample.endDate); }); ``` ## Write samples [Section titled “Write samples”](#write-samples) ```typescript await Health.saveSample({ dataType: 'weight', value: 74.6, startDate: new Date().toISOString(), }); ``` ## Authorization management [Section titled “Authorization management”](#authorization-management) * Use `checkAuthorization()` to verify current read/write scopes and adjust your UI. * Handle denials gracefully by explaining why the data enhances the experience and offering a retry. * Always respect the user’s privacy and only request the minimum scopes your feature requires. # @capgo/capacitor-home-indicator > Hide or show the home indicator on iOS devices to create truly immersive full-screen experiences in your app. iOS home indicator control Full control over the home indicator visibility 📱 Immersive experiences Create full-screen experiences without distractions 🎮 Simple API Hide, show, and check visibility with a clean API ✨ Comprehensive Documentation Check the [Documentation](/docs/plugins/home-indicator/getting-started/) to master the plugin in just a few minutes. # Getting Started > Learn how to install and configure the Capacitor Home Indicator plugin to control the home indicator on iOS devices. 1. **Install the package** * npm ```sh npm i @capgo/capacitor-home-indicator ``` * pnpm ```sh pnpm add @capgo/capacitor-home-indicator ``` * yarn ```sh yarn add @capgo/capacitor-home-indicator ``` * bun ```sh bun add @capgo/capacitor-home-indicator ``` 2. **Sync with native projects** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` 3. **Configure the plugin** **Hide Home Indicator:** ```typescript import { HomeIndicator } from '@capgo/capacitor-home-indicator'; // Hide the home indicator await HomeIndicator.hide(); ``` **Show Home Indicator:** ```typescript // Show the home indicator await HomeIndicator.show(); // Check visibility status const { hidden } = await HomeIndicator.isHidden(); console.log('Is hidden:', hidden); ``` * iOS No additional setup required. The plugin only works on iOS devices with a home indicator (iPhone X and later). * Android This plugin has no effect on Android devices as they don’t have an iOS-style home indicator. 4. **Advanced usage** ```typescript import { HomeIndicator } from '@capgo/capacitor-home-indicator'; import { App } from '@capacitor/app'; export class ImmersiveMode { private isImmersive = false; async enterImmersiveMode() { this.isImmersive = true; // Hide home indicator await HomeIndicator.hide(); // Hide status bar for full immersion // StatusBar.hide(); // If using @capacitor/status-bar } async exitImmersiveMode() { this.isImmersive = false; // Show home indicator await HomeIndicator.show(); // Show status bar // StatusBar.show(); // If using @capacitor/status-bar } async toggleImmersiveMode() { const { hidden } = await HomeIndicator.isHidden(); if (hidden) { await this.exitImmersiveMode(); } else { await this.enterImmersiveMode(); } } setupAppLifecycle() { // Handle app state changes App.addListener('appStateChange', async ({ isActive }) => { if (!isActive && this.isImmersive) { // App went to background, might want to show indicator await HomeIndicator.show(); } else if (isActive && this.isImmersive) { // App came to foreground, restore immersive mode await HomeIndicator.hide(); } }); } } ``` ## API Reference [Section titled “API Reference”](#api-reference) ### Methods [Section titled “Methods”](#methods) #### `hide()` [Section titled “hide()”](#hide) Hide the home indicator. **Returns:** `Promise` #### `show()` [Section titled “show()”](#show) Show the home indicator. **Returns:** `Promise` #### `isHidden()` [Section titled “isHidden()”](#ishidden) Check if the home indicator is currently hidden. **Returns:** `Promise<{ hidden: boolean }>` ### Interfaces [Section titled “Interfaces”](#interfaces) ```typescript interface HiddenResult { hidden: boolean; } ``` ## Platform Notes [Section titled “Platform Notes”](#platform-notes) ### iOS [Section titled “iOS”](#ios) * Only works on devices with home indicator (iPhone X and later) * Requires iOS 11.0 or later * The indicator reappears when users swipe from the bottom ### Android [Section titled “Android”](#android) * This plugin has no effect on Android * Android uses different navigation gestures/buttons ## Common Use Cases [Section titled “Common Use Cases”](#common-use-cases) 1. **Games**: Full-screen gaming without distractions 2. **Video Players**: Immersive video playback 3. **Photo Viewers**: Full-screen photo galleries 4. **Presentations**: Distraction-free presentations 5. **Kiosk Apps**: Public display applications ## Best Practices [Section titled “Best Practices”](#best-practices) 1. **User Control**: Always provide a way to exit immersive mode ```typescript // Add a gesture or button to toggle immersive mode const toggleButton = document.getElementById('toggle-immersive'); toggleButton.addEventListener('click', () => { immersiveMode.toggleImmersiveMode(); }); ``` 2. **Respect System Gestures**: Don’t interfere with system navigation 3. **Save State**: Remember user’s preference for immersive mode ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) **Home indicator not hiding:** * Verify the device has a home indicator (iPhone X+) * Check iOS version is 11.0 or higher * Ensure the app has focus **Indicator reappears unexpectedly:** * This is normal iOS behavior for system gestures # @capgo/capacitor-ibeacon > Detect and monitor Bluetooth beacons for proximity-based features, indoor navigation, and location-aware experiences. Beacon Monitoring Monitor beacon regions and get notified when entering or exiting 🎯 Beacon Ranging Detect nearby beacons and measure their distance in real-time 📡 Background Detection Monitor beacons even when app is in background 🔔 Beacon Advertising Turn your device into an iBeacon transmitter (iOS only) 📱 Cross-platform Works on both iOS and Android with native beacon libraries 📱 Comprehensive Documentation Check the [Documentation](/docs/plugins/ibeacon/getting-started/) to master the plugin in just a few minutes. # Getting Started > Learn how to install and use the iBeacon plugin to detect and monitor Bluetooth beacons in your Capacitor app. 1. **Install the package** * npm ```sh npm i @capgo/capacitor-ibeacon ``` * pnpm ```sh pnpm add @capgo/capacitor-ibeacon ``` * yarn ```sh yarn add @capgo/capacitor-ibeacon ``` * bun ```sh bun add @capgo/capacitor-ibeacon ``` 2. **Sync with native projects** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Configuration [Section titled “Configuration”](#configuration) ### iOS Configuration [Section titled “iOS Configuration”](#ios-configuration) Add the following to your `Info.plist`: ```xml NSLocationWhenInUseUsageDescription This app needs location access to detect nearby beacons NSLocationAlwaysAndWhenInUseUsageDescription This app needs location access to monitor beacons in the background NSBluetoothAlwaysUsageDescription This app uses Bluetooth to detect nearby beacons UIBackgroundModes location ``` ### Android Configuration [Section titled “Android Configuration”](#android-configuration) Add the following to your `AndroidManifest.xml`: ```xml ``` **Important**: For Android, you need to integrate the [AltBeacon](https://altbeacon.github.io/android-beacon-library/) library into your project for beacon detection to work. Add to your app’s `build.gradle`: ```kotlin dependencies { implementation 'org.altbeacon:android-beacon-library:2.20+' } ``` ## Usage [Section titled “Usage”](#usage) ### Basic Setup [Section titled “Basic Setup”](#basic-setup) ```typescript import { CapacitorIbeacon } from '@capgo/capacitor-ibeacon'; // Define your beacon region const beaconRegion = { identifier: 'MyStore', uuid: 'B9407F30-F5F8-466E-AFF9-25556B57FE6D', major: 1, // Optional minor: 2 // Optional }; ``` ### Request Permissions [Section titled “Request Permissions”](#request-permissions) ```typescript // Request "When In Use" permission const requestPermission = async () => { const { status } = await CapacitorIbeacon.requestWhenInUseAuthorization(); console.log('Permission status:', status); // status: 'not_determined' | 'restricted' | 'denied' | 'authorized_always' | 'authorized_when_in_use' }; // Request "Always" permission (for background monitoring) const requestAlwaysPermission = async () => { const { status } = await CapacitorIbeacon.requestAlwaysAuthorization(); console.log('Always permission status:', status); }; // Check current authorization status const checkPermission = async () => { const { status } = await CapacitorIbeacon.getAuthorizationStatus(); console.log('Current status:', status); }; ``` ### Check Device Capabilities [Section titled “Check Device Capabilities”](#check-device-capabilities) ```typescript // Check if Bluetooth is enabled const checkBluetooth = async () => { const { enabled } = await CapacitorIbeacon.isBluetoothEnabled(); if (!enabled) { console.log('Please enable Bluetooth'); } }; // Check if ranging is available const checkRanging = async () => { const { available } = await CapacitorIbeacon.isRangingAvailable(); console.log('Ranging available:', available); }; ``` ### Monitor Beacon Regions [Section titled “Monitor Beacon Regions”](#monitor-beacon-regions) Monitoring detects when you enter or exit a beacon region. This works in the background. ```typescript // Start monitoring const startMonitoring = async () => { await CapacitorIbeacon.startMonitoringForRegion({ identifier: 'MyStore', uuid: 'B9407F30-F5F8-466E-AFF9-25556B57FE6D', major: 1, minor: 2 }); console.log('Started monitoring for beacons'); }; // Stop monitoring const stopMonitoring = async () => { await CapacitorIbeacon.stopMonitoringForRegion({ identifier: 'MyStore', uuid: 'B9407F30-F5F8-466E-AFF9-25556B57FE6D' }); }; ``` ### Range Beacons [Section titled “Range Beacons”](#range-beacons) Ranging provides continuous updates about nearby beacons and their distances. This requires the app to be in the foreground. ```typescript // Start ranging const startRanging = async () => { await CapacitorIbeacon.startRangingBeaconsInRegion({ identifier: 'MyStore', uuid: 'B9407F30-F5F8-466E-AFF9-25556B57FE6D' }); console.log('Started ranging beacons'); }; // Stop ranging const stopRanging = async () => { await CapacitorIbeacon.stopRangingBeaconsInRegion({ identifier: 'MyStore', uuid: 'B9407F30-F5F8-466E-AFF9-25556B57FE6D' }); }; ``` ### Listen to Events [Section titled “Listen to Events”](#listen-to-events) ```typescript import { PluginListenerHandle } from '@capacitor/core'; // Listen for ranging events (continuous distance updates) const rangingListener: PluginListenerHandle = await CapacitorIbeacon.addListener( 'didRangeBeacons', (data) => { console.log('Beacons detected:', data.beacons); data.beacons.forEach(beacon => { console.log(`UUID: ${beacon.uuid}`); console.log(`Major: ${beacon.major}, Minor: ${beacon.minor}`); console.log(`Distance: ${beacon.accuracy}m`); console.log(`Proximity: ${beacon.proximity}`); // immediate, near, far, unknown console.log(`RSSI: ${beacon.rssi}`); }); } ); // Listen for region enter events const enterListener: PluginListenerHandle = await CapacitorIbeacon.addListener( 'didEnterRegion', (data) => { console.log('Entered region:', data.region.identifier); // Show welcome notification or trigger action } ); // Listen for region exit events const exitListener: PluginListenerHandle = await CapacitorIbeacon.addListener( 'didExitRegion', (data) => { console.log('Exited region:', data.region.identifier); // Trigger goodbye action } ); // Listen for region state changes const stateListener: PluginListenerHandle = await CapacitorIbeacon.addListener( 'didDetermineStateForRegion', (data) => { console.log(`Region ${data.region.identifier}: ${data.state}`); // state: 'inside' | 'outside' | 'unknown' } ); // Clean up listeners when done const cleanup = () => { rangingListener.remove(); enterListener.remove(); exitListener.remove(); stateListener.remove(); }; ``` ### Advertise as iBeacon (iOS only) [Section titled “Advertise as iBeacon (iOS only)”](#advertise-as-ibeacon-ios-only) Turn your device into an iBeacon transmitter. ```typescript // Start advertising const startAdvertising = async () => { await CapacitorIbeacon.startAdvertising({ uuid: 'B9407F30-F5F8-466E-AFF9-25556B57FE6D', major: 1, minor: 2, identifier: 'MyBeacon', measuredPower: -59 // Optional: calibrated power at 1 meter }); console.log('Started advertising as iBeacon'); }; // Stop advertising const stopAdvertising = async () => { await CapacitorIbeacon.stopAdvertising(); }; ``` ## Complete Example [Section titled “Complete Example”](#complete-example) ```typescript import { CapacitorIbeacon } from '@capgo/capacitor-ibeacon'; import { PluginListenerHandle } from '@capacitor/core'; export class BeaconService { private listeners: PluginListenerHandle[] = []; async init() { // Request permissions const { status } = await CapacitorIbeacon.requestWhenInUseAuthorization(); if (status !== 'authorized_when_in_use' && status !== 'authorized_always') { throw new Error('Location permission denied'); } // Check Bluetooth const { enabled } = await CapacitorIbeacon.isBluetoothEnabled(); if (!enabled) { throw new Error('Bluetooth is not enabled'); } // Set up event listeners this.setupListeners(); } private setupListeners() { this.listeners.push( await CapacitorIbeacon.addListener('didEnterRegion', (data) => { console.log('Welcome! Entered:', data.region.identifier); this.onEnterRegion(data.region); }) ); this.listeners.push( await CapacitorIbeacon.addListener('didExitRegion', (data) => { console.log('Goodbye! Left:', data.region.identifier); this.onExitRegion(data.region); }) ); this.listeners.push( await CapacitorIbeacon.addListener('didRangeBeacons', (data) => { this.onRangeBeacons(data.beacons); }) ); } async startMonitoring(uuid: string, identifier: string, major?: number, minor?: number) { await CapacitorIbeacon.startMonitoringForRegion({ identifier, uuid, major, minor }); } async startRanging(uuid: string, identifier: string) { await CapacitorIbeacon.startRangingBeaconsInRegion({ identifier, uuid }); } private onEnterRegion(region: any) { // Handle region entry (e.g., show notification, trigger content) console.log('Entered beacon region:', region); } private onExitRegion(region: any) { // Handle region exit console.log('Left beacon region:', region); } private onRangeBeacons(beacons: any[]) { // Process beacon distances const nearestBeacon = beacons.reduce((nearest, beacon) => { return beacon.accuracy < nearest.accuracy ? beacon : nearest; }, beacons[0]); if (nearestBeacon) { console.log('Nearest beacon:', nearestBeacon); this.handleProximity(nearestBeacon); } } private handleProximity(beacon: any) { switch (beacon.proximity) { case 'immediate': // < 0.5m console.log('Very close to beacon'); break; case 'near': // 0.5m - 3m console.log('Near beacon'); break; case 'far': // > 3m console.log('Far from beacon'); break; case 'unknown': console.log('Distance unknown'); break; } } cleanup() { this.listeners.forEach(listener => listener.remove()); this.listeners = []; } } ``` ## API Reference [Section titled “API Reference”](#api-reference) ### startMonitoringForRegion(options) [Section titled “startMonitoringForRegion(options)”](#startmonitoringforregionoptions) Start monitoring for a beacon region. Triggers events when entering/exiting. ```typescript interface BeaconRegion { identifier: string; uuid: string; major?: number; minor?: number; notifyEntryStateOnDisplay?: boolean; } await CapacitorIbeacon.startMonitoringForRegion(options); ``` ### stopMonitoringForRegion(options) [Section titled “stopMonitoringForRegion(options)”](#stopmonitoringforregionoptions) Stop monitoring for a beacon region. ```typescript await CapacitorIbeacon.stopMonitoringForRegion(options); ``` ### startRangingBeaconsInRegion(options) [Section titled “startRangingBeaconsInRegion(options)”](#startrangingbeaconsinregionoptions) Start ranging beacons in a region for continuous distance updates. ```typescript await CapacitorIbeacon.startRangingBeaconsInRegion(options); ``` ### stopRangingBeaconsInRegion(options) [Section titled “stopRangingBeaconsInRegion(options)”](#stoprangingbeaconsinregionoptions) Stop ranging beacons in a region. ```typescript await CapacitorIbeacon.stopRangingBeaconsInRegion(options); ``` ### startAdvertising(options) [Section titled “startAdvertising(options)”](#startadvertisingoptions) Start advertising the device as an iBeacon (iOS only). ```typescript interface BeaconAdvertisingOptions { uuid: string; major: number; minor: number; identifier: string; measuredPower?: number; // Calibrated power at 1 meter } await CapacitorIbeacon.startAdvertising(options); ``` ### stopAdvertising() [Section titled “stopAdvertising()”](#stopadvertising) Stop advertising the device as an iBeacon. ```typescript await CapacitorIbeacon.stopAdvertising(); ``` ### requestWhenInUseAuthorization() [Section titled “requestWhenInUseAuthorization()”](#requestwheninuseauthorization) Request “When In Use” location authorization. ```typescript const result = await CapacitorIbeacon.requestWhenInUseAuthorization(); // Returns: { status: string } ``` ### requestAlwaysAuthorization() [Section titled “requestAlwaysAuthorization()”](#requestalwaysauthorization) Request “Always” location authorization (required for background monitoring). ```typescript const result = await CapacitorIbeacon.requestAlwaysAuthorization(); // Returns: { status: string } ``` ### getAuthorizationStatus() [Section titled “getAuthorizationStatus()”](#getauthorizationstatus) Get current location authorization status. ```typescript const result = await CapacitorIbeacon.getAuthorizationStatus(); // Returns: { status: 'not_determined' | 'restricted' | 'denied' | 'authorized_always' | 'authorized_when_in_use' } ``` ### isBluetoothEnabled() [Section titled “isBluetoothEnabled()”](#isbluetoothenabled) Check if Bluetooth is enabled. ```typescript const result = await CapacitorIbeacon.isBluetoothEnabled(); // Returns: { enabled: boolean } ``` ### isRangingAvailable() [Section titled “isRangingAvailable()”](#israngingavailable) Check if ranging is available on the device. ```typescript const result = await CapacitorIbeacon.isRangingAvailable(); // Returns: { available: boolean } ``` ### enableARMAFilter(options) [Section titled “enableARMAFilter(options)”](#enablearmafilteroptions) Enable ARMA filtering for distance calculations (Android only). ```typescript await CapacitorIbeacon.enableARMAFilter({ enabled: true }); ``` ## Events [Section titled “Events”](#events) ### didRangeBeacons [Section titled “didRangeBeacons”](#didrangebeacons) Fired when beacons are detected during ranging. ```typescript interface RangingEvent { region: BeaconRegion; beacons: Beacon[]; } interface Beacon { uuid: string; major: number; minor: number; rssi: number; // Signal strength proximity: 'immediate' | 'near' | 'far' | 'unknown'; accuracy: number; // Distance in meters } ``` ### didEnterRegion [Section titled “didEnterRegion”](#didenterregion) Fired when entering a monitored beacon region. ```typescript interface RegionEvent { region: BeaconRegion; } ``` ### didExitRegion [Section titled “didExitRegion”](#didexitregion) Fired when exiting a monitored beacon region. ```typescript interface RegionEvent { region: BeaconRegion; } ``` ### didDetermineStateForRegion [Section titled “didDetermineStateForRegion”](#diddeterminestateforregion) Fired when region state is determined. ```typescript interface StateEvent { region: BeaconRegion; state: 'inside' | 'outside' | 'unknown'; } ``` ## Proximity Values [Section titled “Proximity Values”](#proximity-values) * **immediate**: Very close (< 0.5 meters) * **near**: Relatively close (0.5 - 3 meters) * **far**: Further away (> 3 meters) * **unknown**: Distance cannot be determined ## Best Practices [Section titled “Best Practices”](#best-practices) 1. **Request appropriate permissions** * Use “When In Use” for foreground features * Request “Always” only if you need background monitoring * Explain clearly why you need location access 2. **Handle Bluetooth state** ```typescript const { enabled } = await CapacitorIbeacon.isBluetoothEnabled(); if (!enabled) { // Prompt user to enable Bluetooth } ``` 3. **Battery optimization** * Use monitoring instead of ranging when possible (more battery-efficient) * Stop ranging when not actively needed * Consider using larger major/minor ranges to reduce processing 4. **Error handling** ```typescript try { await CapacitorIbeacon.startMonitoringForRegion(region); } catch (error) { console.error('Failed to start monitoring:', error); } ``` 5. **Clean up listeners** Always remove event listeners when component unmounts to prevent memory leaks. ## Platform Notes [Section titled “Platform Notes”](#platform-notes) ### iOS [Section titled “iOS”](#ios) * Requires iOS 10.0+ * Uses native CoreLocation framework * Supports background monitoring with “Always” permission * Can advertise as iBeacon using CoreBluetooth * Ranging requires app to be in foreground ### Android [Section titled “Android”](#android) * Requires Android 6.0 (API 23)+ * Uses AltBeacon library * Requires location permissions even though beacons use Bluetooth * Background monitoring requires ACCESS\_BACKGROUND\_LOCATION (Android 10+) * Cannot advertise as iBeacon (hardware limitation on most devices) ### Web [Section titled “Web”](#web) * Not supported on web platform ## Common Use Cases [Section titled “Common Use Cases”](#common-use-cases) 1. **Proximity Marketing**: Trigger notifications or content when users approach your store 2. **Indoor Navigation**: Guide users through buildings using beacon waypoints 3. **Attendance Tracking**: Automatically check-in when users enter a location 4. **Asset Tracking**: Monitor equipment or inventory movement 5. **Museum Tours**: Provide contextual information as visitors approach exhibits 6. **Smart Home**: Trigger automations based on room presence ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) ### Beacons not detected [Section titled “Beacons not detected”](#beacons-not-detected) * Ensure Bluetooth is enabled * Verify location permissions are granted * Check beacon UUID matches exactly (case-sensitive) * Confirm beacon is powered and transmitting * Try without major/minor filters first ### Background monitoring not working [Section titled “Background monitoring not working”](#background-monitoring-not-working) * Ensure “Always” location permission is granted * Add `location` to UIBackgroundModes (iOS) * Request ACCESS\_BACKGROUND\_LOCATION (Android 10+) * Note: iOS may delay background callbacks to save battery ### Distance measurements inaccurate [Section titled “Distance measurements inaccurate”](#distance-measurements-inaccurate) * Beacon RSSI varies with environment (walls, interference) * Use multiple beacons for triangulation * Calibrate measuredPower at 1 meter from beacon * Enable ARMA filtering on Android for smoother values # @capgo/capacitor-in-app-review > Request native app store reviews on iOS and Android without redirecting users away from your app. Native Experience Uses iOS SKStoreReviewController and Android Play In-App Review API for authentic native review dialogs No Redirects Users stay in your app while leaving reviews - no App Store or Play Store redirects Platform Compliant Automatically follows Apple and Google guidelines for review prompts Simple API Just one method call to request a review - check the [Documentation](/docs/plugins/in-app-review/getting-started/) to get started. # Getting Started > Learn how to install and use the In-App Review plugin to request app store reviews in your Capacitor app. 1. **Install the package** * npm ```sh npm i @capgo/capacitor-in-app-review ``` * pnpm ```sh pnpm add @capgo/capacitor-in-app-review ``` * yarn ```sh yarn add @capgo/capacitor-in-app-review ``` * bun ```sh bun add @capgo/capacitor-in-app-review ``` 2. **Sync with native projects** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Usage [Section titled “Usage”](#usage) Import the plugin and request a review at appropriate moments in your app: ```typescript import { CapgoInAppReview } from '@capgo/capacitor-in-app-review'; // Request a review at a natural point in your app's user flow const requestReview = async () => { try { await CapgoInAppReview.requestReview(); console.log('Review requested successfully'); } catch (error) { console.error('Failed to request review:', error); } }; ``` ## API Reference [Section titled “API Reference”](#api-reference) ### requestReview() [Section titled “requestReview()”](#requestreview) Triggers the native in-app review dialog. ```typescript await CapgoInAppReview.requestReview(); ``` **Important Notes:** * The review dialog may not be displayed every time this method is called * Both Apple and Google have guidelines that limit how often the prompt can appear * There is no guarantee that the user will see the review prompt * The method resolves successfully even if the dialog was not shown * Do not call this in response to a user action like a button tap ### getPluginVersion() [Section titled “getPluginVersion()”](#getpluginversion) Returns the native plugin version. ```typescript const { version } = await CapgoInAppReview.getPluginVersion(); console.log('Plugin version:', version); ``` ## Best Practices [Section titled “Best Practices”](#best-practices) ### When to Request Reviews [Section titled “When to Request Reviews”](#when-to-request-reviews) Request reviews at moments when users are most likely to have a positive experience: ```typescript // Good examples: // - After completing a level in a game // - After successfully completing a task // - After the user has used the app multiple times // - After a positive interaction // Example: Track usage and request review after positive milestone let taskCompletedCount = 0; const onTaskComplete = async () => { taskCompletedCount++; // Request review after user completes 5 tasks if (taskCompletedCount === 5) { await CapgoInAppReview.requestReview(); } }; ``` ### When NOT to Request Reviews [Section titled “When NOT to Request Reviews”](#when-not-to-request-reviews) ```typescript // Bad examples - DO NOT do these: // - On app launch // - In response to a "Rate Us" button tap // - After an error or negative experience // - Too frequently (respect platform quotas) ``` ### Track Review Requests [Section titled “Track Review Requests”](#track-review-requests) Since the platform controls whether the dialog is shown, track your requests: ```typescript import { CapgoInAppReview } from '@capgo/capacitor-in-app-review'; import { Preferences } from '@capacitor/preferences'; const requestReviewIfAppropriate = async () => { const lastRequest = await Preferences.get({ key: 'lastReviewRequest' }); const daysSinceLastRequest = lastRequest.value ? (Date.now() - parseInt(lastRequest.value)) / (1000 * 60 * 60 * 24) : Infinity; // Only request if it's been at least 30 days if (daysSinceLastRequest >= 30) { await CapgoInAppReview.requestReview(); await Preferences.set({ key: 'lastReviewRequest', value: Date.now().toString() }); } }; ``` ## Platform Notes [Section titled “Platform Notes”](#platform-notes) ### iOS [Section titled “iOS”](#ios) * Uses `SKStoreReviewController` * The system automatically limits the display to **3 times within a 365-day period** * The dialog may not appear based on App Store policy * Testing: In development, the dialog will always appear but won’t submit reviews ### Android [Section titled “Android”](#android) * Uses the **Play In-App Review API** * Google Play enforces a quota on how often a user can be shown the dialog * The dialog will **not be shown in debug builds** * For testing, you need to: * Have the app installed from the Play Store (internal testing track works) * Have a Google account on the device ## Complete Example [Section titled “Complete Example”](#complete-example) ```typescript import { CapgoInAppReview } from '@capgo/capacitor-in-app-review'; import { Preferences } from '@capacitor/preferences'; export class ReviewService { private static REVIEW_REQUEST_KEY = 'lastReviewRequest'; private static MIN_DAYS_BETWEEN_REQUESTS = 30; private static MIN_APP_LAUNCHES = 5; async checkAndRequestReview(): Promise { const shouldRequest = await this.shouldRequestReview(); if (shouldRequest) { try { await CapgoInAppReview.requestReview(); await this.recordReviewRequest(); return true; } catch (error) { console.error('Review request failed:', error); return false; } } return false; } private async shouldRequestReview(): Promise { // Check app launch count const launchCount = await this.getAppLaunchCount(); if (launchCount < ReviewService.MIN_APP_LAUNCHES) { return false; } // Check days since last request const lastRequest = await Preferences.get({ key: ReviewService.REVIEW_REQUEST_KEY }); if (lastRequest.value) { const daysSince = (Date.now() - parseInt(lastRequest.value)) / (1000 * 60 * 60 * 24); if (daysSince < ReviewService.MIN_DAYS_BETWEEN_REQUESTS) { return false; } } return true; } private async getAppLaunchCount(): Promise { const count = await Preferences.get({ key: 'appLaunchCount' }); return count.value ? parseInt(count.value) : 0; } private async recordReviewRequest(): Promise { await Preferences.set({ key: ReviewService.REVIEW_REQUEST_KEY, value: Date.now().toString() }); } async incrementLaunchCount(): Promise { const current = await this.getAppLaunchCount(); await Preferences.set({ key: 'appLaunchCount', value: (current + 1).toString() }); } } ``` # @capgo/inappbrowser > Open web content in your app with a fully-featured in-app browser that supports URL change events and navigation controls. URL change events Track navigation with URL change event listeners 🔗 Full navigation control Back, forward, reload, and close controls 🎮 Customizable UI Toolbar color, buttons, and display options 🎨 Comprehensive Documentation Check the [Documentation](/docs/plugins/inappbrowser/getting-started/) to master the plugin in just a few minutes. # Getting Started > Learn how to install and use the InAppBrowser plugin with URL change events for embedded web content in your Capacitor app. 1. **Install the package** * npm ```sh npm i @capgo/inappbrowser ``` * pnpm ```sh pnpm add @capgo/inappbrowser ``` * yarn ```sh yarn add @capgo/inappbrowser ``` * bun ```sh bun add @capgo/inappbrowser ``` 2. **Sync with native projects** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Usage [Section titled “Usage”](#usage) Import the plugin and use its two main entry points: ```typescript import { InAppBrowser, ToolBarType, BackgroundColor } from '@capgo/inappbrowser'; // 1) Simple custom tab / SFSafariViewController const openExternal = async () => { await InAppBrowser.open({ url: 'https://capgo.app', isPresentAfterPageLoad: true, preventDeeplink: false, }); }; // 2) Full WebView with navigation, headers, share, messaging, etc. const openWebView = async () => { await InAppBrowser.openWebView({ url: 'https://capgo.app', title: 'Capgo', toolbarType: ToolBarType.NAVIGATION, backgroundColor: BackgroundColor.BLACK, activeNativeNavigationForWebview: true, showReloadButton: true, shareSubject: 'Check this page', shareDisclaimer: { title: 'Disclaimer', message: 'You are about to share content', confirmBtn: 'Continue', cancelBtn: 'Cancel', }, }); }; // Messaging between app and the opened WebView const setupListeners = async () => { await InAppBrowser.addListener('urlChangeEvent', (event) => { console.log('URL changed to:', event.url); }); await InAppBrowser.addListener('messageFromWebview', (event) => { console.log('Message from web:', event.detail); }); await InAppBrowser.addListener('closeEvent', () => { console.log('WebView closed'); }); }; // Send data to the WebView const sendData = async () => { await InAppBrowser.postMessage({ detail: { action: 'refresh-profile' } }); }; // Close and reload helpers const closeBrowser = () => InAppBrowser.close(); const reloadPage = () => InAppBrowser.reload(); ``` ## API Reference [Section titled “API Reference”](#api-reference) ### open(options: OpenOptions) [Section titled “open(options: OpenOptions)”](#openoptions-openoptions) Opens a URL in a custom tab / SFSafariViewController. ```typescript interface OpenOptions { /** Target URL to load */ url: string; /** Present after the page finishes loading */ isPresentAfterPageLoad?: boolean; /** Prevent deep links from opening external apps */ preventDeeplink?: boolean; } await InAppBrowser.open({ url: 'https://example.com', preventDeeplink: true }); ``` ### openWebView(options: OpenWebViewOptions) [Section titled “openWebView(options: OpenWebViewOptions)”](#openwebviewoptions-openwebviewoptions) Loads a full-featured WebView with navigation UI, headers, credentials, scripting and messaging. ```typescript interface OpenWebViewOptions { url: string; headers?: Record; credentials?: { username: string; password: string }; materialPicker?: boolean; shareDisclaimer?: { title: string; message: string; confirmBtn: string; cancelBtn: string; }; toolbarType?: ToolBarType; shareSubject?: string; title?: string; backgroundColor?: BackgroundColor; activeNativeNavigationForWebview?: boolean; disableGoBackOnNativeApplication?: boolean; isPresentAfterPageLoad?: boolean; isInspectable?: boolean; isAnimated?: boolean; showReloadButton?: boolean; closeModal?: boolean; closeModalTitle?: string; closeModalDescription?: string; closeModalOk?: string; closeModalCancel?: string; visibleTitle?: boolean; toolbarColor?: string; toolbarTextColor?: string; showArrow?: boolean; ignoreUntrustedSSLError?: boolean; preShowScript?: string; preShowScriptInjectionTime?: 'documentStart' | 'pageLoad'; proxyRequests?: string; buttonNearDone?: { ios: { iconType: 'sf-symbol' | 'asset'; icon: string }; android: { iconType: 'asset' | 'vector'; icon: string; width?: number; height?: number }; }; textZoom?: number; preventDeeplink?: boolean; authorizedAppLinks?: string[]; enabledSafeBottomMargin?: boolean; useTopInset?: boolean; enableGooglePaySupport?: boolean; blockedHosts?: string[]; width?: number; height?: number; x?: number; y?: number; } await InAppBrowser.openWebView({ url: 'https://new-page.com', toolbarType: ToolBarType.NAVIGATION, showReloadButton: true, }); ``` `ToolBarType` values: `activity` (close + share), `compact` (close only), `navigation` (back/forward + close), `blank` (no toolbar). `BackgroundColor` values: `white` or `black`. ### close(options?) [Section titled “close(options?)”](#closeoptions) Closes the WebView/custom tab. **Options:** * `isAnimated?: boolean` - Whether to animate the close action ### reload() [Section titled “reload()”](#reload) Reloads the current WebView page. ### goBack() [Section titled “goBack()”](#goback) Goes back in WebView history and returns `{ canGoBack: boolean }`. ### `setUrl({ url: string })` [Section titled “setUrl({ url: string })”](#seturl-url-string) Replaces the current WebView URL. ### `executeScript({ code: string })` [Section titled “executeScript({ code: string })”](#executescript-code-string) Injects JavaScript into the WebView. ### `postMessage({ detail: Record })` [Section titled “postMessage({ detail: Record\ })”](#postmessage-detail-recordstring-any) Sends data from the native app to the WebView (receive in JS via `window.addEventListener('messageFromNative', ...)`). ### `getCookies({ url, includeHttpOnly? })` [Section titled “getCookies({ url, includeHttpOnly? })”](#getcookies-url-includehttponly) Returns cookies for the URL. ### `clearCookies({ url })` / clearAllCookies() / clearCache() [Section titled “clearCookies({ url }) / clearAllCookies() / clearCache()”](#clearcookies-url---clearallcookies--clearcache) Cookie and cache management helpers. ### updateDimensions(options: DimensionOptions) [Section titled “updateDimensions(options: DimensionOptions)”](#updatedimensionsoptions-dimensionoptions) Change WebView size/position at runtime (`width`, `height`, `x`, `y`). ### removeAllListeners() [Section titled “removeAllListeners()”](#removealllisteners) Unregister all listeners for the plugin. ## Events [Section titled “Events”](#events) ### urlChangeEvent [Section titled “urlChangeEvent”](#urlchangeevent) Fired when the URL changes in the browser. ```typescript interface UrlChangeEvent { url: string; } InAppBrowser.addListener('urlChangeEvent', (event) => { console.log('New URL:', event.url); }); ``` ### messageFromWebview [Section titled “messageFromWebview”](#messagefromwebview) Triggered when `window.mobileApp.postMessage(...)` is called inside the WebView. ```typescript InAppBrowser.addListener('messageFromWebview', (event) => { console.log('Payload from web:', event.detail); }); ``` ### closeEvent [Section titled “closeEvent”](#closeevent) Fired when the browser is closed. ```typescript InAppBrowser.addListener('closeEvent', () => { console.log('Browser closed'); }); ``` ### buttonNearDoneClick [Section titled “buttonNearDoneClick”](#buttonneardoneclick) Fired when the custom button added with `buttonNearDone` is pressed. ```typescript InAppBrowser.addListener('buttonNearDoneClick', (event) => { console.log('Button near done tapped', event); }); ``` ### confirmBtnClicked [Section titled “confirmBtnClicked”](#confirmbtnclicked) Triggered when a confirm dialog (disclaimer or close modal) is accepted. ```typescript InAppBrowser.addListener('confirmBtnClicked', (event) => { console.log('Confirm accepted, current URL:', event.url); }); ``` ### browserPageLoaded / pageLoadError [Section titled “browserPageLoaded / pageLoadError”](#browserpageloaded--pageloaderror) Lifecycle events for WebView load success or failure. ```typescript InAppBrowser.addListener('browserPageLoaded', () => console.log('Page loaded')); InAppBrowser.addListener('pageLoadError', () => console.log('Page failed to load')); ``` ## Advanced Usage [Section titled “Advanced Usage”](#advanced-usage) ### OAuth Flow Implementation [Section titled “OAuth Flow Implementation”](#oauth-flow-implementation) ```typescript import { InAppBrowser } from '@capgo/inappbrowser'; export class OAuthService { private listeners: any[] = []; async authenticate(authUrl: string, redirectUri: string) { return new Promise((resolve, reject) => { // Listen for URL changes const urlListener = InAppBrowser.addListener('urlChangeEvent', (event) => { if (event.url.startsWith(redirectUri)) { // Extract OAuth code/token from URL const url = new URL(event.url); const code = url.searchParams.get('code'); if (code) { InAppBrowser.close(); resolve(code); } else { const error = url.searchParams.get('error'); reject(new Error(error || 'OAuth failed')); } } }); this.listeners.push(urlListener); // Open OAuth provider InAppBrowser.open({ url: authUrl, preventDeeplink: true, }); }); } cleanup() { this.listeners.forEach(listener => listener.remove()); this.listeners = []; } } ``` ### Custom Browser UI [Section titled “Custom Browser UI”](#custom-browser-ui) ```typescript const openCustomBrowser = async () => { await InAppBrowser.open({ url: 'https://example.com', isPresentAfterPageLoad: true, preventDeeplink: false, }); }; ``` ### Handling External Links [Section titled “Handling External Links”](#handling-external-links) ```typescript import { InAppBrowser } from '@capgo/inappbrowser'; export class LinkHandler { async openExternalLink(url: string) { // Check if URL should open in browser if (this.shouldOpenInBrowser(url)) { await InAppBrowser.open({ url, preventDeeplink: true, }); } else { // Handle internally window.location.href = url; } } private shouldOpenInBrowser(url: string): boolean { // External domains const externalDomains = ['youtube.com', 'twitter.com', 'facebook.com']; const urlDomain = new URL(url).hostname; return externalDomains.some(domain => urlDomain.includes(domain)); } } ``` ## Best Practices [Section titled “Best Practices”](#best-practices) 1. **Always remove listeners** ```typescript const listener = await InAppBrowser.addListener('urlChangeEvent', handler); // When done listener.remove(); ``` 2. **Handle browser states** ```typescript let browserOpen = false; const launch = async () => { browserOpen = true; await InAppBrowser.openWebView({ url: 'https://example.com' }); }; InAppBrowser.addListener('closeEvent', () => { browserOpen = false; }); ``` 3. **Validate URLs before opening** ```typescript const isValidUrl = (url: string): boolean => { try { new URL(url); return true; } catch { return false; } }; if (isValidUrl(url)) { await InAppBrowser.open({ url }); } ``` 4. **Customize for platform** ```typescript import { Capacitor } from '@capacitor/core'; const options = { url: 'https://example.com', preventDeeplink: Capacitor.getPlatform() === 'ios', }; ``` ## Platform Notes [Section titled “Platform Notes”](#platform-notes) ### iOS [Section titled “iOS”](#ios) * Uses `SFSafariViewController` * Supports iOS 11.0+ * Respects Safe Area insets * Supports custom presentation styles ### Android [Section titled “Android”](#android) * Uses Chrome Custom Tabs * Falls back to WebView if Chrome not available * Supports Android 5.0 (API 21)+ * Toolbar customization supported via `toolbarType`, `toolbarColor`, `buttonNearDone`, etc. ### Web [Section titled “Web”](#web) * Opens in new browser tab/window * Limited customization options * No URL change events # @capgo/capacitor-intent-launcher > Start Android activities, open system settings, and interact with other apps using the Android Intent system. Requires Capacitor 8+. System Settings Open any Android system settings screen directly from your app Launch Apps Open other installed applications by package name Intent Actions Execute over 65+ predefined Android intent actions App Icons Retrieve application icons as base64 images Comprehensive Documentation Check the [Documentation](/docs/plugins/intent-launcher/getting-started/) to master the plugin in just a few minutes. # Getting Started > Learn how to install and use the Intent Launcher plugin to launch Android intents and system settings in your Capacitor app. 1. **Install the package** * npm ```sh npm i @capgo/capacitor-intent-launcher ``` * pnpm ```sh pnpm add @capgo/capacitor-intent-launcher ``` * yarn ```sh yarn add @capgo/capacitor-intent-launcher ``` * bun ```sh bun add @capgo/capacitor-intent-launcher ``` 2. **Sync with native projects** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Usage [Section titled “Usage”](#usage) Import the plugin and use its methods to launch intents: ```typescript import { IntentLauncher, ActivityAction } from '@capgo/capacitor-intent-launcher'; // Open WiFi settings const openWifiSettings = async () => { await IntentLauncher.startActivityAsync({ action: ActivityAction.WIFI_SETTINGS }); }; // Open location settings const openLocationSettings = async () => { await IntentLauncher.startActivityAsync({ action: ActivityAction.LOCATION_SOURCE_SETTINGS }); }; // Open app details page const openAppSettings = async () => { await IntentLauncher.startActivityAsync({ action: ActivityAction.APPLICATION_DETAILS_SETTINGS, data: 'package:com.example.myapp' }); }; ``` ## API Reference [Section titled “API Reference”](#api-reference) ### startActivityAsync(options) [Section titled “startActivityAsync(options)”](#startactivityasyncoptions) Start an Android activity for the given action. ```typescript interface IntentLauncherParams { action: string; // Activity action (use ActivityAction enum) category?: string; // Intent category className?: string; // Component class name data?: string; // URI data extra?: Record; // Extra key-value data flags?: number; // Intent flags bitmask packageName?: string; // Component package name type?: string; // MIME type } interface IntentLauncherResult { resultCode: ResultCode; // Result from activity data?: string; // Optional URI data returned extra?: Record; // Optional extra data returned } const result = await IntentLauncher.startActivityAsync({ action: ActivityAction.WIFI_SETTINGS }); ``` ### openApplication(options) [Section titled “openApplication(options)”](#openapplicationoptions) Open an application by its package name. ```typescript interface OpenApplicationOptions { packageName: string; } // Open Gmail await IntentLauncher.openApplication({ packageName: 'com.google.android.gm' }); ``` ### getApplicationIconAsync(options) [Section titled “getApplicationIconAsync(options)”](#getapplicationiconasyncoptions) Get an application’s icon as base64-encoded PNG. ```typescript interface GetApplicationIconOptions { packageName: string; } interface GetApplicationIconResult { icon: string; // base64 PNG with data:image/.png;base64, prefix } const { icon } = await IntentLauncher.getApplicationIconAsync({ packageName: 'com.google.android.gm' }); // Use in img tag // App icon ``` ### getPluginVersion() [Section titled “getPluginVersion()”](#getpluginversion) Get the current plugin version. ```typescript const { version } = await IntentLauncher.getPluginVersion(); ``` ## Common Activity Actions [Section titled “Common Activity Actions”](#common-activity-actions) ### Settings Actions [Section titled “Settings Actions”](#settings-actions) ```typescript import { IntentLauncher, ActivityAction } from '@capgo/capacitor-intent-launcher'; // Network settings await IntentLauncher.startActivityAsync({ action: ActivityAction.WIFI_SETTINGS }); await IntentLauncher.startActivityAsync({ action: ActivityAction.BLUETOOTH_SETTINGS }); await IntentLauncher.startActivityAsync({ action: ActivityAction.AIRPLANE_MODE_SETTINGS }); await IntentLauncher.startActivityAsync({ action: ActivityAction.DATA_ROAMING_SETTINGS }); // Security settings await IntentLauncher.startActivityAsync({ action: ActivityAction.SECURITY_SETTINGS }); await IntentLauncher.startActivityAsync({ action: ActivityAction.PRIVACY_SETTINGS }); // App settings await IntentLauncher.startActivityAsync({ action: ActivityAction.APPLICATION_SETTINGS }); await IntentLauncher.startActivityAsync({ action: ActivityAction.MANAGE_APPLICATIONS_SETTINGS }); // Display & Sound await IntentLauncher.startActivityAsync({ action: ActivityAction.DISPLAY_SETTINGS }); await IntentLauncher.startActivityAsync({ action: ActivityAction.SOUND_SETTINGS }); // Location await IntentLauncher.startActivityAsync({ action: ActivityAction.LOCATION_SOURCE_SETTINGS }); // Battery await IntentLauncher.startActivityAsync({ action: ActivityAction.BATTERY_SAVER_SETTINGS }); await IntentLauncher.startActivityAsync({ action: ActivityAction.IGNORE_BATTERY_OPTIMIZATION_SETTINGS }); // Notifications await IntentLauncher.startActivityAsync({ action: ActivityAction.APP_NOTIFICATION_SETTINGS }); await IntentLauncher.startActivityAsync({ action: ActivityAction.NOTIFICATION_LISTENER_SETTINGS }); ``` ### Intent Actions [Section titled “Intent Actions”](#intent-actions) ```typescript // View a URL await IntentLauncher.startActivityAsync({ action: ActivityAction.VIEW, data: 'https://example.com' }); // Make a phone call await IntentLauncher.startActivityAsync({ action: ActivityAction.DIAL, data: 'tel:+1234567890' }); // Send email await IntentLauncher.startActivityAsync({ action: ActivityAction.SENDTO, data: 'mailto:example@email.com' }); // Share text await IntentLauncher.startActivityAsync({ action: ActivityAction.SEND, type: 'text/plain', extra: { 'android.intent.extra.TEXT': 'Hello from my app!' } }); ``` ## Complete Example [Section titled “Complete Example”](#complete-example) ```typescript import { IntentLauncher, ActivityAction, ResultCode } from '@capgo/capacitor-intent-launcher'; import { Capacitor } from '@capacitor/core'; export class IntentService { private isAndroid = Capacitor.getPlatform() === 'android'; /** * Open app settings page for the current app */ async openAppSettings(packageName: string) { if (!this.isAndroid) { console.warn('Intent Launcher is Android-only'); return; } await IntentLauncher.startActivityAsync({ action: ActivityAction.APPLICATION_DETAILS_SETTINGS, data: `package:${packageName}` }); } /** * Open WiFi settings */ async openWifiSettings() { if (!this.isAndroid) return; await IntentLauncher.startActivityAsync({ action: ActivityAction.WIFI_SETTINGS }); } /** * Open location settings */ async openLocationSettings() { if (!this.isAndroid) return; const result = await IntentLauncher.startActivityAsync({ action: ActivityAction.LOCATION_SOURCE_SETTINGS }); return result.resultCode === ResultCode.Success; } /** * Request battery optimization exemption */ async requestBatteryExemption(packageName: string) { if (!this.isAndroid) return; await IntentLauncher.startActivityAsync({ action: ActivityAction.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS, data: `package:${packageName}` }); } /** * Open another app */ async openApp(packageName: string) { if (!this.isAndroid) return; try { await IntentLauncher.openApplication({ packageName }); } catch (error) { console.error(`App ${packageName} not installed or cannot be opened`); throw error; } } /** * Get app icon for display */ async getAppIcon(packageName: string): Promise { if (!this.isAndroid) return null; try { const { icon } = await IntentLauncher.getApplicationIconAsync({ packageName }); return icon || null; } catch { return null; } } /** * Share text content */ async shareText(text: string, title?: string) { if (!this.isAndroid) return; await IntentLauncher.startActivityAsync({ action: ActivityAction.SEND, type: 'text/plain', extra: { 'android.intent.extra.TEXT': text, ...(title && { 'android.intent.extra.SUBJECT': title }) } }); } /** * Open URL in browser */ async openUrl(url: string) { if (!this.isAndroid) return; await IntentLauncher.startActivityAsync({ action: ActivityAction.VIEW, data: url }); } /** * Open dialer with number */ async openDialer(phoneNumber: string) { if (!this.isAndroid) return; await IntentLauncher.startActivityAsync({ action: ActivityAction.DIAL, data: `tel:${phoneNumber}` }); } /** * Send email */ async composeEmail(to: string, subject?: string, body?: string) { if (!this.isAndroid) return; const extra: Record = {}; if (subject) extra['android.intent.extra.SUBJECT'] = subject; if (body) extra['android.intent.extra.TEXT'] = body; await IntentLauncher.startActivityAsync({ action: ActivityAction.SENDTO, data: `mailto:${to}`, extra }); } } // Usage const intentService = new IntentService(); // Open settings await intentService.openWifiSettings(); await intentService.openLocationSettings(); // Open another app await intentService.openApp('com.google.android.gm'); // Get app icon const icon = await intentService.getAppIcon('com.google.android.gm'); // Share content await intentService.shareText('Check out this app!', 'App Recommendation'); ``` ## Result Codes [Section titled “Result Codes”](#result-codes) ```typescript enum ResultCode { Success = -1, // Activity completed successfully Canceled = 0, // Activity was canceled FirstUser = 1 // Custom result code } ``` ## Best Practices [Section titled “Best Practices”](#best-practices) 1. **Check platform first** ```typescript import { Capacitor } from '@capacitor/core'; if (Capacitor.getPlatform() !== 'android') { console.warn('Intent Launcher is Android-only'); return; } ``` 2. **Handle errors for missing apps** ```typescript try { await IntentLauncher.openApplication({ packageName: 'com.example.app' }); } catch (error) { // App not installed console.error('App not found'); } ``` 3. **Use proper URI format for app settings** ```typescript // Correct data: 'package:com.example.app' // Not correct data: 'com.example.app' ``` ## Platform Notes [Section titled “Platform Notes”](#platform-notes) ### Android [Section titled “Android”](#android) * Full support for all intent actions * Access to system settings and other apps * Can receive result codes from activities ### iOS [Section titled “iOS”](#ios) * **Not supported** - iOS does not have an equivalent to Android intents * Use iOS-specific methods like opening Settings URL scheme ### Web [Section titled “Web”](#web) * **Not supported** - No intent system on web ## Requirements [Section titled “Requirements”](#requirements) * **Capacitor 8.0.0** or higher # @capgo/capacitor-intercom > Integrate Intercom in your Capacitor app for in-app messaging, support experiences, and customer engagement. Native Messaging Bring in-app Intercom chats, help center access, and support workflows to your app. Cross-Platform Full native implementation on iOS and Android for production-grade customer support. Feature Rich Support for messenger, help center, carousels, articles, surveys, and unread badge tracking. Documentation See the [Getting started guide](/docs/plugins/intercom/getting-started/) for setup and API examples. # Getting Started > Learn how to install and start using @capgo/capacitor-intercom in your Capacitor app. 1. **Install the package** * npm ```sh npm i @capgo/capacitor-intercom ``` * pnpm ```sh pnpm add @capgo/capacitor-intercom ``` * yarn ```sh yarn add @capgo/capacitor-intercom ``` * bun ```sh bun add @capgo/capacitor-intercom ``` 2. **Sync with native projects** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` 3. **Add keys in `capacitor.config.ts` (optional)** You can configure Intercom at startup time with this section: ```json { "plugins": { "CapgoIntercom": { "iosApiKey": "ios_sdk-xxx", "iosAppId": "your_ios_app_id", "androidApiKey": "android_sdk-xxx", "androidAppId": "your_android_app_id" } } } ``` ## Initialize Intercom [Section titled “Initialize Intercom”](#initialize-intercom) Use `loadWithKeys()` to provide credentials at runtime: ```typescript import { CapgoIntercom } from '@capgo/capacitor-intercom' await CapgoIntercom.loadWithKeys({ appId: 'your_app_id', apiKeyIOS: 'ios_sdk-xxx', apiKeyAndroid: 'android_sdk-xxx', }) ``` ## Use Intercom in your app [Section titled “Use Intercom in your app”](#use-intercom-in-your-app) Open the messenger and show support UI in one line: ```typescript import { CapgoIntercom } from '@capgo/capacitor-intercom' await CapgoIntercom.displayMessenger() await CapgoIntercom.displayHelpCenter() ``` ## API quick reference [Section titled “API quick reference”](#api-quick-reference) ```typescript await CapgoIntercom.registerIdentifiedUser({ email: 'user@example.com' }) await CapgoIntercom.displayArticle({ articleId: 'article-id' }) await CapgoIntercom.displayCarousel({ carouselId: 'carousel-id' }) await CapgoIntercom.displayMessageComposer({ message: 'Hi, I need help' }) await CapgoIntercom.registerUnidentifiedUser() await CapgoIntercom.hideMessenger() const { count } = await CapgoIntercom.getUnreadConversationCount() ``` ## Platform notes [Section titled “Platform notes”](#platform-notes) * **iOS**: Requires iOS 10+. * **Android**: Supports Android 5.0+ (API 21+). * Works with both Capacitor and Cordova environments for native apps. You now have a functional Intercom setup and can start adding event logging, identity sync, and support messaging. # @capgo/capacitor-is-root > Detect rooted Android devices and jailbroken iOS devices to enhance app security and protect sensitive data. ## Overview [Section titled “Overview”](#overview) The Capacitor Is Root plugin provides comprehensive root and jailbreak detection for Android devices and emulator detection. This plugin helps enhance app security by identifying compromised devices and emulated environments that may pose security risks. Root detection Advanced Android root detection with multiple methods 🔒 Emulator detection Identify emulated environments and testing frameworks 🛡️ Security validation Multiple detection techniques for enhanced accuracy ✅ Android focused Specialized detection for Android security assessment 🤖 ## Installation [Section titled “Installation”](#installation) ```bash npm install @capgo/capacitor-is-root npx cap sync ``` ## Core API Methods [Section titled “Core API Methods”](#core-api-methods) ### Root Detection [Section titled “Root Detection”](#root-detection) * `isRooted()` - Performs comprehensive root detection using default methods * `isRootedWithBusyBox()` - Extended detection including BusyBox checks * `detectRootManagementApps()` - Identifies installed root management applications * `checkForSuBinary()` - Checks for `su` binary presence in system paths ### Emulator Detection [Section titled “Emulator Detection”](#emulator-detection) * `isRunningOnEmulator()` - Detects common Android emulator fingerprints ## Detection Techniques [Section titled “Detection Techniques”](#detection-techniques) The plugin employs multiple detection methods: ### Root Detection [Section titled “Root Detection”](#root-detection-1) * Checks for root management applications (SuperSU, Magisk, etc.) * Scans for suspicious system properties * Identifies test build tags and debug flags * Validates dangerous binary locations * Examines system path permissions ### Emulator Detection [Section titled “Emulator Detection”](#emulator-detection-1) * Hardware fingerprint analysis * Build property inspection * Emulator-specific characteristics * Virtual environment indicators ## Usage Example [Section titled “Usage Example”](#usage-example) ```typescript import { IsRoot } from '@capgo/capacitor-is-root'; // Basic root detection const rootResult = await IsRoot.isRooted(); if (rootResult.isRooted) { console.log('Device is rooted'); // Handle rooted device appropriately } // Extended root detection with BusyBox const extendedResult = await IsRoot.isRootedWithBusyBox(); if (extendedResult.isRooted) { console.log('Device is rooted (extended check)'); } // Check for emulator const emulatorResult = await IsRoot.isRunningOnEmulator(); if (emulatorResult.isEmulator) { console.log('Running on emulator'); } // Detect root management apps const rootAppsResult = await IsRoot.detectRootManagementApps(); if (rootAppsResult.hasRootApps) { console.log('Root management apps detected'); } ``` ## Security Considerations [Section titled “Security Considerations”](#security-considerations) * Use multiple detection methods for higher accuracy * Implement graceful degradation for detected environments * Consider user privacy when implementing security measures * Regular updates recommended as detection methods evolve ## Documentation [Section titled “Documentation”](#documentation) Check the [complete documentation](/docs/plugins/is-root/getting-started/) for detailed implementation guides and advanced security patterns. # Getting Started > Learn how to install and use the Is Root plugin to detect rooted Android devices and emulators for enhanced app security. ## Installation [Section titled “Installation”](#installation) * npm ```bash npm install @capgo/capacitor-is-root npx cap sync ``` * yarn ```bash yarn add @capgo/capacitor-is-root npx cap sync ``` * pnpm ```bash pnpm add @capgo/capacitor-is-root npx cap sync ``` * bun ```bash bun add @capgo/capacitor-is-root npx cap sync ``` ## Platform Support [Section titled “Platform Support”](#platform-support) * **Android**: Full support for root and emulator detection * **iOS**: No configuration required (plugin is Android-focused) ## Usage Example [Section titled “Usage Example”](#usage-example) ```typescript import { IsRoot } from '@capgo/capacitor-is-root'; // Basic root detection const rootResult = await IsRoot.isRooted(); if (rootResult.isRooted) { console.log('Device is rooted'); // Handle rooted device appropriately // Example: Show warning, limit functionality, or block access } // Extended root detection with BusyBox const extendedResult = await IsRoot.isRootedWithBusyBox(); if (extendedResult.isRooted) { console.log('Device is rooted (extended check)'); } // Check for emulator const emulatorResult = await IsRoot.isRunningOnEmulator(); if (emulatorResult.isEmulator) { console.log('Running on emulator'); // Handle emulator environment } // Detect root management apps const rootAppsResult = await IsRoot.detectRootManagementApps(); if (rootAppsResult.hasRootApps) { console.log('Root management apps detected'); } // Check for su binary const suResult = await IsRoot.checkForSuBinary(); if (suResult.hasSu) { console.log('SU binary found on device'); } ``` ## API Reference [Section titled “API Reference”](#api-reference) ### isRooted() [Section titled “isRooted()”](#isrooted) ```typescript isRooted() => Promise<{ isRooted: boolean }> ``` Performs comprehensive root detection using default methods. **Returns:** `Promise<{ isRooted: boolean }>` ### isRootedWithBusyBox() [Section titled “isRootedWithBusyBox()”](#isrootedwithbusybox) ```typescript isRootedWithBusyBox() => Promise<{ isRooted: boolean }> ``` Extended root detection including BusyBox checks. **Returns:** `Promise<{ isRooted: boolean }>` ### detectRootManagementApps() [Section titled “detectRootManagementApps()”](#detectrootmanagementapps) ```typescript detectRootManagementApps() => Promise<{ hasRootApps: boolean }> ``` Identifies installed root management applications (SuperSU, Magisk, etc.). **Returns:** `Promise<{ hasRootApps: boolean }>` ### checkForSuBinary() [Section titled “checkForSuBinary()”](#checkforsubinary) ```typescript checkForSuBinary() => Promise<{ hasSu: boolean }> ``` Checks for the presence of `su` binary in system paths. **Returns:** `Promise<{ hasSu: boolean }>` ### isRunningOnEmulator() [Section titled “isRunningOnEmulator()”](#isrunningonemulator) ```typescript isRunningOnEmulator() => Promise<{ isEmulator: boolean }> ``` Detects common Android emulator fingerprints. **Returns:** `Promise<{ isEmulator: boolean }>` ## Comprehensive Security Check [Section titled “Comprehensive Security Check”](#comprehensive-security-check) ```typescript import { IsRoot } from '@capgo/capacitor-is-root'; async function performSecurityCheck() { const checks = { rooted: false, emulator: false, rootApps: false, suBinary: false }; try { // Run all detection methods const [rootResult, emulatorResult, rootAppsResult, suResult] = await Promise.all([ IsRoot.isRootedWithBusyBox(), IsRoot.isRunningOnEmulator(), IsRoot.detectRootManagementApps(), IsRoot.checkForSuBinary() ]); checks.rooted = rootResult.isRooted; checks.emulator = emulatorResult.isEmulator; checks.rootApps = rootAppsResult.hasRootApps; checks.suBinary = suResult.hasSu; // Determine security level const securityIssues = Object.values(checks).filter(v => v).length; if (securityIssues > 0) { console.warn(`Device has ${securityIssues} security concern(s)`, checks); return { secure: false, issues: checks }; } return { secure: true, issues: checks }; } catch (error) { console.error('Security check failed:', error); throw error; } } // Use in your app const securityStatus = await performSecurityCheck(); if (!securityStatus.secure) { // Handle insecure device showSecurityWarning(securityStatus.issues); } ``` ## Detection Techniques [Section titled “Detection Techniques”](#detection-techniques) ### Root Detection [Section titled “Root Detection”](#root-detection) The plugin employs multiple detection methods: * Checks for root management applications (SuperSU, Magisk, KingRoot, etc.) * Scans for suspicious system properties * Identifies test build tags and debug flags * Validates dangerous binary locations * Examines system path permissions * Detects known root cloaking apps ### Emulator Detection [Section titled “Emulator Detection”](#emulator-detection) * Hardware fingerprint analysis * Build property inspection * Emulator-specific characteristics * Virtual environment indicators ## Handling Security Issues [Section titled “Handling Security Issues”](#handling-security-issues) ```typescript import { IsRoot } from '@capgo/capacitor-is-root'; async function handleDeviceSecurity() { const rootResult = await IsRoot.isRooted(); if (rootResult.isRooted) { // Option 1: Show warning and continue showWarning('Your device appears to be rooted. Some features may be limited.'); // Option 2: Limit functionality disableSensitiveFeatures(); // Option 3: Block access to app showBlockedScreen('This app cannot run on rooted devices for security reasons.'); return false; } return true; } function showWarning(message: string) { // Show user-friendly warning dialog alert(message); } function disableSensitiveFeatures() { // Disable payment processing, sensitive data access, etc. console.log('Sensitive features disabled due to rooted device'); } function showBlockedScreen(message: string) { // Show blocking screen and exit app alert(message); } ``` ## Best Practices [Section titled “Best Practices”](#best-practices) * Use multiple detection methods for higher accuracy * Implement graceful degradation rather than blocking access entirely * Provide clear user communication about security concerns * Consider the user experience when implementing security measures * Keep the plugin updated as detection methods evolve * Test on both rooted and non-rooted devices * Handle detection failures gracefully ## Security Considerations [Section titled “Security Considerations”](#security-considerations) * No detection method is 100% foolproof * Advanced users can bypass detection mechanisms * Use in combination with server-side security measures * Consider user privacy when implementing security checks * Follow platform guidelines for security implementations * Regular updates recommended as root hiding techniques evolve ## Use Cases [Section titled “Use Cases”](#use-cases) * **Banking and financial apps**: Prevent access on compromised devices * **DRM-protected content**: Protect copyrighted material * **Enterprise apps**: Enforce BYOD security policies * **Payment processing**: Ensure secure transaction environment * **Sensitive data apps**: Protect confidential information # @capgo/ivs-player > Integrate Amazon IVS player for ultra-low latency live streaming with interactive features in your Capacitor apps. Low-latency streaming Stream with sub-second latency using Amazon IVS 📡 Interactive features Support for timed metadata and viewer interactions 💬 Quality adaptation Automatic quality switching based on network conditions 📶 Comprehensive Documentation Check the [Documentation](/docs/plugins/ivs-player/getting-started/) to master the plugin in just a few minutes. # Getting Started > Learn how to install and configure the Capacitor IVS Player plugin to integrate Amazon IVS low-latency streaming in your app. 1. **Install the package** * npm ```sh npm i @capgo/ivs-player ``` * pnpm ```sh pnpm add @capgo/ivs-player ``` * yarn ```sh yarn add @capgo/ivs-player ``` * bun ```sh bun add @capgo/ivs-player ``` 2. **Sync with native projects** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` 3. **Configure the plugin** **Basic Player Setup:** ```typescript import { IvsPlayer } from '@capgo/ivs-player'; // Create a player const { playerId } = await IvsPlayer.create({ url: 'https://your-ivs-playback-url.m3u8', autoplay: true }); // Start playback await IvsPlayer.play({ playerId }); ``` **Player Controls:** ```typescript // Pause playback await IvsPlayer.pause({ playerId }); // Set volume await IvsPlayer.setVolume({ playerId, volume: 0.5 // 0.0 to 1.0 }); // Seek to position await IvsPlayer.seekTo({ playerId, position: 30 // seconds }); ``` * iOS Add to your `Info.plist`: ```xml NSAppTransportSecurity NSAllowsArbitraryLoads ``` * Android Add to your `AndroidManifest.xml`: ```xml ``` 4. **Event handling** ```typescript import { IvsPlayer } from '@capgo/ivs-player'; // Listen for player events IvsPlayer.addListener('onState', (event) => { console.log('Player state:', event.state); // States: idle, buffering, ready, playing, ended }); IvsPlayer.addListener('onError', (event) => { console.error('Player error:', event.error); }); IvsPlayer.addListener('onDuration', (event) => { console.log('Video duration:', event.duration); }); IvsPlayer.addListener('onProgress', (event) => { console.log('Current position:', event.position); }); // Listen for timed metadata IvsPlayer.addListener('onMetadata', (event) => { console.log('Timed metadata:', event.metadata); }); ``` 5. **Advanced usage** ```typescript import { IvsPlayer } from '@capgo/ivs-player'; export class StreamPlayer { private playerId: string | null = null; private listeners: any[] = []; async initialize(streamUrl: string) { // Create player with custom configuration const result = await IvsPlayer.create({ url: streamUrl, autoplay: false, muted: true, // Start muted for autoplay policies controls: true }); this.playerId = result.playerId; this.setupEventListeners(); } private setupEventListeners() { // State changes const stateListener = IvsPlayer.addListener('onState', (event) => { this.handleStateChange(event.state); }); this.listeners.push(stateListener); // Quality changes const qualityListener = IvsPlayer.addListener('onQuality', (event) => { console.log(`Quality changed to: ${event.quality.name}`); }); this.listeners.push(qualityListener); // Buffer updates const bufferListener = IvsPlayer.addListener('onBuffer', (event) => { console.log(`Buffer: ${event.percentage}%`); }); this.listeners.push(bufferListener); } private handleStateChange(state: string) { switch (state) { case 'playing': console.log('Stream is playing'); break; case 'buffering': console.log('Stream is buffering'); break; case 'ended': console.log('Stream ended'); break; } } async play() { if (this.playerId) { await IvsPlayer.play({ playerId: this.playerId }); } } async pause() { if (this.playerId) { await IvsPlayer.pause({ playerId: this.playerId }); } } async setQuality(qualityName: string) { if (this.playerId) { await IvsPlayer.setQuality({ playerId: this.playerId, quality: qualityName }); } } async destroy() { // Remove listeners this.listeners.forEach(listener => listener.remove()); // Destroy player if (this.playerId) { await IvsPlayer.destroy({ playerId: this.playerId }); } } } ``` ## API Reference [Section titled “API Reference”](#api-reference) ### Methods [Section titled “Methods”](#methods) #### `create(options: CreateOptions)` [Section titled “create(options: CreateOptions)”](#createoptions-createoptions) Create a new IVS player instance. **Parameters:** * `options`: Player configuration * `url`: string - IVS playback URL * `autoplay`: boolean - Auto-start playback * `muted`: boolean - Start muted * `controls`: boolean - Show native controls **Returns:** `Promise<{ playerId: string }>` #### `play(options: PlayerOptions)` [Section titled “play(options: PlayerOptions)”](#playoptions-playeroptions) Start playback. #### `pause(options: PlayerOptions)` [Section titled “pause(options: PlayerOptions)”](#pauseoptions-playeroptions) Pause playback. #### `stop(options: PlayerOptions)` [Section titled “stop(options: PlayerOptions)”](#stopoptions-playeroptions) Stop playback and reset position. #### `seekTo(options: SeekOptions)` [Section titled “seekTo(options: SeekOptions)”](#seektooptions-seekoptions) Seek to a specific position. #### `setVolume(options: VolumeOptions)` [Section titled “setVolume(options: VolumeOptions)”](#setvolumeoptions-volumeoptions) Set player volume (0.0 to 1.0). #### `setQuality(options: QualityOptions)` [Section titled “setQuality(options: QualityOptions)”](#setqualityoptions-qualityoptions) Set playback quality. #### `destroy(options: PlayerOptions)` [Section titled “destroy(options: PlayerOptions)”](#destroyoptions-playeroptions) Destroy the player instance. ### Events [Section titled “Events”](#events) * `onState`: Player state changes * `onError`: Playback errors * `onDuration`: Video duration available * `onProgress`: Playback progress updates * `onQuality`: Quality changes * `onMetadata`: Timed metadata events * `onBuffer`: Buffer status updates ### Interfaces [Section titled “Interfaces”](#interfaces) ```typescript interface CreateOptions { url: string; autoplay?: boolean; muted?: boolean; controls?: boolean; } interface PlayerOptions { playerId: string; } interface SeekOptions extends PlayerOptions { position: number; // seconds } interface VolumeOptions extends PlayerOptions { volume: number; // 0.0 to 1.0 } interface QualityOptions extends PlayerOptions { quality: string; // quality name } ``` ## Platform Notes [Section titled “Platform Notes”](#platform-notes) ### iOS [Section titled “iOS”](#ios) * Requires iOS 12.0 or later * Uses native AVPlayer with IVS extensions * Supports Picture-in-Picture on iPads ### Android [Section titled “Android”](#android) * Requires Android 5.0 (API level 21) or later * Uses Amazon IVS Player SDK * Hardware acceleration recommended ## Common Use Cases [Section titled “Common Use Cases”](#common-use-cases) 1. **Live Streaming**: Real-time events, sports, gaming 2. **Interactive Streaming**: Viewer polls, Q\&A sessions 3. **E-commerce**: Live shopping with product highlights 4. **Education**: Live classes with timed content 5. **Entertainment**: Concerts, shows with audience interaction ## Best Practices [Section titled “Best Practices”](#best-practices) 1. **Network Handling**: Monitor connection quality ```typescript IvsPlayer.addListener('onError', (event) => { if (event.error.includes('network')) { // Handle network errors showRetryButton(); } }); ``` 2. **Resource Management**: Always destroy players when done 3. **Autoplay Policies**: Start muted for reliable autoplay 4. **Quality Selection**: Let IVS handle automatic quality switching ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) **Stream not playing:** * Verify the IVS playback URL is valid * Check network permissions * Ensure the stream is live **High latency:** * IVS is optimized for low latency, check your encoder settings * Verify you’re using the IVS-specific playback URL **Quality issues:** * Allow automatic quality switching * Check network bandwidth requirements # @capgo/capacitor-jw-player > Play videos from JW Player with fullscreen interface, playlists, and complete playback control in your Capacitor app. Fullscreen Player Always fullscreen video player interface 📺 Playlist Support Supports both single videos and full playlists 📋 Playback Controls Complete control over play, pause, seek, speed 🎮 Audio & Captions Audio track selection and caption/subtitle support 🔊 Event System Event listeners for player state changes 📡 Getting Started Check the [Getting Started Guide](/docs/plugins/jw-player/getting-started/) to install and configure the plugin. # Getting Started > Learn how to install and integrate JW Player into your Capacitor app for professional video streaming capabilities. ## Installation [Section titled “Installation”](#installation) * npm ```bash npm install @capgo/capacitor-jw-player npx cap sync ``` * yarn ```bash yarn add @capgo/capacitor-jw-player npx cap sync ``` * pnpm ```bash pnpm add @capgo/capacitor-jw-player npx cap sync ``` * bun ```bash bun add @capgo/capacitor-jw-player npx cap sync ``` ## Prerequisites [Section titled “Prerequisites”](#prerequisites) You’ll need a JW Player account and license key to use this plugin. Sign up at [JW Player](https://www.jwplayer.com/) if you don’t have one. ## Platform Configuration [Section titled “Platform Configuration”](#platform-configuration) ### iOS [Section titled “iOS”](#ios) Add your JW Player license key to your `Info.plist`: ```xml JWPlayerLicenseKey YOUR_LICENSE_KEY ``` ### Android [Section titled “Android”](#android) Add your JW Player license key to `android/app/src/main/res/values/strings.xml`: ```xml YOUR_LICENSE_KEY ``` ## Usage Example [Section titled “Usage Example”](#usage-example) ```typescript import { JWPlayer } from '@capgo/capacitor-jw-player'; // Play a single video await JWPlayer.playVideo({ mediaUrl: 'https://example.com/video.mp4', title: 'My Video', description: 'Video description', poster: 'https://example.com/poster.jpg' }); // Play a playlist await JWPlayer.playPlaylist({ playlistUrl: 'https://example.com/playlist.json' }); // Listen for player events JWPlayer.addListener('playerReady', () => { console.log('Player is ready'); }); JWPlayer.addListener('play', (data) => { console.log('Video started playing'); }); JWPlayer.addListener('pause', (data) => { console.log('Video paused'); }); JWPlayer.addListener('complete', (data) => { console.log('Video playback completed'); }); JWPlayer.addListener('error', (error) => { console.error('Player error:', error); }); ``` ## API Reference [Section titled “API Reference”](#api-reference) ### playVideo(options) [Section titled “playVideo(options)”](#playvideooptions) ```typescript playVideo(options: VideoOptions) => Promise ``` Play a single video in fullscreen mode. | Param | Type | | ------------- | -------------- | | **`options`** | `VideoOptions` | ### playPlaylist(options) [Section titled “playPlaylist(options)”](#playplaylistoptions) ```typescript playPlaylist(options: PlaylistOptions) => Promise ``` Play a playlist in fullscreen mode. | Param | Type | | ------------- | ----------------- | | **`options`** | `PlaylistOptions` | ### pause() [Section titled “pause()”](#pause) ```typescript pause() => Promise ``` Pause the current video. ### play() [Section titled “play()”](#play) ```typescript play() => Promise ``` Resume video playback. ### seek(options) [Section titled “seek(options)”](#seekoptions) ```typescript seek(options: { position: number }) => Promise ``` Seek to a specific position in the video. | Param | Type | | ------------- | ---------------------- | | **`options`** | `{ position: number }` | ### setPlaybackSpeed(options) [Section titled “setPlaybackSpeed(options)”](#setplaybackspeedoptions) ```typescript setPlaybackSpeed(options: { speed: number }) => Promise ``` Set the playback speed. | Param | Type | | ------------- | ------------------- | | **`options`** | `{ speed: number }` | ### setVolume(options) [Section titled “setVolume(options)”](#setvolumeoptions) ```typescript setVolume(options: { volume: number }) => Promise ``` Set the audio volume (0.0 to 1.0). | Param | Type | | ------------- | -------------------- | | **`options`** | `{ volume: number }` | ### selectAudioTrack(options) [Section titled “selectAudioTrack(options)”](#selectaudiotrackoptions) ```typescript selectAudioTrack(options: { trackIndex: number }) => Promise ``` Select an audio track by index. | Param | Type | | ------------- | ------------------------ | | **`options`** | `{ trackIndex: number }` | ### selectCaptionTrack(options) [Section titled “selectCaptionTrack(options)”](#selectcaptiontrackoptions) ```typescript selectCaptionTrack(options: { trackIndex: number }) => Promise ``` Select a caption/subtitle track by index. | Param | Type | | ------------- | ------------------------ | | **`options`** | `{ trackIndex: number }` | ### stop() [Section titled “stop()”](#stop) ```typescript stop() => Promise ``` Stop playback and close the player. ## Interfaces [Section titled “Interfaces”](#interfaces) ### VideoOptions [Section titled “VideoOptions”](#videooptions) | Prop | Type | Description | | ----------------- | --------- | --------------------------------------------- | | **`mediaUrl`** | `string` | URL of the video file | | **`title`** | `string` | Video title (optional) | | **`description`** | `string` | Video description (optional) | | **`poster`** | `string` | URL of the poster/thumbnail (optional) | | **`autoStart`** | `boolean` | Auto-start playback (optional, default: true) | ### PlaylistOptions [Section titled “PlaylistOptions”](#playlistoptions) | Prop | Type | Description | | ----------------- | --------- | ------------------------------ | | **`playlistUrl`** | `string` | URL of the JW playlist JSON | | **`autoStart`** | `boolean` | Auto-start playback (optional) | ## Event Listeners [Section titled “Event Listeners”](#event-listeners) ### Available Events [Section titled “Available Events”](#available-events) * `playerReady` - Player is initialized and ready * `play` - Video playback started * `pause` - Video playback paused * `complete` - Video playback completed * `error` - Player encountered an error * `seek` - User sought to a different position * `time` - Playback time updated * `bufferChange` - Buffer state changed ### Event Example [Section titled “Event Example”](#event-example) ```typescript // Listen for time updates JWPlayer.addListener('time', (data) => { console.log('Current time:', data.position); console.log('Duration:', data.duration); }); // Listen for buffer changes JWPlayer.addListener('bufferChange', (data) => { console.log('Buffering:', data.buffering); }); // Remove listener when done const listener = await JWPlayer.addListener('play', (data) => { console.log('Playing'); }); // Later... listener.remove(); ``` ## Advanced Usage [Section titled “Advanced Usage”](#advanced-usage) ### Playing with Captions [Section titled “Playing with Captions”](#playing-with-captions) ```typescript await JWPlayer.playVideo({ mediaUrl: 'https://example.com/video.mp4', title: 'Video with Subtitles', poster: 'https://example.com/poster.jpg', tracks: [ { file: 'https://example.com/captions-en.vtt', label: 'English', kind: 'captions' }, { file: 'https://example.com/captions-es.vtt', label: 'Spanish', kind: 'captions' } ] }); ``` ### Custom Playback Controls [Section titled “Custom Playback Controls”](#custom-playback-controls) ```typescript import { JWPlayer } from '@capgo/capacitor-jw-player'; // Start playing await JWPlayer.play(); // Pause after 10 seconds setTimeout(() => { JWPlayer.pause(); }, 10000); // Seek to 30 seconds await JWPlayer.seek({ position: 30 }); // Set playback speed to 1.5x await JWPlayer.setPlaybackSpeed({ speed: 1.5 }); // Set volume to 50% await JWPlayer.setVolume({ volume: 0.5 }); ``` ## Best Practices [Section titled “Best Practices”](#best-practices) * Always provide a poster image for better user experience * Handle player errors gracefully with appropriate error messages * Clean up event listeners when component unmounts * Test video playback on both iOS and Android devices * Use appropriate video formats (MP4 recommended) * Implement loading states while video initializes * Consider network conditions when streaming ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) ### Video Won’t Play [Section titled “Video Won’t Play”](#video-wont-play) * Verify your JW Player license key is correct * Check that video URL is accessible and valid * Ensure proper CORS headers if using custom server * Test with different video formats ### Fullscreen Issues [Section titled “Fullscreen Issues”](#fullscreen-issues) * The plugin always plays in fullscreen mode * Ensure proper permissions for fullscreen in your app ### Performance [Section titled “Performance”](#performance) * Use appropriate video quality for target devices * Consider adaptive streaming for better performance * Implement proper buffering indicators # @capgo/capacitor-keep-awake > Keep your device screen on programmatically for video players, navigation apps, games, and presentations. Simple API Keep screen on or allow sleep with just one method call Cross-platform Works on iOS, Android, and Web platforms Lightweight Zero dependencies with minimal footprint Status Checking Query current wake lock state at any time Platform Detection Check if wake lock is supported before using Comprehensive Documentation Check the [Documentation](/docs/plugins/keep-awake/getting-started/) to master the plugin in just a few minutes. # Getting Started > Learn how to install and use the Keep Awake plugin to prevent screen dimming in your Capacitor app. 1. **Install the package** * npm ```sh npm i @capgo/capacitor-keep-awake ``` * pnpm ```sh pnpm add @capgo/capacitor-keep-awake ``` * yarn ```sh yarn add @capgo/capacitor-keep-awake ``` * bun ```sh bun add @capgo/capacitor-keep-awake ``` 2. **Sync with native projects** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Platform Setup [Section titled “Platform Setup”](#platform-setup) ### iOS [Section titled “iOS”](#ios) Works out of the box. Uses `UIApplication.shared.isIdleTimerDisabled` to control screen sleep. ### Android [Section titled “Android”](#android) Works out of the box. Uses `WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON` flag. No permissions required. ### Web [Section titled “Web”](#web) Works in modern browsers that support the Screen Wake Lock API. Call `isSupported()` first to check if wake lock is available on the current browser. ## Basic Usage [Section titled “Basic Usage”](#basic-usage) Import the plugin and use its methods to control screen wake lock: ```typescript import { KeepAwake } from '@capgo/capacitor-keep-awake'; // Check if keep awake is supported const checkSupport = async () => { const { isSupported } = await KeepAwake.isSupported(); console.log('Keep awake supported:', isSupported); }; // Keep the screen on const keepScreenOn = async () => { await KeepAwake.keepAwake(); console.log('Screen will stay on'); }; // Allow the screen to sleep const allowScreenSleep = async () => { await KeepAwake.allowSleep(); console.log('Screen can now sleep'); }; // Check current status const checkStatus = async () => { const { isKeptAwake } = await KeepAwake.isKeptAwake(); console.log('Screen is kept awake:', isKeptAwake); }; ``` ## API Reference [Section titled “API Reference”](#api-reference) ### keepAwake() [Section titled “keepAwake()”](#keepawake) Prevents the device from dimming the screen. ```typescript await KeepAwake.keepAwake(); ``` ### allowSleep() [Section titled “allowSleep()”](#allowsleep) Allows the device to dim the screen (disables keep awake). ```typescript await KeepAwake.allowSleep(); ``` ### isSupported() [Section titled “isSupported()”](#issupported) Checks if the keep awake feature is supported on the current platform. ```typescript const { isSupported } = await KeepAwake.isSupported(); // Returns: { isSupported: boolean } ``` ### isKeptAwake() [Section titled “isKeptAwake()”](#iskeptawake) Checks if the device is currently being kept awake. ```typescript const { isKeptAwake } = await KeepAwake.isKeptAwake(); // Returns: { isKeptAwake: boolean } ``` ### getPluginVersion() [Section titled “getPluginVersion()”](#getpluginversion) Gets the native plugin version. ```typescript const { version } = await KeepAwake.getPluginVersion(); // Returns: { version: string } ``` ## Complete Example - Video Player [Section titled “Complete Example - Video Player”](#complete-example---video-player) ```typescript import { KeepAwake } from '@capgo/capacitor-keep-awake'; export class VideoPlayerService { private isPlaying = false; async init() { const { isSupported } = await KeepAwake.isSupported(); if (!isSupported) { console.warn('Keep awake not supported on this platform'); } } async onVideoPlay() { this.isPlaying = true; try { await KeepAwake.keepAwake(); console.log('Screen will stay on during playback'); } catch (error) { console.error('Failed to keep screen awake:', error); } } async onVideoPause() { this.isPlaying = false; try { await KeepAwake.allowSleep(); console.log('Screen can now dim'); } catch (error) { console.error('Failed to allow sleep:', error); } } async onVideoEnd() { this.isPlaying = false; await KeepAwake.allowSleep(); } // Call this when component/page is destroyed async cleanup() { if (this.isPlaying) { await KeepAwake.allowSleep(); } } } ``` ## Complete Example - Navigation App [Section titled “Complete Example - Navigation App”](#complete-example---navigation-app) ```typescript import { KeepAwake } from '@capgo/capacitor-keep-awake'; import { App } from '@capacitor/app'; export class NavigationService { private isNavigating = false; async startNavigation() { this.isNavigating = true; // Keep screen on during navigation await KeepAwake.keepAwake(); // Handle app going to background App.addListener('appStateChange', async ({ isActive }) => { if (!isActive && this.isNavigating) { // App went to background - screen management is handled by OS } else if (isActive && this.isNavigating) { // App came back - ensure screen stays on await KeepAwake.keepAwake(); } }); } async stopNavigation() { this.isNavigating = false; await KeepAwake.allowSleep(); } } ``` ## Complete Example - Presentation Mode [Section titled “Complete Example - Presentation Mode”](#complete-example---presentation-mode) ```typescript import { KeepAwake } from '@capgo/capacitor-keep-awake'; export class PresentationMode { private isPresenting = false; async togglePresentationMode() { const { isSupported } = await KeepAwake.isSupported(); if (!isSupported) { alert('Presentation mode not available on this device'); return; } const { isKeptAwake } = await KeepAwake.isKeptAwake(); if (isKeptAwake) { await KeepAwake.allowSleep(); this.isPresenting = false; console.log('Presentation mode OFF'); } else { await KeepAwake.keepAwake(); this.isPresenting = true; console.log('Presentation mode ON'); } } async forceExit() { if (this.isPresenting) { await KeepAwake.allowSleep(); this.isPresenting = false; } } } ``` ## Best Practices [Section titled “Best Practices”](#best-practices) 1. **Check support first** Always verify keep awake is supported before using it: ```typescript const { isSupported } = await KeepAwake.isSupported(); if (!isSupported) { // Handle gracefully - feature won't work on this platform } ``` 2. **Always allow sleep when done** Make sure to call `allowSleep()` when the feature is no longer needed: ```typescript // In cleanup/destroy lifecycle async ngOnDestroy() { await KeepAwake.allowSleep(); } ``` 3. **Handle errors gracefully** ```typescript try { await KeepAwake.keepAwake(); } catch (error) { console.error('Failed to keep screen awake:', error); } ``` 4. **Consider battery impact** Keeping the screen on drains battery faster. Only use when necessary and disable when not needed. 5. **Handle app lifecycle** Remember to manage wake lock state when app goes to background/foreground. ## Use Cases [Section titled “Use Cases”](#use-cases) * **Video Players** - Keep screen on during playback * **Navigation Apps** - Prevent dimming during turn-by-turn navigation * **Games** - Keep screen active during gameplay * **Presentations** - Prevent sleep during slideshows * **Reading Apps** - Keep screen on while reading * **Cooking Apps** - Keep recipe visible while cooking * **Fitness Apps** - Keep workout instructions visible * **Kiosk Mode** - Prevent idle screen dimming ## Platform Notes [Section titled “Platform Notes”](#platform-notes) ### iOS [Section titled “iOS”](#ios-1) * Uses `UIApplication.shared.isIdleTimerDisabled` * Works across all iOS versions supported by Capacitor * No permissions required ### Android [Section titled “Android”](#android-1) * Uses `WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON` * Works across all Android versions supported by Capacitor * No permissions required ### Web [Section titled “Web”](#web-1) * Uses the Screen Wake Lock API (`navigator.wakeLock`) * Requires a secure context (HTTPS) * Not supported in all browsers - check with `isSupported()` * Wake lock may be released by the browser when the tab is not visible # @capgo/capacitor-launch-navigator > Launch navigation apps with turn-by-turn directions, supporting multiple map providers and custom routing options. ## Overview [Section titled “Overview”](#overview) The Capacitor Launch Navigator plugin enables launching native navigation apps on iOS and Android devices with coordinate-based navigation. This plugin provides seamless integration with popular mapping applications and supports multiple transportation modes for comprehensive navigation solutions. Multi-app support Google Maps, Apple Maps, Waze, Citymapper and more 🗺️ Transport modes Driving, walking, transit, and cycling directions 🧭 App detection Check availability and list installed navigation apps 🔍 Coordinate-based Navigate using latitude/longitude coordinates 📍 ## Installation [Section titled “Installation”](#installation) ```bash npm install @capgo/capacitor-launch-navigator npx cap sync ``` ## iOS Configuration [Section titled “iOS Configuration”](#ios-configuration) Add URL schemes to your `Info.plist` to detect installed navigation apps: ```xml LSApplicationQueriesSchemes googlemaps waze citymapper ``` ## Core API Methods [Section titled “Core API Methods”](#core-api-methods) ### Navigation [Section titled “Navigation”](#navigation) * `navigate(options)` - Launch navigation to specified coordinates * `isAppAvailable(options)` - Check if a specific navigation app is installed * `getAvailableApps()` - List all available navigation apps on device * `getSupportedApps()` - Get all supported apps for current platform ## Usage Example [Section titled “Usage Example”](#usage-example) ```typescript import { LaunchNavigator, TransportMode } from '@capgo/capacitor-launch-navigator'; // Basic navigation to coordinates await LaunchNavigator.navigate({ destination: [37.7749, -122.4194], // San Francisco coordinates }); // Advanced navigation with options await LaunchNavigator.navigate({ destination: [37.7749, -122.4194], options: { start: [37.7849, -122.4094], // Optional starting point transportMode: TransportMode.DRIVING, app: 'google_maps', // Preferred navigation app appDisplayName: 'Google Maps' } }); // Check if specific app is available const result = await LaunchNavigator.isAppAvailable({ app: 'google_maps' }); if (result.available) { console.log('Google Maps is installed'); } else { console.log('Google Maps is not available'); } // Get all available navigation apps const availableApps = await LaunchNavigator.getAvailableApps(); console.log('Available navigation apps:', availableApps); // Get all supported apps for platform const supportedApps = await LaunchNavigator.getSupportedApps(); console.log('Supported apps:', supportedApps); ``` ## Navigation Options [Section titled “Navigation Options”](#navigation-options) ```typescript interface NavigationOptions { start?: [number, number]; // Starting coordinates [lat, lng] transportMode?: TransportMode; // Transportation method app?: string; // Preferred navigation app appDisplayName?: string; // Display name for app launchMode?: LaunchMode; // How to launch the app } ``` ## Transport Modes [Section titled “Transport Modes”](#transport-modes) ```typescript enum TransportMode { DRIVING = 'driving', WALKING = 'walking', TRANSIT = 'transit', CYCLING = 'cycling' } ``` ## Supported Navigation Apps [Section titled “Supported Navigation Apps”](#supported-navigation-apps) ### iOS [Section titled “iOS”](#ios) * Apple Maps (built-in) * Google Maps * Waze * Citymapper * Transit * Moovit * Uber * Lyft * And many more… ### Android [Section titled “Android”](#android) * Google Maps (built-in) * Waze * Citymapper * HERE WeGo * Sygic * MapQuest * Moovit * And many more… ## Coordinate Requirements [Section titled “Coordinate Requirements”](#coordinate-requirements) > ⚠️ **Important**: This plugin only accepts latitude/longitude coordinates for navigation. Use [@capgo/capacitor-nativegeocoder](https://github.com/Cap-go/capacitor-nativegeocoder) to convert addresses to coordinates. ```typescript // Example with geocoding import { NativeGeocoder } from '@capgo/capacitor-nativegeocoder'; // Convert address to coordinates const geocodeResult = await NativeGeocoder.forwardGeocode({ address: '1600 Amphitheatre Parkway, Mountain View, CA' }); if (geocodeResult.results.length > 0) { const coords = geocodeResult.results[0]; // Launch navigation with geocoded coordinates await LaunchNavigator.navigate({ destination: [coords.latitude, coords.longitude] }); } ``` ## App Detection and Selection [Section titled “App Detection and Selection”](#app-detection-and-selection) ```typescript // Check multiple apps and use the first available const appsToCheck = ['google_maps', 'waze', 'apple_maps']; let selectedApp = null; for (const app of appsToCheck) { const result = await LaunchNavigator.isAppAvailable({ app }); if (result.available) { selectedApp = app; break; } } if (selectedApp) { await LaunchNavigator.navigate({ destination: [37.7749, -122.4194], options: { app: selectedApp } }); } else { console.log('No supported navigation apps found'); } ``` ## Error Handling [Section titled “Error Handling”](#error-handling) ```typescript try { await LaunchNavigator.navigate({ destination: [37.7749, -122.4194], options: { app: 'google_maps', transportMode: TransportMode.DRIVING } }); } catch (error) { console.error('Navigation failed:', error); // Handle error - app not available, invalid coordinates, etc. } ``` ## Use Cases [Section titled “Use Cases”](#use-cases) * **Location-based services**: Navigate users to points of interest * **Delivery apps**: Guide drivers to delivery locations * **Event apps**: Direct attendees to venue locations * **Real estate apps**: Navigate to property locations * **Travel apps**: Guide tourists to attractions ## Best Practices [Section titled “Best Practices”](#best-practices) * Always check app availability before attempting navigation * Provide fallback options when preferred apps aren’t available * Use meaningful app display names for user selection * Handle errors gracefully with user-friendly messages * Consider user preferences for default navigation apps ## Documentation [Section titled “Documentation”](#documentation) Check the [complete documentation](/docs/plugins/launch-navigator/getting-started/) for detailed implementation guides and advanced navigation patterns. # Getting Started > Learn how to install and use the Launch Navigator plugin to integrate with native navigation apps on iOS and Android. ## Installation [Section titled “Installation”](#installation) * npm ```bash npm install @capgo/capacitor-launch-navigator npx cap sync ``` * yarn ```bash yarn add @capgo/capacitor-launch-navigator npx cap sync ``` * pnpm ```bash pnpm add @capgo/capacitor-launch-navigator npx cap sync ``` * bun ```bash bun add @capgo/capacitor-launch-navigator npx cap sync ``` ## Platform Configuration [Section titled “Platform Configuration”](#platform-configuration) ### iOS [Section titled “iOS”](#ios) Add URL schemes to your `Info.plist` to detect installed navigation apps: ```xml LSApplicationQueriesSchemes googlemaps waze citymapper transit moovit uber lyft ``` ### Android [Section titled “Android”](#android) No additional configuration required. The plugin will detect installed navigation apps automatically. ## Usage Example [Section titled “Usage Example”](#usage-example) ```typescript import { LaunchNavigator, TransportMode } from '@capgo/capacitor-launch-navigator'; // Basic navigation to coordinates await LaunchNavigator.navigate({ destination: [37.7749, -122.4194], // San Francisco coordinates }); // Advanced navigation with options await LaunchNavigator.navigate({ destination: [37.7749, -122.4194], options: { start: [37.7849, -122.4094], // Optional starting point transportMode: TransportMode.DRIVING, app: 'google_maps', // Preferred navigation app appDisplayName: 'Google Maps' } }); // Check if specific app is available const result = await LaunchNavigator.isAppAvailable({ app: 'google_maps' }); if (result.available) { console.log('Google Maps is installed'); } else { console.log('Google Maps is not available'); } // Get all available navigation apps const availableApps = await LaunchNavigator.getAvailableApps(); console.log('Available navigation apps:', availableApps); // Get all supported apps for platform const supportedApps = await LaunchNavigator.getSupportedApps(); console.log('Supported apps:', supportedApps); ``` ## API Reference [Section titled “API Reference”](#api-reference) ### navigate(options) [Section titled “navigate(options)”](#navigateoptions) ```typescript navigate(options: NavigateOptions) => Promise ``` Launch navigation to specified coordinates. | Param | Type | | ------------- | ----------------- | | **`options`** | `NavigateOptions` | ### isAppAvailable(options) [Section titled “isAppAvailable(options)”](#isappavailableoptions) ```typescript isAppAvailable(options: { app: string }) => Promise<{ available: boolean }> ``` Check if a specific navigation app is installed. | Param | Type | | ------------- | ----------------- | | **`options`** | `{ app: string }` | **Returns:** `Promise<{ available: boolean }>` ### getAvailableApps() [Section titled “getAvailableApps()”](#getavailableapps) ```typescript getAvailableApps() => Promise<{ apps: string[] }> ``` Get list of all available navigation apps on the device. **Returns:** `Promise<{ apps: string[] }>` ### getSupportedApps() [Section titled “getSupportedApps()”](#getsupportedapps) ```typescript getSupportedApps() => Promise<{ apps: string[] }> ``` Get list of all supported apps for the current platform. **Returns:** `Promise<{ apps: string[] }>` ## Interfaces [Section titled “Interfaces”](#interfaces) ### NavigateOptions [Section titled “NavigateOptions”](#navigateoptions-1) | Prop | Type | Description | | ----------------- | ------------------- | ---------------------------------------- | | **`destination`** | `[number, number]` | Destination coordinates \[lat, lng] | | **`options`** | `NavigationOptions` | Additional navigation options (optional) | ### NavigationOptions [Section titled “NavigationOptions”](#navigationoptions) | Prop | Type | Description | | -------------------- | ------------------ | ------------------------------------------- | | **`start`** | `[number, number]` | Starting coordinates \[lat, lng] (optional) | | **`transportMode`** | `TransportMode` | Transportation method (optional) | | **`app`** | `string` | Preferred navigation app (optional) | | **`appDisplayName`** | `string` | Display name for app (optional) | ### TransportMode [Section titled “TransportMode”](#transportmode) ```typescript enum TransportMode { DRIVING = 'driving', WALKING = 'walking', TRANSIT = 'transit', CYCLING = 'cycling' } ``` ## Coordinate Requirement [Section titled “Coordinate Requirement”](#coordinate-requirement) > **Important**: This plugin only accepts latitude/longitude coordinates for navigation. Use [@capgo/capacitor-nativegeocoder](https://github.com/Cap-go/capacitor-nativegeocoder) to convert addresses to coordinates. ```typescript import { NativeGeocoder } from '@capgo/capacitor-nativegeocoder'; import { LaunchNavigator } from '@capgo/capacitor-launch-navigator'; // Convert address to coordinates const geocodeResult = await NativeGeocoder.forwardGeocode({ address: '1600 Amphitheatre Parkway, Mountain View, CA' }); if (geocodeResult.results.length > 0) { const coords = geocodeResult.results[0]; // Launch navigation with geocoded coordinates await LaunchNavigator.navigate({ destination: [coords.latitude, coords.longitude] }); } ``` ## Supported Navigation Apps [Section titled “Supported Navigation Apps”](#supported-navigation-apps) ### iOS [Section titled “iOS”](#ios-1) * Apple Maps (built-in) * Google Maps * Waze * Citymapper * Transit * Moovit * Uber * Lyft * And many more… ### Android [Section titled “Android”](#android-1) * Google Maps (built-in) * Waze * Citymapper * HERE WeGo * Sygic * MapQuest * Moovit * And many more… ## Advanced Examples [Section titled “Advanced Examples”](#advanced-examples) ### Check Multiple Apps and Use First Available [Section titled “Check Multiple Apps and Use First Available”](#check-multiple-apps-and-use-first-available) ```typescript const appsToCheck = ['google_maps', 'waze', 'apple_maps']; let selectedApp = null; for (const app of appsToCheck) { const result = await LaunchNavigator.isAppAvailable({ app }); if (result.available) { selectedApp = app; break; } } if (selectedApp) { await LaunchNavigator.navigate({ destination: [37.7749, -122.4194], options: { app: selectedApp } }); } else { console.log('No supported navigation apps found'); } ``` ### User Selection of Navigation App [Section titled “User Selection of Navigation App”](#user-selection-of-navigation-app) ```typescript // Get available apps const { apps } = await LaunchNavigator.getAvailableApps(); if (apps.length === 0) { alert('No navigation apps available'); return; } // Show app selection to user (pseudo-code) const selectedApp = await showAppSelectionDialog(apps); // Navigate with selected app await LaunchNavigator.navigate({ destination: [37.7749, -122.4194], options: { app: selectedApp } }); ``` ### Navigation with Different Transport Modes [Section titled “Navigation with Different Transport Modes”](#navigation-with-different-transport-modes) ```typescript // Driving directions await LaunchNavigator.navigate({ destination: [37.7749, -122.4194], options: { transportMode: TransportMode.DRIVING } }); // Walking directions await LaunchNavigator.navigate({ destination: [37.7749, -122.4194], options: { transportMode: TransportMode.WALKING } }); // Public transit directions await LaunchNavigator.navigate({ destination: [37.7749, -122.4194], options: { transportMode: TransportMode.TRANSIT } }); ``` ## Best Practices [Section titled “Best Practices”](#best-practices) * Always check app availability before attempting navigation * Provide fallback options when preferred apps aren’t available * Use meaningful app display names for user selection * Handle errors gracefully with user-friendly messages * Consider user preferences for default navigation apps * Test on both iOS and Android devices * Implement proper error handling for invalid coordinates ## Error Handling [Section titled “Error Handling”](#error-handling) ```typescript try { await LaunchNavigator.navigate({ destination: [37.7749, -122.4194], options: { app: 'google_maps', transportMode: TransportMode.DRIVING } }); } catch (error) { console.error('Navigation failed:', error); // Handle error - app not available, invalid coordinates, etc. alert('Unable to launch navigation. Please check your coordinates and try again.'); } ``` ## Use Cases [Section titled “Use Cases”](#use-cases) * **Location-based services**: Navigate users to points of interest * **Delivery apps**: Guide drivers to delivery locations * **Event apps**: Direct attendees to venue locations * **Real estate apps**: Navigate to property locations * **Travel apps**: Guide tourists to attractions * **Service apps**: Direct field workers to job sites # @capgo/capacitor-light-sensor > Read ambient light levels in lux from the device's light sensor with real-time updates and configurable sampling rates. Requires Capacitor 8+. Real-time Updates Receive continuous light sensor readings with configurable update intervals Event-driven API Subscribe to light changes with easy-to-use event listeners Configurable Sampling Adjust update frequency from 200ms to custom intervals Battery Efficient Start and stop sensor on demand to conserve battery Comprehensive Documentation Check the [Documentation](/docs/plugins/light-sensor/getting-started/) to master the plugin in just a few minutes. # Getting Started > Learn how to install and use the Light Sensor plugin to read ambient light levels in your Capacitor app. 1. **Install the package** * npm ```sh npm i @capgo/capacitor-light-sensor ``` * pnpm ```sh pnpm add @capgo/capacitor-light-sensor ``` * yarn ```sh yarn add @capgo/capacitor-light-sensor ``` * bun ```sh bun add @capgo/capacitor-light-sensor ``` 2. **Sync with native projects** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Android Configuration [Section titled “Android Configuration”](#android-configuration) For high sampling rates (below 200ms) on Android 12+, add the HIGH\_SAMPLING\_RATE\_SENSORS permission to your `AndroidManifest.xml`: ```xml ``` ## Usage [Section titled “Usage”](#usage) Import the plugin and use its methods to read light sensor data: ```typescript import { LightSensor } from '@capgo/capacitor-light-sensor'; // Check if sensor is available const checkAvailable = async () => { const { available } = await LightSensor.isAvailable(); console.log('Light sensor available:', available); return available; }; // Start listening to light changes const startListening = async () => { // Add listener first await LightSensor.addListener('lightSensorChange', (data) => { console.log('Illuminance:', data.illuminance, 'lux'); console.log('Timestamp:', data.timestamp); }); // Start the sensor await LightSensor.start({ updateInterval: 500 }); }; // Stop listening const stopListening = async () => { await LightSensor.stop(); await LightSensor.removeAllListeners(); }; ``` ## API Reference [Section titled “API Reference”](#api-reference) ### isAvailable() [Section titled “isAvailable()”](#isavailable) Check if the light sensor is available on the current device. ```typescript const { available } = await LightSensor.isAvailable(); // Returns: { available: boolean } // Note: Always returns false on iOS (light sensor API not available) ``` ### start(options?) [Section titled “start(options?)”](#startoptions) Start listening to light sensor updates. ```typescript interface StartOptions { updateInterval?: number; // milliseconds, default: 200 } await LightSensor.start({ updateInterval: 500 }); ``` ### stop() [Section titled “stop()”](#stop) Stop listening to light sensor updates. ```typescript await LightSensor.stop(); ``` ### addListener(eventName, callback) [Section titled “addListener(eventName, callback)”](#addlistenereventname-callback) Add a listener for light sensor change events. ```typescript interface LightSensorMeasurement { illuminance: number; // light level in lux timestamp: number; // seconds since epoch } const handle = await LightSensor.addListener('lightSensorChange', (data) => { console.log('Light level:', data.illuminance, 'lux'); }); // To remove this specific listener later: handle.remove(); ``` ### removeAllListeners() [Section titled “removeAllListeners()”](#removealllisteners) Remove all listeners for light sensor events. ```typescript await LightSensor.removeAllListeners(); ``` ### checkPermissions() [Section titled “checkPermissions()”](#checkpermissions) Check permission status for high sampling rate sensors. ```typescript const status = await LightSensor.checkPermissions(); // Returns: { highSamplingRate: 'granted' | 'denied' | 'prompt' | 'prompt-with-rationale' } ``` ### requestPermissions() [Section titled “requestPermissions()”](#requestpermissions) Request permission for high sampling rate sensors. ```typescript const status = await LightSensor.requestPermissions(); ``` ### getPluginVersion() [Section titled “getPluginVersion()”](#getpluginversion) Get the current plugin version. ```typescript const { version } = await LightSensor.getPluginVersion(); ``` ## Complete Example [Section titled “Complete Example”](#complete-example) ```typescript import { LightSensor, LightSensorMeasurement } from '@capgo/capacitor-light-sensor'; import type { PluginListenerHandle } from '@capacitor/core'; export class LightSensorService { private listener: PluginListenerHandle | null = null; private isRunning = false; async init(): Promise { const { available } = await LightSensor.isAvailable(); if (!available) { console.warn('Light sensor not available on this device'); return false; } return true; } async start( callback: (illuminance: number) => void, intervalMs: number = 500 ) { if (this.isRunning) return; // Check for high sampling rate permission if needed if (intervalMs < 200) { const status = await LightSensor.checkPermissions(); if (status.highSamplingRate !== 'granted') { await LightSensor.requestPermissions(); } } this.listener = await LightSensor.addListener( 'lightSensorChange', (data: LightSensorMeasurement) => { callback(data.illuminance); } ); await LightSensor.start({ updateInterval: intervalMs }); this.isRunning = true; } async stop() { if (!this.isRunning) return; await LightSensor.stop(); if (this.listener) { this.listener.remove(); this.listener = null; } this.isRunning = false; } // Utility: Get light level description getLightDescription(lux: number): string { if (lux < 1) return 'Very dark'; if (lux < 50) return 'Dark'; if (lux < 200) return 'Dim'; if (lux < 400) return 'Normal indoor'; if (lux < 1000) return 'Bright indoor'; if (lux < 10000) return 'Overcast daylight'; if (lux < 25000) return 'Daylight'; return 'Direct sunlight'; } } // Usage in a component const lightService = new LightSensorService(); async function setupLightSensor() { const available = await lightService.init(); if (available) { await lightService.start((illuminance) => { const description = lightService.getLightDescription(illuminance); console.log(`Light: ${illuminance} lux (${description})`); // Example: Auto-adjust UI theme based on light if (illuminance < 50) { // Switch to dark theme } else { // Switch to light theme } }); } } // Cleanup when done async function cleanup() { await lightService.stop(); } ``` ## Best Practices [Section titled “Best Practices”](#best-practices) 1. **Check availability first** ```typescript const { available } = await LightSensor.isAvailable(); if (!available) { // Provide fallback behavior } ``` 2. **Stop when not needed** Always stop the sensor when it’s not in use to conserve battery: ```typescript // When component unmounts or app goes to background await LightSensor.stop(); await LightSensor.removeAllListeners(); ``` 3. **Use appropriate intervals** * For UI updates: 500ms - 1000ms is usually sufficient * For games/real-time: 200ms * Faster than 200ms requires HIGH\_SAMPLING\_RATE\_SENSORS permission 4. **Handle errors gracefully** ```typescript try { await LightSensor.start({ updateInterval: 500 }); } catch (error) { console.error('Failed to start light sensor:', error); } ``` ## Platform Notes [Section titled “Platform Notes”](#platform-notes) ### Android [Section titled “Android”](#android) * Uses Android’s TYPE\_LIGHT sensor * Minimum interval of 200ms unless HIGH\_SAMPLING\_RATE\_SENSORS permission is granted (Android 12+) * Returns illuminance in lux ### iOS [Section titled “iOS”](#ios) * **Not supported** - iOS does not expose the ambient light sensor API to third-party apps * `isAvailable()` always returns `false` ### Web [Section titled “Web”](#web) * Limited support depending on browser * Uses AmbientLightSensor API where available * Requires HTTPS and user permission ## Requirements [Section titled “Requirements”](#requirements) * **Capacitor 8.0.0** or higher # @capgo/capacitor-live-reload > Connect your Capacitor app to your local development server for instant hot reloading and faster iteration during development. Hot Module Replacement Instant updates without rebuilding your app Remote Dev Server Connect to Vite or any compatible dev server WebSocket Connection Real-time communication for live updates Comprehensive Documentation Check the [Documentation](/docs/plugins/live-reload/getting-started/) to set up live reload. # Getting Started > Learn how to set up live reload for faster development with your Capacitor app. 1. **Install the package** * npm ```sh npm i @capgo/capacitor-live-reload ``` * pnpm ```sh pnpm add @capgo/capacitor-live-reload ``` * yarn ```sh yarn add @capgo/capacitor-live-reload ``` * bun ```sh bun add @capgo/capacitor-live-reload ``` 2. **Sync with native projects** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Setup [Section titled “Setup”](#setup) Configure your Vite dev server to work with live reload. You need to: 1. Disable the built-in HMR client 2. Forward reload events over a dedicated WebSocket endpoint ## Usage [Section titled “Usage”](#usage) ```typescript import { LiveReload } from '@capgo/capacitor-live-reload'; // Configure the dev server await LiveReload.configureServer({ url: 'http://localhost:5173', websocketPath: '/capgo-livereload', autoReconnect: true, reconnectInterval: 2000 }); // Connect to the dev server await LiveReload.connect(); // Listen for reload events LiveReload.addListener('reloadEvent', (event) => { console.log('Reload event:', event); if (event.type === 'full-reload') { console.log('Full page reload triggered'); } else if (event.type === 'file-update') { console.log('File updated:', event.file); } }); // Listen for connection status changes LiveReload.addListener('statusChange', (status) => { console.log('Connection status:', status.connected); console.log('Server URL:', status.url); }); ``` ## API Reference [Section titled “API Reference”](#api-reference) ### configureServer(options) [Section titled “configureServer(options)”](#configureserveroptions) Store remote dev server settings for subsequent connections. ```typescript const status = await LiveReload.configureServer({ url: 'http://192.168.1.100:5173', websocketPath: '/capgo-livereload', headers: { 'Authorization': 'Bearer token' }, autoReconnect: true, reconnectInterval: 2000 }); ``` **Parameters:** * `url` (string): Base URL for the dev server (e.g., `http://dev.local:5173`) * `websocketPath` (string, optional): WebSocket path override (default: `/ws`) * `headers` (Record\, optional): Extra headers for WebSocket connection * `autoReconnect` (boolean, optional): Auto-reconnect on disconnect (default: `true`) * `reconnectInterval` (number, optional): Delay between reconnect attempts in ms (default: `2000`) **Returns:** `LiveReloadStatus` with connection info ### connect() [Section titled “connect()”](#connect) Establish a WebSocket connection if one is not already active. ```typescript const status = await LiveReload.connect(); console.log('Connected:', status.connected); ``` **Returns:** Current connection status ### disconnect() [Section titled “disconnect()”](#disconnect) Close the current WebSocket connection and disable auto reconnect. ```typescript await LiveReload.disconnect(); ``` ### getStatus() [Section titled “getStatus()”](#getstatus) Get the current connection status. ```typescript const status = await LiveReload.getStatus(); console.log('Connected:', status.connected); console.log('URL:', status.url); ``` ### reload() [Section titled “reload()”](#reload) Manually trigger a full reload of the Capacitor WebView. ```typescript await LiveReload.reload(); ``` ### reloadFile(options) [Section titled “reloadFile(options)”](#reloadfileoptions) Reload a single file/module (falls back to full reload if not supported). ```typescript await LiveReload.reloadFile({ path: '/src/components/MyComponent.tsx', hash: 'abc123' }); ``` ### addListener(‘reloadEvent’, callback) [Section titled “addListener(‘reloadEvent’, callback)”](#addlistenerreloadevent-callback) Listen to incoming reload events from the server. ```typescript const handle = await LiveReload.addListener('reloadEvent', (event) => { switch (event.type) { case 'full-reload': console.log('Full reload requested'); break; case 'file-update': console.log('File updated:', event.file?.path); break; case 'error': console.error('Error:', event.message); break; } }); // Remove listener when done await handle.remove(); ``` ### addListener(‘statusChange’, callback) [Section titled “addListener(‘statusChange’, callback)”](#addlistenerstatuschange-callback) Listen to socket status changes. ```typescript await LiveReload.addListener('statusChange', (status) => { console.log(status.connected ? 'Connected' : 'Disconnected'); }); ``` ### removeAllListeners() [Section titled “removeAllListeners()”](#removealllisteners) Remove all registered listeners. ```typescript await LiveReload.removeAllListeners(); ``` ## Complete Example [Section titled “Complete Example”](#complete-example) ```typescript import { LiveReload } from '@capgo/capacitor-live-reload'; export class DevServer { private connected = false; async initialize() { // Only enable in development if (process.env.NODE_ENV !== 'development') { return; } try { // Configure server await LiveReload.configureServer({ url: 'http://192.168.1.100:5173', websocketPath: '/capgo-livereload', autoReconnect: true, reconnectInterval: 3000 }); // Set up listeners before connecting await LiveReload.addListener('reloadEvent', this.handleReloadEvent.bind(this)); await LiveReload.addListener('statusChange', this.handleStatusChange.bind(this)); // Connect await LiveReload.connect(); } catch (error) { console.error('Failed to initialize live reload:', error); } } private handleReloadEvent(event: any) { console.log('Reload event received:', event.type); switch (event.type) { case 'full-reload': this.performFullReload(); break; case 'file-update': this.handleFileUpdate(event.file); break; case 'error': console.error('Server error:', event.message); break; case 'connected': console.log('Server connected'); break; case 'disconnected': console.log('Server disconnected'); break; } } private handleStatusChange(status: any) { this.connected = status.connected; console.log(`Live reload ${status.connected ? 'connected' : 'disconnected'}`); } private performFullReload() { console.log('Performing full page reload...'); window.location.reload(); } private handleFileUpdate(file: any) { console.log('File updated:', file?.path); // HMR will handle this automatically in most cases } async disconnect() { await LiveReload.disconnect(); await LiveReload.removeAllListeners(); this.connected = false; } isConnected(): boolean { return this.connected; } } ``` ## Vite Configuration Example [Section titled “Vite Configuration Example”](#vite-configuration-example) Configure your Vite server to work with the live reload plugin: vite.config.ts ```typescript import { defineConfig } from 'vite'; export default defineConfig({ server: { host: '0.0.0.0', // Allow connections from network port: 5173, hmr: { // Custom WebSocket path for live reload path: '/capgo-livereload', } } }); ``` ## Best Practices [Section titled “Best Practices”](#best-practices) 1. **Only use in development** ```typescript if (import.meta.env.DEV) { await LiveReload.configureServer({ url: 'http://localhost:5173', websocketPath: '/capgo-livereload' }); await LiveReload.connect(); } ``` 2. **Use your local IP for mobile testing** ```typescript const devServerUrl = process.env.VITE_DEV_SERVER_URL || 'http://192.168.1.100:5173'; await LiveReload.configureServer({ url: devServerUrl }); ``` 3. **Handle connection errors gracefully** ```typescript try { await LiveReload.connect(); } catch (error) { console.warn('Could not connect to dev server, using production build'); } ``` 4. **Clean up on app exit** ```typescript window.addEventListener('beforeunload', async () => { await LiveReload.disconnect(); }); ``` 5. **Show connection status in UI** ```typescript LiveReload.addListener('statusChange', (status) => { // Show indicator in dev builds updateDevIndicator(status.connected); }); ``` ## Platform Notes [Section titled “Platform Notes”](#platform-notes) ### iOS [Section titled “iOS”](#ios) * Works with iOS 11.0+ * Ensure dev server is accessible from your device’s network * May need to configure firewall to allow connections ### Android [Section titled “Android”](#android) * Works with Android 5.0 (API 21)+ * Use `adb reverse` for localhost connections: ```bash adb reverse tcp:5173 tcp:5173 ``` ### Web [Section titled “Web”](#web) * Full support for web platform * Direct WebSocket connection to Vite dev server ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) **Connection fails:** * Verify dev server is running * Check that device and computer are on same network * Ensure firewall allows connections on the port * Use IP address instead of localhost for mobile devices **Slow reload:** * Check network speed * Reduce reconnect interval * Optimize Vite build configuration **WebSocket errors:** * Verify websocketPath matches Vite config * Check for port conflicts * Ensure headers are correct if using authentication # @capgo/capacitor-llm > Run LLM models directly on device with native Apple Intelligence integration and hardware acceleration. On-Device Privacy Run LLM models directly on device for privacy and offline capabilities 🤖 Apple Intelligence Native Apple Intelligence integration on iOS 26.0+ 🍎 Multiple Formats Support for .task and .litertlm model formats 📦 Hardware Acceleration Fast inference with native hardware acceleration ⚡ Streaming Responses Real-time streaming responses with event listeners 🔄 Getting Started Check the [Getting Started Guide](/docs/plugins/llm/getting-started/) to install and configure the plugin. # Getting Started > Learn how to install and use the Capacitor LLM plugin to run AI models locally on iOS and Android. ## Installation [Section titled “Installation”](#installation) * npm ```bash npm install @capgo/capacitor-llm npx cap sync ``` * yarn ```bash yarn add @capgo/capacitor-llm npx cap sync ``` * pnpm ```bash pnpm add @capgo/capacitor-llm npx cap sync ``` * bun ```bash bun add @capgo/capacitor-llm npx cap sync ``` ## Platform Configuration [Section titled “Platform Configuration”](#platform-configuration) ### iOS Configuration [Section titled “iOS Configuration”](#ios-configuration) * **iOS 26.0+**: Uses Apple Intelligence by default (no model needed) - **Recommended** * **iOS < 26.0**: Requires MediaPipe custom models (experimental, may have compatibility issues) For custom models on older iOS versions, place model files in your iOS app bundle through Xcode’s “Copy Bundle Resources”. ### Android Configuration [Section titled “Android Configuration”](#android-configuration) Place model files in your Android assets folder: ```plaintext android/app/src/main/assets/ ``` You need **both** files for Android: * `.task` file (main model) * `.litertlm` file (companion file) Download from [Kaggle Gemma models](https://www.kaggle.com/models/google/gemma) → “LiteRT (formerly TFLite)” tab ## Recommended Models [Section titled “Recommended Models”](#recommended-models) ### For Android (Gemma-3 Models) [Section titled “For Android (Gemma-3 Models)”](#for-android-gemma-3-models) * **Gemma 3 270M** - Smallest, most efficient for mobile (\~240-400MB) - **Recommended** * **Gemma 3 1B** - Larger text generation model (\~892MB-1.5GB) Download from [Kaggle Gemma models](https://www.kaggle.com/models/google/gemma) → Click “LiteRT (formerly TFLite)” tab ### For iOS [Section titled “For iOS”](#for-ios) * **Apple Intelligence** (iOS 26.0+) - Built-in, no download needed - **Recommended** * **Gemma-2 2B** (experimental) - May have compatibility issues with `.task` format For custom iOS models, download from [Hugging Face MediaPipe models](https://huggingface.co/collections/google/mediapipe-668392ead2d6768e82fb3b87) ## Usage [Section titled “Usage”](#usage) Import the plugin and initialize: ```typescript import { CapgoLLM } from '@capgo/capacitor-llm'; import { Capacitor } from '@capacitor/core'; // Check if LLM is ready const { readiness } = await CapgoLLM.getReadiness(); console.log('LLM readiness:', readiness); // Set the model based on platform const platform = Capacitor.getPlatform(); if (platform === 'ios') { // iOS: Use Apple Intelligence (default) await CapgoLLM.setModel({ path: 'Apple Intelligence' }); } else { // Android: Use MediaPipe model await CapgoLLM.setModel({ path: '/android_asset/gemma-3-270m-it-int8.task' }); } // Create a chat session const { id: chatId } = await CapgoLLM.createChat(); // Listen for AI responses CapgoLLM.addListener('textFromAi', (event) => { console.log('AI response:', event.text); }); // Listen for completion CapgoLLM.addListener('aiFinished', (event) => { console.log('AI completed response'); }); // Send a message await CapgoLLM.sendMessage({ chatId, message: 'Hello! How are you today?' }); ``` ## Advanced Features [Section titled “Advanced Features”](#advanced-features) ### Download Models [Section titled “Download Models”](#download-models) ```typescript // Download a model from URL await CapgoLLM.downloadModel({ url: 'https://example.com/model.task', filename: 'model.task' }); // For Android, download both .task and .litertlm files await CapgoLLM.downloadModel({ url: 'https://example.com/gemma-3-270m-it-int8.task', companionUrl: 'https://example.com/gemma-3-270m-it-int8.litertlm', filename: 'gemma-3-270m-it-int8.task' }); // Listen for download progress CapgoLLM.addListener('downloadProgress', (event) => { console.log(`Download progress: ${event.progress}%`); console.log(`Downloaded: ${event.downloadedBytes} / ${event.totalBytes}`); }); ``` ### Model Management [Section titled “Model Management”](#model-management) ```typescript // Set a specific model with configuration await CapgoLLM.setModel({ path: '/android_asset/gemma-3-270m-it-int8.task', maxTokens: 2048, topk: 40, temperature: 0.8 }); // Check readiness const { readiness } = await CapgoLLM.getReadiness(); if (readiness === 'ready') { // Model is loaded and ready } // Listen for readiness changes CapgoLLM.addListener('readinessChange', (event) => { console.log('Readiness changed:', event.readiness); }); ``` ## API Methods [Section titled “API Methods”](#api-methods) ### createChat() [Section titled “createChat()”](#createchat) Create a new chat session. ```typescript const { id: chatId } = await CapgoLLM.createChat(); ``` **Returns:** `Promise<{ id: string; instructions?: string }>` ### sendMessage(…) [Section titled “sendMessage(…)”](#sendmessage) Send a message to the LLM. ```typescript await CapgoLLM.sendMessage({ chatId: 'chat-id', message: 'What is the weather like?' }); ``` | Param | Type | Description | | ------------- | -------- | --------------- | | **`chatId`** | `string` | Chat session ID | | **`message`** | `string` | Message to send | ### getReadiness() [Section titled “getReadiness()”](#getreadiness) Check if the LLM is ready to use. ```typescript const { readiness } = await CapgoLLM.getReadiness(); ``` **Returns:** `Promise<{ readiness: string }>` Possible values: * `ready` - Model is loaded and ready * `loading` - Model is being loaded * `not_ready` - Model not yet loaded * `error` - Error loading model ### setModel(…) [Section titled “setModel(…)”](#setmodel) Set the model configuration. ```typescript // iOS: Use Apple Intelligence (recommended) await CapgoLLM.setModel({ path: 'Apple Intelligence' }); // iOS: Use custom MediaPipe model (experimental) await CapgoLLM.setModel({ path: 'Gemma2-2B-IT_multi-prefill-seq_q8_ekv1280', modelType: 'task', maxTokens: 1280 }); // Android: Use MediaPipe model await CapgoLLM.setModel({ path: '/android_asset/gemma-3-270m-it-int8.task', maxTokens: 2048, topk: 40, temperature: 0.8 }); ``` | Param | Type | Description | | ----------------- | -------- | -------------------------------------------------- | | **`path`** | `string` | Model path or “Apple Intelligence” for iOS system | | **`modelType`** | `string` | Optional: Model file type (e.g., “task”, “bin”) | | **`maxTokens`** | `number` | Optional: Maximum tokens the model handles | | **`topk`** | `number` | Optional: Number of tokens considered at each step | | **`temperature`** | `number` | Optional: Randomness in generation (0.0-1.0) | | **`randomSeed`** | `number` | Optional: Random seed for generation | ### downloadModel(…) [Section titled “downloadModel(…)”](#downloadmodel) Download a model from URL and save to device storage. ```typescript await CapgoLLM.downloadModel({ url: 'https://example.com/gemma-3-270m-it-int8.task', companionUrl: 'https://example.com/gemma-3-270m-it-int8.litertlm', filename: 'gemma-3-270m-it-int8.task' }); ``` | Param | Type | Description | | ------------------ | -------- | -------------------------------------------- | | **`url`** | `string` | URL to download from | | **`companionUrl`** | `string` | Optional: URL for companion file (.litertlm) | | **`filename`** | `string` | Optional: Filename to save as | **Returns:** `Promise<{ path: string; companionPath?: string }>` ## Events [Section titled “Events”](#events) ### textFromAi [Section titled “textFromAi”](#textfromai) Fired when AI generates text (streaming response). ```typescript CapgoLLM.addListener('textFromAi', (event) => { console.log('AI text:', event.text); console.log('Chat ID:', event.chatId); console.log('Is chunk:', event.isChunk); }); ``` **Event Data:** * `text` (string) - Incremental text chunk from AI * `chatId` (string) - Chat session ID * `isChunk` (boolean) - Whether this is a complete chunk or partial streaming data ### aiFinished [Section titled “aiFinished”](#aifinished) Fired when AI completes response. ```typescript CapgoLLM.addListener('aiFinished', (event) => { console.log('Completed for chat:', event.chatId); }); ``` **Event Data:** * `chatId` (string) - Chat session ID ### downloadProgress [Section titled “downloadProgress”](#downloadprogress) Fired during model download to report progress. ```typescript CapgoLLM.addListener('downloadProgress', (event) => { console.log('Progress:', event.progress, '%'); console.log('Downloaded:', event.downloadedBytes, '/', event.totalBytes); }); ``` **Event Data:** * `progress` (number) - Percentage of download completed (0-100) * `downloadedBytes` (number) - Bytes downloaded so far * `totalBytes` (number) - Total bytes to download ### readinessChange [Section titled “readinessChange”](#readinesschange) Fired when the readiness status of the LLM changes. ```typescript CapgoLLM.addListener('readinessChange', (event) => { console.log('Readiness changed to:', event.readiness); }); ``` **Event Data:** * `readiness` (string) - The new readiness status ## Complete Example [Section titled “Complete Example”](#complete-example) ```typescript import { CapgoLLM } from '@capgo/capacitor-llm'; import { Capacitor } from '@capacitor/core'; class AIService { private chatId: string | null = null; private messageBuffer: string = ''; async initialize() { // Set up model based on platform const platform = Capacitor.getPlatform(); if (platform === 'ios') { // iOS: Use Apple Intelligence (recommended) await CapgoLLM.setModel({ path: 'Apple Intelligence' }); } else { // Android: Use MediaPipe model await CapgoLLM.setModel({ path: '/android_asset/gemma-3-270m-it-int8.task', maxTokens: 2048, topk: 40, temperature: 0.8 }); } // Wait for model to be ready let isReady = false; while (!isReady) { const { readiness } = await CapgoLLM.getReadiness(); if (readiness === 'ready') { isReady = true; } else if (readiness === 'error') { throw new Error('Failed to load model'); } await new Promise(resolve => setTimeout(resolve, 500)); } // Create chat session const { id } = await CapgoLLM.createChat(); this.chatId = id; // Set up event listeners this.setupListeners(); } private setupListeners() { CapgoLLM.addListener('textFromAi', (event) => { if (event.chatId === this.chatId) { this.messageBuffer += event.text; this.onTextReceived(event.text); } }); CapgoLLM.addListener('aiFinished', (event) => { if (event.chatId === this.chatId) { this.onMessageComplete(this.messageBuffer); this.messageBuffer = ''; } }); } async sendMessage(message: string) { if (!this.chatId) { throw new Error('Chat not initialized'); } await CapgoLLM.sendMessage({ chatId: this.chatId, message }); } onTextReceived(text: string) { // Update UI with streaming text console.log('Received:', text); } onMessageComplete(fullMessage: string) { // Handle complete message console.log('Complete message:', fullMessage); } } // Usage const ai = new AIService(); await ai.initialize(); await ai.sendMessage('Tell me about AI'); ``` ## Platform Support [Section titled “Platform Support”](#platform-support) | Platform | Supported | Requirements | | -------- | --------- | ---------------------------------------- | | iOS | ✅ | iOS 13.0+ (26.0+ for Apple Intelligence) | | Android | ✅ | API 24+ | | Web | ❌ | Not supported | ## Best Practices [Section titled “Best Practices”](#best-practices) 1. **Model Selection**: Choose models based on device capabilities * Use 270M for most mobile devices * Use 1B for high-end devices with more RAM * Test performance on target devices 2. **Memory Management**: Clear chat sessions when done ```typescript // Create new chat for new conversations const { id } = await CapacitorLLM.createChat(); ``` 3. **Error Handling**: Always check readiness before use ```typescript const { readiness } = await CapacitorLLM.getReadiness(); if (readiness !== 'ready') { // Handle not ready state } ``` 4. **Streaming UI**: Update UI incrementally with streaming text * Show text as it arrives via `onAiText` * Mark complete with `onAiCompletion` 5. **Model Download**: Download models during app setup, not on first use ```typescript // During app initialization await CapacitorLLM.downloadModel({ url: 'https://your-cdn.com/model.task', filename: 'model.task' }); ``` ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) ### Model not loading [Section titled “Model not loading”](#model-not-loading) * Verify model file is in correct location * Check model format matches platform (.gguf for iOS, .task for Android) * Ensure sufficient device storage ### Poor performance [Section titled “Poor performance”](#poor-performance) * Try smaller model (270M instead of 1B) * Close other apps to free memory * Test on actual device, not simulator ### No responses [Section titled “No responses”](#no-responses) * Check readiness status is ‘ready’ * Verify event listeners are set up before sending messages * Check console for errors ## Resources [Section titled “Resources”](#resources) * [GitHub Repository](https://github.com/Cap-go/capacitor-llm) * [Gemma Models](https://ai.google.dev/gemma) * [Apple Intelligence](https://developer.apple.com/machine-learning/) * [MediaPipe](https://developers.google.com/mediapipe) # @capgo/capacitor-media-session > Publish track metadata, react to play/pause events, and sync playback state with native media controls. The Media Session plugin bridges Capacitor with the Media Session API so your audio and video players feel native everywhere. Rich metadata Provide album art, artist, and title for lock-screen and notification controls. Playback state sync Keep play / pause buttons and progress indicators aligned with your player. Action callbacks Handle headset buttons and OS media actions from your Capacitor code. Position reporting Push duration, position, and rate so scrubbing works fluidly. Jump to the getting started guide to wire metadata updates into your player lifecycle. # Getting Started > Synchronize your player UI with native media controls using the Media Session plugin. 1. **Install the plugin** * npm ```sh npm i @capgo/capacitor-media-session ``` * pnpm ```sh pnpm add @capgo/capacitor-media-session ``` * yarn ```sh yarn add @capgo/capacitor-media-session ``` * bun ```sh bun add @capgo/capacitor-media-session ``` 2. **Sync platforms** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` > ℹ️ The Media Session API is available on Android (Chrome based browsers, WebView) and many desktop browsers. On unsupported platforms the calls resolve without taking effect, so guard your UI accordingly. ## Publish metadata [Section titled “Publish metadata”](#publish-metadata) ```typescript import { MediaSession } from '@capgo/capacitor-media-session'; await MediaSession.setMetadata({ title: 'Weekly Tech Podcast', artist: 'Capgo Studio', album: 'Season 2', artwork: [ { src: 'https://cdn.example.com/covers/s02e05-512.png', sizes: '512x512', type: 'image/.png' }, ], }); ``` ## Update playback state [Section titled “Update playback state”](#update-playback-state) ```typescript await MediaSession.setPlaybackState({ playbackState: 'playing' }); // Pause later if needed await MediaSession.setPlaybackState({ playbackState: 'paused' }); ``` ## Handle media actions [Section titled “Handle media actions”](#handle-media-actions) ```typescript const pauseHandler = async () => { player.pause(); await MediaSession.setPlaybackState({ playbackState: 'paused' }); }; await MediaSession.setActionHandler({ action: 'pause' }, pauseHandler); await MediaSession.setActionHandler({ action: 'play' }, async () => { await player.play(); await MediaSession.setPlaybackState({ playbackState: 'playing' }); }); // Remove a handler when you dispose the player await MediaSession.setActionHandler({ action: 'pause' }, null); ``` ## Keep position in sync [Section titled “Keep position in sync”](#keep-position-in-sync) ```typescript const updatePosition = async () => { await MediaSession.setPositionState({ duration: player.duration, position: player.currentTime, playbackRate: player.playbackRate, }); }; player.ontimeupdate = updatePosition; player.onratechange = updatePosition; ``` ## Recommendations [Section titled “Recommendations”](#recommendations) * Refresh metadata when the track changes so notifications and smart speakers stay up to date. * Throttle position updates (e.g. every 250 ms) to avoid spamming the native layer. * Always remove action handlers when tearing down a player instance to prevent accidental leakage. # @capgo/capacitor-mqtt > Real-time publish/subscribe integration for IoT, telemetry, and event-driven mobile apps. MQTT Messaging Publish and subscribe to topics for reliable real-time communication 📡 Cross-platform Use one API on iOS, Android, and Web 📱 Broker Connections Configure secure MQTT brokers with client IDs, credentials, and TLS options 🔌 Background Capabilities Handle reconnection and long-running message sessions for production use 🚀 Easy Setup Quick install + sync flow for immediate testing ✅ Developer Experience Learn publish/subscribe and listener patterns in minutes 📚 # Getting Started > Learn how to install and use MQTT in your Capacitor app. 1. **Install the package** * npm ```sh npm i @capgo/capacitor-mqtt ``` * pnpm ```sh pnpm add @capgo/capacitor-mqtt ``` * yarn ```sh yarn add @capgo/capacitor-mqtt ``` * bun ```sh bun add @capgo/capacitor-mqtt ``` 2. **Sync with native projects** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Usage [Section titled “Usage”](#usage) Import the plugin and connect to an MQTT broker: ```typescript import { CapacitorMqtt } from '@capgo/capacitor-mqtt'; await CapacitorMqtt.connect({ brokerUrl: 'wss://broker.hivemq.com:8884/mqtt', clientId: `device-${Date.now()}`, }); await CapacitorMqtt.subscribe({ topic: 'myapp/events', qos: 1 }); await CapacitorMqtt.publish({ topic: 'myapp/events', message: 'hello from app', qos: 1, }); ``` ## API Reference [Section titled “API Reference”](#api-reference) ### connect(options) [Section titled “connect(options)”](#connectoptions) Connect to an MQTT broker. ```typescript interface ConnectOptions { brokerUrl: string; clientId: string; username?: string; password?: string; cleanSession?: boolean; keepAliveInterval?: number; } await CapacitorMqtt.connect({ brokerUrl: 'wss://broker.hivemq.com:8884/mqtt', clientId: 'my-client-id', }); ``` ### subscribe(options) [Section titled “subscribe(options)”](#subscribeoptions) Subscribe to a topic. ```typescript await CapacitorMqtt.subscribe({ topic: 'myapp/events', qos: 1, }); ``` ### publish(options) [Section titled “publish(options)”](#publishoptions) Publish to a topic. ```typescript await CapacitorMqtt.publish({ topic: 'myapp/commands', message: JSON.stringify({ command: 'ping' }), qos: 1, }); ``` ### addListener() [Section titled “addListener()”](#addlistener) Listen for incoming messages. ```typescript const listener = await CapacitorMqtt.addListener('onMessage', (payload) => { console.log(payload.topic, payload.message); }); ``` ### disconnect() [Section titled “disconnect()”](#disconnect) Disconnect from the broker. ```typescript await CapacitorMqtt.disconnect(); ``` # @capgo/capacitor-mute > Check if a device is muted or set to silent mode. Detects mute switch on iOS and volume state on Android. Silent mode detection Detect if device is in silent/mute mode 🔇 Cross-platform Works on both iOS (mute switch) and Android (volume) 📱 Simple API Easy to use with straightforward methods 🎯 Comprehensive Documentation Check the [Documentation](/docs/plugins/mute/getting-started/) to master the plugin in just a few minutes. # Getting Started > Learn how to install and use the Mute plugin to detect silent mode state in your Capacitor app. 1. **Install the package** * npm ```sh npm i @capgo/capacitor-mute ``` * pnpm ```sh pnpm add @capgo/capacitor-mute ``` * yarn ```sh yarn add @capgo/capacitor-mute ``` * bun ```sh bun add @capgo/capacitor-mute ``` 2. **Sync with native projects** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Usage [Section titled “Usage”](#usage) Import the plugin and use its methods to check mute state: ```typescript import { CapacitorMute } from '@capgo/capacitor-mute'; // Check if device is muted const checkMuteState = async () => { const { value } = await CapacitorMute.isMuted(); if (value) { console.log('Device is muted/silent'); // Adjust app behavior for silent mode } else { console.log('Device sound is on'); } }; ``` ## API Reference [Section titled “API Reference”](#api-reference) ### isMuted() [Section titled “isMuted()”](#ismuted) Checks if the device is currently muted/in silent mode. ```typescript const result = await CapacitorMute.isMuted(); // Returns: { value: boolean } ``` ## Complete Example [Section titled “Complete Example”](#complete-example) ```typescript import { CapacitorMute } from '@capgo/capacitor-mute'; export class SoundManager { private isMuted = false; async initialize() { // Get initial mute state await this.checkMuteState(); } async checkMuteState() { try { const { value } = await CapacitorMute.isMuted(); this.isMuted = value; this.updateAppBehavior(); } catch (error) { console.error('Failed to check mute state:', error); } } private updateAppBehavior() { if (this.isMuted) { // Device is muted - adjust app behavior this.disableSoundEffects(); this.showVisualNotifications(); } else { // Device sound is on this.enableSoundEffects(); this.useAudioNotifications(); } } private disableSoundEffects() { // Disable in-app sounds console.log('Disabling sound effects'); } private enableSoundEffects() { // Enable in-app sounds console.log('Enabling sound effects'); } private showVisualNotifications() { // Use visual feedback instead of audio console.log('Using visual notifications'); } private useAudioNotifications() { // Use audio notifications console.log('Using audio notifications'); } } // Usage const soundManager = new SoundManager(); await soundManager.initialize(); // Poll periodically to check for changes setInterval(() => { soundManager.checkMuteState(); }, 5000); // Check every 5 seconds ``` ## Platform Behavior [Section titled “Platform Behavior”](#platform-behavior) ### iOS [Section titled “iOS”](#ios) * Detects the physical mute switch state * No real-time change events (polling required) * Works on iPhone and iPad with mute switch ### Android [Section titled “Android”](#android) * Checks if ringer mode is set to silent or vibrate * No change events (polling required) * Based on AudioManager ringer mode ## Best Practices [Section titled “Best Practices”](#best-practices) 1. **Poll for changes** Since the plugin doesn’t provide real-time events, poll periodically if you need to track changes: ```typescript // Check mute state periodically setInterval(async () => { const { value } = await CapacitorMute.isMuted(); if (value !== previousMuteState) { handleMuteStateChange(value); } }, 5000); ``` 2. **Respect user preferences** Always honor the mute state and adjust your app’s audio behavior accordingly. 3. **Provide visual alternatives** ```typescript const { value: isMuted } = await CapacitorMute.isMuted(); if (isMuted) { // Show visual notifications showToast('New message received'); } else { // Play sound notification playNotificationSound(); } ``` ## Use Cases [Section titled “Use Cases”](#use-cases) 1. **Game Sound Management** ```typescript const shouldPlaySound = async () => { const { value: isMuted } = await CapacitorMute.isMuted(); return !isMuted && userPreferences.soundEnabled; }; ``` 2. **Notification Handling** ```typescript const sendNotification = async (message: string) => { const { value: isMuted } = await CapacitorMute.isMuted(); if (isMuted) { // Use vibration or visual notification await Haptics.vibrate(); showBanner(message); } else { // Play notification sound await playSound('notification.mp3'); } }; ``` 3. **Video Player** ```typescript const initVideoPlayer = async () => { const { value: isMuted } = await CapacitorMute.isMuted(); videoPlayer.setMuted(isMuted); if (isMuted) { showSubtitles(true); showMuteIndicator(); } }; ``` ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) 1. **Always returns false on Android** * Check if device ringer mode is actually set to silent * Some Android devices may have different behavior 2. **No change events available** * The plugin does not provide real-time change events * Implement polling if you need to detect changes 3. **Not working on simulator** * iOS Simulator doesn’t have a mute switch * Test on real devices for accurate results # @capgo/capacitor-mux-player > Stream high-quality Mux video in fullscreen with DRM, analytics, and consistent controls across iOS, Android, and web. Deliver premium playback by wrapping the official Mux native SDKs in a simple Capacitor interface. Native SDK experience Use the same playback stack as the Mux mobile SDKs with adaptive bitrate and DRM. Signed playback Provide playback and DRM tokens for secure delivery of protected streams. Uniform API Trigger playback, dismiss the player, and listen to events with one TypeScript API. Analytics ready Pass environment keys and player names to feed Mux Data telemetry automatically. The getting started guide walks through hooking up the native SDKs, handling tokens, and responding to player events. # Getting Started > Wire up the native Mux Player SDK inside your Capacitor application. 1. **Install the plugin** * npm ```sh npm i @capgo/capacitor-mux-player ``` * pnpm ```sh pnpm add @capgo/capacitor-mux-player ``` * yarn ```sh yarn add @capgo/capacitor-mux-player ``` * bun ```sh bun add @capgo/capacitor-mux-player ``` 2. **Sync native projects** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## iOS preparation [Section titled “iOS preparation”](#ios-preparation) 1. Open the Xcode workspace under `ios/App/`. 2. Add the Swift package `https://github.com/muxinc/mux-player-swift` to your app target so the `MuxPlayerSwift` module is available. 3. Ensure the deployment target is **iOS 15+** and rebuild. ## Android preparation [Section titled “Android preparation”](#android-preparation) The Gradle module ships with the correct repositories and dependencies. If you use a custom corporate proxy, allow requests to `https://muxinc.jfrog.io/artifactory/default-maven-release-local`. ## Launch the player [Section titled “Launch the player”](#launch-the-player) ```typescript import { MuxPlayer } from '@capgo/capacitor-mux-player'; await MuxPlayer.play({ playbackId: 'your-playback-id', environmentKey: 'your-mux-data-key', title: 'Launch Announcement', subtitle: 'Filmed live at Capgo HQ', poster: 'https://stream.example.com/poster.jpg', }); ``` ## Listen to player events [Section titled “Listen to player events”](#listen-to-player-events) ```typescript const readyHandle = await MuxPlayer.addListener('ready', ({ playerName }) => { console.log('Mux player ready', playerName); }); const errorHandle = await MuxPlayer.addListener('error', ({ message }) => { console.error('Mux player error:', message); }); // Clean up after dismissing the player const dismissPlayer = async () => { await MuxPlayer.dismiss(); await readyHandle.remove(); await errorHandle.remove(); }; ``` ## Token-based playback [Section titled “Token-based playback”](#token-based-playback) ```typescript await MuxPlayer.play({ playbackId: 'signed-playback-id', playbackToken: signedPlaybackToken, drmToken: signedDrmToken, // Optional if you enabled DRM policies autoPlay: true, startTime: 120, // begin at 2 minutes }); ``` ## React to player lifecycle [Section titled “React to player lifecycle”](#react-to-player-lifecycle) ```typescript const { active } = await MuxPlayer.isActive(); if (active) { console.log('Player currently visible'); } // Remove all listener registrations when unmounting await MuxPlayer.removeAllListeners(); ``` ## Tips [Section titled “Tips”](#tips) * Generate signed playback and DRM tokens on your backend using the Mux signing keys. * Pass a `playerName` to separate analytics when running multiple players in your app. * Combine with `enableSmartCache` to improve offline resilience on supported platforms. # @capgo/native-audio > Play audio with native performance, low latency, and concurrent playback. Perfect for games, sound effects, and background music. Low latency Native audio APIs for instant playback 🚀 Concurrent playback Play multiple sounds simultaneously 🎵 Background audio Continue playback when app is in background 🎧 Comprehensive Documentation Check the [Documentation](/docs/plugins/native-audio/getting-started/) to master the plugin in just a few minutes. # Getting Started > Learn how to install and use the Native Audio plugin for high-performance audio playback in your Capacitor app. 1. **Install the package** * npm ```sh npm i @capgo/native-audio ``` * pnpm ```sh pnpm add @capgo/native-audio ``` * yarn ```sh yarn add @capgo/native-audio ``` * bun ```sh bun add @capgo/native-audio ``` 2. **Sync with native projects** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` 3. **Add audio files** Place your audio files in the appropriate platform folders: * **iOS**: `ios/App/App/sounds/` * **Android**: `android/app/src/main/res/raw/` ## Usage [Section titled “Usage”](#usage) Import the plugin and preload audio files before playing: ```typescript import { NativeAudio } from '@capgo/native-audio'; // Preload audio files const preloadAudio = async () => { // Simple preload for short sounds await NativeAudio.preload({ assetId: 'click', assetPath: 'sounds/click.mp3', audioChannelNum: 1, isUrl: false }); // Complex preload for music/longer audio await NativeAudio.preloadComplex({ assetId: 'background-music', assetPath: 'sounds/background.mp3', audioChannelNum: 1, volume: 0.5, delay: 0, isUrl: false }); }; // Play audio const playSound = async () => { await NativeAudio.play({ assetId: 'click' }); }; // Play with options const playMusic = async () => { await NativeAudio.play({ assetId: 'background-music', time: 0 // Start from beginning }); }; // Loop audio const loopMusic = async () => { await NativeAudio.loop({ assetId: 'background-music' }); }; // Stop audio const stopMusic = async () => { await NativeAudio.stop({ assetId: 'background-music' }); }; // Unload when done const cleanup = async () => { await NativeAudio.unload({ assetId: 'click' }); await NativeAudio.unload({ assetId: 'background-music' }); }; ``` ## API Reference [Section titled “API Reference”](#api-reference) ### preload(options) [Section titled “preload(options)”](#preloadoptions) Preloads an audio file for simple playback (best for short sounds). ```typescript interface PreloadOptions { assetId: string; assetPath: string; audioChannelNum?: number; isUrl?: boolean; } await NativeAudio.preload({ assetId: 'sound-effect', assetPath: 'sounds/effect.mp3', audioChannelNum: 1, isUrl: false }); ``` ### preloadComplex(options) [Section titled “preloadComplex(options)”](#preloadcomplexoptions) Preloads audio with advanced options (best for music/background audio). ```typescript interface PreloadComplexOptions { assetId: string; assetPath: string; volume?: number; // 0.0 to 1.0 audioChannelNum?: number; delay?: number; isUrl?: boolean; fadeDuration?: number; } await NativeAudio.preloadComplex({ assetId: 'theme-song', assetPath: 'sounds/theme.mp3', volume: 0.7, audioChannelNum: 2, isUrl: false }); ``` ### play(options) [Section titled “play(options)”](#playoptions) Plays a preloaded audio file. ```typescript interface PlayOptions { assetId: string; time?: number; // Start time in seconds } await NativeAudio.play({ assetId: 'sound-effect', time: 0 }); ``` ### loop(options) [Section titled “loop(options)”](#loopoptions) Loops a preloaded audio file continuously. ```typescript await NativeAudio.loop({ assetId: 'background-music' }); ``` ### stop(options) [Section titled “stop(options)”](#stopoptions) Stops playing an audio file. ```typescript await NativeAudio.stop({ assetId: 'background-music' }); ``` ### pause(options) [Section titled “pause(options)”](#pauseoptions) Pauses audio playback. ```typescript await NativeAudio.pause({ assetId: 'background-music' }); ``` ### resume(options) [Section titled “resume(options)”](#resumeoptions) Resumes paused audio. ```typescript await NativeAudio.resume({ assetId: 'background-music' }); ``` ### setVolume(options) [Section titled “setVolume(options)”](#setvolumeoptions) Sets the volume for an audio asset. ```typescript interface SetVolumeOptions { assetId: string; volume: number; // 0.0 to 1.0 } await NativeAudio.setVolume({ assetId: 'background-music', volume: 0.3 }); ``` ### getDuration(options) [Section titled “getDuration(options)”](#getdurationoptions) Gets the duration of an audio file in seconds. ```typescript const { duration } = await NativeAudio.getDuration({ assetId: 'background-music' }); console.log(`Duration: ${duration} seconds`); ``` ### getCurrentTime(options) [Section titled “getCurrentTime(options)”](#getcurrenttimeoptions) Gets the current playback time in seconds. ```typescript const { currentTime } = await NativeAudio.getCurrentTime({ assetId: 'background-music' }); console.log(`Current time: ${currentTime} seconds`); ``` ### isPlaying(options) [Section titled “isPlaying(options)”](#isplayingoptions) Checks if audio is currently playing. ```typescript const { isPlaying } = await NativeAudio.isPlaying({ assetId: 'background-music' }); console.log(`Is playing: ${isPlaying}`); ``` ### unload(options) [Section titled “unload(options)”](#unloadoptions) Unloads an audio file from memory. ```typescript await NativeAudio.unload({ assetId: 'sound-effect' }); ``` ## Advanced Usage [Section titled “Advanced Usage”](#advanced-usage) ### Sound Manager Class [Section titled “Sound Manager Class”](#sound-manager-class) ```typescript import { NativeAudio } from '@capgo/native-audio'; export class SoundManager { private sounds: Map = new Map(); private volume = 1.0; async init() { // Preload all sounds await this.preloadSound('click', 'sounds/click.mp3'); await this.preloadSound('success', 'sounds/success.mp3'); await this.preloadSound('error', 'sounds/error.mp3'); // Preload music await this.preloadMusic('background', 'sounds/background.mp3', 0.5); } private async preloadSound(id: string, path: string) { try { await NativeAudio.preload({ assetId: id, assetPath: path, audioChannelNum: 1, isUrl: false }); this.sounds.set(id, true); } catch (error) { console.error(`Failed to preload ${id}:`, error); } } private async preloadMusic(id: string, path: string, volume: number) { try { await NativeAudio.preloadComplex({ assetId: id, assetPath: path, volume, audioChannelNum: 2, isUrl: false }); this.sounds.set(id, true); } catch (error) { console.error(`Failed to preload ${id}:`, error); } } async playSound(id: string) { if (!this.sounds.has(id)) { console.warn(`Sound ${id} not preloaded`); return; } try { await NativeAudio.play({ assetId: id }); } catch (error) { console.error(`Failed to play ${id}:`, error); } } async playMusic(id: string) { if (!this.sounds.has(id)) return; try { await NativeAudio.loop({ assetId: id }); } catch (error) { console.error(`Failed to play music ${id}:`, error); } } async stopMusic(id: string) { try { await NativeAudio.stop({ assetId: id }); } catch (error) { console.error(`Failed to stop ${id}:`, error); } } async setMasterVolume(volume: number) { this.volume = Math.max(0, Math.min(1, volume)); // Update all loaded sounds for (const [id] of this.sounds) { await NativeAudio.setVolume({ assetId: id, volume: this.volume }); } } async cleanup() { for (const [id] of this.sounds) { await NativeAudio.unload({ assetId: id }); } this.sounds.clear(); } } ``` ### Loading from URLs [Section titled “Loading from URLs”](#loading-from-urls) ```typescript // Load audio from URL await NativeAudio.preloadComplex({ assetId: 'remote-audio', assetPath: 'https://example.com/audio.mp3', isUrl: true, volume: 0.8 }); ``` ### Fade In/Out Effects [Section titled “Fade In/Out Effects”](#fade-inout-effects) ```typescript const fadeIn = async (assetId: string, duration: number) => { const steps = 20; const stepDuration = duration / steps; await NativeAudio.setVolume({ assetId, volume: 0 }); await NativeAudio.play({ assetId }); for (let i = 1; i <= steps; i++) { await new Promise(resolve => setTimeout(resolve, stepDuration)); await NativeAudio.setVolume({ assetId, volume: i / steps }); } }; const fadeOut = async (assetId: string, duration: number) => { const steps = 20; const stepDuration = duration / steps; for (let i = steps; i >= 0; i--) { await NativeAudio.setVolume({ assetId, volume: i / steps }); await new Promise(resolve => setTimeout(resolve, stepDuration)); } await NativeAudio.stop({ assetId }); }; ``` ## Best Practices [Section titled “Best Practices”](#best-practices) 1. **Preload during app initialization** ```typescript import { App } from '@capacitor/app'; App.addListener('appStateChange', async ({ isActive }) => { if (isActive) { await soundManager.init(); } }); ``` 2. **Handle errors gracefully** ```typescript try { await NativeAudio.play({ assetId: 'sound' }); } catch (error) { console.log('Audio playback failed, continuing silently'); } ``` 3. **Unload unused audio** ```typescript // Unload sounds when leaving a screen ionViewWillLeave() { this.unloadScreenSounds(); } ``` 4. **Use appropriate preload methods** * `preload()` for short sound effects (< 5 seconds) * `preloadComplex()` for music and longer audio ## Platform Notes [Section titled “Platform Notes”](#platform-notes) ### iOS [Section titled “iOS”](#ios) * Supports AAC, MP3, WAV, and other Core Audio formats * Uses AVAudioPlayer for complex audio * Uses System Sound Services for simple audio * Supports background audio with proper configuration ### Android [Section titled “Android”](#android) * Supports MP3, OGG, WAV formats * Uses SoundPool for simple audio * Uses MediaPlayer for complex audio * May require WAKE\_LOCK permission for background playback ### File Placement [Section titled “File Placement”](#file-placement) #### iOS [Section titled “iOS”](#ios-1) Place files in `ios/App/App/sounds/` or create a folder reference in Xcode. #### Android [Section titled “Android”](#android-1) Place files in `android/app/src/main/res/raw/`. Note: File names must be lowercase with no special characters. ## Common Issues [Section titled “Common Issues”](#common-issues) 1. **Audio not playing** * Ensure files are in correct directories * Check file format compatibility * Verify assetId matches exactly 2. **Delay in playback** * Use `preload()` for sound effects * Preload before you need to play 3. **Memory issues** * Unload audio files when not needed * Don’t preload too many large files 4. **Background playback** * Configure background audio capability on iOS * Handle audio focus on Android # @capgo/capacitor-native-biometric > Access native biometric authentication APIs for Android and iOS, providing secure and convenient user authentication. Comprehensive methods Supports verification, secure credential storage, secure retrieval, and biometry-change events. Stronger availability signals Reads `isAvailable`, `authenticationStrength`, and secure lock-state details for robust decision-making. Credential access controls Stores credentials with optional Keychain/Keystore access policies using `AccessControl`. Prompt control Configure reason, titles, allowed biometry types, retry count, and platform-specific fallback behavior. Event-driven updates Listen to `biometryChange` and adapt login flows when biometrics change while app is in background. Cross-platform docs See the full guide for setup and API usage in the getting-started page. # Getting Started > Learn how to install and use the Native Biometric plugin for secure biometric authentication in your Capacitor app. 1. **Install the package** * npm ```sh npm i @capgo/capacitor-native-biometric ``` * pnpm ```sh pnpm add @capgo/capacitor-native-biometric ``` * yarn ```sh yarn add @capgo/capacitor-native-biometric ``` * bun ```sh bun add @capgo/capacitor-native-biometric ``` 2. **Sync with native projects** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` 3. **Configure permissions** ### iOS [Section titled “iOS”](#ios) Add Face ID usage description to your `Info.plist`: ```xml NSFaceIDUsageDescription Authenticate securely to access your account ``` ### Android [Section titled “Android”](#android) Add biometric permission to your `AndroidManifest.xml`: ```xml ``` 4. **Check availability before prompting** Always inspect full availability details before showing authentication UIs. ## Usage [Section titled “Usage”](#usage) Import the plugin and use its methods for biometric authentication: ```typescript import { NativeBiometric, BiometryType, AuthenticationStrength, AccessControl, } from '@capgo/capacitor-native-biometric'; const checkBiometric = async () => { const result = await NativeBiometric.isAvailable({ useFallback: true }); console.log('Biometric available:', result.isAvailable); console.log('Strength:', result.authenticationStrength); // STRONG | WEAK | NONE console.log('Primary type:', result.biometryType); console.log('Device is secure:', result.deviceIsSecure); console.log('Strong biometry:', result.strongBiometryIsAvailable); }; const verify = async () => { await NativeBiometric.verifyIdentity({ reason: 'Open secure section', title: 'Biometric Login', subtitle: 'Unlock with face, fingerprint, or credentials', description: 'Authenticate to continue', negativeButtonText: 'Cancel', maxAttempts: 5, allowedBiometryTypes: [BiometryType.FINGERPRINT, BiometryType.FACE_ID], }); }; const saveCredentials = async () => { await NativeBiometric.setCredentials({ username: 'user@example.com', password: 'securepassword', server: 'https://api.example.com', accessControl: AccessControl.BIOMETRY_ANY, }); }; const getCredentials = async () => { const credentials = await NativeBiometric.getSecureCredentials({ server: 'https://api.example.com', reason: 'Sign in again', title: 'Confirm identity', }); console.log(credentials.username); }; const alreadySaved = async () => { const { isSaved } = await NativeBiometric.isCredentialsSaved({ server: 'https://api.example.com', }); console.log('Credentials saved:', isSaved); }; const cleanup = async () => { await NativeBiometric.deleteCredentials({ server: 'https://api.example.com', }); }; const watchChanges = async () => { const handle = await NativeBiometric.addListener('biometryChange', (result) => { console.log('Biometry availability changed:', result.isAvailable); console.log('Type:', result.biometryType); }); // Remove when no longer needed // await handle.remove(); }; ``` ## API Reference [Section titled “API Reference”](#api-reference) ### isAvailable(options) [Section titled “isAvailable(options)”](#isavailableoptions) Checks if biometric authentication is available on the device. ```typescript interface IsAvailableOptions { useFallback: boolean; } interface AvailableResult { isAvailable: boolean; authenticationStrength: AuthenticationStrength; // STRONG | WEAK | NONE biometryType: BiometryType; deviceIsSecure: boolean; strongBiometryIsAvailable: boolean; errorCode?: number; } ``` ### verifyIdentity(options) [Section titled “verifyIdentity(options)”](#verifyidentityoptions) Prompts for biometric authentication. ```typescript interface BiometricOptions { reason?: string; title?: string; subtitle?: string; description?: string; negativeButtonText?: string; useFallback?: boolean; fallbackTitle?: string; maxAttempts?: number; // Android only allowedBiometryTypes?: BiometryType[]; } ``` ### setCredentials(options) [Section titled “setCredentials(options)”](#setcredentialsoptions) Stores credentials. Use `accessControl` to enforce biometric-only retrieval on supported platforms. ```typescript interface SetCredentialOptions { username: string; password: string; server: string; accessControl?: AccessControl; } ``` ### getSecureCredentials(options) [Section titled “getSecureCredentials(options)”](#getsecurecredentialsoptions) Gets stored credentials only when authentication succeeds for protected entries. ```typescript interface GetSecureCredentialsOptions { server: string; reason?: string; title?: string; subtitle?: string; description?: string; negativeButtonText?: string; } ``` ### isCredentialsSaved(options) [Section titled “isCredentialsSaved(options)”](#iscredentialssavedoptions) Checks if credentials exist for a server entry. ```typescript interface IsCredentialsSavedOptions { server: string; } interface IsCredentialsSavedResult { isSaved: boolean; } ``` ### deleteCredentials(options) [Section titled “deleteCredentials(options)”](#deletecredentialsoptions) Deletes stored credentials for a server entry. ```typescript interface DeleteCredentialOptions { server: string; } ``` ### addListener(‘biometryChange’, …) [Section titled “addListener(‘biometryChange’, …)”](#addlistenerbiometrychange) Listen for biometric changes and resume flows safely. ```typescript const handle = await NativeBiometric.addListener('biometryChange', (result) => { // result: AvailableResult }); ``` ## Platform differences [Section titled “Platform differences”](#platform-differences) ### iOS [Section titled “iOS”](#ios-1) * Supports Face ID and Touch ID * `useFallback` follows passcode fallback behavior as configured ### Android [Section titled “Android”](#android-1) * Supports fingerprint, face, iris, and PIN/pattern/password flows * Android constraints apply for fallback UI and biometric type restrictions ## Best Practices [Section titled “Best Practices”](#best-practices) 1. **Always check availability first** 2. **Use full result data** to choose secure routes 3. **Prefer secure credential modes** for sensitive data 4. **Handle cancellation and errors gracefully** 5. **Remove listeners** when they are no longer needed ## Related Documentation [Section titled “Related Documentation”](#related-documentation) * [Migrate from Ionic Identity Vault](/docs/upgrade/from-ionic-identity-vault/) * [Ionic enterprise plugins migration solution](/solutions/ionic-enterprise-plugins/) # @capgo/native-market > A Capacitor plugin for redirecting users to app stores (Google Play or Apple App Store) from your mobile application. Cross-platform Works seamlessly on both Android and iOS platforms 🚀 Easy integration Simple API to redirect users to rate or view your app on their respective app stores 💨 TypeScript support Full TypeScript support for better development experience 😊 Comprehensive Documentation Check the [Documentation](/docs/plugins/native-market/getting-started/) to master the plugin in just a few minutes. # Getting Started > Learn how to install and use the Native Market plugin to redirect users to Google Play Store or Apple App Store from your Capacitor app. 1. **Install the package** * npm ```sh npm i @capgo/native-market ``` * pnpm ```sh pnpm add @capgo/native-market ``` * yarn ```sh yarn add @capgo/native-market ``` * bun ```sh bun add @capgo/native-market ``` 2. **Sync with native projects** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Usage [Section titled “Usage”](#usage) Import the plugin and use its methods to redirect users to app stores: ```typescript import { NativeMarket } from '@capgo/native-market'; // Open app store listing const openAppStore = async () => { await NativeMarket.openStoreListing({ appId: 'com.example.app' // Your app's bundle ID }); }; // Request app review const requestReview = async () => { await NativeMarket.requestReview(); }; // Open app store search const searchInStore = async () => { await NativeMarket.search({ terms: 'fitness app' // Search terms }); }; ``` ## API Reference [Section titled “API Reference”](#api-reference) ### openStoreListing(options) [Section titled “openStoreListing(options)”](#openstorelistingoptions) Opens the app store listing for the specified app. ```typescript interface OpenStoreListingOptions { appId: string; // Bundle ID on iOS, Package name on Android } ``` ### requestReview() [Section titled “requestReview()”](#requestreview) Requests an in-app review from the user. On iOS 10.3+, this shows the rating dialog without leaving the app. ### search(options) [Section titled “search(options)”](#searchoptions) Opens the app store with search results. ```typescript interface SearchOptions { terms: string; // Search terms to use } ``` ## Platform Notes [Section titled “Platform Notes”](#platform-notes) ### iOS [Section titled “iOS”](#ios) * Uses `SKStoreReviewController` for in-app reviews on iOS 10.3+ * Falls back to opening App Store for older versions ### Android [Section titled “Android”](#android) * Opens Google Play Store * Uses in-app review API when available ## Example [Section titled “Example”](#example) ```typescript import { NativeMarket } from '@capgo/native-market'; import { Capacitor } from '@capacitor/core'; export class AppService { async rateApp() { try { // Try in-app review first await NativeMarket.requestReview(); } catch (error) { // Fallback to opening store listing const platform = Capacitor.getPlatform(); const appId = platform === 'ios' ? 'id123456789' // Your iOS app ID : 'com.example.app'; // Your Android package name await NativeMarket.openStoreListing({ appId }); } } } ``` # @capgo/native-purchases > Simplify in-app purchases and subscriptions with a unified API that works seamlessly across iOS and Android platforms. Unified API Single API for iOS and Android purchases 💳 Subscription management Handle subscriptions with automatic renewal 🔄 Receipt validation Built-in receipt validation for security 🔒 Getting Started Check the [Getting Started Guide](/docs/plugins/native-purchases/getting-started/) to install and configure the plugin. ## Platform Setup Guides [Section titled “Platform Setup Guides”](#platform-setup-guides) Android Setup Complete guides for setting up in-app purchases on Android: * [Configure Sandbox Testing](/docs/plugins/native-purchases/android-sandbox-testing/) * [Create Subscriptions](/docs/plugins/native-purchases/android-create-subscription/) * [Introductory Offers](/docs/plugins/native-purchases/android-introductory-offer/) iOS Setup Complete guides for setting up in-app purchases on iOS: * [Configure Sandbox Testing](/docs/plugins/native-purchases/ios-sandbox-testing/) * [Subscription Groups](/docs/plugins/native-purchases/ios-subscription-group/) * [Create Subscriptions](/docs/plugins/native-purchases/ios-create-subscription/) * [Introductory Offers](/docs/plugins/native-purchases/ios-introductory-offer/) # Create Android Auto-Renewable Subscription > Step-by-step guide to creating auto-renewable subscriptions in Google Play Console for the native-purchases plugin. Auto-renewable subscriptions provide access to content, services, or premium features in your app on an ongoing basis. This guide will help you create and configure subscriptions in Google Play Console. ## Overview [Section titled “Overview”](#overview) Subscriptions automatically renew at the end of each billing period until the user cancels. They’re ideal for: * Premium content access * Ad-free experiences * Cloud storage * Ongoing services ## Creating a Subscription [Section titled “Creating a Subscription”](#creating-a-subscription) 1. **Navigate to Subscriptions** In Google Play Console, select your app and choose **Monetize > Subscriptions** from the left menu. Click the **Create subscription** button to begin. ![Navigate to subscriptions](/native-purchases/android/create-subscription/navigate-to-subscriptions.webp) 2. **Enter Basic Information** Provide a subscription name and product ID. The product ID is required for configuration in your app and cannot be changed later. ![Enter subscription details](/native-purchases/android/create-subscription/enter-subscription-details.webp) 3. **Create Base Plan** Google Play requires exactly one base plan per subscription. The native-purchases plugin supports only one base plan to maintain compatibility with iOS. Click **Add base plan** to continue. ![Create base plan](/native-purchases/android/create-subscription/create-base-plan.webp) 4. **Configure Base Plan Details** Enter: * **Base plan ID**: Unique identifier for this plan * **Billing period**: How often users are charged (weekly, monthly, yearly, etc.) * **Grace period**: Time window during which Google maintains the subscription while retrying payment before cancellation ![Configure base plan](/native-purchases/android/create-subscription/configure-base-plan-details.webp) 5. **Set Up Pricing** Access the pricing section and select all countries/regions where you want to offer the subscription. ![Select regions](/native-purchases/android/create-subscription/select-pricing-regions.webp) 6. **Configure Price** Set your base price in your primary currency. Google Play automatically converts this to local currencies. ![Set price](/native-purchases/android/create-subscription/set-base-price.webp) 7. **Review Regional Pricing** Review the automatically converted prices for each country. You can adjust individual prices if needed. ![Review pricing](/native-purchases/android/create-subscription/review-regional-pricing.webp) 8. **Save Configuration** Save your pricing configuration. ![Save pricing](/native-purchases/android/create-subscription/save-pricing-configuration.webp) 9. **Activate Subscription** Click the **Activate** button to make your subscription product live and available for purchase. ![Activate subscription](/native-purchases/android/create-subscription/activate-subscription.webp) ## Important Considerations [Section titled “Important Considerations”](#important-considerations) ### Base Plan Limitation [Section titled “Base Plan Limitation”](#base-plan-limitation) The native-purchases plugin requires exactly one base plan per subscription to ensure consistency with iOS subscription handling. Multiple base plans are not supported. ### Grace Period [Section titled “Grace Period”](#grace-period) The grace period allows Google Play to retry failed payments while maintaining the user’s subscription access. Common grace periods are: * 3 days for monthly subscriptions * 7 days for longer subscriptions ### Subscription Status [Section titled “Subscription Status”](#subscription-status) After creation, your subscription will be in “Draft” status until activated. You can test draft subscriptions in sandbox mode. ## Using in Your App [Section titled “Using in Your App”](#using-in-your-app) Once created, reference the subscription in your app using the product ID: ```typescript import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases'; // Load subscription info const { products } = await NativePurchases.getProducts({ productIdentifiers: ['com.example.premium.monthly'], productType: PURCHASE_TYPE.SUBS, }); const product = products[0]; console.log(`${product.title} — ${product.priceString}`); // Purchase (planIdentifier = Base Plan ID from Google Play Console) const transaction = await NativePurchases.purchaseProduct({ productIdentifier: 'com.example.premium.monthly', planIdentifier: 'monthly-plan', // REQUIRED on Android, ignored on iOS productType: PURCHASE_TYPE.SUBS, }); console.log('Transaction ID', transaction.transactionId); // Later, check purchase state const { purchases } = await NativePurchases.getPurchases({ productType: PURCHASE_TYPE.SUBS, }); const subscription = purchases.find( (purchase) => purchase.productIdentifier === 'com.example.premium.monthly', ); if (subscription && subscription.purchaseState === 'PURCHASED' && subscription.isAcknowledged) { console.log('Subscription active locally'); // For expiration/cancellation, validate purchaseToken through your backend } ``` ## Next Steps [Section titled “Next Steps”](#next-steps) * [Create an introductory offer](/docs/plugins/native-purchases/android-introductory-offer/) to attract new subscribers * [Configure sandbox testing](/docs/plugins/native-purchases/android-sandbox-testing/) to test your subscriptions * Set up backend receipt validation for security ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) **Subscription not appearing in app:** * Verify the product ID matches exactly * Ensure the subscription is activated * Check that your app has the correct package name * Wait 2-3 hours after activation for changes to propagate **Base plan errors:** * Ensure you have exactly one base plan * Verify all required fields are filled * Check that billing period is valid **Pricing issues:** * Confirm at least one country is selected * Verify base price is greater than minimum allowed * Check currency conversion rates are acceptable # Create Android Subscription Introductory Offer > Learn how to create introductory offers for auto-renewable subscriptions on Android to attract new subscribers. Introductory offers allow you to provide eligible users with either a free trial or a discounted introductory price. After the introductory period concludes, subscriptions automatically renew at standard pricing unless cancelled. ## Overview [Section titled “Overview”](#overview) Introductory offers are a powerful tool to: * Reduce barriers to entry for new subscribers * Increase conversion rates * Allow users to try your premium features risk-free * Build long-term subscriber relationships ## Eligibility [Section titled “Eligibility”](#eligibility) Users can receive an introductory offer if they haven’t previously purchased or received an introductory offer for the subscription. Google Play handles eligibility automatically. ## Prerequisites [Section titled “Prerequisites”](#prerequisites) You must first [create an auto-renewable subscription](/docs/plugins/native-purchases/android-create-subscription/) before adding an introductory offer. ## Creating an Introductory Offer [Section titled “Creating an Introductory Offer”](#creating-an-introductory-offer) 1. **Access Offer Configuration** Navigate to your subscription in Google Play Console and select the **Add offer** button. ![Add offer button](/native-purchases/android/introductory-offer/add-offer-button.webp) 2. **Select Base Plan** A modal will appear requiring you to choose your base plan. Typically, you’ll only have one base plan. Click **Add offer** to continue. ![Select base plan](/native-purchases/android/introductory-offer/select-base-plan.webp) 3. **Configure Offer Details** Enter the following information: **Offer ID**: A unique identifier for this offer **Eligibility**: Choose who can receive this offer * **New customers**: Only users who have never subscribed * **Existing customers**: Users who previously subscribed * **Developer determined**: Custom eligibility logic (not supported by native-purchases) Caution The native-purchases plugin does not support the “Developer determined” eligibility option. Use “New customers” or “Existing customers” instead. ![Configure offer](/native-purchases/android/introductory-offer/configure-offer-details.webp) 4. **Add Phases** Click **Add phase** at the bottom of the page to define your offer structure. You can add up to two phases, allowing combinations like: * Free trial only * Discounted price only * Free trial followed by discounted recurring payment 5. **Select Phase Type** Choose from three phase types: **Free Trial** * Complimentary access for a set duration * Example: 7 days free, then $9.99/month **Single Payment** * One-time discounted price for a specific period * Example: $1.99 for 2 months, then $9.99/month **Discounted Recurring Payment** * Reduced per-billing-cycle rate for multiple cycles * Example: $4.99/month for 3 months, then $9.99/month 6. **Configure Phase Duration** Set how long the introductory phase lasts: * Days, weeks, or months * Number of billing cycles 7. **Finalize and Activate** Click **Apply**, then **Save** to activate the offer. The **Activate** button will become available once saved. ## Offer Phase Examples [Section titled “Offer Phase Examples”](#offer-phase-examples) ### Example 1: Simple Free Trial [Section titled “Example 1: Simple Free Trial”](#example-1-simple-free-trial) * Phase 1: 7 days free * Then: $9.99/month standard pricing ### Example 2: Discounted Introduction [Section titled “Example 2: Discounted Introduction”](#example-2-discounted-introduction) * Phase 1: $1.99 for the first month * Then: $9.99/month standard pricing ### Example 3: Extended Trial + Discount [Section titled “Example 3: Extended Trial + Discount”](#example-3-extended-trial--discount) * Phase 1: 14 days free * Phase 2: $4.99/month for 2 months * Then: $9.99/month standard pricing ## Using in Your App [Section titled “Using in Your App”](#using-in-your-app) The native-purchases plugin automatically handles introductory offer eligibility and presentation: ```typescript import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases'; // Fetch products (includes intro offer metadata) const { products } = await NativePurchases.getProducts({ productIdentifiers: ['com.example.premium.monthly'], productType: PURCHASE_TYPE.SUBS, }); const product = products[0]; if (product.introductoryPrice) { console.log(`Intro price: ${product.introductoryPriceString}`); console.log(`Regular price: ${product.priceString}`); console.log( `Offer duration: ${product.introductoryPrice.subscriptionPeriod?.numberOfUnits} ${product.introductoryPrice.subscriptionPeriod?.unit}`, ); } else { console.log('No intro offer configured for this product'); } // Purchase (Google Play applies intro pricing automatically if the user is eligible) const transaction = await NativePurchases.purchaseProduct({ productIdentifier: 'com.example.premium.monthly', planIdentifier: 'monthly-plan', // Base Plan ID from Google Play Console productType: PURCHASE_TYPE.SUBS, }); console.log('Introductory purchase transaction', transaction.transactionId); ``` ## Best Practices [Section titled “Best Practices”](#best-practices) ### Offer Duration [Section titled “Offer Duration”](#offer-duration) * **Free trials**: 3-14 days is optimal for most apps * **Discounted periods**: 1-3 months works well for building habit * **Price discount**: 50-70% off regular price drives conversions ### Marketing [Section titled “Marketing”](#marketing) * Clearly display the intro offer and regular price * Show what happens after the intro period * Make cancellation easy and transparent * Remind users before the intro period ends ### A/B Testing [Section titled “A/B Testing”](#ab-testing) Test different offer structures: * Free trial length * Discount percentage * Discount duration * Single phase vs. multi-phase ## Important Notes [Section titled “Important Notes”](#important-notes) * Only one introductory offer can be active per subscription at a time * Users can only claim an intro offer once per subscription * Intro offers don’t apply to subscription upgrades/downgrades * Changes to intro offers don’t affect existing subscribers ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) **Intro offer not showing:** * Verify the offer is activated in Play Console * Check user eligibility (may have used offer before) * Ensure app is using latest product information **Wrong users receiving offer:** * Review eligibility settings (new vs. existing customers) * Check if user previously subscribed on different device * Verify Play Store account history **Offer not applying at purchase:** * Confirm product ID matches exactly * Check that offer is still active and not expired * Verify date range settings for the offer ## Next Steps [Section titled “Next Steps”](#next-steps) * [Configure sandbox testing](/docs/plugins/native-purchases/android-sandbox-testing/) to test your offers * Monitor conversion rates in Play Console analytics * Consider creating multiple subscription tiers with different offers # Android Play Store Review Guidelines for IAP > Complete guide to passing Google Play review with in-app purchases and subscriptions, including compliance requirements and best practices. Getting your Android app approved on Google Play requires compliance with Google’s policies, especially for apps with in-app purchases and subscriptions. This guide covers everything you need to pass review successfully. ## Google Play Billing Requirements [Section titled “Google Play Billing Requirements”](#google-play-billing-requirements) ### Mandatory Billing System [Section titled “Mandatory Billing System”](#mandatory-billing-system) For digital goods and services, you **must** use Google Play’s billing system: **Digital Goods (Must Use Play Billing):** * Subscriptions to premium features * In-app currency or credits * Digital content (ebooks, music, videos) * Game upgrades and power-ups * App unlocks and premium tiers **Physical Goods (Cannot Use Play Billing):** * Physical merchandise * Real-world services * One-time donations to nonprofits :::caution 2025 Requirement New apps must use the `monetization.subscriptions` APIs for handling subscription catalogs. Legacy billing APIs are deprecated. ::: ### Implementation with Native Purchases [Section titled “Implementation with Native Purchases”](#implementation-with-native-purchases) ```typescript import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases'; // Ensure billing is available on the device const { isBillingSupported } = await NativePurchases.isBillingSupported(); if (!isBillingSupported) throw new Error('Google Play Billing not available'); // Fetch subscription products (Store data is required—never hardcode pricing) const { products } = await NativePurchases.getProducts({ productIdentifiers: ['premium_monthly', 'premium_yearly'], productType: PURCHASE_TYPE.SUBS, }); // Plan identifiers are the Base Plan IDs you create in Google Play Console const transaction = await NativePurchases.purchaseProduct({ productIdentifier: 'premium_monthly', planIdentifier: 'monthly-plan', // REQUIRED on Android, ignored on iOS productType: PURCHASE_TYPE.SUBS, }); console.log('Purchase token for server validation:', transaction.purchaseToken); ``` ## Transparency and Disclosure Requirements [Section titled “Transparency and Disclosure Requirements”](#transparency-and-disclosure-requirements) ### Upfront Pricing Disclosure [Section titled “Upfront Pricing Disclosure”](#upfront-pricing-disclosure) Google Play mandates clear disclosure of all costs before purchase: **Required Elements:** * Exact price in user’s local currency * Billing frequency (monthly, yearly, etc.) * What’s included in the subscription * Total cost for introductory offers * When charges will occur ![UI Design Best Practices](/native-purchases/review-guides/ui-design-dos-donts.webp) **Example of Compliant UI:** ```typescript function SubscriptionCard({ product }) { return (

{product.title}

{/* Show intro offer if available */} {product.introductoryPrice && (

{product.introductoryPriceString}

for {product.introductoryPricePeriod}

)} {/* Regular price */}

{product.priceString}

per {product.subscriptionPeriod}

{/* Clear description */}

{product.description}

{/* Renewal terms */}

Renews automatically. Cancel anytime in Google Play.

); } ``` ### Auto-Renewal Disclosure [Section titled “Auto-Renewal Disclosure”](#auto-renewal-disclosure) Before a subscription auto-renews, Google requires: * Clear notification that renewal will occur * Reminder of the price * Easy access to cancellation Tip The native-purchases plugin works with Google Play to handle auto-renewal notifications automatically. Ensure your subscription products are properly configured in Google Play Console. ### Price Consistency Across Platforms [Section titled “Price Consistency Across Platforms”](#price-consistency-across-platforms) **Critical Rule:** Prices must be consistent across all platforms where your app is available. **Example Violation:** * iOS: $9.99/month * Android: $7.99/month * Web: $11.99/month **Why It Matters:** Users can screenshot price differences and report to Google, triggering policy violations. ## Privacy Policy Requirements [Section titled “Privacy Policy Requirements”](#privacy-policy-requirements) ### Mandatory Privacy Policy [Section titled “Mandatory Privacy Policy”](#mandatory-privacy-policy) If your app includes in-app purchases, you must: 1. **Link in Play Store Listing** * Add privacy policy URL in Play Console * Must be publicly accessible * Must be in the same language as your app 2. **Link Within App** * Display privacy policy in app settings * Show before collecting any user data * Make easily discoverable **Example Implementation:** ```typescript function SettingsScreen() { const openPrivacyPolicy = () => { window.open('https://yourapp.com/privacy', '_blank'); }; const openTerms = () => { window.open('https://yourapp.com/terms', '_blank'); }; return (

Settings

); } ``` ### Data Safety Section [Section titled “Data Safety Section”](#data-safety-section) Google Play requires detailed disclosure in the Data Safety section: **For IAP Apps, Declare:** * Purchase history collection * Email addresses (for receipts) * Device IDs (for fraud prevention) * Payment information handling * Analytics data collection The Data Safety section is legally binding. Inaccurate declarations can result in app removal. ## Common Rejection Reasons [Section titled “Common Rejection Reasons”](#common-rejection-reasons) ### 1. Missing or Incorrect Billing Implementation [Section titled “1. Missing or Incorrect Billing Implementation”](#1-missing-or-incorrect-billing-implementation) **Why It Fails:** * Not using Google Play Billing for digital goods * Using deprecated billing APIs * Implementing custom payment solutions for subscriptions **Prevention:** ```typescript // ✅ Correct: Use native-purchases (uses Google Play Billing) await NativePurchases.purchaseProduct({ productIdentifier: 'premium_monthly' }); // ❌ Wrong: Custom payment processor for subscriptions // await CustomPayment.charge(user, 9.99); ``` ### 2. Unclear Pricing or Hidden Costs [Section titled “2. Unclear Pricing or Hidden Costs”](#2-unclear-pricing-or-hidden-costs) **Why It Fails:** * Price only shown after clicking purchase * Additional fees not disclosed upfront * Vague subscription terms **Prevention:** ```typescript function PurchaseScreen({ product }) { return (
{/* Show ALL costs upfront */}

Premium Subscription

{product.priceString}/month

Taxes may apply based on location

Includes:

  • Ad-free experience
  • Unlimited cloud storage
  • Priority support

Subscription renews automatically unless cancelled at least 24 hours before the end of the current period.

Manage or cancel in Google Play Subscriptions.

); } ``` ### 3. Deceptive Subscription Patterns [Section titled “3. Deceptive Subscription Patterns”](#3-deceptive-subscription-patterns) **Why It Fails:** * Pre-selecting premium options * Hiding cheaper alternatives * Making cancellation difficult * Fake urgency (“Only 3 spots left!”) ![Description Best Practices](/native-purchases/review-guides/description-guidelines-1.webp) ![Marketing Guidelines](/native-purchases/review-guides/description-guidelines-2.webp) **Prevention:** * Display all subscription tiers equally * Make cancellation clear and accessible * Avoid countdown timers or fake scarcity * Don’t use dark patterns to push expensive options ### 4. Incomplete Testing [Section titled “4. Incomplete Testing”](#4-incomplete-testing) **Why It Fails:** * App crashes when purchasing * Products don’t load * Purchase confirmation doesn’t show * Premium features don’t unlock after purchase **Prevention:** ```typescript import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases'; // Comprehensive testing before submission async function testPurchaseFlow() { try { // 1. Test product loading const { products } = await NativePurchases.getProducts({ productIdentifiers: ['premium_monthly', 'premium_yearly'], productType: PURCHASE_TYPE.SUBS, }); console.log('✓ Products loaded:', products.length); // 2. Test purchase flow const transaction = await NativePurchases.purchaseProduct({ productIdentifier: 'premium_monthly', planIdentifier: 'monthly-plan', productType: PURCHASE_TYPE.SUBS, }); console.log('✓ Purchase completed', transaction.transactionId); // 3. Verify entitlements const { purchases } = await NativePurchases.getPurchases({ productType: PURCHASE_TYPE.SUBS, }); if ( purchases.some( (purchase) => purchase.productIdentifier === 'premium_monthly' && ['PURCHASED', '1'].includes(purchase.purchaseState ?? '') && purchase.isAcknowledged, ) ) { console.log('✓ Premium features unlocked'); } // 4. Test restore await NativePurchases.restorePurchases(); console.log('✓ Restore works'); } catch (error) { console.error('✗ Test failed:', error); } } ``` ### 5. Privacy Policy Violations [Section titled “5. Privacy Policy Violations”](#5-privacy-policy-violations) **Why It Fails:** * No privacy policy link in app * Privacy policy not accessible * Data collection not disclosed * Data Safety section inaccurate **Prevention:** * Add privacy policy to Play Store listing * Include link in app settings * Accurately fill out Data Safety section * Update policy when adding new data collection ## Alternative Billing Systems (2025 Update) [Section titled “Alternative Billing Systems (2025 Update)”](#alternative-billing-systems-2025-update) ### Regional Compliance [Section titled “Regional Compliance”](#regional-compliance) Google now allows alternative billing systems in certain regions: **Eligible Regions:** * European Economic Area (EEA) * South Korea * India (coming soon) **Requirements if Using Alternative Billing:** * Must still offer Google Play Billing as option * Clear communication to users about choice * Comply with local regulations * Service fee still applies (reduced) Note For most developers, sticking with Google Play Billing only is simpler. The native-purchases plugin handles this automatically. ## Subscription Management [Section titled “Subscription Management”](#subscription-management) ### Easy Cancellation [Section titled “Easy Cancellation”](#easy-cancellation) Users must be able to: * View active subscriptions easily * Cancel without contacting support * Understand when cancellation takes effect **Implementation:** ```typescript import { NativePurchases } from '@capgo/native-purchases'; function ManageSubscriptionButton() { const openManagement = async () => { try { // Opens Google Play subscription management await NativePurchases.showManageSubscriptions(); } catch (error) { // Fallback to direct URL const playStoreUrl = 'https://play.google.com/store/account/subscriptions'; window.open(playStoreUrl, '_blank'); } }; return ( ); } ``` ### Cancellation Grace Period [Section titled “Cancellation Grace Period”](#cancellation-grace-period) **Required Disclosure:** * When does cancellation take effect? * Do users keep access until period ends? * Are partial refunds available? ```typescript function CancellationInfo() { return (

Cancellation Policy

  • Cancel anytime in Google Play
  • Access continues until end of billing period
  • No refunds for partial periods
  • Resubscribe anytime to regain access
); } ``` ## Pre-Submission Checklist [Section titled “Pre-Submission Checklist”](#pre-submission-checklist) ![Pre-Submission Checklist](/native-purchases/review-guides/pre-submission-checklist.webp) 1. **Verify Billing Implementation** * Using Google Play Billing (via native-purchases) * All subscription products created in Play Console * Products are activated and published * Pricing set for all target countries 2. **Test Purchase Flows** * Create license test account * Test each subscription tier * Verify products load correctly * Test purchase completion * Verify premium features unlock * Test subscription restoration * Test on multiple devices 3. **Review All Copy** * Pricing displayed clearly before purchase * All fees disclosed upfront * Subscription terms are clear * Cancellation process explained * No misleading claims 4. **Privacy Compliance** * Privacy policy linked in Play Console * Privacy policy accessible in app * Data Safety section completed accurately * Permissions justified and documented 5. **Content Rating** * Complete content rating questionnaire * Ensure rating matches actual content * Declare in-app purchases in questionnaire 6. **Prepare Store Listing** * App description accurate * Screenshots show current version * Feature graphic meets requirements * All required assets uploaded ## Review Timeline [Section titled “Review Timeline”](#review-timeline) **Initial Review:** 7 days on average (can be faster) **Updates:** Typically faster than initial submission **Policy Violations:** Immediate suspension possible **Appeals:** 7-14 days for review :::tip Rolling Reviews Unlike Apple, Google reviews apps continuously. Your app may go live at any time during the review period, not at a fixed time. ::: ## Testing Before Submission [Section titled “Testing Before Submission”](#testing-before-submission) ### License Testing [Section titled “License Testing”](#license-testing) 1. **Add Test Account:** * Go to Play Console * Setup > License Testing * Add Gmail account for testing 2. **Test in Sandbox:** ```typescript import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases'; // Test purchases with license test account async function testInSandbox() { const { isBillingSupported } = await NativePurchases.isBillingSupported(); if (!isBillingSupported) { console.error('Billing not supported in this environment'); return; } // Fetch products (returns test pricing when using a license tester) const { products } = await NativePurchases.getProducts({ productIdentifiers: ['premium_monthly'], productType: PURCHASE_TYPE.SUBS, }); console.log('Test products:', products); // Make test purchase (no charge) const transaction = await NativePurchases.purchaseProduct({ productIdentifier: 'premium_monthly', planIdentifier: 'monthly-plan', productType: PURCHASE_TYPE.SUBS, }); console.log('Test purchase complete:', transaction.transactionId); } ``` 3. **Verify Test Banner:** * When purchasing with test account * Should see “Test purchase” notification * No real charges occur ### Internal Testing Track [Section titled “Internal Testing Track”](#internal-testing-track) Before production release: 1. Create Internal Testing track in Play Console 2. Upload APK/AAB 3. Add tester email addresses 4. Testers download from Play Store (testing track) 5. Verify purchase flows work end-to-end ## Best Practices for Native Purchases [Section titled “Best Practices for Native Purchases”](#best-practices-for-native-purchases) ### Handle All Purchase States [Section titled “Handle All Purchase States”](#handle-all-purchase-states) ```typescript import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases'; async function handlePurchase(productId: string, planIdentifier?: string) { try { setLoading(true); const transaction = await NativePurchases.purchaseProduct({ productIdentifier: productId, planIdentifier, productType: planIdentifier ? PURCHASE_TYPE.SUBS : PURCHASE_TYPE.INAPP, }); console.log('Purchase token:', transaction.purchaseToken ?? transaction.receipt); // Success - check entitlements from the store const { purchases } = await NativePurchases.getPurchases({ productType: planIdentifier ? PURCHASE_TYPE.SUBS : PURCHASE_TYPE.INAPP, }); const isOwned = purchases.some( (purchase) => purchase.productIdentifier === productId && (purchase.purchaseState === 'PURCHASED' || purchase.purchaseState === '1') && purchase.isAcknowledged, ); if (isOwned) { unlockPremiumFeatures(); showSuccess('Premium activated!'); } } catch (error: any) { // Handle specific error cases switch (error.code) { case 'USER_CANCELLED': // User backed out - no error needed console.log('Purchase cancelled'); break; case 'ITEM_ALREADY_OWNED': // They already own it - restore instead showInfo('You already own this! Restoring...'); await NativePurchases.restorePurchases(); break; case 'ITEM_UNAVAILABLE': showError('This subscription is currently unavailable. Please try again later.'); break; case 'NETWORK_ERROR': showError('Network error. Please check your connection and try again.'); break; default: showError('Purchase failed. Please try again.'); console.error('Purchase error:', error); } } finally { setLoading(false); } } ``` ### Implement Restore Purchases [Section titled “Implement Restore Purchases”](#implement-restore-purchases) ```typescript import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases'; function RestorePurchasesButton() { const [loading, setLoading] = useState(false); const handleRestore = async () => { setLoading(true); try { await NativePurchases.restorePurchases(); const { purchases } = await NativePurchases.getPurchases({ productType: PURCHASE_TYPE.SUBS, }); const hasSubscription = purchases.some( (purchase) => purchase.productType === 'subs' && purchase.isAcknowledged, ); if (hasSubscription) { unlockPremiumFeatures(); showSuccess('Subscriptions restored!'); return; } // Check one-time unlocks if needed const { purchases: iaps } = await NativePurchases.getPurchases({ productType: PURCHASE_TYPE.INAPP, }); const hasInApp = iaps.some((purchase) => purchase.productIdentifier === 'premium_unlock'); if (hasInApp) { unlockPremiumFeatures(); showSuccess('Previous purchases restored!'); return; } showInfo('No previous purchases found.'); } catch (error) { showError('Failed to restore purchases. Please try again.'); } finally { setLoading(false); } }; return ( ); } ``` ### Check Subscription Status [Section titled “Check Subscription Status”](#check-subscription-status) ```typescript import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases'; async function checkSubscriptionStatus() { try { const { purchases } = await NativePurchases.getPurchases({ productType: PURCHASE_TYPE.SUBS, }); const subscription = purchases.find( (purchase) => purchase.productIdentifier === 'premium_monthly' && (purchase.purchaseState === 'PURCHASED' || purchase.purchaseState === '1') && purchase.isAcknowledged, ); if (!subscription) { showPaywall(); return; } console.log('Subscription active:', { productId: subscription.productIdentifier, expiresAt: subscription.expirationDate, willRenew: subscription.willCancel === false, purchaseToken: subscription.purchaseToken, }); unlockPremiumFeatures(); } catch (error) { console.error('Failed to check subscription:', error); } } ``` ## If Your App Gets Rejected [Section titled “If Your App Gets Rejected”](#if-your-app-gets-rejected) ### Common Policy Violations [Section titled “Common Policy Violations”](#common-policy-violations) **Payments Policy:** * Not using Google Play Billing * Misleading subscription terms * Hidden costs **User Data Policy:** * Missing privacy policy * Inaccurate Data Safety declarations * Excessive permissions ### Resolution Steps [Section titled “Resolution Steps”](#resolution-steps) 1. **Review the Violation Notice** * Read the specific policy cited * Understand what Google flagged * Check examples they provided 2. **Fix the Issue** * Address root cause, not just symptoms * Test thoroughly after fix * Document all changes made 3. **Submit Appeal (if applicable)** ![Clarification and Appeal Process](/native-purchases/review-guides/clarification-process.webp) ```plaintext Subject: Policy Violation Appeal - [App Name] Dear Google Play Review Team, I have received notification that my app violates [Policy X.Y]. I have made the following changes to comply: 1. [Specific change made] 2. [Specific change made] 3. [Specific change made] The updated version [version number] addresses all concerns raised. Test account for verification: Email: test@example.com Password: TestPass123 Thank you for your consideration. ``` ![Request Documentation Example](/native-purchases/review-guides/request-documents.webp) 4. **Resubmit or Update** * Upload fixed version * Resubmit for review * Monitor status in Play Console ## Additional Resources [Section titled “Additional Resources”](#additional-resources) * [Google Play Developer Policy Center](https://play.google.com/about/developer-content-policy/) * [Google Play Billing Documentation](https://developer.android.com/google/play/billing) * [Subscriptions Best Practices](https://developer.android.com/google/play/billing/subscriptions) * [Play Console Help](https://support.google.com/googleplay/android-developer/) ## Need Expert Help? [Section titled “Need Expert Help?”](#need-expert-help) Navigating Play Store review can be complex, especially with the new 2025 testing requirements. If you need personalized assistance: **[Book a consultation call with our team](https://cal.com/team/capgo/capacitor-consulting-services)** for help with: * Complete Play Store review preparation * Testing track setup and tester recruitment * IAP implementation review * Data Safety and privacy compliance * Rejection troubleshooting and appeals * Complete app submission process Our experts have guided hundreds of apps through successful Play Store submissions and can help you navigate the 2025 requirements. ## Support [Section titled “Support”](#support) Need help with implementation? * Review the [Native Purchases documentation](/docs/plugins/native-purchases/getting-started/) * Check [Android sandbox testing guide](/docs/plugins/native-purchases/android-sandbox-testing/) * Visit [Google Play Developer Support](https://support.google.com/googleplay/android-developer/) # Configure Android Sandbox Testing > Learn how to set up sandbox testing for in-app purchases on Android using Google Play Console. Testing in-app purchases requires proper configuration in Google Play Console. This guide will walk you through setting up sandbox testing for your Android app. ## Prerequisites [Section titled “Prerequisites”](#prerequisites) You need an active Google Play Console account with a current subscription (renewed annually). ## Setup Process [Section titled “Setup Process”](#setup-process) 1. **Add Testing Account** Navigate to your Google Play Console, select your app, and go to **Setup > License testing**. Add the primary Google account associated with the Android device you’re using for testing. ![Add testing account](/native-purchases/android/sandbox-testing/add-testing-account.webp) 2. **Create Testing Track** Go to **Testing > Closed testing** or **Testing > Internal testing** and create a new track. You must upload at least one signed version of your app to this track. ![Create testing track](/native-purchases/android/sandbox-testing/create-testing-track.webp) 3. **Create Tester List** After setting up the testing track, create an email list and add the testing device’s account email address. ![Create tester list](/native-purchases/android/sandbox-testing/create-tester-list.webp) 4. **Join Testing Program** Open the opt-in URL from your test device and click the **“Become a tester”** button to enroll. ![Join testing](/native-purchases/android/sandbox-testing/join-testing-program.webp) 5. **Upload Signed APK** Generate a signed APK or use Android App Bundle to upload your app to the testing track. Rolling out isn’t required - just upload and await approval. ![Upload APK](/native-purchases/android/sandbox-testing/upload-signed-apk.webp) 6. **Build and Test** Run your app on the test device. When attempting a purchase, you should see a message: > “This is a test order; you will not be charged.” ![Test purchase](/native-purchases/android/sandbox-testing/test-purchase-confirmation.webp) ## Important Notes [Section titled “Important Notes”](#important-notes) * Test accounts will not be charged for purchases * Test purchases use the same flow as production purchases * You can test all subscription features including trials and introductory offers * Test subscriptions have accelerated renewal periods for faster testing ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) **Products not showing in test mode:** * Ensure your app is uploaded to a testing track * Verify the test account is added to License testing * Check that products are active in Google Play Console **“Item not available” error:** * Wait 2-3 hours after creating products for them to become available * Ensure your app’s package name matches the one in Play Console * Verify you’re signed in with a test account **Test purchases showing as real charges:** * Double-check the account is added to License testing * Ensure you’re using the build from the testing track * Verify the testing banner appears during purchase ## Additional Resources [Section titled “Additional Resources”](#additional-resources) For more details, refer to the [official Google Play documentation](https://developer.android.com/google/play/billing/test) on testing in-app purchases. # Getting Started > Learn how to install and use the @capgo/native-purchases plugin to implement one-time purchases and subscriptions with StoreKit 2 and Google Play Billing 7. 1. **Install the package** * npm ```sh npm i @capgo/native-purchases ``` * pnpm ```sh pnpm add @capgo/native-purchases ``` * yarn ```sh yarn add @capgo/native-purchases ``` * bun ```sh bun add @capgo/native-purchases ``` 2. **Sync with native projects** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` 3. **Check billing support** ```typescript import { NativePurchases } from '@capgo/native-purchases'; const { isBillingSupported } = await NativePurchases.isBillingSupported(); if (!isBillingSupported) { throw new Error('Billing is not available on this device'); } ``` 4. **Load products directly from the stores** ```typescript import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases'; const { products } = await NativePurchases.getProducts({ productIdentifiers: [ 'com.example.premium.monthly', 'com.example.premium.yearly', 'com.example.one_time_unlock' ], productType: PURCHASE_TYPE.SUBS, // Use PURCHASE_TYPE.INAPP for one‑time products }); products.forEach((product) => { console.log(product.title, product.priceString); }); ``` 5. **Implement purchase & restore flows** ```typescript import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases'; const monthlyPlanId = 'monthly-plan'; // Base Plan ID from Google Play Console const transaction = await NativePurchases.purchaseProduct({ productIdentifier: 'com.example.premium.monthly', planIdentifier: monthlyPlanId, // REQUIRED for Android subscriptions, ignored on iOS productType: PURCHASE_TYPE.SUBS, quantity: 1, }); console.log('Transaction ID', transaction.transactionId); await NativePurchases.restorePurchases(); ``` * iOS * Create in-app products and subscriptions in App Store Connect. * Use StoreKit Local Testing or Sandbox testers for QA. * No manifest edits required. Make sure your products are approved. * Android * Create in-app products and subscriptions in Google Play Console. * Upload at least an internal test build and add license testers. * Add the billing permission to `AndroidManifest.xml`: ```xml ``` ## Purchase service example [Section titled “Purchase service example”](#purchase-service-example) ```typescript import { NativePurchases, PURCHASE_TYPE, Transaction } from '@capgo/native-purchases'; import { Capacitor } from '@capacitor/core'; class PurchaseService { private premiumProduct = 'com.example.premium.unlock'; private monthlySubId = 'com.example.premium.monthly'; private monthlyPlanId = 'monthly-plan'; // Base Plan ID (Android only) async initialize() { const { isBillingSupported } = await NativePurchases.isBillingSupported(); if (!isBillingSupported) throw new Error('Billing unavailable'); const { products } = await NativePurchases.getProducts({ productIdentifiers: [this.premiumProduct, this.monthlySubId], productType: PURCHASE_TYPE.SUBS, }); console.log('Loaded products', products); if (Capacitor.getPlatform() === 'ios') { NativePurchases.addListener('transactionUpdated', (transaction) => { this.handleTransaction(transaction); }); } } async buyPremium(appAccountToken?: string) { const transaction = await NativePurchases.purchaseProduct({ productIdentifier: this.premiumProduct, productType: PURCHASE_TYPE.INAPP, appAccountToken, }); await this.processTransaction(transaction); } async buyMonthly(appAccountToken?: string) { const transaction = await NativePurchases.purchaseProduct({ productIdentifier: this.monthlySubId, planIdentifier: this.monthlyPlanId, // REQUIRED for Android subscriptions productType: PURCHASE_TYPE.SUBS, appAccountToken, }); await this.processTransaction(transaction); } async restore() { await NativePurchases.restorePurchases(); await this.refreshEntitlements(); } async openManageSubscriptions() { await NativePurchases.manageSubscriptions(); } private async processTransaction(transaction: Transaction) { this.unlockContent(transaction.productIdentifier); this.validateOnServer(transaction).catch(console.error); } private unlockContent(productIdentifier: string) { // persist entitlement locally console.log('Unlocked', productIdentifier); } private async refreshEntitlements() { const { purchases } = await NativePurchases.getPurchases({ productType: PURCHASE_TYPE.SUBS, }); console.log('Current purchases', purchases); } private async handleTransaction(transaction: Transaction) { console.log('StoreKit transaction update:', transaction); await this.processTransaction(transaction); } private async validateOnServer(transaction: Transaction) { await fetch('/api/validate-purchase', { method: 'POST', body: JSON.stringify({ transactionId: transaction.transactionId, receipt: transaction.receipt, purchaseToken: transaction.purchaseToken, }), }); } } ``` ## Required purchase options [Section titled “Required purchase options”](#required-purchase-options) | Option | Platform | Description | | ------------------- | --------------------- | -------------------------------------------------------------------------------------------------------------------------------- | | `productIdentifier` | iOS + Android | SKU/Product ID configured in App Store Connect / Google Play Console. | | `productType` | Android only | `PURCHASE_TYPE.INAPP` or `PURCHASE_TYPE.SUBS`. Defaults to `INAPP`. Always set to `SUBS` for subscriptions. | | `planIdentifier` | Android subscriptions | Base Plan ID from Google Play Console. Required for subscriptions, ignored on iOS and in-app purchases. | | `quantity` | iOS | Only for in-app purchases, defaults to `1`. Android always purchases one item. | | `appAccountToken` | iOS + Android | UUID/string linking the purchase to your user. Required to be UUID on iOS; Android accepts any obfuscated string up to 64 chars. | | `isConsumable` | Android | Set to `true` to auto-consume tokens after granting entitlement for consumables. Defaults to `false`. | ## Checking entitlement status [Section titled “Checking entitlement status”](#checking-entitlement-status) Use `getPurchases()` for a cross-platform view of every transaction the stores report: ```typescript import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases'; const { purchases } = await NativePurchases.getPurchases({ productType: PURCHASE_TYPE.SUBS, }); purchases.forEach((purchase) => { if (purchase.isActive && purchase.expirationDate) { console.log('iOS sub active until', purchase.expirationDate); } const isAndroidIapValid = ['PURCHASED', '1'].includes(purchase.purchaseState ?? '') && purchase.isAcknowledged; if (isAndroidIapValid) { console.log('Grant in-app entitlement for', purchase.productIdentifier); } }); ``` ### Platform behavior [Section titled “Platform behavior”](#platform-behavior) * **iOS**: Subscriptions include `isActive`, `expirationDate`, `willCancel`, and StoreKit 2 listener support. In-app purchases require server receipt validation. * **Android**: `isActive`/`expirationDate` are not populated; call the Google Play Developer API with the `purchaseToken` for authoritative status. `purchaseState` must be `PURCHASED` and `isAcknowledged` must be `true`. ## API quick reference [Section titled “API quick reference”](#api-quick-reference) * `isBillingSupported()` – check for StoreKit / Google Play availability. * `getProduct()` / `getProducts()` – fetch price, localized title, description, intro offers. * `purchaseProduct()` – initiate StoreKit 2 or Billing client purchase flow. * `restorePurchases()` – replay historical purchases and sync to current device. * `getPurchases()` – list all iOS transactions or Play Billing purchases. * `manageSubscriptions()` – open the native subscription management UI. * `addListener('transactionUpdated')` – handle pending StoreKit 2 transactions when your app starts (iOS only). ## Best practices [Section titled “Best practices”](#best-practices) 1. **Show store pricing** – Apple requires displaying `product.title` and `product.priceString`; never hardcode. 2. **Use `appAccountToken`** – deterministically generate a UUID (v5) from your user ID to link purchases to accounts. 3. **Validate server-side** – send `receipt` (iOS) / `purchaseToken` (Android) to your backend for verification. 4. **Handle errors gracefully** – check for user cancellations, network failures, and unsupported billing environments. 5. **Test thoroughly** – follow the [iOS sandbox guide](/docs/plugins/native-purchases/ios-sandbox-testing/) and [Android sandbox guide](/docs/plugins/native-purchases/android-sandbox-testing/). 6. **Offer restore & management** – add UI buttons wired to `restorePurchases()` and `manageSubscriptions()`. ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) **Products not loading** * Make sure the bundle ID / application ID matches store configuration. * Confirm the product IDs are active and approved (App Store) or activated (Google Play). * Wait several hours after creating products; store propagation is not instant. **Purchase cancelled or stuck** * Users can cancel mid-flow; wrap calls in `try/catch` and surface friendly error messages. * For Android, ensure test accounts install the app from Play Store (internal track) so Billing works. * Check logcat/Xcode for billing errors when running on device. **Subscription state incorrect** * Use `getPurchases()` to compare store data with your local entitlement cache. * On Android, always query the Google Play Developer API with the `purchaseToken` to obtain expiration dates or refund status. * On iOS, check `isActive`/`expirationDate` and validate receipts to detect refunds or revocations. # iOS App Store Review Guidelines for IAP > Complete guide to passing App Store review with in-app purchases and subscriptions, including common rejection reasons and best practices. Getting your app approved on the App Store requires careful attention to Apple’s guidelines, especially when implementing in-app purchases and subscriptions. This guide covers everything you need to know to pass review on your first submission. ![iOS App Store Review Process](/native-purchases/review-guides/ios-review-hero.webp) ## In-App Purchase Requirements [Section titled “In-App Purchase Requirements”](#in-app-purchase-requirements) ### Pricing Transparency (Critical) [Section titled “Pricing Transparency (Critical)”](#pricing-transparency-critical) Apple requires crystal-clear pricing disclosure before any purchase: **Must-Have Elements:** * Display exact price before purchase button * Show billing frequency (e.g., “$9.99/month”) * Clearly state what users get for their money * Indicate when charges will occur **Common Rejection:** > “Subscription pricing must be clear and upfront.” :::caution Price Consistency All prices must match across: * App Store metadata listing * In-app purchase screens * Subscription management screens Even a $1 discrepancy between store listing ($4.99) and app ($5.99) will trigger automatic rejection. ::: ### Subscription Plan Presentation [Section titled “Subscription Plan Presentation”](#subscription-plan-presentation) **Required Disclosures:** * All available subscription tiers displayed together * Clear comparison of features per tier * No auto-defaulting to premium tiers through UI tricks * Easy-to-locate cancellation instructions ![UI Design Dos and Don'ts](/native-purchases/review-guides/ui-design-dos-donts.webp) **Example of Compliant UI:** ```typescript import { NativePurchases } from '@capgo/native-purchases'; function SubscriptionScreen() { return (

Choose Your Plan

{/* Show all tiers equally */} {/* Clear cancellation info */} Cancel anytime in Settings > Subscriptions. No refunds for partial periods.
); } ``` ### Restore Purchases [Section titled “Restore Purchases”](#restore-purchases) **Required Implementation:** Every app with IAP must provide a way for users to restore previous purchases without contacting support. ```typescript import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases'; async function restorePurchases() { try { await NativePurchases.restorePurchases(); const { purchases } = await NativePurchases.getPurchases({ productType: PURCHASE_TYPE.SUBS, }); const activeSub = purchases.find( (purchase) => purchase.isActive && purchase.expirationDate, ); if (activeSub) { unlockPremiumFeatures(); showMessage('Purchases restored successfully!'); return; } const { purchases: iaps } = await NativePurchases.getPurchases({ productType: PURCHASE_TYPE.INAPP, }); const hasIap = iaps.some((purchase) => purchase.productIdentifier === 'premium_unlock'); showMessage( hasIap ? 'Premium purchase restored!' : 'No previous purchases found.', ); } catch (error) { showError('Failed to restore purchases. Please try again.'); } } // Add a visible "Restore Purchases" button ``` ## Common Rejection Reasons [Section titled “Common Rejection Reasons”](#common-rejection-reasons) ### 1. App Crashes or Broken Functionality [Section titled “1. App Crashes or Broken Functionality”](#1-app-crashes-or-broken-functionality) **Why It Fails:** * App crashes on launch * Purchase flow fails to complete * Features shown in screenshots don’t work **Prevention:** * Test on real devices (not just simulators) * Test all subscription flows end-to-end * Verify receipt validation works * Check network error handling ### 2. Metadata Mismatches [Section titled “2. Metadata Mismatches”](#2-metadata-mismatches) **Why It Fails:** * Screenshots show features not in current build * Description mentions functionality that doesn’t exist * Pricing in metadata differs from in-app pricing ![Metadata Checklist](/native-purchases/review-guides/metadata-checklist.webp) **Prevention:** ```typescript // Document exactly what's in each tier const SUBSCRIPTION_FEATURES = { basic: ['Ad-free', 'Cloud sync', 'Basic themes'], premium: ['Ad-free', 'Cloud sync', 'All themes', 'Priority support'] }; // Use these in both your app AND App Store description ``` ### 3. Missing Permission Explanations [Section titled “3. Missing Permission Explanations”](#3-missing-permission-explanations) **Why It Fails:** * Requesting camera/location/health without explanation * Permission requests buried multiple screens deep * Vague or generic permission descriptions **Prevention:** Update your `Info.plist` with clear explanations: ```xml NSCameraUsageDescription Camera access is needed to scan product barcodes for quick subscription upgrades. NSLocationWhenInUseUsageDescription Location helps us show relevant local content in your Premium subscription. ``` ### 4. Misleading Marketing [Section titled “4. Misleading Marketing”](#4-misleading-marketing) **Why It Fails:** * Claims like “#1 app in world” without proof * “Unlimited” features that have hidden limits * Fake urgency tactics (“Only 2 spots left!”) ![Description Guidelines Examples](/native-purchases/review-guides/description-guidelines-1.webp) ![Additional Description Guidelines](/native-purchases/review-guides/description-guidelines-2.webp) **Prevention:** * Be specific and factual in descriptions * Avoid superlatives without evidence * Don’t pressure users with fake scarcity ### 5. Hidden Cancellation Process [Section titled “5. Hidden Cancellation Process”](#5-hidden-cancellation-process) **Why It Fails:** * No mention of how to cancel * Cancellation button hidden or obscured * Multi-step cancellation process without Apple’s native flow **Prevention:** ```typescript // Always inform users about cancellation function SubscriptionInfo() { return (

How to Cancel

  1. Open iPhone Settings
  2. Tap your name at the top
  3. Tap Subscriptions
  4. Select this app and tap Cancel

Or manage directly in the App Store app.

); } async function openSubscriptionManagement() { // Direct link to iOS subscription management await NativePurchases.showManageSubscriptions(); } ``` ## Privacy & Data Usage (Section 5.1.1) [Section titled “Privacy & Data Usage (Section 5.1.1)”](#privacy--data-usage-section-511) Apple has significantly tightened privacy requirements in 2025. ### Required Disclosures [Section titled “Required Disclosures”](#required-disclosures) **For Every Permission:** 1. Why you need it (specific use case) 2. When it will be used 3. How data is stored/shared 4. Whether it’s optional or required ### Example: Proper Permission Flow [Section titled “Example: Proper Permission Flow”](#example-proper-permission-flow) ```typescript async function requestCameraPermission() { // Show explanation BEFORE requesting await showDialog({ title: 'Camera Access', message: 'We need camera access to let you scan barcodes for quick product lookup. Your photos are never uploaded or stored.', buttons: ['Not Now', 'Allow'] }); // Then request permission const result = await Camera.requestPermissions(); return result.camera === 'granted'; } ``` ### Privacy Nutrition Labels [Section titled “Privacy Nutrition Labels”](#privacy-nutrition-labels) Ensure your App Store privacy labels accurately reflect: * Purchase history collection * Email addresses (for receipts) * Device IDs (for fraud prevention) * Usage data (for analytics) Inaccurate privacy labels are a common rejection reason in 2025. Audit your data collection carefully. ## Pre-Submission Checklist [Section titled “Pre-Submission Checklist”](#pre-submission-checklist) ![Pre-Submission Checklist](/native-purchases/review-guides/pre-submission-checklist.webp) 1. **Test All Purchase Flows** * Buy each subscription tier * Test free trials * Verify introductory offers apply correctly * Test restore purchases * Verify Family Sharing (if enabled) * Test on multiple devices 2. **Verify Pricing Consistency** * Check App Store metadata matches in-app prices * Verify all currencies are correct * Confirm free trial durations match descriptions * Check introductory offer terms are accurate 3. **Review All Copy** * Remove placeholder text * Verify claims are testable * Check grammar and spelling * Ensure descriptions match current build * Remove competitor mentions 4. **Test Permissions** * Request only necessary permissions * Show clear explanations before requesting * Test “Deny” flows (app should still work) * Verify Info.plist descriptions are clear 5. **Prepare Test Account** * Create sandbox test account * Document login credentials in App Review Notes * Verify test account has active subscription * Test that reviewer can complete purchase flow 6. **Check Metadata** * Screenshots match current UI * App preview video (if any) shows current version * Description accurately describes features * Age rating matches content * Privacy policy is accessible in-app 7. **Write Detailed Review Notes** ```plaintext Test Account: Email: reviewer@test.com Password: TestPass123! Testing Instructions: 1. Log in with test account above 2. Tap "Upgrade to Premium" button 3. Select "Monthly Premium" subscription 4. Complete purchase (no charge in sandbox) 5. Verify premium features unlock Note: Subscription pricing is clearly shown before purchase. Cancellation instructions are in Settings > Account. ``` ## Review Timeline [Section titled “Review Timeline”](#review-timeline) ![App Store Review Timeline](/native-purchases/review-guides/review-timeline.webp) **Standard Review:** 24-48 hours **Peak Periods:** 3-5 days (App Store holiday releases) **Weekends:** No reviews processed **Expedited Review:** Available for critical bug fixes (request via App Store Connect) Tip Submit early in the week to avoid weekend delays. Monday submissions typically get reviewed by Wednesday. ## 2025 Guideline Updates [Section titled “2025 Guideline Updates”](#2025-guideline-updates) ### New Requirements [Section titled “New Requirements”](#new-requirements) **1. AI Functionality Disclosure** If your app uses AI for any features, you must: * Clearly label AI-generated content * Explain how AI is used * Document content safety measures **2. Enhanced Subscription Clarity** * Side-by-side plan comparisons required * No “dark patterns” that hide cheaper options * Clear downgrade/upgrade paths **3. Privacy Intensification** * Section 5.1.1 enforcement increased * More scrutiny on data collection justification * Stricter requirements for children’s apps ### What Changed from 2024 [Section titled “What Changed from 2024”](#what-changed-from-2024) * Modular submissions now allowed (update product pages independently) * In-app events can be submitted separately * Stricter enforcement of misleading subscription UIs * New guidance on cryptocurrency/NFT apps ## Best Practices for Native Purchases Plugin [Section titled “Best Practices for Native Purchases Plugin”](#best-practices-for-native-purchases-plugin) ### Implement Proper Error Handling [Section titled “Implement Proper Error Handling”](#implement-proper-error-handling) ```typescript import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases'; async function handlePurchase(productId: string) { try { const transaction = await NativePurchases.purchaseProduct({ productIdentifier: productId, productType: PURCHASE_TYPE.SUBS, }); // Success await validateReceiptOnServer(transaction.receipt); showSuccess('Subscription activated!'); unlockFeatures(); } catch (error: any) { // Handle specific error cases if (error.code === 'USER_CANCELLED') { // User cancelled - don't show error console.log('Purchase cancelled by user'); } else if (error.code === 'PAYMENT_PENDING') { showInfo('Payment is pending. Please check back later.'); } else if (error.code === 'PRODUCT_ALREADY_PURCHASED') { // Restore instead await NativePurchases.restorePurchases(); } else { // Show user-friendly error showError('Unable to complete purchase. Please try again.'); } } } ``` ### Display Loading States [Section titled “Display Loading States”](#display-loading-states) ```typescript function PurchaseButton({ productId }: { productId: string }) { const [loading, setLoading] = useState(false); const handlePurchase = async () => { setLoading(true); try { await NativePurchases.purchaseProduct({ productIdentifier: productId }); } finally { setLoading(false); } }; return ( ); } ``` ### Show Terms Clearly [Section titled “Show Terms Clearly”](#show-terms-clearly) ```typescript function SubscriptionTerms() { return (

Subscription automatically renews unless cancelled at least 24 hours before the end of the current period.

Your account will be charged for renewal within 24 hours prior to the end of the current period.

Subscriptions may be managed by the user and auto-renewal may be turned off in Account Settings after purchase.

Terms of Service | Privacy Policy

); } ``` ## If Your App Gets Rejected [Section titled “If Your App Gets Rejected”](#if-your-app-gets-rejected) ### Steps to Resolve [Section titled “Steps to Resolve”](#steps-to-resolve) 1. **Read the rejection carefully** * Note the specific guideline cited (e.g., 3.1.1, 5.1.1) * Understand exactly what Apple flagged 2. **Fix the issue thoroughly** * Don’t just patch - fix root cause * Test the fix extensively * Document what you changed 3. **Respond in Resolution Center** ```plaintext Thank you for your feedback. I have addressed the issue: Issue: Subscription pricing not clear upfront Fix: Added explicit pricing display on subscription selection screen showing "$9.99/month" before purchase button. Also added cancellation instructions on the same screen. The changes are in this submission and can be tested using the provided test account. ``` 4. **Resubmit promptly** * Resubmissions are typically reviewed faster * Usually within 24 hours ### Appeal Process [Section titled “Appeal Process”](#appeal-process) If you believe the rejection is incorrect: ![App Store Clarification Process](/native-purchases/review-guides/clarification-process.webp) 1. Click “Appeal” in App Store Connect 2. Provide clear evidence: * Screenshots showing compliance * References to specific guidelines * Explanation of how you meet requirements 3. Be professional and factual 4. Include test account if functionality is hard to find ![Request for Documents Example](/native-purchases/review-guides/request-documents.webp) ## Additional Resources [Section titled “Additional Resources”](#additional-resources) * [Apple App Store Review Guidelines](https://developer.apple.com/app-store/review/guidelines/) * [In-App Purchase Guidelines](https://developer.apple.com/app-store/review/guidelines/#in-app-purchase) * [Subscriptions Best Practices](https://developer.apple.com/app-store/subscriptions/) * [App Store Connect Help](https://developer.apple.com/help/app-store-connect/) ## Support [Section titled “Support”](#support) If you’re still having issues: * Review the [Native Purchases documentation](/docs/plugins/native-purchases/getting-started/) * Check [common troubleshooting issues](/docs/plugins/native-purchases/getting-started/#troubleshooting) * Contact Apple Developer Support for guideline clarifications ### Need Expert Help? [Section titled “Need Expert Help?”](#need-expert-help) Struggling with app review or need personalized assistance? **[Book a consultation call with our team](https://cal.com/team/capgo/capacitor-consulting-services)** for dedicated support with: * IAP implementation review and optimization * App Store review preparation and strategy * Submission checklist review * Rejection resolution and appeals * Complete testing and validation Our experts have successfully helped hundreds of apps pass review! # Create iOS Auto-Renewable Subscription > Step-by-step guide to creating auto-renewable subscriptions in App Store Connect for the native-purchases plugin. Auto-renewable subscriptions provide recurring access to content, services, or premium features in your iOS app. This guide walks you through creating subscriptions in App Store Connect. ## Overview [Section titled “Overview”](#overview) Auto-renewable subscriptions automatically renew at the end of each billing period until users cancel. They’re perfect for: * Premium content and features * Ad-free experiences * Cloud storage and sync * Streaming services * Professional tools and utilities ## Prerequisites [Section titled “Prerequisites”](#prerequisites) Before creating subscriptions, you must: 1. [Create a subscription group](/docs/plugins/native-purchases/ios-subscription-group/) to organize your subscriptions 2. Have an active Apple Developer Program membership 3. Complete banking and tax information in App Store Connect ## Creating a Subscription [Section titled “Creating a Subscription”](#creating-a-subscription) 1. **Navigate to Subscriptions** In App Store Connect, select your app and go to **Monetize > Subscriptions**. Select your subscription group or create a new one if needed. ![Navigate to subscriptions](/native-purchases/ios/create-subscription/navigate-to-subscriptions.webp) 2. **Create New Subscription** Click the **+** icon next to your subscription group to add a new subscription. 3. **Enter Basic Information** **Reference Name**: Descriptive name for your internal use (not shown to customers) * Examples: “Premium Monthly”, “Ultimate Annual”, “Basic Plan” **Product ID**: Unique identifier for this subscription (cannot be changed later) * Format: `com.yourcompany.yourapp.premium_monthly` * Use descriptive, lowercase names with underscores * Required for configuring the native-purchases plugin ![Enter subscription details](/native-purchases/ios/create-subscription/enter-subscription-info.webp) 4. **Configure Duration** Select the subscription duration from available options: * 1 week * 1 month * 2 months * 3 months * 6 months * 1 year The duration determines how often users are billed. 5. **Set Up Pricing** Click **Add Subscription Price** to configure pricing: **Base Territory**: Select your primary market (usually your country) **Price**: Set the subscription price * Apple automatically converts to other currencies * Choose from Apple’s price tiers * Consider perceived value and market rates ![Configure pricing](/native-purchases/ios/create-subscription/configure-pricing.webp) 6. **Family Sharing (Optional)** Decide whether to enable Family Sharing, which allows up to 6 family members to access the subscription. Caution Once Family Sharing is enabled, it cannot be turned off for this product. **Enable if:** * Content is appropriate for family use * You want to increase value proposition * Your business model supports it **Don’t enable if:** * Subscription is for individual use only * Content is personalized to the user * You want to maximize revenue per user 7. **Add Localizations** Add subscription display information in all languages your app supports: **Subscription Display Name**: Customer-facing name (e.g., “Premium Monthly”) **Description**: Brief description of what the subscription includes * Keep it concise and benefit-focused * Mention key features * Highlight value proposition ![Add localizations](/native-purchases/ios/create-subscription/add-localization.webp) 8. **App Store Promotional Image (Optional)** Upload a promotional image for this subscription (312x390 pixels): * Shows in the App Store subscription page * Should match your app’s design * Include subscription name for clarity Note While images are optional for initial submission, they’re required for promotional display in the App Store. You can add them later. 9. **Save and Submit** Click **Save** to create the subscription. **For First Subscription:** * Must be submitted with a new app version * Include in your next App Store submission * Cannot submit independently **For Subsequent Subscriptions:** * Can be submitted directly from the Subscriptions page * Don’t require a new app version * Available after first subscription is approved ## Subscription Status [Section titled “Subscription Status”](#subscription-status) Your subscription will have one of these statuses: | Status | Description | Can Test? | | ---------------------- | -------------------------- | ------------- | | **Missing Metadata** | Incomplete setup | Yes (sandbox) | | **Ready to Submit** | Complete but not submitted | Yes (sandbox) | | **Waiting for Review** | Submitted to Apple | Yes (sandbox) | | **In Review** | Being reviewed by Apple | Yes (sandbox) | | **Approved** | Available for purchase | Yes | | **Rejected** | Needs changes | Yes (sandbox) | Tip You can test subscriptions in sandbox mode even if they show “Missing Metadata” or “Ready to Submit”! ## Using in Your App [Section titled “Using in Your App”](#using-in-your-app) Once created, reference the subscription in your app using the product ID: ```typescript import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases'; // Fetch subscription products direct from StoreKit const { products } = await NativePurchases.getProducts({ productIdentifiers: [ 'com.yourcompany.yourapp.premium_monthly', 'com.yourcompany.yourapp.premium_annual', ], productType: PURCHASE_TYPE.SUBS, }); products.forEach((product) => { console.log(`${product.title}: ${product.priceString}`); console.log(`Duration: ${product.subscriptionPeriod}`); console.log(`Description: ${product.description}`); }); // Purchase a subscription (StoreKit 2 automatically handles intro pricing and offers) try { const transaction = await NativePurchases.purchaseProduct({ productIdentifier: 'com.yourcompany.yourapp.premium_monthly', productType: PURCHASE_TYPE.SUBS, }); console.log('Transaction ID:', transaction.transactionId); // StoreKit receipts are included on iOS for server-side validation await sendReceiptToBackend(transaction.receipt); } catch (error) { console.error('Purchase failed:', error); } // Check subscription status using the store's data const { purchases } = await NativePurchases.getPurchases({ productType: PURCHASE_TYPE.SUBS, }); const premium = purchases.find( (purchase) => purchase.productIdentifier === 'com.yourcompany.yourapp.premium_monthly', ); if (premium?.isActive) { console.log('Expires:', premium.expirationDate); console.log('Will renew:', premium.willCancel === false); console.log('Store state:', premium.subscriptionState); unlockPremiumFeatures(); } else { showPaywall(); } ``` ## Best Practices [Section titled “Best Practices”](#best-practices) ### Pricing Strategy [Section titled “Pricing Strategy”](#pricing-strategy) * **Monthly plans**: Lower barrier to entry, builds habit * **Annual plans**: Better value, higher LTV, lower churn * **Multiple tiers**: Basic, Premium, Ultimate for different user segments * **Competitive analysis**: Research similar apps’ pricing ### Product IDs [Section titled “Product IDs”](#product-ids) * Use consistent naming: `company.app.tier_duration` * Include tier and duration in ID: `premium_monthly`, `ultimate_annual` * Avoid changing product IDs (they’re permanent) * Document all product IDs for your team ### Family Sharing [Section titled “Family Sharing”](#family-sharing) * Enable for family-oriented apps (games, educational, entertainment) * Consider impact on revenue * Test sharing behavior thoroughly * Communicate sharing capability in marketing ### Localization [Section titled “Localization”](#localization) * Translate all subscription names and descriptions * Consider regional pricing differences * Test display in all supported languages * Use culturally appropriate marketing language ### Promotional Images [Section titled “Promotional Images”](#promotional-images) * Maintain consistent visual style * Include subscription name and key benefit * Update for seasonal promotions * Match app’s overall design language ## Common Subscription Patterns [Section titled “Common Subscription Patterns”](#common-subscription-patterns) ### Single Tier (Freemium) [Section titled “Single Tier (Freemium)”](#single-tier-freemium) ```plaintext Free App + Premium Subscription - Basic: Free (limited features) - Premium Monthly: $4.99 - Premium Annual: $39.99 (save 33%) ``` ### Multi-Tier (Good, Better, Best) [Section titled “Multi-Tier (Good, Better, Best)”](#multi-tier-good-better-best) ```plaintext - Basic Monthly: $4.99 - Premium Monthly: $9.99 - Ultimate Monthly: $19.99 - Basic Annual: $49.99 - Premium Annual: $99.99 - Ultimate Annual: $199.99 ``` ### Consumable + Subscription Hybrid [Section titled “Consumable + Subscription Hybrid”](#consumable--subscription-hybrid) ```plaintext - Credit packs (consumable) - Monthly subscription (unlimited credits) - Annual subscription (unlimited + bonus features) ``` ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) **Subscription not loading in app:** * Verify product ID matches exactly (case-sensitive) * Check subscription is in subscription group * Ensure bundle identifier matches App Store Connect * Wait 2-3 hours after creating product **Cannot submit subscription:** * Complete all required fields (name, description, price) * Add at least one localization * Verify banking/tax info is approved * Check if first subscription (requires app version) **Family Sharing toggle disabled:** * Already enabled (cannot be disabled) * Check in subscription details * Contact Apple Support if stuck **Price tier not available:** * May be restricted in some territories * Choose alternative tier * Contact Apple for pricing questions **“Invalid Product ID” error:** * Must be reverse domain format * Cannot contain spaces or special characters * Check for typos * Verify uniqueness across all products ## Next Steps [Section titled “Next Steps”](#next-steps) * [Create an introductory offer](/docs/plugins/native-purchases/ios-introductory-offer/) to attract new subscribers * [Configure sandbox testing](/docs/plugins/native-purchases/ios-sandbox-testing/) to test your subscriptions * Set up promotional offers for win-back and retention * Implement subscription analytics tracking ## Additional Resources [Section titled “Additional Resources”](#additional-resources) For more details, refer to the [official Apple documentation on auto-renewable subscriptions](https://developer.apple.com/documentation/storekit/in-app_purchase/subscriptions_and_offers). # Create iOS Subscription Introductory Offer > Learn how to create introductory offers for auto-renewable subscriptions on iOS to attract and convert new subscribers. Introductory offers allow you to provide eligible users with free trials or discounted introductory pricing to reduce barriers to entry and increase subscription conversions. ## Overview [Section titled “Overview”](#overview) Introductory offers are one of the most effective tools for growing your subscriber base. They allow users to: * Try your premium features risk-free * Experience value before committing * Start at a lower price point * Build confidence in your product ## Offer Types [Section titled “Offer Types”](#offer-types) iOS supports three types of introductory offers: ### 1. Free Trial [Section titled “1. Free Trial”](#1-free-trial) Customers get complimentary access for a specified duration. After the trial, they’re charged at standard rates if they don’t cancel. **Examples:** * 7 days free * 14 days free * 1 month free **Best for:** * High-value subscriptions * Feature-rich apps * Building user habit ### 2. Pay Up Front [Section titled “2. Pay Up Front”](#2-pay-up-front) Customers pay a single discounted price that covers the introductory period. **Examples:** * $1.99 for 2 months (then $9.99/month) * $9.99 for 3 months (then $19.99/month) **Best for:** * Commitment signals * Cash flow needs * Testing price sensitivity ### 3. Pay As You Go [Section titled “3. Pay As You Go”](#3-pay-as-you-go) Customers pay a reduced price for multiple billing cycles. **Examples:** * $1.99/month for 3 months (then $9.99/month) * $4.99/month for 6 months (then $14.99/month) **Best for:** * Gradual commitment * Long-term value demonstration * Reducing perceived risk ## Eligibility Requirements [Section titled “Eligibility Requirements”](#eligibility-requirements) Users can only receive introductory offers if they: * Haven’t previously received an introductory offer for the product * Haven’t received an introductory offer for any product in the same subscription group * Haven’t had an active subscription to the product Note Apple handles eligibility checking automatically. The native-purchases plugin provides methods to check eligibility before presenting offers. ## Prerequisites [Section titled “Prerequisites”](#prerequisites) You must first [create an auto-renewable subscription](/docs/plugins/native-purchases/ios-create-subscription/) before adding an introductory offer. ## Creating an Introductory Offer [Section titled “Creating an Introductory Offer”](#creating-an-introductory-offer) 1. **Navigate to Subscription** In App Store Connect, go to your app’s **Monetize > Subscriptions** section and select the subscription you want to add an offer to. 2. **Add Subscription Price** Click the **+** icon next to “Subscription Prices” to open the pricing modal. 3. **Create Introductory Offer** Select **“Create introductory offer”** from the options. ![Create introductory offer](/native-purchases/ios/introductory-offer/create-introductory-offer.webp) 4. **Configure Countries and Start Date** **Countries and Regions**: Select where the offer will be available * Choose all countries for maximum reach * Or limit to specific markets for testing **Start Date**: When the offer becomes available * Can be immediate or scheduled for the future * Useful for coordinating with marketing campaigns **End Date (Optional)**: When the offer expires * Leave blank for ongoing availability * Set a date for limited-time promotions 5. **Select Offer Type** Choose one of the three offer types: **Free** (Free Trial) * Select duration (days, weeks, months) * Examples: 7 days, 2 weeks, 1 month **Pay Up Front** * Set single payment price * Set duration covered by payment * Example: $1.99 for 2 months **Pay As You Go** * Set discounted price per period * Set number of periods * Example: $2.99/month for 3 months 6. **Review and Confirm** Review the summary showing: * Offer type and duration * Pricing details * Regular price after intro period * Availability dates and countries 7. **Save** Click **Save** to create the introductory offer. It will be available for testing immediately in sandbox mode. ## Offer Configuration Examples [Section titled “Offer Configuration Examples”](#offer-configuration-examples) ### Example 1: Standard Free Trial [Section titled “Example 1: Standard Free Trial”](#example-1-standard-free-trial) ```plaintext Type: Free Duration: 7 days Then: $9.99/month ``` **User Journey:** * Day 1-7: Free access * Day 8: First charge of $9.99 * Monthly charges continue ### Example 2: Upfront Discounted Period [Section titled “Example 2: Upfront Discounted Period”](#example-2-upfront-discounted-period) ```plaintext Type: Pay Up Front Price: $4.99 Duration: 3 months Then: $9.99/month ``` **User Journey:** * Day 1: Charged $4.99 * 90 days access * Day 91: Charged $9.99/month ### Example 3: Gradual Introduction [Section titled “Example 3: Gradual Introduction”](#example-3-gradual-introduction) ```plaintext Type: Pay As You Go Price: $2.99/month Periods: 6 months Then: $9.99/month ``` **User Journey:** * Months 1-6: $2.99/month * Month 7+: $9.99/month ## Using in Your App [Section titled “Using in Your App”](#using-in-your-app) The native-purchases plugin automatically handles introductory offer presentation and eligibility: ```typescript import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases'; // Fetch products with intro offer information const { products } = await NativePurchases.getProducts({ productIdentifiers: ['com.yourapp.premium_monthly'], productType: PURCHASE_TYPE.SUBS, }); const product = products[0]; // Display intro offer details (StoreKit sends localized metadata) if (product.introductoryPrice) { console.log('Intro price:', product.introductoryPriceString); console.log('Intro period:', product.introductoryPricePeriod); console.log('Intro cycles:', product.introductoryPriceCycles); console.log('Regular price:', product.priceString); } else { console.log('No intro offer configured'); } // Purchase (StoreKit automatically applies intro pricing if eligible) try { const transaction = await NativePurchases.purchaseProduct({ productIdentifier: 'com.yourapp.premium_monthly', productType: PURCHASE_TYPE.SUBS, }); console.log('Subscription active, receipt length:', transaction.receipt?.length); await validateReceiptOnServer(transaction.receipt); } catch (error) { console.error('Purchase failed:', error); } ``` ## Displaying Intro Offers to Users [Section titled “Displaying Intro Offers to Users”](#displaying-intro-offers-to-users) ### Best Practices for UI [Section titled “Best Practices for UI”](#best-practices-for-ui) **Clear Value Proposition:** ```plaintext Try Premium Free for 7 Days Then $9.99/month. Cancel anytime. ``` **Emphasize Savings:** ```plaintext Start at Just $1.99 Get 3 months of Premium for only $1.99 Then $9.99/month ``` **Transparent Communication:** ```plaintext Your Free Trial • Access all premium features • No charge for 7 days • $9.99/month after trial • Cancel anytime, even during trial ``` ### Example Implementation [Section titled “Example Implementation”](#example-implementation) ```typescript function formatIntroOffer(product: any): string { if (!product.introductoryPrice) { return `${product.priceString} per ${product.subscriptionPeriod}`; } const intro = product.introductoryPrice; const regular = product.priceString; if (intro.price === 0) { // Free trial return `Try free for ${intro.periodString}, then ${regular}`; } else if (intro.cycles === 1) { // Pay up front return `${intro.priceString} for ${intro.periodString}, then ${regular}`; } else { // Enterprise return `${intro.priceString} for ${intro.cycles} ${intro.periodString}s, then ${regular}`; } } ``` ## Marketing Best Practices [Section titled “Marketing Best Practices”](#marketing-best-practices) ### Trial Length Strategy [Section titled “Trial Length Strategy”](#trial-length-strategy) * **3-7 days**: Quick decision apps, games * **7-14 days**: Standard for most apps * **14-30 days**: Complex tools, professional apps * **30+ days**: High-value B2B or enterprise ### Pricing Psychology [Section titled “Pricing Psychology”](#pricing-psychology) * **$0.99-$1.99**: Very low barrier, good for testing * **50% off**: Strong perceived value * **First month free**: Common, familiar pattern ### Communication Timing [Section titled “Communication Timing”](#communication-timing) * **Before trial ends**: Remind users of upcoming charge * **Highlight value**: Show usage stats, achievements * **Easy cancellation**: Build trust with transparent process ## Testing Intro Offers [Section titled “Testing Intro Offers”](#testing-intro-offers) Use sandbox testing to verify behavior: ```typescript import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases'; // In sandbox mode, accelerated subscription durations apply: // - 3 days free trial = 3 minutes // - 1 week free trial = 3 minutes // - 1 month free trial = 5 minutes const { products } = await NativePurchases.getProducts({ productIdentifiers: ['premium_monthly'], productType: PURCHASE_TYPE.SUBS, }); // Purchase with intro offer const transaction = await NativePurchases.purchaseProduct({ productIdentifier: 'premium_monthly', productType: PURCHASE_TYPE.SUBS, }); console.log('Intro purchase transaction:', transaction.transactionId); // Wait for accelerated renewal setTimeout(async () => { const { purchases } = await NativePurchases.getPurchases({ productType: PURCHASE_TYPE.SUBS, }); const premium = purchases.find((purchase) => purchase.productIdentifier === 'premium_monthly'); console.log('After trial state:', premium?.subscriptionState); }, 180000); // 3 minutes for weekly trial ``` ## Important Notes [Section titled “Important Notes”](#important-notes) ### Eligibility Rules [Section titled “Eligibility Rules”](#eligibility-rules) * One intro offer per user per subscription group (lifetime) * Applies to new subscribers only * Cannot be used again after cancellation * Not available for subscription upgrades/crossgrades ### StoreKit API [Section titled “StoreKit API”](#storekit-api) * `introductoryPrice` shows intro offer details * `eligibility` method checks if user qualifies * Automatically applied at purchase time * No special purchase method needed ### Limitations [Section titled “Limitations”](#limitations) * Only one intro offer active per subscription at a time * Cannot combine with other discount types * Cannot change eligibility rules * Apple controls eligibility checking ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) **Intro offer not showing:** * Check if offer is activated in App Store Connect * Verify user hasn’t used an intro offer before * Ensure user hasn’t subscribed to anything in the group * Test with new sandbox account **Eligibility check failing:** * Wait for App Store sync (can take 2-3 hours) * Verify product ID is correct * Check subscription group configuration * Test in sandbox with fresh test account **Wrong price displaying:** * Check regional pricing settings * Verify currency conversion * Ensure offer dates are current * Refresh product information **Sandbox testing issues:** * Use accelerated durations (3 min = 1 week) * Create new test accounts for each test * Wait for trial to complete naturally * Check renewal count (max 6 in sandbox) ## Analytics and Optimization [Section titled “Analytics and Optimization”](#analytics-and-optimization) ### Track These Metrics [Section titled “Track These Metrics”](#track-these-metrics) * Intro offer acceptance rate * Trial-to-paid conversion rate * Cancellation during trial * Retention after first charge * Revenue impact ### A/B Testing Ideas [Section titled “A/B Testing Ideas”](#ab-testing-ideas) * Free trial vs. paid intro * Trial length variations * Discount percentage * Single payment vs. recurring discount ### Optimization Strategy [Section titled “Optimization Strategy”](#optimization-strategy) ```typescript // Track offer performance analytics.track('intro_offer_displayed', { product_id: product.identifier, offer_type: product.introductoryPriceType, offer_duration: product.introductoryPricePeriod }); analytics.track('intro_offer_accepted', { product_id: product.identifier }); // Monitor conversion NativePurchases.addListener('transactionUpdated', (transaction) => { if (transaction.productIdentifier === product.identifier && transaction.isActive) { analytics.track('trial_converted', { transactionId: transaction.transactionId, productId: transaction.productIdentifier, }); } }); ``` ## Next Steps [Section titled “Next Steps”](#next-steps) * [Configure sandbox testing](/docs/plugins/native-purchases/ios-sandbox-testing/) to test your intro offers * Set up promotional offers for win-back campaigns * Implement subscription analytics * Create targeted marketing campaigns ## Additional Resources [Section titled “Additional Resources”](#additional-resources) For more details, refer to the [official Apple documentation on introductory offers](https://developer.apple.com/documentation/storekit/in-app_purchase/subscriptions_and_offers/implementing_introductory_offers_in_your_app). # Configure iOS Sandbox Testing > Learn how to set up sandbox testing for in-app purchases on iOS using App Store Connect and Xcode. Testing in-app purchases on iOS requires proper configuration in App Store Connect and on your test devices. This guide covers everything you need to get started with sandbox testing. ## Prerequisites [Section titled “Prerequisites”](#prerequisites) * **Apple Developer Program**: Active membership with annual renewal * **Agreements**: Signed “Paid Applications Agreement” with banking and tax information completed * **Xcode Project**: Configured with proper bundle identifier and capabilities Note Banking and tax setup approval can take hours to days. Complete this well in advance of testing. ## Setup Process [Section titled “Setup Process”](#setup-process) 1. **Sign Paid Applications Agreement** In App Store Connect, navigate to **Agreements, Tax, and Banking** and complete: * Sign the Paid Applications Agreement * Add your banking information * Complete tax forms Wait for Apple to approve your information (this can take 24-48 hours). 2. **Create Sandbox Test User** In App Store Connect, go to **Users and Access > Sandbox Testers**. Click the **+** button to create a new sandbox tester. **Important**: Use an email address that is NOT already associated with an Apple ID. You can use email aliases: * Gmail: `youremail+test@gmail.com` * iCloud: `youremail+test@icloud.com` ![Create sandbox tester](/native-purchases/ios/sandbox-testing/sandbox-tester-setup.webp) 3. **Configure Test Device (iOS 12+)** Starting with iOS 12, you no longer need to sign out of your iTunes account to test purchases. On your iOS device: 1. Open **Settings** 2. Tap **App Store** 3. Scroll to the bottom 4. Tap **Sandbox Account** 5. Sign in with your sandbox test account Tip This is much more convenient than the old method of signing out of your iTunes account! 4. **Configure Xcode Project** Ensure your Xcode project has: **Bundle Identifier** * Must match the identifier in your Developer Center * Must match the identifier in App Store Connect **In-App Purchase Capability** 1. Select your project in Xcode 2. Go to **Signing & Capabilities** 3. Click **+ Capability** 4. Add **In-App Purchase** 5. **Create In-App Purchase Products** In App Store Connect, navigate to your app and create your in-app purchase products (subscriptions, consumables, etc.). Products must be in at least “Ready to Submit” status for sandbox testing. 6. **Test Your Implementation** Build and run your app on a test device. When you attempt a purchase, you should see: > **\[Environment: Sandbox]** This confirmation indicates you’re in the sandbox environment and won’t be charged real money. ## Important Notes [Section titled “Important Notes”](#important-notes) ### Sandbox Environment Characteristics [Section titled “Sandbox Environment Characteristics”](#sandbox-environment-characteristics) * **No real charges**: All purchases are free in sandbox mode * **Accelerated subscriptions**: Subscription durations are shortened for faster testing * 1 week subscription = 3 minutes * 1 month subscription = 5 minutes * 2 months subscription = 10 minutes * 3 months subscription = 15 minutes * 6 months subscription = 30 minutes * 1 year subscription = 1 hour * **Auto-renewal limit**: Subscriptions auto-renew up to 6 times in sandbox * **Immediate cancellation**: Cancelled subscriptions expire immediately ### Sandbox Account Management [Section titled “Sandbox Account Management”](#sandbox-account-management) * Create multiple test accounts for different scenarios * Use test accounts only on test devices * Don’t use personal Apple ID for sandbox testing * Test accounts can purchase any product regardless of region ## Using Sandbox Testing [Section titled “Using Sandbox Testing”](#using-sandbox-testing) ```typescript import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases'; const { isBillingSupported } = await NativePurchases.isBillingSupported(); if (!isBillingSupported) { throw new Error('StoreKit not supported on this device'); } // Fetch products (automatically uses sandbox when available) const { products } = await NativePurchases.getProducts({ productIdentifiers: ['premium_monthly'], productType: PURCHASE_TYPE.SUBS, }); // Make test purchase const transaction = await NativePurchases.purchaseProduct({ productIdentifier: 'premium_monthly', productType: PURCHASE_TYPE.SUBS, }); console.log('Test purchase successful!', transaction.transactionId); ``` ## Verification [Section titled “Verification”](#verification) When properly configured, you should observe: 1. **Sandbox banner** during purchase: “\[Environment: Sandbox]” 2. **Products load** successfully 3. **Purchases complete** without actual charges 4. **Receipts validate** correctly 5. **Subscriptions renew** automatically (at accelerated rate) ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) **Products not loading:** * Verify bundle identifier matches App Store Connect * Check that agreements are signed and approved * Ensure products are at least “Ready to Submit” status * Wait 2-3 hours after creating products **“Cannot connect to iTunes Store”:** * Verify sandbox account is configured correctly * Check device is connected to internet * Try signing out and back into sandbox account * Restart the app **Purchases failing silently:** * Check Xcode console for error messages * Verify In-App Purchase capability is enabled * Ensure sandbox account email is not a real Apple ID * Try creating a new sandbox test account **Receipt validation errors:** * Use sandbox receipt validation endpoint in testing * Production endpoint: `https://buy.itunes.apple.com/verifyReceipt` * Sandbox endpoint: `https://sandbox.itunes.apple.com/verifyReceipt` * The native-purchases plugin handles this automatically **Wrong subscription duration:** * Remember subscriptions are accelerated in sandbox * Use the conversion chart above for expected durations * Subscriptions auto-renew max 6 times in sandbox **“This Apple ID has not yet been used in the iTunes Store”:** * This is normal for new sandbox accounts * Proceed with the purchase to activate the account * Only happens on first use ## Best Practices [Section titled “Best Practices”](#best-practices) 1. **Create multiple test accounts** for different test scenarios 2. **Test all subscription durations** to verify behavior 3. **Test cancellation and renewal** flows 4. **Verify receipt validation** works correctly 5. **Test restore purchases** functionality 6. **Check subscription upgrade/downgrade** behavior 7. **Test with poor network conditions** ## Production vs. Sandbox [Section titled “Production vs. Sandbox”](#production-vs-sandbox) | Feature | Sandbox | Production | | --------------------- | ----------- | -------------- | | Real charges | No | Yes | | Subscription duration | Accelerated | Normal | | Auto-renewal limit | 6 times | Unlimited | | Cancellation effect | Immediate | End of period | | Receipt endpoint | Sandbox URL | Production URL | | Test accounts only | Yes | No | ## Additional Resources [Section titled “Additional Resources”](#additional-resources) For more details, refer to the [official Apple StoreKit documentation](https://developer.apple.com/documentation/storekit/in-app_purchase/testing_in-app_purchases_with_sandbox) on sandbox testing. # Create iOS Subscription Group > Learn how to create and configure subscription groups in App Store Connect for organizing your app's subscription offerings. Subscription groups are essential for organizing and managing multiple subscription levels in your iOS app. Understanding how they work is crucial for implementing upgrade, downgrade, and crossgrade functionality. ## What is a Subscription Group? [Section titled “What is a Subscription Group?”](#what-is-a-subscription-group) A subscription group is a collection of related subscriptions that users can choose between. Users can only subscribe to one subscription within a group at a time. When they switch subscriptions, Apple handles the transition automatically. ## Why Subscription Groups Matter [Section titled “Why Subscription Groups Matter”](#why-subscription-groups-matter) Subscription groups enable: * **Tiered pricing**: Offer basic, premium, and ultimate plans * **Different durations**: Monthly, yearly, and lifetime options * **Upgrade/downgrade logic**: Automatic handling of subscription changes * **Simplified management**: Group related subscriptions together ## Subscription Levels [Section titled “Subscription Levels”](#subscription-levels) Within a group, each subscription should be ranked from highest value (level 1) to lowest value. This ranking determines how subscription changes are classified: ![Subscription group hierarchy](/native-purchases/ios/subscription-group/subscription-group-hierarchy.webp) ### Level Examples [Section titled “Level Examples”](#level-examples) **Level 1** (Highest Value) * Premium Annual ($99.99/year) * Ultimate Monthly ($19.99/month) **Level 2** (Medium Value) * Standard Annual ($49.99/year) * Premium Monthly ($9.99/month) **Level 3** (Lowest Value) * Basic Annual ($29.99/year) * Standard Monthly ($4.99/month) ## Subscription Change Types [Section titled “Subscription Change Types”](#subscription-change-types) Apple automatically handles three types of subscription changes based on the level ranking: ### 1. Upgrade [Section titled “1. Upgrade”](#1-upgrade) Moving to a **higher-tier** subscription (e.g., level 2 → level 1). **Behavior:** * Takes effect **immediately** * User receives **prorated refund** for remaining time * New subscription starts right away **Example:** ```typescript // User currently has: Standard Monthly (Level 2) // User upgrades to: Premium Annual (Level 1) // Result: Immediate access to Premium, refund for unused Standard time ``` ### 2. Downgrade [Section titled “2. Downgrade”](#2-downgrade) Moving to a **lower-tier** subscription (e.g., level 1 → level 2). **Behavior:** * Takes effect at **next renewal date** * User keeps current subscription until period ends * New subscription starts automatically after expiration **Example:** ```typescript // User currently has: Premium Annual (Level 1) // User downgrades to: Standard Monthly (Level 2) // Result: Premium access continues until annual renewal date, then switches ``` ### 3. Crossgrade [Section titled “3. Crossgrade”](#3-crossgrade) Switching to another subscription **at the same tier level**. **Behavior depends on duration:** **Different Duration** → Behaves like **downgrade** * Takes effect at next renewal date * Example: Monthly Premium (Level 1) → Annual Premium (Level 1) **Same Duration** → Behaves like **upgrade** * Takes effect immediately * Example: Premium Monthly (Level 1) → Ultimate Monthly (Level 1) ## Creating a Subscription Group [Section titled “Creating a Subscription Group”](#creating-a-subscription-group) 1. **Navigate to Subscriptions** In App Store Connect, select your app and go to **Monetize > Subscriptions**. 2. **Create Group** Click **+** next to “Subscription Groups” to create a new group. 3. **Name the Group** Choose a descriptive name that reflects the subscriptions it contains: * “Premium Access” * “Cloud Storage Plans” * “Pro Features” 4. **Add Subscriptions** After creating the group, add individual subscriptions to it. Each subscription will have a level ranking. 5. **Set Level Rankings** Arrange subscriptions from highest value (1) to lowest value. Consider: * Annual plans typically rank higher than monthly * Higher-priced tiers rank above lower-priced ones * Ultimate/premium tiers rank highest ## Using in Your App [Section titled “Using in Your App”](#using-in-your-app) The native-purchases plugin automatically handles subscription group logic: ```typescript import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases'; // Fetch all subscriptions in a group const { products } = await NativePurchases.getProducts({ productIdentifiers: ['premium_monthly', 'premium_annual', 'ultimate_monthly'], productType: PURCHASE_TYPE.SUBS, }); // Display current subscription using StoreKit transactions const { purchases } = await NativePurchases.getPurchases({ productType: PURCHASE_TYPE.SUBS, }); const activeSubs = purchases.filter((purchase) => purchase.isActive); // Detect pending downgrade/cancellation (StoreKit sets willCancel === true) const pendingChange = purchases.find((purchase) => purchase.willCancel === true); if (pendingChange) { console.log('Subscription will stop auto-renewing on', pendingChange.expirationDate); } // Purchase (StoreKit handles upgrades/downgrades automatically) await NativePurchases.purchaseProduct({ productIdentifier: 'premium_annual', productType: PURCHASE_TYPE.SUBS, }); // Listen for StoreKit updates (fires on upgrades/downgrades/refunds) NativePurchases.addListener('transactionUpdated', (transaction) => { console.log('Subscription updated:', transaction); }); ``` ## Handling Subscription Changes [Section titled “Handling Subscription Changes”](#handling-subscription-changes) ### Detecting Change Type [Section titled “Detecting Change Type”](#detecting-change-type) ```typescript import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases'; // Get current subscription info const { purchases } = await NativePurchases.getPurchases({ productType: PURCHASE_TYPE.SUBS, }); const currentSubscription = purchases.find( (purchase) => purchase.subscriptionState === 'subscribed', ); if (currentSubscription) { // StoreKit reports if user cancelled auto-renew if (currentSubscription.willCancel) { console.log( `User cancelled. Access remains until ${currentSubscription.expirationDate}`, ); } if (currentSubscription.isUpgraded) { console.log('User recently upgraded to this plan.'); } } // Listen for automatic upgrades/downgrades NativePurchases.addListener('transactionUpdated', (transaction) => { console.log('Subscription changed!', transaction); if (transaction.subscriptionState === 'revoked') { revokeAccess(); } else if (transaction.isActive) { unlockPremiumFeatures(); } }); ``` ### User Communication [Section titled “User Communication”](#user-communication) Always communicate the change behavior clearly: **For Upgrades:** > “You’ll get immediate access to Premium features. We’ll prorate your current subscription.” **For Downgrades:** > “You’ll keep Premium access until \[renewal date], then switch to Standard.” **For Crossgrades:** > “Your plan will change to Annual billing at the next renewal on \[date].” ## Server monitoring [Section titled “Server monitoring”](#server-monitoring) Use Apple’s App Store Server Notifications v2 or your own receipt-validation backend to mirror StoreKit changes in your database. Pair server notifications with the `transactionUpdated` listener so both client and backend stay in sync. ## Best Practices [Section titled “Best Practices”](#best-practices) ### Group Organization [Section titled “Group Organization”](#group-organization) * Keep related subscriptions in the same group * Don’t mix unrelated features (e.g., storage and ad removal) * Create separate groups for different feature sets ### Level Ranking Strategy [Section titled “Level Ranking Strategy”](#level-ranking-strategy) * Annual plans → Higher level than monthly (for same tier) * Higher-priced tiers → Higher level * Consider value, not just price ### User Experience [Section titled “User Experience”](#user-experience) * Show current subscription clearly * Display all available options in the group * Indicate which changes are immediate vs. at renewal * Allow easy switching between plans ### Testing [Section titled “Testing”](#testing) * Test all upgrade scenarios * Test all downgrade scenarios * Verify crossgrade behavior * Check webhook firing ## Common Scenarios [Section titled “Common Scenarios”](#common-scenarios) ### Scenario 1: Three-Tier Monthly Plans [Section titled “Scenario 1: Three-Tier Monthly Plans”](#scenario-1-three-tier-monthly-plans) ```plaintext Level 1: Ultimate Monthly ($19.99) Level 2: Premium Monthly ($9.99) Level 3: Basic Monthly ($4.99) ``` * Basic → Premium: Upgrade (immediate) * Premium → Ultimate: Upgrade (immediate) * Ultimate → Premium: Downgrade (at renewal) * Basic → Ultimate: Upgrade (immediate) ### Scenario 2: Mixed Duration Plans [Section titled “Scenario 2: Mixed Duration Plans”](#scenario-2-mixed-duration-plans) ```plaintext Level 1: Premium Annual ($99.99/year) Level 2: Premium Monthly ($9.99/month) ``` * Monthly → Annual: Crossgrade (at renewal) * Annual → Monthly: Downgrade (at renewal) ### Scenario 3: Multi-Tier Multi-Duration [Section titled “Scenario 3: Multi-Tier Multi-Duration”](#scenario-3-multi-tier-multi-duration) ```plaintext Level 1: Ultimate Annual ($199/year) Level 2: Ultimate Monthly ($19.99/month) Level 3: Premium Annual ($99/year) Level 4: Premium Monthly ($9.99/month) Level 5: Basic Annual ($49/year) Level 6: Basic Monthly ($4.99/month) ``` This setup provides maximum flexibility while maintaining clear upgrade/downgrade logic. ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) **Subscription not appearing in group:** * Verify it’s assigned to the correct group * Check that it’s in at least “Ready to Submit” status * Ensure product ID is correct **Wrong upgrade/downgrade behavior:** * Review level rankings (1 = highest) * Verify subscription tiers make sense * Check that levels are set correctly **Products from different groups:** * Users can subscribe to multiple groups simultaneously * This is intentional - keep related products in same group **getActiveProducts showing multiple subscriptions:** * Check if subscriptions are in different groups * Verify user isn’t subscribed via Family Sharing * Review subscription status in App Store Connect ## Additional Resources [Section titled “Additional Resources”](#additional-resources) For more details, refer to the [official Apple documentation on subscription groups](https://developer.apple.com/app-store/subscriptions/). # @capgo/nativegeocoder > Convert between addresses and geographic coordinates using native platform geocoding APIs for accurate location data. Forward geocoding Convert addresses to geographic coordinates 📍 Reverse geocoding Convert coordinates to human-readable addresses 🏠 Native accuracy Uses platform-native geocoding for best results 🎯 Comprehensive Documentation Check the [Documentation](/docs/plugins/nativegeocoder/getting-started/) to master the plugin in just a few minutes. # Getting Started > Learn how to install and use the Native Geocoder plugin for address and coordinate conversion in your Capacitor app. 1. **Install the package** * npm ```sh npm i @capgo/nativegeocoder ``` * pnpm ```sh pnpm add @capgo/nativegeocoder ``` * yarn ```sh yarn add @capgo/nativegeocoder ``` * bun ```sh bun add @capgo/nativegeocoder ``` 2. **Sync with native projects** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` 3. **Configure permissions** ### iOS [Section titled “iOS”](#ios) Add location usage description to your `Info.plist`: ```xml NSLocationWhenInUseUsageDescription To convert addresses to coordinates ``` ### Android [Section titled “Android”](#android) Add permissions to your `AndroidManifest.xml`: ```xml ``` ## Usage [Section titled “Usage”](#usage) Import the plugin and use its geocoding methods: ```typescript import { NativeGeocoder } from '@capgo/nativegeocoder'; // Forward geocoding: Address to coordinates const forwardGeocode = async () => { const results = await NativeGeocoder.forwardGeocode({ addressString: '1600 Amphitheatre Parkway, Mountain View, CA', useLocale: true, maxResults: 1 }); const location = results.addresses[0]; console.log('Latitude:', location.latitude); console.log('Longitude:', location.longitude); }; // Reverse geocoding: Coordinates to address const reverseGeocode = async () => { const results = await NativeGeocoder.reverseGeocode({ latitude: 37.4220656, longitude: -122.0840897, useLocale: true, maxResults: 1 }); const address = results.addresses[0]; console.log('Street:', address.thoroughfare); console.log('City:', address.locality); console.log('Country:', address.countryName); }; ``` ## API Reference [Section titled “API Reference”](#api-reference) ### forwardGeocode(options) [Section titled “forwardGeocode(options)”](#forwardgeocodeoptions) Converts an address string to geographic coordinates. ```typescript interface ForwardGeocodeOptions { addressString: string; useLocale?: boolean; maxResults?: number; apiKey?: string; // Android only } interface GeocodeResult { addresses: Address[]; } interface Address { latitude: number; longitude: number; countryCode?: string; countryName?: string; postalCode?: string; administrativeArea?: string; subAdministrativeArea?: string; locality?: string; subLocality?: string; thoroughfare?: string; subThoroughfare?: string; } ``` ### reverseGeocode(options) [Section titled “reverseGeocode(options)”](#reversegeocodeoptions) Converts geographic coordinates to address information. ```typescript interface ReverseGeocodeOptions { latitude: number; longitude: number; useLocale?: boolean; maxResults?: number; apiKey?: string; // Android only } ``` ## Complete Examples [Section titled “Complete Examples”](#complete-examples) ### Address Search with Error Handling [Section titled “Address Search with Error Handling”](#address-search-with-error-handling) ```typescript import { NativeGeocoder } from '@capgo/nativegeocoder'; export class GeocodingService { async searchAddress(address: string): Promise<{lat: number, lng: number} | null> { try { const results = await NativeGeocoder.forwardGeocode({ addressString: address, useLocale: true, maxResults: 5 }); if (results.addresses.length > 0) { const location = results.addresses[0]; return { lat: location.latitude, lng: location.longitude }; } return null; } catch (error) { console.error('Geocoding failed:', error); return null; } } async getAddressFromCoordinates(lat: number, lng: number): Promise { try { const results = await NativeGeocoder.reverseGeocode({ latitude: lat, longitude: lng, useLocale: true, maxResults: 1 }); if (results.addresses.length > 0) { const address = results.addresses[0]; return this.formatAddress(address); } return null; } catch (error) { console.error('Reverse geocoding failed:', error); return null; } } private formatAddress(address: Address): string { const parts = [ address.subThoroughfare, address.thoroughfare, address.locality, address.administrativeArea, address.postalCode, address.countryName ].filter(part => part != null && part !== ''); return parts.join(', '); } } ``` ### Location Picker Component [Section titled “Location Picker Component”](#location-picker-component) ```typescript import { NativeGeocoder } from '@capgo/nativegeocoder'; import { Geolocation } from '@capacitor/geolocation'; export class LocationPicker { currentLocation: { lat: number; lng: number } | null = null; currentAddress: string = ''; async getCurrentLocation() { try { // Get current coordinates const position = await Geolocation.getCurrentPosition(); this.currentLocation = { lat: position.coords.latitude, lng: position.coords.longitude }; // Get address for coordinates const results = await NativeGeocoder.reverseGeocode({ latitude: this.currentLocation.lat, longitude: this.currentLocation.lng, useLocale: true, maxResults: 1 }); if (results.addresses.length > 0) { const address = results.addresses[0]; this.currentAddress = [ address.thoroughfare, address.locality, address.countryName ].filter(Boolean).join(', '); } } catch (error) { console.error('Failed to get location:', error); } } async searchLocation(query: string) { try { const results = await NativeGeocoder.forwardGeocode({ addressString: query, useLocale: true, maxResults: 10 }); return results.addresses.map(address => ({ coordinates: { lat: address.latitude, lng: address.longitude }, displayName: this.formatDisplayName(address) })); } catch (error) { console.error('Search failed:', error); return []; } } private formatDisplayName(address: Address): string { const mainPart = [ address.thoroughfare, address.locality ].filter(Boolean).join(', '); const subPart = [ address.administrativeArea, address.countryName ].filter(Boolean).join(', '); return mainPart + (subPart ? ` (${subPart})` : ''); } } ``` ## Best Practices [Section titled “Best Practices”](#best-practices) 1. **Request permissions first** ```typescript import { Geolocation } from '@capacitor/geolocation'; const requestPermissions = async () => { const permissions = await Geolocation.requestPermissions(); if (permissions.location !== 'granted') { throw new Error('Location permission required'); } }; ``` 2. **Handle errors gracefully** ```typescript try { const results = await NativeGeocoder.forwardGeocode({ addressString: address }); } catch (error) { // Handle specific error cases if (error.message.includes('network')) { console.error('Network error'); } else if (error.message.includes('permission')) { console.error('Permission denied'); } } ``` 3. **Use maxResults wisely** * For user search: Use 5-10 results * For automatic conversion: Use 1 result * More results = slower response 4. **Cache results when possible** ```typescript const geocodeCache = new Map(); async function geocodeWithCache(address: string) { if (geocodeCache.has(address)) { return geocodeCache.get(address); } const result = await NativeGeocoder.forwardGeocode({ addressString: address }); geocodeCache.set(address, result); return result; } ``` ## Platform Differences [Section titled “Platform Differences”](#platform-differences) ### iOS [Section titled “iOS”](#ios-1) * Uses `CLGeocoder` from CoreLocation * No API key required * Respects user’s locale automatically ### Android [Section titled “Android”](#android-1) * Uses Android Geocoder API * Optional Google API key for better results * May fall back to Google’s web service ### API Key Configuration (Android) [Section titled “API Key Configuration (Android)”](#api-key-configuration-android) For better results on Android, you can provide a Google API key: ```typescript await NativeGeocoder.forwardGeocode({ addressString: address, apiKey: 'YOUR_GOOGLE_API_KEY' // Android only }); ``` ## Common Issues [Section titled “Common Issues”](#common-issues) 1. **No results returned** * Check internet connection * Verify address format * Try with more general address 2. **Permission errors** * Ensure location permissions are granted * Check Info.plist/AndroidManifest.xml 3. **Inaccurate results** * Use more specific addresses * Include postal codes when available * Consider using coordinates for precise locations # @capgo/capacitor-navigation-bar > Set custom colors for the Android navigation bar to match your app's theme and create a cohesive visual experience. Android customization Full control over Android navigation bar appearance 🎨 Dynamic theming Change colors dynamically to match your app’s theme 🌈 Light/Dark support Support for light and dark navigation bar styles 🌓 Comprehensive Documentation Check the [Documentation](/docs/plugins/navigation-bar/getting-started/) to master the plugin in just a few minutes. # Getting Started > Learn how to install and configure the Capacitor Navigation Bar plugin to customize the Android navigation bar color and style. 1. **Install the package** * npm ```sh npm i @capgo/capacitor-navigation-bar ``` * pnpm ```sh pnpm add @capgo/capacitor-navigation-bar ``` * yarn ```sh yarn add @capgo/capacitor-navigation-bar ``` * bun ```sh bun add @capgo/capacitor-navigation-bar ``` 2. **Sync with native projects** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` 3. **Configure the plugin** **Set Navigation Bar Color:** ```typescript import { NavigationBar } from '@capgo/capacitor-navigation-bar'; // Set navigation bar color await NavigationBar.setColor({ color: '#FF0000', // Red color darkButtons: false // Use light buttons }); ``` **Dynamic Theme Support:** ```typescript // Set color based on theme const isDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches; await NavigationBar.setColor({ color: isDarkMode ? '#000000' : '#FFFFFF', darkButtons: !isDarkMode }); ``` * Android **Minimum Requirements:** * Android 5.0 (API level 21) or higher * The plugin only works on Android devices No additional setup required in AndroidManifest.xml. * iOS This plugin is Android-only. iOS does not have a customizable navigation bar. 4. **Advanced usage** ```typescript import { NavigationBar } from '@capgo/capacitor-navigation-bar'; export class ThemeService { // Set transparent navigation bar async setTransparent() { await NavigationBar.setColor({ color: '#00000000', // Transparent darkButtons: true }); } // Match app theme async matchAppTheme(primaryColor: string, isLight: boolean) { await NavigationBar.setColor({ color: primaryColor, darkButtons: isLight }); } // Get current navigation bar color async getCurrentColor() { const result = await NavigationBar.getColor(); console.log('Current color:', result.color); return result.color; } // Reset to default async resetToDefault() { await NavigationBar.setColor({ color: '#000000', darkButtons: false }); } } ``` 5. **Integration with app lifecycle** ```typescript import { App } from '@capacitor/app'; import { NavigationBar } from '@capgo/capacitor-navigation-bar'; // Update navigation bar on app state changes App.addListener('appStateChange', async ({ isActive }) => { if (isActive) { // Restore your app's navigation bar color await NavigationBar.setColor({ color: '#YourAppColor', darkButtons: true }); } }); // Handle theme changes window.matchMedia('(prefers-color-scheme: dark)') .addEventListener('change', async (e) => { await NavigationBar.setColor({ color: e.matches ? '#121212' : '#FFFFFF', darkButtons: !e.matches }); }); ``` ## API Reference [Section titled “API Reference”](#api-reference) ### Methods [Section titled “Methods”](#methods) #### `setColor(options: ColorOptions)` [Section titled “setColor(options: ColorOptions)”](#setcoloroptions-coloroptions) Set the navigation bar color and button style. **Parameters:** * `options`: Configuration object * `color`: string - Hex color code (e.g., ‘#FF0000’) * `darkButtons`: boolean - Use dark buttons (true) or light buttons (false) **Returns:** `Promise` #### `getColor()` [Section titled “getColor()”](#getcolor) Get the current navigation bar color. **Returns:** `Promise<{ color: string }>` ### Interfaces [Section titled “Interfaces”](#interfaces) ```typescript interface ColorOptions { color: string; // Hex color code darkButtons: boolean; // Button style } interface ColorResult { color: string; // Current hex color } ``` ## Platform Notes [Section titled “Platform Notes”](#platform-notes) ### Android [Section titled “Android”](#android) * Requires Android 5.0 (API level 21) or higher * Color changes are immediate * Transparent colors are supported (use alpha channel) * The navigation bar might not be visible on devices with gesture navigation ### iOS [Section titled “iOS”](#ios) * This plugin has no effect on iOS devices * iOS does not provide APIs to customize the system navigation ## Common Use Cases [Section titled “Common Use Cases”](#common-use-cases) 1. **Brand Consistency**: Match navigation bar with your app’s primary color 2. **Theme Support**: Adapt to light/dark themes dynamically 3. **Immersive Experiences**: Use transparent navigation for full-screen content 4. **Status Indication**: Change color to indicate app states (recording, errors, etc.) ## Best Practices [Section titled “Best Practices”](#best-practices) 1. **Color Contrast**: Ensure sufficient contrast between navigation bar and buttons ```typescript // Good contrast examples setColor({ color: '#FFFFFF', darkButtons: true }); // White bar, dark buttons setColor({ color: '#000000', darkButtons: false }); // Black bar, light buttons ``` 2. **Theme Consistency**: Update navigation bar when theme changes 3. **Accessibility**: Consider users with visual impairments when choosing colors 4. **Performance**: Avoid frequent color changes that might distract users ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) **Navigation bar not changing:** * Verify the device is running Android 5.0+ * Check if the device has gesture navigation enabled * Ensure color format is valid hex (e.g., ‘#FF0000’) **Buttons not visible:** * Check the `darkButtons` parameter matches your background color * Light backgrounds need `darkButtons: true` * Dark backgrounds need `darkButtons: false` **Color appears different:** * Some Android versions may modify the color slightly * Use fully opaque colors for best results # @capgo/capacitor-nfc > Read and write NFC tags with native support for iOS and Android, enabling contactless interactions and smart tag experiences. Tag Discovery Detect NFC tags automatically when they come near the device 📡 Read NDEF Records Read structured data from NFC tags including text, URLs, and custom payloads 📖 Write to Tags Write NDEF messages to compatible NFC tags 📝 Tag Formatting Format NDEF-formattable tags for writing data 🔧 Peer-to-Peer Share data between devices using Android Beam (Android only) 📲 Cross-platform Works on both iOS and Android with consistent API 📱 # Getting Started > Learn how to install and use the NFC plugin to read and write NFC tags in your Capacitor app. 1. **Install the package** * npm ```sh npm i @capgo/capacitor-nfc ``` * pnpm ```sh pnpm add @capgo/capacitor-nfc ``` * yarn ```sh yarn add @capgo/capacitor-nfc ``` * bun ```sh bun add @capgo/capacitor-nfc ``` 2. **Sync with native projects** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Configuration [Section titled “Configuration”](#configuration) ### iOS Configuration [Section titled “iOS Configuration”](#ios-configuration) Add the NFC usage description to your `Info.plist`: ```xml NFCReaderUsageDescription This app needs NFC access to read and write tags ``` Enable the Near Field Communication Tag Reading capability in your Xcode project. ### Android Configuration [Section titled “Android Configuration”](#android-configuration) Add the NFC permission to your `AndroidManifest.xml`: ```xml ``` ## Usage [Section titled “Usage”](#usage) ### Start Scanning for NFC Tags [Section titled “Start Scanning for NFC Tags”](#start-scanning-for-nfc-tags) ```typescript import { CapacitorNfc } from '@capgo/capacitor-nfc'; await CapacitorNfc.startScanning({ invalidateAfterFirstRead: false, // Keep session open (iOS) alertMessage: 'Hold a tag near the top of your device.', }); const listener = await CapacitorNfc.addListener('nfcEvent', (event) => { console.log('Tag detected:', event.type); console.log('Tag ID:', event.tag?.id); console.log('NDEF message:', event.tag?.ndefMessage); }); ``` ### Read NFC Tag [Section titled “Read NFC Tag”](#read-nfc-tag) ```typescript await CapacitorNfc.addListener('nfcEvent', (event) => { if (event.tag?.ndefMessage) { event.tag.ndefMessage.forEach(record => { console.log('TNF:', record.tnf); console.log('Type:', record.type); console.log('Payload:', record.payload); // Decode text record if (record.tnf === 1 && record.type[0] === 0x54) { // Text record const langLen = record.payload[0] & 0x3f; const text = new TextDecoder().decode( new Uint8Array(record.payload.slice(langLen + 1)) ); console.log('Text:', text); } }); } }); ``` ### Write to NFC Tag [Section titled “Write to NFC Tag”](#write-to-nfc-tag) ```typescript // Prepare a text record const encoder = new TextEncoder(); const langBytes = Array.from(encoder.encode('en')); const textBytes = Array.from(encoder.encode('Hello NFC')); const payload = [langBytes.length & 0x3f, ...langBytes, ...textBytes]; await CapacitorNfc.write({ allowFormat: true, records: [ { tnf: 0x01, // TNF Well-known type: [0x54], // 'T' for Text id: [], payload, }, ], }); console.log('Tag written successfully'); ``` ### Write URL to NFC Tag [Section titled “Write URL to NFC Tag”](#write-url-to-nfc-tag) ```typescript const url = 'https://capgo.app'; const urlBytes = Array.from(new TextEncoder().encode(url)); await CapacitorNfc.write({ allowFormat: true, records: [ { tnf: 0x01, // TNF Well-known type: [0x55], // 'U' for URI id: [], payload: [0x01, ...urlBytes], // 0x01 = https:// }, ], }); ``` ### Erase NFC Tag [Section titled “Erase NFC Tag”](#erase-nfc-tag) ```typescript await CapacitorNfc.erase(); console.log('Tag erased'); ``` ### Make Tag Read-Only [Section titled “Make Tag Read-Only”](#make-tag-read-only) ```typescript await CapacitorNfc.makeReadOnly(); console.log('Tag is now read-only'); ``` ### Stop Scanning [Section titled “Stop Scanning”](#stop-scanning) ```typescript await listener.remove(); await CapacitorNfc.stopScanning(); ``` ### Check NFC Status [Section titled “Check NFC Status”](#check-nfc-status) ```typescript const { status } = await CapacitorNfc.getStatus(); console.log('NFC status:', status); // Possible values: 'NFC_OK', 'NO_NFC', 'NFC_DISABLED', 'NDEF_PUSH_DISABLED' if (status === 'NFC_DISABLED') { // Open system settings await CapacitorNfc.showSettings(); } ``` ### Android Beam (P2P Sharing) [Section titled “Android Beam (P2P Sharing)”](#android-beam-p2p-sharing) ```typescript // Share data via Android Beam const message = { records: [ { tnf: 0x01, type: [0x54], // Text id: [], payload: [/* text record payload */], }, ], }; await CapacitorNfc.share(message); // Later, stop sharing await CapacitorNfc.unshare(); ``` ## API Reference [Section titled “API Reference”](#api-reference) ### startScanning(options?) [Section titled “startScanning(options?)”](#startscanningoptions) Start listening for NFC tags. ```typescript interface StartScanningOptions { invalidateAfterFirstRead?: boolean; // iOS only, defaults to true alertMessage?: string; // iOS only androidReaderModeFlags?: number; // Android only } await CapacitorNfc.startScanning(options); ``` ### stopScanning() [Section titled “stopScanning()”](#stopscanning) Stop the NFC scanning session. ```typescript await CapacitorNfc.stopScanning(); ``` ### write(options) [Section titled “write(options)”](#writeoptions) Write NDEF records to the last discovered tag. ```typescript interface WriteTagOptions { records: NdefRecord[]; allowFormat?: boolean; // Defaults to true } interface NdefRecord { tnf: number; // Type Name Format type: number[]; // Record type id: number[]; // Record ID payload: number[]; // Record payload } await CapacitorNfc.write(options); ``` ### erase() [Section titled “erase()”](#erase) Erase the last discovered tag. ```typescript await CapacitorNfc.erase(); ``` ### makeReadOnly() [Section titled “makeReadOnly()”](#makereadonly) Make the last discovered tag read-only (permanent). ```typescript await CapacitorNfc.makeReadOnly(); ``` ### share(options) [Section titled “share(options)”](#shareoptions) Share NDEF message via Android Beam (Android only). ```typescript await CapacitorNfc.share({ records: [...] }); ``` ### unshare() [Section titled “unshare()”](#unshare) Stop sharing (Android only). ```typescript await CapacitorNfc.unshare(); ``` ### getStatus() [Section titled “getStatus()”](#getstatus) Get current NFC adapter status. ```typescript const { status } = await CapacitorNfc.getStatus(); // Returns: 'NFC_OK' | 'NO_NFC' | 'NFC_DISABLED' | 'NDEF_PUSH_DISABLED' ``` ### showSettings() [Section titled “showSettings()”](#showsettings) Open system NFC settings. ```typescript await CapacitorNfc.showSettings(); ``` ## Events [Section titled “Events”](#events) ### nfcEvent [Section titled “nfcEvent”](#nfcevent) Fired when an NFC tag is discovered. ```typescript interface NfcEvent { type: 'tag' | 'ndef' | 'ndef-mime' | 'ndef-formattable'; tag?: NfcTag; } interface NfcTag { id: number[]; techTypes: string[]; type: string | null; maxSize: number | null; isWritable: boolean | null; canMakeReadOnly: boolean | null; ndefMessage: NdefRecord[] | null; } ``` ### nfcStateChange [Section titled “nfcStateChange”](#nfcstatechange) Fired when NFC adapter availability changes (Android only). ```typescript interface NfcStateChangeEvent { status: NfcStatus; enabled: boolean; } ``` ## Complete Example [Section titled “Complete Example”](#complete-example) ```typescript import { CapacitorNfc } from '@capgo/capacitor-nfc'; export class NfcService { private listener: any; async startReading() { // Check NFC status const { status } = await CapacitorNfc.getStatus(); if (status === 'NO_NFC') { throw new Error('NFC not available on this device'); } if (status === 'NFC_DISABLED') { await CapacitorNfc.showSettings(); return; } // Start scanning await CapacitorNfc.startScanning({ invalidateAfterFirstRead: false, alertMessage: 'Ready to scan NFC tags', }); // Listen for tags this.listener = await CapacitorNfc.addListener('nfcEvent', (event) => { this.handleNfcEvent(event); }); } private handleNfcEvent(event: any) { console.log('NFC Event:', event.type); if (event.tag?.ndefMessage) { event.tag.ndefMessage.forEach(record => { this.processRecord(record); }); } } private processRecord(record: any) { // Process text records if (record.tnf === 1 && record.type[0] === 0x54) { const langLen = record.payload[0] & 0x3f; const text = new TextDecoder().decode( new Uint8Array(record.payload.slice(langLen + 1)) ); console.log('Text:', text); } // Process URI records if (record.tnf === 1 && record.type[0] === 0x55) { const uriCode = record.payload[0]; const uri = new TextDecoder().decode( new Uint8Array(record.payload.slice(1)) ); console.log('URI:', uri); } } async writeText(text: string) { const encoder = new TextEncoder(); const langBytes = Array.from(encoder.encode('en')); const textBytes = Array.from(encoder.encode(text)); const payload = [langBytes.length & 0x3f, ...langBytes, ...textBytes]; await CapacitorNfc.write({ allowFormat: true, records: [ { tnf: 0x01, type: [0x54], id: [], payload, }, ], }); } async stopReading() { if (this.listener) { await this.listener.remove(); } await CapacitorNfc.stopScanning(); } } ``` ## NDEF Record Types [Section titled “NDEF Record Types”](#ndef-record-types) ### TNF (Type Name Format) [Section titled “TNF (Type Name Format)”](#tnf-type-name-format) * `0x00`: Empty * `0x01`: Well-known (e.g., Text, URI) * `0x02`: MIME media type * `0x03`: Absolute URI * `0x04`: External type * `0x05`: Unknown * `0x06`: Unchanged * `0x07`: Reserved ### Common Record Types [Section titled “Common Record Types”](#common-record-types) * **Text**: `type: [0x54]` (‘T’) * **URI**: `type: [0x55]` (‘U’) * **Smart Poster**: `type: [0x53, 0x70]` (‘Sp’) ### URI Prefixes [Section titled “URI Prefixes”](#uri-prefixes) * `0x00`: (no prefix) * `0x01`: `https://` * `0x02`: `https://` * `0x03`: `http://` * `0x04`: `https://www.` ## Best Practices [Section titled “Best Practices”](#best-practices) 1. **Check NFC Status**: Always verify NFC is available and enabled 2. **Handle Permissions**: Request NFC permissions appropriately 3. **Stop Scanning**: Always stop scanning when done to save battery 4. **Error Handling**: Wrap NFC operations in try-catch blocks 5. **Test on Devices**: NFC features don’t work on simulators/emulators ## Platform Notes [Section titled “Platform Notes”](#platform-notes) ### iOS [Section titled “iOS”](#ios) * Requires iOS 11.0+ * Uses Core NFC framework * Supports background tag reading (iOS 13+) * Limited to reading NDEF-formatted tags * Cannot write tags in background * Requires NFCReaderUsageDescription in Info.plist ### Android [Section titled “Android”](#android) * Requires Android 4.4 (API 19)+ * Uses Android NFC API * Supports both foreground and background tag reading * Can write to tags * Supports Android Beam (P2P) on devices with NFC * Requires NFC permission in AndroidManifest.xml ### Web [Section titled “Web”](#web) * Not supported on web platform # @capgo/capacitor-pay > Offer native Apple Pay and Google Pay checkouts with a single TypeScript API that adapts per platform. Capgo Pay bridges the latest Apple Pay and Google Pay SDKs so you can deliver one seamless checkout experience across devices. Unified API Call `isPayAvailable` and `requestPayment` once and let the plugin handle each platform. Rapid setup Ship fully native payment sheets without manually wiring platform-specific code. Tokenized payments Receive payment tokens ready for your gateway or PSP integrations. Customizable flows Configure line items, contact fields, and billing requirements to fit your checkout. Dive into the getting started guide for setup checklists and sample payloads for each platform. # Getting Started > Configure Apple Pay and Google Pay for your Capacitor checkout with the Pay plugin. 1. **Install the plugin** * npm ```sh npm i @capgo/capacitor-pay ``` * pnpm ```sh pnpm add @capgo/capacitor-pay ``` * yarn ```sh yarn add @capgo/capacitor-pay ``` * bun ```sh bun add @capgo/capacitor-pay ``` 2. **Sync native projects** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Native prerequisites [Section titled “Native prerequisites”](#native-prerequisites) * **Apple Pay (iOS)** 1. Create an Apple Pay merchant ID in the Apple Developer portal. 2. Generate and upload the payment processing certificate required by your gateway. 3. Enable the **Apple Pay** capability in Xcode and make sure your provisioning profile includes it. 4. Update `Info.plist` with a usage description if your gateway requires network access to validate payments. * **Google Pay (Android)** 1. Sign in to the [Google Pay Business Console](https://pay.google.com/business/console/). 2. Configure your payment gateway parameters or set up direct tokenization for testing. 3. If you plan to use production cards, switch `environment` to `PRODUCTION` and register test cards for QA. 4. Confirm that Google Play services are available on the device (required by Google Pay). Refer to the guides under `docs/` in the plugin repository for step-by-step screenshots. ## Check availability [Section titled “Check availability”](#check-availability) ```typescript import { Pay } from '@capgo/capacitor-pay'; const availability = await Pay.isPayAvailable({ apple: { supportedNetworks: ['visa', 'masterCard', 'amex'] }, google: { isReadyToPayRequest: { allowedPaymentMethods: [ { type: 'CARD', parameters: { allowedAuthMethods: ['PAN_ONLY', 'CRYPTOGRAM_3DS'], allowedCardNetworks: ['VISA', 'MASTERCARD'], }, }, ], }, }, }); if (!availability.available) { console.warn('Native pay unavailable:', availability); } ``` ## Request payment (Apple Pay) [Section titled “Request payment (Apple Pay)”](#request-payment-apple-pay) ```typescript const appleResult = await Pay.requestPayment({ apple: { merchantIdentifier: 'merchant.com.example.app', countryCode: 'US', currencyCode: 'USD', supportedNetworks: ['visa', 'masterCard'], paymentSummaryItems: [ { label: 'Starter Plan', amount: '9.99' }, { label: 'Capgo Store', amount: '9.99' }, ], requiredShippingContactFields: ['name', 'emailAddress'], }, }); const token = appleResult.apple?.paymentData; ``` ## Request payment (Google Pay) [Section titled “Request payment (Google Pay)”](#request-payment-google-pay) ```typescript const googleResult = await Pay.requestPayment({ google: { environment: 'test', paymentDataRequest: { apiVersion: 2, apiVersionMinor: 0, allowedPaymentMethods: [ { type: 'CARD', parameters: { allowedAuthMethods: ['PAN_ONLY', 'CRYPTOGRAM_3DS'], allowedCardNetworks: ['VISA', 'MASTERCARD'], billingAddressRequired: true, billingAddressParameters: { format: 'FULL' }, }, tokenizationSpecification: { type: 'PAYMENT_GATEWAY', parameters: { gateway: 'example', gatewayMerchantId: 'exampleMerchantId', }, }, }, ], merchantInfo: { merchantName: 'Capgo Store', }, transactionInfo: { totalPriceStatus: 'FINAL', totalPrice: '9.99', currencyCode: 'USD', countryCode: 'US', }, }, }, }); const paymentData = googleResult.google?.paymentData; ``` ## Handling responses [Section titled “Handling responses”](#handling-responses) * Tokenize the returned payload with your payment processor and confirm the transaction server-side. * Surface a fallback path (card entry, invoice) if native pay is unavailable or the user cancels the sheet. * Cache the availability result so you do not display the buttons on unsupported devices. # @capgo/capacitor-pdf-generator > Render pixel-perfect PDFs from templates, receipts, or reports without leaving your Capacitor app. Convert HTML or remote pages into shareable PDFs with Capgo’s native renderer for iOS and Android. HTML to PDF Feed in HTML templates and get a ready-to-share PDF document in seconds. Remote capture Snapshot any accessible URL and save or distribute the generated PDF. Flexible outputs Return base64 data or trigger the native share sheet depending on your workflow. Document controls Specify orientation, page size, and filenames for every render. Head over to the getting started guide for platform notes and usage examples. # Getting Started > Render HTML and remote pages into PDFs with the Capgo PDF Generator plugin. 1. **Install the plugin** * npm ```sh npm i @capgo/capacitor-pdf-generator ``` * pnpm ```sh pnpm add @capgo/capacitor-pdf-generator ``` * yarn ```sh yarn add @capgo/capacitor-pdf-generator ``` * bun ```sh bun add @capgo/capacitor-pdf-generator ``` 2. **Sync platforms** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Generate from HTML [Section titled “Generate from HTML”](#generate-from-html) ```typescript import { PdfGenerator } from '@capgo/capacitor-pdf-generator'; const receiptHtml = `

Capgo Store

Thank you for your purchase.

`; const pdf = await PdfGenerator.fromData({ data: receiptHtml, documentSize: 'A4', orientation: 'portrait', type: 'base64', fileName: 'receipt.pdf', }); if (pdf.type === 'base64') { const link = document.createElement('a'); link.href = `data:application/pdf;base64,${pdf.base64}`; link.download = 'receipt.pdf'; link.click(); } ``` ## Capture a remote URL [Section titled “Capture a remote URL”](#capture-a-remote-url) ```typescript await PdfGenerator.fromURL({ url: 'https://docs.capgo.app/invoice?id=123', orientation: 'landscape', documentSize: 'A4', type: 'share', // opens the native share dialog fileName: 'invoice-123.pdf', }); ``` ## Tips [Section titled “Tips”](#tips) * Include inline CSS in your HTML string or provide a `baseUrl` so relative assets resolve correctly. * When returning base64 data on mobile, move it to the filesystem (e.g., Capacitor Filesystem) before sharing. * landscape orientation is ideal for wide tables, while portrait fits most documents. * Consider using scoped CSS fonts (e.g., system fonts) for consistent rendering across platforms. # @capgo/capacitor-pedometer > Access comprehensive pedometer data for fitness and health tracking with real-time updates and historical data queries. Step Counting Accurate step detection and counting 👟 Distance Tracking Calculate distance traveled in real-time 📏 Pace & Cadence Monitor walking and running speed 🏃 Historical Data Query pedometer data from specific time ranges 📊 Floor Tracking Count floors climbed on iOS devices 🪜 Getting Started Check the [Getting Started Guide](/docs/plugins/pedometer/getting-started/) to install and configure the plugin. # Getting Started with Pedometer > Learn how to integrate step counting and fitness tracking into your Capacitor app This guide will walk you through integrating the Capacitor Pedometer plugin into your application. ## Installation [Section titled “Installation”](#installation) Install the plugin using npm: ```bash npm install @capgo/capacitor-pedometer npx cap sync ``` ## iOS Configuration [Section titled “iOS Configuration”](#ios-configuration) Add motion usage description to your `Info.plist`: ```xml NSMotionUsageDescription We need access to your motion data to track steps and activity ``` ## Android Configuration [Section titled “Android Configuration”](#android-configuration) The plugin automatically adds the `ACTIVITY_RECOGNITION` permission. For Android 10+ (API 29), you need to request this permission at runtime. ## Basic Usage [Section titled “Basic Usage”](#basic-usage) ### Import the Plugin [Section titled “Import the Plugin”](#import-the-plugin) ```typescript import { CapacitorPedometer } from '@capgo/capacitor-pedometer'; ``` ### Check Availability [Section titled “Check Availability”](#check-availability) ```typescript const checkPedometer = async () => { const result = await CapacitorPedometer.isAvailable(); console.log('Step counting available:', result.stepCounting); console.log('Distance available:', result.distance); console.log('Floor counting available:', result.floorCounting); console.log('Pace available:', result.pace); console.log('Cadence available:', result.cadence); }; ``` ### Request Permissions [Section titled “Request Permissions”](#request-permissions) ```typescript const requestPermission = async () => { const permission = await CapacitorPedometer.requestPermissions(); console.log('Permission status:', permission.activityRecognition); }; ``` ### Get Current Measurements [Section titled “Get Current Measurements”](#get-current-measurements) ```typescript const getMeasurements = async () => { const measurements = await CapacitorPedometer.getMeasurement({ startDate: new Date(Date.now() - 24 * 60 * 60 * 1000), // 24 hours ago endDate: new Date() }); console.log('Steps:', measurements.numberOfSteps); console.log('Distance:', measurements.distance, 'meters'); console.log('Floors ascended:', measurements.floorsAscended); console.log('Floors descended:', measurements.floorsDescended); }; ``` ### Start Real-time Updates [Section titled “Start Real-time Updates”](#start-real-time-updates) ```typescript // Add listener for real-time updates CapacitorPedometer.addListener('measurement', (data) => { console.log('Steps:', data.numberOfSteps); console.log('Distance:', data.distance); console.log('Pace:', data.currentPace); console.log('Cadence:', data.currentCadence); }); // Start receiving updates await CapacitorPedometer.startMeasurementUpdates(); ``` ### Stop Updates [Section titled “Stop Updates”](#stop-updates) ```typescript await CapacitorPedometer.stopMeasurementUpdates(); CapacitorPedometer.removeAllListeners(); ``` ## Complete Example [Section titled “Complete Example”](#complete-example) ```typescript import { CapacitorPedometer } from '@capgo/capacitor-pedometer'; class PedometerService { private listener: any; async initialize() { // Check availability const available = await CapacitorPedometer.isAvailable(); if (!available.stepCounting) { console.error('Step counting not available'); return false; } // Request permissions const permission = await CapacitorPedometer.requestPermissions(); if (permission.activityRecognition !== 'granted') { console.error('Permission denied'); return false; } return true; } async startTracking() { // Add listener this.listener = CapacitorPedometer.addListener('measurement', (data) => { this.onMeasurement(data); }); // Start updates await CapacitorPedometer.startMeasurementUpdates(); console.log('Pedometer tracking started'); } async stopTracking() { await CapacitorPedometer.stopMeasurementUpdates(); if (this.listener) { this.listener.remove(); } console.log('Pedometer tracking stopped'); } onMeasurement(data: any) { console.log('Steps today:', data.numberOfSteps); console.log('Distance:', (data.distance / 1000).toFixed(2), 'km'); } async getTodaySteps() { const today = new Date(); today.setHours(0, 0, 0, 0); const measurements = await CapacitorPedometer.getMeasurement({ startDate: today, endDate: new Date() }); return measurements.numberOfSteps; } } // Usage const pedometer = new PedometerService(); await pedometer.initialize(); await pedometer.startTracking(); ``` ## Best Practices [Section titled “Best Practices”](#best-practices) 1. **Check Availability**: Always verify pedometer features before use 2. **Request Permissions**: Handle permission requests gracefully 3. **Battery Optimization**: Stop updates when not needed 4. **Error Handling**: Wrap calls in try-catch blocks 5. **Background Updates**: Configure background modes for continuous tracking ## Common Issues [Section titled “Common Issues”](#common-issues) ### Permission Denied [Section titled “Permission Denied”](#permission-denied) ```typescript const handlePermissionDenied = async () => { const permission = await CapacitorPedometer.checkPermissions(); if (permission.activityRecognition === 'denied') { alert('Motion tracking permission is required. Please enable it in Settings.'); } }; ``` ### Not Available on Device [Section titled “Not Available on Device”](#not-available-on-device) ```typescript const checkSupport = async () => { const available = await CapacitorPedometer.isAvailable(); if (!available.stepCounting) { alert('Step counting is not available on this device'); return false; } return true; }; ``` ## Next Steps [Section titled “Next Steps”](#next-steps) * Explore the [API Reference](https://github.com/Cap-go/capacitor-pedometer#api) for complete documentation * Check out the [example app](https://github.com/Cap-go/capacitor-pedometer/tree/main/example) * See the [tutorial](/plugins/capacitor-pedometer) for advanced implementation # @capgo/capacitor-persistent-account > Manage user accounts with persistent storage, secure session handling, and seamless authentication across app sessions. ## Overview [Section titled “Overview”](#overview) The Capacitor Persistent Account plugin enables secure storage and persistence of user account data between app installations. This plugin ensures that user account information remains available even after app reinstalls, providing seamless user experience and account continuity. Cross-install persistence Data survives app uninstall/reinstall cycles 💾 Secure storage Secure account data storage with system integration 🔐 Simple API Clean read/write interface for account management 📊 Cross-platform Native iOS and Android implementation 📱 ## Installation [Section titled “Installation”](#installation) ```bash npm install @capgo/capacitor-persistent-account npx cap sync ``` ## Core API Methods [Section titled “Core API Methods”](#core-api-methods) ### Data Management [Section titled “Data Management”](#data-management) * `saveAccount(options: { data: unknown })` - Securely save account data to persistent storage * `readAccount()` - Retrieve stored account data, returns `Promise<{ data: unknown | null }>` ## Key Features [Section titled “Key Features”](#key-features) * **Cross-installation persistence**: Account data survives app uninstall and reinstall * **Secure storage**: Uses platform-specific secure storage mechanisms * **Type flexibility**: Store any serializable account data structure * **Cross-platform support**: Native implementation for both iOS and Android ## Usage Example [Section titled “Usage Example”](#usage-example) ```typescript import { PersistentAccount } from '@capgo/capacitor-persistent-account'; // Define your account data structure interface UserAccount { userId: string; username: string; email: string; preferences: { theme: string; notifications: boolean; }; } // Save account data const accountData: UserAccount = { userId: '12345', username: 'john_doe', email: 'john@example.com', preferences: { theme: 'dark', notifications: true } }; await PersistentAccount.saveAccount({ data: accountData }); // Read account data const result = await PersistentAccount.readAccount(); if (result.data) { const account = result.data as UserAccount; console.log('Restored account:', account.username); } else { console.log('No account data found'); } ``` ## Use Cases [Section titled “Use Cases”](#use-cases) * **User onboarding**: Preserve user progress through app reinstalls * **Account recovery**: Restore user sessions after app updates * **Preferences storage**: Maintain user settings and configurations * **Offline-first apps**: Store essential user data locally ## Platform Implementation [Section titled “Platform Implementation”](#platform-implementation) ### iOS [Section titled “iOS”](#ios) * Utilizes iOS Keychain Services for secure, persistent storage * Data survives app deletion and device restores ### Android [Section titled “Android”](#android) * Uses Android Account Manager or shared preferences with backup * Maintains data across app reinstalls and device migrations ## Security Considerations [Section titled “Security Considerations”](#security-considerations) * Account data is stored using platform-specific secure storage * Consider data encryption for sensitive information * Implement proper data validation when reading stored accounts * Follow platform guidelines for user data handling ## Documentation [Section titled “Documentation”](#documentation) Check the [complete documentation](/docs/plugins/persistent-account/getting-started/) for detailed implementation guides and best practices. # Getting Started > Learn how to install and use the Persistent Account plugin to maintain user data across app installations. ## Installation [Section titled “Installation”](#installation) * npm ```bash npm install @capgo/capacitor-persistent-account npx cap sync ``` * yarn ```bash yarn add @capgo/capacitor-persistent-account npx cap sync ``` * pnpm ```bash pnpm add @capgo/capacitor-persistent-account npx cap sync ``` * bun ```bash bun add @capgo/capacitor-persistent-account npx cap sync ``` ## Platform Support [Section titled “Platform Support”](#platform-support) * **iOS**: Uses iOS Keychain Services for secure, persistent storage * **Android**: Uses Android Account Manager or shared preferences with backup ## Usage Example [Section titled “Usage Example”](#usage-example) ```typescript import { PersistentAccount } from '@capgo/capacitor-persistent-account'; // Define your account data structure interface UserAccount { userId: string; username: string; email: string; preferences: { theme: string; notifications: boolean; }; } // Save account data const accountData: UserAccount = { userId: '12345', username: 'john_doe', email: 'john@example.com', preferences: { theme: 'dark', notifications: true } }; await PersistentAccount.saveAccount({ data: accountData }); // Read account data const result = await PersistentAccount.readAccount(); if (result.data) { const account = result.data as UserAccount; console.log('Restored account:', account.username); } else { console.log('No account data found'); } ``` ## API Reference [Section titled “API Reference”](#api-reference) ### saveAccount(options) [Section titled “saveAccount(options)”](#saveaccountoptions) ```typescript saveAccount(options: { data: unknown }) => Promise ``` Securely save account data to persistent storage. | Param | Type | | ------------- | ------------------- | | **`options`** | `{ data: unknown }` | ### readAccount() [Section titled “readAccount()”](#readaccount) ```typescript readAccount() => Promise<{ data: unknown | null }> ``` Retrieve stored account data. **Returns:** `Promise<{ data: unknown | null }>` ## Practical Implementation [Section titled “Practical Implementation”](#practical-implementation) ### Complete Auth Service Example [Section titled “Complete Auth Service Example”](#complete-auth-service-example) ```typescript import { PersistentAccount } from '@capgo/capacitor-persistent-account'; interface UserAccount { userId: string; username: string; email: string; authToken?: string; preferences: { theme: string; language: string; notifications: boolean; }; } class AuthService { // Save user account after login async saveUserAccount(user: UserAccount) { try { await PersistentAccount.saveAccount({ data: user }); console.log('User account saved successfully'); } catch (error) { console.error('Failed to save account:', error); throw error; } } // Restore user account on app start async restoreUserAccount(): Promise { try { const result = await PersistentAccount.readAccount(); if (result.data) { const account = result.data as UserAccount; console.log('Restored user account:', account.username); return account; } console.log('No saved account found'); return null; } catch (error) { console.error('Failed to restore account:', error); return null; } } // Update user preferences async updatePreferences(preferences: Partial) { const account = await this.restoreUserAccount(); if (account) { const updatedAccount: UserAccount = { ...account, preferences: { ...account.preferences, ...preferences } }; await this.saveUserAccount(updatedAccount); } } // Clear account data (on logout) async clearAccount() { try { await PersistentAccount.saveAccount({ data: null }); console.log('Account data cleared'); } catch (error) { console.error('Failed to clear account:', error); } } } // Usage const authService = new AuthService(); // On login await authService.saveUserAccount({ userId: '12345', username: 'john_doe', email: 'john@example.com', authToken: 'abc123xyz', preferences: { theme: 'dark', language: 'en', notifications: true } }); // On app start const savedAccount = await authService.restoreUserAccount(); if (savedAccount) { // User was previously logged in console.log('Welcome back,', savedAccount.username); } // Update preferences await authService.updatePreferences({ theme: 'light', notifications: false }); // On logout await authService.clearAccount(); ``` ### App Initialization with Account Restoration [Section titled “App Initialization with Account Restoration”](#app-initialization-with-account-restoration) ```typescript import { PersistentAccount } from '@capgo/capacitor-persistent-account'; async function initializeApp() { try { // Try to restore saved account const result = await PersistentAccount.readAccount(); if (result.data) { const account = result.data as UserAccount; // Validate token is still valid const isValid = await validateAuthToken(account.authToken); if (isValid) { // Restore user session setCurrentUser(account); navigateToHome(); } else { // Token expired, show login navigateToLogin(); } } else { // No saved account, show login navigateToLogin(); } } catch (error) { console.error('Failed to initialize app:', error); navigateToLogin(); } } // Call on app start initializeApp(); ``` ### Syncing with Backend [Section titled “Syncing with Backend”](#syncing-with-backend) ```typescript import { PersistentAccount } from '@capgo/capacitor-persistent-account'; async function syncAccountWithBackend() { const result = await PersistentAccount.readAccount(); if (result.data) { const localAccount = result.data as UserAccount; try { // Fetch latest account data from server const response = await fetch(`/api/users/${localAccount.userId}`); const serverAccount = await response.json(); // Merge server data with local preferences const mergedAccount: UserAccount = { ...serverAccount, preferences: { ...serverAccount.preferences, ...localAccount.preferences // Local preferences take priority } }; // Save merged data await PersistentAccount.saveAccount({ data: mergedAccount }); return mergedAccount; } catch (error) { console.error('Failed to sync with backend:', error); // Return local account as fallback return localAccount; } } return null; } ``` ## Best Practices [Section titled “Best Practices”](#best-practices) * **Type Safety**: Define clear TypeScript interfaces for your account data * **Validation**: Always validate data when reading from persistent storage * **Error Handling**: Implement proper try-catch blocks for all operations * **Security**: Don’t store sensitive data like passwords in plain text * **Token Management**: Refresh auth tokens when restoring accounts * **Data Size**: Keep stored data minimal to ensure quick read/write operations * **Null Checks**: Always check if data exists before using it * **Backup Strategy**: Consider syncing with backend for additional safety ## Security Considerations [Section titled “Security Considerations”](#security-considerations) * Account data is stored using platform-specific secure storage mechanisms * Data persists across app uninstalls and reinstalls * Consider encrypting sensitive information before storing * Implement proper data validation when reading stored accounts * Follow platform guidelines for user data handling * Use authentication tokens with expiration for security * Clear account data appropriately on user logout ## Platform Implementation [Section titled “Platform Implementation”](#platform-implementation) ### iOS [Section titled “iOS”](#ios) * Utilizes iOS Keychain Services for secure, persistent storage * Data survives app deletion and device restores * Protected by iOS security mechanisms ### Android [Section titled “Android”](#android) * Uses Android Account Manager or shared preferences with backup * Maintains data across app reinstalls and device migrations * Protected by Android system security ## Use Cases [Section titled “Use Cases”](#use-cases) * **User Onboarding**: Preserve user progress through app reinstalls * **Account Recovery**: Restore user sessions after app updates * **Preferences Storage**: Maintain user settings and configurations * **Offline-First Apps**: Store essential user data locally * **Session Management**: Keep users logged in across app restarts * **Device Migration**: Transfer user data to new devices # @capgo/capacitor-persona > Start Persona inquiry flows natively on iOS and Android, prefill fields, and handle complete/cancel/error events in one Capacitor API. Native Persona SDKs Uses Persona iOS and Android SDKs for secure identity verification flows. One launch API Start inquiries by template ID, template version, or existing inquiry/session token. Event-driven results Listen for `inquiryComplete`, `inquiryCanceled`, and `inquiryError` to power your UX. Prefilled fields Pass trusted context fields (for example IDs and profile attributes) when launching. # Getting Started > Learn how to install and launch Persona inquiries in your Capacitor app. 1. **Install the package** * npm ```sh npm i @capgo/capacitor-persona ``` * pnpm ```sh pnpm add @capgo/capacitor-persona ``` * yarn ```sh yarn add @capgo/capacitor-persona ``` * bun ```sh bun add @capgo/capacitor-persona ``` 2. **Sync native projects** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## iOS Setup [Section titled “iOS Setup”](#ios-setup) Persona can require camera, location, and bluetooth depending on your inquiry template. Add usage descriptions in `Info.plist`: ```xml NSCameraUsageDescription This app uses the camera for identity verification. NSLocationWhenInUseUsageDescription This app uses location to support identity verification. NSBluetoothAlwaysUsageDescription This app uses bluetooth to improve identity verification checks. ``` ## Android Setup [Section titled “Android Setup”](#android-setup) No extra Java/Kotlin wiring is required. The plugin already configures Persona’s Android Maven repository and dependency. ## Basic Usage [Section titled “Basic Usage”](#basic-usage) ```ts import { Persona } from '@capgo/capacitor-persona'; await Persona.addListener('inquiryComplete', (result) => { console.log('Inquiry complete', result.inquiryId, result.status); }); await Persona.addListener('inquiryCanceled', (result) => { console.log('Inquiry canceled', result.inquiryId, result.sessionToken); }); await Persona.addListener('inquiryError', (result) => { console.error('Inquiry error', result.error, result.errorCode, result.cause); }); await Persona.startInquiry({ templateId: 'itmpl_EXAMPLE', environment: 'sandbox', referenceId: 'user_123', fields: { name_first: 'Alex', is_verified_user: true, }, }); ``` ## Resume an Existing Inquiry [Section titled “Resume an Existing Inquiry”](#resume-an-existing-inquiry) When resuming a flow created on your backend, pass `inquiryId` with a valid `sessionToken`: ```ts await Persona.startInquiry({ inquiryId: 'inq_123', sessionToken: 'usertok_abc', environment: 'production', }); ``` ## API Reference [Section titled “API Reference”](#api-reference) ### `startInquiry(options)` [Section titled “startInquiry(options)”](#startinquiryoptions) Launches Persona inquiry UI. Key options: * `templateId`: Recommended for new inquiries. * `templateVersion`: Use a specific template version. * `inquiryId` + `sessionToken`: Resume an existing inquiry. * `environment`: `'production' | 'sandbox'` (default: `'production'`). * `fields`: Key/value map to pre-write inquiry fields. ### Events [Section titled “Events”](#events) * `inquiryComplete`: Inquiry reached a completed state. * `inquiryCanceled`: User exited the inquiry. * `inquiryError`: Persona returned an unrecoverable error. ## Best Practices [Section titled “Best Practices”](#best-practices) 1. **Use webhooks for critical decisions**\ Treat client callbacks as UX signals. Base compliance or account decisions on Persona webhook events from your backend. 2. **Validate options before launch**\ Ensure you pass either `templateId`, `templateVersion`, or `inquiryId`. 3. **Handle cancellation and errors**\ Always listen for `inquiryCanceled` and `inquiryError` so users can recover gracefully. ## Platform Notes [Section titled “Platform Notes”](#platform-notes) * **iOS**: Requires usage descriptions in `Info.plist` for template-dependent checks. * **Android**: Uses Persona SDK v2 and launches inquiry via native activity contract. * **Web**: Not supported; the web implementation throws an unavailable error. # @capgo/capacitor-photo-library > Build custom galleries that tap into the native photo library with robust authorization flows. Capgo Photo Library helps you list albums, page through assets, and request thumbnails while keeping performance in check. Authorization helpers Request permissions and handle limited access states gracefully. Paged fetching Paginate the gallery with cursor-based loading and customizable limits. Optimized thumbnails Fetch thumbnail URLs at fixed dimensions for smooth scrolling lists. Media picking Let users select photos or videos with configurable selection limits. Check the getting started guide for permission setup and gallery examples. # Getting Started > Integrate the native photo gallery with pagination and media picking. 1. **Install the plugin** * npm ```sh npm i @capgo/capacitor-photo-library ``` * pnpm ```sh pnpm add @capgo/capacitor-photo-library ``` * yarn ```sh yarn add @capgo/capacitor-photo-library ``` * bun ```sh bun add @capgo/capacitor-photo-library ``` 2. **Sync platforms** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Platform permissions [Section titled “Platform permissions”](#platform-permissions) * **iOS**: Update `Info.plist` with `NSPhotoLibraryUsageDescription` (and `NSPhotoLibraryAddUsageDescription` if you plan to save media). * **Android**: For Android 13+, declare the photo/video permissions (`READ_MEDIA_IMAGES`, `READ_MEDIA_VIDEO`). Capacitor 5 handles this automatically, but double-check custom manifests. ## Request access [Section titled “Request access”](#request-access) ```typescript import { PhotoLibrary } from '@capgo/capacitor-photo-library'; const { state } = await PhotoLibrary.requestAuthorization(); if (state !== 'authorized' && state !== 'limited') { throw new Error('Photo access denied'); } ``` ## Load the gallery [Section titled “Load the gallery”](#load-the-gallery) ```typescript import { Capacitor } from '@capacitor/core'; const { assets, hasMore, nextPage } = await PhotoLibrary.getLibrary({ limit: 100, includeAlbums: false, thumbnailWidth: 256, thumbnailHeight: 256, }); const gallery = assets.map((asset) => ({ id: asset.id, fileName: asset.fileName, created: asset.creationDate, thumbnailUrl: asset.thumbnail ? asset.thumbnail.webPath ?? Capacitor.convertFileSrc(asset.thumbnail.path) : undefined, photoUrl: asset.file ? asset.file.webPath ?? Capacitor.convertFileSrc(asset.file.path) : undefined, })); ``` To fetch additional pages, call `PhotoLibrary.getLibrary({ cursor: nextPage })`. ## Let users pick media [Section titled “Let users pick media”](#let-users-pick-media) ```typescript const selection = await PhotoLibrary.pickMedia({ selectionLimit: 5, includeVideos: true, }); console.log('User selected', selection.assets.length, 'items'); ``` ## Clean up cached files [Section titled “Clean up cached files”](#clean-up-cached-files) Generated thumbnails and exports are stored in the app cache directory under `photoLibrary`. Remove them with your preferred file API when you need to reclaim storage. # @capgo/capacitor-printer > Print documents, HTML content, PDFs, images and web views directly from your Capacitor app on iOS and Android. Multiple Formats Print HTML, PDFs, images, and web content 📄 Native UI Uses native print dialogs on iOS and Android 🖨️ Easy Integration Simple API for quick integration into your app 🚀 Cross-platform Works on both iOS and Android devices 📱 Customizable Control print options like orientation and page size ⚙️ Comprehensive Documentation Check the [Documentation](/docs/plugins/printer/getting-started/) to master the plugin in just a few minutes. # Getting Started > Learn how to install and use the Printer plugin to print content from your Capacitor app. 1. **Install the package** * npm ```sh npm i @capgo/capacitor-printer ``` * pnpm ```sh pnpm add @capgo/capacitor-printer ``` * yarn ```sh yarn add @capgo/capacitor-printer ``` * bun ```sh bun add @capgo/capacitor-printer ``` 2. **Sync with native projects** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Usage [Section titled “Usage”](#usage) Import the plugin and print various types of content: ```typescript import { Printer } from '@capgo/capacitor-printer'; // Print HTML content const printHTML = async () => { await Printer.print({ content: '

Hello World

This is a test print.

', name: 'test-print' }); }; // Print a PDF file const printPDF = async () => { await Printer.print({ content: 'file:///path/to/document.pdf', name: 'my-document' }); }; // Print an image const printImage = async () => { await Printer.print({ content: 'file:///path/to/image.jpg', name: 'my-image' }); }; ``` ## API Reference [Section titled “API Reference”](#api-reference) ### print(options) [Section titled “print(options)”](#printoptions) Opens the native print dialog with the specified content. ```typescript interface PrintOptions { content: string; // HTML string, file URI, or base64 data name?: string; // Print job name orientation?: 'portrait' | 'landscape'; grayscale?: boolean; // Print in grayscale (default: false) } await Printer.print(options); ``` ### canPrint() [Section titled “canPrint()”](#canprint) Checks if printing is available on the device. ```typescript const { value } = await Printer.canPrint(); console.log('Printing available:', value); ``` ## Complete Example [Section titled “Complete Example”](#complete-example) ```typescript import { Printer } from '@capgo/capacitor-printer'; import { Filesystem, Directory } from '@capacitor/filesystem'; export class PrintService { async checkPrintAvailability(): Promise { const { value } = await Printer.canPrint(); return value; } async printHTML(htmlContent: string, jobName: string = 'Document') { try { const canPrint = await this.checkPrintAvailability(); if (!canPrint) { throw new Error('Printing not available on this device'); } await Printer.print({ content: htmlContent, name: jobName, orientation: 'portrait' }); } catch (error) { console.error('Print failed:', error); throw error; } } async printDocument(filePath: string, jobName?: string) { await Printer.print({ content: filePath, name: jobName || 'Document' }); } async printReceipt(receiptData: any) { const html = `

Receipt

Date: ${new Date().toLocaleDateString()}

${receiptData.items.map((item: any) => `
${item.name} $${item.price.toFixed(2)}
`).join('')}
Total: $${receiptData.total.toFixed(2)}
`; await this.printHTML(html, 'Receipt'); } async printFromURL(url: string) { // Download the file first const response = await fetch(url); const blob = await response.blob(); const reader = new FileReader(); return new Promise((resolve, reject) => { reader.onloadend = async () => { try { await Printer.print({ content: reader.result as string, name: 'Downloaded Document' }); resolve(true); } catch (error) { reject(error); } }; reader.onerror = reject; reader.readAsDataURL(blob); }); } } ``` ## Advanced Usage [Section titled “Advanced Usage”](#advanced-usage) ### Printing with Custom Styling [Section titled “Printing with Custom Styling”](#printing-with-custom-styling) ```typescript const printStyledDocument = async () => { const html = `

Professional Document

This is a professionally styled document ready for printing.

`; await Printer.print({ content: html, name: 'Styled Document', orientation: 'portrait' }); }; ``` ### Printing Tables [Section titled “Printing Tables”](#printing-tables) ```typescript const printTable = async (data: any[]) => { const tableRows = data.map(row => ` ${row.id} ${row.name} ${row.value} `).join(''); const html = ` ${tableRows}
ID Name Value
`; await Printer.print({ content: html, name: 'Data Table' }); }; ``` ### Printing Images [Section titled “Printing Images”](#printing-images) ```typescript const printImageWithDetails = async (imagePath: string, title: string) => { const html = `

${title}

${title} `; await Printer.print({ content: html, name: title }); }; ``` ## Best Practices [Section titled “Best Practices”](#best-practices) 1. **Check Availability**: Always check if printing is available before attempting to print 2. **Valid Content**: Ensure HTML is well-formed and file paths are valid 3. **Job Names**: Use descriptive names for print jobs to help users identify them 4. **Styling**: Use inline CSS or embedded styles for consistent print output 5. **Error Handling**: Wrap print calls in try-catch blocks for user feedback ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) ### Common Issues [Section titled “Common Issues”](#common-issues) **Print dialog not appearing**: Check that printing is available with canPrint() **Content not printing**: Verify that HTML is valid and file paths are correct **Images not showing**: Ensure image paths are absolute and files are accessible **Styling issues**: Use @page CSS rules and test print output # @capgo/capacitor-realtimekit > Add video conferencing and real-time communication to your app with Cloudflare Calls integration and built-in UI. Cloudflare Calls Powered by Cloudflare’s global infrastructure ☁️ Built-in Meeting UI Ready-to-use video conferencing interface 📹 WebRTC Support High-quality real-time audio and video 🎥 Background Modes Continue calls when app is backgrounded 🔄 Screen Sharing Share screen during meetings on Android 📱 Getting Started Check the [Getting Started Guide](/docs/plugins/realtimekit/getting-started/) to install and configure the plugin. # Getting Started with RealtimeKit > Learn how to integrate Cloudflare Calls video conferencing into your Capacitor app This guide will walk you through integrating the Capacitor RealtimeKit plugin to add video conferencing powered by Cloudflare Calls to your application. ## Installation [Section titled “Installation”](#installation) Install the plugin using npm: ```bash npm install @capgo/capacitor-realtimekit npx cap sync ``` ## Dependencies [Section titled “Dependencies”](#dependencies) This plugin uses the Cloudflare RealtimeKit SDK: * **iOS**: RealtimeKitCoreiOS (automatically installed via Swift Package Manager) * **Android**: `com.cloudflare.realtimekit:ui-android` version `0.2.2` ### Customizing Android RealtimeKit Version [Section titled “Customizing Android RealtimeKit Version”](#customizing-android-realtimekit-version) In your app’s `build.gradle`: ```kotlin buildscript { ext { realtimekitUiVersion = '0.2.2' // or your desired version } } ``` ## iOS Configuration [Section titled “iOS Configuration”](#ios-configuration) Add the following to your app’s `Info.plist`: ```xml NSCameraUsageDescription We need camera access for video calls NSMicrophoneUsageDescription We need microphone access for audio calls NSPhotoLibraryUsageDescription We need photo library access to share images NSBluetoothPeripheralUsageDescription We need Bluetooth access for audio routing UIBackgroundModes audio voip fetch remote-notification ``` ## Android Configuration [Section titled “Android Configuration”](#android-configuration) Add the following permissions to your `AndroidManifest.xml`: ```xml ``` ## Basic Usage [Section titled “Basic Usage”](#basic-usage) ### Import the Plugin [Section titled “Import the Plugin”](#import-the-plugin) ```typescript import { CapacitorRealtimekit } from '@capgo/capacitor-realtimekit'; ``` ### Initialize the Plugin [Section titled “Initialize the Plugin”](#initialize-the-plugin) ```typescript async function initializeRealtimeKit() { try { await CapacitorRealtimekit.initialize(); console.log('RealtimeKit initialized'); } catch (error) { console.error('Failed to initialize RealtimeKit:', error); } } ``` ### Start a Meeting [Section titled “Start a Meeting”](#start-a-meeting) ```typescript async function startMeeting(meetingUrl: string) { try { await CapacitorRealtimekit.startMeeting({ url: meetingUrl }); console.log('Meeting started'); } catch (error) { console.error('Failed to start meeting:', error); } } ``` ## Complete Example [Section titled “Complete Example”](#complete-example) Here’s a comprehensive video conferencing service: ```typescript import { CapacitorRealtimekit } from '@capgo/capacitor-realtimekit'; export interface MeetingConfig { url: string; displayName?: string; audioEnabled?: boolean; videoEnabled?: boolean; } export class VideoConferenceService { private isInitialized = false; private currentMeetingUrl: string | null = null; async initialize(): Promise { if (this.isInitialized) { console.log('RealtimeKit already initialized'); return true; } try { await CapacitorRealtimekit.initialize(); this.isInitialized = true; console.log('RealtimeKit initialized successfully'); return true; } catch (error) { console.error('Failed to initialize RealtimeKit:', error); return false; } } async startMeeting(config: MeetingConfig): Promise { if (!this.isInitialized) { const initialized = await this.initialize(); if (!initialized) { throw new Error('Failed to initialize RealtimeKit'); } } try { await CapacitorRealtimekit.startMeeting({ url: config.url }); this.currentMeetingUrl = config.url; console.log('Meeting started:', config.url); } catch (error) { console.error('Failed to start meeting:', error); throw error; } } async joinMeeting(meetingUrl: string, displayName?: string): Promise { const config: MeetingConfig = { url: meetingUrl, displayName: displayName }; await this.startMeeting(config); } getCurrentMeetingUrl(): string | null { return this.currentMeetingUrl; } isInMeeting(): boolean { return this.currentMeetingUrl !== null; } async getPluginVersion(): Promise { try { const result = await CapacitorRealtimekit.getPluginVersion(); return result.version; } catch (error) { console.error('Failed to get plugin version:', error); return 'unknown'; } } } // Usage const videoService = new VideoConferenceService(); // Initialize on app start await videoService.initialize(); // Start a meeting await videoService.startMeeting({ url: 'https://your-cloudflare-calls-url.com/meeting/123' }); // Join an existing meeting await videoService.joinMeeting( 'https://your-cloudflare-calls-url.com/meeting/456', 'John Doe' ); ``` ## Cloudflare Calls Setup [Section titled “Cloudflare Calls Setup”](#cloudflare-calls-setup) To use this plugin, you need to set up Cloudflare Calls: ### 1. Create Cloudflare Account [Section titled “1. Create Cloudflare Account”](#1-create-cloudflare-account) Sign up at [Cloudflare](https://dash.cloudflare.com/sign-up) if you don’t have an account. ### 2. Enable Calls API [Section titled “2. Enable Calls API”](#2-enable-calls-api) 1. Navigate to your Cloudflare dashboard 2. Go to **Calls** section 3. Enable the Calls API 4. Get your API credentials ### 3. Create Meeting URLs [Section titled “3. Create Meeting URLs”](#3-create-meeting-urls) You need a backend service to create meeting URLs. Example using Cloudflare Workers: ```typescript // Cloudflare Worker example export default { async fetch(request: Request): Promise { const { pathname } = new URL(request.url); if (pathname === '/create-meeting') { const meetingId = generateMeetingId(); const meetingUrl = `https://your-app.calls.cloudflare.com/${meetingId}`; return new Response(JSON.stringify({ meetingId, meetingUrl }), { headers: { 'Content-Type': 'application/json' } }); } return new Response('Not found', { status: 404 }); } }; function generateMeetingId(): string { return Math.random().toString(36).substring(2, 15); } ``` ## Advanced Usage [Section titled “Advanced Usage”](#advanced-usage) ### Meeting with Custom Configuration [Section titled “Meeting with Custom Configuration”](#meeting-with-custom-configuration) ```typescript class MeetingManager { private videoService: VideoConferenceService; constructor() { this.videoService = new VideoConferenceService(); } async createAndJoinMeeting(userName: string): Promise { // Initialize if needed await this.videoService.initialize(); // Call your backend to create a meeting const meetingUrl = await this.createMeetingOnBackend(); // Join the meeting await this.videoService.joinMeeting(meetingUrl, userName); return meetingUrl; } async createMeetingOnBackend(): Promise { const response = await fetch('https://your-api.com/create-meeting', { method: 'POST', headers: { 'Content-Type': 'application/json' } }); const data = await response.json(); return data.meetingUrl; } async shareMeetingLink(meetingUrl: string) { // Use Capacitor Share API if ('share' in navigator) { await (navigator as any).share({ title: 'Join my meeting', text: 'Join me for a video call', url: meetingUrl }); } } } ``` ### Handling Permissions [Section titled “Handling Permissions”](#handling-permissions) ```typescript import { Capacitor } from '@capacitor/core'; async function checkAndRequestPermissions(): Promise { if (Capacitor.getPlatform() === 'web') { // Request browser permissions try { const stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: true }); // Stop the stream, we just needed permission stream.getTracks().forEach(track => track.stop()); return true; } catch (error) { console.error('Permission denied:', error); return false; } } // On native platforms, permissions are requested automatically return true; } // Use before starting a meeting const hasPermissions = await checkAndRequestPermissions(); if (hasPermissions) { await videoService.startMeeting({ url: meetingUrl }); } ``` ## React Integration [Section titled “React Integration”](#react-integration) ```typescript import { useEffect, useState } from 'react'; import { VideoConferenceService } from './VideoConferenceService'; function VideoCallComponent() { const [videoService] = useState(() => new VideoConferenceService()); const [isReady, setIsReady] = useState(false); useEffect(() => { videoService.initialize().then(setIsReady); }, []); const joinMeeting = async (meetingUrl: string) => { if (!isReady) { console.error('Video service not ready'); return; } try { await videoService.joinMeeting(meetingUrl, 'User Name'); } catch (error) { console.error('Failed to join meeting:', error); } }; return (
); } ``` ## Vue Integration [Section titled “Vue Integration”](#vue-integration) ```typescript import { ref, onMounted } from 'vue'; import { VideoConferenceService } from './VideoConferenceService'; export default { setup() { const videoService = new VideoConferenceService(); const isReady = ref(false); onMounted(async () => { isReady.value = await videoService.initialize(); }); const joinMeeting = async (meetingUrl: string) => { if (!isReady.value) { console.error('Video service not ready'); return; } try { await videoService.joinMeeting(meetingUrl, 'User Name'); } catch (error) { console.error('Failed to join meeting:', error); } }; return { isReady, joinMeeting }; } }; ``` ## Best Practices [Section titled “Best Practices”](#best-practices) 1. **Initialize Early**: Initialize RealtimeKit when your app starts 2. **Handle Errors**: Always wrap calls in try-catch blocks 3. **Request Permissions**: Ensure camera/microphone permissions before starting 4. **Test Network**: Check internet connectivity before joining 5. **Background Audio**: Configure background modes for iOS 6. **User Experience**: Show loading states during initialization 7. **Clean URLs**: Validate meeting URLs before use ## Common Issues [Section titled “Common Issues”](#common-issues) ### Meeting Not Starting [Section titled “Meeting Not Starting”](#meeting-not-starting) ```typescript async function troubleshootMeeting(meetingUrl: string) { // Check initialization const version = await videoService.getPluginVersion(); console.log('Plugin version:', version); // Verify URL format if (!meetingUrl.startsWith('https://')) { console.error('Invalid meeting URL, must use HTTPS'); return; } // Try starting meeting try { await videoService.startMeeting({ url: meetingUrl }); } catch (error) { console.error('Meeting failed:', error); } } ``` ### Permission Denied [Section titled “Permission Denied”](#permission-denied) On iOS, ensure Info.plist has all required usage descriptions. On Android, verify AndroidManifest.xml permissions are present. ### Audio Issues on iOS [Section titled “Audio Issues on iOS”](#audio-issues-on-ios) Ensure background audio mode is configured in Info.plist: ```xml UIBackgroundModes audio voip ``` ## Next Steps [Section titled “Next Steps”](#next-steps) * Explore the [API Reference](https://github.com/Cap-go/capacitor-realtimekit#api) for complete method documentation * Read [Cloudflare Calls Documentation](https://developers.cloudflare.com/calls/) * Check out the [example app](https://github.com/Cap-go/capacitor-realtimekit/tree/main/example) * See the [tutorial](/plugins/capacitor-realtimekit) for complete implementation # @capgo/capacitor-ricoh360-camera > Control Ricoh 360° cameras for immersive panoramic photo and video capture with full device integration. 360° capture Capture immersive panoramic photos and videos 🌐 Device control Full control over Ricoh Theta camera settings 📸 Live preview Real-time 360° preview streaming 👁️ Comprehensive Documentation Check the [Documentation](/docs/plugins/ricoh360-camera/getting-started/) to master the plugin in just a few minutes. # Getting Started > Learn how to install and use the Ricoh 360° Camera plugin to control Ricoh Theta cameras in your Capacitor app. ## Installation [Section titled “Installation”](#installation) * npm ```bash npm install @capgo/capacitor-ricoh360-camera npx cap sync ``` * yarn ```bash yarn add @capgo/capacitor-ricoh360-camera npx cap sync ``` * pnpm ```bash pnpm add @capgo/capacitor-ricoh360-camera npx cap sync ``` * bun ```bash bun add @capgo/capacitor-ricoh360-camera npx cap sync ``` ## Platform Configuration [Section titled “Platform Configuration”](#platform-configuration) ### iOS [Section titled “iOS”](#ios) Add camera and photo library permissions to your `Info.plist`: ```xml NSCameraUsageDescription This app needs access to the camera to capture 360° photos and videos NSPhotoLibraryAddUsageDescription This app needs access to save 360° photos and videos to your library ``` ### Android [Section titled “Android”](#android) Add required permissions to your `AndroidManifest.xml`: ```xml ``` ## Supported Devices [Section titled “Supported Devices”](#supported-devices) This plugin supports Ricoh Theta series cameras: * Ricoh Theta V * Ricoh Theta Z1 * Ricoh Theta SC2 * Ricoh Theta X ## Usage Example [Section titled “Usage Example”](#usage-example) ```typescript import { Ricoh360Camera } from '@capgo/capacitor-ricoh360-camera'; // Initialize camera connection await Ricoh360Camera.initialize(); // Connect to Ricoh Theta camera via WiFi await Ricoh360Camera.connect({ ssid: 'THETAYL12345678', password: '12345678' }); // Start live preview await Ricoh360Camera.startPreview({ container: 'preview-container' // ID of HTML element }); // Capture a 360° photo const result = await Ricoh360Camera.capturePhoto(); console.log('Photo saved:', result.filePath); // Start recording 360° video await Ricoh360Camera.startRecording(); // Stop recording after some time setTimeout(async () => { const video = await Ricoh360Camera.stopRecording(); console.log('Video saved:', video.filePath); }, 10000); // Stop preview await Ricoh360Camera.stopPreview(); // Disconnect from camera await Ricoh360Camera.disconnect(); ``` ## API Reference [Section titled “API Reference”](#api-reference) ### initialize() [Section titled “initialize()”](#initialize) ```typescript initialize() => Promise ``` Initialize the camera plugin. ### connect(options) [Section titled “connect(options)”](#connectoptions) ```typescript connect(options: ConnectOptions) => Promise ``` Connect to a Ricoh Theta camera via WiFi. | Param | Type | | ------------- | ---------------- | | **`options`** | `ConnectOptions` | ### disconnect() [Section titled “disconnect()”](#disconnect) ```typescript disconnect() => Promise ``` Disconnect from the camera. ### startPreview(options) [Section titled “startPreview(options)”](#startpreviewoptions) ```typescript startPreview(options: PreviewOptions) => Promise ``` Start live 360° preview stream. | Param | Type | | ------------- | ---------------- | | **`options`** | `PreviewOptions` | ### stopPreview() [Section titled “stopPreview()”](#stoppreview) ```typescript stopPreview() => Promise ``` Stop the live preview stream. ### capturePhoto(options?) [Section titled “capturePhoto(options?)”](#capturephotooptions) ```typescript capturePhoto(options?: CaptureOptions) => Promise ``` Capture a 360° photo. | Param | Type | | ------------- | --------------------------- | | **`options`** | `CaptureOptions` (optional) | **Returns:** `Promise` ### startRecording(options?) [Section titled “startRecording(options?)”](#startrecordingoptions) ```typescript startRecording(options?: RecordingOptions) => Promise ``` Start recording 360° video. | Param | Type | | ------------- | ----------------------------- | | **`options`** | `RecordingOptions` (optional) | ### stopRecording() [Section titled “stopRecording()”](#stoprecording) ```typescript stopRecording() => Promise ``` Stop video recording. **Returns:** `Promise` ### getCameraInfo() [Section titled “getCameraInfo()”](#getcamerainfo) ```typescript getCameraInfo() => Promise ``` Get camera information and capabilities. **Returns:** `Promise` ### setExposure(options) [Section titled “setExposure(options)”](#setexposureoptions) ```typescript setExposure(options: ExposureOptions) => Promise ``` Set camera exposure settings. | Param | Type | | ------------- | ----------------- | | **`options`** | `ExposureOptions` | ### setWhiteBalance(options) [Section titled “setWhiteBalance(options)”](#setwhitebalanceoptions) ```typescript setWhiteBalance(options: WhiteBalanceOptions) => Promise ``` Set white balance mode. | Param | Type | | ------------- | --------------------- | | **`options`** | `WhiteBalanceOptions` | ## Interfaces [Section titled “Interfaces”](#interfaces) ### ConnectOptions [Section titled “ConnectOptions”](#connectoptions-1) | Prop | Type | Description | | -------------- | -------- | ----------------------------- | | **`ssid`** | `string` | WiFi SSID of the Ricoh camera | | **`password`** | `string` | WiFi password of the camera | ### PreviewOptions [Section titled “PreviewOptions”](#previewoptions) | Prop | Type | Description | | --------------- | -------- | ------------------------------------- | | **`container`** | `string` | HTML element ID for preview container | ### CaptureOptions [Section titled “CaptureOptions”](#captureoptions) | Prop | Type | Description | | ------------------- | --------- | --------------------------------- | | **`quality`** | `number` | Image quality (1-100) (optional) | | **`saveToGallery`** | `boolean` | Save to device gallery (optional) | ### RecordingOptions [Section titled “RecordingOptions”](#recordingoptions) | Prop | Type | Description | | ----------------- | -------- | ----------------------------------------------------- | | **`maxDuration`** | `number` | Maximum recording duration in seconds (optional) | | **`quality`** | `string` | Video quality: ‘high’ \| ‘medium’ \| ‘low’ (optional) | ### CaptureResult [Section titled “CaptureResult”](#captureresult) | Prop | Type | Description | | -------------- | -------- | ------------------------- | | **`filePath`** | `string` | Path to the captured file | | **`fileUrl`** | `string` | URL to access the file | ### CameraInfo [Section titled “CameraInfo”](#camerainfo) | Prop | Type | Description | | --------------------- | -------- | --------------------- | | **`model`** | `string` | Camera model name | | **`serialNumber`** | `string` | Camera serial number | | **`firmwareVersion`** | `string` | Firmware version | | **`batteryLevel`** | `number` | Battery level (0-100) | ### ExposureOptions [Section titled “ExposureOptions”](#exposureoptions) | Prop | Type | Description | | ------------- | -------- | ------------------------------------------ | | **`mode`** | `string` | Exposure mode: ‘auto’ \| ‘manual’ \| ‘iso’ | | **`iso`** | `number` | ISO value (100, 200, 400, etc.) (optional) | | **`shutter`** | `number` | Shutter speed (optional) | ### WhiteBalanceOptions [Section titled “WhiteBalanceOptions”](#whitebalanceoptions) | Prop | Type | Description | | ---------- | -------- | ------------------------------------------------------------------------------ | | **`mode`** | `string` | White balance: ‘auto’ \| ‘daylight’ \| ‘cloudy’ \| ‘tungsten’ \| ‘fluorescent’ | ## Complete Example [Section titled “Complete Example”](#complete-example) ```typescript import { Ricoh360Camera } from '@capgo/capacitor-ricoh360-camera'; class Camera360Controller { async setup() { try { // Initialize await Ricoh360Camera.initialize(); // Connect to camera await Ricoh360Camera.connect({ ssid: 'THETAYL12345678', password: '12345678' }); // Get camera info const info = await Ricoh360Camera.getCameraInfo(); console.log('Connected to:', info.model); console.log('Battery level:', info.batteryLevel + '%'); // Configure exposure await Ricoh360Camera.setExposure({ mode: 'auto' }); // Configure white balance await Ricoh360Camera.setWhiteBalance({ mode: 'auto' }); // Start preview await Ricoh360Camera.startPreview({ container: 'camera-preview' }); } catch (error) { console.error('Failed to setup camera:', error); } } async takePhoto() { try { const result = await Ricoh360Camera.capturePhoto({ quality: 95, saveToGallery: true }); console.log('Photo captured:', result.filePath); return result; } catch (error) { console.error('Failed to capture photo:', error); } } async recordVideo(durationSeconds: number) { try { // Start recording await Ricoh360Camera.startRecording({ maxDuration: durationSeconds, quality: 'high' }); console.log('Recording started...'); // Stop after duration setTimeout(async () => { const result = await Ricoh360Camera.stopRecording(); console.log('Video saved:', result.filePath); }, durationSeconds * 1000); } catch (error) { console.error('Failed to record video:', error); } } async cleanup() { try { await Ricoh360Camera.stopPreview(); await Ricoh360Camera.disconnect(); console.log('Camera disconnected'); } catch (error) { console.error('Failed to cleanup:', error); } } } // Usage const camera = new Camera360Controller(); await camera.setup(); await camera.takePhoto(); await camera.recordVideo(10); // 10 seconds await camera.cleanup(); ``` ## Best Practices [Section titled “Best Practices”](#best-practices) * Always initialize the plugin before connecting to the camera * Ensure the device is connected to the camera’s WiFi network * Handle errors gracefully, especially connection failures * Stop preview and disconnect when done to save battery * Check camera battery level before long recording sessions * Test on actual Ricoh Theta hardware * Use appropriate video quality based on storage constraints ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) ### Connection Issues [Section titled “Connection Issues”](#connection-issues) * Verify the camera is powered on * Check WiFi SSID and password are correct * Ensure no other app is connected to the camera * Reset camera WiFi settings if connection fails ### Preview Not Showing [Section titled “Preview Not Showing”](#preview-not-showing) * Verify the HTML container element exists * Check camera permissions are granted * Ensure preview is started after successful connection ### Capture Failures [Section titled “Capture Failures”](#capture-failures) * Check available storage space * Verify camera battery level is sufficient * Ensure camera is not in sleep mode # @capgo/capacitor-screen-orientation > Screen orientation plugin with support for detecting true physical device orientation - even when orientation lock is enabled. Physical Orientation Detection Detect true physical device orientation using motion sensors, even when orientation lock is enabled Orientation Lock Detection Determine if the user has enabled orientation lock by comparing physical vs UI orientation Cross-platform Works on iOS (Core Motion), Android (Accelerometer), and Web Real-time Updates Get instant notifications when device orientation changes with event listeners # Getting Started > Learn how to install and use the Screen Orientation plugin to control and detect device orientation in your Capacitor app. 1. **Install the package** * npm ```sh npm i @capgo/capacitor-screen-orientation ``` * pnpm ```sh pnpm add @capgo/capacitor-screen-orientation ``` * yarn ```sh yarn add @capgo/capacitor-screen-orientation ``` * bun ```sh bun add @capgo/capacitor-screen-orientation ``` 2. **Sync with native projects** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` 3. **iOS Configuration (optional)** To detect physical device orientation using motion sensors on iOS, add to your `Info.plist`: ```xml NSMotionUsageDescription This app uses motion sensors to detect device orientation. ``` ## Basic Usage [Section titled “Basic Usage”](#basic-usage) ```typescript import { ScreenOrientation } from '@capgo/capacitor-screen-orientation'; // Get current orientation const current = await ScreenOrientation.orientation(); console.log('Current orientation:', current.type); // Lock to landscape await ScreenOrientation.lock({ orientation: 'landscape' }); // Unlock orientation await ScreenOrientation.unlock(); // Listen for orientation changes const listener = await ScreenOrientation.addListener( 'screenOrientationChange', (result) => { console.log('Orientation changed:', result.type); } ); ``` ## Physical Orientation Detection [Section titled “Physical Orientation Detection”](#physical-orientation-detection) This plugin has a unique feature: it can detect the **true physical orientation** of the device using motion sensors, even when the user has enabled orientation lock on their device. Note The UI will still respect the user’s orientation lock setting. Motion tracking allows you to know how the device is physically held, but the UI won’t rotate if orientation lock is enabled. ```typescript import { ScreenOrientation } from '@capgo/capacitor-screen-orientation'; // Start motion-based tracking await ScreenOrientation.startOrientationTracking({ bypassOrientationLock: true }); // Now orientation change events will reflect physical orientation const listener = await ScreenOrientation.addListener( 'screenOrientationChange', (result) => { console.log('Physical orientation:', result.type); } ); // Check if orientation lock is enabled const lockStatus = await ScreenOrientation.isOrientationLocked(); if (lockStatus.locked) { console.log('User has orientation lock enabled'); console.log('Physical:', lockStatus.physicalOrientation); console.log('UI:', lockStatus.uiOrientation); } // Stop tracking when done await ScreenOrientation.stopOrientationTracking(); ``` ## API Reference [Section titled “API Reference”](#api-reference) ### orientation() [Section titled “orientation()”](#orientation) Get the current screen orientation. ```typescript const result = await ScreenOrientation.orientation(); // Returns: { type: OrientationType } ``` **OrientationType values:** * `'portrait-primary'` - Portrait, home button at bottom * `'portrait-secondary'` - Portrait, upside down * `'landscape-primary'` - Landscape, home button on right * `'landscape-secondary'` - Landscape, home button on left ### lock(options) [Section titled “lock(options)”](#lockoptions) Lock the screen to a specific orientation. ```typescript interface OrientationLockOptions { orientation: OrientationLockType; bypassOrientationLock?: boolean; // Enable motion tracking } await ScreenOrientation.lock({ orientation: 'landscape' }); ``` **OrientationLockType values:** * `'any'` - Any orientation * `'natural'` - Device’s natural orientation * `'landscape'` - Any landscape mode * `'portrait'` - Any portrait mode * `'portrait-primary'` / `'portrait-secondary'` * `'landscape-primary'` / `'landscape-secondary'` ### unlock() [Section titled “unlock()”](#unlock) Unlock the screen orientation. ```typescript await ScreenOrientation.unlock(); ``` ### startOrientationTracking(options?) [Section titled “startOrientationTracking(options?)”](#startorientationtrackingoptions) Start tracking physical device orientation using motion sensors. ```typescript await ScreenOrientation.startOrientationTracking({ bypassOrientationLock: true }); ``` ### stopOrientationTracking() [Section titled “stopOrientationTracking()”](#stoporientationtracking) Stop motion-based orientation tracking. ```typescript await ScreenOrientation.stopOrientationTracking(); ``` ### isOrientationLocked() [Section titled “isOrientationLocked()”](#isorientationlocked) Check if device orientation lock is enabled. ```typescript const result = await ScreenOrientation.isOrientationLocked(); // Returns: { // locked: boolean, // physicalOrientation?: OrientationType, // uiOrientation?: OrientationType // } ``` Tip This method requires motion tracking to be active to work properly. ### addListener(eventName, callback) [Section titled “addListener(eventName, callback)”](#addlistenereventname-callback) Listen for orientation changes. ```typescript const listener = await ScreenOrientation.addListener( 'screenOrientationChange', (result) => { console.log('New orientation:', result.type); } ); // Remove when done await listener.remove(); ``` ### removeAllListeners() [Section titled “removeAllListeners()”](#removealllisteners) Remove all event listeners. ```typescript await ScreenOrientation.removeAllListeners(); ``` ## Complete Example [Section titled “Complete Example”](#complete-example) ```typescript import { ScreenOrientation } from '@capgo/capacitor-screen-orientation'; class OrientationManager { private listener: any = null; async init() { // Start motion tracking for physical orientation detection await ScreenOrientation.startOrientationTracking({ bypassOrientationLock: true }); // Listen for changes this.listener = await ScreenOrientation.addListener( 'screenOrientationChange', this.onOrientationChange.bind(this) ); // Get initial orientation const { type } = await ScreenOrientation.orientation(); console.log('Initial orientation:', type); } onOrientationChange(result: { type: string }) { console.log('Orientation changed to:', result.type); // Adjust UI based on orientation if (result.type.includes('landscape')) { this.showLandscapeUI(); } else { this.showPortraitUI(); } } showLandscapeUI() { // Landscape-specific UI adjustments } showPortraitUI() { // Portrait-specific UI adjustments } async lockLandscape() { await ScreenOrientation.lock({ orientation: 'landscape' }); } async lockPortrait() { await ScreenOrientation.lock({ orientation: 'portrait' }); } async freeRotation() { await ScreenOrientation.unlock(); } async checkIfUserHasOrientationLock() { const { locked, physicalOrientation, uiOrientation } = await ScreenOrientation.isOrientationLocked(); if (locked) { console.log(`Device is held in ${physicalOrientation} but UI shows ${uiOrientation}`); return true; } return false; } async destroy() { await ScreenOrientation.stopOrientationTracking(); if (this.listener) { await this.listener.remove(); } } } ``` ## Platform Notes [Section titled “Platform Notes”](#platform-notes) ### iOS [Section titled “iOS”](#ios) * Uses Core Motion framework for physical orientation detection * Requires `NSMotionUsageDescription` in Info.plist for motion sensors * Full support for all orientation types ### Android [Section titled “Android”](#android) * Uses accelerometer sensor for physical orientation detection * No additional permissions required * Full support for all orientation types ### Web [Section titled “Web”](#web) * Uses Screen Orientation API * Motion sensor detection is limited * Some browsers may not support all lock types # @capgo/capacitor-screen-recorder > Record your device's screen with this powerful Capacitor plugin, perfect for creating tutorials, demos, and capturing app interactions. High quality recording Record screen in high quality with customizable settings 🎥 Audio support Record with microphone audio and system sounds 🎤 Easy integration Simple API with start/stop recording methods 😊 Comprehensive Documentation Check the [Documentation](/docs/plugins/screen-recorder/getting-started/) to master the plugin in just a few minutes. # Getting Started > Learn how to install and use the Screen Recorder plugin to capture screen recordings in your Capacitor app. 1. **Install the package** * npm ```sh npm i @capgo/capacitor-screen-recorder ``` * pnpm ```sh pnpm add @capgo/capacitor-screen-recorder ``` * yarn ```sh yarn add @capgo/capacitor-screen-recorder ``` * bun ```sh bun add @capgo/capacitor-screen-recorder ``` 2. **Sync with native projects** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` 3. **Configure permissions** ### iOS [Section titled “iOS”](#ios) Add usage descriptions to your `Info.plist`: ```xml NSMicrophoneUsageDescription To record audio with screen recording NSPhotoLibraryUsageDescription To save screen recordings ``` ### Android [Section titled “Android”](#android) Add permissions to your `AndroidManifest.xml`: ```xml ``` ## Usage [Section titled “Usage”](#usage) Import the plugin and use its methods to record the screen: ```typescript import { ScreenRecorder } from '@capgo/capacitor-screen-recorder'; // Start recording const startRecording = async () => { try { await ScreenRecorder.start(); console.log('Recording started'); } catch (error) { console.error('Failed to start recording:', error); } }; // Stop recording const stopRecording = async () => { try { const result = await ScreenRecorder.stop(); console.log('Recording saved to:', result.videoUrl); // You can now use the video URL // For example, share it or play it back } catch (error) { console.error('Failed to stop recording:', error); } }; // Check if recording is supported const checkSupport = async () => { const { value } = await ScreenRecorder.isSupported(); console.log('Screen recording supported:', value); }; // Check if currently recording const checkRecordingStatus = async () => { const { value } = await ScreenRecorder.isRecording(); console.log('Currently recording:', value); }; ``` ## API Reference [Section titled “API Reference”](#api-reference) ### start() [Section titled “start()”](#start) Starts screen recording. ```typescript await ScreenRecorder.start(); ``` ### stop() [Section titled “stop()”](#stop) Stops recording and returns the video file path. ```typescript interface RecordingResult { videoUrl: string; } const result = await ScreenRecorder.stop(); ``` ### isSupported() [Section titled “isSupported()”](#issupported) Checks if screen recording is supported on the device. ```typescript const { value } = await ScreenRecorder.isSupported(); // Returns: { value: boolean } ``` ### isRecording() [Section titled “isRecording()”](#isrecording) Checks if screen recording is currently active. ```typescript const { value } = await ScreenRecorder.isRecording(); // Returns: { value: boolean } ``` ## Advanced Usage [Section titled “Advanced Usage”](#advanced-usage) ### Recording with Options [Section titled “Recording with Options”](#recording-with-options) ```typescript // iOS-specific options const startWithOptions = async () => { await ScreenRecorder.start({ // iOS only options recordAudio: true, microphoneAudio: true, showIOSNotification: true, notificationText: "Recording in progress..." }); }; ``` ### Complete Recording Flow [Section titled “Complete Recording Flow”](#complete-recording-flow) ```typescript import { ScreenRecorder } from '@capgo/capacitor-screen-recorder'; import { Share } from '@capacitor/share'; export class RecordingService { private isRecording = false; async toggleRecording() { if (this.isRecording) { await this.stopRecording(); } else { await this.startRecording(); } } private async startRecording() { try { // Check if supported const { value: isSupported } = await ScreenRecorder.isSupported(); if (!isSupported) { throw new Error('Screen recording not supported'); } // Start recording await ScreenRecorder.start(); this.isRecording = true; console.log('Recording started'); } catch (error) { console.error('Failed to start recording:', error); throw error; } } private async stopRecording() { try { const result = await ScreenRecorder.stop(); this.isRecording = false; console.log('Recording saved:', result.videoUrl); // Option to share the recording await this.shareRecording(result.videoUrl); } catch (error) { console.error('Failed to stop recording:', error); throw error; } } private async shareRecording(videoUrl: string) { try { await Share.share({ title: 'Screen Recording', text: 'Check out my screen recording!', url: videoUrl, dialogTitle: 'Share Recording' }); } catch (error) { console.error('Failed to share recording:', error); } } } ``` ## Best Practices [Section titled “Best Practices”](#best-practices) 1. **Check support before use** ```typescript const { value } = await ScreenRecorder.isSupported(); if (!value) { // Hide recording feature or show alternative } ``` 2. **Handle permissions properly** On iOS, the system will automatically prompt for permission. On Android, ensure you request necessary permissions. 3. **Provide user feedback** Show clear indicators when recording is active: ```typescript let recordingInterval: any; const startRecording = async () => { await ScreenRecorder.start(); // Show recording indicator recordingInterval = setInterval(() => { // Update UI to show recording duration }, 1000); }; const stopRecording = async () => { clearInterval(recordingInterval); await ScreenRecorder.stop(); }; ``` 4. **Handle interruptions** ```typescript import { App } from '@capacitor/app'; App.addListener('appStateChange', async ({ isActive }) => { if (!isActive) { // Consider stopping recording when app goes to background const { value } = await ScreenRecorder.isRecording(); if (value) { await ScreenRecorder.stop(); } } }); ``` ## Platform Notes [Section titled “Platform Notes”](#platform-notes) ### iOS [Section titled “iOS”](#ios-1) * Requires iOS 11.0+ * Uses `ReplayKit` framework * System shows recording indicator in status bar * User must confirm recording start ### Android [Section titled “Android”](#android-1) * Requires Android 5.0 (API 21)+ * Uses `MediaProjection` API * Shows notification during recording * Some devices may have manufacturer-specific limitations ### Web [Section titled “Web”](#web) * Not supported on web platform * Will return `isSupported: false` ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) 1. **Recording fails to start** * Ensure all permissions are granted * Check if another app is already recording * Verify device supports screen recording 2. **No audio in recording** * Check microphone permissions * Ensure `recordAudio` option is enabled * Some devices may not support system audio recording 3. **Video file not found** * Check file permissions * Ensure sufficient storage space * Verify the returned video URL is valid # @capgo/capacitor-shake > Detect shake gestures in your app to trigger actions, show debug menus, or create interactive experiences. Simple gesture detection Easily detect when users shake their device 📱 Customizable sensitivity Adjust shake detection sensitivity to your needs ⚡ Cross-platform Works seamlessly on iOS and Android devices 🌍 Comprehensive Documentation Check the [Documentation](/docs/plugins/shake/getting-started/) to master the plugin in just a few minutes. # Getting Started > Learn how to install and configure the Capacitor Shake plugin to detect shake gestures and create interactive experiences in your app. 1. **Install the package** * npm ```sh npm i @capgo/capacitor-shake ``` * pnpm ```sh pnpm add @capgo/capacitor-shake ``` * yarn ```sh yarn add @capgo/capacitor-shake ``` * bun ```sh bun add @capgo/capacitor-shake ``` 2. **Sync with native projects** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` 3. **Configure the plugin** **Basic Usage Example:** ```typescript import { Shake } from '@capgo/capacitor-shake'; // Start listening for shake gestures await Shake.start(); // Listen for shake events Shake.addListener('shake', () => { console.log('Device was shaken!'); // Perform your action here }); ``` **Sensitivity Configuration:** ```typescript // Configure shake sensitivity await Shake.start({ threshold: 3.5 // Adjust sensitivity (default: 3.5) }); ``` * iOS No additional setup required for iOS. * Android No additional setup required for Android. 4. **Stop listening for shakes** ```typescript // Stop shake detection when not needed await Shake.stop(); // Remove specific listener const handle = await Shake.addListener('shake', () => { console.log('Shaken!'); }); handle.remove(); ``` 5. **Example implementation** ```typescript import { Shake } from '@capgo/capacitor-shake'; import { App } from '@capacitor/app'; export class ShakeService { private shakeListener: any; async initialize() { // Start shake detection await Shake.start({ threshold: 4.0 }); // Add shake listener this.shakeListener = await Shake.addListener('shake', () => { this.handleShake(); }); // Clean up on app pause App.addListener('pause', () => { Shake.stop(); }); // Resume on app resume App.addListener('resume', () => { Shake.start({ threshold: 4.0 }); }); } private handleShake() { console.log('Shake detected!'); // Show debug menu, refresh data, or trigger any action } async cleanup() { if (this.shakeListener) { this.shakeListener.remove(); } await Shake.stop(); } } ``` ## API Reference [Section titled “API Reference”](#api-reference) ### Methods [Section titled “Methods”](#methods) #### `start(options?: ShakeOptions)` [Section titled “start(options?: ShakeOptions)”](#startoptions-shakeoptions) Start listening for shake gestures. **Parameters:** * `options` (optional): Configuration object * `threshold`: number - Sensitivity threshold (default: 3.5) #### `stop()` [Section titled “stop()”](#stop) Stop listening for shake gestures. #### `addListener('shake', callback: () => void)` [Section titled “addListener('shake', callback: () => void)”](#addlistenershake-callback---void) Add a listener for shake events. **Returns:** Promise with a handle to remove the listener ### Interfaces [Section titled “Interfaces”](#interfaces) ```typescript interface ShakeOptions { threshold?: number; // Shake sensitivity threshold } ``` ## Platform Notes [Section titled “Platform Notes”](#platform-notes) ### iOS [Section titled “iOS”](#ios) * Uses the device’s accelerometer to detect shake gestures * Works in both foreground and background (if app has background permissions) ### Android [Section titled “Android”](#android) * Uses the SensorManager to detect shake events * Requires no special permissions * Works when app is in foreground ## Common Use Cases [Section titled “Common Use Cases”](#common-use-cases) 1. **Debug Menu**: Show developer options when device is shaken 2. **Feedback**: Trigger feedback form or bug report 3. **Refresh**: Refresh app data or clear cache 4. **Games**: Use shake as a game control mechanism 5. **Undo**: Implement shake-to-undo functionality ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) **Shake not detected:** * Ensure the plugin is started with `Shake.start()` * Try adjusting the threshold value (lower = more sensitive) * Check that listeners are properly registered **Too sensitive/not sensitive enough:** * Adjust the `threshold` parameter in `start()` options * Values typically range from 2.0 (very sensitive) to 5.0 (less sensitive) # @capgo/capacitor-share-target > Enable your app to receive text, images, and files shared from other apps with cross-platform support. Universal Sharing Receive content shared from any app 🔗 Multiple Content Types Handle text, images, videos, and files 📁 Cross-Platform iOS and Android support with native integration 📱 Event-Based Listen for share events in real-time 📡 Flexible Configuration Customize accepted MIME types 🎛️ Getting Started Check the [Getting Started Guide](/docs/plugins/share-target/getting-started/) to install and configure the plugin. # Getting Started with Share Target > Learn how to make your app receive shared content from other apps This guide will walk you through integrating the Capacitor Share Target plugin to enable your app to receive shared content from other applications. ## Installation [Section titled “Installation”](#installation) Install the plugin using npm: ```bash npm install @capgo/capacitor-share-target npx cap sync ``` ## Android Configuration [Section titled “Android Configuration”](#android-configuration) Add intent filters to your `AndroidManifest.xml` inside the `` tag: ### Accept Text Content [Section titled “Accept Text Content”](#accept-text-content) ```xml ``` ### Accept Images [Section titled “Accept Images”](#accept-images) ```xml ``` ### Accept All Content Types [Section titled “Accept All Content Types”](#accept-all-content-types) ```xml ``` ## iOS Configuration [Section titled “iOS Configuration”](#ios-configuration) For iOS, you need to create a Share Extension: ### 1. Create Share Extension in Xcode [Section titled “1. Create Share Extension in Xcode”](#1-create-share-extension-in-xcode) 1. Open your project in Xcode 2. Go to **File > New > Target** 3. Select **Share Extension** and click **Next** 4. Name it (e.g., “ShareExtension”) and click **Finish** 5. If prompted, activate the scheme ### 2. Configure App Group [Section titled “2. Configure App Group”](#2-configure-app-group) 1. In your main app target, go to **Signing & Capabilities** 2. Click **+ Capability** and add **App Groups** 3. Create or select an app group (e.g., `group.com.yourcompany.yourapp`) 4. Repeat for the Share Extension target ### 3. Update Share Extension Code [Section titled “3. Update Share Extension Code”](#3-update-share-extension-code) The Share Extension needs to save shared data to the app group container so your main app can access it. For detailed iOS setup, see [Apple’s Share Extension documentation](https://developer.apple.com/documentation/uikit/uiactivityviewcontroller). ## Basic Usage [Section titled “Basic Usage”](#basic-usage) ### Import the Plugin [Section titled “Import the Plugin”](#import-the-plugin) ```typescript import { CapacitorShareTarget } from '@capgo/capacitor-share-target'; ``` ### Listen for Shared Content [Section titled “Listen for Shared Content”](#listen-for-shared-content) ```typescript CapacitorShareTarget.addListener('shareReceived', (event) => { console.log('Received share event'); console.log('Title:', event.title); console.log('Texts:', event.texts); // Handle shared files if (event.files && event.files.length > 0) { event.files.forEach(file => { console.log(`File: ${file.name}`); console.log(`Type: ${file.mimeType}`); console.log(`URI: ${file.uri}`); }); } }); ``` ## Complete Example [Section titled “Complete Example”](#complete-example) Here’s a comprehensive example handling different types of shared content: ```typescript import { CapacitorShareTarget } from '@capgo/capacitor-share-target'; class ShareTargetService { private listener: any; initialize() { this.listener = CapacitorShareTarget.addListener('shareReceived', (event) => { this.handleSharedContent(event); }); console.log('Share target listener initialized'); } handleSharedContent(event: any) { console.log('=== Share Received ==='); // Handle title if (event.title) { console.log('Title:', event.title); this.showNotification('Shared: ' + event.title); } // Handle text content if (event.texts && event.texts.length > 0) { event.texts.forEach((text: string, index: number) => { console.log(`Text ${index + 1}:`, text); this.processSharedText(text); }); } // Handle files if (event.files && event.files.length > 0) { console.log(`Received ${event.files.length} file(s)`); event.files.forEach((file: any) => { console.log('File details:'); console.log(' Name:', file.name); console.log(' Type:', file.mimeType); console.log(' URI:', file.uri); this.processSharedFile(file); }); } } processSharedText(text: string) { // Check if it's a URL if (this.isURL(text)) { console.log('Shared URL detected:', text); // Handle URL (e.g., create bookmark) this.saveBookmark(text); } else { console.log('Shared text detected'); // Handle plain text (e.g., create note) this.createNote(text); } } processSharedFile(file: any) { const fileType = file.mimeType.split('/')[0]; switch (fileType) { case 'image': console.log('Processing shared image'); this.handleImage(file); break; case 'video': console.log('Processing shared video'); this.handleVideo(file); break; case 'audio': console.log('Processing shared audio'); this.handleAudio(file); break; case 'application': console.log('Processing shared document'); this.handleDocument(file); break; default: console.log('Processing generic file'); this.handleGenericFile(file); } } handleImage(file: any) { // Process image file console.log('Saving image:', file.name); // Implementation: Save to gallery, upload, etc. } handleVideo(file: any) { // Process video file console.log('Saving video:', file.name); } handleAudio(file: any) { // Process audio file console.log('Saving audio:', file.name); } handleDocument(file: any) { // Process document file console.log('Saving document:', file.name); } handleGenericFile(file: any) { // Process generic file console.log('Saving file:', file.name); } isURL(text: string): boolean { try { new URL(text); return true; } catch { return false; } } saveBookmark(url: string) { console.log('Creating bookmark for:', url); // Implementation } createNote(text: string) { console.log('Creating note with text:', text.substring(0, 50)); // Implementation } showNotification(message: string) { console.log('Notification:', message); // Show toast or notification } cleanup() { if (this.listener) { this.listener.remove(); } } } // Usage const shareTarget = new ShareTargetService(); shareTarget.initialize(); // Cleanup when app closes // shareTarget.cleanup(); ``` ## React Integration [Section titled “React Integration”](#react-integration) ```typescript import { useEffect } from 'react'; import { CapacitorShareTarget } from '@capgo/capacitor-share-target'; function useShareTarget(onShareReceived: (event: any) => void) { useEffect(() => { const listener = CapacitorShareTarget.addListener('shareReceived', onShareReceived); return () => { listener.remove(); }; }, [onShareReceived]); } // Usage in component function App() { useShareTarget((event) => { console.log('Share received:', event); // Handle shared content }); return
Your App
; } ``` ## Vue Integration [Section titled “Vue Integration”](#vue-integration) ```typescript import { onMounted, onUnmounted } from 'vue'; import { CapacitorShareTarget } from '@capgo/capacitor-share-target'; export default { setup() { let listener: any; onMounted(() => { listener = CapacitorShareTarget.addListener('shareReceived', (event) => { console.log('Share received:', event); // Handle shared content }); }); onUnmounted(() => { if (listener) { listener.remove(); } }); } }; ``` ## Handling Different Content Types [Section titled “Handling Different Content Types”](#handling-different-content-types) ### URLs [Section titled “URLs”](#urls) ```typescript if (event.texts && event.texts.length > 0) { const text = event.texts[0]; if (text.startsWith('http://') || text.startsWith('https://')) { // Handle URL window.open(text, '_blank'); } } ``` ### Images [Section titled “Images”](#images) ```typescript if (event.files) { const images = event.files.filter(f => f.mimeType.startsWith('image/')); images.forEach(async (image) => { // Read and display image const response = await fetch(image.uri); const blob = await response.blob(); const imageUrl = URL.createObjectURL(blob); // Display or process image console.log('Image URL:', imageUrl); }); } ``` ### Multiple Files [Section titled “Multiple Files”](#multiple-files) ```typescript if (event.files && event.files.length > 1) { console.log(`Processing ${event.files.length} files`); const processPromises = event.files.map(file => processFile(file) ); await Promise.all(processPromises); console.log('All files processed'); } ``` ## Best Practices [Section titled “Best Practices”](#best-practices) 1. **Handle Multiple Content Types**: Be prepared to receive text, URLs, and files 2. **Validate Content**: Check MIME types before processing 3. **Provide Feedback**: Show users what was received 4. **Handle Errors**: File URIs might be invalid or inaccessible 5. **Clean Up Listeners**: Remove listeners when not needed 6. **Test Thoroughly**: Test with different apps and content types 7. **Request Permissions**: Some file types may require additional permissions ## Common Issues [Section titled “Common Issues”](#common-issues) ### Share Not Working on Android [Section titled “Share Not Working on Android”](#share-not-working-on-android) Ensure intent filters are correctly configured in `AndroidManifest.xml` within the `` tag that has `android.intent.action.MAIN`. ### iOS Share Extension Not Appearing [Section titled “iOS Share Extension Not Appearing”](#ios-share-extension-not-appearing) 1. Verify App Group is configured in both targets 2. Ensure Share Extension is activated 3. Check that Info.plist in Share Extension has correct configuration ### File Access Errors [Section titled “File Access Errors”](#file-access-errors) ```typescript try { const response = await fetch(file.uri); const blob = await response.blob(); // Process blob } catch (error) { console.error('Failed to access file:', error); // Show error to user } ``` ## Next Steps [Section titled “Next Steps”](#next-steps) * Explore the [API Reference](https://github.com/Cap-go/capacitor-share-target#api) for complete method documentation * Check out the [example app](https://github.com/Cap-go/capacitor-share-target/tree/main/example) for advanced usage * See the [tutorial](/plugins/capacitor-share-target) for complete implementation examples # @capgo/capacitor-sim > Retrieve SIM carrier metadata and handle permission flows with a lightweight Capacitor bridge. Use Capgo SIM to query carrier data, slot indexes, and permission states while remaining inside Capacitor. Multi-SIM support Enumerate every installed SIM along with slot indices and subscription IDs. Permission helpers Check and request the telephony permission required on Android. Consistent schema Work with normalized carrier fields like MCC, MNC, ISO country, and phone numbers. iOS-aware Handle platform nuances such as limited carrier data on iOS 16.4+. Grab the getting started guide to integrate permissions and consumption patterns. # Getting Started > Retrieve SIM card details and handle runtime permissions with the Capgo SIM plugin. 1. **Install the plugin** * npm ```sh npm i @capgo/capacitor-sim ``` * pnpm ```sh pnpm add @capgo/capacitor-sim ``` * yarn ```sh yarn add @capgo/capacitor-sim ``` * bun ```sh bun add @capgo/capacitor-sim ``` 2. **Sync platforms** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Permissions [Section titled “Permissions”](#permissions) * **Android**: Ensure `android.permission.READ_PHONE_STATE` (and on Android 13+, `READ_BASIC_PHONE_STATE`) is granted. Capacitor includes the manifest declarations, but you must request the permission before calling `getSimCards`. * **iOS**: Carrier information is available automatically; the platform does not prompt for additional permissions. Be mindful of the limited data returned on iOS 16.4+ due to OS restrictions. ## Request runtime access (Android) [Section titled “Request runtime access (Android)”](#request-runtime-access-android) ```typescript import { Sim } from '@capgo/capacitor-sim'; const status = await Sim.checkPermissions(); if (status.readSimCard !== 'granted') { const updated = await Sim.requestPermissions(); if (updated.readSimCard !== 'granted') { throw new Error('Telephony permission denied'); } } ``` ## Read SIM cards [Section titled “Read SIM cards”](#read-sim-cards) ```typescript const { simCards } = await Sim.getSimCards(); simCards.forEach((card, index) => { console.log(`Slot ${card.simSlotIndex ?? index}:`, { carrier: card.carrierName, mcc: card.mobileCountryCode, mnc: card.mobileNetworkCode, country: card.isoCountryCode, subscriptionId: card.subscriptionId, }); }); ``` ## Platform considerations [Section titled “Platform considerations”](#platform-considerations) * **Dual SIM devices**: Iterate through the returned array; each entry corresponds to a slot. When present, `subscriptionId` helps you interact with Android’s telephony APIs. * **iOS 16.4+**: Apple redacts several carrier attributes. Expect placeholder values (`--`, `65535`) and plan fallbacks in your UI. * **Web**: The plugin resolves with empty data because browsers cannot access SIM information. # @capgo/capacitor-social-login > A Capacitor plugin for social authentication with Google, Apple, Facebook, Twitter, and any OAuth2 provider, making user login seamless and secure. ## Video Walkthrough [Section titled “Video Walkthrough”](#video-walkthrough) Watch a quick demo of the plugin setup and login flow in action. [Play](https://youtube.com/watch?v=iTINYxvSJrE) Multiple providers One API for Google, Apple, Facebook, Twitter/X, and generic OAuth2. OIDC / OAuth2 Multi-provider OAuth2 with OIDC discovery and provider aliases. Auth helpers Token helpers for refresh, expiry checks, JWT decoding, and redirect handling. Provider-specific behavior Supports Google offline mode, Apple proper token exchange, and Facebook business flows. Provider bundling Configure enabled providers in `capacitor.config` to reduce binary size. Migration guides See upgrade docs for Ionic Auth Connect and legacy provider migration. ## Provider guides [Section titled “Provider guides”](#provider-guides) [ OAuth2 and OIDC Providers](/docs/plugins/social-login/oauth2/) [Setup guides for GitHub, Azure AD, Auth0, Okta, Keycloak, and custom providers.](/docs/plugins/social-login/oauth2/) [ Auth Connect Migration](/docs/plugins/social-login/migrations/ionic-auth-connect/) [Keep familiar provider names like Auth0, Azure, Cognito, Okta, and OneLogin.](/docs/plugins/social-login/migrations/ionic-auth-connect/) ## Integrations [Section titled “Integrations”](#integrations) [ Integration Guides](/docs/plugins/social-login/integrations/) [Better Auth, Firebase, and Supabase integration entry points in one place.](/docs/plugins/social-login/integrations/) # Apple login on Android > This guide provides a comprehensive walkthrough on setting up Apple Login using Capacitor for iOS devices, detailing each step to ensure a smooth integration process. Apple login on android is hacky. Apple has no official support for `Sign in with Apple` on Android, so the solution is slightly hacky. Android currently uses a chrome tabs to display an OAuth2 website. This approach has the challenges: * Difficult configuration * A backend is required ## Understanding the flow on android. [Section titled “Understanding the flow on android.”](#understanding-the-flow-on-android) Let me use a diagram to explain the flow on android: ``` flowchart TD A("await SocialLogin.login()") -->|Handled in the plugin|B(Generate the login URL) B --> |Pass the link| C(Open the Chrome browser) C --> D(Wait for the user to login) D --> |Apple redirects to your backend|E(Handle the data returned from Apple) E --> F(Redirect back to the app) F --> G(Return to JS) ``` Now that you are aware of the challlanges and the flow, let’s begin the configuration. ## Creating the service ID [Section titled “Creating the service ID”](#creating-the-service-id) 1. Login into the [Apple Developer Portal](https://developer.apple.com). 2. Click on `Identifiers`. ![Apple Developer Portal Identifiers section](/social-login-assets/apple_dev_portal_iden.png) You should see a screen that looks like this: ![Apple Developer Portal Identifiers screen](/social-login-assets/apple_dev_portal_iden_2.png) 1. Ensure that this field says `App IDs` 2. Make sure that you can find your App ID. Note If you don’t have configured Apple Login for IOS, you will have to create one. For me, I already have one created. The app ID I will use is `me.wcaleniewolny.test.ionic.vue`. If you don’t have one, please create one using the [create app step](#creating-the-app). 3. Make sure that the `Sign in with Apple` capability is enabled for your app 1. Click on your app ![Selecting your app from the list](/social-login-assets/apple_dev_click_on_app.png) 2. Ensure that the `Sign in with Apple` capability is enabled ![Sign in with Apple capability enabled checkbox](/social-login-assets/apple_dev_sign_in_with_apple_enabled.png) 3. If it isn’t enabled, enable it. 4. Go back to all `All Identifiers` ![All Identifiers navigation button](/social-login-assets/apple_dev_go_back_iden.png) 5. Click on `App Ids` and go to `Services IDs` ![Navigation to Services IDs section](/social-login-assets/apple_dev_go_to_services_id.png) 6. Creare a new identifier 1. Click on the plus button ![Add new service ID button](/social-login-assets/apple_dev_iden_add.png) 2. Select `Servcice IDs` and click `Continue` ![Selecting Service IDs option](/social-login-assets/apple_dev_service_and_cont.png) 3. Enter a description and a identifiers and click `Continuie`. ![Entering service ID details](/social-login-assets/apple_dev_reg_service_2.png) Note This `identifiers` will become the `clientId` that you will pass in the `initialize` function AND `ANDROID_SERVICE_ID` for the backend. **Please save it!!!** Note Service ID doesn’t have to match the App ID, but I recommend setting the service ID to `YOUR_APP_ID.service` . As a reminder, I am using `me.wcaleniewolny.test.ionic.vue` for my app ID but I am using `ee.forgr.io.ionic.service2` as the service ID. 4. Please verify the details and click `Register` ![Confirming service ID registration](/social-login-assets/apple_dev_service_ref_fin.png) 5. Click on the the newly created service ![Selecting newly created service ID](/social-login-assets/apple_dev_open_serv.png) 6. Enable the `Sign in with Apple` option ![Enabling Sign in with Apple for service ID](/social-login-assets/apple_dev_serv_enable_sign_with_apple.png) 7. Configure the `Sign In with Apple` ![Configure button for Sign in with Apple](/social-login-assets/apple_dev_conf_serv_sign_with_apple.png) 8. Ensure that the `Primary App ID` is set to the App ID configured in the previous step ![Setting Primary App ID dropdown](/social-login-assets/apple_dev_service_prim_id.png) 9. Add the domain that you are going to host you backend on. ![Setting domain and return URL fields](/social-login-assets/apple_dev_serv_create_next.png) Note This backend **has** to be running on HTTPS. As for the `Return URLs`, you might want to come back to this after reading the next section of this tutorial and after configuring the backend. For the purposes of this tutorial, I will use `https://xyz.wcaleniewolny.me/login/callback` for the return URL and `xyz.wcaleniewolny.me` the domain. Press next. 10. Confirm the data and click `Done` ![Confirming domain and return URL configuration](/social-login-assets/apple_dev_serv_conf_done.png) 11. Click on `Continue` ![Continue button for service configuration](/social-login-assets/apple_dev_cont_serv_creat.png) 12. Click on `Save` ![Save button for service configuration](/social-login-assets/apple_dev_cont_serv_creat_save.png) ## Creating the key [Section titled “Creating the key”](#creating-the-key) 1. Go back to all `All Identifiers` ![All Identifiers navigation button](/social-login-assets/apple_dev_go_back_iden.png) 2. Click on `Keys` ![Keys section in Apple Developer Portal](/social-login-assets/apple_dev_key_selc.png) 3. Click on the plus icon ![Add new key button](/social-login-assets/apple_dev_key_plus.png) 4. Name your key ![Entering key name field](/social-login-assets/apple_key_name.png) Note This name isn’t important, you can put anything. 5. Select `Sign in with Apple` and click `Configure` ![Enabling and configuring Sign in with Apple for the key](/social-login-assets/apple_dev_key_sing_apple_conf.png) 6. Select the primary App ID, and press `Save` ![Selecting primary App ID for the key](/social-login-assets/apple_dev_key_prim_app_id.png) Note This must be the same App ID as the ID in the previous steps. 7. Click on `Continue` ![Continue button for key configuration](/social-login-assets/apple_dev_key_const.png) 8. Click on `Register` ![Register button for key creation](/social-login-assets/apple_dev_key_reg.png) 9. Copy the key ID and download the key. ![Key ID and download button screen](/social-login-assets/apple_dev_key_downl.png) Caution **IMPORTANT:** Save this ID, in the backend it will be called `KEY_ID`. Download the key. Make sure to never share this key. 10. Find the downloaded key and save it in the backend folder. ![Downloaded key file](/social-login-assets/apple_dev_downloaded_key.png) ## Getting the Team ID [Section titled “Getting the Team ID”](#getting-the-team-id) In order to use `Login with Apple` on Android, you need to get the `Team ID`. It will be used in the backend. 1. Go to [this website](https://developer.apple.com/account/) and scroll down 2. Find the `Team ID` ![Team ID location in developer account](/social-login-assets/apple_dev_team_id.png) ## Configuring the app redirect [Section titled “Configuring the app redirect”](#configuring-the-app-redirect) As you saw in the diagram, the backend performs a step called `Redirect back to the app`. This requires manual changes to your app. 1. Modify the `AndroidManifest.xml` 1. Open the file, I will use `AndroidStudio` ![AndroidManifest.xml file in Android Studio](/social-login-assets/studio_android_manifest_file.png) 2. Find the `MainActivity` and add the following Intent filter ![Intent filter code to add in MainActivity](/social-login-assets/studio_manifest_code_to_add.png) ```xml ``` 2. Modify the `MainActivity` 1. Please open the `MainActivity` ![MainActivity.java file in Android Studio](/social-login-assets/studio_main_activ_file.png) 2. Add the following code: ![Code to add to MainActivity for handling deep links](/social-login-assets/studio_main_actv_new_code.png) ```java @Override protected void onNewIntent(Intent intent) { String action = intent.getAction(); Uri data = intent.getData(); if (Intent.ACTION_VIEW.equals(action) && data != null) { PluginHandle pluginHandle = getBridge().getPlugin("SocialLogin"); if (pluginHandle == null) { Log.i("Apple Login Intent", "SocialLogin login handle is null"); return; } Plugin plugin = pluginHandle.getInstance(); if (!(plugin instanceof SocialLoginPlugin)) { Log.i("Apple Login Intent", "SocialLogin plugin instance is not SocialLoginPlugin"); return; } ((SocialLoginPlugin) plugin).handleAppleLoginIntent(intent); return; } super.onNewIntent(intent); } ``` Caution This example assumes that you don’t have any deep links configured. If you do, please adjust the code Note You have just added a new deep link into your app. The deep link will look something like this: `capgo-demo-app://path`. You can change the `android:scheme` and the `android:host` to modify how this deep link looks. **Important:** In the backend configuration, this deep link will become `BASE_REDIRECT_URL` ## Backend configuration [Section titled “Backend configuration”](#backend-configuration) A backend is required for Android, but configuring a backend will also impact IOS. An example backend is provided [here](https://github.com/WcaleNieWolny/capgo-social-login-backend-demo/blob/main/index.ts) This example provides the following: * A simple JSON database * A way to request the JWT from Apple’s servers * A simple JWT verification Note I use `PM2` in order to host this example. An example `ecosystem.config.js` can be found [here](https://github.com/WcaleNieWolny/capgo-social-login-backend-demo/blob/main/ecosystem.config.js.example) Given everything that I said in this tutorial, here is how the `env` section would look: * `ANDROID_SERVICE_ID` = Service ID * `IOS_SERVICE_ID` = App ID ```js env: { PRIVATE_KEY_FILE: "AuthKey_U93M8LBQK3.p8", KEY_ID: "U93M8LBQK3", TEAM_ID: "UVTJ336J2D", ANDROID_SERVICE_ID: "ee.forgr.io.ionic.starter.service2", IOS_SERVICE_ID: "me.wcaleniewolny.test.ionic.vue", PORT: 3000, REDIRECT_URI: "https://xyz.wcaleniewolny.me/login/callback", BASE_REDIRECT_URL: "capgo-demo-app://path" } ``` #### Using the plugin [Section titled “Using the plugin”](#using-the-plugin) The usage of the `login` function doesn’t change, it’s the same as IOS. Please take a look at that section for more info. **HOWEVER**, the `initialize` method changes a bit. ```typescript await SocialLogin.initialize({ apple: { clientId: 'ee.forgr.io.ionic.starter.service2', redirectUrl: 'https://appleloginvps.wcaleniewolny.me/login/callback' } }) ``` Danger Note, that adding `redirectUrl` **WILL** affect IOS !!!!! ## Creating the app [Section titled “Creating the app”](#creating-the-app) Note If you already have an App ID, you can skip this step. Don’t follow this step if you have configured Apple Login for IOS. 1. If you don’t already have an App ID, click on the plus button ![Add new identifier plus button](/social-login-assets/apple_dev_iden_plus.png) 2. Select `App IDs` and click continue ![Selecting App IDs type](/social-login-assets/apple_dev_new_app_id.png) 3. Click on type `App` and click `Continue` ![Selecting App type](/social-login-assets/apple_dev_new_app_type.png) 4. Enter the description and the app ID ![Entering app description and bundle ID](/social-login-assets/apple_dev_new_app_desc_id.png) 5. Enable `Sign with Apple` capability ![Enabling Sign in with Apple capability](/social-login-assets/apple_dev_enable_sign_with_apple.png) 6. Click `Continue` ![Continue button for app registration](/social-login-assets/apple_dev_register_continue.png) 7. Confirm the details and click `Register` ![Confirming app registration details](/social-login-assets/apple_dev_confirm_register.png) # Apple Login Setup > This guide provides detailed instructions on setting up Apple Login using Capacitor, covering all necessary steps and requirements for a successful integration. ### Introduction [Section titled “Introduction”](#introduction) In this guide, you are going to learn how to configure Apple Login with Capacitor. In order to do this, you will need the following: * an Apple Developer Account * A computer running macOS (IOS only) * Xcode installed (IOS only) * A custom backend (Android only) # Apple login on IOS > This comprehensive guide will walk you through the process of setting up Apple Login using Capacitor on iOS devices, ensuring a seamless integration by covering all necessary steps and configurations. Let’s break down what you are going to need in order to setup Apple login on IOS. 1. Configure the capabilities of your app. In order to do this, please open Xcode, click on `App` ![App XCode](/social-login-assets/app-xcode.png) 2. Make sure that you select the right target. ![XCode App Target](/social-login-assets/xcode-app-target.png) 3. Please make sure that you add the `Sign in with Apple` capability. ![App XCode Add Capability](/social-login-assets/app-xcode-add-cap.png) ![App XCode Add Capability](/social-login-assets/sign-with-apple-cap.png) Caution If you don’t see the `Sign in with Apple` capability, configure the [Account & Organizational Data Sharing](https://developer.apple.com/account/resources/services/cwa/configure/) 4. Initialize the Apple Login in your app. Note I am using Vue as my framework, the exact implementation will vary depending on the framework of your choice ```ts // onMounted is vue specific onMounted(() => { SocialLogin.initialize({ apple: {} }) }); ``` 5. Create a button that will begin the login process. Said button should call the following function: ```ts async function loginApple() { const res = await SocialLogin.login({ provider: 'apple', options: {} }) ``` 6. Run your app on a ***PHYSICAL*** device and test it. If you followed the steps closely you will see the following screen after clicking your button. ![Apple Sign In prompt on iOS device](/social-login-assets/apple-sign-in-ios-final.png) That’s it! You are all set. # Apple login for web browsers > This guide provides a detailed walkthrough on setting up Apple Login using Capacitor for web applications, utilizing the @capgo/capacitor-social-login plugin to ensure a seamless integration process. Configuring the web login is not trivial. It’s more difficult than setting up `Sign in with Apple` on iOS but more difficult than setting up `Sign in with Apple` on Android. ## Generating the service [Section titled “Generating the service”](#generating-the-service) Note This step is redundant if you have already configured `Sign in with Apple` on Android. Please follow the guide [here](/docs/plugins/social-login/apple/android/#creating-the-service-id/) to generate the service. ## Configuring the `Return URLs` [Section titled “Configuring the Return URLs”](#configuring-the-return-urls) 1. **Go to your Service ID configuration** In the [Apple Developer Portal](https://developer.apple.com), navigate to `Identifiers` > `Services IDs` and click on your service ID. 2. **Configure Sign in with Apple** Click on `Configure` next to `Sign in with Apple`. ![apple\_dev\_configure\_login](/social-login-assets/apple_dev_configure_login.webp) 3. **Add the Return URLs** Click on the `+` button to add a new return URL. ![apple\_dev\_return\_url\_plus](/social-login-assets/apple_dev_return_url_plus.webp) 4. **Add the Return URLs** Add your domain for your web application in `Domains and Subdomains`. Caution You **CANNOT** add `localhost` or `127.0.0.1` as a domain here. Then, add your domain with the `https://` prefix and the path from which you will call Apple Login. For example, if your domain is `https://my-app.com` and you will call Apple Login from `/login`, you should add `https://my-app.com/login` as the return URL. Caution You **MUST** add both the domain with a trailing slash and without a trailing slash. ![apple\_dev\_return\_url\_add](/social-login-assets/apple_dev_return_url_add.webp) 5. **Save the changes** 1. Click on the `Next` button to save the changes. 2. Click on the `Save` button to save the changes. 6. You should be ready to test the login for JavaScript. Please note that you cannot test from localhost. # Better Auth Integration > Use @capgo/capacitor-social-login with Better Auth for native Google, Apple, and Facebook sign-in, plus Generic OAuth providers. ## Overview [Section titled “Overview”](#overview) Better Auth works well with `@capgo/capacitor-social-login` when you want native sign-in on the device but still want Better Auth to create and manage the session on your backend. This page focuses on the two integration patterns that fit best: * Native token handoff for Google, Apple, and Facebook * Better Auth Generic OAuth for providers like Auth0, Okta, Keycloak, and custom OIDC servers ## Which pattern to use [Section titled “Which pattern to use”](#which-pattern-to-use) ### Use native token handoff [Section titled “Use native token handoff”](#use-native-token-handoff) Use `SocialLogin.login()` first, then send the returned token to Better Auth with `authClient.signIn.social()` when you use: * Google * Apple * Facebook ### Use Better Auth Generic OAuth [Section titled “Use Better Auth Generic OAuth”](#use-better-auth-generic-oauth) Let Better Auth own the full OAuth redirect flow when you use: * Auth0 * Okta * Keycloak * GitHub * OneLogin * Any custom OAuth2 or OIDC provider That keeps the session exchange on the Better Auth side and avoids duplicating redirect logic between two systems. ## Better Auth server setup [Section titled “Better Auth server setup”](#better-auth-server-setup) Start by configuring Better Auth with the social providers you want to support: ```typescript import { betterAuth } from 'better-auth'; export const auth = betterAuth({ baseURL: process.env.BETTER_AUTH_URL, socialProviders: { google: { clientId: process.env.GOOGLE_CLIENT_ID as string, clientSecret: process.env.GOOGLE_CLIENT_SECRET as string, }, apple: { clientId: process.env.APPLE_CLIENT_ID as string, clientSecret: process.env.APPLE_CLIENT_SECRET as string, appBundleIdentifier: process.env.APPLE_APP_BUNDLE_IDENTIFIER as string, }, facebook: { clientId: process.env.FACEBOOK_CLIENT_ID as string, clientSecret: process.env.FACEBOOK_CLIENT_SECRET as string, }, }, trustedOrigins: ['https://appleid.apple.com'], }); ``` Note For Apple on native iOS, Better Auth documents that you should provide `appBundleIdentifier` so the Apple ID token audience matches the bundle identifier used by iOS. ## Better Auth client setup [Section titled “Better Auth client setup”](#better-auth-client-setup) ```typescript import { createAuthClient } from 'better-auth/client'; export const authClient = createAuthClient({ baseURL: 'https://auth.example.com', }); ``` If you use React, use the Better Auth React client package your app already uses. The token handoff pattern stays the same. ## Google example [Section titled “Google example”](#google-example) This is the cleanest integration path for native mobile Google sign-in: ```typescript import { SocialLogin } from '@capgo/capacitor-social-login'; import { authClient } from '@/lib/auth-client'; const googleResult = await SocialLogin.login({ provider: 'google', options: { scopes: ['profile', 'email'], }, }); if (googleResult.result.responseType !== 'online' || !googleResult.result.idToken) { throw new Error('Google online mode with idToken is required for Better Auth.'); } await authClient.signIn.social({ provider: 'google', idToken: { token: googleResult.result.idToken, accessToken: googleResult.result.accessToken?.token, }, callbackURL: '/dashboard', }); ``` ## Apple example [Section titled “Apple example”](#apple-example) For Apple, pass the same nonce to both the native login request and Better Auth: ```typescript import { SocialLogin } from '@capgo/capacitor-social-login'; import { authClient } from '@/lib/auth-client'; const nonce = crypto.randomUUID(); const appleResult = await SocialLogin.login({ provider: 'apple', options: { scopes: ['email', 'name'], nonce, }, }); if (!appleResult.result.idToken) { throw new Error('Apple idToken is required for Better Auth.'); } await authClient.signIn.social({ provider: 'apple', idToken: { token: appleResult.result.idToken, nonce, accessToken: appleResult.result.accessToken?.token, }, callbackURL: '/dashboard', }); ``` ## Facebook example [Section titled “Facebook example”](#facebook-example) Better Auth documents two Facebook handoff modes: * iOS Limited Login: pass the `idToken` * Access-token flow: pass the access token as both `token` and `accessToken` This works with the response shape from `@capgo/capacitor-social-login`: ```typescript import { SocialLogin } from '@capgo/capacitor-social-login'; import { authClient } from '@/lib/auth-client'; const facebookResult = await SocialLogin.login({ provider: 'facebook', options: { permissions: ['email', 'public_profile'], }, }); const betterAuthToken = facebookResult.result.idToken ? { token: facebookResult.result.idToken, } : facebookResult.result.accessToken?.token ? { token: facebookResult.result.accessToken.token, accessToken: facebookResult.result.accessToken.token, } : null; if (!betterAuthToken) { throw new Error('Facebook idToken or access token is required for Better Auth.'); } await authClient.signIn.social({ provider: 'facebook', idToken: betterAuthToken, callbackURL: '/dashboard', }); ``` ## Generic OAuth providers with Better Auth [Section titled “Generic OAuth providers with Better Auth”](#generic-oauth-providers-with-better-auth) For Auth0, Okta, Keycloak, GitHub, Microsoft Entra ID, and similar providers, Better Auth’s Generic OAuth plugin is usually the better fit than passing tokens from `SocialLogin.login({ provider: 'oauth2' })`. ### Better Auth server [Section titled “Better Auth server”](#better-auth-server) ```typescript import { betterAuth } from 'better-auth'; import { genericOAuth } from 'better-auth/plugins'; export const auth = betterAuth({ plugins: [ genericOAuth({ config: [ { providerId: 'keycloak', discoveryUrl: 'https://sso.example.com/realms/mobile/.well-known/openid-configuration', clientId: process.env.KEYCLOAK_CLIENT_ID as string, clientSecret: process.env.KEYCLOAK_CLIENT_SECRET as string, }, ], }), ], }); ``` ### Better Auth client [Section titled “Better Auth client”](#better-auth-client) ```typescript import { createAuthClient } from 'better-auth/client'; import { genericOAuthClient } from 'better-auth/client/plugins'; export const authClient = createAuthClient({ baseURL: 'https://auth.example.com', plugins: [genericOAuthClient()], }); await authClient.signIn.oauth2({ providerId: 'keycloak', callbackURL: '/dashboard', }); ``` ## Provider examples for Better Auth Generic OAuth [Section titled “Provider examples for Better Auth Generic OAuth”](#provider-examples-for-better-auth-generic-oauth) Better Auth ships pre-configured helpers for several providers. These are the closest match to the extra provider examples you see in the social-login plugin docs. ### Auth0 [Section titled “Auth0”](#auth0) ```typescript import { betterAuth } from 'better-auth'; import { auth0, genericOAuth } from 'better-auth/plugins'; export const auth = betterAuth({ plugins: [ genericOAuth({ config: [ auth0({ providerId: 'auth0', domain: 'dev-example.eu.auth0.com', clientId: process.env.AUTH0_CLIENT_ID as string, clientSecret: process.env.AUTH0_CLIENT_SECRET as string, scopes: ['openid', 'profile', 'email', 'offline_access'], }), ], }), ], }); ``` ```typescript await authClient.signIn.oauth2({ providerId: 'auth0', callbackURL: '/dashboard', }); ``` ### Microsoft Entra ID [Section titled “Microsoft Entra ID”](#microsoft-entra-id) ```typescript import { betterAuth } from 'better-auth'; import { genericOAuth, microsoftEntraId } from 'better-auth/plugins'; export const auth = betterAuth({ plugins: [ genericOAuth({ config: [ microsoftEntraId({ providerId: 'entra', tenantId: 'common', clientId: process.env.AZURE_CLIENT_ID as string, clientSecret: process.env.AZURE_CLIENT_SECRET as string, scopes: ['openid', 'profile', 'email', 'User.Read'], }), ], }), ], }); ``` ```typescript await authClient.signIn.oauth2({ providerId: 'entra', callbackURL: '/dashboard', }); ``` ### Okta [Section titled “Okta”](#okta) ```typescript import { betterAuth } from 'better-auth'; import { genericOAuth, okta } from 'better-auth/plugins'; export const auth = betterAuth({ plugins: [ genericOAuth({ config: [ okta({ providerId: 'okta', issuer: 'https://dev-12345.okta.com/oauth2/default', clientId: process.env.OKTA_CLIENT_ID as string, clientSecret: process.env.OKTA_CLIENT_SECRET as string, scopes: ['openid', 'profile', 'email', 'offline_access'], }), ], }), ], }); ``` ```typescript await authClient.signIn.oauth2({ providerId: 'okta', callbackURL: '/dashboard', }); ``` ### Keycloak [Section titled “Keycloak”](#keycloak) ```typescript import { betterAuth } from 'better-auth'; import { genericOAuth, keycloak } from 'better-auth/plugins'; export const auth = betterAuth({ plugins: [ genericOAuth({ config: [ keycloak({ providerId: 'keycloak', issuer: 'https://sso.example.com/realms/mobile', clientId: process.env.KEYCLOAK_CLIENT_ID as string, clientSecret: process.env.KEYCLOAK_CLIENT_SECRET as string, scopes: ['openid', 'profile', 'email', 'offline_access'], }), ], }), ], }); ``` ```typescript await authClient.signIn.oauth2({ providerId: 'keycloak', callbackURL: '/dashboard', }); ``` ### GitHub with manual Generic OAuth config [Section titled “GitHub with manual Generic OAuth config”](#github-with-manual-generic-oauth-config) GitHub does not have a Better Auth helper on the Generic OAuth page, so use manual configuration: ```typescript import { betterAuth } from 'better-auth'; import { genericOAuth } from 'better-auth/plugins'; export const auth = betterAuth({ plugins: [ genericOAuth({ config: [ { providerId: 'github', clientId: process.env.GITHUB_CLIENT_ID as string, clientSecret: process.env.GITHUB_CLIENT_SECRET as string, authorizationUrl: 'https://github.com/login/oauth/authorize', tokenUrl: 'https://github.com/login/oauth/access_token', userInfoUrl: 'https://api.github.com/user', scopes: ['read:user', 'user:email'], pkce: true, }, ], }), ], }); ``` ```typescript await authClient.signIn.oauth2({ providerId: 'github', callbackURL: '/dashboard', }); ``` ## Notes and caveats [Section titled “Notes and caveats”](#notes-and-caveats) 1. **Use Google online mode** Better Auth needs the `idToken`, so `google.mode: 'offline'` is not the right fit for this handoff flow. 2. **Reuse the Apple nonce** Generate it once, send it to Apple native login, then send the same value to Better Auth. 3. **Handle Facebook differently by platform** Limited Login on iOS gives you an ID token. Other flows may only give an access token. 4. **Do not mix Generic OAuth flows unless you have a reason** If Better Auth owns the OAuth provider configuration, let Better Auth own the redirect flow too. ## Further reading [Section titled “Further reading”](#further-reading) * [Better Auth Google provider docs](https://better-auth.com/docs/authentication/google) * [Better Auth Apple provider docs](https://better-auth.com/docs/authentication/apple) * [Better Auth Facebook provider docs](https://better-auth.com/docs/authentication/facebook) * [Better Auth Generic OAuth plugin docs](https://better-auth.com/docs/plugins/generic-oauth) * [Social Login OAuth2 and OIDC providers](/docs/plugins/social-login/oauth2/) # Facebook Login Setup > This guide provides a comprehensive walkthrough on setting up Facebook Login using Capacitor, ensuring seamless integration and enhanced user authentication for your application. ## Introduction [Section titled “Introduction”](#introduction) In this guide, you will learn how to setup Facebook Login with Capgo Social Login. You will need the following: * A Facebook Developer Account * Your app’s package name/bundle ID * Access to a terminal for generating key hashes (Android) ## General Setup [Section titled “General Setup”](#general-setup) If you don’t already have a Facebook app created, follow these steps: 1. Create a Facebook App Follow the tutorial to [Create an App](https://developers.facebook.com/docs/development/create-an-app/) 2. Add Facebook Login to your app In your Facebook Developer Dashboard, add the Facebook Login product to your app 3. Before you can release your app to the public, follow this [tutorial](https://developers.facebook.com/docs/development/release/) to publish it ## Important Information [Section titled “Important Information”](#important-information) Here’s where to find the key information you’ll need for integration: 1. `CLIENT_TOKEN`: ![Facebook developer dashboard showing where to find the client token](/social-login-assets/fb_where_to_fiind_client_token.png) 2. `APP_ID`: ![Facebook developer dashboard showing where to find the app ID](/social-login-assets/fb_where_to_find_app_id.png) 3. `APP_NAME`: ![Facebook developer dashboard showing where to find the app name](/social-login-assets/fb_where_to_find_app_name.png) ## Android Setup [Section titled “Android Setup”](#android-setup) 1. Add internet permission to your `AndroidManifest.xml` Ensure this line is present: ```xml ``` 2. Generate your Android key hash This is a crucial security step required by Facebook. Open your terminal and run: ```bash keytool -exportcert -alias androiddebugkey -keystore ~/.android/debug.keystore | openssl sha1 -binary | openssl base64 -A ``` When prompted for a password, use: `android` Note For release builds, you’ll need to use your release keystore: ```bash keytool -exportcert -alias your-key-name -keystore your-keystore-path | openssl sha1 -binary | openssl base64 -A ``` 3. Add the key hash to your Facebook app 1. Go to your app’s dashboard on Facebook Developers 2. Navigate to Settings > Basic 3. Scroll down to “Android” section 4. Click “Add Platform” if Android isn’t added yet and fill in the details 5. Add the key hash you generated 6. For production, add both debug and release key hashes 4. Update your `AndroidManifest.xml` to include: ```xml ... ``` Caution Make sure to replace `[APP_ID]` with your actual Facebook app ID in the `android:scheme` attribute ## iOS Setup [Section titled “iOS Setup”](#ios-setup) 1. Add the iOS platform in Facebook Developer Console 1. Go to your app’s dashboard on Facebook Developers 2. Navigate to Settings > Basic 3. Scroll down to very bottom of the page and click “Add Platform” 4. Select iOS and fill in the required details 2. Open your Xcode project and navigate to Info.plist 3. Add the following entries to your Info.plist: ```xml FacebookAppID [APP-ID] FacebookClientToken [CLIENT-TOKEN] FacebookDisplayName [APP-NAME] LSApplicationQueriesSchemes fbapi fb-messenger-share-api CFBundleURLTypes CFBundleURLSchemes fb[APP-ID] ``` Caution Replace the following values: * `[APP-ID]` with your Facebook app ID * `[CLIENT-TOKEN]` with your client token * `[APP-NAME]` with your app’s name 4. Modify the `AppDelegate.swift` ```swift import FBSDKCoreKit @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. // Initialize Facebook SDK FBSDKCoreKit.ApplicationDelegate.shared.application( application, didFinishLaunchingWithOptions: launchOptions ) return true } func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool { // Called when the app was launched with a url. Feel free to add additional processing here, // but if you want the App API to support tracking app url opens, make sure to keep this call if (FBSDKCoreKit.ApplicationDelegate.shared.application( app, open: url, sourceApplication: options[UIApplication.OpenURLOptionsKey.sourceApplication] as? String, annotation: options[UIApplication.OpenURLOptionsKey.annotation] )) { return true; } else { return ApplicationDelegateProxy.shared.application(app, open: url, options: options) } } } ``` ## Using Facebook Login in Your App [Section titled “Using Facebook Login in Your App”](#using-facebook-login-in-your-app) Caution **Before You Start**: Remember that with the new Facebook SDK, the token type you receive depends entirely on the user’s App Tracking choice, not on your code configuration. Always implement both access token and JWT token handling in your backend to ensure authentication works for all users. 1. Initialize the Facebook login in your app ```typescript import { SocialLogin } from '@capgo/capacitor-social-login'; // Initialize during app startup await SocialLogin.initialize({ facebook: { appId: 'APP_ID', clientToken: 'CLIENT_TOKEN', } }) ``` 2. Implement the login function ```typescript async function loginWithFacebook() { try { const result = await SocialLogin.login({ provider: 'facebook', options: { permissions: ['email', 'public_profile'], limitedLogin: false // See Limited Login section below for important details } }); console.log('Facebook login result:', result); // Handle successful login } catch (error) { console.error('Facebook login error:', error); // Handle error } } ``` Note **Limited Login (iOS Only)**: Set `limitedLogin` to true if you want to use Facebook’s Limited Login feature. This is an iOS-only feature that provides enhanced privacy by restricting the data shared during login. **Important Limitations:** * **iOS Only**: Limited Login only affects iOS devices and has no impact on Android * **ATT Override**: Even if you set `limitedLogin: false`, Facebook will automatically force it to `true` if the user hasn’t granted App Tracking Transparency (ATT) permission * **Always Handle Both Cases**: Your app should always be prepared to handle both limited and full login scenarios **Checking ATT Status:** ```typescript // Check if user has granted tracking permission const trackingStatus = await SocialLogin.providerSpecificCall({ call: 'facebook#requestTracking', options: {} }); console.log('Tracking status:', trackingStatus.status); // 'authorized', 'denied', 'notDetermined', or 'restricted' ``` **Recommended Implementation if access\_token is preferred:** ```typescript async function loginWithFacebook() { try { // Check ATT status first const trackingStatus = await SocialLogin.providerSpecificCall({ call: 'facebook#requestTracking', options: {} }); const result = await SocialLogin.login({ provider: 'facebook', options: { permissions: ['email', 'public_profile'], limitedLogin: trackingStatus.status === 'denied' // Auto-adjust based on ATT } }); // Handle different response types based on limited login if (result.result.accessToken) { // Your app logic should work with both limited and full login console.log('Login successful:', result); } } catch (error) { console.error('Facebook login error:', error); } } ``` **What Happens in Limited Login:** * **Reduced Data Access**: Some user data may not be available * **Different Token Types**: Access tokens may have different capabilities * **Privacy Compliance**: Helps comply with iOS privacy requirements **Important**: Always test your app with both limited and full login scenarios to ensure your app works correctly in both cases. You can learn more about Limited Login [here](https://developers.facebook.com/docs/facebook-login/limited-login/). 3. **Get User Profile Data** After successful login, you can retrieve additional profile information: ```typescript async function getFacebookProfile() { try { const profileResponse = await SocialLogin.providerSpecificCall({ call: 'facebook#getProfile', options: { fields: ['id', 'name', 'email', 'first_name', 'last_name', 'picture'] } }); console.log('Facebook profile:', profileResponse.profile); return profileResponse.profile; } catch (error) { console.error('Failed to get Facebook profile:', error); return null; } } // Example usage after login async function loginAndGetProfile() { const loginResult = await loginWithFacebook(); if (loginResult) { const profile = await getFacebookProfile(); if (profile) { console.log('User ID:', profile.id); console.log('Name:', profile.name); console.log('Email:', profile.email); console.log('Profile Picture:', profile.picture?.data?.url); } } } ``` Tip **Available Profile Fields**: You can request any fields available in Facebook’s Graph API. Common fields include: `id`, `name`, `email`, `first_name`, `last_name`, `picture`, `birthday`, `gender`, `location`, `hometown`. Note that some fields may require additional permissions. **Token Type Limitation**: The `getProfile` call only works when you have an **access token** (standard login with tracking allowed). If the user denied tracking or you’re using limited login (JWT token only), this call will fail. In that case, use the profile data provided in the initial login response. ## ⚠️ Critical: Backend Token Handling [Section titled “⚠️ Critical: Backend Token Handling”](#️-critical-backend-token-handling) Danger **CRITICAL iOS BEHAVIOR**: Limited Login and App Tracking Transparency (ATT) are **iOS-ONLY** features. On Android, you will always receive access tokens regardless of the `limitedLogin` setting. **iOS Token Behavior**: * **`limitedLogin: true`** → Always JWT token (iOS only) * **`limitedLogin: false` + User ALLOWS tracking** → Access token * **`limitedLogin: false` + User DENIES tracking** → JWT token (iOS automatically overrides your setting) **Android Token Behavior**: Always access token, `limitedLogin` setting is ignored. Your backend must handle **two different token types** because iOS users can receive either access tokens or JWT tokens depending on their App Tracking Transparency choice, while Android users always receive access tokens. ### Token Types by Platform [Section titled “Token Types by Platform”](#token-types-by-platform) | Platform | limitedLogin Setting | User ATT Choice | Result Token Type | | ----------- | -------------------- | --------------- | ------------------------- | | **iOS** | `true` | Any | JWT Token | | **iOS** | `false` | Allows tracking | Access Token | | **iOS** | `false` | Denies tracking | JWT Token (auto-override) | | **Android** | Any | N/A | Access Token (always) | ### Backend Implementation [Section titled “Backend Implementation”](#backend-implementation) 1. **Detect Token Type and Handle Accordingly** ```typescript async function loginWithFacebook() { try { const loginResult = await SocialLogin.login({ provider: 'facebook', options: { permissions: ['email', 'public_profile'], limitedLogin: false // iOS: depends on ATT, Android: ignored } }); if (loginResult.accessToken) { // Access token (Android always, iOS when tracking allowed) return handleAccessToken(loginResult.accessToken.token); } else if (loginResult.idToken) { // JWT token (iOS only when tracking denied or limitedLogin: true) return handleJWTToken(loginResult.idToken); } } catch (error) { console.error('Facebook login error:', error); } } ``` 2. **Firebase Integration Example** ```typescript import { OAuthProvider, FacebookAuthProvider, signInWithCredential } from 'firebase/auth'; async function handleAccessToken(accessToken: string, nonce: string) { // For access tokens, use OAuthProvider (new method) const fbOAuth = new OAuthProvider("facebook.com"); const credential = fbOAuth.credential({ idToken: accessToken, rawNonce: nonce }); try { const userResponse = await signInWithCredential(auth, credential); return userResponse; } catch (error) { console.error('Firebase OAuth error:', error); return false; } } async function handleJWTToken(jwtToken: string) { // For JWT tokens, send to your backend for validation try { const response = await fetch('/api/auth/facebook-jwt', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ jwtToken }) }); const result = await response.json(); return result; } catch (error) { console.error('JWT validation error:', error); return false; } } ``` 3. **Backend JWT Validation** ```typescript // Backend: Validate JWT token from Facebook import jwt from 'jsonwebtoken'; import { Request, Response } from 'express'; app.post('/api/auth/facebook-jwt', async (req: Request, res: Response) => { const { jwtToken } = req.body; try { // Verify JWT token with Facebook's public key // See: https://developers.facebook.com/docs/facebook-login/limited-login/token/validating/#standard-claims const decoded = jwt.verify(jwtToken, getFacebookPublicKey(), { algorithms: ['RS256'], audience: process.env.FACEBOOK_APP_ID, issuer: 'https://www.facebook.com' // From: https://www.facebook.com/.well-known/openid-configuration/?_rdr }); // Extract user info from JWT const userInfo = { id: decoded.sub, email: decoded.email, name: decoded.name, isJWTAuth: true }; // Create your app's session/token const sessionToken = createUserSession(userInfo); res.json({ success: true, token: sessionToken, user: userInfo }); } catch (error) { console.error('JWT validation failed:', error); res.status(401).json({ success: false, error: 'Invalid token' }); } }); ``` 4. **Generic Backend Token Handler** ```typescript // Handle both token types in your backend async function authenticateFacebookUser(tokenData: any) { if (tokenData.accessToken) { // Handle access token - validate with Facebook Graph API const response = await fetch(`https://graph.facebook.com/me?access_token=${tokenData.accessToken}&fields=id,name,email`); const userInfo = await response.json(); return { user: userInfo, tokenType: 'access_token', expiresIn: tokenData.expiresIn || 3600 }; } else if (tokenData.jwtToken) { // Handle JWT token - decode and validate // See: https://developers.facebook.com/docs/facebook-login/limited-login/token/validating/#standard-claims const decoded = jwt.verify(tokenData.jwtToken, getFacebookPublicKey()); return { user: { id: decoded.sub, name: decoded.name, email: decoded.email }, tokenType: 'jwt', expiresIn: decoded.exp - Math.floor(Date.now() / 1000) }; } else { throw new Error('No valid token provided'); } } ``` ### Key Considerations [Section titled “Key Considerations”](#key-considerations) Danger **Critical iOS-Only Understanding**: * **iOS**: User’s App Tracking choice determines token type, NOT your code settings (even when `limitedLogin: false`) * **Android**: Always receives access tokens, regardless of `limitedLogin` setting * **Limited Login is iOS-ONLY** - Android ignores this setting completely **Access Token (Standard Login)**: * ✅ **Android**: Always available (iOS-only restrictions don’t apply) * ✅ **iOS**: Only when user explicitly allows app tracking * ✅ Can be used to access Facebook Graph API * ✅ Longer expiration times * ✅ More user data available * ❌ **Becoming less common on iOS** as users increasingly deny tracking **JWT Token (iOS-Only Privacy Mode)**: * ❌ **Android**: Never occurs (not supported) * ✅ **iOS**: When tracking denied or `limitedLogin: true` * ✅ Respects iOS user privacy preferences * ❌ Contains basic user info only * ❌ Shorter expiration times * ❌ No access to Facebook Graph API * ⚠️ **Now the most common scenario for iOS users** **Platform-Specific Behavior**: * **iOS apps**: Must handle both access tokens AND JWT tokens * **Android apps**: Only need to handle access tokens * **Cross-platform apps**: Must implement both token handling methods Tip **Essential for iOS**: You MUST implement both token handling methods. Many iOS developers assume they’ll always get access tokens and their apps break when users deny tracking. ## Secure Context Requirements (Web/Capacitor) [Section titled “Secure Context Requirements (Web/Capacitor)”](#secure-context-requirements-webcapacitor) ### Crypto API Limitations [Section titled “Crypto API Limitations”](#crypto-api-limitations) The updated Facebook login flow requires the **Web Crypto API** for nonce generation, which is only available in **secure contexts**: ```typescript // This requires secure context (HTTPS or localhost) async function sha256(message: string) { const msgBuffer = new TextEncoder().encode(message); const hashBuffer = await crypto.subtle.digest("SHA-256", msgBuffer); // ❌ Fails in insecure context // ... } ``` ### Development Environment Issues [Section titled “Development Environment Issues”](#development-environment-issues) **Common Problem**: `ionic serve` with HTTP URLs breaks Facebook authentication | Environment | Crypto API Available | Facebook Login Works | | --------------------------- | -------------------- | -------------------- | | `http://localhost:3000` | ✅ Yes | ✅ Yes | | `http://127.0.0.1:3000` | ✅ Yes | ✅ Yes | | `http://192.168.1.100:3000` | ❌ No | ❌ No | | `https://any-domain.com` | ✅ Yes | ✅ Yes | ### Solutions for Capacitor Development [Section titled “Solutions for Capacitor Development”](#solutions-for-capacitor-development) 1. **Use localhost for web testing** ```bash # Instead of ionic serve --host=0.0.0.0 ionic serve --host=localhost ``` 2. **Enable HTTPS in Ionic** ```bash ionic serve --ssl ``` 3. **Test on actual devices** ```bash # Capacitor apps run in secure context on devices ionic cap run ios ionic cap run android ``` 4. **Alternative nonce generation for development** ```typescript async function generateNonce() { if (typeof crypto !== 'undefined' && crypto.subtle) { // Secure context - use crypto.subtle return await sha256(Math.random().toString(36).substring(2, 10)); } else { // Fallback for development (not secure for production) console.warn('Using fallback nonce - not secure for production'); return btoa(Math.random().toString(36).substring(2, 10)); } } ``` ### Firebase Integration Note [Section titled “Firebase Integration Note”](#firebase-integration-note) Recent Firebase documentation requires JWT tokens with nonces for Facebook authentication, regardless of login settings. This approach works with both `limitedLogin: true` and `limitedLogin: false`: ```typescript // Both modes can return JWT tokens depending on user choice const loginResult = await SocialLogin.login({ provider: 'facebook', options: { permissions: ['email', 'public_profile'], limitedLogin: false, // true = always JWT, false = depends on user tracking choice nonce: nonce } }); ``` **Development Limitation**: If you’re using `ionic serve` on a network IP (not localhost), Facebook login will fail due to crypto API restrictions. Use localhost or HTTPS for web testing. Tip **Production Safety**: Capacitor apps on iOS/Android always run in secure contexts, so this limitation only affects web development environments. ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) ### Common Issues and Solutions [Section titled “Common Issues and Solutions”](#common-issues-and-solutions) 1. **Key hash errors on Android** * Double check that you’ve added the correct key hash to the Facebook dashboard * For release builds, make sure you’ve added both debug and release key hashes * Verify you’re using the correct keystore when generating the hash 2. **Facebook login button doesn’t appear** * Verify all manifest entries are correct * Check that your Facebook App ID and Client Token are correct * Ensure you’ve properly initialized the SDK 3. **Common iOS issues** * Make sure all Info.plist entries are correct * Verify URL schemes are properly configured * Check that your bundle ID matches what’s registered in the Facebook dashboard ### Testing [Section titled “Testing”](#testing) 1. **Before testing, add test users in the Facebook Developer Console** * Go to Roles > Test Users * Create a test user * Use these credentials for testing 2. **Test both debug and release builds** * Debug build with debug key hash * Release build with release key hash * Test on both emulator and physical devices Remember to test the full login flow, including: * Successful login * Login cancellation * Error handling * Logout functionality # Firebase Google Login on Android > Learn how to set up Google Sign-In with Firebase Authentication on Android using the Capacitor Social Login plugin. ## Introduction [Section titled “Introduction”](#introduction) This guide will help you integrate Google Sign-In with Firebase Authentication on Android. I assume you have already completed the [general Firebase Google setup](./general/) Note I will assume that you have not yet created your Android app in the Firebase Console. If you have, your steps will be slightly different. ## Setup Steps [Section titled “Setup Steps”](#setup-steps) 1. Go to your project overview over at [console.cloud.google.com](https://console.cloud.google.com/) ![Firebase Project Overview](/social-login-assets/firebase_project_overview.webp) 2. Click on the `Add app` button ![Firebase Add App Button](/social-login-assets/firebase_project_add_app.webp) Note It is possible that you will have to look here for this button. This applies only if you have already created a different app in the Firebase Console. ![Firebase Add App Button](/social-login-assets/firebase_project_add_app_2.webp) 3. Select `Android` ![Firebase Add App Android Button](/social-login-assets/firebase_project_add_app_android.webp) 4. Fill the first part of the form 1. Fill the `Android package name` 1. Open Android Studio at your app using `npx cap open android` 2. At the very bottom of the navigator, find the `Gradle Scripts` ![Gradle Scripts section in Android Studio project navigator](/social-login-assets/studio_gradle_scripts.png) 3. Find `build.gradle` for the module `app` ![build.gradle (Module: app) file in Gradle Scripts section](/social-login-assets/studio_build_gradle.png) 4. Copy the `android.defaultConfig.applicationId`. This will be your `package name` in the Firebase console ![Build.gradle file showing applicationId configuration](/social-login-assets/studio_build_gradle_app_id.png) Note The ID shown here will differ from the one I will use for the rest of the guide. I will use `app.capgo.plugin.SocialLogin` for the rest of the guide. 5. Paste it in the Firebase console ![Firebase Add App Android Package Name Field](/social-login-assets/firebase_project_add_app_android_package_name.webp) 2. Click on the `Register app` button ![Firebase Add App Android Register Button](/social-login-assets/firebase_project_add_app_android_register.webp) 5. Skip the `Download and then add config file` step ![Firebase Add App Android Skip 'Download and then add config file'](/social-login-assets/firebase_project_add_app_android_skip_download.webp) 6. Skip the `Add firebase SDK` step ![Firebase Add App Android Skip 'Add firebase SDK'](/social-login-assets/firebase_project_add_app_android_skip_download_firebase_sdk.webp) 7. Click on the `Continue to console` button ![Firebase Add App Android Continue to Console Button](/social-login-assets/firebase_project_add_app_android_continue_to_console.webp) 8. If you do not get automatically authenticated, go to `settings` -> `general` -> `your apps` -> find your android app and click on it ![Firebase Add App Android Settings General Your Apps Button](/social-login-assets/firebase_project_add_app_android_settings_general_your_apps.webp) 9. Get your SHA1 fingerprint Follow steps 10-11 from the [Google Login Android setup guide](/docs/plugins/social-login/google/android/#using-google-login-on-android): 1. Now, open the terminal. Make sure that you are in the `android` folder of your app and run `./gradlew signInReport` ![Terminal showing gradlew signInReport command](/social-login-assets/term_sign_report.png) 2. Scroll to the top of this command. You should see the following. Copy the `SHA1`. ![Terminal output showing SHA1 certificate fingerprint](/social-login-assets/term_sign_report_res.png) Note I will use a slightly different SHA1 for the rest of the guide, because I have changed computes since writing the original Google login android setup guide. Caution The SHA1 is very important to get right. If you mess up, the authentication will fail in strange ways. Please ****[READ THE GOOGLE LOGIN ANDROID SETUP GUIDE](/docs/plugins/social-login/google/android/#using-google-login-on-android)**** to get it right. 10. Add the SHA1 to the Firebase project 1. Click on the `Add fingerprint` button ![Firebase Add App Android Add Fingerprint Button](/social-login-assets/firebase_project_add_app_android_add_fingerprint.webp) 2. Paste the SHA1 you copied in the previous step ![Firebase Add App Android Add Fingerprint SHA1 Field](/social-login-assets/firebase_project_add_app_android_add_fingerprint_sha1.webp) 3. Click on the `Save` button ![Firebase Add App Android Add Fingerprint Save Button](/social-login-assets/firebase_project_add_app_android_add_fingerprint_add.webp) 11. Get your web client ID 1. Go to `Build` -> `Authentication` ![Firebase Authentication Menu](/social-login-assets/firebase_select_authentication.webp) 2. Click on the `Sign-in method` button ![Firebase Authentication Sign-in Method Button](/social-login-assets/firebase_select_authentication_sign_in_method.webp) 3. Click on the `Google` provider ![Firebase Authentication Sign-in Method Google Provider](/social-login-assets/firebase_select_authentication_sign_in_method_google.webp) 4. Click on the `Web SDK configuration` button ![Firebase Authentication Sign-in Method Web SDK Configuration Button](/social-login-assets/firebase_select_authentication_sign_in_method_web_sdk_configuration.webp) 5. Copy the `Web client ID`. This will be your `webClientId` in the `initialize` method of the plugin. ![Firebase Authentication Sign-in Method Web SDK Configuration Web Client ID](/social-login-assets/firebase_select_authentication_sign_in_method_web_sdk_configuration_web_client_id.webp) 12. Use the web client ID in JS. Note I recommend using the `authenticateWithGoogle` helper function available in the [authUtils.ts](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/authUtils.ts) file of the example app. At this point, you are ****TECHNICALLY**** ready to use Google Sign-In with Firebase Authentication on Android. However, I would recommend double-checking the setup in the Google Cloud console as explained in the next step. ## Double-check the setup in Google Cloud console [Section titled “Double-check the setup in Google Cloud console”](#double-check-the-setup-in-google-cloud-console) In order to make sure that the setup is correct, you should double-check the setup in the Google Cloud console. 1. Go to [console.cloud.google.com](https://console.cloud.google.com/) 2. Find your project 1. Click on the project selector ![Google Cloud Console Project Selector](/social-login-assets/firebase_double_check_gc_project_select.webp) 2. Search up your project by the exact name of your Firebase project and click on it. In my case, it is `sociallogin-tutorial-app`. ![Firebase Project Selector Project](/social-login-assets/firebase_gc_project_select_project.webp) 3. Open the search bar and open `credentials` 1. Open the search bar ![Google Cloud Console Search Bar](/social-login-assets/firebase_double_check_gc_search_bar.webp) 2. Search for `credentials` and click on the `APIs and Services` one (number 2 on the screenshot) ![Google Cloud Console Credentials Search](/social-login-assets/firebase_gc_credentials_search.webp) 4. Verify that you see both the Android and Web client IDs in the list. ![Google Cloud Console Credentials List](/social-login-assets/firebase_gc_credentials_list.webp) Caution If you do not see both the Android and Web client IDs in the list, you have made a mistake in the setup. Please go back and check your steps. It is also possible, and it has happened to me, that you already have added the Android SHA1 hash with the same app ID in a different project. This will result in Firbase being unable to create an Android client ID. In this case, you will need to remove the SHA1 from the other project as well as on Firebase (using the Firebase console to remove the android app) and recreate it on Firbase 5. Verify that the Android client ID is correctly configured in the Firebase console. 1. Click on the `Android` app ![Google Cloud Console Android App Select](/social-login-assets/firebase_gc_android_app_select.webp) 2. Confirm that the SHA1 hash is correctly configured and that it matches the one you copied in the previous steps. ![Google Cloud Console Android App SHA1 Configured](/social-login-assets/firebase_gc_android_app_sha1_configured.webp) 6. Verify that the Web client ID is correctly configured in the Firebase console. 1. Click on the `Web` app ![Google Cloud Console Web App Select](/social-login-assets/firebase_gc_web_app_select.webp) 2. Confirm that the client ID matches the one you copied in the previous steps. ![Google Cloud Console Web App Client ID Configured](/social-login-assets/firebase_gc_web_app_client_id_configured.webp) Note Please ignore the rest of the settings of the web client. We will discuss this on the [web setup guide](/docs/plugins/social-login/firebase/google/web/). Voila! You are now ready to use Google Sign-In with Firebase Authentication on Android. ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) If authentication hangs or fails: * Verify the `idToken` audience matches your Firebase web client ID * Check that Google Sign-In is enabled in Firebase Console * Ensure the SHA-1 fingerprint is correctly configured * Review the [example app code](https://github.com/Cap-go/capacitor-social-login/tree/main/example-app/src/authUtils.ts) for reference # Firebase Google Login - General Setup > Learn how to set up Google Sign-In with Firebase Authentication using the Capacitor Social Login plugin. ## Introduction [Section titled “Introduction”](#introduction) This guide will walk you through integrating Google Sign-In with Firebase Authentication using the Capacitor Social Login plugin. This setup allows you to use native Google Sign-In on mobile platforms while leveraging Firebase Auth for backend authentication. ## Setup Steps [Section titled “Setup Steps”](#setup-steps) 1. Please go to [console.cloud.google.com](https://console.cloud.google.com/) 2. Select the project you want to use ![Firebase Project Selector](/social-login-assets/firebase_project_select.webp) 3. Go to the `Authentication` menu 1. Click on `build` 2. Click on `Authentication` ![Firebase Authentication Menu](/social-login-assets/firebase_select_authentication.webp) 4. Click on the `Get started` button ![Firebase Get Started Button](/social-login-assets/firebase_auth_start.webp) 5. Select EITHER `Email/Password` AND `Google` OR `Google` ONLY Note I will select `Email/Password` AND `Google`, as I want to use both, but you could select only `Google`. This is something you can change later. ![Firebase Select Email/Password and Google Button](/social-login-assets/firebase_google_email_auth.webp) ![Firebase enable email authentication](/social-login-assets/firebase_auth_enable_email.webp) ![Firebase save enable google authentication](/social-login-assets/firebase_auth_enable_email_save.webp) ![Firebase enable add provider button](/social-login-assets/firebase_auth_add_provider_after_email.webp) ![Firebase add Google provider after having added email provider](/social-login-assets/firebase_auth_add_google_provider_after_email.webp) 6. Enable the `Google` provider ![Firebase enable google authentication](/social-login-assets/firebase_auth_add_google_provider_enable.webp) 7. Add the support email ![Firebase add support email](/social-login-assets/firebase_auth_add_support_email_google_auth.webp) ![Firebase add support email part 2](/social-login-assets/firebase_auth_add_support_email_google_auth_2.webp) 8. Change the `Public-facing name for project`. Note This will be displayed to the users, so I recommend changing it to something more descriptive. ![Firebase change public facing name](/social-login-assets/firebase_auth_change_public_facing_name.webp) 9. Click on the `Save` button ![Firebase save public facing name](/social-login-assets/firebase_auth_save_google_auth.webp) Voilà, you have now enabled Google Sign-In with Firebase Authentication 🎉 # Firebase Google Login on iOS > Learn how to set up Google Sign-In with Firebase Authentication on iOS using the Capacitor Social Login plugin. ## Introduction [Section titled “Introduction”](#introduction) This guide will help you integrate Google Sign-In with Firebase Authentication on iOS. I assume you have already completed the [general Firebase Google setup](./general/). Note I will assume that you have not yet created your iOS app in the Firebase Console. If you have, your steps will be slightly different. ## Setup Steps [Section titled “Setup Steps”](#setup-steps) 1. Go to your project overview over at [console.cloud.google.com](https://console.cloud.google.com/) ![Firebase Project Overview](/social-login-assets/firebase_project_overview.webp) 2. Click on the `Add app` button ![Firebase Add App Button](/social-login-assets/firebase_project_add_app.webp) Note It is possible that you will have to look here for this button. This applies only if you have already created a different app in the Firebase Console. ![Firebase Add App Button](/social-login-assets/firebase_project_add_app_2.webp) 3. Select `iOS` ![Firebase Add App iOS Button](/social-login-assets/firebase_project_add_app_ios.webp) 4. Fill the first part of the form 1. Fill the `Apple bundle ID` 1. Open Xcode at your app using `npx cap open ios` 2. Double click on `App` ![App target in Xcode project navigator](/social-login-assets/xcode_app_click.png) 3. Ensure that you are on `Targets -> App` ![Targets section in Xcode with App selected](/social-login-assets/xcode_targets_app.png) 4. Find your `Bundle Identifier` ![Bundle Identifier field in Xcode project settings](/social-login-assets/xcode_bundle_id.png) Note The ID shown here will differ from the one I will use for the rest of the guide. I will use `app.capgo.plugin.SocialLogin` for the rest of the guide. 5. Copy the `Bundle Identifier` and paste it in the Firebase console ![Firebase Add App iOS Bundle ID Field](/social-login-assets/firebase_project_add_app_ios_bundle_id.webp) 2. Click on the `Register app` button ![Firebase Add App iOS Register Button](/social-login-assets/firebase_project_add_app_ios_register.webp) 5. Skip the `Download config file` step ![Firebase Add App iOS Skip Download Button](/social-login-assets/firebase_project_add_app_ios_skip_download.webp) 6. Skip the `Add firebase SDK` step ![Firebase Add App iOS Skip Download Firebase SDK Button](/social-login-assets/firebase_project_add_app_ios_skip_download_firebase_sdk_2.webp) 7. Skip the `Add initialization code` step ![Firebase Add App iOS Skip Add Initialization Code Button](/social-login-assets/firebase_project_add_app_ios_skip_download_firebase_sdk.webp) 8. Click on the `Continue to console` button ![Firebase Add App iOS Continue to Console Button](/social-login-assets/firebase_project_add_app_ios_continue_to_console.webp) 9. Get your iOS client ID and your `YOUR_DOT_REVERSED_IOS_CLIENT_ID` 1. Go to Google Cloud Console at [console.cloud.google.com](https://console.cloud.google.com/) 2. Find your project 1. Click on the project selector ![Google Cloud Console Project Selector](/social-login-assets/firebase_double_check_gc_project_select.webp) 2. Search up your project by the exact name of your Firebase project and click on it. In my case, it is `sociallogin-tutorial-app`. ![Firebase Project Selector Project](/social-login-assets/firebase_gc_project_select_project.webp) 3. Open the search bar and open `credentials` 1. Open the search bar ![Google Cloud Console Search Bar](/social-login-assets/firebase_double_check_gc_search_bar.webp) 2. Search for `credentials` and click on the `APIs and Services` one (number 2 on the screenshot) ![Google Cloud Console Credentials Search](/social-login-assets/firebase_gc_credentials_search.webp) 4. Click on the `iOS client for [YOUR_APP_ID] (auto created by Google Service)` one. In my case, it is `sociallogin-tutorial-app`. ![Google Cloud Console Credentials iOS Client ID](/social-login-assets/firebase_gc_credentials_ios_client_id.webp) 5. Copy the `Client ID` as well as the `iOS URL scheme`. This will be respectively your `iOSClientId` and `YOUR_DOT_REVERSED_IOS_CLIENT_ID`. Note You will pass the `iOSClientId` in the `initialize` method of the plugin, while you will use the `YOUR_DOT_REVERSED_IOS_CLIENT_ID` in the `Info.plist` file of your app, as explained in the next part of this guide. ![Google Cloud Console Credentials iOS Client ID Copy](/social-login-assets/firebase_gc_credentials_ios_client_id_copy.webp) 10. Get your web client ID 1. Go back to the Firebase console and go to `Build` -> `Authentication` ![Firebase Authentication Menu](/social-login-assets/firebase_select_authentication.webp) 2. Click on the `Sign-in method` button ![Firebase Authentication Sign-in Method Button](/social-login-assets/firebase_select_authentication_sign_in_method.webp) 3. Click on the `Google` provider ![Firebase Authentication Sign-in Method Google Provider](/social-login-assets/firebase_select_authentication_sign_in_method_google.webp) 4. Click on the `Web SDK configuration` button ![Firebase Authentication Sign-in Method Web SDK Configuration Button](/social-login-assets/firebase_select_authentication_sign_in_method_web_sdk_configuration.webp) 5. Copy the `Web client ID`. This will be your `webClientId` in the `initialize` method of the plugin. ![Firebase Authentication Sign-in Method Web SDK Configuration Web Client ID](/social-login-assets/firebase_select_authentication_sign_in_method_web_sdk_configuration_web_client_id.webp) 11. Modify your app’s Info.plist 1. Open Xcode and find the `Info.plist` file ![Info.plist file in Xcode project navigator](/social-login-assets/xcode_info_file.png) 2. Right click this file and open it as source code ![Right-click menu showing Open As Source Code option](/social-login-assets/xcode_open_as_src_code.png) 3. At the bottom of your `Plist` file, you will see a `` tag ![Closing dict tag in Info.plist file](/social-login-assets/xcode_dict_tag.png) 4. Insert the following fragment just before the closing `` tag ![Info.plist with URL schemes code inserted before closing dict tag](/social-login-assets/xcode_plist_inserted.png) ```xml CFBundleURLTypes CFBundleURLSchemes YOUR_DOT_REVERSED_IOS_CLIENT_ID GIDClientID YOUR_IOS_CLIENT_ID.apps.googleusercontent.com ``` 5. Change the `YOUR_DOT_REVERSED_IOS_CLIENT_ID` to the value copied in step 9 (the iOS URL scheme) ![Info.plist with actual reversed client ID inserted in URL schemes](/social-login-assets/xcode_plist_final.png) Caution Ensure that this value **STARTS** with `com.googleusercontent.apps` 12. Change the `YOUR_IOS_CLIENT_ID` to the iOS Client ID you copied in step 9 13. Save the file with `Command + S` 14. Modify the `AppDelegate.swift` 1. Open the AppDelegate ![AppDelegate.swift file in Xcode project navigator](/social-login-assets/xcode_app_deleg.png) 2. Insert `import GoogleSignIn` at the top of the file ![AppDelegate.swift with GoogleSignIn import added](/social-login-assets/xcode_app_deleg_google_sign_in.png) 3. Find the `func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:])` function ![Original application openURL function in AppDelegate](/social-login-assets/xcode_app_deleg_app_fn.png) 4. Modify the function to look like this ```swift func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool { // Called when the app was launched with a url. Feel free to add additional processing here, // but if you want the App API to support tracking app url opens, make sure to keep this call var handled: Bool handled = GIDSignIn.sharedInstance.handle(url) if handled { return true } return ApplicationDelegateProxy.shared.application(app, open: url, options: options) } ``` ![Modified application openURL function with GoogleSignIn handling](/social-login-assets/xcode_app_deleg_app_fn_mod.png) 5. Save the file with `Command + S` 15. Using the Google login in your app At this step, you are ready to use the Google login in your app. Please use the [authUtils.ts](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/authUtils.ts) file of the example app to authenticate with Google. The user will be automatically created in Firebase Auth on first sign-in ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) If authentication hangs or fails: * Verify the `idToken` audience matches your Firebase web client ID * Check that Google Sign-In is enabled in Firebase Console * Ensure Info.plist has the correct URL schemes and GIDClientID * Verify `iOSServerClientId` matches your web client ID * Review the [example app code](https://github.com/Cap-go/capacitor-social-login/tree/main/example-app/src/authUtils.ts) for reference # Firebase Google Login on Web > Learn how to set up Google Sign-In with Firebase Authentication on Web using Firebase's built-in Google Sign-In. ## Introduction [Section titled “Introduction”](#introduction) The Capacitor Social Login plugin **does not support web platforms**. For web applications, you should use Firebase’s built-in Google Sign-In directly, which provides a more reliable popup-based authentication flow. ## Why Not Use the Plugin on Web? [Section titled “Why Not Use the Plugin on Web?”](#why-not-use-the-plugin-on-web) The Capacitor Social Login plugin is designed for native mobile platforms (Android and iOS) where it can leverage platform-specific authentication flows. For web, Firebase’s native `signInWithPopup` method is: * ✅ More reliable and better supported * ✅ Handles browser session storage automatically * ✅ Provides better error handling * ✅ No additional configuration needed ## Setup Steps [Section titled “Setup Steps”](#setup-steps) 1. **Configure Firebase Project** Ensure your Firebase project has Google Sign-In enabled: * Go to [Firebase Console](https://console.firebase.google.com/) * Navigate to Authentication > Sign-in method * Enable Google Sign-In provider 2. Add your authorized domains 1. Go to your project overview over at [console.cloud.google.com](https://console.cloud.google.com/) ![Firebase Project Overview](/social-login-assets/firebase_project_overview.webp) 2. Open the `Authentication` menu ![Firebase Authentication Menu](/social-login-assets/firebase_select_authentication.webp) 3. Click on the `Settings` button ![Firebase Authentication Sign-in Method Button](/social-login-assets/firebase_select_authentication_settings.webp) 4. Setup the `Authorized domains` ![Firebase Authentication Settings Authorized Domains](/social-login-assets/firebase_select_authentication_settings_authorized_domains.webp) ## Example Implementation [Section titled “Example Implementation”](#example-implementation) See the [authUtils.ts](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/authUtils.ts) file in the example app for a complete implementation that: * Uses Firebase’s `signInWithPopup` for web platforms * Uses Capacitor Social Login plugin for Android/iOS platforms * Handles platform detection automatically The example shows how to conditionally use Firebase’s built-in method for web while using the plugin for native platforms. ## Additional Resources [Section titled “Additional Resources”](#additional-resources) * [Firebase Authentication Documentation](https://firebase.google.com/docs/auth) - Complete Firebase Auth documentation * [Firebase Google Sign-In for Web](https://firebase.google.com/docs/auth/web/google-signin) - Official Firebase guide for Google Sign-In on web * [Google Login Setup Guide](/docs/plugins/social-login/google/general/) - Guide for configuring authorized domains and OAuth consent screen # Firebase Integration Introduction > Learn how to integrate Firebase Authentication with the Capacitor Social Login plugin for a complete authentication solution. ## Overview [Section titled “Overview”](#overview) This tutorial will guide you through setting up Firebase Authentication with the Capacitor Social Login plugin. This integration allows you to use native social login providers (Google, Apple, Facebook, Twitter) on mobile platforms while leveraging Firebase Auth for backend authentication and Firestore for data storage. ## What You’ll Learn [Section titled “What You’ll Learn”](#what-youll-learn) * How to configure Firebase Authentication * How to integrate Capacitor Social Login plugin with Firebase Auth * Platform-specific setup for Android, iOS, and Web ## What You’ll Need [Section titled “What You’ll Need”](#what-youll-need) Before you begin, make sure you have: 1. **A Firebase Project** * Create a project at [Firebase Console](https://console.firebase.google.com/) * Enable Authentication (Email/Password and Google Sign-In) * Get your Firebase configuration credentials 2. **Firebase JS SDK** * Install Firebase in your project: ```bash npm install firebase ``` 3. **A Capacitor Project** * An existing Capacitor application * Capacitor Social Login plugin installed: ```bash npm install @capgo/capacitor-social-login npx cap sync ``` ## Example Application [Section titled “Example Application”](#example-application) A complete working example is available in the repository: **Code Repository**: [You can find the code repository here](https://github.com/Cap-go/capacitor-social-login/tree/main/example-app) The example app demonstrates: * Email/password authentication with Firebase * Google Sign-In integration (Android, iOS, and Web) * A simple key-value store using Firebase Firestore collections * User-specific data storage in Firestore subcollections ## A word about using the Firebase SDK on Capacitor [Section titled “A word about using the Firebase SDK on Capacitor”](#a-word-about-using-the-firebase-sdk-on-capacitor) When using the Firebase JS SDK on Capacitor, you need to be aware that when using the authentication methods, you need to initialize the Firebase Auth instance a bit differently. On the web platform, you would use the `getAuth` function to get the Firebase Auth instance. ```typescript import { initializeApp } from 'firebase/app'; import { getAuth } from 'firebase/auth'; const app = initializeApp(firebaseConfig); const auth = getAuth(app); ``` Unfortunately, on Capacitor, this does not work and causes Firebase auth to hang. As stated in [this blog post](https://harryherskowitz.com/2021/08/23/firebase-capacitor.html), you need to use the `initializeAuth` function to initialize the Firebase Auth instance. This looks like this: ```typescript import { initializeApp } from 'firebase/app'; import { initializeAuth } from 'firebase/auth'; const app = initializeApp(firebaseConfig); function whichAuth() { let auth; if (Capacitor.isNativePlatform()) { auth = initializeAuth(app, { persistence: indexedDBLocalPersistence, }); } else { auth = getAuth(app); } return auth; } export const auth = whichAuth(); ``` ## Next Steps [Section titled “Next Steps”](#next-steps) Continue with the setup guides: * [Firebase Setup](./google/general/) - Configure Firebase project * [Android Setup](./google/android/) - Android-specific configuration * [iOS Setup](./google/ios/) - iOS-specific configuration * [Web Setup](./google/web/) - Web-specific configuration # Getting Started > Discover how to install and configure the Capacitor Social Login plugin to enhance your app's authentication with seamless integration for Google, Apple, Facebook, and generic OAuth2 logins. 1. **Install the package** * npm ```sh npm i @capgo/capacitor-social-login ``` * pnpm ```sh pnpm add @capgo/capacitor-social-login ``` * yarn ```sh yarn add @capgo/capacitor-social-login ``` * bun ```sh bun add @capgo/capacitor-social-login ``` 2. **Sync with native projects** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` 3. **Initialize in app startup** ```typescript import { SocialLogin } from '@capgo/capacitor-social-login'; await SocialLogin.initialize({ google: { webClientId: 'your-google-web-client-id', iOSServerClientId: 'your-google-server-client-id', mode: 'online', }, apple: { clientId: 'your-apple-service-id', useProperTokenExchange: true, useBroadcastChannel: true, }, facebook: { appId: 'your-facebook-app-id', }, twitter: { clientId: 'your-twitter-client-id', redirectUrl: 'myapp://oauth/twitter', }, oauth2: { github: { appId: 'your-github-client-id', authorizationBaseUrl: 'https://github.com/login/oauth/authorize', accessTokenEndpoint: 'https://github.com/login/oauth/access_token', redirectUrl: 'myapp://oauth/github', scope: 'read:user user:email', pkceEnabled: true, }, }, }); ``` ## Core flow examples [Section titled “Core flow examples”](#core-flow-examples) ### Login [Section titled “Login”](#login) ```typescript await SocialLogin.login({ provider: 'google', options: { scopes: ['profile', 'email'] }, }); await SocialLogin.login({ provider: 'oauth2', options: { providerId: 'github', scope: 'read:user user:email', }, }); ``` ### Session checks [Section titled “Session checks”](#session-checks) ```typescript const status = await SocialLogin.isLoggedIn({ provider: 'google' }); await SocialLogin.logout({ provider: 'google' }); ``` ### Auth codes and refresh [Section titled “Auth codes and refresh”](#auth-codes-and-refresh) ```typescript // For providers that support this mode const authCodeResult = await SocialLogin.getAuthorizationCode({ provider: 'google' }); await SocialLogin.refresh({ provider: 'google', options: {} as never }); ``` ### Advanced helpers [Section titled “Advanced helpers”](#advanced-helpers) ```typescript const jwt = await SocialLogin.decodeIdToken({ idToken: 'eyJhbGciOi...', }); const { date } = await SocialLogin.getAccessTokenExpirationDate({ accessTokenExpirationDate: Date.now() + 3600 * 1000, }); const expired = await SocialLogin.isAccessTokenExpired({ accessTokenExpirationDate: Date.now() + 1000, }); const active = await SocialLogin.isRefreshTokenAvailable({ refreshToken: 'a-token' }); ``` ## Provider-specific notes [Section titled “Provider-specific notes”](#provider-specific-notes) ### Google offline mode [Section titled “Google offline mode”](#google-offline-mode) `google.mode: 'offline'` returns `serverAuthCode` from login. In this mode logout, isLoggedIn, getAuthorizationCode, and refresh are not available. ### Apple [Section titled “Apple”](#apple) Set `useProperTokenExchange: true` for strict token handling and `useBroadcastChannel: true` for Android simplified setup. ### OAuth2 web redirect flow [Section titled “OAuth2 web redirect flow”](#oauth2-web-redirect-flow) Use `OAuth2LoginOptions.flow: 'redirect'` for web flows that navigate away from the page. ## Provider configuration in Capacitor [Section titled “Provider configuration in Capacitor”](#provider-configuration-in-capacitor) You can disable unused providers to reduce native binaries: ```typescript import type { CapacitorConfig } from '@capacitor/cli'; const config: CapacitorConfig = { appId: 'com.example.app', appName: 'My App', webDir: 'dist', plugins: { SocialLogin: { providers: { google: true, facebook: true, apple: true, twitter: false, }, }, }, }; ``` ## Related documentation [Section titled “Related documentation”](#related-documentation) * [Integrations overview](/docs/plugins/social-login/integrations/) * [Better Auth integration](/docs/plugins/social-login/better-auth/) * [OAuth2 and OIDC providers](/docs/plugins/social-login/oauth2/) * [Migrate from Ionic Auth Connect](/docs/upgrade/from-ionic-auth-connect/) * [Social Login Auth Connect migration guide](/docs/plugins/social-login/migrations/ionic-auth-connect/) * [Migrate legacy providers](/docs/plugins/social-login/migrations/google/) * [Ionic enterprise plugins migration solution](/solutions/ionic-enterprise-plugins/) # Google Login on Android > This guide provides a comprehensive walkthrough on setting up Google Login using Capacitor for Android devices, detailing each step to ensure a smooth integration process and addressing potential challenges you may encounter. ## Introduction [Section titled “Introduction”](#introduction) In this guide, you will learn how to setup Google Login with Capgo Social Login for Android. I assume that you have already read the [general setup guide](/docs/plugins/social-login/google/general/). ## Using Google login on Android [Section titled “Using Google login on Android”](#using-google-login-on-android) In this part, you will learn how to setup Google login in Android. Caution The Android SHA1 certificate is beyond painful and I wouldn’t wish it on anyone to have to set this up. The following steps assume the simplest scenario of an app that isn’t published to Google Play Store and that is only used by the local simulator, or development hardware device. If you have deployed your app to Google Play Store, you **MUST** add an additional Android client ID that contains the SHA1 from Google Play console for production releases. You can find the SHA1 hash that Google Play uses to sign your release bundle under `Test and release > Setup > App Signing`. Finally, it’s important to mention that if you mess up, the error will NOT be obvious. It may be very difficult to debug. If you struggle with the setup, please look at the [Github issues](https://github.com/Cap-go/capacitor-social-login/issues). Additionally, you may look at the troubleshooting section of the [Google Login setup for Android](#troubleshooting) for more information. Note You may create multiple Android client IDs. This is required if you have multiple SHA1 certificates. 1. Create an Android client ID. 1. Click on the search bar ![Google Console search bar](/social-login-assets/google_cons_search.png) 2. Search for `credentials` and click on the `APIs and Services` one (number 2 on the screenshot) ![Search results showing credentials option with APIs and Services highlighted](/social-login-assets/google_cons_cred_search.png) 3. Click on the `create credentials` ![Create credentials button in Google Console](/social-login-assets/google_cons_create_cred.png) 4. Select `OAuth client ID` ![OAuth client ID option in credentials creation menu](/social-login-assets/google_cons_cred_oauth.png) 5. Select the `Android` application type ![Application type selection with Android option highlighted](/social-login-assets/google_cons_app_type_android.png) 6. Open Android Studio 7. At the very bottom of the navigator, find the `Gradle Scripts` ![Gradle Scripts section in Android Studio project navigator](/social-login-assets/studio_gradle_scripts.png) 8. Find `build.gradle` for the module `app` ![build.gradle (Module: app) file in Gradle Scripts section](/social-login-assets/studio_build_gradle.png) 9. Copy the `android.defaultConfig.applicationId`. This will be your `package name` in the Google console ![Build.gradle file showing applicationId configuration](/social-login-assets/studio_build_gradle_app_id.png) 10. Now, open the terminal. Make sure that you are in the `android` folder of your app and run `./gradlew signInReport` ![Terminal showing gradlew signInReport command](/social-login-assets/term_sign_report.png) 11. Scroll to the top of this command. You should see the following. Copy the `SHA1`. ![Terminal output showing SHA1 certificate fingerprint](/social-login-assets/term_sign_report_res.png) 12. Now, go back to the Google Console. Enter your `applicationId` as the `Package Name` and your SHA1 in the certificate field and click `create` ![Android client creation form with package name and SHA1 fields filled in](/social-login-assets/google_cons_creat_android_client.png) 2. Create a web client (this is required for Android) 1. Go to the `Create credentials` page in Google Console 2. Set application type to `Web` ![Application type selection with Web option highlighted](/social-login-assets/google_cons_app_type_web.png) 3. Click `Create` ![Web client creation form with Create button at bottom](/social-login-assets/google_cons_web_app_create.png) 4. Copy the client ID, you’ll use this as the `webClientId` in your JS/TS code ![Client ID details showing Web client ID to copy](/social-login-assets/google_cons_copy_web_client_id.png) 3. Modify your `MainActivity` 1. Please open your app in Android Studio. You can run `cap open android` 2. Find `MainActivity.java` 1. Open the `app` folder ![App folder in Android Studio project navigator](/social-login-assets/studio_app_folder.png) 2. Find `java` ![Java folder in Android Studio project structure](/social-login-assets/studio_app_java.png) 3. Find your `MainActivity.java` and click on it ![MainActivity.java file in package structure](/social-login-assets/studio_app_java_activity_main.png) 3. Modify `MainActivity.java`. Please add the following code ```java import ee.forgr.capacitor.social.login.GoogleProvider; import ee.forgr.capacitor.social.login.SocialLoginPlugin; import ee.forgr.capacitor.social.login.ModifiedMainActivityForSocialLoginPlugin; import com.getcapacitor.PluginHandle; import com.getcapacitor.Plugin; import android.content.Intent; import android.util.Log; import com.getcapacitor.BridgeActivity; // ModifiedMainActivityForSocialLoginPlugin is VERY VERY important !!!!!! public class MainActivity extends BridgeActivity implements ModifiedMainActivityForSocialLoginPlugin { @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode >= GoogleProvider.REQUEST_AUTHORIZE_GOOGLE_MIN && requestCode < GoogleProvider.REQUEST_AUTHORIZE_GOOGLE_MAX) { PluginHandle pluginHandle = getBridge().getPlugin("SocialLogin"); if (pluginHandle == null) { Log.i("Google Activity Result", "SocialLogin login handle is null"); return; } Plugin plugin = pluginHandle.getInstance(); if (!(plugin instanceof SocialLoginPlugin)) { Log.i("Google Activity Result", "SocialLogin plugin instance is not SocialLoginPlugin"); return; } ((SocialLoginPlugin) plugin).handleGoogleLoginIntent(requestCode, data); } } // This function will never be called, leave it empty @Override public void IHaveModifiedTheMainActivityForTheUseWithSocialLoginPlugin() {} } ``` 4. Save the file 4. Use Google Login in your application 1. First, import `SocialLogin` ```typescript import { SocialLogin } from '@capgo/capacitor-social-login'; ``` 2. Call initialize. This should be called only once. ```typescript // onMounted is Vue specific // webClientId is the client ID you got in the web client creation step not the android client ID. onMounted(() => { SocialLogin.initialize({ google: { webClientId: '673324426943-avl4v9ubdas7a0u7igf7in03pdj1dkmg.apps.googleusercontent.com', } }) }) ``` 3. Call `SocialLogin.login`. Create a button and run the following code on click. ```typescript const res = await SocialLogin.login({ provider: 'google', options: {} }) // handle the response console.log(JSON.stringify(res)) ``` 5. Configure the emulator for testing 1. Go into `Device manager` and click the plus button ![Device Manager in Android Studio with plus button highlighted](/social-login-assets/studio_device_man.png) 2. Create a virtual device ![Create Virtual Device button in Virtual Device Configuration](/social-login-assets/studio_create_virt_dev.png) 3. Select any device with a `Play Store` icon ![Hardware selection showing devices with Play Store support](/social-login-assets/studio_new_dev_select_hardware.png) As you can see, the `pixel 8` supports the `Play Store` services 4. Click `next` ![Next button in device creation wizard](/social-login-assets/studio_new_dev_next_1.png) 5. Make sure that the OS image is of type `Google Play`. **IT MUST** be of type `Google Play` ![System image selection showing Google Play type images](/social-login-assets/studio_new_dev_google_play_dev_type.png) 6. Click next ![Next button in system image selection screen](/social-login-assets/studio_new_dev_next_1.png) 7. Confirm your device. You can name your emulator as you prefer ![Device configuration verification screen with Finish button](/social-login-assets/studio_new_dev_next_3.png) 8. Go into `Device Manager` and boot up your simulator ![Device Manager with virtual device listed and play button](/social-login-assets/studio_dev_manager.png) 9. After the simulator boots up, go into its settings ![Android emulator showing settings app](/social-login-assets/emul_and_settings_1.png) 10. Go into `Google Play` ![Settings screen with Google Play option](/social-login-assets/emul_and_settings_2.png) 11. Click `Update` and wait about 60 seconds ![Google Play update screen with Update button](/social-login-assets/emul_and_settings_update_play_store.png) 6. Test your application If you did everything correctly, you should see the Google login flow working properly: ![Demo of Google login flow on Android showing sign-in process and successful authentication](/social-login-assets/google_android_final_login_show.gif) ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) If you have any issues, please look at the [Github issues](https://github.com/Cap-go/capacitor-social-login/issues). The issues with Google login are **ALWAYS** related to the SHA1 certificate. If you cannot get the development SHA1 certificate, try to use a custom keystore. [Here](https://github.com/Cap-go/capacitor-social-login/issues/147#issuecomment-2849742574) is a comment explaining how to add keystore to your project. # Google Login Setup > This guide provides a comprehensive overview on setting up Google Login using Capacitor, detailing each step to ensure a seamless integration process and addressing potential challenges you may encounter. ## Introduction [Section titled “Introduction”](#introduction) In this guide, you will learn how to setup Google Login with Capgo Social Login. You will need the following in order to setup Google Login: * A Google account ## General setup [Section titled “General setup”](#general-setup) Note This step is required regardless of which the platform you decide to use. In this part, you will setup the login screen displayed by Google. 1. Please go to [console.cloud.google.com](https://console.cloud.google.com/) 2. Click on the project selector ![Google Console Project Selector](/social-login-assets/google_cons_project_selector.png) 3. If you don’t have a project already, please **create a new project**. 1. Click on `New project` ![New Project button in Google Console](/social-login-assets/google_cons_new_project_btn.png) 2. Name your project and click `Create` ![Project naming screen showing name field and Create button](/social-login-assets/google_cons_name_projec.png) 3. Ensure that you are on the right project ![Project name showing in the selector indicating correct project selection](/social-login-assets/google_cons_right_proj.png) 4. Start to configure the `OAuth consent screen` 1. Click on the search bar ![Google Console search bar](/social-login-assets/google_cons_search.png) 2. Search for `OAuth consent screen` and click on it ![Search results showing OAuth consent screen option](/social-login-assets/google_cons_search_2.png) 3. Configure the consent screen Note I will assume that you are developing an app open to the public, so I will use the `external` user type. Please select the user type that suits you the best AND click `create` Click on `create` ![OAuth consent screen user type selection with External and Internal options](/social-login-assets/google_cons_oauth_const_scr.png) 5. Fill the information about your app 1. Let’s start with the `App Information` ![App Information section showing App name and User support email fields](/social-login-assets/google_cons_app_inf.png) * Please type in your `App Name` Caution **THIS WILL BE DISPLAYED TO THE USERS** * Enter the `user support email` Note You can learn more about the support email [here](https://support.google.com/cloud/answer/10311615#user-support-email\&zippy=%2Cuser-support-email/) 2. You **CAN** add the app logo. ![App logo upload section in OAuth consent screen](/social-login-assets/google_cons_app_logo.png) Note This is not obligatory and I will skip this step 3. You **SHOULD** configure the `App domain` ![App domain configuration section with authorized domains field](/social-login-assets/google_cons_app_doma.png) Note I will not do that because this is just a simple demonstration that will **NOT** get published, but I strongly recommend filling this section. 4. You **HAVE TO** provide the developer’s email ![Developer contact information section with email field](/social-login-assets/google_cons_dev_cont_inf.png) 5. Click on `save and continue` ![Save and Continue button at bottom of form](/social-login-assets/google_cons_cons_sav_cont.png) 6. Configure the scopes 1. Click on `add or remove scopes` ![Add or remove scopes button in scopes configuration screen](/social-login-assets/google_cons_add_rm_sco.png) 2. Select the following scopes and click `update` ![Scope selection dialog with email and profile scopes selected](/social-login-assets/google_cons_update_scope.png) 3. Click `save and continue` ![Save and Continue button in scopes screen](/social-login-assets/google_cons_scope_save.png) 7. Add a test user 1. Click on `add users` ![Add users button in test users section](/social-login-assets/google_cons_add_test_usr.png) 2. Enter your Google email, click enter, and click `add` ![Email input field and Add button for test users](/social-login-assets/google_cons_add_test_usr_2.png) 3. Click `save and continue` ![Save and Continue button in test users screen](/social-login-assets/google_cons_test_usr_save.png) 8. Click `back to dashboard` ![Back to dashboard button at bottom of completion page](/social-login-assets/google_cons_back_to_dahs.png) 9. Submit your app for verification Note I strongly recommend submitting you app for verification. This is outside the scope of this tutorial. You can learn more [here](https://support.google.com/cloud/answer/13463073/). This isn’t required for local testing, but is required for production. ## Differences between online access and offline access [Section titled “Differences between online access and offline access”](#differences-between-online-access-and-offline-access) There are multiple ways to use Google Login with Capacitor. Here is a table that summarizes the differences between the two: | | Online access | Offline access | | :---------------------: | :-----------: | :------------: | | Requires a backend | ❌ | ✅ | | Long-lived access token | ❌ | ✅ | | Easy setup | ✅ | ❌ | Note **Offline mode** means that your backend can access Google’s APIs even when the user is not actively using your app (i.e., when the user is “offline” from your app’s perspective). Long-lived access tokens enable this functionality by allowing the backend to call Google APIs on behalf of the user at any time. Caution **Offline mode REQUIRES a backend server.** When using offline mode, the frontend receives minimal information (primarily just a server auth code). Without a backend to exchange this code for tokens and user information, offline mode provides no useful data to your frontend application. If you still do not know which one you should choose, please consider the following scenarios: 1. You want the user to login, immediately after you are going to issue him a custom JWT. Your app will NOT call Google APIs In this case, choose online access. 2. Your app will call some Google APIs from the client, but never from the backend In this case, choose online access 3. Your app will call some google APIs from the backend, but only when the user is actively using the app In this case, choose online access 4. Your app will periodically check the user’s calendar, even when he is not actively using the app In this case, choose offline access ## An example backend for online access [Section titled “An example backend for online access”](#an-example-backend-for-online-access) In this part of the tutorial, I will show how to validate the user on your backend. This example will be very simple and it will be based on the following technologies: * [Typescript](https://www.typescriptlang.org/) * [Hono](https://hono.dev/) * [Javascript’s fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch) You can find the code for this example [here](https://github.com/WcaleNieWolny/capgo-social-login-backend-demo/blob/141c01d93a85240e31a0d488a89df13c842708b1/index.ts#L135-L153) As you can see: ![VS Code showing Google authentication code that verifies tokens](/social-login-assets/vscode_auth_google.png) The idea is rather simple. You send a simple `GET` request to `https://www.googleapis.com/oauth2/v3/tokeninfo` and this returns you whether the token is valid or not and if it it is, it gives you the email of the user. It also gives you some other info about the user token ![Google OAuth Playground showing token information response with user details](/social-login-assets/google_auth_playground_token_info.png) From there, you could issue the user with your own JWT or issue some sort of session cookie. The possibilities are endless, for the final auth implementation. If you do want to call Google API’s, I would strongly recommend looking at [Google’s OAuth 2.0 Playground](https://developers.google.com/oauthplayground). From there you can easily see what APIs you can call. ## Using offline access with your own backend [Section titled “Using offline access with your own backend”](#using-offline-access-with-your-own-backend) In order to use offline access you will need the following: * An HTTP server In this example, I will be using the following technologies to provide the offline access in my app: * [Hono](https://hono.dev/) * [Hono Zod validator](https://hono.dev/docs/guides/validation#with-zod) * [Zod](https://zod.dev/) * [Hono JWT](https://hono.dev/docs/helpers/jwt#jwt-authentication-helper) * [LowDb](https://www.npmjs.com/package/lowdb) (a simple database) The code for this example can be found [here](https://github.com/WcaleNieWolny/capgo-social-login-backend-demo/blob/aac7a8c909f650a8c2cd7f88c97f5f3c594ce9ba/index.ts#L139-L287) As for the client code, it looks like this: ```typescript import { Capacitor } from '@capacitor/core'; import { GoogleLoginOfflineResponse, SocialLogin } from '@capgo/capacitor-social-login'; import { usePopoutStore } from '@/popoutStore'; // <-- specific to my app const baseURL = "[redacted]"; async function fullLogin() { await SocialLogin.initialize({ google: { webClientId: '[redacted]', iOSClientId: '[redacted]', iOSServerClientId: 'The same value as webClientId', mode: 'offline' // <-- important } }) const response = await SocialLogin.login({ provider: 'google', options: { forceRefreshToken: true // <-- important } }) if (response.provider === 'google') { const result = response.result as GoogleLoginOfflineResponse const res = await fetch(`${baseURL}/auth/google_offline`, { headers: { "Content-Type": "application/json" }, body: JSON.stringify({ serverAuthCode: result.serverAuthCode, platform: Capacitor.getPlatform() }), method: "POST" }) if (res.status !== 200) { popoutStore.popout("Full google login failed", "check console"); return } const { jwt } = await res.json(); const userinfo = await fetch(`${baseURL}/auth/get_google_user`, { headers: { Authorization: `Bearer ${jwt}` } }) if (userinfo.status !== 200) { popoutStore.popout("Full google (userinfo) login failed", "check console"); return } popoutStore.popout("userinfo res", await userinfo.text()); } } ``` # Google Login on iOS > This guide offers a comprehensive walkthrough for configuring Google Login with Capacitor on iOS, detailing each step to ensure a smooth integration process. ## Introduction [Section titled “Introduction”](#introduction) In this guide, you will learn how to setup Google Login with Capgo Social Login for iOS. I assume that you have already read the [general setup guide](/docs/plugins/social-login/google/general/). ## Using Google login on iOS [Section titled “Using Google login on iOS”](#using-google-login-on-ios) In this part, you will learn how to setup Google login in iOS. 1. Create an iOS client ID in the Google console 1. Click on the search bar ![Google Console search bar](/social-login-assets/google_cons_search.png) 2. Search for `credentials` and click on the `APIs and Services` one (number 2 on the screenshot) ![Search results showing credentials option with APIs and Services highlighted](/social-login-assets/google_cons_cred_search.png) 3. Click on the `create credentials` ![Create credentials button in Google Console](/social-login-assets/google_cons_create_cred.png) 4. Select `OAuth client ID` ![OAuth client ID option in credentials creation menu](/social-login-assets/google_cons_cred_oauth.png) 5. Select the `Application type` to `iOS` ![Application type selection with iOS option highlighted](/social-login-assets/goolge_cons_cred_type_app_tye.png) 6. Find the bundle ID 1. Open Xcode 2. Double click on `App` ![App target in Xcode project navigator](/social-login-assets/xcode_app_click.png) 3. Ensure that you are on `Targets -> App` ![Targets section in Xcode with App selected](/social-login-assets/xcode_targets_app.png) 4. Find your `Bundle Identifier` ![Bundle Identifier field in Xcode project settings](/social-login-assets/xcode_bundle_id.png) 5. Go back to the Google Console and paste your `Bundle Identifier` into `Bundle ID` ![Bundle ID field in Google Console iOS client creation form](/social-login-assets/google_cons_ios_bd_id.png) 7. Optionally, add your `App Store ID` or `Team ID` into the client ID if you have published your app to App Store 8. After filling all the details, click `create` ![Create button at bottom of iOS client creation form](/social-login-assets/google_cons_ios_cred_creat.png) 9. Click `OK` ![OK button on client ID created confirmation dialog](/social-login-assets/google_cons_ios_click_ok.png) 10. Open the newly created iOS client ![Newly created iOS client in credentials list](/social-login-assets/google_cons_open_new_ios.png) 11. Copy the following data ![Client ID details showing Client ID and reversed client ID to copy](/social-login-assets/google_cons_ios_what_to_copy.png) Note The `nr. 1` in this image will later become the `iOSClientId` in the `initialize` call. The `nr. 2` in this image will later become `YOUR_DOT_REVERSED_IOS_CLIENT_ID` 2. Modify your app’s Info.plist 1. Open Xcode and find the `Info.plist` file ![Info.plist file in Xcode project navigator](/social-login-assets/xcode_info_file.png) 2. Right click this file and open it as source code ![Right-click menu showing Open As Source Code option](/social-login-assets/xcode_open_as_src_code.png) 3. At the bottom of your `Plist` file, you will see a `` tag ![Closing dict tag in Info.plist file](/social-login-assets/xcode_dict_tag.png) 4. Insert the following fragment just before the closing `` tag ![Info.plist with URL schemes code inserted before closing dict tag](/social-login-assets/xcode_plist_inserted.png) ```xml CFBundleURLTypes CFBundleURLSchemes YOUR_DOT_REVERSED_IOS_CLIENT_ID ``` 5. Change the `YOUR_DOT_REVERSED_IOS_CLIENT_ID` to the value copied in the previous step ![Info.plist with actual reversed client ID inserted in URL schemes](/social-login-assets/xcode_plist_final.png) Caution Ensure that this value **STARTS** with `com.googleusercontent.apps` 6. Save the file with `Command + S` 3. Modify the `AppDelegate.swift` 1. Open the AppDelegate ![AppDelegate.swift file in Xcode project navigator](/social-login-assets/xcode_app_deleg.png) 2. Insert `import GoogleSignIn` at the top of the file ![AppDelegate.swift with GoogleSignIn import added](/social-login-assets/xcode_app_deleg_google_sign_in.png) 3. Find the `func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:])` function ![Original application openURL function in AppDelegate](/social-login-assets/xcode_app_deleg_app_fn.png) 4. Modify the function to look like this ```swift func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool { // Called when the app was launched with a url. Feel free to add additional processing here, // but if you want the App API to support tracking app url opens, make sure to keep this call var handled: Bool handled = GIDSignIn.sharedInstance.handle(url) if handled { return true } return ApplicationDelegateProxy.shared.application(app, open: url, options: options) } ``` ![Modified application openURL function with GoogleSignIn handling](/social-login-assets/xcode_app_deleg_app_fn_mod.png) 5. Save the file with `Command + S` 4. Setup Google login in your JavaScript/TypeScript code 1. Import `SocialLogin` and `Capacitor` ```typescript import { SocialLogin } from '@capgo/capacitor-social-login'; import { Capacitor } from '@capacitor/core'; ``` 2. Call the initialize method (this should be called only once) **Basic setup (online mode - recommended for most apps):** ```typescript // onMounted is Vue specific onMounted(() => { SocialLogin.initialize({ google: { iOSClientId: '673324426943-redacted.apps.googleusercontent.com', mode: 'online' // Default mode } }) }) ``` **Advanced setup with additional client IDs:** ```typescript onMounted(() => { SocialLogin.initialize({ google: { webClientId: 'YOUR_WEB_CLIENT_ID', // Optional: for web platform support iOSClientId: 'YOUR_IOS_CLIENT_ID', // Required: from step 1 iOSServerClientId: 'YOUR_WEB_CLIENT_ID', // Optional: same as webClientId, needed for some advanced features mode: 'online' // 'online' or 'offline' } }) }) ``` Note **Client ID Requirements:** * `iOSClientId`: **Required** - Must end with `googleusercontent.com` (from step 1 above) * `webClientId`: **Optional** - Only needed if you also support web platform or need advanced features * `iOSServerClientId`: **Optional** - Should be the same value as `webClientId` when provided For advanced setup with `webClientId` and `iOSServerClientId`, see the [web setup guide](/docs/plugins/social-login/google/web/) for creating these credentials. Caution **About offline mode:** When using `mode: 'offline'`, the login response will not contain user data directly. Instead, you’ll receive a server auth code that must be exchanged for user information via your backend server. See the [general setup guide](/docs/plugins/social-login/google/general/#using-offline-access-with-your-own-backend) for implementation details. 3. Implement the login function. Create a button and run the following code on click **For online mode:** ```typescript const res = await SocialLogin.login({ provider: 'google', options: {} }) // handle the response - contains user data console.log(JSON.stringify(res)) ``` **For offline mode:** ```typescript const res = await SocialLogin.login({ provider: 'google', options: { forceRefreshToken: true // Recommended for offline mode } }) // res contains serverAuthCode, not user data // Send serverAuthCode to your backend to get user information console.log('Server auth code:', res.result.serverAuthCode) ``` 5. Test your application 1. Build your app and run `cap sync` 2. If you’ve done everything correctly, you should see the Google login flow working properly ![Demo of Google login flow on iOS showing sign-in process and successful authentication](/social-login-assets/google_final_ios_v2.gif) Note The language in the Google prompt depends on your device’s language settings. ## Known Problems [Section titled “Known Problems”](#known-problems) ### Privacy Screen Plugin Incompatibility [Section titled “Privacy Screen Plugin Incompatibility”](#privacy-screen-plugin-incompatibility) The Google Login plugin is incompatible with [@capacitor/privacy-screen](https://github.com/ionic-team/capacitor-privacy-screen). When using both plugins together, the Google login webview will be interrupted by the privacy screen. **Workaround:** Call `await PrivacyScreen.disable();` before calling the login function: ```typescript import { PrivacyScreen } from '@capacitor/privacy-screen'; import { SocialLogin } from '@capgo/capacitor-social-login'; await PrivacyScreen.disable(); await SocialLogin.login({ provider: 'google', options: {} }); ``` # Google Login on Web > This guide provides a comprehensive walkthrough for setting up Google Login on web applications using Capacitor and the @capgo/capacitor-social-login plugin, ensuring a seamless integration process by covering all necessary steps and configurations. ## Introduction [Section titled “Introduction”](#introduction) In this guide, you will learn how to setup Google Login with Capgo Social Login for web applications. I assume that you have already read the [general setup guide](/docs/plugins/social-login/google/general/). ## Using Google login on the web [Section titled “Using Google login on the web”](#using-google-login-on-the-web) Using the google login on the web is rather simple. In order to use it, you have to do the following: 1. Create a web client in the Google Console Note If you have already configured Google Login for Android, you can skip this step as you’ve already created a web client. You can proceed directly to step 2. 1. Click on the search bar ![Google Console search bar](/social-login-assets/google_cons_search.png) 2. Search for `credentials` and click on the `APIs and Services` option (number 2 on the screenshot) ![Search results showing credentials option with APIs and Services highlighted](/social-login-assets/google_cons_cred_search.png) 3. Click on the `create credentials` ![Create credentials button in Google Console](/social-login-assets/google_cons_create_cred.png) 4. Select `OAuth client ID` ![OAuth client ID option in credentials creation menu](/social-login-assets/google_cons_cred_oauth.png) 5. Select the `Application type` as `Web application` ![Application type selection with Web option highlighted](/social-login-assets/google_cons_app_type_web.png) 6. Name your client and click `Create` ![Web client creation form with name field highlighted](/social-login-assets/google_cons_web_app_create.png) 7. Copy the client ID, you’ll use this as the `webClientId` in your application ![Client ID details showing Web client ID to copy](/social-login-assets/google_cons_copy_web_client_id.png) 2. Configure the web client in the Google Console 1. Please open the [credentials page](https://console.cloud.google.com/apis/credentials) and click on your web client ![Credentials list showing web client to click](/social-login-assets/google_cons_open_web_client_id.png) 2. Now, please add the `Authorized JavaScript origins`. This should include all the addresses that you might use for your app. In might case, I will **ONLY** use localhost, but since I use a custom port I have to add both `http://localhost` and `http://localhost:5173` 1. Please click on `add URI` ![Authorized JavaScript origins section with ADD URI button](/social-login-assets/google_cons_authorized_js_add_btn.png) 2. Please type your URL ![ADD URI dialog with localhost URL entered](/social-login-assets/google_cons_authorized_js_typed_url.png) 3. Please repeat until you added all the URLs 4. When you finish, your screen should look something like this ![Authorized JavaScript origins with multiple localhost URLs added](/social-login-assets/google_cons_authorized_js_final.png) 3. Now, please add some `Authorized redirect URIs`. This will depend on what page do you depend to use the CapacitorSocialLogin plugin on. In my case, I am going to be using it on `http://localhost:5173/auth` 1. Please click on `ADD URI` ![Authorized redirect URIs section with ADD URI button](/social-login-assets/google_cons_web_add_redirect_url_1.png) 2. Enter your URL and click `ADD URL` again ![ADD URI dialog with redirect URL entered](/social-login-assets/google_cons_web_add_redirect_url_2.png) 4. Click `save` ![Save button at bottom of web client configuration](/social-login-assets/google_cons_web_app_save.png) 3. Now, you should be ready to call `login` from JavaScript like so: 1. First, import `SocialLogin` ```typescript import { SocialLogin } from '@capgo/capacitor-social-login'; ``` 2. Then, call initialize. This should be called ONLY once. ```typescript // onMounted is Vue specific // webClientId is the client ID you got in the web client creation step not the android client ID. onMounted(() => { SocialLogin.initialize({ google: { webClientId: '673324426943-avl4v9ubdas7a0u7igf7in03pdj1dkmg.apps.googleusercontent.com', } }) }) ``` Web Redirect Handling When using Google login on web, you **MUST** call any function from the plugin when the redirect happens to initialize the plugin so it can handle the redirect and close the popup window. You can call either `isLoggedIn()` OR `initialize()` - both will trigger the redirect handling: ```typescript // Option 1: Call isLoggedIn when the redirect page loads SocialLogin.isLoggedIn({ provider: 'google' }).catch(() => { // Ignore the result, this is just to initialize the plugin }); // Option 2: Call initialize when the redirect page loads SocialLogin.initialize({ google: { webClientId: 'YOUR_WEB_CLIENT_ID.apps.googleusercontent.com', } }).catch(() => { // Ignore any errors, this is just to handle the redirect }); ``` 3. Create a login button that calls `SocialLogin.login` when clicked ```typescript const res = await SocialLogin.login({ provider: 'google', options: {} }) // Handle the response console.log(JSON.stringify(res)); ``` # Integrations > Backend and auth-platform integrations for @capgo/capacitor-social-login, including Better Auth, Firebase, Supabase, and OAuth providers such as Auth0, Okta, Cognito, and Keycloak. ## Overview [Section titled “Overview”](#overview) These guides show how to combine `@capgo/capacitor-social-login` with backend authentication platforms and session layers. Use these when you want native mobile login on the device but still want a backend or auth service to own users, sessions, and token verification. ## OAuth provider integrations [Section titled “OAuth provider integrations”](#oauth-provider-integrations) [ Auth0](/docs/plugins/social-login/integrations/auth0/) [Auth Connect preset and direct OAuth2 examples.](/docs/plugins/social-login/integrations/auth0/) [ Microsoft Entra ID](/docs/plugins/social-login/integrations/azure/) [Azure preset support and direct OAuth2 configuration.](/docs/plugins/social-login/integrations/azure/) [ AWS Cognito](/docs/plugins/social-login/integrations/cognito/) [Cognito preset and direct endpoint examples.](/docs/plugins/social-login/integrations/cognito/) [ GitHub](/docs/plugins/social-login/integrations/github/) [Generic OAuth2 configuration for GitHub login.](/docs/plugins/social-login/integrations/github/) [ Keycloak](/docs/plugins/social-login/integrations/keycloak/) [OIDC discovery and direct endpoint examples.](/docs/plugins/social-login/integrations/keycloak/) [ Okta](/docs/plugins/social-login/integrations/okta/) [Okta preset and direct OAuth2 examples.](/docs/plugins/social-login/integrations/okta/) [ OneLogin](/docs/plugins/social-login/integrations/onelogin/) [OneLogin preset and direct endpoint examples.](/docs/plugins/social-login/integrations/onelogin/) ## Backend and session integrations [Section titled “Backend and session integrations”](#backend-and-session-integrations) [ Better Auth](/docs/plugins/social-login/better-auth/) [Native token handoff examples for Google, Apple, Facebook, and Generic OAuth guidance.](/docs/plugins/social-login/better-auth/) [ Firebase](/docs/plugins/social-login/firebase/introduction/) [Use native provider login with Firebase Authentication and Firestore.](/docs/plugins/social-login/firebase/introduction/) [ Supabase](/docs/plugins/social-login/supabase/introduction/) [Use native provider login with Supabase Auth and PostgreSQL.](/docs/plugins/social-login/supabase/introduction/) # Auth0 > Integrate Auth0 with @capgo/capacitor-social-login using the Auth Connect preset wrapper or direct OAuth2 configuration. ## Overview [Section titled “Overview”](#overview) `@capgo/capacitor-social-login` supports Auth0 in two ways: * `SocialLoginAuthConnect` with the `auth0` preset * Direct `oauth2` configuration if you want full control over endpoints ## Auth Connect preset example [Section titled “Auth Connect preset example”](#auth-connect-preset-example) ```typescript import { SocialLoginAuthConnect } from '@capgo/capacitor-social-login'; await SocialLoginAuthConnect.initialize({ authConnect: { auth0: { domain: 'https://your-tenant.auth0.com', clientId: 'your-auth0-client-id', redirectUrl: 'myapp://oauth/auth0', audience: 'https://your-api.example.com', }, }, }); const result = await SocialLoginAuthConnect.login({ provider: 'auth0', }); ``` ## Direct OAuth2 example [Section titled “Direct OAuth2 example”](#direct-oauth2-example) ```typescript import { SocialLogin } from '@capgo/capacitor-social-login'; await SocialLogin.initialize({ oauth2: { auth0: { appId: 'your-auth0-client-id', authorizationBaseUrl: 'https://your-tenant.auth0.com/authorize', accessTokenEndpoint: 'https://your-tenant.auth0.com/oauth/token', redirectUrl: 'myapp://oauth/auth0', scope: 'openid profile email offline_access', pkceEnabled: true, additionalParameters: { audience: 'https://your-api.example.com', }, logoutUrl: 'https://your-tenant.auth0.com/v2/logout', }, }, }); const result = await SocialLogin.login({ provider: 'oauth2', options: { providerId: 'auth0', }, }); ``` ## Related docs [Section titled “Related docs”](#related-docs) * [OAuth2 and OIDC providers](/docs/plugins/social-login/oauth2/) * [Ionic Auth Connect migration](/docs/plugins/social-login/migrations/ionic-auth-connect/) * [Better Auth integration](/docs/plugins/social-login/better-auth/) # Microsoft Entra ID > Integrate Microsoft Entra ID or Azure AD with @capgo/capacitor-social-login using the Auth Connect preset wrapper or direct OAuth2 configuration. ## Overview [Section titled “Overview”](#overview) Microsoft Entra ID is supported through: * The `azure` Auth Connect preset * Direct `oauth2` configuration against Microsoft identity endpoints ## Auth Connect preset example [Section titled “Auth Connect preset example”](#auth-connect-preset-example) ```typescript import { SocialLoginAuthConnect } from '@capgo/capacitor-social-login'; await SocialLoginAuthConnect.initialize({ authConnect: { azure: { tenantId: 'common', clientId: 'your-azure-client-id', redirectUrl: 'myapp://oauth/azure', }, }, }); const result = await SocialLoginAuthConnect.login({ provider: 'azure', }); ``` ## Direct OAuth2 example [Section titled “Direct OAuth2 example”](#direct-oauth2-example) ```typescript import { SocialLogin } from '@capgo/capacitor-social-login'; await SocialLogin.initialize({ oauth2: { azure: { appId: 'your-azure-client-id', authorizationBaseUrl: 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize', accessTokenEndpoint: 'https://login.microsoftonline.com/common/oauth2/v2.0/token', redirectUrl: 'myapp://oauth/azure', scope: 'openid profile email User.Read', pkceEnabled: true, resourceUrl: 'https://graph.microsoft.com/v1.0/me', }, }, }); const result = await SocialLogin.login({ provider: 'oauth2', options: { providerId: 'azure', }, }); ``` ## Notes [Section titled “Notes”](#notes) * Replace `common` with your tenant ID for single-tenant apps. * `resourceUrl` can point to Microsoft Graph when you want profile data immediately after login. ## Related docs [Section titled “Related docs”](#related-docs) * [OAuth2 and OIDC providers](/docs/plugins/social-login/oauth2/) * [Ionic Auth Connect migration](/docs/plugins/social-login/migrations/ionic-auth-connect/) * [Better Auth integration](/docs/plugins/social-login/better-auth/) # AWS Cognito > Integrate Amazon Cognito with @capgo/capacitor-social-login using the Auth Connect preset wrapper or direct OAuth2 configuration. ## Overview [Section titled “Overview”](#overview) Amazon Cognito is supported through the `cognito` Auth Connect preset. You can also configure it manually with direct OAuth2 endpoints. ## Auth Connect preset example [Section titled “Auth Connect preset example”](#auth-connect-preset-example) ```typescript import { SocialLoginAuthConnect } from '@capgo/capacitor-social-login'; await SocialLoginAuthConnect.initialize({ authConnect: { cognito: { domain: 'https://your-domain.auth.region.amazoncognito.com', clientId: 'your-cognito-client-id', redirectUrl: 'myapp://oauth/cognito', }, }, }); const result = await SocialLoginAuthConnect.login({ provider: 'cognito', }); ``` ## Direct OAuth2 example [Section titled “Direct OAuth2 example”](#direct-oauth2-example) ```typescript import { SocialLogin } from '@capgo/capacitor-social-login'; await SocialLogin.initialize({ oauth2: { cognito: { appId: 'your-cognito-client-id', authorizationBaseUrl: 'https://your-domain.auth.region.amazoncognito.com/oauth2/authorize', accessTokenEndpoint: 'https://your-domain.auth.region.amazoncognito.com/oauth2/token', redirectUrl: 'myapp://oauth/cognito', scope: 'openid profile email', pkceEnabled: true, resourceUrl: 'https://your-domain.auth.region.amazoncognito.com/oauth2/userInfo', logoutUrl: 'https://your-domain.auth.region.amazoncognito.com/logout', }, }, }); const result = await SocialLogin.login({ provider: 'oauth2', options: { providerId: 'cognito', }, }); ``` ## Related docs [Section titled “Related docs”](#related-docs) * [Ionic Auth Connect migration](/docs/plugins/social-login/migrations/ionic-auth-connect/) * [OAuth2 and OIDC providers](/docs/plugins/social-login/oauth2/) # GitHub > Integrate GitHub OAuth with @capgo/capacitor-social-login using the built-in generic OAuth2 provider. ## Overview [Section titled “Overview”](#overview) GitHub is supported through the built-in generic `oauth2` provider. ## Example [Section titled “Example”](#example) ```typescript import { SocialLogin } from '@capgo/capacitor-social-login'; await SocialLogin.initialize({ oauth2: { github: { appId: 'your-github-client-id', authorizationBaseUrl: 'https://github.com/login/oauth/authorize', accessTokenEndpoint: 'https://github.com/login/oauth/access_token', redirectUrl: 'myapp://oauth/github', scope: 'read:user user:email', pkceEnabled: true, resourceUrl: 'https://api.github.com/user', }, }, }); const result = await SocialLogin.login({ provider: 'oauth2', options: { providerId: 'github', }, }); console.log(result.result.accessToken?.token); console.log(result.result.resourceData); ``` ## Related docs [Section titled “Related docs”](#related-docs) * [OAuth2 and OIDC providers](/docs/plugins/social-login/oauth2/) * [Better Auth integration](/docs/plugins/social-login/better-auth/) # Keycloak > Integrate Keycloak with @capgo/capacitor-social-login using OIDC discovery or direct OAuth2 endpoint configuration. ## Overview [Section titled “Overview”](#overview) Keycloak works best through the built-in `oauth2` provider with OIDC discovery via `issuerUrl`. ## OIDC discovery example [Section titled “OIDC discovery example”](#oidc-discovery-example) ```typescript import { SocialLogin } from '@capgo/capacitor-social-login'; await SocialLogin.initialize({ oauth2: { keycloak: { issuerUrl: 'https://sso.example.com/realms/mobile', clientId: 'mobile-app', redirectUrl: 'myapp://oauth/keycloak', scope: 'openid profile email offline_access', pkceEnabled: true, }, }, }); const result = await SocialLogin.login({ provider: 'oauth2', options: { providerId: 'keycloak', }, }); ``` ## Direct endpoint example [Section titled “Direct endpoint example”](#direct-endpoint-example) ```typescript await SocialLogin.initialize({ oauth2: { keycloak: { appId: 'mobile-app', authorizationBaseUrl: 'https://sso.example.com/realms/mobile/protocol/openid-connect/auth', accessTokenEndpoint: 'https://sso.example.com/realms/mobile/protocol/openid-connect/token', redirectUrl: 'myapp://oauth/keycloak', scope: 'openid profile email offline_access', pkceEnabled: true, resourceUrl: 'https://sso.example.com/realms/mobile/protocol/openid-connect/userinfo', logoutUrl: 'https://sso.example.com/realms/mobile/protocol/openid-connect/logout', }, }, }); ``` ## Related docs [Section titled “Related docs”](#related-docs) * [OAuth2 and OIDC providers](/docs/plugins/social-login/oauth2/) * [Better Auth integration](/docs/plugins/social-login/better-auth/) # Okta > Integrate Okta with @capgo/capacitor-social-login using the Auth Connect preset wrapper or direct OAuth2 configuration. ## Overview [Section titled “Overview”](#overview) Okta is supported through: * The `okta` Auth Connect preset * Direct `oauth2` configuration against your Okta issuer ## Auth Connect preset example [Section titled “Auth Connect preset example”](#auth-connect-preset-example) ```typescript import { SocialLoginAuthConnect } from '@capgo/capacitor-social-login'; await SocialLoginAuthConnect.initialize({ authConnect: { okta: { issuer: 'https://dev-12345.okta.com/oauth2/default', clientId: 'your-okta-client-id', redirectUrl: 'myapp://oauth/okta', }, }, }); const result = await SocialLoginAuthConnect.login({ provider: 'okta', }); ``` ## Direct OAuth2 example [Section titled “Direct OAuth2 example”](#direct-oauth2-example) ```typescript import { SocialLogin } from '@capgo/capacitor-social-login'; await SocialLogin.initialize({ oauth2: { okta: { appId: 'your-okta-client-id', authorizationBaseUrl: 'https://your-domain.okta.com/oauth2/default/v1/authorize', accessTokenEndpoint: 'https://your-domain.okta.com/oauth2/default/v1/token', redirectUrl: 'myapp://oauth/okta', scope: 'openid profile email offline_access', pkceEnabled: true, resourceUrl: 'https://your-domain.okta.com/oauth2/default/v1/userinfo', }, }, }); const result = await SocialLogin.login({ provider: 'oauth2', options: { providerId: 'okta', }, }); ``` ## Related docs [Section titled “Related docs”](#related-docs) * [Ionic Auth Connect migration](/docs/plugins/social-login/migrations/ionic-auth-connect/) * [OAuth2 and OIDC providers](/docs/plugins/social-login/oauth2/) * [Better Auth integration](/docs/plugins/social-login/better-auth/) # OneLogin > Integrate OneLogin with @capgo/capacitor-social-login using the Auth Connect preset wrapper or direct OAuth2 configuration. ## Overview [Section titled “Overview”](#overview) OneLogin is supported through the `onelogin` Auth Connect preset. You can also configure it manually using direct OAuth2 endpoints. ## Auth Connect preset example [Section titled “Auth Connect preset example”](#auth-connect-preset-example) ```typescript import { SocialLoginAuthConnect } from '@capgo/capacitor-social-login'; await SocialLoginAuthConnect.initialize({ authConnect: { onelogin: { issuer: 'https://your-tenant.onelogin.com/oidc/2', clientId: 'your-onelogin-client-id', redirectUrl: 'myapp://oauth/onelogin', }, }, }); const result = await SocialLoginAuthConnect.login({ provider: 'onelogin', }); ``` ## Direct OAuth2 example [Section titled “Direct OAuth2 example”](#direct-oauth2-example) ```typescript import { SocialLogin } from '@capgo/capacitor-social-login'; await SocialLogin.initialize({ oauth2: { onelogin: { appId: 'your-onelogin-client-id', authorizationBaseUrl: 'https://your-tenant.onelogin.com/oidc/2/auth', accessTokenEndpoint: 'https://your-tenant.onelogin.com/oidc/2/token', redirectUrl: 'myapp://oauth/onelogin', scope: 'openid profile email', pkceEnabled: true, resourceUrl: 'https://your-tenant.onelogin.com/oidc/2/me', logoutUrl: 'https://your-tenant.onelogin.com/oidc/2/logout', }, }, }); const result = await SocialLogin.login({ provider: 'oauth2', options: { providerId: 'onelogin', }, }); ``` ## Related docs [Section titled “Related docs”](#related-docs) * [Ionic Auth Connect migration](/docs/plugins/social-login/migrations/ionic-auth-connect/) * [OAuth2 and OIDC providers](/docs/plugins/social-login/oauth2/) # Apple Sign-In Migration to @capgo/social-login > This comprehensive guide provides detailed instructions for transitioning from the @capacitor-community/apple-sign-in plugin to the @capgo/capacitor-social-login plugin, ensuring a smooth migration process and improved capabilities. ## Overview [Section titled “Overview”](#overview) This guide outlines the transition from the legacy `@capacitor-community/apple-sign-in` plugin to the modern `@capgo/capacitor-social-login` package. The new plugin provides a unified interface for multiple social authentication providers with improved TypeScript support and active maintenance. ## Installation [Section titled “Installation”](#installation) 1. Remove the old package: ```bash npm uninstall @capacitor-community/apple-sign-in ``` 2. Install the new package: ```bash npm install @capgo/capacitor-social-login npx cap sync ``` ## Code Changes [Section titled “Code Changes”](#code-changes) ### Import Changes [Section titled “Import Changes”](#import-changes) ```diff import { SignInWithApple } from '@capacitor-community/apple-sign-in'; import { SocialLogin } from '@capgo/capacitor-social-login'; ``` ### Initialization [Section titled “Initialization”](#initialization) **Key Change**: The new plugin requires an initialization step that wasn’t needed before. ```diff // No initialization needed in old package // For iOS: Basic configuration await SocialLogin.initialize({ apple: {} // Basic iOS configuration }); // For Android: Additional configuration required await SocialLogin.initialize({ apple: { clientId: 'YOUR_SERVICE_ID', // Service ID from Apple Developer Portal redirectUrl: 'https://your-backend.com/callback' // Your backend callback URL } }); ``` **Important Note**: For iOS, you provide basic configuration, while Android requires additional details including a Service ID and backend callback URL for web-based OAuth authentication. ### Sign In [Section titled “Sign In”](#sign-in) The login process simplifies from multiple parameters to a cleaner API: ```diff const result = await SignInWithApple.authorize({ clientId: 'com.your.app', redirectURI: 'https://your-app.com/callback', scopes: 'email name', state: '12345', nonce: 'nonce' }); const result = await SocialLogin.login({ provider: 'apple', options: { // Optional: Add scopes if needed scopes: ['email', 'name'], nonce: 'nonce' } }); ``` The new plugin uses `login()` with `provider: 'apple'` and optional scopes rather than passing individual configuration values like `clientId` and `redirectURI`. ### Response Type Changes [Section titled “Response Type Changes”](#response-type-changes) Results now include an `accessToken` object with expiration details and a structured `profile` section, replacing the flatter response format of the original package: ```typescript // Old response type interface AppleSignInResponse { response: { user: string; email: string | null; givenName: string | null; familyName: string | null; identityToken: string | null; authorizationCode: string | null; }; } // New response type interface SocialLoginResponse { provider: 'apple'; result: { accessToken: { token: string; expiresIn?: number; refreshToken?: string; } | null; idToken: string | null; profile: { user: string; email: string | null; givenName: string | null; familyName: string | null; }; }; } ``` ### New Capabilities [Section titled “New Capabilities”](#new-capabilities) The updated plugin introduces functionality that wasn’t available in the predecessor: **Checking Login Status** ```diff // Not available in old package const status = await SocialLogin.isLoggedIn({ provider: 'apple' }); ``` **Logout Functionality** ```diff // Not available in old package await SocialLogin.logout({ provider: 'apple' }); ``` These methods provide `isLoggedIn()` to verify authentication status and `logout()` functionality. ## Platform Specific Changes [Section titled “Platform Specific Changes”](#platform-specific-changes) ### iOS Setup [Section titled “iOS Setup”](#ios-setup) **iOS** maintains familiar setup procedures through Xcode capabilities: 1. The iOS setup remains largely the same. You still need to: * Enable “Sign In with Apple” capability in Xcode * Configure your app in the Apple Developer Portal * No additional code changes required for iOS ### Android Setup [Section titled “Android Setup”](#android-setup) **Android** now receives native support via web-based OAuth authentication: The new plugin provides Android support out of the box, but requires additional setup: 1. Create a Services ID in the Apple Developer Portal 2. Configure a web authentication endpoint 3. Set up your Android app to handle the OAuth flow 4. Backend service configuration is required For detailed Android setup instructions, please refer to the [Android Setup Guide](/docs/plugins/social-login/apple/android/). ## Key Advantages [Section titled “Key Advantages”](#key-advantages) The modernized package provides: 1. **Unified APIs** across multiple social providers (Google, Facebook, Apple) 2. **Improved TypeScript typing** with better type definitions 3. **Active community maintenance** compared to the deprecated version 4. **Built-in Android support** through web-based authentication 5. **Persistent login state management** 6. **Better error handling** with consistent error types ## Breaking Changes [Section titled “Breaking Changes”](#breaking-changes) 1. **Explicit initialization is now required** - no default configuration 2. **Response object structure has changed** - nested result format 3. **Android implementation requires a backend service** for OAuth 4. **Token refresh handling is different** - improved token management 5. **Error handling and error types have changed** - more detailed errors For more detailed setup instructions, please refer to the [official documentation](/docs/plugins/social-login/apple/general/). # Facebook Login Migration to @capgo/social-login > This detailed guide provides step-by-step instructions for transitioning from the @capacitor-community/facebook-login plugin to the @capgo/capacitor-social-login plugin, ensuring a smooth migration process with enhanced features. ## Overview [Section titled “Overview”](#overview) This guide provides comprehensive instructions for migrating from `@capacitor-community/facebook-login` to `@capgo/capacitor-social-login`. The new plugin modernizes Facebook authentication with a unified API that supports multiple social providers, improved TypeScript support, and enhanced capabilities. ## Installation [Section titled “Installation”](#installation) 1. Remove the old package: ```bash npm uninstall @capacitor-community/facebook-login ``` 2. Install the new package: ```bash npm install @capgo/capacitor-social-login npx cap sync ``` ## Code Changes [Section titled “Code Changes”](#code-changes) ### Import Changes [Section titled “Import Changes”](#import-changes) ```diff import { FacebookLogin } from '@capacitor-community/facebook-login'; import { SocialLogin } from '@capgo/capacitor-social-login'; ``` ### Initialization [Section titled “Initialization”](#initialization) **Key Change**: The new package requires explicit setup in your code: ```diff // Old package required no explicit initialization in code // Configuration was done only in native platforms // New package requires explicit initialization await SocialLogin.initialize({ facebook: { appId: 'YOUR_FACEBOOK_APP_ID', // Required for web and Android clientToken: 'YOUR_CLIENT_TOKEN' // Required for Android } }); ``` ### Login [Section titled “Login”](#login) The login method now accepts a provider parameter: ```diff const FACEBOOK_PERMISSIONS = ['email', 'public_profile']; const result = await FacebookLogin.login({ permissions: FACEBOOK_PERMISSIONS }); const result = await SocialLogin.login({ provider: 'facebook', options: { permissions: ['email', 'public_profile'], limitedLogin: false, nonce: 'optional_nonce' } }); ``` ### Response Type Changes [Section titled “Response Type Changes”](#response-type-changes) The response structure has been modernized with a more comprehensive profile object: ```typescript // Old response type interface FacebookLoginResponse { accessToken: { applicationId: string; userId: string; token: string; expires: string; }; recentlyGrantedPermissions: string[]; recentlyDeniedPermissions: string[]; } // New response type interface FacebookLoginResponse { provider: 'facebook'; result: { accessToken: { token: string; applicationId?: string; expires?: string; userId?: string; permissions?: string[]; declinedPermissions?: string[]; } | null; idToken: string | null; profile: { userID: string; email: string | null; friendIDs: string[]; birthday: string | null; ageRange: { min?: number; max?: number } | null; gender: string | null; location: { id: string; name: string } | null; hometown: { id: string; name: string } | null; profileURL: string | null; name: string | null; imageURL: string | null; }; }; } ``` **Key Differences**: * Response now includes a `provider` field identifying the authentication provider * More detailed `profile` object with additional user information * Consistent structure across all social login providers ### Checking Login Status [Section titled “Checking Login Status”](#checking-login-status) ```diff const result = await FacebookLogin.getCurrentAccessToken(); const isLoggedIn = result && result.accessToken; const status = await SocialLogin.isLoggedIn({ provider: 'facebook' }); const isLoggedIn = status.isLoggedIn; ``` ### Logout [Section titled “Logout”](#logout) ```diff await FacebookLogin.logout(); await SocialLogin.logout({ provider: 'facebook' }); ``` ## Platform Specific Changes [Section titled “Platform Specific Changes”](#platform-specific-changes) ### Android Setup [Section titled “Android Setup”](#android-setup) Configuration is now handled through the initialize method: ```diff // AndroidManifest.xml changes remain the same // strings.xml become irrelevant // Additionally initialize in your code: await SocialLogin.initialize({ facebook: { appId: 'your-app-id', clientToken: 'your-client-token' // New requirement } }); ``` **Important**: Client token is now required for Android authentication. ### iOS Setup [Section titled “iOS Setup”](#ios-setup) 1. The iOS setup in `AppDelegate.swift` remains the same: ```swift import FBSDKCoreKit // In application:didFinishLaunchingWithOptions: FBSDKCoreKit.ApplicationDelegate.shared.application( application, didFinishLaunchingWithOptions: launchOptions ) // In application:openURL:options: ApplicationDelegate.shared.application( app, open: url, sourceApplication: options[UIApplication.OpenURLOptionsKey.sourceApplication] as? String, annotation: options[UIApplication.OpenURLOptionsKey.annotation] ) ``` 2. The `Info.plist` configuration remains the same: ```xml CFBundleURLTypes CFBundleURLSchemes fb[APP_ID] FacebookAppID [APP_ID] FacebookClientToken [CLIENT_TOKEN] FacebookDisplayName [APP_NAME] LSApplicationQueriesSchemes fbapi fbauth fb-messenger-share-api fbauth2 fbshareextension ``` ## Breaking Changes [Section titled “Breaking Changes”](#breaking-changes) Summary of breaking changes when migrating: 1. **Explicit initialization is now required** - Must call `initialize()` before use 2. **Response object structure has changed significantly** - New nested result format with enhanced profile data 3. **Client token is now required for Android** - Additional configuration needed 4. **Different method names and parameter structures** - Provider-based approach 5. **Error handling and error types have changed** - More detailed error information ## Key Advantages [Section titled “Key Advantages”](#key-advantages) The new plugin provides: * **Unified API** across multiple social providers (Google, Apple, Facebook) * **Improved TypeScript support** with better type definitions * **Enhanced profile data** with more user information * **Active maintenance** and community support * **Consistent error handling** across all providers * **Better token management** with proper expiration handling For more detailed setup instructions, please refer to the [official documentation](/docs/plugins/social-login/facebook/). # Google Auth Migration to @capgo/social-login > This guide outlines transitioning from the older Google Auth plugin to the newer Capgo social login package, which unifies multiple social authentication providers. ## Overview [Section titled “Overview”](#overview) This guide provides comprehensive steps for migrating from `@codetrix-studio/capacitor-google-auth` to `@capgo/capacitor-social-login`, ensuring a smooth transition and improved authentication experience. The new plugin unifies multiple social authentication providers under a single, consistent API. ## Installation [Section titled “Installation”](#installation) 1. Remove the old package: ```bash npm uninstall @codetrix-studio/capacitor-google-auth ``` 2. Install the new package: ```bash npm install @capgo/capacitor-social-login npx cap sync ``` ## Important Changes in Google Auth Setup [Section titled “Important Changes in Google Auth Setup”](#important-changes-in-google-auth-setup) ### Web Client ID Requirement [Section titled “Web Client ID Requirement”](#web-client-id-requirement) **Critical Change**: The updated plugin requires using a Web Client ID across all platforms. You’ll need to: 1. Create a Web Client ID in Google Cloud Console if you don’t have one ([How to get the credentials](/docs/plugins/social-login/google/general/)) 2. Use this Web Client ID in the `webClientId` field for all platforms 3. For Android, you still need to create an Android Client ID with your SHA1, but this is only for verification purposes - the token won’t be used ([Android setup guide](/docs/plugins/social-login/google/android/)) ## Code Changes [Section titled “Code Changes”](#code-changes) ### Import Changes [Section titled “Import Changes”](#import-changes) ```diff import { GoogleAuth } from '@codetrix-studio/capacitor-google-auth'; import { SocialLogin } from '@capgo/capacitor-social-login'; ``` ### Initialization [Section titled “Initialization”](#initialization) The setup transforms from a simple `GoogleAuth.initialize()` call to a more structured `SocialLogin.initialize()` with nested Google configuration: ```diff GoogleAuth.initialize({ clientId: 'CLIENT_ID.apps.googleusercontent.com', scopes: ['profile', 'email'], grantOfflineAccess: true, }); await SocialLogin.initialize({ google: { webClientId: 'WEB_CLIENT_ID.apps.googleusercontent.com', // Use Web Client ID for all platforms iOSClientId: 'IOS_CLIENT_ID', // for iOS mode: 'offline' // replaces grantOfflineAccess } }); ``` ### Sign In [Section titled “Sign In”](#sign-in) The login method changes from `GoogleAuth.signIn()` to `SocialLogin.login()` with explicit provider specification: ```diff const user = await GoogleAuth.signIn(); const res = await SocialLogin.login({ provider: 'google', options: { scopes: ['email', 'profile'], forceRefreshToken: true // if you need refresh token } }); ``` ## Platform Specific Changes [Section titled “Platform Specific Changes”](#platform-specific-changes) ### Android [Section titled “Android”](#android) 1. Update your `MainActivity.java` ([Full Android setup guide](/docs/plugins/social-login/google/android/)): ```diff import ee.forgr.capacitor.social.login.GoogleProvider; import ee.forgr.capacitor.social.login.SocialLoginPlugin; import ee.forgr.capacitor.social.login.ModifiedMainActivityForSocialLoginPlugin; import com.getcapacitor.PluginHandle; import com.getcapacitor.Plugin; import android.content.Intent; import android.util.Log; public class MainActivity extends BridgeActivity { public class MainActivity extends BridgeActivity implements ModifiedMainActivityForSocialLoginPlugin { @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode >= GoogleProvider.REQUEST_AUTHORIZE_GOOGLE_MIN && requestCode < GoogleProvider.REQUEST_AUTHORIZE_GOOGLE_MAX) { PluginHandle pluginHandle = getBridge().getPlugin("SocialLogin"); if (pluginHandle == null) { Log.i("Google Activity Result", "SocialLogin login handle is null"); return; } Plugin plugin = pluginHandle.getInstance(); if (!(plugin instanceof SocialLoginPlugin)) { Log.i("Google Activity Result", "SocialLogin plugin instance is not SocialLoginPlugin"); return; } ((SocialLoginPlugin) plugin).handleGoogleLoginIntent(requestCode, data); } } public void IHaveModifiedTheMainActivityForTheUseWithSocialLoginPlugin() {} } ``` ### iOS [Section titled “iOS”](#ios) 1. No major changes needed in AppDelegate.swift ([iOS setup guide](/docs/plugins/social-login/google/ios/)) 2. Update your configuration in `capacitor.config.json`, we don’t use it in the new plugin: ```diff { "plugins": { "GoogleAuth": { "scopes": ["profile", "email"], "serverClientId": "xxxxxx-xxxxxxxxxxxxxxxxxx.apps.googleusercontent.com", "forceCodeForRefreshToken": true } } ``` ### Web [Section titled “Web”](#web) 1. Remove the Google Sign-In meta tags from your `index.html` if you were using them: ```diff ``` ## Response Type Changes [Section titled “Response Type Changes”](#response-type-changes) The authentication response now provides a structured object containing provider information, access tokens, ID tokens, and user profile data: ```typescript interface GoogleLoginResponse { provider: 'google'; result: { accessToken: { token: string; expires: string; // ... other token fields } | null; idToken: string | null; profile: { email: string | null; familyName: string | null; givenName: string | null; id: string | null; name: string | null; imageUrl: string | null; }; }; } ``` The response structure includes: * **provider**: Identifies the authentication provider (`'google'`) * **result.accessToken**: Access token details with expiration * **result.idToken**: ID token for authentication * **result.profile**: User profile information including email, name, and image URL ## Additional Capabilities [Section titled “Additional Capabilities”](#additional-capabilities) The new package supports multiple social authentication providers beyond Google: * [Apple Sign-In](/docs/plugins/social-login/apple/general/) * [Facebook Login](/docs/plugins/social-login/facebook/) This unified approach provides: * Consistent API across all providers * Improved TypeScript support * Better error handling * Active maintenance and community support Check the [main documentation](/docs/plugins/social-login/google/general/) for detailed setup instructions for these additional providers. # Ionic Auth Connect Migration to @capgo/capacitor-social-login > Migrate from Ionic Auth Connect to Capgo Social Login with OAuth2 and provider-native sign-in. ## Overview [Section titled “Overview”](#overview) Capgo Social Login replaces Ionic Auth Connect with a provider-native OAuth2 flow for Google, Apple, Facebook, and other identity providers. It supports multiple providers in one plugin and works across iOS, Android, and Web. ## Why this works [Section titled “Why this works”](#why-this-works) The plugin includes an Auth Connect compatibility wrapper named `SocialLoginAuthConnect`. It maps familiar Ionic Auth Connect provider IDs onto the built-in OAuth2 engine, so you can keep using names such as `auth0`, `azure`, and `okta`. ## Install [Section titled “Install”](#install) ```bash bun add @capgo/capacitor-social-login bunx cap sync ``` ## Replace your imports [Section titled “Replace your imports”](#replace-your-imports) ```typescript // Before import { AuthConnect } from '@ionic-enterprise/auth-connect'; // After import { SocialLoginAuthConnect } from '@capgo/capacitor-social-login'; ``` ## Initialize providers [Section titled “Initialize providers”](#initialize-providers) Use the `authConnect` presets when you want the same provider IDs that Ionic Auth Connect used: ```typescript await SocialLoginAuthConnect.initialize({ authConnect: { auth0: { domain: 'https://your-tenant.auth0.com', clientId: 'your-auth0-client-id', redirectUrl: 'myapp://oauth/auth0', audience: 'https://your-api.example.com', }, azure: { tenantId: 'common', clientId: 'your-azure-client-id', redirectUrl: 'myapp://oauth/azure', }, cognito: { domain: 'https://your-domain.auth.region.amazoncognito.com', clientId: 'your-cognito-client-id', redirectUrl: 'myapp://oauth/cognito', }, okta: { issuer: 'https://dev-12345.okta.com/oauth2/default', clientId: 'your-okta-client-id', redirectUrl: 'myapp://oauth/okta', }, onelogin: { issuer: 'https://your-tenant.onelogin.com/oidc/2', clientId: 'your-onelogin-client-id', redirectUrl: 'myapp://oauth/onelogin', }, }, }); ``` ## Supported provider IDs [Section titled “Supported provider IDs”](#supported-provider-ids) * `auth0` * `azure` * `cognito` * `okta` * `onelogin` ## Login, logout, and token access [Section titled “Login, logout, and token access”](#login-logout-and-token-access) ```typescript const result = await SocialLoginAuthConnect.login({ provider: 'auth0', }); const status = await SocialLoginAuthConnect.isLoggedIn({ provider: 'auth0', }); const code = await SocialLoginAuthConnect.getAuthorizationCode({ provider: 'auth0', }); await SocialLoginAuthConnect.logout({ provider: 'auth0', }); ``` ## Provider-specific preset examples [Section titled “Provider-specific preset examples”](#provider-specific-preset-examples) ### Auth0 preset example [Section titled “Auth0 preset example”](#auth0-preset-example) ```typescript await SocialLoginAuthConnect.initialize({ authConnect: { auth0: { domain: 'https://your-tenant.auth0.com', clientId: 'your-auth0-client-id', redirectUrl: 'myapp://oauth/auth0', audience: 'https://your-api.example.com', }, }, }); const auth0Result = await SocialLoginAuthConnect.login({ provider: 'auth0', }); console.log(auth0Result.result.idToken); ``` ### Azure preset example [Section titled “Azure preset example”](#azure-preset-example) ```typescript await SocialLoginAuthConnect.initialize({ authConnect: { azure: { tenantId: 'common', clientId: 'your-azure-client-id', redirectUrl: 'myapp://oauth/azure', }, }, }); const azureResult = await SocialLoginAuthConnect.login({ provider: 'azure', }); console.log(azureResult.result.resourceData); ``` ### Cognito preset example [Section titled “Cognito preset example”](#cognito-preset-example) ```typescript await SocialLoginAuthConnect.initialize({ authConnect: { cognito: { domain: 'https://your-domain.auth.region.amazoncognito.com', clientId: 'your-cognito-client-id', redirectUrl: 'myapp://oauth/cognito', }, }, }); const cognitoResult = await SocialLoginAuthConnect.login({ provider: 'cognito', }); console.log(cognitoResult.result.idToken); ``` ### Okta preset example [Section titled “Okta preset example”](#okta-preset-example) ```typescript await SocialLoginAuthConnect.initialize({ authConnect: { okta: { issuer: 'https://dev-12345.okta.com/oauth2/default', clientId: 'your-okta-client-id', redirectUrl: 'myapp://oauth/okta', }, }, }); const oktaResult = await SocialLoginAuthConnect.login({ provider: 'okta', }); console.log(oktaResult.result.resourceData); ``` ### OneLogin preset example [Section titled “OneLogin preset example”](#onelogin-preset-example) ```typescript await SocialLoginAuthConnect.initialize({ authConnect: { onelogin: { issuer: 'https://your-tenant.onelogin.com/oidc/2', clientId: 'your-onelogin-client-id', redirectUrl: 'myapp://oauth/onelogin', }, }, }); const oneloginResult = await SocialLoginAuthConnect.login({ provider: 'onelogin', }); console.log(oneloginResult.result.idToken); ``` ## Overriding endpoints [Section titled “Overriding endpoints”](#overriding-endpoints) Each preset creates a default OAuth2 configuration from `domain` or `issuer`. If your tenant uses custom endpoints, override them directly: ```typescript await SocialLoginAuthConnect.initialize({ authConnect: { onelogin: { issuer: 'https://your-tenant.onelogin.com/oidc/2', clientId: 'your-onelogin-client-id', redirectUrl: 'myapp://oauth/onelogin', authorizationBaseUrl: 'https://your-tenant.onelogin.com/oidc/2/auth', accessTokenEndpoint: 'https://your-tenant.onelogin.com/oidc/2/token', resourceUrl: 'https://your-tenant.onelogin.com/oidc/2/me', logoutUrl: 'https://your-tenant.onelogin.com/oidc/2/logout', }, }, }); ``` ## Direct OAuth2 configuration [Section titled “Direct OAuth2 configuration”](#direct-oauth2-configuration) If you do not want presets, configure the same providers directly in the generic OAuth2 docs: * [OAuth2 and OIDC provider guide](/docs/plugins/social-login/oauth2/) ## Migration notes [Section titled “Migration notes”](#migration-notes) 1. **The compatibility layer is OAuth2-based** It keeps the provider names, not Ionic’s native implementation details. 2. **Refresh tokens still depend on scopes** Request `offline_access` or the provider-specific equivalent when you need refresh tokens. 3. **Custom endpoints can override presets** If the preset is close but not exact, override only the endpoints that differ. 4. **Direct `oauth2` entries win** If you define both `authConnect.auth0` and `oauth2.auth0`, the direct `oauth2` config takes precedence. ## Related Documentation [Section titled “Related Documentation”](#related-documentation) * [Social Login getting started](/docs/plugins/social-login/getting-started/) * [OAuth2 and OIDC providers](/docs/plugins/social-login/oauth2/) * [Migrate from Ionic Auth Connect](/docs/upgrade/from-ionic-auth-connect/) * [Ionic enterprise plugins migration solution](/solutions/ionic-enterprise-plugins/) # V7 Migration Guide > Guide for migrating from earlier versions to V7 of @capgo/capacitor-social-login ## Introduction [Section titled “Introduction”](#introduction) Caution This guide is for the `V1`/`V7` version of the plugin for people who were still using this plugin with the `0.x.x` version. This guide will cover the following: * Migrating to the V1 version from the `main` version * Migrating to the V1 version from the `development` version ## Important changes in V1 [Section titled “Important changes in V1”](#important-changes-in-v1) V1 is just a port of the development version into main. It does, however, include a lot of important changes that are not available in the `main` V0 version. Those changes include: * Access scopes for Google login * Offline mode for Google login * Unification of the different implementations * Extensive testing was conducted to ensure that all implementations of the Google Provider behave in the same way between platforms ## Migration from the V0 main version [Section titled “Migration from the V0 main version”](#migration-from-the-v0-main-version) * Changes in the `MainActivity.java` for Android * Please follow the [Google Setup Guide](/docs/plugins/social-login/google/android/). Specifically, please search for `MainActivity.java` * Please add redirect urls in the Google Console. Without adding redirect urls, Google login will not work. * Again, please follow the [Google Setup Guide](/docs/plugins/social-login/google/android/). Specifically, please search for `Authorized redirect URIs` * Please ensure that you are not using `grantOfflineAccess` in the config. This feature is not supported in V1. * Please ensure that authentication works on all the platforms. ## Migration from the V0 development version [Section titled “Migration from the V0 development version”](#migration-from-the-v0-development-version) * Changes in the `MainActivity.java` for Android * Please follow the [Google Setup Guide](/docs/plugins/social-login/google/android/). Specifically, please search for `MainActivity.java`. In V1, you **HAVE TO** implement `ModifiedMainActivityForSocialLoginPlugin` in your main activity. This change is crucial for the plugin to work * Please add redirect urls in the Google Console. Without adding redirect urls, Google login will not work. * Again, please follow the [Google Setup Guide](/docs/plugins/social-login/google/android/). Specifically, please search for `Authorized redirect URIs` * Please ensure that types and variable names are correct. Please know that types and variables might not match between development and V1. * Please ensure that authentication works on all the platforms. # Generic OAuth2 Providers > Configure GitHub, Azure AD, Auth0, Okta, Keycloak, and other OAuth2 or OIDC providers with the Capgo Social Login plugin. ## Introduction [Section titled “Introduction”](#introduction) The Capgo Social Login plugin includes a built-in OAuth2 and OpenID Connect engine. You can use it to connect any standards-based identity provider, including: * GitHub * Azure AD / Microsoft Entra ID * Auth0 * Okta * Keycloak * Custom OAuth2 or OIDC servers The `oauth2` configuration is multi-provider by design. You can register several providers at once and then select one at login time with `providerId`. ## What you need [Section titled “What you need”](#what-you-need) Before you configure a provider, collect: * Your OAuth client ID * A redirect URL that matches your app scheme or web callback URL * An authorization endpoint * A token endpoint for authorization code flow, or an `issuerUrl` for OIDC discovery * The scopes your app needs, such as `openid profile email` ## Multi-provider configuration [Section titled “Multi-provider configuration”](#multi-provider-configuration) Use `SocialLogin.initialize()` once during app startup and register every provider you need: ```typescript import { SocialLogin } from '@capgo/capacitor-social-login'; await SocialLogin.initialize({ oauth2: { github: { appId: 'your-github-client-id', authorizationBaseUrl: 'https://github.com/login/oauth/authorize', accessTokenEndpoint: 'https://github.com/login/oauth/access_token', redirectUrl: 'myapp://oauth/github', scope: 'read:user user:email', pkceEnabled: true, resourceUrl: 'https://api.github.com/user', }, azure: { appId: 'your-azure-client-id', authorizationBaseUrl: 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize', accessTokenEndpoint: 'https://login.microsoftonline.com/common/oauth2/v2.0/token', redirectUrl: 'myapp://oauth/azure', scope: 'openid profile email User.Read', pkceEnabled: true, resourceUrl: 'https://graph.microsoft.com/v1.0/me', }, auth0: { issuerUrl: 'https://your-tenant.auth0.com', appId: 'your-auth0-client-id', redirectUrl: 'myapp://oauth/auth0', scope: 'openid profile email offline_access', pkceEnabled: true, additionalParameters: { audience: 'https://your-api.example.com', }, }, }, }); ``` ## OIDC discovery and aliases [Section titled “OIDC discovery and aliases”](#oidc-discovery-and-aliases) If your provider exposes an OpenID Connect discovery document, `issuerUrl` is the simplest setup: ```typescript await SocialLogin.initialize({ oauth2: { keycloak: { issuerUrl: 'https://sso.example.com/realms/mobile', clientId: 'mobile-app', redirectUrl: 'myapp://oauth/keycloak', scope: 'openid profile email offline_access', pkceEnabled: true, }, }, }); ``` The plugin also supports common OAuth and OIDC aliases: * `clientId` as an alias of `appId` * `authorizationEndpoint` as an alias of `authorizationBaseUrl` * `tokenEndpoint` as an alias of `accessTokenEndpoint` * `endSessionEndpoint` as an alias of `logoutUrl` * `scopes` as an alias of `scope` Also available: * `additionalParameters` for auth request overrides * `additionalTokenParameters` for token exchange overrides * `additionalResourceHeaders` for custom resource endpoint headers * `additionalLogoutParameters` and `postLogoutRedirectUrl` for logout flows * `loginHint`, `prompt`, and `iosPrefersEphemeralSession` ## Auth Connect-compatible presets [Section titled “Auth Connect-compatible presets”](#auth-connect-compatible-presets) If you are migrating from Ionic Auth Connect and want to keep the same provider names, use `SocialLoginAuthConnect`. ```typescript import { SocialLoginAuthConnect } from '@capgo/capacitor-social-login'; await SocialLoginAuthConnect.initialize({ authConnect: { auth0: { domain: 'https://your-tenant.auth0.com', clientId: 'your-auth0-client-id', redirectUrl: 'myapp://oauth/auth0', audience: 'https://your-api.example.com', }, azure: { tenantId: 'common', clientId: 'your-azure-client-id', redirectUrl: 'myapp://oauth/azure', }, okta: { issuer: 'https://dev-12345.okta.com/oauth2/default', clientId: 'your-okta-client-id', redirectUrl: 'myapp://oauth/okta', }, }, }); ``` Supported preset provider IDs: * `auth0` * `azure` * `cognito` * `okta` * `onelogin` If a provider needs custom endpoints, either override them in the preset or bypass presets and configure the provider directly in `oauth2`. ## Configuration options [Section titled “Configuration options”](#configuration-options) | Option | Type | Required | Description | | ------------------------------------------------ | ------------------------ | -------- | ---------------------------------------------- | | `appId` / `clientId` | string | Yes | OAuth2 client identifier | | `issuerUrl` | string | No | OIDC discovery base URL | | `authorizationBaseUrl` / `authorizationEndpoint` | string | Yes\* | Authorization endpoint URL | | `accessTokenEndpoint` / `tokenEndpoint` | string | No\* | Token endpoint URL | | `redirectUrl` | string | Yes | Callback URL | | `scope` / `scopes` | string / string\[] | No | Requested scopes | | `pkceEnabled` | boolean | No | Defaults to `true` | | `responseType` | `'code'` or `'token'` | No | Defaults to `'code'` | | `resourceUrl` | string | No | User info or resource endpoint | | `logoutUrl` / `endSessionEndpoint` | string | No | Logout or end-session URL | | `postLogoutRedirectUrl` | string | No | Redirect URL after logout | | `additionalParameters` | `Record` | No | Extra auth request params | | `additionalTokenParameters` | `Record` | No | Extra token request params | | `additionalResourceHeaders` | `Record` | No | Extra headers for `resourceUrl` | | `additionalLogoutParameters` | `Record` | No | Extra logout params | | `loginHint` | string | No | Shortcut for `additionalParameters.login_hint` | | `prompt` | string | No | Shortcut for `additionalParameters.prompt` | | `iosPrefersEphemeralSession` | boolean | No | Prefer ephemeral browser session on iOS | | `logsEnabled` | boolean | No | Enable verbose debug logging | `authorizationBaseUrl` and `accessTokenEndpoint` are only optional when `issuerUrl` is enough for discovery. Explicit endpoints always win over discovered values. ## Using OAuth2 login [Section titled “Using OAuth2 login”](#using-oauth2-login) ### Login [Section titled “Login”](#login) ```typescript const result = await SocialLogin.login({ provider: 'oauth2', options: { providerId: 'github', scope: 'read:user user:email', loginHint: 'user@example.com', }, }); ``` ### Redirect flow on web [Section titled “Redirect flow on web”](#redirect-flow-on-web) Use `flow: 'redirect'` if you want a full-page redirect instead of a popup: ```typescript await SocialLogin.login({ provider: 'oauth2', options: { providerId: 'auth0', flow: 'redirect', }, }); ``` On the page that receives the callback, parse the login result: ```typescript const result = await SocialLogin.handleRedirectCallback(); if (result?.provider === 'oauth2') { console.log(result.result.providerId); } ``` ### Login status and logout [Section titled “Login status and logout”](#login-status-and-logout) ```typescript const status = await SocialLogin.isLoggedIn({ provider: 'oauth2', providerId: 'github', }); await SocialLogin.logout({ provider: 'oauth2', providerId: 'github', }); ``` ### Refresh tokens [Section titled “Refresh tokens”](#refresh-tokens) ```typescript await SocialLogin.refresh({ provider: 'oauth2', options: { providerId: 'github', }, }); const refreshed = await SocialLogin.refreshToken({ provider: 'oauth2', providerId: 'github', refreshToken: 'existing-refresh-token', }); ``` `refresh()` uses the refresh token stored by the plugin. `refreshToken()` lets you pass a refresh token yourself and returns the fresh OAuth2 response. ### Get the current access token [Section titled “Get the current access token”](#get-the-current-access-token) ```typescript const code = await SocialLogin.getAuthorizationCode({ provider: 'oauth2', providerId: 'github', }); console.log(code.accessToken); ``` ## Provider-specific examples [Section titled “Provider-specific examples”](#provider-specific-examples) ### GitHub example [Section titled “GitHub example”](#github-example) Use GitHub when you want a simple OAuth app flow and basic profile data: ```typescript await SocialLogin.initialize({ oauth2: { github: { appId: 'your-github-client-id', authorizationBaseUrl: 'https://github.com/login/oauth/authorize', accessTokenEndpoint: 'https://github.com/login/oauth/access_token', redirectUrl: 'myapp://oauth/github', scope: 'read:user user:email', pkceEnabled: true, resourceUrl: 'https://api.github.com/user', }, }, }); const githubResult = await SocialLogin.login({ provider: 'oauth2', options: { providerId: 'github', }, }); console.log(githubResult.result.accessToken?.token); console.log(githubResult.result.resourceData); ``` ### Azure AD / Microsoft Entra ID example [Section titled “Azure AD / Microsoft Entra ID example”](#azure-ad--microsoft-entra-id-example) Use Azure when you need Microsoft Graph data such as the user profile: ```typescript await SocialLogin.initialize({ oauth2: { azure: { appId: 'your-azure-client-id', authorizationBaseUrl: 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize', accessTokenEndpoint: 'https://login.microsoftonline.com/common/oauth2/v2.0/token', redirectUrl: 'myapp://oauth/azure', scope: 'openid profile email User.Read', pkceEnabled: true, resourceUrl: 'https://graph.microsoft.com/v1.0/me', }, }, }); const azureResult = await SocialLogin.login({ provider: 'oauth2', options: { providerId: 'azure', }, }); console.log(azureResult.result.idToken); console.log(azureResult.result.resourceData); ``` ### Auth0 example [Section titled “Auth0 example”](#auth0-example) Auth0 is a good fit when you need OIDC plus a custom API audience: ```typescript await SocialLogin.initialize({ oauth2: { auth0: { appId: 'your-auth0-client-id', authorizationBaseUrl: 'https://your-tenant.auth0.com/authorize', accessTokenEndpoint: 'https://your-tenant.auth0.com/oauth/token', redirectUrl: 'myapp://oauth/auth0', scope: 'openid profile email offline_access', pkceEnabled: true, additionalParameters: { audience: 'https://your-api.example.com', }, }, }, }); const auth0Result = await SocialLogin.login({ provider: 'oauth2', options: { providerId: 'auth0', flow: 'redirect', }, }); ``` If you use redirect flow on web, read the result back on the callback page: ```typescript const auth0Result = await SocialLogin.handleRedirectCallback(); if (auth0Result?.provider === 'oauth2') { console.log(auth0Result.result.idToken); } ``` ### Okta example [Section titled “Okta example”](#okta-example) ```typescript await SocialLogin.initialize({ oauth2: { okta: { appId: 'your-okta-client-id', authorizationBaseUrl: 'https://your-domain.okta.com/oauth2/default/v1/authorize', accessTokenEndpoint: 'https://your-domain.okta.com/oauth2/default/v1/token', redirectUrl: 'myapp://oauth/okta', scope: 'openid profile email offline_access', pkceEnabled: true, resourceUrl: 'https://your-domain.okta.com/oauth2/default/v1/userinfo', }, }, }); const oktaResult = await SocialLogin.login({ provider: 'oauth2', options: { providerId: 'okta', }, }); console.log(oktaResult.result.resourceData); ``` ### Keycloak example [Section titled “Keycloak example”](#keycloak-example) Use discovery when your provider publishes `/.well-known/openid-configuration`: ```typescript await SocialLogin.initialize({ oauth2: { keycloak: { issuerUrl: 'https://sso.example.com/realms/mobile', clientId: 'mobile-app', redirectUrl: 'myapp://oauth/keycloak', scope: 'openid profile email offline_access', pkceEnabled: true, }, }, }); const keycloakResult = await SocialLogin.login({ provider: 'oauth2', options: { providerId: 'keycloak', }, }); console.log(keycloakResult.result.idToken); ``` ## OAuth2 response shape [Section titled “OAuth2 response shape”](#oauth2-response-shape) Successful OAuth2 logins return: | Field | Description | | -------------- | ------------------------------------------------ | | `providerId` | The configured provider key used for the login | | `accessToken` | Access token payload or `null` | | `idToken` | OIDC ID token if the provider returned one | | `refreshToken` | Refresh token if the requested scopes allowed it | | `resourceData` | Raw JSON fetched from `resourceUrl` | | `scope` | Granted scopes | | `tokenType` | Usually `bearer` | | `expiresIn` | Token lifetime in seconds | ## Provider setup reference [Section titled “Provider setup reference”](#provider-setup-reference) ### GitHub [Section titled “GitHub”](#github) 1. **Create an OAuth app** Open [GitHub Developer Settings](https://github.com/settings/developers) and create a new OAuth App. 2. **Set the callback URL** Use your app redirect URL, for example `myapp://oauth/github`. 3. **Configure the plugin** ```typescript await SocialLogin.initialize({ oauth2: { github: { appId: 'your-github-client-id', authorizationBaseUrl: 'https://github.com/login/oauth/authorize', accessTokenEndpoint: 'https://github.com/login/oauth/access_token', redirectUrl: 'myapp://oauth/github', scope: 'read:user user:email', pkceEnabled: true, resourceUrl: 'https://api.github.com/user', }, }, }); ``` ### Azure AD / Microsoft Entra ID [Section titled “Azure AD / Microsoft Entra ID”](#azure-ad--microsoft-entra-id) 1. **Register an app** Go to Azure Portal, open `App registrations`, and create a native or mobile app registration. 2. **Add the redirect URI** Add a mobile or desktop redirect URI that matches your app callback URL. 3. **Configure the plugin** ```typescript await SocialLogin.initialize({ oauth2: { azure: { appId: 'your-azure-client-id', authorizationBaseUrl: 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize', accessTokenEndpoint: 'https://login.microsoftonline.com/common/oauth2/v2.0/token', redirectUrl: 'myapp://oauth/azure', scope: 'openid profile email User.Read', pkceEnabled: true, resourceUrl: 'https://graph.microsoft.com/v1.0/me', }, }, }); ``` Note Replace `common` with your tenant ID if your app is single-tenant. ### Auth0 [Section titled “Auth0”](#auth0) 1. **Create a native application** Open the [Auth0 Dashboard](https://manage.auth0.com) and create a Native app. 2. **Set allowed callback URLs** Add the exact redirect URL used by your Capacitor app. 3. **Configure the plugin** ```typescript await SocialLogin.initialize({ oauth2: { auth0: { appId: 'your-auth0-client-id', authorizationBaseUrl: 'https://your-tenant.auth0.com/authorize', accessTokenEndpoint: 'https://your-tenant.auth0.com/oauth/token', redirectUrl: 'myapp://oauth/auth0', scope: 'openid profile email offline_access', pkceEnabled: true, additionalParameters: { audience: 'https://your-api.example.com', }, logoutUrl: 'https://your-tenant.auth0.com/v2/logout', }, }, }); ``` ### Okta [Section titled “Okta”](#okta) 1. **Create an OIDC native app** In Okta Admin Console, create an OIDC Native Application. 2. **Add your redirect URI** Register the exact callback URL used by your app. 3. **Configure the plugin** ```typescript await SocialLogin.initialize({ oauth2: { okta: { appId: 'your-okta-client-id', authorizationBaseUrl: 'https://your-domain.okta.com/oauth2/default/v1/authorize', accessTokenEndpoint: 'https://your-domain.okta.com/oauth2/default/v1/token', redirectUrl: 'myapp://oauth/okta', scope: 'openid profile email offline_access', pkceEnabled: true, resourceUrl: 'https://your-domain.okta.com/oauth2/default/v1/userinfo', }, }, }); ``` ### Keycloak and custom OIDC providers [Section titled “Keycloak and custom OIDC providers”](#keycloak-and-custom-oidc-providers) If your provider supports OpenID Connect discovery, prefer `issuerUrl`: ```typescript await SocialLogin.initialize({ oauth2: { keycloak: { issuerUrl: 'https://sso.example.com/realms/mobile', clientId: 'mobile-app', redirectUrl: 'myapp://oauth/keycloak', scope: 'openid profile email offline_access', pkceEnabled: true, }, }, }); ``` If discovery is not available, configure the authorization and token endpoints manually. ## Platform-specific notes [Section titled “Platform-specific notes”](#platform-specific-notes) ### iOS [Section titled “iOS”](#ios) * The plugin uses `ASWebAuthenticationSession`. * Set `iosPrefersEphemeralSession: true` if you want a private browser session with no shared cookies. ### Android [Section titled “Android”](#android) * OAuth redirects return through your app scheme and host. * Make sure the provider callback URL exactly matches your Android deep link setup. * The plugin already handles the OAuth activity. Only add custom intent filters if your app needs a different redirect pattern. ### Web [Section titled “Web”](#web) * Popup flow is the default and works well for single-page apps. * Redirect flow is better when the provider blocks popups or your auth rules require top-level navigation. * Some providers block direct browser token exchange with CORS. In those cases, use a backend exchange or a provider setup that allows public clients. ## Security best practices [Section titled “Security best practices”](#security-best-practices) 1. **Use PKCE** Keep `pkceEnabled: true` for public clients. 2. **Prefer authorization code flow** `responseType: 'code'` is safer than implicit flow. 3. **Validate tokens on your backend** Decode and verify issuer, audience, expiration, and signature server-side. 4. **Store refresh tokens securely** For native apps, pair this plugin with [@capgo/capacitor-persistent-account](https://github.com/Cap-go/capacitor-persistent-account). 5. **Use HTTPS everywhere** Production auth endpoints and logout endpoints should always use HTTPS. ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) ### `providerId is required` [Section titled “providerId is required”](#providerid-is-required) Every OAuth2 method needs the configured provider key: ```typescript await SocialLogin.login({ provider: 'oauth2', options: { providerId: 'github' }, }); ``` ### `OAuth2 provider "xxx" not configured` [Section titled “OAuth2 provider "xxx" not configured”](#oauth2-provider-xxx-not-configured) Call `SocialLogin.initialize()` before login and make sure the `providerId` matches the object key under `oauth2`. ### Redirect URL mismatch [Section titled “Redirect URL mismatch”](#redirect-url-mismatch) * Compare the configured redirect URL in your app and provider dashboard character by character. * Watch for trailing slashes, scheme mismatches, and different hosts. * Make sure mobile app URL schemes are registered before testing on device. ### No refresh token returned [Section titled “No refresh token returned”](#no-refresh-token-returned) Most providers only return refresh tokens when you request scopes like `offline_access` or explicitly force consent. Review the provider-specific policy. ### Debugging token exchange [Section titled “Debugging token exchange”](#debugging-token-exchange) Enable `logsEnabled: true` on the provider config to inspect generated URLs and token exchange details. ## Related docs [Section titled “Related docs”](#related-docs) * [Social Login getting started](/docs/plugins/social-login/getting-started/) * [Ionic Auth Connect migration](/docs/plugins/social-login/migrations/ionic-auth-connect/) # Supabase Apple Login on Android > Learn how to set up Apple Sign-In with Supabase Authentication on Android using the Capacitor Social Login plugin. ## Prerequisites [Section titled “Prerequisites”](#prerequisites) This guide will help you integrate Apple Sign-In with Supabase Authentication on Android. It is assumed that you have already completed: * the [Supabase Apple Login - General Setup](./general/). Important Apple Sign-In on Android requires a backend server because Apple doesn’t provide native Android support. We’ll use a Supabase Edge Function as the backend. ## Step 1: Deploy the Backend Edge Function [Section titled “Step 1: Deploy the Backend Edge Function”](#step-1-deploy-the-backend-edge-function) First, we need to deploy the Supabase Edge Function that will handle the Apple OAuth callback. 1. **Navigate to your Supabase project directory** ```bash cd your-project/supabase ``` 2. **Create the edge function** (if it doesn’t exist) ```bash supabase functions new apple-signin-callback ``` 3. **Copy the edge function code** The complete edge function implementation is available in the [example app](https://github.com/Cap-go/capacitor-social-login/tree/main/example-app/supabase/functions/apple-signin-callback). Copy the following files to your project: * `supabase/functions/apple-signin-callback/index.ts` - Main edge function code * `supabase/functions/apple-signin-callback/deno.json` - Import map for dependencies (includes `jose` library for JWT signing) 4. **Configure JWT verification** The Apple OAuth callback endpoint must be public (no authentication required) because Apple will redirect to it. Update your `supabase/config.toml` file: ```toml [functions.apple-signin-callback] enabled = true verify_jwt = false # Important: Set to false for public OAuth callback import_map = "./functions/apple-signin-callback/deno.json" entrypoint = "./functions/apple-signin-callback/index.ts" ``` Important Setting `verify_jwt = false` makes this endpoint public. This is required because Apple’s OAuth redirect doesn’t include Supabase authentication headers. The endpoint validates the OAuth flow itself. 5. **Deploy the function** ```bash supabase functions deploy apple-signin-callback ``` 6. **Get your function URL** After deployment, you’ll get a URL like: ```plaintext https://your-project-ref.supabase.co/functions/v1/apple-signin-callback ``` If you cannot find it, you can do the following: 1. Open `https://supabase.com/dashboard/project/YOUR_PROJECT_REF/functions` 2. Click on the `apple-signin-callback` function URL to copy it. ![Supabase Functions Apple Sign-In Callback](/social-login-assets/supabase_functions_apple_signin_callback.webp) Save this URL You’ll need this URL in the next step when configuring Apple Developer Portal. ## Step 2: Configure Apple Developer Portal [Section titled “Step 2: Configure Apple Developer Portal”](#step-2-configure-apple-developer-portal) Now we need to configure Apple Developer Portal with the backend URL and get all the required values. Function URL Make sure you have your Supabase Edge Function URL from Step 1 before proceeding. You’ll need it to configure the Return URL in Apple Developer Portal. 1. **Follow the Apple Login Android Setup Guide** Complete the [Apple Login Android Setup guide](/docs/plugins/social-login/apple/android/) to: * Create a Service ID * Generate a private key (.p8 file) * Get your Team ID and Key ID * Configure the Return URL 2. **Set the Return URL in Apple Developer Portal** When configuring the Return URL in Apple Developer Portal (step 6.9 of the Apple guide), use your Supabase Edge Function URL: ```plaintext https://your-project-ref.supabase.co/functions/v1/apple-signin-callback ``` Important: Use Supabase Edge Function URL **Do NOT** use the redirect URL from the [Apple Login Android Setup guide](/docs/plugins/social-login/apple/android/). That guide uses a custom backend server URL. For Supabase integration, you **must** use your Supabase Edge Function URL instead. Exact Match Required The Return URL must match **exactly** what you configure here. Apple is very strict about redirect URI matching. 3. **Collect all required values** After completing the Apple setup guide, you should have: * **APPLE\_TEAM\_ID**: Your Apple Developer Team ID * **APPLE\_KEY\_ID**: The Key ID from Apple Developer Portal * **APPLE\_PRIVATE\_KEY**: Your .p8 private key file (needs to be base64 encoded) * **ANDROID\_SERVICE\_ID**: Your Apple Service ID (e.g., `com.example.app.service`) * **BASE\_REDIRECT\_URL**: Your deep link URL (e.g., `capgo-demo-app://path`) Deep Link URL The `BASE_REDIRECT_URL` is the deep link scheme configured in your `AndroidManifest.xml`. This is where the backend will redirect after authentication. The value of your deep link is dependent on the `android:scheme="capgo-demo-app"` code. If you have set `android:scheme="capgo-demo-app"` in your `AndroidManifest.xml`, then your deep link will be `capgo-demo-app://path`. ## Step 3: Set Supabase Secrets [Section titled “Step 3: Set Supabase Secrets”](#step-3-set-supabase-secrets) Now we need to configure the environment variables (secrets) for the Supabase Edge Function. 1. **Encode your private key** First, encode your Apple private key (.p8 file) to base64: ```bash base64 -i AuthKey_XXXXX.p8 ``` Copy the entire output (it’s a single long string). 2. **Set secrets using Supabase CLI** ```bash supabase secrets set APPLE_TEAM_ID=your_team_id supabase secrets set APPLE_KEY_ID=your_key_id supabase secrets set APPLE_PRIVATE_KEY=your_base64_encoded_key supabase secrets set ANDROID_SERVICE_ID=your.service.id supabase secrets set BASE_REDIRECT_URL=your-app://path supabase secrets set APPLE_REDIRECT_URI=https://your-project-ref.supabase.co/functions/v1/apple-signin-callback ``` Replace Placeholders Replace all the placeholder values with your actual values from Step 2. 3. **Alternative: Set secrets in Supabase Dashboard** If you prefer using the dashboard: 1. Go to your Supabase project dashboard 2. Navigate to **Edge Functions** → **Settings** → **Secrets** 3. Add each secret variable with its value Android App Configuration The [Apple Login Android Setup guide](/docs/plugins/social-login/apple/android/) already covers configuring your Android app: * Adding the deep link intent filter to `AndroidManifest.xml` * Adding the `onNewIntent` handler to `MainActivity.java` Make sure you’ve completed those steps before proceeding. The deep link scheme you configure there will be your `BASE_REDIRECT_URL` in Step 3. ## Step 4: Use the Authentication Helper [Section titled “Step 4: Use the Authentication Helper”](#step-4-use-the-authentication-helper) Now you can use the authentication helper in your app code. ### Implementation [Section titled “Implementation”](#implementation) The complete implementation is available in the [example app’s `supabaseAuthUtils.ts`](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/supabaseAuthUtils.ts) file. ### Using the Authentication Helper [Section titled “Using the Authentication Helper”](#using-the-authentication-helper) ```typescript import { authenticateWithAppleSupabase } from './supabaseAuthUtils'; const result = await authenticateWithAppleSupabase(); if (result.success) { console.log('Signed in:', result.user); // Navigate to your authenticated area } else { console.error('Error:', result.error); } ``` ### Update the Helper Function [Section titled “Update the Helper Function”](#update-the-helper-function) When using the `authenticateWithAppleSupabase` helper function, you **must** update the following values to match your configuration: 1. **Update `redirectUrl`** - Set this to your Supabase Edge Function URL: ```typescript const redirectUrl = platform === 'android' ? 'https://your-project-ref.supabase.co/functions/v1/apple-signin-callback' : undefined; ``` 2. **Update `clientId`** - Set this to your Apple Service ID: ```typescript await SocialLogin.initialize({ apple: { clientId: isIOS ? undefined // iOS uses bundle ID automatically : 'your.service.id.here', // Your Apple Service ID for Android redirectUrl: redirectUrl, }, }); ``` Important Replace `'your.service.id.here'` with your actual Apple Service ID (the same value you used for `ANDROID_SERVICE_ID` in Step 3). See the [complete implementation](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/supabaseAuthUtils.ts) for reference. ## Step 5: Test the Integration [Section titled “Step 5: Test the Integration”](#step-5-test-the-integration) 1. **Build and run your Android app** ```bash npx cap sync android npx cap run android ``` 2. **Test the authentication flow** * Tap the “Sign in with Apple” button * You should see the Apple OAuth page in a browser * After authenticating, you should be redirected back to your app * Check the console logs for any errors 3. **Verify the flow** The complete flow should be: 1. User taps “Sign in with Apple” 2. App opens browser with Apple OAuth 3. User authenticates with Apple 4. Apple redirects to: `https://your-project-ref.supabase.co/functions/v1/apple-signin-callback` 5. Edge function exchanges code for tokens 6. Edge function redirects to: `your-app://path?id_token=...&access_token=...` 7. Android app receives the deep link and processes the identity token 8. App signs in to Supabase with the identity token ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) If authentication fails: * **Redirect URI mismatch**: Verify the Return URL in Apple Developer Portal matches exactly with `APPLE_REDIRECT_URI` secret * **Deep link not working**: Check that `AndroidManifest.xml` intent filter matches your `BASE_REDIRECT_URL` * **Missing secrets**: Verify all secrets are set correctly using `supabase secrets list` * **Token exchange fails**: Check edge function logs in Supabase Dashboard for detailed error messages * **App doesn’t receive callback**: Ensure `onNewIntent` is properly implemented in MainActivity * Review the [example app code](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/supabaseAuthUtils.ts) for reference ## How It Works [Section titled “How It Works”](#how-it-works) On Android, Apple Sign-In uses an OAuth redirect flow: 1. **Initialization**: The plugin is initialized with your Service ID and backend redirect URL 2. **OAuth Flow**: Opens a browser/Chrome Custom Tab with Apple’s OAuth page 3. **Backend Callback**: Apple redirects to your Supabase Edge Function with an authorization code 4. **Token Exchange**: The edge function exchanges the code for tokens using Apple’s token endpoint 5. **Deep Link Redirect**: The edge function redirects back to your app with the identity token 6. **Supabase Authentication**: The app receives the token and signs in to Supabase This flow is necessary because Apple doesn’t provide native Android support for Sign in with Apple. # Supabase Apple Login - General Setup > Learn how to integrate Apple Sign-In with Supabase Authentication using the Capacitor Social Login plugin. ## Overview [Section titled “Overview”](#overview) This guide will help you integrate Apple Sign-In with Supabase Authentication. Apple Sign-In provides a secure, privacy-focused authentication method that works across iOS, Android, and Web platforms. ## Prerequisites [Section titled “Prerequisites”](#prerequisites) Before starting, ensure you have: 1. [Created a Supabase project](https://database.new/) 2. Read the [Apple Login General Setup](/docs/plugins/social-login/apple/general/) guide to setup Apple OAuth credentials 3. Followed the respective platform-specific guides to setup Apple OAuth credentials for your target platform: * [Android Setup](/docs/plugins/social-login/apple/android/) * [iOS Setup](/docs/plugins/social-login/apple/ios/) * [Web Setup](/docs/plugins/social-login/apple/web/) Note Before starting the Supabase tutorial, you need to generate the client IDs for the platforms you plan to use. For iOS, the client ID is the same as your app ID. For Android and Web, the client ID is the same as your service ID. You will use them in step 7 of this guide. ## Enabling Apple OAuth provider in Supabase [Section titled “Enabling Apple OAuth provider in Supabase”](#enabling-apple-oauth-provider-in-supabase) 1. Go to your [Supabase Dashboard](https://app.supabase.com/) 2. Click on your project ![Supabase Project Selector](/social-login-assets/supabase_project_select.webp) 3. Do go to the `Authentication` menu ![Supabase Authentication Menu](/social-login-assets/supabase_authentication_menu.webp) 4. Click on the `Providers` tab ![Supabase Providers Tab](/social-login-assets/supabase_providers_tab.webp) 5. Find the `Apple` provider ![Supabase Apple Provider](/social-login-assets/supabase_apple_provider.webp) 6. Enable the `Apple` provider ![Supabase Apple Provider Enable](/social-login-assets/supabase_apple_provider_enable.webp) 7. Fill in the client ID configuration: Note If you are using Apple login for iOS, the client ID is the same as your app ID. If you are using Apple login for Android or Web, the client ID is the same as your service ID. If you are using both, you need to provide both the app ID and the service ID. ![Supabase Apple Provider Client ID](/social-login-assets/supabase_apple_provider_client_id.webp) 8. Click on the `Save` button ![Supabase Apple Provider Save](/social-login-assets/supabase_apple_provider_save.webp) Note You ****DO NOT HAVE TO**** setup `Secret key (for OAuth)`. We will do a custom backend implementation to handle the Apple login. Voilà, you have now enabled Apple Sign-In with Supabase Authentication 🎉 ## Using the Authentication Helper [Section titled “Using the Authentication Helper”](#using-the-authentication-helper) The complete implementation includes a helper function `authenticateWithAppleSupabase()` that handles the entire Apple Sign-In flow with Supabase. This function: * Initializes Apple Sign-In with platform-specific configuration * Handles the authentication flow (native on iOS, OAuth redirect on Android/Web) * Extracts the identity token from Apple * Signs in to Supabase with the identity token Complete Implementation The complete implementation is available in the [example app’s `supabaseAuthUtils.ts`](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/supabaseAuthUtils.ts) file. ### Basic Usage [Section titled “Basic Usage”](#basic-usage) ```typescript import { authenticateWithAppleSupabase } from './supabaseAuthUtils'; const result = await authenticateWithAppleSupabase(); if (result.success) { console.log('Signed in:', result.user); // Navigate to your authenticated area } else { console.error('Error:', result.error); } ``` ### How It Works [Section titled “How It Works”](#how-it-works) The helper function automatically handles platform-specific differences: * **iOS**: Uses native Apple Sign-In (no redirect URL needed, uses bundle ID automatically) * **Android**: Uses OAuth redirect flow with backend edge function (requires Service ID) * **Web**: Uses OAuth popup flow (requires Service ID and current page URL as redirect) The function returns an identity token from Apple, which is then used to authenticate with Supabase using `supabase.auth.signInWithIdToken()`. # Supabase Apple Login on iOS Setup > Learn how to integrate Apple Sign-In with Supabase Authentication on iOS. ## Prerequisites [Section titled “Prerequisites”](#prerequisites) This guide will help you integrate Apple Sign-In with Supabase Authentication on iOS. It is assumed that you have already completed: * the [Apple Login iOS setup](/docs/plugins/social-login/apple/ios/) * the [Supabase Apple Login - General Setup](./general/). ## Implementation [Section titled “Implementation”](#implementation) The complete implementation is available in the [example app’s `supabaseAuthUtils.ts`](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/supabaseAuthUtils.ts) file. This guide explains the key concepts and how to use it. ### Using the Authentication Helper [Section titled “Using the Authentication Helper”](#using-the-authentication-helper) The `authenticateWithAppleSupabase` function handles the entire authentication flow: ```typescript import { authenticateWithAppleSupabase } from './supabaseAuthUtils'; const result = await authenticateWithAppleSupabase(); if (result.success) { console.log('Signed in:', result.user); // Navigate to your authenticated area } else { console.error('Error:', result.error); } ``` ## How It Works [Section titled “How It Works”](#how-it-works) On iOS, Apple Sign-In uses the native implementation: 1. **Initialization**: The plugin uses your app’s bundle ID automatically (no `clientId` needed) 2. **Native Sign-In**: Uses Apple’s native Sign in with Apple button and authentication flow 3. **Identity Token**: Apple returns an identity token (JWT) containing user information 4. **Supabase Authentication**: The identity token is sent to Supabase using `signInWithIdToken()` The helper function automatically detects the iOS platform and configures everything appropriately. ## Important Notes [Section titled “Important Notes”](#important-notes) ### Bundle ID Configuration [Section titled “Bundle ID Configuration”](#bundle-id-configuration) * iOS uses your app’s bundle ID automatically for Apple Sign-In * Make sure your bundle ID matches what’s configured in Apple Developer Portal * The bundle ID should have “Sign in with Apple” capability enabled ### Supabase Client ID [Section titled “Supabase Client ID”](#supabase-client-id) In Supabase, configure your Apple provider with: * **Client ID**: Your iOS App ID (bundle ID) - e.g., `app.capgo.plugin.SocialLogin` If you’re also using Android/Web, you’ll need to provide both the App ID and Service ID in Supabase’s Client ID field (comma-separated). ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) If authentication fails: * **Bundle ID mismatch**: Verify your bundle ID matches in both Xcode and Apple Developer Portal * **Capability not enabled**: Ensure “Sign in with Apple” capability is enabled in Xcode * **Supabase configuration**: Verify your App ID is correctly configured in Supabase Apple provider settings * **Token validation fails**: Check that the identity token is being received from Apple * Review the [example app code](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/supabaseAuthUtils.ts) for reference # Supabase Apple Login on Web > Learn how to integrate Apple Sign-In with Supabase Authentication on Web. ## Prerequisites [Section titled “Prerequisites”](#prerequisites) This guide will help you integrate Apple Sign-In with Supabase Authentication on Web. It is assumed that you have already completed: * the [Apple Login Web setup](/docs/plugins/social-login/apple/web/) * the [Supabase Apple Login - General Setup](./general/). ## Implementation [Section titled “Implementation”](#implementation) The complete implementation is available in the [example app’s `supabaseAuthUtils.ts`](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/supabaseAuthUtils.ts) file. This guide explains the key concepts and how to use it. ### Using the Authentication Helper [Section titled “Using the Authentication Helper”](#using-the-authentication-helper) The `authenticateWithAppleSupabase` function handles the entire authentication flow: ```typescript import { authenticateWithAppleSupabase } from './supabaseAuthUtils'; const result = await authenticateWithAppleSupabase(); if (result.success) { console.log('Signed in:', result.user); // Navigate to your authenticated area } else { console.error('Error:', result.error); } ``` ## How It Works [Section titled “How It Works”](#how-it-works) On Web, Apple Sign-In uses an OAuth popup flow: 1. **Initialization**: The plugin is initialized with your Service ID and the current page URL as the redirect URL 2. **OAuth Popup**: Opens a popup window with Apple’s OAuth page 3. **User Authentication**: User authenticates with Apple in the popup 4. **Identity Token**: Apple returns an identity token (JWT) containing user information 5. **Supabase Authentication**: The identity token is sent to Supabase using `signInWithIdToken()` The helper function automatically detects the web platform and configures everything appropriately. ## Important Notes [Section titled “Important Notes”](#important-notes) ### Service ID Configuration [Section titled “Service ID Configuration”](#service-id-configuration) * Web requires your Apple Service ID (same as Android) * The Service ID must be configured in Apple Developer Portal with the correct Return URLs * Make sure your domain is added to the allowed domains in Apple Developer Portal ### Redirect URL [Section titled “Redirect URL”](#redirect-url) * On web, the redirect URL is automatically set to `window.location.href` (current page URL) * This must match one of the Return URLs configured in Apple Developer Portal * Ensure both the URL with and without trailing slash are configured in Apple Developer Portal ### Supabase Client ID [Section titled “Supabase Client ID”](#supabase-client-id) In Supabase, configure your Apple provider with: * **Client ID**: Your Apple Service ID (e.g., `com.example.app.service`) If you’re also using iOS, you’ll need to provide both the App ID and Service ID in Supabase’s Client ID field (comma-separated). ### Update the Helper Function [Section titled “Update the Helper Function”](#update-the-helper-function) When using the `authenticateWithAppleSupabase` helper function, you **must** update the `clientId` to match your Apple Service ID: ```typescript await SocialLogin.initialize({ apple: { clientId: isIOS ? undefined // iOS uses bundle ID automatically : 'your.service.id.here', // Your Apple Service ID for Web and Android redirectUrl: redirectUrl, }, }); ``` Important Replace `'your.service.id.here'` with your actual Apple Service ID (the same value you configured in Apple Developer Portal). ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) If authentication fails: * **Service ID mismatch**: Verify your Service ID matches in both Apple Developer Portal and your code * **Return URL not configured**: Ensure your current page URL (with and without trailing slash) is configured in Apple Developer Portal * **Popup blocked**: Check browser settings - some browsers block popups by default * **Domain not allowed**: Verify your domain is added to the allowed domains in Apple Developer Portal * **Supabase configuration**: Verify your Service ID is correctly configured in Supabase Apple provider settings * Review the [example app code](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/supabaseAuthUtils.ts) for reference # Supabase Google Login on Android > Learn how to set up Google Sign-In with Supabase Authentication on Android using the Capacitor Social Login plugin. ## Introduction [Section titled “Introduction”](#introduction) This guide will help you integrate Google Sign-In with Supabase Authentication on Android. It is assumed that you have already completed: * the [Google Login Android setup](/docs/plugins/social-login/google/android/) * the [Supabase Google Login - General Setup](/docs/plugins/social-login/supabase/google/general/). ## Implementation [Section titled “Implementation”](#implementation) The complete implementation is available in the [example app’s `supabaseAuthUtils.ts`](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/supabaseAuthUtils.ts) file. This guide explains the key concepts and how to use it. ### Using the Authentication Helper [Section titled “Using the Authentication Helper”](#using-the-authentication-helper) The `authenticateWithGoogleSupabase` function handles the entire authentication flow: ```typescript import { authenticateWithGoogleSupabase } from './supabaseAuthUtils'; const result = await authenticateWithGoogleSupabase(); if (result.success) { console.log('Signed in:', result.user); // Navigate to your authenticated area } else { console.error('Error:', result.error); } ``` ## How It Works [Section titled “How It Works”](#how-it-works) For a detailed explanation of how the authentication flow works, including nonce generation, JWT validation, and Supabase sign-in, see the [How It Works section in the General Setup guide](/docs/plugins/social-login/supabase/google/general/#how-it-works). For the complete code reference, see the [Complete Code Reference section in the General Setup guide](/docs/plugins/social-login/supabase/google/general/#complete-code-reference). ## Important Notes [Section titled “Important Notes”](#important-notes) ### Nonce Handling [Section titled “Nonce Handling”](#nonce-handling) The nonce implementation follows the pattern from the [React Native Google Sign In documentation](https://react-native-google-signin.github.io/docs/security#usage-with-supabase): * `rawNonce` goes to Supabase’s `signInWithIdToken()` * Supabase makes a hash of `rawNonce` and compares it with the `nonceDigest` which is included in the ID token from Google Sign-In * `nonceDigest` (SHA-256 hash, hex-encoded) goes to the `nonce` parameter in Google Sign-In APIs ### Automatic Retry [Section titled “Automatic Retry”](#automatic-retry) The implementation includes automatic retry logic: * If JWT validation fails on first attempt, it logs out and retries once * This handles cases where cached tokens might have incorrect nonces * If the retry also fails, an error is returned ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) If authentication fails: * **Invalid audience**: Verify your Google Client IDs match in both Google Cloud Console and Supabase * **Nonce mismatch**: Check console logs - the function will automatically retry, but you can manually logout first if needed * **Token validation fails**: Ensure you’re using `mode: 'online'` in the initialize call to get an idToken * Review the [example app code](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/supabaseAuthUtils.ts) for reference # Supabase Google Login - General Setup > Learn how to set up Google Sign-In with Supabase Authentication using the Capacitor Social Login plugin. ## Introduction [Section titled “Introduction”](#introduction) This guide will walk you through integrating Google Sign-In with Supabase Authentication using the Capacitor Social Login plugin. This setup allows you to use native Google Sign-In on mobile platforms while leveraging Supabase Auth for backend authentication. ## Prerequisites [Section titled “Prerequisites”](#prerequisites) Before starting, ensure you have: 1. [Created a Supabase project](https://database.new/) 2. Read the [Google Login General Setup](/docs/plugins/social-login/google/general/) guide to setup Google OAuth credentials 3. Followed the respective platform-specific guides to setup Google OAuth credentials for your target platform: * [Android Setup](/docs/plugins/social-login/google/android/) * [iOS Setup](/docs/plugins/social-login/google/ios/) * [Web Setup](/docs/plugins/social-login/google/web/) Note Before starting the Supabase tutorial, you need to generate the client IDs for the platforms you plan to use. You will use them in step 7 of this guide. ## Enabling Google OAuth provider in Supabase [Section titled “Enabling Google OAuth provider in Supabase”](#enabling-google-oauth-provider-in-supabase) 1. Go to your [Supabase Dashboard](https://app.supabase.com/) 2. Click on your project ![Supabase Project Selector](/social-login-assets/supabase_project_select.webp) 3. Do go to the `Authentication` menu ![Supabase Authentication Menu](/social-login-assets/supabase_authentication_menu.webp) 4. Click on the `Providers` tab ![Supabase Providers Tab](/social-login-assets/supabase_providers_tab.webp) 5. Find the `Google` provider ![Supabase Google Provider](/social-login-assets/supabase_google_provider.webp) 6. Enable the provider ![Supabase Google Provider Enable](/social-login-assets/supabase_google_provider_enable.webp) 7. Add the client IDs for the platforms you plan to use ![Supabase Google Provider Add Client IDs](/social-login-assets/supabase_google_provider_add_client_ids.webp) Note This included the web client ID, the iOS client ID and the Android client ID. You can skip providing some of them, depending on the platforms you plan to use. 8. Click on the `Save` button ![Supabase Google Provider Save](/social-login-assets/supabase_google_provider_save.webp) Note You ****SHOULD NOT**** setup `Client Secret (for OAuth)` or `Callback URL (for OAuth)`. Some sources might also suggest setting `Skip nonce checks` for Google on iOS, but with this guide this isn’t needed. Voilà, you have now enabled Google Sign-In with Supabase Authentication 🎉 ## How Google Sign-In with Supabase Authentication Helper Works [Section titled “How Google Sign-In with Supabase Authentication Helper Works”](#how-google-sign-in-with-supabase-authentication-helper-works) This section explains how the Google Sign-In integration with Supabase works under the hood. Understanding this flow will help you implement and troubleshoot the authentication process. Complete Implementation The complete implementation is available in the [example app’s `supabaseAuthUtils.ts`](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/supabaseAuthUtils.ts) file. ### 1. Nonce Generation [Section titled “1. Nonce Generation”](#1-nonce-generation) The implementation generates a secure nonce pair following the [Supabase nonce requirements](https://react-native-google-signin.github.io/docs/security#usage-with-supabase): ```typescript // Generate URL-safe random nonce function getUrlSafeNonce(): string { const array = new Uint8Array(32); crypto.getRandomValues(array); return Array.from(array, (byte) => byte.toString(16).padStart(2, '0')).join(''); } // Hash the nonce with SHA-256 async function sha256Hash(message: string): Promise { const encoder = new TextEncoder(); const data = encoder.encode(message); const hashBuffer = await crypto.subtle.digest('SHA-256', data); const hashArray = Array.from(new Uint8Array(hashBuffer)); return hashArray.map((b) => b.toString(16).padStart(2, '0')).join(''); } // Generate nonce pair async function getNonce(): Promise<{ rawNonce: string; nonceDigest: string }> { const rawNonce = getUrlSafeNonce(); const nonceDigest = await sha256Hash(rawNonce); return { rawNonce, nonceDigest }; } ``` **Flow:** * `rawNonce`: URL-safe random string (64 hex characters) * `nonceDigest`: SHA-256 hash of `rawNonce` (hex-encoded) * `nonceDigest` is passed to Google Sign-In → Google includes the nonce digest in the ID token * `rawNonce` is passed to Supabase → Supabase hashes the raw nonce and compares with the token’s nonce ### 2. Google Sign-In [Section titled “2. Google Sign-In”](#2-google-sign-in) The function initializes the plugin and signs in with Google: ```typescript await SocialLogin.initialize({ google: { webClientId: 'YOUR_WEB_CLIENT_ID.apps.googleusercontent.com', // iOS only: iOSClientId: 'YOUR_IOS_CLIENT_ID.apps.googleusercontent.com', mode: 'online', // Required to get idToken }, }); const response = await SocialLogin.login({ provider: 'google', options: { scopes: ['email', 'profile'], nonce: nonceDigest, // Pass the SHA-256 hashed nonce }, }); ``` ### 3. JWT Validation [Section titled “3. JWT Validation”](#3-jwt-validation) Before sending the token to Supabase, the implementation validates the JWT token: ```typescript function validateJWTToken(idToken: string, expectedNonceDigest: string): { valid: boolean; error?: string } { const decodedToken = decodeJWT(idToken); // Check audience matches your Google Client IDs const audience = decodedToken.aud; if (!VALID_GOOGLE_CLIENT_IDS.includes(audience)) { return { valid: false, error: 'Invalid audience' }; } // Check nonce matches const tokenNonce = decodedToken.nonce; if (tokenNonce && tokenNonce !== expectedNonceDigest) { return { valid: false, error: 'Nonce mismatch' }; } return { valid: true }; } ``` **Why validate before Supabase?** Validating the JWT token before sending the token to Supabase serves several important purposes: 1. **Prevent Invalid Requests**: If the token has an incorrect audience or nonce mismatch, Supabase will reject the token anyway. Validating first avoids unnecessary API calls and provides clearer error messages. 2. **Token Caching Issues**: On some platforms (especially iOS), Google Sign-In SDK can cache tokens for performance. When a cached token is returned, the cached token may have been generated with a different nonce (or no nonce at all), causing Supabase to reject the token with a “nonce mismatch” error. By validating before sending to Supabase, we can detect this issue early and automatically retry with a fresh token. 3. **Security** (iOS): On iOS, validation ensures the token was issued for your specific Google Client IDs, preventing potential security issues from using tokens intended for other applications. 4. **Better Error Handling**: Detecting issues before Supabase allows for automatic retry logic, which is essential for handling iOS caching issues transparently. If validation fails, the function automatically: 1. Logs out from Google (clears cached tokens - critical on iOS) 2. Retries authentication once (forces fresh token generation with correct nonce) 3. If retry also fails, returns an error ### 4. Supabase Sign-In [Section titled “4. Supabase Sign-In”](#4-supabase-sign-in) Finally, the validated token is sent to Supabase: ```typescript const { data, error } = await supabase.auth.signInWithIdToken({ provider: 'google', token: googleResponse.idToken, nonce: rawNonce, // Pass the raw (unhashed) nonce }); ``` ## Complete Code Reference [Section titled “Complete Code Reference”](#complete-code-reference) The complete implementation is available in the [example app’s `supabaseAuthUtils.ts`](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/supabaseAuthUtils.ts) file, which includes: * `getUrlSafeNonce()` - Generates URL-safe random nonce * `sha256Hash()` - Hashes string with SHA-256 * `getNonce()` - Generates nonce pair * `decodeJWT()` - Decodes JWT token * `validateJWTToken()` - Validates JWT audience and nonce * `authenticateWithGoogleSupabase()` - Main authentication function with automatic retry ### Additional Example Files [Section titled “Additional Example Files”](#additional-example-files) * [SupabasePage.tsx](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/SupabasePage.tsx) - Example component with redirect handling (Web) * [SupabaseCreateAccountPage.tsx](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/SupabaseCreateAccountPage.tsx) - Example create account page ## Next Steps [Section titled “Next Steps”](#next-steps) Please proceed to the platform-specific setup guide for your target platform: * [Android Setup](../android/) * [iOS Setup](../ios/) * [Web Setup](../web/) # Supabase Google Login on iOS > Learn how to set up Google Sign-In with Supabase Authentication on iOS using the Capacitor Social Login plugin. ## Introduction [Section titled “Introduction”](#introduction) This guide will help you integrate Google Sign-In with Supabase Authentication on iOS. It is assumed that you have already completed: * the [Google Login iOS setup](/docs/plugins/social-login/google/ios/) * the [Supabase Google Login - General Setup](/docs/plugins/social-login/supabase/google/general/). ## Implementation [Section titled “Implementation”](#implementation) The complete implementation is available in the [example app’s `supabaseAuthUtils.ts`](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/supabaseAuthUtils.ts) file. This guide explains the key concepts and how to use it. ### Using the Authentication Helper [Section titled “Using the Authentication Helper”](#using-the-authentication-helper) The `authenticateWithGoogleSupabase` function handles the entire authentication flow: ```typescript import { authenticateWithGoogleSupabase } from './supabaseAuthUtils'; const result = await authenticateWithGoogleSupabase(); if (result.success) { console.log('Signed in:', result.user); // Navigate to your authenticated area } else { console.error('Error:', result.error); } ``` ## How It Works [Section titled “How It Works”](#how-it-works) For a detailed explanation of how the authentication flow works, including nonce generation, JWT validation, and Supabase sign-in, see the [How It Works section in the General Setup guide](/docs/plugins/social-login/supabase/google/general/#how-it-works). ## Important Caveats [Section titled “Important Caveats”](#important-caveats) ### iOS Token Caching and Nonce Issues [Section titled “iOS Token Caching and Nonce Issues”](#ios-token-caching-and-nonce-issues) iOS Nonce Caching Issue On iOS, Google Sign-In can cache tokens, which may cause the nonce validation to fail. The `validateJWTToken` function detects this and automatically handles it: 1. **Automatic Detection**: The function checks if the nonce in the token matches the expected `nonceDigest` 2. **Automatic Retry**: If validation fails, it automatically logs out from Google and retries once 3. **Error Handling**: If the retry also fails, an error is returned **Why this happens**: iOS Google Sign-In SDK caches tokens for performance. When a cached token is returned, it may have been generated with a different nonce (or no nonce), causing a mismatch. **The solution**: The implementation automatically handles this by logging out and retrying, which forces Google to generate a fresh token with the correct nonce. **Manual Workaround** (if automatic retry doesn’t work): ```typescript // Logout first to clear cached tokens await SocialLogin.logout({ provider: 'google' }); // Then authenticate const result = await authenticateWithGoogleSupabase(); ``` This ensures a fresh token is obtained with the correct nonce. For the complete code reference, see the [Complete Code Reference section in the General Setup guide](/docs/plugins/social-login/supabase/google/general/#complete-code-reference). ## Important Notes [Section titled “Important Notes”](#important-notes) ### Nonce Handling [Section titled “Nonce Handling”](#nonce-handling) The nonce implementation follows the pattern from the [React Native Google Sign In documentation](https://react-native-google-signin.github.io/docs/security#usage-with-supabase): * `rawNonce` goes to Supabase’s `signInWithIdToken()` * Supabase makes a hash of `rawNonce` and compares it with the `nonceDigest` which is included in the ID token from Google Sign-In * `nonceDigest` (SHA-256 hash, hex-encoded) goes to the `nonce` parameter in Google Sign-In APIs ### Automatic Retry Mechanism [Section titled “Automatic Retry Mechanism”](#automatic-retry-mechanism) The `authenticateWithGoogleSupabase` function includes a `retry` parameter: * First call (`retry=false`): If validation fails, automatically logs out and retries once * Retry call (`retry=true`): If validation fails again, immediately returns an error This handles the iOS token caching issue automatically. ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) If authentication fails: * **Nonce mismatch**: The function automatically retries - check console logs for details. If it persists, manually logout first * **Invalid audience**: Verify your Google Client IDs match in both Google Cloud Console and Supabase (both iOS and Web client IDs) * **Token validation fails**: Ensure you’re using `mode: 'online'` in the initialize call to get an idToken * **Info.plist configuration**: Ensure Info.plist has the correct URL schemes and GIDClientID * Review the [example app code](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/supabaseAuthUtils.ts) for reference # Supabase Google Login on Web > Learn how to set up Google Sign-In with Supabase Authentication on Web using the Capacitor Social Login plugin. ## Introduction [Section titled “Introduction”](#introduction) This guide will help you integrate Google Sign-In with Supabase Authentication on Web. It is assumed that you have already completed: * the [Google Login Web setup](/docs/plugins/social-login/google/web/) * the [Supabase Google Login - General Setup](/docs/plugins/social-login/supabase/google/general/). ## Implementation [Section titled “Implementation”](#implementation) The complete implementation is available in the [example app’s `supabaseAuthUtils.ts`](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/supabaseAuthUtils.ts) file. This guide explains the key concepts and how to use it. ### Using the Authentication Helper [Section titled “Using the Authentication Helper”](#using-the-authentication-helper) The `authenticateWithGoogleSupabase` function handles the entire authentication flow: ```typescript import { authenticateWithGoogleSupabase } from './supabaseAuthUtils'; const result = await authenticateWithGoogleSupabase(); if (result.success) { console.log('Signed in:', result.user); // Navigate to your authenticated area } else { console.error('Error:', result.error); } ``` ## Critical: Redirect Handling [Section titled “Critical: Redirect Handling”](#critical-redirect-handling) Critical: Redirect Handling When using Google login on web, you **MUST** call any function from the plugin when the redirect happens to initialize the plugin so it can handle the redirect and close the popup window. You can call either `isLoggedIn()` OR `initialize()` - both will trigger the redirect handling. This is essential for the OAuth popup flow to work correctly. ### Implementation Example [Section titled “Implementation Example”](#implementation-example) Add this to your component that handles Google Sign-In: ```typescript import { useEffect } from 'react'; import { SocialLogin } from '@capgo/capacitor-social-login'; function SupabasePage() { // Check Google login status on mount to invoke redirect handling // This doesn't serve any functional purpose in the UI but ensures // that any pending OAuth redirects are properly processed useEffect(() => { const checkGoogleLoginStatus = async () => { try { await SocialLogin.isLoggedIn({ provider: 'google' }); // We don't use the result, this is just to trigger redirect handling } catch (error) { // Ignore errors - this is just for redirect handling console.log('Google login status check completed (redirect handling)'); } }; checkGoogleLoginStatus(); }, []); // ... rest of your component } ``` See the [SupabasePage.tsx](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/SupabasePage.tsx) for a complete example. ## How It Works [Section titled “How It Works”](#how-it-works) For a detailed explanation of how the authentication flow works, including nonce generation, JWT validation, and Supabase sign-in, see the [How It Works section in the General Setup guide](/docs/plugins/social-login/supabase/google/general/#how-it-works). For the complete code reference, see the [Complete Code Reference section in the General Setup guide](/docs/plugins/social-login/supabase/google/general/#complete-code-reference). Also see: * [SupabasePage.tsx](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/SupabasePage.tsx) - Example component with redirect handling * [SupabaseCreateAccountPage.tsx](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/SupabaseCreateAccountPage.tsx) - Example create account page ## Important Notes [Section titled “Important Notes”](#important-notes) ### Redirect Handling [Section titled “Redirect Handling”](#redirect-handling) When using Google login on web, you **MUST** call any function from the plugin when the redirect happens to initialize the plugin so it can handle the redirect and close the popup window. You can call either `isLoggedIn()` OR `initialize()` - both will trigger the redirect handling. This is essential for the OAuth popup flow to work correctly. Without this, the popup window won’t close after authentication. ### Nonce Handling [Section titled “Nonce Handling”](#nonce-handling) The nonce implementation follows the pattern from the [React Native Google Sign In documentation](https://react-native-google-signin.github.io/docs/security#usage-with-supabase): * `rawNonce` goes to Supabase’s `signInWithIdToken()` * Supabase makes a hash of `rawNonce` and compares it with the `nonceDigest` which is included in the ID token from Google Sign-In * `nonceDigest` (SHA-256 hash, hex-encoded) goes to the `nonce` parameter in Google Sign-In APIs ### Automatic Retry [Section titled “Automatic Retry”](#automatic-retry) The implementation includes automatic retry logic: * If JWT validation fails on first attempt, it logs out and retries once * This handles cases where cached tokens might have incorrect nonces * If the retry also fails, an error is returned ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) If authentication fails: * **Redirect not working**: Ensure you’re calling `isLoggedIn()` on component mount (see example above) * **Invalid audience**: Verify your Google Client IDs match in both Google Cloud Console and Supabase * **Authorized redirect URLs**: Check that authorized redirect URLs are configured in both Google Cloud Console and Supabase * **Nonce mismatch**: Check console logs - the function will automatically retry, but you can manually logout first if needed * **Token validation fails**: Ensure you’re using `mode: 'online'` in the initialize call to get an idToken * Review the [example app code](https://github.com/Cap-go/capacitor-social-login/blob/main/example-app/src/supabaseAuthUtils.ts) for reference # Supabase Integration Introduction > Learn how to integrate Supabase Authentication with the Capacitor Social Login plugin for a complete authentication solution. ## Overview [Section titled “Overview”](#overview) This tutorial will guide you through setting up Supabase Authentication with the Capacitor Social Login plugin. This integration allows you to use native social login providers (Google, Apple, Facebook, Twitter) on mobile platforms while leveraging Supabase Auth for backend authentication and PostgreSQL for data storage. ## What You’ll Learn [Section titled “What You’ll Learn”](#what-youll-learn) * How to configure Supabase Authentication * How to integrate Capacitor Social Login plugin with Supabase Auth * Platform-specific setup for Android, iOS, and Web * How to handle nonces securely for Supabase ## What You’ll Need [Section titled “What You’ll Need”](#what-youll-need) Before you begin, make sure you have: 1. **A Supabase Project** * Create a project at [Supabase Dashboard](https://app.supabase.com/) * Enable Google OAuth provider * Get your Supabase project URL and anon key 2. **Supabase JS SDK** * Install Supabase in your project: ```bash npm install @supabase/supabase-js ``` 3. **A Capacitor Project** * An existing Capacitor application * Capacitor Social Login plugin installed: ```bash npm install @capgo/capacitor-social-login npx cap sync ``` 4. **Platform-Specific Google Setup** * Complete the Google Sign-In setup for your target platforms: * [Google Login Android Setup](/docs/plugins/social-login/google/android/) * [Google Login iOS Setup](/docs/plugins/social-login/google/ios/) * [Google Login Web Setup](/docs/plugins/social-login/google/web/) ## Example Application [Section titled “Example Application”](#example-application) A complete working example is available in the repository: **Code Repository**: [You can find the code repository here](https://github.com/Cap-go/capacitor-social-login/tree/main/example-app) The example app demonstrates: * Email/password authentication with Supabase * Google Sign-In integration (Android, iOS, and Web) * A simple key-value store using Supabase PostgreSQL tables * User-specific data storage with Row Level Security (RLS) ## Key Implementation Details [Section titled “Key Implementation Details”](#key-implementation-details) ### Nonce Handling [Section titled “Nonce Handling”](#nonce-handling) Supabase requires special nonce handling for security. The implementation follows the [React Native Google Sign In documentation](https://react-native-google-signin.github.io/docs/security#usage-with-supabase): * Generate a `rawNonce` (URL-safe random string) * Hash it with SHA-256 to get `nonceDigest` * Pass `nonceDigest` to Google Sign-In * Pass `rawNonce` to Supabase (Supabase hashes it internally for comparison) ### JWT Validation [Section titled “JWT Validation”](#jwt-validation) The example implementation includes JWT validation to ensure: * The token audience matches your configured Google Client IDs * The nonce matches the expected digest * Automatic retry on validation failure (especially important for iOS) ### Platform-Specific Considerations [Section titled “Platform-Specific Considerations”](#platform-specific-considerations) * **iOS**: Token caching can cause nonce issues - the implementation handles this automatically * **Web**: Must call `isLoggedIn()` on mount to handle OAuth redirects * **Android**: Standard implementation with SHA-1 fingerprint configuration ## Next Steps [Section titled “Next Steps”](#next-steps) Continue with the setup guides: * [Supabase Google Login - General Setup](./google/general/) - Overview and prerequisites * [Android Setup](./google/android/) - Android-specific configuration * [iOS Setup](./google/ios/) - iOS-specific configuration * [Web Setup](./google/web/) - Web-specific configuration ### Apple Sign-In [Section titled “Apple Sign-In”](#apple-sign-in) * [Supabase Apple Login - General Setup](./Apple/general/) - Overview and prerequisites * [iOS Setup](./Apple/ios/) - iOS-specific configuration * [Android Setup](./Apple/android/) - Android-specific configuration # @capgo/capacitor-speech-recognition > Comprehensive on-device speech recognition with real-time transcription, streaming partial results, and cross-platform parity. Real-time Transcription Get instant partial results as users speak 🎤 Cross-Platform Parity Consistent API and behavior across iOS and Android 📱 Segmented Sessions Split recognition into segments based on silence 🔇 Punctuation Support Automatic punctuation on iOS 16+ 📝 Permission Helpers Built-in permission request and status checking 🔒 Language Support Access to device’s supported recognition languages 🌍 Getting Started Check the [Getting Started Guide](/docs/plugins/speech-recognition/getting-started/) to install and configure the plugin. # Getting Started > Learn how to install and use the Speech Recognition plugin to add voice transcription to your Capacitor app 1. **Install the package** * npm ```sh npm i @capgo/capacitor-speech-recognition ``` * pnpm ```sh pnpm add @capgo/capacitor-speech-recognition ``` * yarn ```sh yarn add @capgo/capacitor-speech-recognition ``` * bun ```sh bun add @capgo/capacitor-speech-recognition ``` 2. **Sync with native projects** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` 3. **Configure platform permissions** (see below) ## Platform Configuration [Section titled “Platform Configuration”](#platform-configuration) ### iOS [Section titled “iOS”](#ios) Add the following keys to your app’s `Info.plist` file: ```xml NSSpeechRecognitionUsageDescription We need access to speech recognition to transcribe your voice NSMicrophoneUsageDescription We need access to your microphone to record audio for transcription ``` ### Android [Section titled “Android”](#android) The plugin automatically adds the required `RECORD_AUDIO` permission to your `AndroidManifest.xml`. No additional configuration is needed. ## Basic Usage [Section titled “Basic Usage”](#basic-usage) ### Check Availability [Section titled “Check Availability”](#check-availability) Before using speech recognition, check if it’s available on the device: ```typescript import { SpeechRecognition } from '@capgo/capacitor-speech-recognition'; const checkAvailability = async () => { const { available } = await SpeechRecognition.available(); if (!available) { console.warn('Speech recognition is not supported on this device'); return false; } return true; }; ``` ### Request Permissions [Section titled “Request Permissions”](#request-permissions) Request the necessary permissions before starting recognition: ```typescript const requestPermissions = async () => { const { speechRecognition } = await SpeechRecognition.requestPermissions(); if (speechRecognition === 'granted') { console.log('Permission granted'); return true; } else { console.log('Permission denied'); return false; } }; ``` ### Start Recognition [Section titled “Start Recognition”](#start-recognition) Start listening for speech with optional configuration: ```typescript // Basic usage await SpeechRecognition.start({ language: 'en-US', maxResults: 3, partialResults: true, }); // With all options await SpeechRecognition.start({ language: 'en-US', maxResults: 5, prompt: 'Speak now...', // Android only popup: false, // Android only partialResults: true, addPunctuation: true, // iOS 16+ only allowForSilence: 2000, // Android only, milliseconds }); ``` ### Listen for Results [Section titled “Listen for Results”](#listen-for-results) Subscribe to partial results while recognition is active: ```typescript const partialListener = await SpeechRecognition.addListener( 'partialResults', (event) => { const transcription = event.matches?.[0]; console.log('Partial result:', transcription); } ); // Don't forget to remove the listener when done await partialListener.remove(); ``` ### Stop Recognition [Section titled “Stop Recognition”](#stop-recognition) Stop listening and clean up resources: ```typescript await SpeechRecognition.stop(); ``` ## Complete Example [Section titled “Complete Example”](#complete-example) Here’s a complete example showing how to use the plugin: ```typescript import { SpeechRecognition } from '@capgo/capacitor-speech-recognition'; export class VoiceRecognitionService { private partialListener: any = null; private isListening = false; async initialize(): Promise { // Check availability const { available } = await SpeechRecognition.available(); if (!available) { throw new Error('Speech recognition not available'); } // Request permissions const { speechRecognition } = await SpeechRecognition.requestPermissions(); if (speechRecognition !== 'granted') { throw new Error('Permission denied'); } return true; } async startListening( onPartialResult: (text: string) => void, onFinalResult: (text: string) => void ): Promise { if (this.isListening) { console.warn('Already listening'); return; } try { // Set up partial results listener this.partialListener = await SpeechRecognition.addListener( 'partialResults', (event) => { const text = event.matches?.[0] || ''; onPartialResult(text); } ); // Start recognition const result = await SpeechRecognition.start({ language: 'en-US', maxResults: 3, partialResults: true, addPunctuation: true, }); this.isListening = true; // Handle final result if partialResults is false if (result.matches && result.matches.length > 0) { onFinalResult(result.matches[0]); } } catch (error) { console.error('Error starting speech recognition:', error); throw error; } } async stopListening(): Promise { if (!this.isListening) { return; } try { await SpeechRecognition.stop(); // Clean up listener if (this.partialListener) { await this.partialListener.remove(); this.partialListener = null; } this.isListening = false; } catch (error) { console.error('Error stopping speech recognition:', error); throw error; } } async getSupportedLanguages(): Promise { const { languages } = await SpeechRecognition.getSupportedLanguages(); return languages; } async checkListeningState(): Promise { const { listening } = await SpeechRecognition.isListening(); return listening; } } ``` ## API Reference [Section titled “API Reference”](#api-reference) ### available() [Section titled “available()”](#available) Checks whether the native speech recognition service is usable on the current device. ```typescript const result = await SpeechRecognition.available(); // Returns: { available: boolean } ``` ### start(options?) [Section titled “start(options?)”](#startoptions) Begins capturing audio and transcribing speech. ```typescript interface SpeechRecognitionStartOptions { language?: string; // Locale identifier (e.g., 'en-US') maxResults?: number; // Maximum number of results (default: 5) prompt?: string; // Android only: Dialog prompt popup?: boolean; // Android only: Show system dialog partialResults?: boolean; // Stream partial results addPunctuation?: boolean; // iOS 16+ only: Add punctuation allowForSilence?: number; // Android only: Silence timeout in ms } const result = await SpeechRecognition.start(options); // Returns: { matches?: string[] } ``` ### stop() [Section titled “stop()”](#stop) Stops listening and tears down native resources. ```typescript await SpeechRecognition.stop(); ``` ### getSupportedLanguages() [Section titled “getSupportedLanguages()”](#getsupportedlanguages) Gets the locales supported by the underlying recognizer. **Note**: Android 13+ devices no longer expose this list; in that case `languages` will be empty. ```typescript const result = await SpeechRecognition.getSupportedLanguages(); // Returns: { languages: string[] } ``` ### isListening() [Section titled “isListening()”](#islistening) Returns whether the plugin is actively listening for speech. ```typescript const result = await SpeechRecognition.isListening(); // Returns: { listening: boolean } ``` ### checkPermissions() [Section titled “checkPermissions()”](#checkpermissions) Gets the current permission state. ```typescript const result = await SpeechRecognition.checkPermissions(); // Returns: { speechRecognition: 'prompt' | 'prompt-with-rationale' | 'granted' | 'denied' } ``` ### requestPermissions() [Section titled “requestPermissions()”](#requestpermissions) Requests the microphone + speech recognition permissions. ```typescript const result = await SpeechRecognition.requestPermissions(); // Returns: { speechRecognition: 'prompt' | 'prompt-with-rationale' | 'granted' | 'denied' } ``` ## Event Listeners [Section titled “Event Listeners”](#event-listeners) ### partialResults [Section titled “partialResults”](#partialresults) Listen for partial transcription updates while `partialResults` is enabled. ```typescript const listener = await SpeechRecognition.addListener( 'partialResults', (event: { matches: string[] }) => { console.log('Partial:', event.matches?.[0]); } ); ``` ### segmentResults (Android only) [Section titled “segmentResults (Android only)”](#segmentresults-android-only) Listen for segmented recognition results. ```typescript const listener = await SpeechRecognition.addListener( 'segmentResults', (event: { matches: string[] }) => { console.log('Segment:', event.matches?.[0]); } ); ``` ### endOfSegmentedSession (Android only) [Section titled “endOfSegmentedSession (Android only)”](#endofsegmentedsession-android-only) Listen for segmented session completion events. ```typescript const listener = await SpeechRecognition.addListener( 'endOfSegmentedSession', () => { console.log('Segmented session ended'); } ); ``` ### listeningState [Section titled “listeningState”](#listeningstate) Listen for changes to the native listening state. ```typescript const listener = await SpeechRecognition.addListener( 'listeningState', (event: { status: 'started' | 'stopped' }) => { console.log('Listening state:', event.status); } ); ``` ### removeAllListeners() [Section titled “removeAllListeners()”](#removealllisteners) Removes all registered listeners. ```typescript await SpeechRecognition.removeAllListeners(); ``` ## Best Practices [Section titled “Best Practices”](#best-practices) 1. **Always check availability and permissions** ```typescript const { available } = await SpeechRecognition.available(); if (!available) return; const { speechRecognition } = await SpeechRecognition.requestPermissions(); if (speechRecognition !== 'granted') return; ``` 2. **Clean up listeners** Always remove listeners when they’re no longer needed to prevent memory leaks: ```typescript await listener.remove(); // or await SpeechRecognition.removeAllListeners(); ``` 3. **Handle errors gracefully** ```typescript try { await SpeechRecognition.start({ language: 'en-US' }); } catch (error) { console.error('Speech recognition failed:', error); // Show user-friendly error message } ``` 4. **Provide visual feedback** Use the `listeningState` event to show users when the app is actively listening. 5. **Test with different accents and languages** Speech recognition accuracy varies by language and accent. Test thoroughly with your target audience. ## Platform Notes [Section titled “Platform Notes”](#platform-notes) ### iOS [Section titled “iOS”](#ios-1) * Requires iOS 10.0+ * Uses native `SFSpeechRecognizer` * Supports punctuation on iOS 16+ * Requires both microphone and speech recognition permissions * Recognition may fail if device language doesn’t match requested language ### Android [Section titled “Android”](#android-1) * Requires Android 6.0 (API 23)+ * Uses `SpeechRecognizer` API * Supports segmented sessions with configurable silence detection * Android 13+ doesn’t expose list of supported languages * Some devices may show system recognition UI ### Web [Section titled “Web”](#web) * Limited support via Web Speech API * Not all browsers support speech recognition * Requires HTTPS connection * May have different behavior across browsers ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) ### Permission Denied [Section titled “Permission Denied”](#permission-denied) If permissions are denied, guide users to app settings: ```typescript const { speechRecognition } = await SpeechRecognition.checkPermissions(); if (speechRecognition === 'denied') { // Show instructions to enable permissions in Settings } ``` ### No Results Returned [Section titled “No Results Returned”](#no-results-returned) * Check microphone is working * Ensure quiet environment * Verify language code matches device capabilities * Check network connection (some platforms require it) ### Recognition Stops Unexpectedly [Section titled “Recognition Stops Unexpectedly”](#recognition-stops-unexpectedly) * Use `isListening()` to check state * Listen to `listeningState` events * Implement auto-restart logic if needed ## Next Steps [Section titled “Next Steps”](#next-steps) * [View API reference on GitHub](https://github.com/Cap-go/capacitor-speech-recognition#api) * [Report issues](https://github.com/Cap-go/capacitor-speech-recognition/issues) * [Explore example app](https://github.com/Cap-go/capacitor-speech-recognition/tree/main/example) # @capgo/capacitor-speech-synthesis > Convert text to speech with full control over language, voice, pitch, rate, and volume on iOS, Android, and Web. Multiple Languages Support for dozens of languages and regional variants 🌍 Voice Selection Choose from available system voices for each language 🎤 Fine Control Adjust pitch, rate, and volume to customize speech output ⚙️ Cross-platform Consistent API across iOS, Android, and Web platforms 📱 Real-time Control Pause, resume, and cancel speech playback on the fly 🎮 Comprehensive Documentation Check the [Documentation](/docs/plugins/speech-synthesis/getting-started/) to master the plugin in just a few minutes. # Getting Started > Learn how to install and use the Speech Synthesis plugin to convert text to speech in your Capacitor app. 1. **Install the package** * npm ```sh npm i @capgo/capacitor-speech-synthesis ``` * pnpm ```sh pnpm add @capgo/capacitor-speech-synthesis ``` * yarn ```sh yarn add @capgo/capacitor-speech-synthesis ``` * bun ```sh bun add @capgo/capacitor-speech-synthesis ``` 2. **Sync with native projects** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Usage [Section titled “Usage”](#usage) Import the plugin and synthesize speech from text: ```typescript import { SpeechSynthesis } from '@capgo/capacitor-speech-synthesis'; // Basic text-to-speech const speak = async () => { await SpeechSynthesis.speak({ text: 'Hello, world!', language: 'en-US' }); }; // Advanced usage with voice control const speakWithVoice = async () => { await SpeechSynthesis.speak({ text: 'This is a test of speech synthesis.', language: 'en-US', rate: 1.0, // Speech rate: 0.1 to 10.0 pitch: 1.0, // Voice pitch: 0.5 to 2.0 volume: 1.0, // Volume: 0.0 to 1.0 voice: 'com.apple.ttsbundle.Samantha-compact' }); }; ``` ## API Reference [Section titled “API Reference”](#api-reference) ### speak(options) [Section titled “speak(options)”](#speakoptions) Speaks the given text using the specified voice settings. ```typescript interface SpeakOptions { text: string; // Text to speak language?: string; // Language code (e.g., 'en-US', 'es-ES') rate?: number; // Speech rate: 0.1 to 10.0 (default: 1.0) pitch?: number; // Voice pitch: 0.5 to 2.0 (default: 1.0) volume?: number; // Volume: 0.0 to 1.0 (default: 1.0) voice?: string; // Voice identifier (get from getVoices()) } await SpeechSynthesis.speak(options); ``` ### stop() [Section titled “stop()”](#stop) Stops any ongoing speech synthesis. ```typescript await SpeechSynthesis.stop(); ``` ### getVoices() [Section titled “getVoices()”](#getvoices) Gets the list of available voices for speech synthesis. ```typescript interface Voice { voiceURI: string; // Unique voice identifier name: string; // Human-readable voice name lang: string; // Language code default: boolean; // Whether this is the default voice } const { voices } = await SpeechSynthesis.getVoices(); console.log('Available voices:', voices); ``` ### getSupportedLanguages() [Section titled “getSupportedLanguages()”](#getsupportedlanguages) Gets the list of supported language codes. ```typescript const { languages } = await SpeechSynthesis.getSupportedLanguages(); console.log('Supported languages:', languages); ``` ### isSpeaking() [Section titled “isSpeaking()”](#isspeaking) Checks if speech synthesis is currently active. ```typescript const { speaking } = await SpeechSynthesis.isSpeaking(); console.log('Currently speaking:', speaking); ``` ## Complete Example [Section titled “Complete Example”](#complete-example) ```typescript import { SpeechSynthesis } from '@capgo/capacitor-speech-synthesis'; export class TextToSpeechService { private currentVoice: string | undefined; async init() { // Get available voices const { voices } = await SpeechSynthesis.getVoices(); // Find English voice const englishVoice = voices.find(v => v.lang.startsWith('en')); this.currentVoice = englishVoice?.voiceURI; console.log(`Using voice: ${englishVoice?.name}`); } async speak(text: string, options?: Partial) { try { // Stop any ongoing speech await SpeechSynthesis.stop(); // Speak the text await SpeechSynthesis.speak({ text, language: 'en-US', rate: 1.0, pitch: 1.0, volume: 1.0, voice: this.currentVoice, ...options }); } catch (error) { console.error('Speech synthesis failed:', error); throw error; } } async speakWithRate(text: string, rate: number) { await this.speak(text, { rate }); } async stop() { await SpeechSynthesis.stop(); } async checkIfSpeaking(): Promise { const { speaking } = await SpeechSynthesis.isSpeaking(); return speaking; } async listVoicesByLanguage(languageCode: string) { const { voices } = await SpeechSynthesis.getVoices(); return voices.filter(v => v.lang.startsWith(languageCode)); } } ``` ## Advanced Usage [Section titled “Advanced Usage”](#advanced-usage) ### Multi-language Support [Section titled “Multi-language Support”](#multi-language-support) ```typescript // Speak in different languages const speakMultiLanguage = async () => { await SpeechSynthesis.speak({ text: 'Hello!', language: 'en-US' }); await SpeechSynthesis.speak({ text: 'Bonjour!', language: 'fr-FR' }); await SpeechSynthesis.speak({ text: 'こんにちは!', language: 'ja-JP' }); }; ``` ### Voice Selection [Section titled “Voice Selection”](#voice-selection) ```typescript // Select a specific voice const selectVoice = async (languageCode: string) => { const { voices } = await SpeechSynthesis.getVoices(); // Filter voices by language const languageVoices = voices.filter(v => v.lang.startsWith(languageCode) ); // Use the first available voice if (languageVoices.length > 0) { await SpeechSynthesis.speak({ text: 'Testing voice selection', language: languageCode, voice: languageVoices[0].voiceURI }); } }; ``` ### Reading Long Text [Section titled “Reading Long Text”](#reading-long-text) ```typescript // Split and speak long text in chunks const speakLongText = async (longText: string) => { const sentences = longText.match(/[^.!?]+[.!?]+/g) || [longText]; for (const sentence of sentences) { await SpeechSynthesis.speak({ text: sentence.trim(), language: 'en-US', rate: 0.9 }); // Wait a bit between sentences await new Promise(resolve => setTimeout(resolve, 500)); } }; ``` ## Best Practices [Section titled “Best Practices”](#best-practices) 1. **Voice Selection**: Always check available voices before using a specific voice ID 2. **Language Codes**: Use standard language codes (e.g., ‘en-US’, ‘es-ES’, ‘fr-FR’) 3. **Rate Control**: Keep rate between 0.5 and 2.0 for natural-sounding speech 4. **Stop Before Speak**: Call stop() before starting new speech to avoid overlap 5. **Error Handling**: Wrap all calls in try-catch blocks for robust error handling ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) ### Common Issues [Section titled “Common Issues”](#common-issues) **No speech output**: Check device volume and ensure text is not empty **Voice not found**: Use getVoices() to find available voices on the device **Speech interrupted**: Stop any ongoing speech before starting new synthesis **Language not supported**: Check getSupportedLanguages() for available languages # @capgo/capacitor-stream-call > Enable professional video calling functionality in your app using the Stream Video SDK with full-screen support and caller information. Stream Video SDK Powered by Stream’s professional video infrastructure 🎥 Full-Screen Calls Lock-screen and full-screen call support 📱 Caller Information Display caller name and profile image 👤 Camera Controls Switch cameras and toggle video/audio 📹 Call Management Accept, reject, and manage calls seamlessly 📞 Multi-Language Localization support for iOS and Android 🌍 Getting Started Check the [Getting Started Guide](/docs/plugins/streamcall/getting-started/) to install and configure the plugin. # Getting Started > Learn how to install and integrate Stream Video SDK for professional video calling in your Capacitor app. ## Installation [Section titled “Installation”](#installation) * npm ```bash npm install @capgo/capacitor-stream-call npx cap sync ``` * yarn ```bash yarn add @capgo/capacitor-stream-call npx cap sync ``` * pnpm ```bash pnpm add @capgo/capacitor-stream-call npx cap sync ``` * bun ```bash bun add @capgo/capacitor-stream-call npx cap sync ``` ## Prerequisites [Section titled “Prerequisites”](#prerequisites) You’ll need a Stream account and API credentials. Sign up at [Stream](https://getstream.io/) if you don’t have one. ## Platform Configuration [Section titled “Platform Configuration”](#platform-configuration) ### iOS [Section titled “iOS”](#ios) Add required permissions to your `Info.plist`: ```xml NSCameraUsageDescription This app needs camera access for video calls NSMicrophoneUsageDescription This app needs microphone access for video calls ``` ### Android [Section titled “Android”](#android) Add required permissions to your `AndroidManifest.xml`: ```xml ``` ## Usage Example [Section titled “Usage Example”](#usage-example) ```typescript import { StreamCall } from '@capgo/capacitor-stream-call'; // Initialize Stream SDK await StreamCall.initialize({ apiKey: 'your-stream-api-key', userId: 'user-123', userToken: 'user-token' }); // Create a call await StreamCall.createCall({ callId: 'call-123', callType: 'default' }); // Join a call await StreamCall.joinCall({ callId: 'call-123' }); // Enable/disable camera await StreamCall.toggleCamera({ enabled: true }); // Enable/disable microphone await StreamCall.toggleMicrophone({ enabled: true }); // Switch camera (front/back) await StreamCall.switchCamera(); // Leave the call await StreamCall.leaveCall(); // Listen for call events StreamCall.addListener('callStarted', (data) => { console.log('Call started:', data); }); StreamCall.addListener('callEnded', (data) => { console.log('Call ended:', data); }); StreamCall.addListener('participantJoined', (data) => { console.log('Participant joined:', data); }); StreamCall.addListener('participantLeft', (data) => { console.log('Participant left:', data); }); ``` ## API Reference [Section titled “API Reference”](#api-reference) ### initialize(options) [Section titled “initialize(options)”](#initializeoptions) ```typescript initialize(options: InitializeOptions) => Promise ``` Initialize the Stream Video SDK. | Param | Type | | ------------- | ------------------- | | **`options`** | `InitializeOptions` | ### createCall(options) [Section titled “createCall(options)”](#createcalloptions) ```typescript createCall(options: CreateCallOptions) => Promise ``` Create a new video call. | Param | Type | | ------------- | ------------------- | | **`options`** | `CreateCallOptions` | ### joinCall(options) [Section titled “joinCall(options)”](#joincalloptions) ```typescript joinCall(options: JoinCallOptions) => Promise ``` Join an existing video call. | Param | Type | | ------------- | ----------------- | | **`options`** | `JoinCallOptions` | ### leaveCall() [Section titled “leaveCall()”](#leavecall) ```typescript leaveCall() => Promise ``` Leave the current call. ### toggleCamera(options) [Section titled “toggleCamera(options)”](#togglecameraoptions) ```typescript toggleCamera(options: { enabled: boolean }) => Promise ``` Enable or disable the camera. | Param | Type | | ------------- | ---------------------- | | **`options`** | `{ enabled: boolean }` | ### toggleMicrophone(options) [Section titled “toggleMicrophone(options)”](#togglemicrophoneoptions) ```typescript toggleMicrophone(options: { enabled: boolean }) => Promise ``` Enable or disable the microphone. | Param | Type | | ------------- | ---------------------- | | **`options`** | `{ enabled: boolean }` | ### switchCamera() [Section titled “switchCamera()”](#switchcamera) ```typescript switchCamera() => Promise ``` Switch between front and back camera. ### setSpeakerphone(options) [Section titled “setSpeakerphone(options)”](#setspeakerphoneoptions) ```typescript setSpeakerphone(options: { enabled: boolean }) => Promise ``` Enable or disable speakerphone. | Param | Type | | ------------- | ---------------------- | | **`options`** | `{ enabled: boolean }` | ### sendCallInvite(options) [Section titled “sendCallInvite(options)”](#sendcallinviteoptions) ```typescript sendCallInvite(options: InviteOptions) => Promise ``` Send a call invitation to a user. | Param | Type | | ------------- | --------------- | | **`options`** | `InviteOptions` | ### acceptCall(options) [Section titled “acceptCall(options)”](#acceptcalloptions) ```typescript acceptCall(options: { callId: string }) => Promise ``` Accept an incoming call. | Param | Type | | ------------- | -------------------- | | **`options`** | `{ callId: string }` | ### rejectCall(options) [Section titled “rejectCall(options)”](#rejectcalloptions) ```typescript rejectCall(options: { callId: string }) => Promise ``` Reject an incoming call. | Param | Type | | ------------- | -------------------- | | **`options`** | `{ callId: string }` | ## Interfaces [Section titled “Interfaces”](#interfaces) ### InitializeOptions [Section titled “InitializeOptions”](#initializeoptions-1) | Prop | Type | Description | | --------------- | -------- | --------------------------------- | | **`apiKey`** | `string` | Stream API key | | **`userId`** | `string` | User ID | | **`userToken`** | `string` | User authentication token | | **`userName`** | `string` | User display name (optional) | | **`userImage`** | `string` | User profile image URL (optional) | ### CreateCallOptions [Section titled “CreateCallOptions”](#createcalloptions-1) | Prop | Type | Description | | -------------- | ---------- | ----------------------------------------- | | **`callId`** | `string` | Unique call identifier | | **`callType`** | `string` | Call type (e.g., ‘default’, ‘audio-only’) | | **`members`** | `string[]` | Array of user IDs to invite (optional) | ### JoinCallOptions [Section titled “JoinCallOptions”](#joincalloptions-1) | Prop | Type | Description | | ------------ | -------- | --------------- | | **`callId`** | `string` | Call ID to join | ### InviteOptions [Section titled “InviteOptions”](#inviteoptions) | Prop | Type | Description | | ------------ | -------- | ----------------- | | **`callId`** | `string` | Call ID | | **`userId`** | `string` | User ID to invite | ## Event Listeners [Section titled “Event Listeners”](#event-listeners) ### Available Events [Section titled “Available Events”](#available-events) * `callStarted` - Call has started * `callEnded` - Call has ended * `participantJoined` - A participant joined the call * `participantLeft` - A participant left the call * `incomingCall` - Incoming call received * `callAccepted` - Call was accepted * `callRejected` - Call was rejected * `error` - An error occurred ### Event Example [Section titled “Event Example”](#event-example) ```typescript // Listen for incoming calls StreamCall.addListener('incomingCall', (data) => { console.log('Incoming call from:', data.callerId); console.log('Caller name:', data.callerName); // Show incoming call UI showIncomingCallScreen({ callerId: data.callerId, callerName: data.callerName, callerImage: data.callerImage, callId: data.callId }); }); // Listen for call acceptance StreamCall.addListener('callAccepted', (data) => { console.log('Call accepted'); // Navigate to call screen }); // Listen for errors StreamCall.addListener('error', (error) => { console.error('Call error:', error.message); // Handle error appropriately }); // Remove listener when done const listener = await StreamCall.addListener('callStarted', (data) => { console.log('Call started'); }); // Later... listener.remove(); ``` ## Complete Example [Section titled “Complete Example”](#complete-example) ```typescript import { StreamCall } from '@capgo/capacitor-stream-call'; class VideoCallService { async initialize(userId: string, userName: string) { try { await StreamCall.initialize({ apiKey: 'your-stream-api-key', userId: userId, userToken: await this.getUserToken(userId), userName: userName }); this.setupEventListeners(); } catch (error) { console.error('Failed to initialize Stream:', error); } } setupEventListeners() { // Handle incoming calls StreamCall.addListener('incomingCall', async (data) => { const accepted = await this.showIncomingCallDialog(data); if (accepted) { await StreamCall.acceptCall({ callId: data.callId }); await StreamCall.joinCall({ callId: data.callId }); } else { await StreamCall.rejectCall({ callId: data.callId }); } }); // Handle call events StreamCall.addListener('callStarted', () => { console.log('Call started'); }); StreamCall.addListener('callEnded', () => { console.log('Call ended'); this.navigateToHome(); }); StreamCall.addListener('participantJoined', (data) => { console.log('Participant joined:', data.participantName); }); } async startCall(recipientId: string) { try { const callId = `call-${Date.now()}`; // Create and join call await StreamCall.createCall({ callId: callId, callType: 'default', members: [recipientId] }); await StreamCall.joinCall({ callId: callId }); // Send invitation await StreamCall.sendCallInvite({ callId: callId, userId: recipientId }); console.log('Call started'); } catch (error) { console.error('Failed to start call:', error); } } async endCall() { try { await StreamCall.leaveCall(); console.log('Call ended'); } catch (error) { console.error('Failed to end call:', error); } } async toggleVideo(enabled: boolean) { await StreamCall.toggleCamera({ enabled }); } async toggleAudio(enabled: boolean) { await StreamCall.toggleMicrophone({ enabled }); } async flipCamera() { await StreamCall.switchCamera(); } private async getUserToken(userId: string): Promise { // Fetch user token from your backend const response = await fetch(`/api/stream-token?userId=${userId}`); const data = await response.json(); return data.token; } private async showIncomingCallDialog(data: any): Promise { // Show native dialog or custom UI return confirm(`Incoming call from ${data.callerName}`); } private navigateToHome() { // Navigate to home screen window.location.href = '/'; } } // Usage const videoCall = new VideoCallService(); await videoCall.initialize('user-123', 'John Doe'); // Start a call await videoCall.startCall('user-456'); // Toggle controls await videoCall.toggleVideo(false); // Disable video await videoCall.toggleAudio(false); // Mute await videoCall.flipCamera(); // Switch camera // End call await videoCall.endCall(); ``` ## Best Practices [Section titled “Best Practices”](#best-practices) * Initialize the SDK early in your app lifecycle * Handle permission requests before starting calls * Implement proper error handling for network issues * Clean up listeners when components unmount * Test on actual devices (not just emulators) * Implement reconnection logic for network interruptions * Provide visual feedback for call states * Handle background/foreground transitions ## Localization [Section titled “Localization”](#localization) The plugin supports multiple languages for native UI elements. Configure in your platform-specific settings. ## Use Cases [Section titled “Use Cases”](#use-cases) * One-on-one video calls * Group video conferences * Audio-only calls * Screen sharing sessions * Customer support video chat * Telemedicine consultations * Remote collaboration # @capgo/capacitor-textinteraction > Remove the iOS magnifier lens in kiosk or game experiences, then restore it before text entry. Control Apple’s text interaction system to tailor UX on kiosk displays, games, and interactive installations. iOS specific Toggle the magnifier lens and selection handles introduced in iOS 15. Simple API Call a single method to enable or disable text interaction state. Safe defaults Receives a boolean result so you can detect unsupported versions. Kiosk friendly Prevent long-press copy/paste on controlled installations or signage. Use the getting started guide for a minimal integration pattern and reminders to re-enable interaction before showing text inputs. # Getting Started > Learn how to install and use the Text Interaction plugin to control iOS text selection magnifier and loupe. ## Installation [Section titled “Installation”](#installation) * npm ```bash npm install @capgo/capacitor-textinteraction npx cap sync ``` * yarn ```bash yarn add @capgo/capacitor-textinteraction npx cap sync ``` * pnpm ```bash pnpm add @capgo/capacitor-textinteraction npx cap sync ``` * bun ```bash bun add @capgo/capacitor-textinteraction npx cap sync ``` ## Platform Support [Section titled “Platform Support”](#platform-support) * **iOS**: iOS 15.0+ (requires UITextInteraction API) * **Android**: No-op (returns unsupported) * **Web**: No-op (returns unsupported) ## Usage Example [Section titled “Usage Example”](#usage-example) ```typescript import { TextInteraction } from '@capgo/capacitor-textinteraction'; // Disable text interaction (remove magnifier/loupe) const result = await TextInteraction.setEnabled({ enabled: false }); if (result.success) { console.log('Text interaction disabled'); } else { console.log('Not supported on this platform'); } // Re-enable text interaction before showing text inputs await TextInteraction.setEnabled({ enabled: true }); ``` ## API Reference [Section titled “API Reference”](#api-reference) ### setEnabled(options) [Section titled “setEnabled(options)”](#setenabledoptions) ```typescript setEnabled(options: { enabled: boolean }) => Promise<{ success: boolean }> ``` Enable or disable iOS text interaction (magnifier lens and selection handles). | Param | Type | | ------------- | ---------------------- | | **`options`** | `{ enabled: boolean }` | **Returns:** `Promise<{ success: boolean }>` * Returns `{ success: true }` if the operation was successful (iOS 15+) * Returns `{ success: false }` if not supported (Android, Web, or iOS < 15) ## Practical Use Cases [Section titled “Practical Use Cases”](#practical-use-cases) ### Kiosk Applications [Section titled “Kiosk Applications”](#kiosk-applications) Disable text interaction in kiosk mode to prevent users from accessing copy/paste menus: ```typescript import { TextInteraction } from '@capgo/capacitor-textinteraction'; async function enterKioskMode() { // Disable text interaction in kiosk mode const result = await TextInteraction.setEnabled({ enabled: false }); if (result.success) { console.log('Kiosk mode: Text interaction disabled'); } } async function exitKioskMode() { // Re-enable text interaction when exiting kiosk mode await TextInteraction.setEnabled({ enabled: true }); console.log('Kiosk mode: Text interaction enabled'); } ``` ### Game Experiences [Section titled “Game Experiences”](#game-experiences) Remove magnifier lens during gameplay and restore it for text input: ```typescript import { TextInteraction } from '@capgo/capacitor-textinteraction'; class GameController { async startGame() { // Disable text interaction during gameplay await TextInteraction.setEnabled({ enabled: false }); console.log('Game started - text interaction disabled'); } async showTextInput() { // Enable text interaction before showing input await TextInteraction.setEnabled({ enabled: true }); console.log('Text input ready - interaction enabled'); // Show input field this.displayInputField(); } async hideTextInput() { // Hide input and disable interaction again this.hideInputField(); await TextInteraction.setEnabled({ enabled: false }); } } ``` ### Interactive Installations [Section titled “Interactive Installations”](#interactive-installations) Control text interaction for digital signage and interactive displays: ```typescript import { TextInteraction } from '@capgo/capacitor-textinteraction'; async function setupInteractiveDisplay() { // Disable text interaction for display mode await TextInteraction.setEnabled({ enabled: false }); // Set up touch handlers for display interaction document.addEventListener('touchstart', handleDisplayTouch); } async function enableUserInput() { // Enable text interaction when user needs to input data const result = await TextInteraction.setEnabled({ enabled: true }); if (result.success) { showInputForm(); } else { // Fallback for unsupported platforms showInputFormWithoutTextInteraction(); } } ``` ### Form Management [Section titled “Form Management”](#form-management) Toggle text interaction based on form state: ```typescript import { TextInteraction } from '@capgo/capacitor-textinteraction'; class FormManager { private textInteractionEnabled = true; async disableTextSelection() { const result = await TextInteraction.setEnabled({ enabled: false }); this.textInteractionEnabled = false; return result.success; } async enableTextSelection() { const result = await TextInteraction.setEnabled({ enabled: true }); this.textInteractionEnabled = true; return result.success; } async onFormFocus() { // Always enable before showing keyboard await this.enableTextSelection(); } async onFormBlur() { // Optionally disable after input is complete await this.disableTextSelection(); } } ``` ### Conditional Text Interaction [Section titled “Conditional Text Interaction”](#conditional-text-interaction) Platform-aware text interaction control: ```typescript import { TextInteraction } from '@capgo/capacitor-textinteraction'; import { Capacitor } from '@capacitor/core'; async function toggleTextInteraction(enabled: boolean) { // Only attempt on iOS if (Capacitor.getPlatform() === 'ios') { const result = await TextInteraction.setEnabled({ enabled }); if (result.success) { console.log(`Text interaction ${enabled ? 'enabled' : 'disabled'}`); } else { console.log('Text interaction not supported on this iOS version'); } } else { console.log('Text interaction control only available on iOS'); } } // Usage await toggleTextInteraction(false); // Disable await toggleTextInteraction(true); // Enable ``` ## Best Practices [Section titled “Best Practices”](#best-practices) * **Always re-enable before text input**: Disable text interaction when needed, but always re-enable it before showing text input fields or keyboards * **Check for platform support**: Verify `result.success` to handle unsupported platforms gracefully * **iOS-specific feature**: This plugin is iOS-only; implement fallbacks for Android and Web * **User experience**: Consider UX implications of disabling text interaction * **State management**: Track enabled/disabled state to avoid redundant calls ## Important Reminders [Section titled “Important Reminders”](#important-reminders) 1. **Re-enable for text inputs**: Always call `setEnabled({ enabled: true })` before showing text input fields, otherwise users won’t be able to properly select or edit text 2. **Platform detection**: The plugin returns `{ success: false }` on Android, Web, and iOS versions below 15.0 3. **No visual feedback**: Disabling text interaction removes the magnifier but doesn’t provide visual feedback - implement your own UI indicators if needed 4. **Safe defaults**: The text interaction system is enabled by default, matching iOS’s standard behavior ## Limitations [Section titled “Limitations”](#limitations) * iOS 15.0+ only (requires UITextInteraction API) * No effect on Android or Web platforms * Cannot selectively disable only magnifier or only selection handles * No callback for when users attempt to use disabled features ## Use Cases [Section titled “Use Cases”](#use-cases) * **Kiosk applications**: Prevent copy/paste on controlled installations * **Game experiences**: Remove magnifier during gameplay * **Interactive displays**: Control text interaction on digital signage * **Custom text editors**: Build specialized text editing experiences * **Security**: Prevent text selection in sensitive areas ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) ### Text Interaction Not Disabling [Section titled “Text Interaction Not Disabling”](#text-interaction-not-disabling) * Verify iOS version is 15.0 or higher * Check that `result.success` returns `true` * Ensure no other code is re-enabling text interaction ### Users Can’t Edit Text [Section titled “Users Can’t Edit Text”](#users-cant-edit-text) * Make sure you called `setEnabled({ enabled: true })` before showing text inputs * Verify the enable call succeeded by checking `result.success` * Test on actual iOS device (not just simulator) # @capgo/capacitor-twilio-voice > Integrate Twilio Voice API for high-quality VoIP calling with call management, audio controls, and real-time communication. ## Overview [Section titled “Overview”](#overview) The Capacitor Twilio Voice plugin enables high-quality VoIP calling functionality in iOS and Android applications using Twilio’s Voice API. This plugin provides comprehensive call management, authentication, and audio controls for creating professional calling experiences. VoIP calling High-quality voice calls over internet using Twilio Voice API 📞 Call management Make, accept, reject, and end calls with full lifecycle control 🎛️ Audio controls Mute, speaker toggle, and audio routing options 🎤 Platform support Native iOS and Android implementation with push notifications 📱 ## Installation [Section titled “Installation”](#installation) ```bash npm install @capgo/capacitor-twilio-voice npx cap sync ``` ## Core API Methods [Section titled “Core API Methods”](#core-api-methods) ### Authentication [Section titled “Authentication”](#authentication) * `login(options: { accessToken: string })` - Authenticate with Twilio using access token * `logout()` - End user session and clear call state * `isLoggedIn()` - Check current authentication status ### Call Management [Section titled “Call Management”](#call-management) * `makeCall(options: { to: string })` - Initiate outgoing call to specified number * `acceptCall(options: { callSid: string })` - Accept incoming call * `rejectCall(options: { callSid: string })` - Reject incoming call * `endCall(options?: { callSid?: string })` - Terminate active call * `muteCall(options: { muted: boolean, callSid?: string })` - Mute/unmute call audio * `setSpeaker(options: { enabled: boolean })` - Toggle speaker output ## Event Listeners [Section titled “Event Listeners”](#event-listeners) The plugin provides comprehensive event handling for: * Registration events for connection status * Call state changes (connected, disconnected, ringing) * Quality warnings and connection issues * Incoming call notifications ## Platform Configuration [Section titled “Platform Configuration”](#platform-configuration) ### iOS Setup [Section titled “iOS Setup”](#ios-setup) * Requires PushKit integration for incoming calls * Certificate configuration for production use * Microphone permissions in Info.plist ### Android Setup [Section titled “Android Setup”](#android-setup) * Firebase setup for push notifications * Microphone permissions in AndroidManifest.xml * Background service configuration ## Quick Start Example [Section titled “Quick Start Example”](#quick-start-example) ```typescript import { TwilioVoice } from '@capgo/capacitor-twilio-voice'; // Authenticate with Twilio await TwilioVoice.login({ accessToken: 'your-twilio-access-token' }); // Make a call await TwilioVoice.makeCall({ to: '+1234567890' }); // Listen for call events TwilioVoice.addListener('callConnected', (data) => { console.log('Call connected:', data); }); ``` ## Documentation [Section titled “Documentation”](#documentation) Check the [complete documentation](/docs/plugins/twilio-voice/getting-started/) for detailed setup instructions, advanced configuration, and integration examples. # Getting Started > Learn how to install and integrate Twilio Voice for VoIP calling in your Capacitor app. ## Installation [Section titled “Installation”](#installation) * npm ```bash npm install @capgo/capacitor-twilio-voice npx cap sync ``` * yarn ```bash yarn add @capgo/capacitor-twilio-voice npx cap sync ``` * pnpm ```bash pnpm add @capgo/capacitor-twilio-voice npx cap sync ``` * bun ```bash bun add @capgo/capacitor-twilio-voice npx cap sync ``` ## Prerequisites [Section titled “Prerequisites”](#prerequisites) You’ll need a Twilio account and access tokens for authentication. Sign up at [Twilio](https://www.twilio.com/) if you don’t have an account. ## Platform Configuration [Section titled “Platform Configuration”](#platform-configuration) ### iOS [Section titled “iOS”](#ios) Add required permissions to your `Info.plist`: ```xml NSMicrophoneUsageDescription This app needs microphone access for voice calls ``` For incoming calls with PushKit: 1. Enable Push Notifications in Xcode capabilities 2. Add VoIP background mode 3. Configure VoIP certificate in Twilio console ### Android [Section titled “Android”](#android) Add required permissions to your `AndroidManifest.xml`: ```xml ``` Configure Firebase for push notifications: 1. Add `google-services.json` to your Android project 2. Configure FCM in Twilio console ## Usage Example [Section titled “Usage Example”](#usage-example) ```typescript import { TwilioVoice } from '@capgo/capacitor-twilio-voice'; // Authenticate with Twilio await TwilioVoice.login({ accessToken: 'your-twilio-access-token' }); // Make a call await TwilioVoice.makeCall({ to: '+1234567890' }); // Listen for call events TwilioVoice.addListener('callConnected', (data) => { console.log('Call connected:', data.callSid); }); TwilioVoice.addListener('callDisconnected', (data) => { console.log('Call disconnected:', data.callSid); }); TwilioVoice.addListener('incomingCall', (data) => { console.log('Incoming call from:', data.from); // Accept the call TwilioVoice.acceptCall({ callSid: data.callSid }); // Or reject it // TwilioVoice.rejectCall({ callSid: data.callSid }); }); // Mute/unmute call await TwilioVoice.muteCall({ muted: true, callSid: 'current-call-sid' }); // Toggle speaker await TwilioVoice.setSpeaker({ enabled: true }); // End call await TwilioVoice.endCall({ callSid: 'current-call-sid' }); // Logout await TwilioVoice.logout(); ``` ## API Reference [Section titled “API Reference”](#api-reference) ### login(options) [Section titled “login(options)”](#loginoptions) ```typescript login(options: { accessToken: string }) => Promise ``` Authenticate with Twilio using an access token. | Param | Type | | ------------- | ------------------------- | | **`options`** | `{ accessToken: string }` | ### logout() [Section titled “logout()”](#logout) ```typescript logout() => Promise ``` End user session and clear call state. ### isLoggedIn() [Section titled “isLoggedIn()”](#isloggedin) ```typescript isLoggedIn() => Promise<{ loggedIn: boolean }> ``` Check current authentication status. **Returns:** `Promise<{ loggedIn: boolean }>` ### makeCall(options) [Section titled “makeCall(options)”](#makecalloptions) ```typescript makeCall(options: { to: string; params?: Record }) => Promise<{ callSid: string }> ``` Initiate an outgoing call to a specified number. | Param | Type | | ------------- | ------------------------------------------------- | | **`options`** | `{ to: string; params?: Record }` | **Returns:** `Promise<{ callSid: string }>` ### acceptCall(options) [Section titled “acceptCall(options)”](#acceptcalloptions) ```typescript acceptCall(options: { callSid: string }) => Promise ``` Accept an incoming call. | Param | Type | | ------------- | --------------------- | | **`options`** | `{ callSid: string }` | ### rejectCall(options) [Section titled “rejectCall(options)”](#rejectcalloptions) ```typescript rejectCall(options: { callSid: string }) => Promise ``` Reject an incoming call. | Param | Type | | ------------- | --------------------- | | **`options`** | `{ callSid: string }` | ### endCall(options?) [Section titled “endCall(options?)”](#endcalloptions) ```typescript endCall(options?: { callSid?: string }) => Promise ``` Terminate an active call. | Param | Type | | ------------- | --------------------------------- | | **`options`** | `{ callSid?: string }` (optional) | ### muteCall(options) [Section titled “muteCall(options)”](#mutecalloptions) ```typescript muteCall(options: { muted: boolean; callSid?: string }) => Promise ``` Mute or unmute call audio. | Param | Type | | ------------- | -------------------------------------- | | **`options`** | `{ muted: boolean; callSid?: string }` | ### setSpeaker(options) [Section titled “setSpeaker(options)”](#setspeakeroptions) ```typescript setSpeaker(options: { enabled: boolean }) => Promise ``` Toggle speaker output. | Param | Type | | ------------- | ---------------------- | | **`options`** | `{ enabled: boolean }` | ### sendDigits(options) [Section titled “sendDigits(options)”](#senddigitsoptions) ```typescript sendDigits(options: { digits: string; callSid?: string }) => Promise ``` Send DTMF tones during a call. | Param | Type | | ------------- | -------------------------------------- | | **`options`** | `{ digits: string; callSid?: string }` | ## Event Listeners [Section titled “Event Listeners”](#event-listeners) ### Available Events [Section titled “Available Events”](#available-events) * `registered` - Successfully registered with Twilio * `unregistered` - Unregistered from Twilio * `registrationFailed` - Registration failed * `incomingCall` - Incoming call received * `callConnected` - Call successfully connected * `callDisconnected` - Call disconnected * `callRinging` - Outgoing call is ringing * `callReconnecting` - Call is reconnecting * `callReconnected` - Call reconnected after interruption * `qualityWarning` - Call quality warning * `error` - An error occurred ### Event Examples [Section titled “Event Examples”](#event-examples) ```typescript // Handle incoming calls TwilioVoice.addListener('incomingCall', (data) => { console.log('Incoming call from:', data.from); console.log('Call SID:', data.callSid); // Show incoming call UI showIncomingCallScreen({ from: data.from, callSid: data.callSid }); }); // Handle call state changes TwilioVoice.addListener('callConnected', (data) => { console.log('Call connected:', data.callSid); startCallTimer(); }); TwilioVoice.addListener('callDisconnected', (data) => { console.log('Call ended:', data.callSid); stopCallTimer(); hideCallScreen(); }); // Handle quality warnings TwilioVoice.addListener('qualityWarning', (data) => { console.warn('Call quality warning:', data.warning); showQualityWarning(data.warning); }); // Handle errors TwilioVoice.addListener('error', (error) => { console.error('Twilio error:', error.message); handleError(error); }); // Remove listeners when done const listener = await TwilioVoice.addListener('callConnected', (data) => { console.log('Connected'); }); // Later... listener.remove(); ``` ## Complete Example [Section titled “Complete Example”](#complete-example) ```typescript import { TwilioVoice } from '@capgo/capacitor-twilio-voice'; class VoiceCallService { private currentCallSid: string | null = null; private isMuted = false; private isSpeakerOn = false; async initialize(accessToken: string) { try { // Login to Twilio await TwilioVoice.login({ accessToken }); // Check login status const { loggedIn } = await TwilioVoice.isLoggedIn(); console.log('Login status:', loggedIn); // Setup event listeners this.setupEventListeners(); } catch (error) { console.error('Failed to initialize:', error); } } setupEventListeners() { // Registration events TwilioVoice.addListener('registered', () => { console.log('Successfully registered with Twilio'); }); TwilioVoice.addListener('registrationFailed', (error) => { console.error('Registration failed:', error); }); // Incoming call TwilioVoice.addListener('incomingCall', async (data) => { console.log('Incoming call from:', data.from); const accepted = await this.showIncomingCallDialog(data.from); if (accepted) { await TwilioVoice.acceptCall({ callSid: data.callSid }); this.currentCallSid = data.callSid; } else { await TwilioVoice.rejectCall({ callSid: data.callSid }); } }); // Call events TwilioVoice.addListener('callConnected', (data) => { console.log('Call connected'); this.currentCallSid = data.callSid; this.showCallScreen(); }); TwilioVoice.addListener('callDisconnected', () => { console.log('Call disconnected'); this.currentCallSid = null; this.hideCallScreen(); }); TwilioVoice.addListener('callRinging', () => { console.log('Call ringing...'); }); // Quality warnings TwilioVoice.addListener('qualityWarning', (data) => { console.warn('Call quality warning:', data.warning); this.showQualityIndicator(data.warning); }); } async makeCall(phoneNumber: string) { try { const result = await TwilioVoice.makeCall({ to: phoneNumber, params: { // Optional custom parameters customerId: 'customer-123' } }); this.currentCallSid = result.callSid; console.log('Call initiated:', result.callSid); } catch (error) { console.error('Failed to make call:', error); } } async endCall() { if (this.currentCallSid) { await TwilioVoice.endCall({ callSid: this.currentCallSid }); this.currentCallSid = null; } } async toggleMute() { this.isMuted = !this.isMuted; await TwilioVoice.muteCall({ muted: this.isMuted, callSid: this.currentCallSid || undefined }); } async toggleSpeaker() { this.isSpeakerOn = !this.isSpeakerOn; await TwilioVoice.setSpeaker({ enabled: this.isSpeakerOn }); } async sendDTMF(digits: string) { if (this.currentCallSid) { await TwilioVoice.sendDigits({ digits, callSid: this.currentCallSid }); } } async logout() { await TwilioVoice.logout(); } private async showIncomingCallDialog(from: string): Promise { // Show native dialog or custom UI return confirm(`Incoming call from ${from}`); } private showCallScreen() { // Show call UI console.log('Showing call screen'); } private hideCallScreen() { // Hide call UI console.log('Hiding call screen'); } private showQualityIndicator(warning: string) { // Show quality warning console.log('Quality warning:', warning); } } // Usage const voiceService = new VoiceCallService(); // Initialize with access token from your backend const token = await fetchTwilioToken(); await voiceService.initialize(token); // Make a call await voiceService.makeCall('+1234567890'); // Control call await voiceService.toggleMute(); await voiceService.toggleSpeaker(); await voiceService.sendDTMF('1'); // End call await voiceService.endCall(); // Logout await voiceService.logout(); ``` ## Best Practices [Section titled “Best Practices”](#best-practices) * Fetch access tokens from your backend server, never embed them in the app * Implement token refresh logic for long-running sessions * Handle network interruptions with reconnection logic * Provide visual feedback for call states * Test on actual devices with push notifications * Implement proper error handling * Clean up listeners when components unmount * Request microphone permissions before making calls ## Security Considerations [Section titled “Security Considerations”](#security-considerations) * Never store Twilio credentials in the app * Generate access tokens on your backend * Implement token expiration and refresh * Use HTTPS for all token requests * Validate incoming calls server-side ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) ### Calls Not Connecting [Section titled “Calls Not Connecting”](#calls-not-connecting) * Verify access token is valid and not expired * Check network connectivity * Ensure microphone permissions are granted * Verify Twilio account is properly configured ### Push Notifications Not Working [Section titled “Push Notifications Not Working”](#push-notifications-not-working) * Verify PushKit/FCM certificates are configured in Twilio * Check device is registered for push notifications * Test with production certificates ### Audio Issues [Section titled “Audio Issues”](#audio-issues) * Check microphone permissions * Verify speaker/bluetooth settings * Test audio routing on actual devices ## Caller Name Display (CapacitorTwilioCallerName) [Section titled “Caller Name Display (CapacitorTwilioCallerName)”](#caller-name-display-capacitortwiliocallername) By default, incoming calls display the caller’s phone number or client ID. You can customize this by passing a `CapacitorTwilioCallerName` parameter from your TwiML backend to display a friendly name instead. ### Backend Setup [Section titled “Backend Setup”](#backend-setup) When generating your TwiML response for the `` dial, add the `CapacitorTwilioCallerName` parameter: ```java // Java example Parameter callerNameParam = new Parameter.Builder() .name("CapacitorTwilioCallerName") .value("John Doe") .build(); Client client = new Client.Builder(identity) .parameter(callerNameParam) .build(); Dial dial = new Dial.Builder() .client(client) .build(); ``` ```javascript // Node.js example const VoiceResponse = require('twilio').twiml.VoiceResponse; const response = new VoiceResponse(); const dial = response.dial(); dial.client({ name: 'CapacitorTwilioCallerName', value: 'John Doe' }, identity); ``` ### How It Works [Section titled “How It Works”](#how-it-works) 1. When your backend receives an incoming call, it generates TwiML to route the call 2. Include the `CapacitorTwilioCallerName` parameter with the caller’s display name 3. The plugin automatically extracts this parameter and uses it for: * iOS CallKit incoming call screen * Android incoming call notification * The `from` field in `incomingCall` events * The `pendingInvites` array in call status If `CapacitorTwilioCallerName` is not provided, the plugin falls back to the caller’s phone number or client ID. # @capgo/capacitor-updater > The core technology that enables live updates in Capacitor apps, plus a dedicated Electron updater package for desktop deployments. Over-the-air updates Push updates instantly without app store reviews 🚀 Advanced API Full control over update flow with comprehensive API methods 🛠️ Self-hosting ready Run your own update server for complete control 🏠 Comprehensive Documentation Check the [Plugin API](/docs/plugins/updater/api/) to master the plugin in just a few minutes. ## Quick Links [Section titled “Quick Links”](#quick-links) [Live Updates Documentation ](/docs/live-updates/)Learn about channels, update behavior, rollbacks, and more in the main Live Updates section. [Getting Started Guide ](/docs/getting-started/quickstart/)Follow our quickstart guide to add live updates to your app. ## Plugin Documentation [Section titled “Plugin Documentation”](#plugin-documentation) This technical reference covers: * **[Plugin API](/docs/plugins/updater/api/)** - All available methods and their usage * **[Plugin Settings](/docs/plugins/updater/settings/)** - Configuration options for capacitor.config * **[Known Issues](/docs/plugins/updater/known-issues/)** - Common problems and solutions * **[Debugging](/docs/plugins/updater/debugging/)** - How to troubleshoot update issues * **[Events](/docs/plugins/updater/events/)** - Available update events * **[Local Development](/docs/plugins/updater/local-dev/getting-started/)** - Test updates locally * **[Self-Hosted Mode](/docs/plugins/updater/self-hosted/getting-started/)** - Run your own update server * **[Electron Updater](/docs/plugins/electron-updater/)** - Live updates for Electron desktop apps ## Installation [Section titled “Installation”](#installation) ```bash npm install @capgo/capacitor-updater npx cap sync ``` For setup and configuration, see the [Getting Started guide](/docs/getting-started/quickstart/). # Functions and settings > All available method and settings of the plugin # Updater Plugin Config [Section titled “Updater Plugin Config”](#updater-plugin-config) See the Github [Readme](https://github.com/Cap-go/capacitor-updater) for more information. CapacitorUpdater can be configured with these options: | Prop | Type | Description | Default | Since | | ---------------------------- | -------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------- | ------- | | **`appReadyTimeout`** | `number` | Configure the number of milliseconds the native plugin should wait before considering an update ‘failed’. Available on Android, iOS, and Electron. | `10000 // (10 seconds)` | | | **`responseTimeout`** | `number` | Configure the number of milliseconds the native plugin should wait before considering API timeout. Available on Android, iOS, and Electron. | `20000 // (20 seconds)` | | | **`autoDeleteFailed`** | `boolean` | Configure whether the plugin should use automatically delete failed bundles. Available on Android, iOS, and Electron. | `true` | | | **`autoDeletePrevious`** | `boolean` | Configure whether the plugin should use automatically delete previous bundles after a successful update. Available on Android, iOS, and Electron. | `true` | | | **`autoUpdate`** | `boolean` | Configure whether the plugin should use Auto Update via an update server. Available on Android, iOS, and Electron. | `true` | | | **`resetWhenUpdate`** | `boolean` | Automatically delete previous downloaded bundles when a newer native app bundle is installed to the device. Available on Android, iOS, and Electron. | `true` | | | **`updateUrl`** | `string` | Configure the URL / endpoint to which update checks are sent. Available on Android, iOS, and Electron. | `https://plugin.capgo.app/updates` | | | **`channelUrl`** | `string` | Configure the URL / endpoint for channel operations. Available on Android, iOS, and Electron. | `https://plugin.capgo.app/channel_self` | | | **`statsUrl`** | `string` | Configure the URL / endpoint to which update statistics are sent. Available on Android, iOS, and Electron. Set to "" to disable stats reporting. | `https://plugin.capgo.app/stats` | | | **`publicKey`** | `string` | Configure the public key for end to end live update encryption Version 2. Available on Android, iOS, and Electron. | `undefined` | 6.2.0 | | **`version`** | `string` | Configure the current version of the app. This will be used for the first update request. If not set, the plugin will get the version from the native code. Available on Android, iOS, and Electron. | `undefined` | 4.17.48 | | **`directUpdate`** | `boolean \| ‘always’ \| ‘atInstall’ \| ‘onLaunch’` | Configure when the plugin should direct install updates. Only for autoUpdate mode. Works well for apps less than 10MB and with uploads done using the —delta flag. Zip or apps more than 10MB will be relatively slow for users to update. - false: Never do direct updates (use default behavior: download at start, set when backgrounded) - atInstall: Direct update only when app is installed, updated from store, otherwise act as directUpdate = false - onLaunch: Direct update only on app installed, updated from store or after app kill, otherwise act as directUpdate = false - always: Direct update in all previous cases (app installed, updated from store, after app kill or app resume), never act as directUpdate = false - true: (deprecated) Same as “always” for backward compatibility Available on Android, iOS, and Electron. | `false` | 5.1.0 | | **`autoSplashscreen`** | `boolean` | Automatically handle splashscreen hiding when using directUpdate. When enabled, the plugin will automatically hide the splashscreen after updates are applied or when no update is needed. This removes the need to manually listen for appReady events and call SplashScreen.hide(). Only works when directUpdate is set to “atInstall”, “always”, or true. Requires the @capacitor/splash-screen plugin to be installed and configured with launchAutoHide: false. Requires autoUpdate and directUpdate to be enabled. Available on Android and iOS. | `false` | 7.6.0 | | **`periodCheckDelay`** | `number` | Configure the delay period for period update check. the unit is in seconds. Available on Android, iOS, and Electron. Cannot be less than 600 seconds (10 minutes). | `600 // (10 minutes)` | | | **`localS3`** | `boolean` | Configure the CLI to use a local server for testing or self-hosted update server. | `undefined` | 4.17.48 | | **`localHost`** | `string` | Configure the CLI to use a local server for testing or self-hosted update server. | `undefined` | 4.17.48 | | **`localWebHost`** | `string` | Configure the CLI to use a local server for testing or self-hosted update server. | `undefined` | 4.17.48 | | **`localSupa`** | `string` | Configure the CLI to use a local server for testing or self-hosted update server. | `undefined` | 4.17.48 | | **`localSupaAnon`** | `string` | Configure the CLI to use a local server for testing. | `undefined` | 4.17.48 | | **`localApi`** | `string` | Configure the CLI to use a local api for testing. | `undefined` | 6.3.3 | | **`localApiFiles`** | `string` | Configure the CLI to use a local file api for testing. | `undefined` | 6.3.3 | | **`allowModifyUrl`** | `boolean` | Allow the plugin to modify the updateUrl, statsUrl and channelUrl dynamically from the JavaScript side. | `false` | 5.4.0 | | **`defaultChannel`** | `string` | Set the default channel for the app in the config. Case sensitive. This will setting will override the default channel set in the cloud, but will still respect overrides made in the cloud. | `undefined` | 5.5.0 | | **`appId`** | `string` | Configure the app id for the app in the config. | `undefined` | 6.0.0 | | **`keepUrlPathAfterReload`** | `boolean` | Configure the plugin to keep the URL path after a reload. WARNING: When a reload is triggered, ‘window\.history’ will be cleared. | `false` | 6.8.0 | | **`disableJSLogging`** | `boolean` | Disable the JavaScript logging of the plugin. if true, the plugin will not log to the JavaScript console. only the native log will be done | `false` | 7.3.0 | | **`shakeMenu`** | `boolean` | Enable shake gesture to show update menu for debugging/testing purposes | `false` | 7.5.0 | ## Examples [Section titled “Examples”](#examples) In `capacitor.config.json`: ```json { "plugins": { "CapacitorUpdater": { "appReadyTimeout": 1000 // (1 second), "responseTimeout": 10 // (10 second), "autoDeleteFailed": false, "autoDeletePrevious": false, "autoUpdate": false, "resetWhenUpdate": false, "updateUrl": https://example.com/api/auto_update, "channelUrl": https://example.com/api/channel, "statsUrl": https://example.com/api/stats, "publicKey": undefined, "version": undefined, "directUpdate": undefined, "autoSplashscreen": undefined, "periodCheckDelay": undefined, "localS3": undefined, "localHost": undefined, "localWebHost": undefined, "localSupa": undefined, "localSupaAnon": undefined, "localApi": undefined, "localApiFiles": undefined, "allowModifyUrl": undefined, "defaultChannel": undefined, "appId": undefined, "keepUrlPathAfterReload": undefined, "disableJSLogging": undefined, "shakeMenu": undefined } } } ``` In `capacitor.config.ts`: ```ts import { CapacitorConfig } from '@capacitor/cli'; const config: CapacitorConfig = { plugins: { CapacitorUpdater: { appReadyTimeout: 1000 // (1 second), responseTimeout: 10 // (10 second), autoDeleteFailed: false, autoDeletePrevious: false, autoUpdate: false, resetWhenUpdate: false, updateUrl: https://example.com/api/auto_update, channelUrl: https://example.com/api/channel, statsUrl: https://example.com/api/stats, publicKey: undefined, version: undefined, directUpdate: undefined, autoSplashscreen: undefined, periodCheckDelay: undefined, localS3: undefined, localHost: undefined, localWebHost: undefined, localSupa: undefined, localSupaAnon: undefined, localApi: undefined, localApiFiles: undefined, allowModifyUrl: undefined, defaultChannel: undefined, appId: undefined, keepUrlPathAfterReload: undefined, disableJSLogging: undefined, shakeMenu: undefined, }, }, }; export default config; ``` * [`notifyAppReady()`](#notifyappready) * [`setUpdateUrl(...)`](#setupdateurl) * [`setStatsUrl(...)`](#setstatsurl) * [`setChannelUrl(...)`](#setchannelurl) * [`download(...)`](#download) * [`next(...)`](#next) * [`set(...)`](#set) * [`delete(...)`](#delete) * [`list(...)`](#list) * [`reset(...)`](#reset) * [`current()`](#current) * [`reload()`](#reload) * [`setMultiDelay(...)`](#setmultidelay) * [`cancelDelay()`](#canceldelay) * [`getLatest(...)`](#getlatest) * [`setChannel(...)`](#setchannel) * [`unsetChannel(...)`](#unsetchannel) * [`getChannel()`](#getchannel) * [`listChannels()`](#listchannels) * [`setCustomId(...)`](#setcustomid) * [`getBuiltinVersion()`](#getbuiltinversion) * [`getDeviceId()`](#getdeviceid) * [`getPluginVersion()`](#getpluginversion) * [`isAutoUpdateEnabled()`](#isautoupdateenabled) * [`removeAllListeners()`](#removealllisteners) * [`addListener('download', ...)`](#addlistenerdownload-) * [`addListener('noNeedUpdate', ...)`](#addlistenernoneedupdate-) * [`addListener('updateAvailable', ...)`](#addlistenerupdateavailable-) * [`addListener('downloadComplete', ...)`](#addlistenerdownloadcomplete-) * [`addListener('majorAvailable', ...)`](#addlistenermajoravailable-) * [`addListener('updateFailed', ...)`](#addlistenerupdatefailed-) * [`addListener('downloadFailed', ...)`](#addlistenerdownloadfailed-) * [`addListener('appReloaded', ...)`](#addlistenerappreloaded-) * [`addListener('appReady', ...)`](#addlistenerappready-) * [`isAutoUpdateAvailable()`](#isautoupdateavailable) * [`getNextBundle()`](#getnextbundle) * [`setShakeMenu(...)`](#setshakemenu) * [`isShakeMenuEnabled()`](#isshakemenuenabled) * [Interfaces](#interfaces) * [Type Aliases](#type-aliases) # Methods [Section titled “Methods”](#methods) ## notifyAppReady() [Section titled “notifyAppReady()”](#notifyappready) ```typescript notifyAppReady() => Promise ``` Notify Capacitor Updater that the current bundle is working (a rollback will occur if this method is not called on every app launch) By default this method should be called in the first 10 sec after app launch, otherwise a rollback will occur. Change this behaviour with {@link appReadyTimeout} **Returns:** `Promise` *** ## setUpdateUrl(…) [Section titled “setUpdateUrl(…)”](#setupdateurl) ```typescript setUpdateUrl(options: UpdateUrl) => Promise ``` Set the updateUrl for the app, this will be used to check for updates. | Param | Type | Description | | ------------- | ----------- | ------------------------------------------------- | | **`options`** | `UpdateUrl` | contains the URL to use for checking for updates. | **Since:** 5.4.0 *** ## setStatsUrl(…) [Section titled “setStatsUrl(…)”](#setstatsurl) ```typescript setStatsUrl(options: StatsUrl) => Promise ``` Set the statsUrl for the app, this will be used to send statistics. Passing an empty string will disable statistics gathering. | Param | Type | Description | | ------------- | ---------- | ----------------------------------------------- | | **`options`** | `StatsUrl` | contains the URL to use for sending statistics. | **Since:** 5.4.0 *** ## setChannelUrl(…) [Section titled “setChannelUrl(…)”](#setchannelurl) ```typescript setChannelUrl(options: ChannelUrl) => Promise ``` Set the channelUrl for the app, this will be used to set the channel. | Param | Type | Description | | ------------- | ------------ | ------------------------------------------------ | | **`options`** | `ChannelUrl` | contains the URL to use for setting the channel. | **Since:** 5.4.0 *** ## download(…) [Section titled “download(…)”](#download) ```typescript download(options: DownloadOptions) => Promise ``` Download a new bundle from the provided URL, it should be a zip file, with files inside or with a unique id inside with all your files | Param | Type | Description | | ------------- | ----------------- | --------------------------------------------------------------------------------- | | **`options`** | `DownloadOptions` | The {@link [DownloadOptions](#downloadoptions)} for downloading a new bundle zip. | **Returns:** `Promise` *** ## next(…) [Section titled “next(…)”](#next) ```typescript next(options: BundleId) => Promise ``` Set the next bundle to be used when the app is reloaded. | Param | Type | Description | | ------------- | ---------- | -------------------------------------------------------------------------------------------------- | | **`options`** | `BundleId` | Contains the ID of the next Bundle to set on next app launch. {@link [BundleInfo.id](#bundleinfo)} | **Returns:** `Promise` *** ## set(…) [Section titled “set(…)”](#set) ```typescript set(options: BundleId) => Promise ``` Set the current bundle and immediately reloads the app. | Param | Type | Description | | ------------- | ---------- | -------------------------------------------------------------------------------------- | | **`options`** | `BundleId` | A {@link [BundleId](#bundleid)} object containing the new bundle id to set as current. | *** ## delete(…) [Section titled “delete(…)”](#delete) ```typescript delete(options: BundleId) => Promise ``` Deletes the specified bundle from the native app storage. Use with {@link list} to get the stored Bundle IDs. | Param | Type | Description | | ------------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------------- | | **`options`** | `BundleId` | A {@link [BundleId](#bundleid)} object containing the ID of a bundle to delete (note, this is the bundle id, NOT the version name) | *** ## list(…) [Section titled “list(…)”](#list) ```typescript list(options?: ListOptions | undefined) => Promise ``` Get all locally downloaded bundles in your app | Param | Type | Description | | ------------- | ------------- | ----------------------------------------------------------- | | **`options`** | `ListOptions` | The {@link [ListOptions](#listoptions)} for listing bundles | **Returns:** `Promise` *** ## reset(…) [Section titled “reset(…)”](#reset) ```typescript reset(options?: ResetOptions | undefined) => Promise ``` Reset the app to the `builtin` bundle (the one sent to Apple App Store / Google Play Store ) or the last successfully loaded bundle. | Param | Type | Description | | ------------- | -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **`options`** | `ResetOptions` | Containing {@link [ResetOptions.toLastSuccessful](#resetoptions)}, `true` resets to the builtin bundle and `false` will reset to the last successfully loaded bundle. | *** ## current() [Section titled “current()”](#current) ```typescript current() => Promise ``` Get the current bundle, if none are set it returns `builtin`. currentNative is the original bundle installed on the device **Returns:** `Promise` *** ## reload() [Section titled “reload()”](#reload) ```typescript reload() => Promise ``` Reload the view *** ## setMultiDelay(…) [Section titled “setMultiDelay(…)”](#setmultidelay) ```typescript setMultiDelay(options: MultiDelayConditions) => Promise ``` Sets a {@link [DelayCondition](#delaycondition)} array containing conditions that the Plugin will use to delay the update. After all conditions are met, the update process will run start again as usual, so update will be installed after a backgrounding or killing the app. For the `date` kind, the value should be an iso8601 date string. For the `background` kind, the value should be a number in milliseconds. For the `nativeVersion` kind, the value should be the version number. For the `kill` kind, the value is not used. The function has inconsistent behavior the option kill do trigger the update after the first kill and not after the next background like other options. This will be fixed in a future major release. | Param | Type | Description | | ------------- | ---------------------- | ----------------------------------------------------------------------------------------------- | | **`options`** | `MultiDelayConditions` | Containing the {@link [MultiDelayConditions](#multidelayconditions)} array of conditions to set | **Since:** 4.3.0 *** ## cancelDelay() [Section titled “cancelDelay()”](#canceldelay) ```typescript cancelDelay() => Promise ``` Cancels a {@link [DelayCondition](#delaycondition)} to process an update immediately. **Since:** 4.0.0 *** ## getLatest(…) [Section titled “getLatest(…)”](#getlatest) ```typescript getLatest(options?: GetLatestOptions | undefined) => Promise ``` Get Latest bundle available from update Url | Param | Type | | ------------- | ------------------ | | **`options`** | `GetLatestOptions` | **Returns:** `Promise` **Since:** 4.0.0 *** ## setChannel(…) [Section titled “setChannel(…)”](#setchannel) ```typescript setChannel(options: SetChannelOptions) => Promise ``` Sets the channel for this device. The channel must have `allow_device_self_set` enabled for this to work. **Important notes:** * Do not use this method to set the channel at boot. Use the `defaultChannel` in your Capacitor config instead. * This method is intended for use after the app is ready and the user has interacted (e.g., opting into a beta program). * **Public channels cannot be self-assigned.** If a channel is marked as `public`, calling `setChannel()` will return an error. To use a public channel, call `unsetChannel()` instead - the device will automatically fall back to the matching public channel. * Use `listChannels()` to discover which channels are available and whether they allow self-assignment. | Param | Type | Description | | ------------- | ------------------- | --------------------------------------------------------------------- | | **`options`** | `SetChannelOptions` | Is the {@link [SetChannelOptions](#setchanneloptions)} channel to set | **Returns:** `Promise` **Since:** 4.7.0 *** ## unsetChannel(…) [Section titled “unsetChannel(…)”](#unsetchannel) ```typescript unsetChannel(options: UnsetChannelOptions) => Promise ``` Unset the channel override for this device. After calling this method, the device will automatically receive updates from the **public channel** that matches its conditions (platform, device type, build type). This is useful when: * You want to move a device back to the default update track * You want to use a public channel (since public channels cannot be self-assigned via `setChannel()`) | Param | Type | | ------------- | --------------------- | | **`options`** | `UnsetChannelOptions` | **Since:** 4.7.0 *** ## getChannel() [Section titled “getChannel()”](#getchannel) ```typescript getChannel() => Promise ``` Get the channel for this device **Returns:** `Promise` **Since:** 4.8.0 *** ## listChannels() [Section titled “listChannels()”](#listchannels) ```typescript listChannels() => Promise ``` List all channels available for this device. Returns channels that are compatible with the device’s current environment (platform, emulator/real device, dev/prod build) and are either public or allow self-assignment. Each channel in the result includes: * `public`: If `true`, this is a **default channel**. You cannot self-assign to it using `setChannel()`. Instead, if you remove your channel assignment using `unsetChannel()`, the device will automatically receive updates from this public channel. * `allow_self_set`: If `true`, this is a **self-assignable channel**. You can explicitly assign the device to this channel using `setChannel()`. **Returns:** `Promise` **Since:** 7.5.0 *** ## setCustomId(…) [Section titled “setCustomId(…)”](#setcustomid) ```typescript setCustomId(options: SetCustomIdOptions) => Promise ``` Set a custom ID for this device | Param | Type | Description | | ------------- | -------------------- | ------------------------------------------------------------------------ | | **`options`** | `SetCustomIdOptions` | is the {@link [SetCustomIdOptions](#setcustomidoptions)} customId to set | **Since:** 4.9.0 *** ## getBuiltinVersion() [Section titled “getBuiltinVersion()”](#getbuiltinversion) ```typescript getBuiltinVersion() => Promise ``` Get the native app version or the builtin version if set in config **Returns:** `Promise` **Since:** 5.2.0 *** ## getDeviceId() [Section titled “getDeviceId()”](#getdeviceid) ```typescript getDeviceId() => Promise ``` Get unique ID used to identify device (sent to auto update server) **Returns:** `Promise` *** ## getPluginVersion() [Section titled “getPluginVersion()”](#getpluginversion) ```typescript getPluginVersion() => Promise ``` Get the native Capacitor Updater plugin version (sent to auto update server) **Returns:** `Promise` *** ## isAutoUpdateEnabled() [Section titled “isAutoUpdateEnabled()”](#isautoupdateenabled) ```typescript isAutoUpdateEnabled() => Promise ``` Get the state of auto update config. **Returns:** `Promise` *** ## removeAllListeners() [Section titled “removeAllListeners()”](#removealllisteners) ```typescript removeAllListeners() => Promise ``` Remove all listeners for this plugin. **Since:** 1.0.0 *** ## addListener(‘download’, …) [Section titled “addListener(‘download’, …)”](#addlistenerdownload) ```typescript addListener(eventName: 'download', listenerFunc: (state: DownloadEvent) => void) => Promise ``` Listen for bundle download event in the App. Fires once a download has started, during downloading and when finished. This will return you all download percent during the download | Param | Type | | ------------------ | -------------------------------- | | **`eventName`** | `’download’` | | **`listenerFunc`** | `(state: DownloadEvent) => void` | **Returns:** `Promise` **Since:** 2.0.11 *** ## addListener(‘noNeedUpdate’, …) [Section titled “addListener(‘noNeedUpdate’, …)”](#addlistenernoneedupdate) ```typescript addListener(eventName: 'noNeedUpdate', listenerFunc: (state: NoNeedEvent) => void) => Promise ``` Listen for no need to update event, useful when you want force check every time the app is launched | Param | Type | | ------------------ | ------------------------------ | | **`eventName`** | `’noNeedUpdate’` | | **`listenerFunc`** | `(state: NoNeedEvent) => void` | **Returns:** `Promise` **Since:** 4.0.0 *** ## addListener(‘updateAvailable’, …) [Section titled “addListener(‘updateAvailable’, …)”](#addlistenerupdateavailable) ```typescript addListener(eventName: 'updateAvailable', listenerFunc: (state: UpdateAvailableEvent) => void) => Promise ``` Listen for available update event, useful when you want to force check every time the app is launched | Param | Type | | ------------------ | --------------------------------------- | | **`eventName`** | `’updateAvailable’` | | **`listenerFunc`** | `(state: UpdateAvailableEvent) => void` | **Returns:** `Promise` **Since:** 4.0.0 *** ## addListener(‘downloadComplete’, …) [Section titled “addListener(‘downloadComplete’, …)”](#addlistenerdownloadcomplete) ```typescript addListener(eventName: 'downloadComplete', listenerFunc: (state: DownloadCompleteEvent) => void) => Promise ``` Listen for downloadComplete events. | Param | Type | | ------------------ | ---------------------------------------- | | **`eventName`** | `’downloadComplete’` | | **`listenerFunc`** | `(state: DownloadCompleteEvent) => void` | **Returns:** `Promise` **Since:** 4.0.0 *** ## addListener(‘majorAvailable’, …) [Section titled “addListener(‘majorAvailable’, …)”](#addlistenermajoravailable) ```typescript addListener(eventName: 'majorAvailable', listenerFunc: (state: MajorAvailableEvent) => void) => Promise ``` Listen for Major update event in the App, let you know when major update is blocked by setting disableAutoUpdateBreaking | Param | Type | | ------------------ | -------------------------------------- | | **`eventName`** | `’majorAvailable’` | | **`listenerFunc`** | `(state: MajorAvailableEvent) => void` | **Returns:** `Promise` **Since:** 2.3.0 *** ## addListener(‘updateFailed’, …) [Section titled “addListener(‘updateFailed’, …)”](#addlistenerupdatefailed) ```typescript addListener(eventName: 'updateFailed', listenerFunc: (state: UpdateFailedEvent) => void) => Promise ``` Listen for update fail event in the App, let you know when update has fail to install at next app start | Param | Type | | ------------------ | ------------------------------------ | | **`eventName`** | `’updateFailed’` | | **`listenerFunc`** | `(state: UpdateFailedEvent) => void` | **Returns:** `Promise` **Since:** 2.3.0 *** ## addListener(‘downloadFailed’, …) [Section titled “addListener(‘downloadFailed’, …)”](#addlistenerdownloadfailed) ```typescript addListener(eventName: 'downloadFailed', listenerFunc: (state: DownloadFailedEvent) => void) => Promise ``` Listen for download fail event in the App, let you know when a bundle download has failed | Param | Type | | ------------------ | -------------------------------------- | | **`eventName`** | `’downloadFailed’` | | **`listenerFunc`** | `(state: DownloadFailedEvent) => void` | **Returns:** `Promise` **Since:** 4.0.0 *** ## addListener(‘appReloaded’, …) [Section titled “addListener(‘appReloaded’, …)”](#addlistenerappreloaded) ```typescript addListener(eventName: 'appReloaded', listenerFunc: () => void) => Promise ``` Listen for reload event in the App, let you know when reload has happened | Param | Type | | ------------------ | --------------- | | **`eventName`** | `’appReloaded’` | | **`listenerFunc`** | `() => void` | **Returns:** `Promise` **Since:** 4.3.0 *** ## addListener(‘appReady’, …) [Section titled “addListener(‘appReady’, …)”](#addlistenerappready) ```typescript addListener(eventName: 'appReady', listenerFunc: (state: AppReadyEvent) => void) => Promise ``` Listen for app ready event in the App, let you know when app is ready to use | Param | Type | | ------------------ | -------------------------------- | | **`eventName`** | `’appReady’` | | **`listenerFunc`** | `(state: AppReadyEvent) => void` | **Returns:** `Promise` **Since:** 5.1.0 *** ## isAutoUpdateAvailable() [Section titled “isAutoUpdateAvailable()”](#isautoupdateavailable) ```typescript isAutoUpdateAvailable() => Promise ``` Get if auto update is available (not disabled by serverUrl). **Returns:** `Promise` *** ## getNextBundle() [Section titled “getNextBundle()”](#getnextbundle) ```typescript getNextBundle() => Promise ``` Get the next bundle that will be used when the app reloads. Returns null if no next bundle is set. **Returns:** `Promise` **Since:** 6.8.0 *** ## setShakeMenu(…) [Section titled “setShakeMenu(…)”](#setshakemenu) ```typescript setShakeMenu(options: SetShakeMenuOptions) => Promise ``` Enable or disable the shake menu for debugging/testing purposes | Param | Type | Description | | ------------- | --------------------- | -------------------------------------------------------- | | **`options`** | `SetShakeMenuOptions` | Contains enabled boolean to enable or disable shake menu | **Since:** 7.5.0 *** ## isShakeMenuEnabled() [Section titled “isShakeMenuEnabled()”](#isshakemenuenabled) ```typescript isShakeMenuEnabled() => Promise ``` Get the current state of the shake menu **Returns:** `Promise` **Since:** 7.5.0 *** ## Interfaces [Section titled “Interfaces”](#interfaces) ### AppReadyResult [Section titled “AppReadyResult”](#appreadyresult) | Prop | Type | | ------------ | ------------ | | **`bundle`** | `BundleInfo` | ### BundleInfo [Section titled “BundleInfo”](#bundleinfo) | Prop | Type | | ---------------- | -------------- | | **`id`** | `string` | | **`version`** | `string` | | **`downloaded`** | `string` | | **`checksum`** | `string` | | **`status`** | `BundleStatus` | ### UpdateUrl [Section titled “UpdateUrl”](#updateurl) | Prop | Type | | --------- | -------- | | **`url`** | `string` | ### StatsUrl [Section titled “StatsUrl”](#statsurl) | Prop | Type | | --------- | -------- | | **`url`** | `string` | ### ChannelUrl [Section titled “ChannelUrl”](#channelurl) | Prop | Type | | --------- | -------- | | **`url`** | `string` | ### DownloadOptions [Section titled “DownloadOptions”](#downloadoptions) This URL and versions are used to download the bundle from the server, If you use backend all information will be given by the method getLatest. If you don’t use backend, you need to provide the URL and version of the bundle. Checksum and sessionKey are required if you encrypted the bundle with the CLI command encrypt, you should receive them as result of the command. | Prop | Type | Description | Default | Since | | ---------------- | ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | ----- | | **`url`** | `string` | The URL of the bundle zip file (e.g: dist.zip) to be downloaded. (This can be any URL. E.g: Amazon S3, a GitHub tag, any other place you’ve hosted your bundle.) | | | | **`version`** | `string` | The version code/name of this bundle/version | | | | **`sessionKey`** | `string` | The session key for the update, when the bundle is encrypted with a session key | `undefined` | 4.0.0 | | **`checksum`** | `string` | The checksum for the update, it should be in sha256 and encrypted with private key if the bundle is encrypted | `undefined` | 4.0.0 | | **`manifest`** | `ManifestEntry[]` | The manifest for Delta (manifest) multi-file downloads | `undefined` | 6.1.0 | ### ManifestEntry [Section titled “ManifestEntry”](#manifestentry) | Prop | Type | | ------------------ | ---------------- | | **`file_name`** | `string \| null` | | **`file_hash`** | `string \| null` | | **`download_url`** | `string \| null` | ### BundleId [Section titled “BundleId”](#bundleid) | Prop | Type | | -------- | -------- | | **`id`** | `string` | ### BundleListResult [Section titled “BundleListResult”](#bundlelistresult) | Prop | Type | | ------------- | -------------- | | **`bundles`** | `BundleInfo[]` | ### ListOptions [Section titled “ListOptions”](#listoptions) | Prop | Type | Description | Default | Since | | --------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ------ | | **`raw`** | `boolean` | Whether to return the raw bundle list or the manifest. If true, the list will attempt to read the internal database instead of files on disk. | `false` | 6.14.0 | ### ResetOptions [Section titled “ResetOptions”](#resetoptions) | Prop | Type | | ---------------------- | --------- | | **`toLastSuccessful`** | `boolean` | ### CurrentBundleResult [Section titled “CurrentBundleResult”](#currentbundleresult) | Prop | Type | | ------------ | ------------ | | **`bundle`** | `BundleInfo` | | **`native`** | `string` | ### MultiDelayConditions [Section titled “MultiDelayConditions”](#multidelayconditions) | Prop | Type | | --------------------- | ------------------ | | **`delayConditions`** | `DelayCondition[]` | ### DelayCondition [Section titled “DelayCondition”](#delaycondition) | Prop | Type | Description | | ----------- | ---------------- | ---------------------------------------- | | **`kind`** | `DelayUntilNext` | Set up delay conditions in setMultiDelay | | **`value`** | `string` | | ### LatestVersion [Section titled “LatestVersion”](#latestversion) | Prop | Type | Description | Since | | ---------------- | ----------------- | -------------------------- | ----- | | **`version`** | `string` | Result of getLatest method | 4.0.0 | | **`checksum`** | `string` | | 6 | | **`major`** | `boolean` | | | | **`message`** | `string` | | | | **`sessionKey`** | `string` | | | | **`error`** | `string` | | | | **`old`** | `string` | | | | **`url`** | `string` | | | | **`manifest`** | `ManifestEntry[]` | | 6.1 | ### GetLatestOptions [Section titled “GetLatestOptions”](#getlatestoptions) | Prop | Type | Description | Default | Since | | ------------- | -------- | ------------------------------------------------------------------------------------------------ | ----------- | ----- | | **`channel`** | `string` | The channel to get the latest version for The channel must allow ‘self\_assign’ for this to work | `undefined` | 6.8.0 | ### ChannelRes [Section titled “ChannelRes”](#channelres) | Prop | Type | Description | Since | | ------------- | -------- | ----------------------------- | ----- | | **`status`** | `string` | Current status of set channel | 4.7.0 | | **`error`** | `string` | | | | **`message`** | `string` | | | ### SetChannelOptions [Section titled “SetChannelOptions”](#setchanneloptions) | Prop | Type | | ----------------------- | --------- | | **`channel`** | `string` | | **`triggerAutoUpdate`** | `boolean` | ### UnsetChannelOptions [Section titled “UnsetChannelOptions”](#unsetchanneloptions) | Prop | Type | | ----------------------- | --------- | | **`triggerAutoUpdate`** | `boolean` | ### GetChannelRes [Section titled “GetChannelRes”](#getchannelres) | Prop | Type | Description | Since | | -------------- | --------- | ----------------------------- | ----- | | **`channel`** | `string` | Current status of get channel | 4.8.0 | | **`error`** | `string` | | | | **`message`** | `string` | | | | **`status`** | `string` | | | | **`allowSet`** | `boolean` | | | ### ListChannelsResult [Section titled “ListChannelsResult”](#listchannelsresult) | Prop | Type | Description | Since | | -------------- | --------------- | -------------------------- | ----- | | **`channels`** | `ChannelInfo[]` | List of available channels | 7.5.0 | ### ChannelInfo [Section titled “ChannelInfo”](#channelinfo) | Prop | Type | Description | Since | | -------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | | **`id`** | `string` | The channel ID | 7.5.0 | | **`name`** | `string` | The channel name | 7.5.0 | | **`public`** | `boolean` | If true, this is a default/fallback channel. Devices cannot self-assign to public channels. Instead, when a device removes its channel override (using `unsetChannel()`), it will automatically receive updates from the matching public channel. | 7.5.0 | | **`allow_self_set`** | `boolean` | If true, devices can explicitly self-assign to this channel using `setChannel()`. This is typically used for beta testing, A/B testing, or opt-in update tracks. | 7.5.0 | ### SetCustomIdOptions [Section titled “SetCustomIdOptions”](#setcustomidoptions) | Prop | Type | | -------------- | -------- | | **`customId`** | `string` | ### BuiltinVersion [Section titled “BuiltinVersion”](#builtinversion) | Prop | Type | | ------------- | -------- | | **`version`** | `string` | ### DeviceId [Section titled “DeviceId”](#deviceid) | Prop | Type | | -------------- | -------- | | **`deviceId`** | `string` | ### PluginVersion [Section titled “PluginVersion”](#pluginversion) | Prop | Type | | ------------- | -------- | | **`version`** | `string` | ### AutoUpdateEnabled [Section titled “AutoUpdateEnabled”](#autoupdateenabled) | Prop | Type | | ------------- | --------- | | **`enabled`** | `boolean` | ### PluginListenerHandle [Section titled “PluginListenerHandle”](#pluginlistenerhandle) | Prop | Type | | ------------ | --------------------- | | **`remove`** | `() => Promise` | ### DownloadEvent [Section titled “DownloadEvent”](#downloadevent) | Prop | Type | Description | Since | | ------------- | ------------ | ---------------------------------------------- | ----- | | **`percent`** | `number` | Current status of download, between 0 and 100. | 4.0.0 | | **`bundle`** | `BundleInfo` | | | ### NoNeedEvent [Section titled “NoNeedEvent”](#noneedevent) | Prop | Type | Description | Since | | ------------ | ------------ | ---------------------------------------------- | ----- | | **`bundle`** | `BundleInfo` | Current status of download, between 0 and 100. | 4.0.0 | ### UpdateAvailableEvent [Section titled “UpdateAvailableEvent”](#updateavailableevent) | Prop | Type | Description | Since | | ------------ | ------------ | ---------------------------------------------- | ----- | | **`bundle`** | `BundleInfo` | Current status of download, between 0 and 100. | 4.0.0 | ### DownloadCompleteEvent [Section titled “DownloadCompleteEvent”](#downloadcompleteevent) | Prop | Type | Description | Since | | ------------ | ------------ | ------------------------------------ | ----- | | **`bundle`** | `BundleInfo` | Emit when a new update is available. | 4.0.0 | ### MajorAvailableEvent [Section titled “MajorAvailableEvent”](#majoravailableevent) | Prop | Type | Description | Since | | ------------- | -------- | ------------------------------------------ | ----- | | **`version`** | `string` | Emit when a new major bundle is available. | 4.0.0 | ### UpdateFailedEvent [Section titled “UpdateFailedEvent”](#updatefailedevent) | Prop | Type | Description | Since | | ------------ | ------------ | ------------------------------------- | ----- | | **`bundle`** | `BundleInfo` | Emit when a update failed to install. | 4.0.0 | ### DownloadFailedEvent [Section titled “DownloadFailedEvent”](#downloadfailedevent) | Prop | Type | Description | Since | | ------------- | -------- | -------------------------- | ----- | | **`version`** | `string` | Emit when a download fail. | 4.0.0 | ### AppReadyEvent [Section titled “AppReadyEvent”](#appreadyevent) | Prop | Type | Description | Since | | ------------ | ------------ | ------------------------------------- | ----- | | **`bundle`** | `BundleInfo` | Emitted when the app is ready to use. | 5.2.0 | | **`status`** | `string` | | | ### AutoUpdateAvailable [Section titled “AutoUpdateAvailable”](#autoupdateavailable) | Prop | Type | | --------------- | --------- | | **`available`** | `boolean` | ### SetShakeMenuOptions [Section titled “SetShakeMenuOptions”](#setshakemenuoptions) | Prop | Type | | ------------- | --------- | | **`enabled`** | `boolean` | ### ShakeMenuEnabled [Section titled “ShakeMenuEnabled”](#shakemenuenabled) | Prop | Type | | ------------- | --------- | | **`enabled`** | `boolean` | ## Type Aliases [Section titled “Type Aliases”](#type-aliases) ### BundleStatus [Section titled “BundleStatus”](#bundlestatus) pending: The bundle is pending to be **SET** as the next bundle. downloading: The bundle is being downloaded. success: The bundle has been downloaded and is ready to be **SET** as the next bundle. error: The bundle has failed to download. `‘success’ | ‘error’ | ‘pending’ | ‘downloading’` ### DelayUntilNext [Section titled “DelayUntilNext”](#delayuntilnext) `‘background’ | ‘kill’ | ‘nativeVersion’ | ‘date’` # Common Update Problems > Understand common Capgo live-update failure codes, what causes them, and how to fix them quickly. When an update check fails, Capgo usually returns an `error` code and a `message` in the `/updates` response. This page explains the most common failures and the fastest fixes. ## Read this first [Section titled “Read this first”](#read-this-first) * `no_new_version_available` is a normal state, not a failure. * Many “update found but not applied” reports are policy/configuration refusals rather than cache lag, especially when the response includes an explicit `error` code. * Use `npx @capgo/cli@latest app debug` while reproducing the issue to see request/response details. ## Common failure codes [Section titled “Common failure codes”](#common-failure-codes) ### `disable_auto_update_to_major` [Section titled “disable\_auto\_update\_to\_major”](#disable_auto_update_to_major) **Cause** Your channel blocks major upgrades (`disable_auto_update = major`) and the target bundle major is above the device baseline version. **Typical symptom** `version: 1.0.8` with `old: 0.0.0` means the device reports baseline `0.0.0`, so major upgrades are rejected. **How to interpret it** The backend compares major versions using device baseline `old` and target `version`. * If target is `1.0.1`, baseline major must be `1` (for example `1.0.0`). * If target is `10.0.1`, baseline major must be `10` (for example `10.0.0`). **Fix option A (recommended): align device baseline major** Set `plugins.CapacitorUpdater.version` in `capacitor.config.*` so its **MAJOR** matches the bundle MAJOR you want to deliver (for example `1.0.0` for `1.0.1`, `10.0.0` for `10.0.1`). Then apply this config to the installed app once: 1. Run `npx cap sync`. 2. Rebuild and reinstall the native app. **Fix option B: relax channel policy** Allow cross-major auto-updates in channel settings (only if that rollout strategy is intentional). Related docs: * [Version Targeting: Disable Auto-Update Across Major Versions](/docs/live-updates/version-targeting/#disable-auto-update-across-major-versions) * [Channels: Disable Auto Update strategies](/docs/live-updates/channels/#disable-auto-update-strategies) ### `disable_auto_update_to_minor` / `disable_auto_update_to_patch` [Section titled “disable\_auto\_update\_to\_minor / disable\_auto\_update\_to\_patch”](#disable_auto_update_to_minor--disable_auto_update_to_patch) **Cause** Channel policy is stricter (`minor` or `patch`) than the update being offered. **Fix** * Upload a bundle compatible with the current policy, or * change channel policy in dashboard/CLI. Related docs: * [Channels: Disable Auto Update strategies](/docs/live-updates/channels/#disable-auto-update-strategies) ### `disable_auto_update_to_metadata` [Section titled “disable\_auto\_update\_to\_metadata”](#disable_auto_update_to_metadata) **Cause** Channel uses metadata-based targeting (`version_number`) and the device baseline is below required `min_update_version`. **Fix** * Align device baseline (`CapacitorUpdater.version`) with installed native app version, or * adjust `min_update_version` / channel strategy. Related docs: * [Channels: Disable Auto Update strategies](/docs/live-updates/channels/#disable-auto-update-strategies) ### `disable_auto_update_under_native` [Section titled “disable\_auto\_update\_under\_native”](#disable_auto_update_under_native) **Cause** Channel prevents downgrades below the native baseline. **Fix** * Upload a bundle version greater than or equal to native baseline, or * disable “under native” downgrade protection for that channel. Related docs: * [Version Targeting: Auto-Downgrade Prevention](/docs/live-updates/version-targeting/#strategy-4-auto-downgrade-prevention) ### `cannot_update_via_private_channel` [Section titled “cannot\_update\_via\_private\_channel”](#cannot_update_via_private_channel) **Cause** Selected/default channel does not allow device self-assignment. **Fix** * Use a different channel with self-assignment enabled, or * make the channel public / enable self-assignment. Related docs: * [Channels: Using setChannel() from Your App](/docs/live-updates/channels/#using-setchannel-from-your-app) ### `unknown_version_build` / `semver_error` [Section titled “unknown\_version\_build / semver\_error”](#unknown_version_build--semver_error) **Cause** Device baseline version is missing (`unknown`) or not valid semver. **Fix** * Set `plugins.CapacitorUpdater.version` to a valid semver like `1.2.3`. * Sync and rebuild native app. Related docs: * [Channels: Bundle Versioning and Channels](/docs/live-updates/channels/#bundle-versioning-and-channels) * [Troubleshooting: Updates not applying](/docs/getting-started/troubleshooting/#updates-not-applying) ### `unsupported_plugin_version` [Section titled “unsupported\_plugin\_version”](#unsupported_plugin_version) **Cause** Updater plugin version is too old for current backend requirements. **Fix** * Upgrade `@capgo/capacitor-updater`. * Run `npx cap sync`. * Rebuild and reinstall native app. ### `disabled_platform_ios` / `disabled_platform_android` [Section titled “disabled\_platform\_ios / disabled\_platform\_android”](#disabled_platform_ios--disabled_platform_android) **Cause** Channel has updates disabled for that platform. **Fix** * Enable platform toggle on the channel. ### `disable_prod_build` / `disable_dev_build` / `disable_device` / `disable_emulator` [Section titled “disable\_prod\_build / disable\_dev\_build / disable\_device / disable\_emulator”](#disable_prod_build--disable_dev_build--disable_device--disable_emulator) **Cause** Channel disallows current build type or runtime target. **Fix** * Align channel options (`allow_prod`, `allow_dev`, `allow_device`, `allow_emulator`) with your test target. ### `key_id_mismatch` [Section titled “key\_id\_mismatch”](#key_id_mismatch) **Cause** Bundle encryption key and device key differ. **Fix** * Use the same encryption key/public key across app config and bundle encryption workflow. ### `no_channel` / `null_channel_data` [Section titled “no\_channel / null\_channel\_data”](#no_channel--null_channel_data) **Cause** No valid channel was resolved for the device. **Fix** * Set a cloud default channel, or * set `defaultChannel` in test builds, or * assign channel override for device. Related docs: * [Channels](/docs/live-updates/channels/) ### `on_premise_app` [Section titled “on\_premise\_app”](#on_premise_app) **Cause** The backend returned HTTP 429 with `on_premise_app`. This happens in three situations: 1. **App ID does not exist in Capgo** — the `app_id` sent by the device is not registered, so the backend has no record of it. 2. **App is flagged as on-premise** — the app exists but is configured for self-hosted updates, so the Capgo cloud endpoint refuses to serve it. 3. **Organization plan is cancelled** — the app’s organization no longer has an active subscription. **Common mistake** A typo in `plugins.CapacitorUpdater.appId` (in `capacitor.config.ts`) or a mismatch with the app ID registered in the Capgo dashboard. The backend cannot distinguish “unknown app” from “on-premise app”, so it returns the same error code. **Fix** * Verify the `app_id` matches exactly what is shown in the Capgo dashboard (case-sensitive). * If the app is not registered yet, run `npx @capgo/cli@latest app add`. * If the app is intentionally on-premise, set `plugins.CapacitorUpdater.updateUrl` to your self-hosted update endpoint instead of the Capgo cloud URL. * If the organization plan expired, renew or upgrade the plan. ## Quick diagnostic checklist [Section titled “Quick diagnostic checklist”](#quick-diagnostic-checklist) 1. Confirm app ID and channel are correct for the build. 2. Confirm `CapacitorUpdater.version` matches installed native app version. 3. Confirm channel policy (`disable_auto_update`) matches intended rollout. 4. Confirm platform/build target toggles allow this device. 5. Run `npx @capgo/cli@latest app debug` and read backend error code. ## Need more help? [Section titled “Need more help?”](#need-more-help) * [Troubleshooting](/docs/getting-started/troubleshooting/) * [How to get support](/docs/getting-help/) # Cordova > Exploring the potential availability of the capacitor-updater plugin for Cordova and the challenges involved in its development. You’ve been wondering if this plugin will ever be available for Cordova. We have started a R\&D repository for that, but it’s a huge amount of work. ## Problems [Section titled “Problems”](#problems) We know we can do it but for that, we have to read all the code of Cordova codebase as we did for Capacitor, to understand how to make it work with ap Capgo features. The Android version is easier to do since both use Java, but iOS needs a full rewrite because Swift is still not well-supported in Cordova ## Solution [Section titled “Solution”](#solution) In the mean time heres what you can do: * [Support us](https://github.com/sponsors/cap-go) on GitHub and we can prioritize that. This will need at least 1 month of work. * Hire us as a [Consultant](https://capgo.app/consulting/), we are used to help big companies migrate to Capacitor, it usually takes a month, and the [benefit](https://ionic.io/resources/articles/capacitor-vs-cordova-modern-hybrid-app-development) is huge for your team # Debugging > How to debug your updates of Capgo and understand the issue you can have with your configuration ## Understanding cloud logs: [Section titled “Understanding cloud logs:”](#understanding-cloud-logs) If you get a cloud refusal code and need concrete remediation steps, see [Common Update Problems](/docs/plugins/updater/commonproblems/). ### Sent from the backend [Section titled “Sent from the backend”](#sent-from-the-backend) | code | Description | | ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **InvalidIp** | The user is located in a Google data center and the update is less than 4 hours old. This is done to prevent Google bots’ devices from counting as devices in your account. | | **needPlanUpgrade** (previously **needUpgrade**) | Indicates that you have reached the limit of your plan, and the device will not receive updates until you upgrade or until the next month. | | **noNew** | The device has the latest available version. | | **disablePlatformIos** | The device is on the iOS platform, but that is disabled in the channel settings. | | **disablePlatformAndroid** | The device is on the Android platform, but that is disabled in the channel settings. | | **disableAutoUpdate** | ”major" | | **disableAutoUpdateUnderNative** | The device has version (`1.2.3`), and the channel has an update (`1.2.2`) under the device version to send, but that is disabled in the channel settings. | | **disableDevBuild** | The device has a dev build, but that is disabled in the channel settings. | | **disableEmulator** | The device is an emulator, but that is disabled in the channel settings. | | **cannotGetBundle** | Failed to generate a valid signed URL for the bundle download. This occurs when the bundle URL generation fails or returns an invalid URL (not starting with http/https) and there’s no manifest available as a fallback. | | **cannotUpdateViaPrivateChannel** | The device tried to self-associate with a private channel, but the channel settings don’t allow device self-association (`allow_device_self_set` is false) and the channel is not public. | | **channelMisconfigured** | The channel is configured to disable auto-update by version number (`disable_auto_update: 'version_number'`), but the bundle’s `min_update_version` field is null, making it impossible to determine which devices should receive the update. | | **disableAutoUpdateMetadata** | Auto-update is disabled by version number metadata. The channel requires the device’s version to be at least `min_update_version`, but the device’s current version is lower than this threshold. | | **disableAutoUpdateToMajor** | Channel setting `disable_auto_update: 'major'` prevents updates that would increase the major version number (e.g., blocking 1.x.x from updating to 2.x.x). | | **disableAutoUpdateToMinor** | Channel setting `disable_auto_update: 'minor'` prevents updates that would increase the minor version number (e.g., blocking 1.2.x from updating to 1.3.x). | | **disableAutoUpdateToPatch** | Channel setting `disable_auto_update: 'patch'` prevents updates that would increase the patch version number, or allows only patch-level updates within the same major.minor version (e.g., 1.2.3 can update to 1.2.4 but not 1.2.2 or 1.3.0). | | **missingBundle** | The bundle assigned to this channel has no downloadable content. This means the bundle has no `external_url`, no `r2_path`, it’s not a built-in version, and there are no manifest entries available for download. | | **NoChannelOrOverride** | No default channel is configured for this app and the device has no specific channel override assigned. At least one must be present for updates to work. | | **rateLimited** | The device has been rate limited due to excessive requests. | | **keyMismatch** | The device’s encryption public key does not match the public key used to encrypt the bundle. This happens when: (1) The public key in your app’s `capacitor.config.json` is different from the one used when uploading the bundle, or (2) You rotated your encryption keys but haven’t updated all devices yet. The response includes both `deviceKeyId` and `bundleKeyId` (first 4 characters of each public key) to help identify the mismatch. To fix this, ensure the same key pair is used both for uploading bundles (CLI uses private key) and in the app (capacitor.config.json contains public key). | ### Sent from the device [Section titled “Sent from the device”](#sent-from-the-device) | code | Description | | -------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **ping** | Internal test action used to verify the stats system is working correctly. | | **get** | Info for downloading the new version has been sent to the device. | | **delete** | One bundle has been deleted on the device. | | **set** | A bundle has been set on the device. | | **set\_fail** | The bundle failed to set. | | **reset** | The device reset to the `builtin` bundle. | | **download\_XX** | A new bundle has been downloaded - progress indicated by XX% (increments of 10%). | | **download\_complete** | The new bundle has finished downloading. | | **download\_manifest\_start** | The device started downloading the update manifest. | | **download\_manifest\_complete** | The device finished downloading the update manifest. | | **download\_zip\_start** | The device started downloading the bundle archive. | | **download\_zip\_complete** | The device finished downloading the bundle archive. | | **download\_manifest\_file\_fail** | One manifest entry failed to download; the stats payload puts `version_name` in the form `version:fileName` to pinpoint the asset. | | **download\_manifest\_checksum\_fail** | The manifest file failed checksum validation. | | **download\_manifest\_brotli\_fail** | The manifest file failed to decompress using Brotli. | | **download\_fail** | The new bundle failed to download. | | **update\_fail** | The new bundle has been installed but failed to call `notifyAppReady`. | | **checksum\_fail** | The new bundle failed to validate the checksum. This can happen for several reasons: **1) Checksum type mismatch:** The latest version of the CLI and plugins (version 5.10.0+, 6.25.0+ or 7+) use SHA256 checksums, while older plugins used CRC32. If you see a checksum fail, check if the checksum is CRC32 (a shorter hash) rather than SHA256. This usually indicates the bundle was uploaded with an old version of the CLI. Verify your bundle version in the Capgo dashboard - bundles created since version 5.10.0/6.25.0/7 should use SHA256. If you’re seeing CRC32 checksums, ensure you have the latest plugin version installed locally (the CLI checks your local plugin version to determine which checksum type to upload), then upgrade your CLI and re-upload the bundle. **2) Encryption key mismatch (on plugin versions below 8.3.0 or 5/6/7.38.0):** On older plugin versions, if the device’s public key doesn’t match the key used to encrypt the bundle, the decryption will fail silently and cause a checksum failure. If you’re using encryption and see `checksum_fail`, verify that the public key in your app’s `capacitor.config.json` matches the private key used to upload the bundle. Upgrading to plugin version 8.3.0+ (or 5/6/7.38.0+) will give you a proper `keyMismatch` error from the server instead, making this issue easier to diagnose. | | **windows\_path\_fail** | The zip has files who contain windows path who are illegal | | **canonical\_path\_fail** | The path of files is not canonical | | **directory\_path\_fail** | There is an error in the path of zip files | | **unzip\_fail** | unzip failed | | **low\_mem\_fail** | Download failed because of low memory in the device | | **app\_moved\_to\_background** | The application entered the background state. | | **app\_moved\_to\_foreground** | The application entered the foreground state. | | **decrypt\_fail** | Failed to decrypt the downloaded bundle. | | **getChannel** | The current channel for the device was queried. | | **setChannel** | A channel was successfully set for the device. | | **uninstall** | The application was uninstalled or Capgo data cleared. | | **blocked\_by\_server\_url** | Server.url is present in your capacitor config, this make Capacitor serve remote url and ignore local files, while our updater is made to function with local file, Server.url Is consider by Capacitor Makers as bad practice in production and will lead to many issue and plugin not working correctly. | ### Bundle status [Section titled “Bundle status”](#bundle-status) * `SUCCESS`: install bundle done * `ERROR`: install or download failed * `PENDING`: Download done, pending release * `DELETED`: Bundle deleted, still presented for stats * `DOWNLOADING`: Currently downloading a bundle ## Understanding device logs: [Section titled “Understanding device logs:”](#understanding-device-logs) ### Debug command: [Section titled “Debug command:”](#debug-command) There is a debug command for Capgo cloud users. ```bash npx @capgo/cli@latest app debug ``` This will allow you to check all events happening in the app and find a solution if updates don’t happen. ### IOS [Section titled “IOS”](#ios) to find your logs on Xcode [Getting the Device Log in Xcode ](https://intercom.help/deploygate/en/articles/4682692-getting-the-device-log-in-xcode) ### Android: [Section titled “Android:”](#android) to find your logs on Android studio [View logs with Logcat ](https://developer.android.com/studio/debug/am-logcat) ### Explanations Logs [Section titled “Explanations Logs”](#explanations-logs) * `Failed to download from` **=>** same as **download\_fail** * `notifyAppReady was not called, roll back current bundle` => same as as **update\_fail** ## Finding the downloaded bundle in your device [Section titled “Finding the downloaded bundle in your device”](#finding-the-downloaded-bundle-in-your-device) ### iOS [Section titled “iOS”](#ios-1) To debug on iOS, you need to dump the app on your computer, you can do it like this: Xcode has a built-in feature for inspecting the file system of developer-installed apps on an iOS device. ![Xcode Window menu showing Devices and Simulators option](/ios_debug_update_1.webp) To achieve this: * Connect your device to your Mac and select Window > Devices in the Xcode menubar. * Select your device in the left pane under the Devices section. * This will show a list of developer-installed apps for that device. * Select the app you want to inspect and then select the 3 dots icon near the bottom of the screen. * Here you can view the current file system by selecting download a snapshot of it. ![Xcode Devices panel showing app container download option](/ios_debug_update_2.webp) Selecting Download Container… will download and export a snapshot of the file system as a .xcappdata file that you can browse through. ![Downloaded xcappdata file with Show Package Contents context menu](/ios_debug_update_3.webp) Right-click on this file and select Show Package Contents to open the folder. Open the App Data folder, and you should now see a few folders like Documents, Library, tmp, etc. ![iOS app container folder structure showing Documents and Library folders](/ios_debug_update_4.webp) Then you will find a version in 2 folders: `library/NoCloud/ionic_built_snapshots` is necessary after the app reboot and `documents/versions` for hot reload ### Android [Section titled “Android”](#android-1) To debug on Android, you need to access the device from Android Studio: * Click View > Tool Windows > Device File Explorer or click the Device File Explorer button in the tool window bar to open the Device File Explorer. * Select a device from the dropdown list. * Open the path **data/data/APP\_NAME/** where **APP\_NAME is your app ID.** ![Android Studio Device File Explorer showing app data directory](/android_debug_update.webp) Then Find the `versions` folder to see all the versions Did you know? On Android, all versions are stored in one folder, unlike IOS where it has to be duplicated in two locations. ## Understanding ios production crash logs [Section titled “Understanding ios production crash logs”](#understanding-ios-production-crash-logs) [How to review your app's crash logs ](https://developer.apple.com/news/?id=nra79npr) # Events > All events you can listen to in the Capacitor Updater plugin for monitoring update status and progress The Capacitor Updater plugin provides several events you can listen to for monitoring the update process and responding to different states. ## Event Listener Setup [Section titled “Event Listener Setup”](#event-listener-setup) To listen to events, use the `addListener` method on the `CapacitorUpdater` object: ```typescript import { CapacitorUpdater } from '@capgo/capacitor-updater'; // Add a listener const listener = await CapacitorUpdater.addListener('eventName', (event) => { // Handle the event }); // Remove the listener when no longer needed listener.remove(); // Remove all listeners await CapacitorUpdater.removeAllListeners(); ``` ## Available Events [Section titled “Available Events”](#available-events) ### `download` [Section titled “download”](#download) Fired during the bundle download process. Provides download progress information. ```typescript CapacitorUpdater.addListener('download', (event) => { console.log(`Download progress: ${event.percent}%`); console.log('Bundle info:', event.bundle); }); ``` **Event Data:** * `percent`: number - Download progress percentage (0-100) * `bundle`: BundleInfo - Information about the bundle being downloaded Note This event fires multiple times during download to report progress. ### `noNeedUpdate` [Section titled “noNeedUpdate”](#noneedupdate) Fired when a check for updates determines that no update is needed. ```typescript CapacitorUpdater.addListener('noNeedUpdate', (event) => { console.log('App is up to date'); console.log('Current bundle:', event.bundle); }); ``` **Event Data:** * `bundle`: BundleInfo - Information about the current bundle ### `updateAvailable` [Section titled “updateAvailable”](#updateavailable) Fired when a new update is available for download. ```typescript CapacitorUpdater.addListener('updateAvailable', (event) => { console.log('Update available'); console.log('New bundle:', event.bundle); // You can trigger a download here if needed }); ``` **Event Data:** * `bundle`: BundleInfo - Information about the available update bundle ### `downloadComplete` [Section titled “downloadComplete”](#downloadcomplete) Fired when a bundle download has completed successfully. ```typescript CapacitorUpdater.addListener('downloadComplete', (event) => { console.log('Download completed'); console.log('Downloaded bundle:', event.bundle); // You might want to set this bundle as next }); ``` **Event Data:** * `bundle`: BundleInfo - Information about the downloaded bundle ### `majorAvailable` [Section titled “majorAvailable”](#majoravailable) Fired when a major update is available but blocked by auto-update settings. ```typescript CapacitorUpdater.addListener('majorAvailable', (event) => { console.log('Major update available:', event.version); // Notify user about major update }); ``` **Event Data:** * `version`: string - The version number of the major update Tip Major updates typically require user confirmation or app store updates. Use this event to notify users appropriately. ### `updateFailed` [Section titled “updateFailed”](#updatefailed) Fired when an update has failed to install at the next app start. ```typescript CapacitorUpdater.addListener('updateFailed', (event) => { console.error('Update failed to install'); console.log('Failed bundle:', event.bundle); // Handle rollback or retry logic }); ``` **Event Data:** * `bundle`: BundleInfo - Information about the bundle that failed to install ### `downloadFailed` [Section titled “downloadFailed”](#downloadfailed) Fired when a bundle download has failed. ```typescript CapacitorUpdater.addListener('downloadFailed', (event) => { console.error('Download failed for version:', event.version); // Handle download retry logic }); ``` **Event Data:** * `version`: string - The version that failed to download ### `appReloaded` [Section titled “appReloaded”](#appreloaded) Fired when the app has been reloaded. ```typescript CapacitorUpdater.addListener('appReloaded', () => { console.log('App has been reloaded'); // Perform any necessary reinitialization }); ``` **Event Data:** None ### `appReady` [Section titled “appReady”](#appready) Fired when the app is ready to use after an update. ```typescript CapacitorUpdater.addListener('appReady', (event) => { console.log('App is ready'); console.log('Current bundle:', event.bundle); console.log('Status:', event.status); }); ``` **Event Data:** * `bundle`: BundleInfo - Information about the current bundle * `status`: string - The ready status Caution Remember to call `notifyAppReady()` within the configured timeout (default 10 seconds) to prevent automatic rollback. ## BundleInfo Object [Section titled “BundleInfo Object”](#bundleinfo-object) Many events include a `BundleInfo` object with the following properties: ```typescript interface BundleInfo { id: string; // Unique bundle identifier version: string; // Bundle version downloaded: string; // Download timestamp checksum?: string; // Bundle checksum (if available) status: BundleStatus; // Bundle status } ``` Where `BundleStatus` can be: * `'success'` - Bundle downloaded successfully * `'error'` - Bundle download/installation failed * `'pending'` - Bundle is pending to be set as next * `'downloading'` - Bundle is currently downloading ## Example: Complete Update Flow [Section titled “Example: Complete Update Flow”](#example-complete-update-flow) Here’s an example of handling the complete update flow with events: Caution This `UpdateManager` class does not include the `CapacitorUpdater.notifyAppReady()` call required for auto-update flow. Please see the [placing notifyAppReady call correctly](/docs/plugins/updater/auto-update#placing-notifyappready-call-correctly) docs for more information. ```typescript import { CapacitorUpdater } from '@capgo/capacitor-updater'; export class UpdateManager { private listeners: any[] = []; async setupListeners() { // Listen for available updates this.listeners.push( await CapacitorUpdater.addListener('updateAvailable', async (event) => { console.log('Update available:', event.bundle.version); // Auto-download the update await CapacitorUpdater.download({ url: event.bundle.url, version: event.bundle.version }); }) ); // Monitor download progress this.listeners.push( await CapacitorUpdater.addListener('download', (event) => { console.log(`Downloading: ${event.percent}%`); // Update UI progress bar this.updateProgressBar(event.percent); }) ); // Handle download completion this.listeners.push( await CapacitorUpdater.addListener('downloadComplete', async (event) => { console.log('Download complete:', event.bundle.version); // Set as next bundle await CapacitorUpdater.next({ id: event.bundle.id }); }) ); // Handle failures this.listeners.push( await CapacitorUpdater.addListener('downloadFailed', (event) => { console.error('Download failed:', event.version); this.showError('Update download failed. Please try again later.'); }) ); this.listeners.push( await CapacitorUpdater.addListener('updateFailed', (event) => { console.error('Update installation failed:', event.bundle.version); this.showError('Update installation failed. The app has been rolled back.'); }) ); // Handle app ready this.listeners.push( await CapacitorUpdater.addListener('appReady', async (event) => { console.log('App ready with bundle:', event.bundle.version); }) ); } cleanup() { // Remove all listeners when no longer needed this.listeners.forEach(listener => listener.remove()); this.listeners = []; } private updateProgressBar(percent: number) { // Update your UI progress bar } private showError(message: string) { // Show error to user } } ``` ## Best Practices [Section titled “Best Practices”](#best-practices) 1. **Always call `notifyAppReady()`**: When using auto-update, always call this method after your app initializes to prevent rollback. 2. **Handle failures gracefully**: Implement proper error handling for download and update failures. 3. **Provide user feedback**: Use the download progress event to show update progress to users. 4. **Clean up listeners**: Remove event listeners when they’re no longer needed to prevent memory leaks. 5. **Test update scenarios**: Test various update scenarios including failures, rollbacks, and major updates. # Getting Started > Quick start guide for integrating live updates into your Capacitor app with the Updater plugin. ## Installation [Section titled “Installation”](#installation) * npm ```bash npm install @capgo/capacitor-updater npx cap sync ``` * yarn ```bash yarn add @capgo/capacitor-updater npx cap sync ``` * pnpm ```bash pnpm add @capgo/capacitor-updater npx cap sync ``` * bun ```bash bun add @capgo/capacitor-updater npx cap sync ``` ## Quick Start [Section titled “Quick Start”](#quick-start) For most users, we recommend following the [main Quickstart guide](/docs/getting-started/quickstart/) which covers both the plugin installation and Capgo cloud integration. This getting-started guide focuses on the technical plugin details for advanced users who want to understand the underlying mechanisms or implement self-hosted updates. ## Overview [Section titled “Overview”](#overview) The Capacitor Updater plugin enables over-the-air (OTA) updates for your Capacitor applications. This allows you to push updates to your app without going through app store reviews. ## How It Works [Section titled “How It Works”](#how-it-works) 1. **Bundle Download**: The plugin downloads update bundles (ZIP files containing your web assets) 2. **Extraction**: Bundles are extracted to the device’s storage 3. **Hot Reload**: The app switches to the new bundle without requiring a restart 4. **Fallback**: If an update fails, the app reverts to the previous working version ## Usage Modes [Section titled “Usage Modes”](#usage-modes) ### 1. Auto-Update Mode (Recommended) [Section titled “1. Auto-Update Mode (Recommended)”](#1-auto-update-mode-recommended) The simplest way to use the plugin with automatic update management: ```typescript import { CapacitorUpdater } from '@capgo/capacitor-updater'; // Plugin handles everything automatically // Configure in capacitor.config.ts ``` Add to your `capacitor.config.ts`: ```typescript { plugins: { CapacitorUpdater: { autoUpdate: true, updateUrl: 'https://your-update-server.com/api/updates' } } } ``` ### 2. Manual Mode [Section titled “2. Manual Mode”](#2-manual-mode) For advanced control over the update process: ```typescript import { CapacitorUpdater } from '@capgo/capacitor-updater'; // Download an update const bundle = await CapacitorUpdater.download({ url: 'https://your-server.com/updates/v1.0.1.zip', version: '1.0.1' }); // Set the bundle (will be used on next app start) await CapacitorUpdater.set({ id: bundle.id }); // Or reload immediately await CapacitorUpdater.reload(); ``` ## Platform Configuration [Section titled “Platform Configuration”](#platform-configuration) ### iOS [Section titled “iOS”](#ios) No additional configuration required. The plugin works out of the box. ### Android [Section titled “Android”](#android) No additional configuration required. The plugin works out of the box. ## Basic API Usage [Section titled “Basic API Usage”](#basic-api-usage) ### Download an Update [Section titled “Download an Update”](#download-an-update) ```typescript import { CapacitorUpdater } from '@capgo/capacitor-updater'; const bundle = await CapacitorUpdater.download({ url: 'https://example.com/update.zip', version: '1.0.1' }); console.log('Downloaded bundle:', bundle.id); ``` ### Set Active Bundle [Section titled “Set Active Bundle”](#set-active-bundle) ```typescript // Set bundle to be used on next app start await CapacitorUpdater.set({ id: bundle.id }); ``` ### Reload with New Bundle [Section titled “Reload with New Bundle”](#reload-with-new-bundle) ```typescript // Reload app immediately with new bundle await CapacitorUpdater.reload(); ``` ### List Bundles [Section titled “List Bundles”](#list-bundles) ```typescript const { bundles } = await CapacitorUpdater.list(); console.log('Available bundles:', bundles); ``` ### Delete a Bundle [Section titled “Delete a Bundle”](#delete-a-bundle) ```typescript await CapacitorUpdater.delete({ id: 'bundle-id' }); ``` ### Get Current Bundle [Section titled “Get Current Bundle”](#get-current-bundle) ```typescript const { bundle } = await CapacitorUpdater.current(); console.log('Current bundle:', bundle.version); ``` ## Event Listeners [Section titled “Event Listeners”](#event-listeners) Listen for update events: ```typescript import { CapacitorUpdater } from '@capgo/capacitor-updater'; // Listen for download progress CapacitorUpdater.addListener('download', (info) => { console.log('Download progress:', info.percent); }); // Listen for download completion CapacitorUpdater.addListener('downloadComplete', (bundle) => { console.log('Download complete:', bundle.version); }); // Listen for update failures CapacitorUpdater.addListener('updateFailed', (error) => { console.error('Update failed:', error); }); // Listen for successful updates CapacitorUpdater.addListener('updateAvailable', (info) => { console.log('Update available:', info.version); }); ``` ## Configuration Options [Section titled “Configuration Options”](#configuration-options) Configure the plugin in your `capacitor.config.ts`: ```typescript { plugins: { CapacitorUpdater: { // Auto-update settings autoUpdate: true, updateUrl: 'https://api.example.com/updates', // Update behavior resetWhenUpdate: true, directUpdate: false, // Version settings version: '1.0.0', // Security allowModifyUrl: false, // Stats collection statsUrl: 'https://api.example.com/stats', // Channel (for Capgo cloud) defaultChannel: 'production' } } } ``` ## Integration Patterns [Section titled “Integration Patterns”](#integration-patterns) ### With Capgo Cloud [Section titled “With Capgo Cloud”](#with-capgo-cloud) The easiest way to get started: ```typescript // Install the Capgo CLI npm install -g @capgo/cli // Login to Capgo npx @capgo/cli login // Upload your first bundle npx @capgo/cli bundle upload // The plugin auto-updates from Capgo cloud ``` See the [main Quickstart guide](/docs/getting-started/quickstart/) for details. ### Self-Hosted Updates [Section titled “Self-Hosted Updates”](#self-hosted-updates) Host your own update server: ```typescript // Configure your update endpoint { plugins: { CapacitorUpdater: { autoUpdate: true, updateUrl: 'https://your-server.com/api/check-update' } } } ``` Your server should return: ```json { "version": "1.0.1", "url": "https://your-server.com/updates/1.0.1.zip" } ``` See [Self-Hosted Mode](/docs/plugins/updater/self-hosted/getting-started/) for complete details. ### Manual Update Flow [Section titled “Manual Update Flow”](#manual-update-flow) Complete control over updates: ```typescript import { CapacitorUpdater } from '@capgo/capacitor-updater'; async function checkAndUpdate() { // Check for updates from your server const response = await fetch('https://api.example.com/check-update'); const { version, url } = await response.json(); // Download the update const bundle = await CapacitorUpdater.download({ url, version }); // Notify bundle is ready await CapacitorUpdater.notifyAppReady(); // Set as next version await CapacitorUpdater.set({ id: bundle.id }); // Reload when ready await CapacitorUpdater.reload(); } ``` ## Best Practices [Section titled “Best Practices”](#best-practices) * Always call `notifyAppReady()` when your app successfully loads * Test updates thoroughly before pushing to production * Implement proper error handling for network failures * Use version numbers consistently * Keep bundle sizes small for faster downloads * Monitor update success rates ## Next Steps [Section titled “Next Steps”](#next-steps) * [Plugin API Reference](/docs/plugins/updater/api/) - Complete API documentation * [Plugin Settings](/docs/plugins/updater/settings/) - All configuration options * [Events](/docs/plugins/updater/events/) - Available update events * [Self-Hosted Mode](/docs/plugins/updater/self-hosted/getting-started/) - Run your own update server * [Local Development](/docs/plugins/updater/local-dev/getting-started/) - Test updates locally * [Debugging](/docs/plugins/updater/debugging/) - Troubleshooting guide ## Support [Section titled “Support”](#support) * [Known Issues](/docs/plugins/updater/known-issues/) - Common problems and solutions * [GitHub Discussions](https://github.com/Cap-go/capacitor-updater/discussions) - Community support * [Discord](https://discord.gg/VnYRvBfgA6) - Real-time chat # Known issues > Known issues with Capacitor and Capgo, and our updater, this page will help you understand the weird issue you have with our tool ## Ionic live reload [Section titled “Ionic live reload”](#ionic-live-reload) * When you develop, if you use the Ionic live reload feature from the CLI, it will override the plugin, so you will never see your update. ## Quasar live reload [Section titled “Quasar live reload”](#quasar-live-reload) * It uses the same system as ionic under the hood, so you will not see your updates. ## Updates fail [Section titled “Updates fail”](#updates-fail) * This usually happens when large updates (> 20mb) are pushed, a big percentage of users will not get the last version.\ In the past, users needed to keep the app open until the download was done, now we use background download, but it’s still limited to a few seconds. ## Android [Section titled “Android”](#android) ### Cannot download [Section titled “Cannot download”](#cannot-download) We have seen some issues with devices in india, and got user on the call, made them try different DNS servers, and it worked. So if you have the issue, try to use a different DNS server like Cloudflare or Google DNS. Cloudflare: 1.1.1.1 and 1.0.0.1 Google DNS: 8.8.8.8 and 8.8.4.4 or dns.google [How to setup a preferred DNS server on Android? ](https://www.androidpolice.com/use-preferred-dns-server-android-tutorial/) ### Self Hosted [Section titled “Self Hosted”](#self-hosted) When you are pushing a self-hosted update, be mindful you cannot use “HTTP” endpoint as it’s against the security policies of Android apps, if you still want to do it, follow this guide: [How to allow all Network connection types HTTP and HTTPS in Android (9) Pie? ](https://stackoverflow.com/a/51902630/5511370) ### Unzip [Section titled “Unzip”](#unzip) Unzip issue: DEFLATED entries can have EXT descriptor If you zipped your bundle with something different than the CLI, the format or your zip could be incorrect, please use the CLI command `npx @capgo/cli zip BUNDLE_FOLDER`. This is a known issue of Java: [Unzip issue: DEFLATED entries can have EXT descriptor ](https://bugs.openjdk.org/browse/JDK-8143613) ### Clearfix issue [Section titled “Clearfix issue”](#clearfix-issue) * If you have issues with usesCleartextTraffic, it’s because the plugin follows the good practice recommended by sonar cloud, in 90% of the cases it will work just fine, but with some plugins that causes issues. To fix it, add in `android/app/src/main/AndroidManifest.xml` in the `` key : ```xml tools:replace="android:usesCleartextTraffic" xmlns:tools="http://schemas.android.com/tools" ``` ## IOS [Section titled “IOS”](#ios) ### Privacy manifest [Section titled “Privacy manifest”](#privacy-manifest) Add the `NSPrivacyAccessedAPICategoryUserDefaults` dictionary key to your [Privacy Manifest](https://capacitorjs.com/docs/ios/privacy-manifest) (usually `ios/App/PrivacyInfo.xcprivacy`): ```xml NSPrivacyAccessedAPITypes NSPrivacyAccessedAPIType NSPrivacyAccessedAPICategoryUserDefaults NSPrivacyAccessedAPITypeReasons CA92.1 ``` We recommend to declare [`CA92.1`](https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_use_of_required_reason_api#4278401) as the reason for accessing the [`UserDefaults`](https://developer.apple.com/documentation/foundation/userdefaults) API. ### Network permissions [Section titled “Network permissions”](#network-permissions) When using local server for testing update, the app will ask for network permission, it’s a normal behavior, it’s not the case when you use a remote server. ## Both OS [Section titled “Both OS”](#both-os) When doing manual mode updates, some events are not easy to catch, for example the update fail triggers just before your JS code reloads, so you will not be able to catch it. One alternative is to list the bundles and check error stats to know if the update fails. We need to find a better way to handle this in the future, but it’s not a priority, since auto mode is the recommended way to do update. PRs are welcome to help us improve this. ## CLI [Section titled “CLI”](#cli) if your CLI has troubles doing anything, Check if **appId** and **appName** are present in your **capacitor.config.ts** Follow the guide of the official doc: [Capacitor Configuration ](https://capacitorjs.com/docs/config) # Using the capacitor updater with self-hosted capgo > How to use the capacitor updater with self-hosted Capgo, be fully autonomous with your own instance of Capgo ## What this tutorial will cover? [Section titled “What this tutorial will cover?”](#what-this-tutorial-will-cover) This tutorial will show how to use capacitor updater in a dev environment with self hosted capgo ## Requirements [Section titled “Requirements”](#requirements) 1. [Cloned capgo](https://github.com/Cap-go/capgo) ## Getting started [Section titled “Getting started”](#getting-started) To use the capacitor updater with self-hosted capgo edit the `capacitor.config.ts` from your app directory and set it like this: ```ts const config: CapacitorConfig = { appId: 'com.demo.app', appName: 'demoApp', webDir: 'dist', bundledWebRuntime: false, plugins: { CapacitorUpdater : { statsUrl: "https://localhost:54321/functions/v1/stats", channelUrl: "https://localhost:54321/functions/v1/channel_self", updateUrl: "https://localhost:54321/functions/v1/updates" }, }, }; ``` This will enable you to use local capgo in development. However, by default, this is not enough. > By default iOS, Android, and Electron expect you to use HTTPS, you need to use a tool like Ngrock or localcan to proxy your API in https. There is a way in Android to enable [plaintext communication](https://developer.android.com/topic/security/risks/cleartext). This can be achieved by modifying [AndroidManifest.xml](https://github.com/Cap-go/capacitor-updater/blob/main/android/src/main/AndroidManifest.xml) and adding `android:usesCleartextTraffic="true"` in the `application` tag A full example of this change can be seen [here](https://gist.github.com/WcaleNieWolny/061a015acdebe35eaf3afd7030797701) There also could be a problem that prevents the android app from connecting. If you do not see any requests being send to edge functions run ```bash adb reverse tcp:54321 tcp:54321 ``` # Using the CLI with self-hosted capgo > This guide explains how to use the CLI with a self-hosted Capgo instance, enabling you to utilize Capgo tools on your own server instead of the cloud service. ## What this tutorial will cover? [Section titled “What this tutorial will cover?”](#what-this-tutorial-will-cover) This tutorial will show how to use CLI in a dev environment with self hosted capgo ## Getting started [Section titled “Getting started”](#getting-started) To use the CLI with self-hosted capgo edit the `capacitor.config.ts` from your app directory and set it like this: ```ts const config: CapacitorConfig = { appId: 'com.demo.app', appName: 'demoApp', webDir: 'dist', bundledWebRuntime: false, plugins: { CapacitorUpdater : { localHost: "http://localhost:5173", localWebHost: "http://localhost:5173", localSupa: "http://localhost:54321", localSupaAnon: "see_notes", }, }, }; ``` Note: To get `localSupaAnon` please follow [this tutorial](/docs/plugin/self-hosted/local-dev/getting-started/) and copy the `anon key` into `localSupaAnon` # Contributing > A detailed guide on how to contribute to Capgo's open source projects, including the benefits of contributing, the steps involved in making contributions, and the resources available to assist contributors in the process ## Why contribute? [Section titled “Why contribute?”](#why-contribute) First of all, thank you for considering contributing to capgo open source projects! It’s people like you that make capgo open source projects such great tools. Here are some reasons you might want to cinsder to contribute: * Contributing to capgo open source projects is a great way to earn some money from the [countless bounties](https://console.algora.io/org/Capgo) that are offered by the capgo team * Contributing to capgo open source projects is a great way to add a feature you would like to see * Contributing to capgo open source projects is a great way to fix a bug you encountered * [Capgo main’s license](https://github.com/Cap-go/capgo/blob/main/LICENSE) requires you to open source any changes you make to it. By contributing your code, you get to keep your changes open source and let others use it ## How to contribute [Section titled “How to contribute”](#how-to-contribute) * First of all, you need to fork the repository you want to contribute to * Second of all you need to commit and push your changes to that repository * Lastly, you need to open a pull request * That’s it! Now you just need to wait for the capgo team to review your PR ## Further documents to read [Section titled “Further documents to read”](#further-documents-to-read) * Capgo’s [CONTRIBUTING.MD](https://github.com/Cap-go/capgo/blob/main/CONTRIBUTING.md) * Capgo’s [BOUNTY.md](https://github.com/Cap-go/capgo/blob/main/BOUNTY.md) # Getting started > This tutorial will show how to start the Supabase from scratch as well as start edge functions, to run Capgo on your own ## What this tutorial will cover? [Section titled “What this tutorial will cover?”](#what-this-tutorial-will-cover) This tutorial will show how to start the supabase from scratch as well as start edge functions ## Requirements [Section titled “Requirements”](#requirements) 1. Cloned [capgo](https://github.com/Cap-go/capgo) 2. [supabase](https://supabase.com/) ## Getting started [Section titled “Getting started”](#getting-started) To get started run ```bash supabase start ``` Next should see something like this: ```js Started supabase local development setup. API URL: http://localhost:54321 GraphQL URL: http://localhost:54321/graphql/v1 DB URL: postgresql://postgres:postgres@localhost:54322/postgres Studio URL: http://localhost:54323 Inbucket URL: http://localhost:54324 JWT secret: [truncated] anon key: supa_key_anon service_role key: supa_key_admin ``` Next open `configs.json` and set the following values: ```json { "base_domain": { "prod": "console.capgo.app", "development": "development.console.capgo.app", "local": "localhost:3332" }, "supa_anon": { "prod": "supa_key_anon", "development": "supa_key_anon", "local": "supa_key_anon" }, "supa_url": { "prod": "http://localhost:54321", "development": "http://localhost:54321", "local": "http://localhost:54321" } } ``` where `supa_key_anon` is the value from the previous step. Danger ⚠️ Do not commit `configs.json` into the remote repo Next, verify that you can go to [localhost:54323](http://localhost:54323/projects) and that the table `users` looks something like this ![Supabase dashboard showing users table](/supabase.webp) If it does start edge functions by running: ```bash supabase functions serve ``` and start frontend by running: ```bash bun run serve ``` # Placing CapacitorUpdater.notifyAppReady() call correctly > How to properly place the CapacitorUpdater.notifyAppReady() call in your app to ensure correct auto-update flow. The placement of the `CapacitorUpdater.notifyAppReady()` call is crucial for the correct auto-update flow. If not done correctly, the app will not be able to update to the latest version. Each version that does not call `notifyAppReady()` within 10 seconds will be marked as invalid and will be replaced by the previous valid version or the default (built-in) version. In this guide we will show you how to properly place the `notifyAppReady()` call in your app to ensure correct auto-update flow. ### `notifyAppReady()` Call Placement questionnaire [Section titled “notifyAppReady() Call Placement questionnaire”](#notifyappready-call-placement-questionnaire) 1. **What language/framework do you primarily use in your app?** ![JS + DOM API](/icons/js.svg) JS + DOM API ![TS + DOM API](/icons/ts.svg) TS + DOM API ![React](/icons/react.svg) React ![Angular](/icons/angular.svg) Angular ![Vue](/icons/vue.svg) Vue ![Svelte](/icons/svelte.svg) Svelte ![Qwik](/icons/qwik.svg) Qwik 2. **** 3. **** 4. **** Note If you cannot find your framework in the questionnaire, please don’t hesitate to ask on our [Discord](https://discord.capgo.app). # Auto Update > How to use the update endpoint of Capgo with the auto-update plugin in self-hosted mode, what they are used for and what to expect This documentation will explain how to run your auto-update server. ## Serve your bundle [Section titled “Serve your bundle”](#serve-your-bundle) Make sure your bundle is served over HTTPS, and the server has the right CORS headers to allow the app to download the update. e.g. `https://myserver.com/app/updates/updates.json` If you’re unfamiliar with serving a bundle, we recommend you try Capgo Cloud or see an example here: [Serving a Bundle ](/docs/self-hosted/auto-update/update-endpoint) ## Configuration [Section titled “Configuration”](#configuration) Add an `updateUrl` to your `capacitor.config.json`. ```json { "plugins": { "CapacitorUpdater": { "updateUrl": "https://myserver.com/app/updates/updates.json", } } } ``` Caution When you are pushing a self-hosted update, be mindful you cannot use “HTTP” endpoint as it’s against the security policies of Android apps, for testing purposes you can [allow it](https://stackoverflow.com/questions/45940861/android-8-cleartext-http-traffic-not-permitted). ## Update API [Section titled “Update API”](#update-api) The plugin will do a POST call to your API each time the app is open, with this body: ```typescript interface AppInfos { "platform": "ios" | "android" | "electron", "device_id": "UUID_of_device_unique_by_install", "app_id": "APPID_FROM_CAPACITOR_CONFIG", "custom_id": "your_custom_id_set_on_runtime", "plugin_version": "PLUGIN_VERSION", "version_build": "VERSION_NUMBER_FROM_NATIVE_CODE", "version_code": "VERSION_CODE_FROM_NATIVE_CODE", "version_name": "LAST_DOWNLOADER_VERSION" | "builtin" "version_os": "VERSION_OF_SYSTEM_OS", "is_emulator": boolean, "is_prod": boolean, } ``` The server API should respond, in JSON, to the capacitor-updater plugin. With this data if an update is necessary: ```json { "version": "1.2.3", "url": "https://myserver.com/app/updates/my-new-app-2.0.0.zip", "checksum": "sha256_checksum_of_bundle" } ``` In Auto-update mode the server should compare the versions and return the right one, if the URL key is present, the plugin starts the download process. If you add “message” and “error” key, the version will not be set, and the message will be displayed in logs instead. `version` key should be in [`semver`](https://semver.org/) format. The zip should have `index.html` as a file at the root, or only one folder at the root with `index.html` inside. You can use the command of the CLI to zip your bundle: Create a bundle with your files to serve from your server ```bash npx @capgo/cli bundle zip --path [/path/to/my/bundle] ``` ## Generating Bundle Checksum [Section titled “Generating Bundle Checksum”](#generating-bundle-checksum) **Important:** You must use the Capgo CLI to create your bundle zip file. The Capgo plugin requires a specific zip format and structure that is only guaranteed when using the official CLI tool. Standard zip utilities may create incompatible archives. To generate the checksum for your bundle, use the Capgo CLI zip command with the `--json` flag: Create bundle with checksum information ```bash npx @capgo/cli bundle zip [appId] --json ``` This command will: * Create a properly formatted zip file compatible with the Capgo plugin * Generate the SHA256 checksum for integrity verification * Output bundle information in JSON format Example output: ```json { "version": "1.2.3", "checksum": "a1b2c3d4e5f6789...", "size": 1234567 } ``` Use the `checksum` value from this output in your API response to ensure the plugin can verify the bundle integrity before installation. # Encrypted Bundles > A comprehensive guide on utilizing the manual update plugin in a self-hosted environment, detailing the steps and processes involved in managing encrypted bundles for secure and efficient updates. ## End-to-end Encryption [Section titled “End-to-end Encryption”](#end-to-end-encryption) Starting with version 4.15.0 the plugin allows you to send encrypted updates. ### Step 1: Create a private key [Section titled “Step 1: Create a private key”](#step-1-create-a-private-key) Create a private key ```bash npx @capgo/cli key create ``` ### Step 2: Create and zip your bundle [Section titled “Step 2: Create and zip your bundle”](#step-2-create-and-zip-your-bundle) Create bundle zip with checksum ```bash npx @capgo/cli bundle zip [appId] --key-v2 --json ``` The `--key-v2` flag uses the new encryption system with better checksums, and the `--json` flag will output the bundle information including the checksum that you’ll need for encryption. ### Step 3: Encrypt your bundle [Section titled “Step 3: Encrypt your bundle”](#step-3-encrypt-your-bundle) Encrypt bundled zip with checksum ```bash npx @capgo/cli encrypt [path/to/zip] [checksum] ``` The `checksum` parameter is the SHA256 checksum generated by the zip command in step 2. The encrypt command will return an `ivSessionKey` and generate an encrypted checksum.Remember to rename `ivSessionKey` key as `session_key` in the update payload. ### Step 4: Use in your update payload [Section titled “Step 4: Use in your update payload”](#step-4-use-in-your-update-payload) ```json { "version": "1.2.3", "url": "https://myserver.com/app/updates/my-new-app-2.0.0.zip", "session_key": "encrypted_session_key", "checksum": "encrypted_checksum_from_encrypt_command" } ``` The `session_key` is the `ivSessionKey` returned by the encrypt command, and the `checksum` is the encrypted checksum generated during encryption (not the original checksum from the zip command). Then your app will be able to use the private key to decrypt the `session_key` and use the decrypted `session_key` to decrypt the update. The encrypted checksum ensures bundle integrity verification. ## Learn More [Section titled “Learn More”](#learn-more) [End-to-End Encryption Guide ](https://capgo.app/blog/introducing-end-to-end-security-to-capacitor-updater-with-code-signing/)Deep dive into how Capgo's encryption system works with RSA + AES cryptography [Self-hosted Live Updates ](https://capgo.app/blog/self-hosted-live-updates/)Complete workflow for setting up self-hosted updates # Getting started > A comprehensive guide on setting up and managing your own auto-update server for seamless application updates and maintenance This documentation will explain how to run your own auto-update server. ## Introduction [Section titled “Introduction”](#introduction) If you find this work helpful, please consider supporting my work by becoming a [Github sponsor](https://github.com/sponsors/riderx). I made a bet to open-source all the code I built here instead of paywalling it. By opening it up instead of fighting and hiding, I believe we can make the world a better place. Furthermore, I want to focus on Capgo tooling, and make it an open and transparent business. But to make it possible, it is necessary for all of us to do our part, including you 🥹. If Capgo doesn’t suit you, then pay your own price and [back a bootstrapped Maker](https://github.com/sponsors/riderx) on your terms. [Consider Contributing ](/docs/plugin/self-hosted/contributing/) ## Features parity [Section titled “Features parity”](#features-parity) If you choose to go with your own server, you will lose the 5-min setup flow.\ You need to implement all of these features yourself. | Features | Capgo | Self hosted | | ------------------------ | ----- | ----------- | | Updates | ✅ | 🚧 | | Auto revert | ✅ | 🚧 | | Email alerts on fail | ✅ | 🚧 | | Channels | ✅ | 🚧 | | Channels Override | ✅ | 🚧 | | Device Override | ✅ | 🚧 | | Channels Settings | ✅ | 🚧 | | Device Settings | ✅ | 🚧 | | Custom ID | ✅ | 🚧 | | Auto Set Channels | ✅ | 🚧 | | API Channels | ✅ | 🚧 | | Updates Statistics | ✅ | 🚧 | | Fail Download Statistics | ✅ | 🚧 | | App Usage Statistics | ✅ | 🚧 | | Update Encryption | ✅ | 🚧 | | Delta (manifest) updates | ✅ | ❌ | Danger If you send a bad update to your users you can and will break their app. > Be mindful that you can’t use the Capgo cloud and your server at the same time. Danger The Over-the-Air (OTA) update feature is applicable only for modifications made to HTML, CSS, and JavaScript files. If you make any changes to the native code, such as updates to Capacitor plugins, it is mandatory to resubmit the application to the app store for approval. ## Choose between Auto and Manual [Section titled “Choose between Auto and Manual”](#choose-between-auto-and-manual) In auto mode, part of the logic is handled by the Native code, updates are decided server side, this is more secure and allows fine grain updates, partial deployment to one device or group and more. In manual mode, all the logic is handled by the JS. [Auto Update ](/docs/plugin/self-hosted/auto-update/) [Manual ](/docs/plugin/self-hosted/manual-update/) ## Install Capacitor updater [Section titled “Install Capacitor updater”](#install-capacitor-updater) Install the Capacitor updater ```bash npm install @capgo/capacitor-updater npx cap sync ``` ## Prepare your bundle [Section titled “Prepare your bundle”](#prepare-your-bundle) To send updates to your app, you need to zip it. The best way to be certain your zip is good is to use the Capgo CLI for zipping. Create a bundle with your files to serve from your server ```bash npx @capgo/cli@latest bundle zip ``` You will have to serve this zip from your server on your own. [Auto Update ](/docs/plugin/self-hosted/auto-update/) [Manual ](/docs/plugin/self-hosted/manual-update/) Note If this seems like a lot of work, try the trial Capgo Cloud. Note Delta (manifest) updates will not work in manual updates, but they can work in auto update. Right now it’s not documented because it’s a pretty complex process. # Channel API Endpoint > How to build channel management endpoints for self-hosted Capgo to handle device-channel assignments and channel queries Channels are a core mechanism for managing app updates in Capgo. In self-hosted mode, you need to implement channel endpoints to handle device assignments, channel queries, and channel management operations. ## Understanding Channels [Section titled “Understanding Channels”](#understanding-channels) Channels allow you to: * **Control update distribution**: Assign different app versions to different user groups * **A/B testing**: Test new features with specific user segments * **Staged rollouts**: Gradually deploy updates to minimize risk * **Environment separation**: Separate development, staging, and production updates ## Configuration [Section titled “Configuration”](#configuration) Configure the channel endpoint URL in your `capacitor.config.json`: ```json { "plugins": { "CapacitorUpdater": { "channelUrl": "https://myserver.com/api/channel_self" } } } ``` ## Channel Operations [Section titled “Channel Operations”](#channel-operations) The plugin performs different channel operations that your endpoint needs to handle: ### 1. List Compatible Channels (GET Request) [Section titled “1. List Compatible Channels (GET Request)”](#1-list-compatible-channels-get-request) When the plugin calls `listChannels()`, it sends a GET request to retrieve all channels that are compatible with the device. This returns channels that match the device’s environment (dev/prod, emulator/real device) and allow either public access or self-assignment. #### Request Format [Section titled “Request Format”](#request-format) ```typescript // GET /api/channel_self // Headers: { "Content-Type": "application/json" } // Query parameters: interface ListChannelsRequest { app_id: string platform: "ios" | "android" | "electron" is_emulator: boolean is_prod: boolean key_id?: string } ``` #### Response Format [Section titled “Response Format”](#response-format) ```json [ { "id": 1, "name": "production", "public": true, "allow_self_set": false }, { "id": 2, "name": "beta", "public": false, "allow_self_set": true } ] ``` #### Understanding Channel Types [Section titled “Understanding Channel Types”](#understanding-channel-types) The response includes two important flags for each channel: * **`public: true`**: This is a **default channel**. Devices cannot self-assign to it using `setChannel()`. Instead, if a device removes its channel assignment (using `unsetChannel()`), it will automatically receive updates from this public channel if it matches the device’s conditions. * **`allow_self_set: true`**: This is a **self-assignable channel**. Devices can explicitly assign themselves to this channel using `setChannel()`. This is useful for beta testing, A/B testing, or allowing users to opt-in to specific update tracks. Note A channel can be either `public` OR `allow_self_set`, but typically not both. Public channels serve as the default fallback, while self-assignable channels require explicit opt-in. ### 2. Get Channel (PUT Request) [Section titled “2. Get Channel (PUT Request)”](#2-get-channel-put-request) When the plugin calls `getChannel()`, it sends a PUT request to retrieve the device’s current channel assignment. #### Request Format [Section titled “Request Format”](#request-format-1) ```typescript // PUT /api/channel_self // Headers: { "Content-Type": "application/json" } // Body: interface GetChannelRequest { device_id: string app_id: string platform: "ios" | "android" | "electron" plugin_version: string version_build: string version_code: string version_name: string is_emulator: boolean is_prod: boolean defaultChannel?: string channel?: string // For newer plugin versions, contains local channel override } ``` #### Response Format [Section titled “Response Format”](#response-format-1) ```json { "status": "ok", "channel": "production", "allowSet": true, "message": "", "error": "" } ``` ### 3. Set Channel (POST Request) [Section titled “3. Set Channel (POST Request)”](#3-set-channel-post-request) When the plugin calls `setChannel()`, it sends a POST request to assign the device to a specific channel. #### Request Format [Section titled “Request Format”](#request-format-2) ```typescript // POST /api/channel_self interface SetChannelRequest { device_id: string app_id: string channel: string platform: "ios" | "android" | "electron" plugin_version: string version_build: string version_code: string version_name: string is_emulator: boolean is_prod: boolean } ``` #### Response Format [Section titled “Response Format”](#response-format-2) ```json { "status": "ok", "message": "Device assigned to channel successfully", "error": "" } ``` #### Error Cases [Section titled “Error Cases”](#error-cases) When a device tries to assign itself to a **public channel** (one with `public: true`), your endpoint should return an error: ```json { "status": "error", "error": "public_channel_self_set_not_allowed", "message": "This channel is public and does not allow device self-assignment. Unset the channel and the device will automatically use the public channel." } ``` When a device tries to assign itself to a channel that doesn’t allow self-assignment: ```json { "status": "error", "error": "channel_self_set_not_allowed", "message": "This channel does not allow devices to self associate" } ``` ### 4. Unset Channel (DELETE Request) [Section titled “4. Unset Channel (DELETE Request)”](#4-unset-channel-delete-request) When the plugin calls `unsetChannel()`, it sends a DELETE request to remove the device’s channel assignment. #### Request Format [Section titled “Request Format”](#request-format-3) ```typescript // DELETE /api/channel_self interface UnsetChannelRequest { device_id: string app_id: string platform: "ios" | "android" | "electron" plugin_version: string version_build: string version_code: string version_name: string } ``` ## Implementation Example [Section titled “Implementation Example”](#implementation-example) Here’s a JavaScript example of how to implement the channel endpoint: ```typescript interface ChannelRequest { device_id: string app_id: string channel?: string platform: "ios" | "android" | "electron" plugin_version: string version_build: string version_code: string version_name: string } interface ChannelResponse { status: "ok" | "error" channel?: string allowSet?: boolean message?: string error?: string } export const handler = async (event) => { const method = event.httpMethod || event.method const body = JSON.parse(event.body || '{}') as ChannelRequest const { device_id, app_id, channel, platform } = body try { switch (method) { case 'GET': return await getDeviceChannel(device_id, app_id) case 'POST': return await setDeviceChannel(device_id, app_id, channel!, platform) case 'DELETE': return await unsetDeviceChannel(device_id, app_id) default: return { status: "error", error: "Method not allowed" } } } catch (error) { return { status: "error", error: error.message } } } async function getDeviceChannel(deviceId: string, appId: string): Promise { // Query your database for device channel assignment const assignment = await database.getDeviceChannel(deviceId, appId) if (assignment) { return { status: "ok", channel: assignment.channel, allowSet: assignment.allowSelfAssign } } // Return default channel if no assignment found return { status: "ok", channel: "production", // Your default channel allowSet: true } } async function setDeviceChannel( deviceId: string, appId: string, channel: string, platform: string ): Promise { // Validate channel exists and allows self-assignment const channelConfig = await database.getChannelConfig(channel, appId) if (!channelConfig) { return { status: "error", error: "Channel not found" } } if (!channelConfig.allowDeviceSelfSet) { return { status: "error", error: "Channel does not allow self-assignment" } } // Check platform restrictions if (platform === "ios" && !channelConfig.ios) { return { status: "error", error: "Channel not available for iOS" } } if (platform === "android" && !channelConfig.android) { return { status: "error", error: "Channel not available for Android" } } if (platform === "electron" && !channelConfig.electron) { return { status: "error", error: "Channel not available for Electron" } } // Save the assignment await database.setDeviceChannel(deviceId, appId, channel) return { status: "ok", message: "Device assigned to channel successfully" } } async function unsetDeviceChannel(deviceId: string, appId: string): Promise { // Remove device channel assignment await database.removeDeviceChannel(deviceId, appId) return { status: "ok", message: "Device channel assignment removed" } } ``` ## Channel Configuration [Section titled “Channel Configuration”](#channel-configuration) Your channel system should support these configuration options: ```typescript interface ChannelConfig { name: string appId: string // Platform targeting ios: boolean // Allow updates to iOS devices android: boolean // Allow updates to Android devices electron: boolean // Allow updates to Electron apps // Device type restrictions allow_emulator: boolean // Allow updates on emulator/simulator devices allow_device: boolean // Allow updates on real/physical devices // Build type restrictions allow_dev: boolean // Allow updates on development builds (is_prod=false) allow_prod: boolean // Allow updates on production builds (is_prod=true) // Channel assignment public: boolean // Default channel - devices fall back to this when no override allowDeviceSelfSet: boolean // Allow devices to self-assign via setChannel() // Update policies disableAutoUpdate: "major" | "minor" | "version_number" | "none" disableAutoUpdateUnderNative: boolean } ``` ### Device Filtering Logic [Section titled “Device Filtering Logic”](#device-filtering-logic) When listing compatible channels (GET request), you should filter channels based on these conditions: 1. **Platform check**: Channel must allow the device’s platform (`ios`, `android`, or `electron`) 2. **Device type check**: * If `is_emulator=true`: Channel must have `allow_emulator=true` * If `is_emulator=false`: Channel must have `allow_device=true` 3. **Build type check**: * If `is_prod=true`: Channel must have `allow_prod=true` * If `is_prod=false`: Channel must have `allow_dev=true` 4. **Visibility check**: Channel must be either `public=true` OR `allow_device_self_set=true` ```typescript // Example filtering logic function getCompatibleChannels( platform: 'ios' | 'android' | 'electron', isEmulator: boolean, isProd: boolean, channels: ChannelConfig[] ): ChannelConfig[] { return channels.filter(channel => { // Platform check if (!channel[platform]) return false // Device type check if (isEmulator && !channel.allow_emulator) return false if (!isEmulator && !channel.allow_device) return false // Build type check if (isProd && !channel.allow_prod) return false if (!isProd && !channel.allow_dev) return false // Must be accessible (public or self-assignable) if (!channel.public && !channel.allowDeviceSelfSet) return false return true }) } ``` ## Database Schema Example [Section titled “Database Schema Example”](#database-schema-example) You’ll need to store channel configurations and device assignments: ```sql -- Channels table CREATE TABLE channels ( id SERIAL PRIMARY KEY, name VARCHAR(255) NOT NULL, app_id VARCHAR(255) NOT NULL, -- Platform targeting ios BOOLEAN DEFAULT true, android BOOLEAN DEFAULT true, electron BOOLEAN DEFAULT true, -- Device type restrictions allow_emulator BOOLEAN DEFAULT true, -- Allow emulator/simulator devices allow_device BOOLEAN DEFAULT true, -- Allow real/physical devices -- Build type restrictions allow_dev BOOLEAN DEFAULT true, -- Allow development builds allow_prod BOOLEAN DEFAULT true, -- Allow production builds -- Channel assignment public BOOLEAN DEFAULT false, -- Default channel (fallback) allow_device_self_set BOOLEAN DEFAULT false, -- Allow self-assignment -- Update policies disable_auto_update VARCHAR(50) DEFAULT 'none', disable_auto_update_under_native BOOLEAN DEFAULT false, created_at TIMESTAMP DEFAULT NOW(), UNIQUE(name, app_id) ); -- Device channel assignments table CREATE TABLE device_channels ( id SERIAL PRIMARY KEY, device_id VARCHAR(255) NOT NULL, app_id VARCHAR(255) NOT NULL, channel_name VARCHAR(255) NOT NULL, assigned_at TIMESTAMP DEFAULT NOW(), UNIQUE(device_id, app_id) ); ``` ## Error Handling [Section titled “Error Handling”](#error-handling) Handle common error scenarios: ```typescript // Channel not found { "status": "error", "error": "Channel 'beta' not found" } // Self-assignment not allowed { "status": "error", "error": "Channel does not allow device self-assignment" } // Platform not supported { "status": "error", "error": "Channel not available for this platform" } // Invalid request { "status": "error", "error": "Missing required field: device_id" } ``` ## Best Practices [Section titled “Best Practices”](#best-practices) 1. **Security**: Validate all channel assignments against your business rules 2. **Logging**: Log all channel operations for auditing and debugging 3. **Performance**: Cache channel configurations to reduce database queries 4. **Validation**: Verify device\_id and app\_id authenticity 5. **Rate Limiting**: Implement rate limiting to prevent abuse ## Integration with Updates [Section titled “Integration with Updates”](#integration-with-updates) Channel assignments work together with your [Update API Endpoint](/docs/plugin/self-hosted/handling-updates/). When a device requests an update, check its channel assignment to determine which version to serve: ```typescript async function getUpdateForDevice(deviceId: string, appId: string) { // Get device's channel assignment const channelAssignment = await getDeviceChannel(deviceId, appId) const channel = channelAssignment.channel || 'production' // Get the version assigned to this channel const channelVersion = await getChannelVersion(channel, appId) return { version: channelVersion.version, url: channelVersion.url, checksum: channelVersion.checksum } } ``` This creates a complete self-hosted channel management system that gives you full control over how updates are distributed to your users. # Statistics API > How to use the statistics endpoint of Capgo with the auto-update plugin in self-hosted mode, what they are used for and what to expect ## Statistics API [Section titled “Statistics API”](#statistics-api) Starting from version 1.3.0 the update system is able to send stats! By default, all stats are sent to our server, to understand usage and research. Note No private data is sent for stats, only random UUID, version update, version native app, platform, action, and app ID. If you want to send this data to your server instead, change the config below: ```tsx // capacitor.config.json { "appId": "**.***.**", "appName": "Name", "plugins": { "CapacitorUpdater": { "statsUrl": "YOUR_URL" } } } ``` ## Data Structure [Section titled “Data Structure”](#data-structure) What your server will receive is: ```tsx interface AppInfosStats { "action": "set", // can be set, delete, set_fail, reset, revert // Then it's the same info as update "app_id": "**.***.**", // app identifier in the store "device_id": "*******", // unique id per app install "platform": "ios", // or android, or electron "custom_id": "user_1", // represent your user "version_name": "1.2.3", // version of the web build "version_build": "1.2.0", // version of the native build "version_code": "120", // build number of the native build "version_os": "16", // OS version of the device "plugin_version": "4.0.0"// to make your api behave differently with different plugins "is_emulator": false, "is_prod": false, } ``` You can also totally disable it, with an empty string. Keep in mind, statistics are made private friendly and help me to understand how people use the plugin, to resolve issues and improve it. ## Expected “no update” behavior [Section titled “Expected “no update” behavior”](#expected-no-update-behavior) When your update endpoint has **no new version**, it should respond with an error payload like: ```json { "error": "no_new_version_available", "message": "No new version available" } ``` The `error` code must be exactly `no_new_version_available`. The `message` can be any string you want (it’s only for logging/debugging). This is the expected behavior and it is still returned with HTTP `200`. If your update endpoint instead returns a `200` response without a `url`, the plugin will treat it as a download failure and send a `download_fail` stat. ## Implementation Example [Section titled “Implementation Example”](#implementation-example) Here is an example of code in JavaScript to save the stats of the plugin: ```typescript interface AppInfos { version_name: string action: 'ping' | 'delete' | 'reset' | 'set' | 'get' | 'set_fail' | 'update_fail' | 'download_fail' | 'windows_path_fail' | 'canonical_path_fail' | 'directory_path_fail' | 'unzip_fail' | 'low_mem_fail' | 'download_10' | 'download_20' | 'download_30' | 'download_40' | 'download_50' | 'download_60' | 'download_70' | 'download_80' | 'download_90' | 'download_complete' | 'download_manifest_start' | 'download_manifest_complete' | 'download_zip_start' | 'download_zip_complete' | 'download_manifest_file_fail' | 'download_manifest_checksum_fail' | 'download_manifest_brotli_fail' | 'decrypt_fail' | 'app_moved_to_foreground' | 'app_moved_to_background' | 'uninstall' | 'needPlanUpgrade' | 'missingBundle' | 'noNew' | 'disablePlatformIos' | 'disablePlatformAndroid' | 'disableAutoUpdateToMajor' | 'cannotUpdateViaPrivateChannel' | 'disableAutoUpdateToMinor' | 'disableAutoUpdateToPatch' | 'channelMisconfigured' | 'disableAutoUpdateMetadata' | 'disableAutoUpdateUnderNative' | 'disableDevBuild' | 'disableEmulator' | 'cannotGetBundle' | 'checksum_fail' | 'NoChannelOrOverride' | 'setChannel' | 'getChannel' | 'rateLimited' | 'disableAutoUpdate' | 'InvalidIp' | 'keyMismatch' | 'blocked_by_server_url' version_build: string version_code: string version_os: string plugin_version: string platform: string app_id: string device_id: string custom_id?: string is_prod?: boolean is_emulator?: boolean } export const handler: Handler = async (event) => { const body = JSON.parse(event.body || '{}') as AppInfos const { platform, app_id, action, version_code, version_os, device_id, version_name, version_build, plugin_version, } = body console.log('update asked', platform, app_id, action, version_os, version_code, device_id, version_name, version_build, plugin_version) // Save it in your database return { status: 'ok' } } ``` This endpoint should return a JSON: ```json { "status": "ok" } ``` ## Actions [Section titled “Actions”](#actions) For detailed descriptions of all action codes and their meanings, please refer to the debugging documentation: * **Actions sent from the device**: See the [debugging documentation - Sent from the device](/docs/plugins/updater/debugging/#sent-from-the-device) section * **Actions sent from the backend**: See the [debugging documentation - Sent from the backend](/docs/plugins/updater/debugging/#sent-from-the-backend) section [Handling Updates ](/docs/plugin/self-hosted/handling-updates/) # Update API Endpoint > How to build your update server API endpoint to respond to Capgo plugin requests with proper bundle information and checksums Here is an example of code in JavaScript to send an update to the plugin ```typescript interface AppInfos { version_name: string version_build: string version_os: string custom_id?: string is_prod?: boolean is_emulator?: boolean plugin_version: string platform: string app_id: string device_id: string } export const handler: Handler = async (event) => { const body = JSON.parse(event.body || '{}') as AppInfos const { platform, app_id, version_os, device_id, version_name, version_build, plugin_version, } = body console.log('update asked', platform, app_id, version_os, device_id, version_name, version_build, plugin_version) if (version_name === '1.0.0') { return { version: '1.0.1', url: 'https://apiurl.com/mybuild_101.zip', checksum: 'sha256_checksum_of_bundle', } } else if (version_name === '1.0.1') { return { version: '1.0.2', url: 'https://apiurl.com/mybuild_102.zip', checksum: 'sha256_checksum_of_bundle', } } else { return { message: 'Error version not found' version: '', url: '', } } } ``` ## Response Format [Section titled “Response Format”](#response-format) For **non-encrypted bundles**, your endpoint should return: ```json { "version": "1.0.2", "url": "https://apiurl.com/mybuild_102.zip", "checksum": "sha256_checksum_of_bundle" } ``` For **encrypted bundles**, you also need to include the session key: ```json { "version": "1.0.2", "url": "https://apiurl.com/mybuild_102.zip", "checksum": "encrypted_checksum_from_encrypt_command", "session_key": "ivSessionKey_from_encrypt_command" } ``` And if no update or error, add the `message` key and optionally an `error`: ```json { "message": "Version not found", "error": "The backend crashed", "version": "1.0.2", } ``` ## Field Descriptions [Section titled “Field Descriptions”](#field-descriptions) * **`checksum`**: SHA256 hash of your bundle zip file for integrity verification * **`session_key`**: Required only for encrypted bundles - this is the `ivSessionKey` returned by the encrypt command * **`version`**: Version identifier in semver format * **`url`**: HTTPS URL where the bundle can be downloaded ## Bundle Creation [Section titled “Bundle Creation”](#bundle-creation) To learn how to create compatible bundles and generate checksums, see the [Auto Update documentation](/docs/plugin/self-hosted/auto-update/#generating-bundle-checksum). For encrypted bundles, see the [Encrypted Bundles documentation](/docs/plugin/self-hosted/encrypted-bundles/) which explains the complete encryption workflow. # Manual Update > A detailed guide on utilizing the manual update plugin within a self-hosted environment, providing comprehensive instructions on configuration and usage to effectively manage updates without relying on automatic processes. ## Configuration [Section titled “Configuration”](#configuration) Add this to your `capacitor.config.json`, to disable auto-update. ```tsx // capacitor.config.json { "appId": "**.***.**", "appName": "Name", "plugins": { "CapacitorUpdater": { "autoUpdate": false, } } } ``` ## Usage [Section titled “Usage”](#usage) You can use this example or re-create the logic in your app. Caution We are forcing the user to update the app with a static version declared in the code. This is not recommended, you should use a dynamic version from your server. Danger We are not doing any version checking, decryption or checksum validation in this example. You should do that on your own. ```tsx import { CapacitorUpdater } from '@capgo/capacitor-updater' import { SplashScreen } from '@capacitor/splash-screen' import { App } from '@capacitor/app' let data = {version: ""} CapacitorUpdater.notifyAppReady() App.addListener('appStateChange', async(state) => { if (state.isActive) { // Do the download during user active app time to prevent failed download data = await CapacitorUpdater.download({ version: '0.0.4', url: 'https://github.com/Cap-go/demo-app/releases/download/0.0.4/dist.zip', }) } if (!state.isActive && data.version !== "") { // Do the switch when user leave app SplashScreen.show() try { await CapacitorUpdater.set(data) } catch (err) { console.log(err) SplashScreen.hide() // in case the set fail, otherwise the new app will have to hide it } } }) ``` Note If this seems like a lot of work consider trying [Capgo trial](https://capgo.app/register/). It will handle all of this for you. # Settings > All available settings for Capacitor Updater, all the configuration you can set in you capacitor config and what they used for To have more fine-grained control over the update system, you can configure it with these settings: Tip Any changes to these settings in capacitor.config file, require syncing the platform and releasing to the store for production apps to receive them. ## `allowModifyUrl` [Section titled “allowModifyUrl”](#allowmodifyurl) > Allow the plugin to modify the updateUrl, statsUrl and channelUrl dynamically from the JavaScript side. Available on Android, iOS, and Electron. Default: `false` capacitor.config.json ```json { "plugins": { "CapacitorUpdater": { "allowModifyUrl": true } } } ``` ## `appId` [Section titled “appId”](#appid) > Configure the app id for the app in the config. Available on Android, iOS, and Electron. Default: `undefined` capacitor.config.json ```json { "plugins": { "CapacitorUpdater": { "appId": "com.example.app" } } } ``` ## `appReadyTimeout` [Section titled “appReadyTimeout”](#appreadytimeout) > Configure the number of milliseconds the native plugin should wait before considering an update ‘failed’. Available on Android, iOS, and Electron. Default: `10000` (10 seconds) capacitor.config.json ```json { "plugins": { "CapacitorUpdater": { "appReadyTimeout": 1000 } } } ``` ## `autoDeleteFailed` [Section titled “autoDeleteFailed”](#autodeletefailed) > Configure whether the plugin should automatically delete failed bundles. Available on Android, iOS, and Electron. Default: `true` capacitor.config.json ```json { "plugins": { "CapacitorUpdater": { "autoDeleteFailed": false } } } ``` ## `autoDeletePrevious` [Section titled “autoDeletePrevious”](#autodeleteprevious) > Configure whether the plugin should automatically delete previous bundles after a successful update. Available on Android, iOS, and Electron. Default: `true` capacitor.config.json ```json { "plugins": { "CapacitorUpdater": { "autoDeletePrevious": false } } } ``` ## `autoSplashscreen` [Section titled “autoSplashscreen”](#autosplashscreen) > Automatically handle splashscreen hiding when using directUpdate. When enabled, the plugin will automatically hide the splashscreen after updates are applied or when no update is needed. This removes the need to manually listen for appReady events and call SplashScreen.hide(). Only works when directUpdate is set to “atInstall”, “always”, or true. Requires the @capacitor/splash-screen plugin to be installed and configured with launchAutoHide: false. Requires autoUpdate and directUpdate to be enabled. Available on Android, iOS, and Electron. Default: `false` capacitor.config.json ```json { "plugins": { "CapacitorUpdater": { "autoUpdate": true, "directUpdate": "atInstall", "autoSplashscreen": true } } } ``` ## `autoUpdate` [Section titled “autoUpdate”](#autoupdate) > Configure whether the plugin should use Auto Update via an update server. Available on Android, iOS, and Electron. Default: `true` capacitor.config.json ```json { "plugins": { "CapacitorUpdater": { "autoUpdate": false } } } ``` ## `channelUrl` [Section titled “channelUrl”](#channelurl) > Configure the URL / endpoint for channel operations. Available on Android, iOS, and Electron. Default: `https://plugin.capgo.app/channel_self` capacitor.config.json ```json { "plugins": { "CapacitorUpdater": { "channelUrl": "https://example.com/api/channel" } } } ``` ## `defaultChannel` [Section titled “defaultChannel”](#defaultchannel) > Set the default channel for the app in the config. Case sensitive. This setting will override the default channel set in the cloud, but will still respect overrides made in the cloud. Available on Android, iOS, and Electron. Default: `undefined` capacitor.config.json ```json { "plugins": { "CapacitorUpdater": { "defaultChannel": "production" } } } ``` ## `directUpdate` [Section titled “directUpdate”](#directupdate) > Configure when the plugin should direct install updates. Only for autoUpdate mode. Works well for apps less than 10MB and with uploads done using the —delta flag. Zip or apps more than 10MB will be relatively slow for users to update. Options: * `false`: Never do direct updates (use default behavior: download at start, set when backgrounded) * `'atInstall'`: Direct update only when app is installed, updated from store, otherwise act as directUpdate = false * `'onLaunch'`: Direct update only on app installed, updated from store or after app kill, otherwise act as directUpdate = false * `'always'`: Direct update in all previous cases (app installed, updated from store, after app kill or app resume), never act as directUpdate = false * `true`: (deprecated) Same as “always” for backward compatibility Available on Android, iOS, and Electron. Default: `false` capacitor.config.json ```json { "plugins": { "CapacitorUpdater": { "autoUpdate": true, "directUpdate": "atInstall" } } } ``` ## `disableJSLogging` [Section titled “disableJSLogging”](#disablejslogging) > Disable the JavaScript logging of the plugin. If true, the plugin will not log to the JavaScript console. Only the native log will be done. Available on Android, iOS, and Electron. Default: `false` capacitor.config.json ```json { "plugins": { "CapacitorUpdater": { "disableJSLogging": true } } } ``` ## `keepUrlPathAfterReload` [Section titled “keepUrlPathAfterReload”](#keepurlpathafterreload) > Configure the plugin to keep the URL path after a reload. Caution When a reload is triggered, ‘window\.history’ will be cleared. Available on Android, iOS, and Electron. Default: `false` capacitor.config.json ```json { "plugins": { "CapacitorUpdater": { "keepUrlPathAfterReload": true } } } ``` ## `periodCheckDelay` [Section titled “periodCheckDelay”](#periodcheckdelay) > Configure the delay period for period update check. The unit is in seconds. Cannot be less than 600 seconds (10 minutes). Available on Android, iOS, and Electron. Default: `600` (10 minutes) capacitor.config.json ```json { "plugins": { "CapacitorUpdater": { "periodCheckDelay": 600 // (10 minutes) } } } ``` ## `publicKey` [Section titled “publicKey”](#publickey) > Configure the public key for end to end live update encryption Version 2. Available on Android, iOS, and Electron. Default: `undefined` capacitor.config.json ```json { "plugins": { "CapacitorUpdater": { "publicKey": "YOUR_PUBLIC_KEY" } } } ``` ## `resetWhenUpdate` [Section titled “resetWhenUpdate”](#resetwhenupdate) > Automatically delete previous downloaded bundles when a newer native app bundle is installed to the device. Available on Android, iOS, and Electron. Default: `true` capacitor.config.json ```json { "plugins": { "CapacitorUpdater": { "resetWhenUpdate": false } } } ``` ## `responseTimeout` [Section titled “responseTimeout”](#responsetimeout) > Configure the number of milliseconds the native plugin should wait before considering API timeout. Available on Android, iOS, and Electron. Default: `20` (20 seconds) capacitor.config.json ```json { "plugins": { "CapacitorUpdater": { "responseTimeout": 10 // (10 seconds) } } } ``` ## `shakeMenu` [Section titled “shakeMenu”](#shakemenu) > Enable shake gesture to show update menu for debugging/testing purposes. Available on Android, iOS, and Electron. Default: `false` capacitor.config.json ```json { "plugins": { "CapacitorUpdater": { "shakeMenu": true } } } ``` ## `statsUrl` [Section titled “statsUrl”](#statsurl) > Configure the URL / endpoint to which update statistics are sent. Available on Android, iOS, and Electron. Set to "" to disable stats reporting. Default: `https://plugin.capgo.app/stats` capacitor.config.json ```json { "plugins": { "CapacitorUpdater": { "statsUrl": "https://example.com/api/stats" } } } ``` ## `updateUrl` [Section titled “updateUrl”](#updateurl) > Configure the URL / endpoint to which update checks are sent. Available on Android, iOS, and Electron. Default: `https://plugin.capgo.app/updates` capacitor.config.json ```json { "plugins": { "CapacitorUpdater": { "updateUrl": "https://example.com/api/auto_update" } } } ``` ## `version` [Section titled “version”](#version) > Configure the current version of the app. This will be used for the first update request. If not set, the plugin will get the version from the native code. Available on Android, iOS, and Electron. Default: `undefined` capacitor.config.json ```json { "plugins": { "CapacitorUpdater": { "version": "1.0.0" } } } ``` ## Development Settings [Section titled “Development Settings”](#development-settings) ### `localApi` [Section titled “localApi”](#localapi) > Configure the CLI to use a local api for testing. Default: `undefined` capacitor.config.json ```json { "plugins": { "CapacitorUpdater": { "localApi": "http://localhost:54321/functions/v1" } } } ``` ### `localApiFiles` [Section titled “localApiFiles”](#localapifiles) > Configure the CLI to use a local file api for testing. Default: `undefined` capacitor.config.json ```json { "plugins": { "CapacitorUpdater": { "localApiFiles": "http://localhost:54321/functions/v1/files" } } } ``` ### `localHost` [Section titled “localHost”](#localhost) > Configure the CLI to use a local server for testing or self-hosted update server. Default: `undefined` capacitor.config.json ```json { "plugins": { "CapacitorUpdater": { "localHost": "http://localhost:5173" } } } ``` ### `localSupa` [Section titled “localSupa”](#localsupa) > Configure the CLI to use a local server for testing or self-hosted update server. Default: `undefined` capacitor.config.json ```json { "plugins": { "CapacitorUpdater": { "localSupa": "http://localhost:54321" } } } ``` ### `localSupaAnon` [Section titled “localSupaAnon”](#localsupaanon) > Configure the CLI to use a local server for testing. Default: `undefined` capacitor.config.json ```json { "plugins": { "CapacitorUpdater": { "localSupaAnon": "YOUR_LOCAL_ANON_KEY" } } } ``` ### `localWebHost` [Section titled “localWebHost”](#localwebhost) > Configure the CLI to use a local server for testing or self-hosted update server. Default: `undefined` capacitor.config.json ```json { "plugins": { "CapacitorUpdater": { "localWebHost": "http://localhost:5173" } } } ``` Tip There are additional settings available in the [Capgo web app](https://console.capgo.app/login) that can be configured per channel without requiring a native app release. # @capgo/capacitor-uploader > Upload files in the background with progress tracking, resumable uploads, and network-aware handling for Capacitor apps. Background uploads Continue uploads even when app is in background 🚀 Progress tracking Real-time upload progress with detailed statistics 📊 Resumable uploads Automatically resume interrupted uploads 🔄 Comprehensive Documentation Check the [Documentation](/docs/plugins/uploader/getting-started/) to master the plugin in just a few minutes. # Getting Started > Learn how to install and use the Background Uploader plugin for reliable file uploads in your Capacitor app. 1. **Install the package** * npm ```sh npm i @capgo/capacitor-uploader ``` * pnpm ```sh pnpm add @capgo/capacitor-uploader ``` * yarn ```sh yarn add @capgo/capacitor-uploader ``` * bun ```sh bun add @capgo/capacitor-uploader ``` 2. **Sync with native projects** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` 3. **Configure permissions** ### iOS [Section titled “iOS”](#ios) Add background modes to your `Info.plist`: ```xml UIBackgroundModes processing ``` ### Android [Section titled “Android”](#android) Add permissions to your `AndroidManifest.xml`: ```xml ``` ## Usage [Section titled “Usage”](#usage) Import the plugin and use its methods to upload files: ```typescript import { Uploader } from '@capgo/capacitor-uploader'; // Start an upload const startUpload = async () => { const upload = await Uploader.startUpload({ filePath: 'file:///path/to/your/file.jpg', serverUrl: 'https://your-server.com/upload', method: 'POST', headers: { 'Authorization': 'Bearer your-token' }, parameters: { 'userId': '12345', 'type': 'profile' }, notificationTitle: 'Uploading Photo' }); console.log('Upload ID:', upload.id); }; // Listen for upload events const listener = await Uploader.addListener('events', (event) => { switch (event.name) { case 'uploading': console.log(`Upload ${event.payload.id}: ${event.payload.percent}%`); break; case 'completed': console.log('Upload completed:', event.payload.id); console.log('Status code:', event.payload.statusCode); break; case 'failed': console.error('Upload failed:', event.payload.id); console.error('Error:', event.payload.error); break; } }); // Remember to remove listener when done // listener.remove(); // Cancel an upload const cancelUpload = async (uploadId: string) => { await Uploader.cancelUpload({ id: uploadId }); }; // Get all active uploads const getActiveUploads = async () => { const uploads = await Uploader.getUploads(); console.log('Active uploads:', uploads); }; ``` ## API Reference [Section titled “API Reference”](#api-reference) ### startUpload(options) [Section titled “startUpload(options)”](#startuploadoptions) Starts a new file upload. ```typescript interface UploadOptions { filePath: string; serverUrl: string; method?: 'POST' | 'PUT'; headers?: { [key: string]: string }; parameters?: { [key: string]: string }; notificationTitle?: string; notificationBody?: string; useUtf8Charset?: boolean; maxRetries?: number; } ``` ### cancelUpload(options) [Section titled “cancelUpload(options)”](#canceluploadoptions) Cancels an ongoing upload. ```typescript interface CancelOptions { id: string; } ``` ### getUploads() [Section titled “getUploads()”](#getuploads) Returns all active uploads. ### removeUpload(options) [Section titled “removeUpload(options)”](#removeuploadoptions) Removes an upload from the queue. ```typescript interface RemoveOptions { id: string; } ``` ## Events [Section titled “Events”](#events) ### events [Section titled “events”](#events-1) All upload events are delivered through a single `events` listener. The event contains a `name` field to identify the type: ```typescript Uploader.addListener('events', (event: UploadEvent) => { switch (event.name) { case 'uploading': // Upload in progress console.log(`Progress: ${event.payload.percent}%`); console.log(`ID: ${event.payload.id}`); break; case 'completed': // Upload finished successfully console.log(`Completed: ${event.payload.id}`); console.log(`Status code: ${event.payload.statusCode}`); break; case 'failed': // Upload failed console.error(`Failed: ${event.payload.id}`); console.error(`Error: ${event.payload.error}`); break; } }); ``` **Event Types:** * `uploading` - Progress update with `percent` and `id` * `completed` - Upload finished with `id` and `statusCode` * `failed` - Upload failed with `id` and `error` message ## Advanced Features [Section titled “Advanced Features”](#advanced-features) ### Multipart Form Upload [Section titled “Multipart Form Upload”](#multipart-form-upload) ```typescript const uploadWithFormData = async () => { const upload = await Uploader.startUpload({ filePath: 'file:///path/to/photo.jpg', serverUrl: 'https://api.example.com/upload', method: 'POST', parameters: { 'name': 'profile-photo', 'description': 'User profile photo' }, headers: { 'X-API-Key': 'your-api-key' } }); }; ``` ### Binary Upload [Section titled “Binary Upload”](#binary-upload) ```typescript const uploadBinary = async () => { const upload = await Uploader.startUpload({ filePath: 'file:///path/to/data.bin', serverUrl: 'https://api.example.com/binary', method: 'PUT', headers: { 'Content-Type': 'application/octet-stream' } }); }; ``` ### Network-Aware Uploading [Section titled “Network-Aware Uploading”](#network-aware-uploading) ```typescript import { Network } from '@capacitor/network'; const smartUpload = async () => { const status = await Network.getStatus(); if (status.connectionType === 'wifi') { // Start large file uploads on WiFi await startLargeUpload(); } else if (status.connectionType === 'cellular') { // Queue for later or warn user console.log('Using cellular data'); } }; ``` ## Best Practices [Section titled “Best Practices”](#best-practices) 1. **Handle all event types** ```typescript const setupUploadHandlers = () => { Uploader.addListener('events', (event) => { switch (event.name) { case 'uploading': handleProgress(event.payload); break; case 'completed': handleCompleted(event.payload); break; case 'failed': handleError(event.payload); break; } }); }; ``` 2. **Clean up listeners** ```typescript // Add listener const listener = await Uploader.addListener('events', handleEvent); // Clean up when done listener.remove(); ``` 3. **Retry failed uploads** ```typescript const retryUpload = async (filePath: string, serverUrl: string) => { try { await Uploader.startUpload({ filePath, serverUrl, maxRetries: 3 }); } catch (error) { console.error('Upload failed after retries:', error); } }; ``` 4. **Show upload notifications** ```typescript await Uploader.startUpload({ filePath: 'file:///path/to/file', serverUrl: 'https://server.com/upload', notificationTitle: 'Uploading File', notificationBody: 'Your file is being uploaded...' }); ``` ## Platform Notes [Section titled “Platform Notes”](#platform-notes) ### iOS [Section titled “iOS”](#ios-1) * Uses `URLSession` for background uploads * Requires background processing capability * Uploads continue when app is suspended ### Android [Section titled “Android”](#android-1) * Uses `WorkManager` for reliable uploads * Shows foreground service notification during uploads * Respects battery optimization settings # @capgo/capacitor-video-player > Powerful native video player with support for fullscreen, embedded modes, subtitles, and comprehensive playback controls. Multiple Modes Fullscreen and embedded playback modes Subtitle Support Display subtitles in multiple languages Full Control Play, pause, seek, volume, and playback rate control Comprehensive Documentation Check the [Documentation](/docs/plugins/video-player/getting-started/) to integrate video playback. # Getting Started > Learn how to integrate native video playback in your Capacitor app. 1. **Install the package** * npm ```sh npm i @capgo/capacitor-video-player ``` * pnpm ```sh pnpm add @capgo/capacitor-video-player ``` * yarn ```sh yarn add @capgo/capacitor-video-player ``` * bun ```sh bun add @capgo/capacitor-video-player ``` 2. **Sync with native projects** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Basic Usage [Section titled “Basic Usage”](#basic-usage) ```typescript import { CapacitorVideoPlayer } from '@capgo/capacitor-video-player'; // Initialize a video player const playVideo = async () => { const result = await CapacitorVideoPlayer.initPlayer({ mode: 'fullscreen', url: 'https://example.com/video.mp4', playerId: 'myPlayer', componentTag: 'div' }); console.log('Player initialized:', result); }; // Play the video await CapacitorVideoPlayer.play({ playerId: 'myPlayer' }); // Pause the video await CapacitorVideoPlayer.pause({ playerId: 'myPlayer' }); // Get current time const { value } = await CapacitorVideoPlayer.getCurrentTime({ playerId: 'myPlayer' }); console.log('Current time:', value); // Seek to position await CapacitorVideoPlayer.setCurrentTime({ playerId: 'myPlayer', seektime: 30 // seconds }); // Set volume (0-1) await CapacitorVideoPlayer.setVolume({ playerId: 'myPlayer', volume: 0.5 }); ``` ## API Reference [Section titled “API Reference”](#api-reference) ### initPlayer(options) [Section titled “initPlayer(options)”](#initplayeroptions) Initialize a video player instance. ```typescript await CapacitorVideoPlayer.initPlayer({ mode: 'fullscreen', // or 'embedded' url: 'https://example.com/video.mp4', playerId: 'player1', subtitle: 'https://example.com/subtitles.vtt', language: 'en', rate: 1.0, exitOnEnd: true, loopOnEnd: false, pipEnabled: true, showControls: true }); ``` ### play(options) [Section titled “play(options)”](#playoptions) Play the video. ```typescript await CapacitorVideoPlayer.play({ playerId: 'player1' }); ``` ### pause(options) [Section titled “pause(options)”](#pauseoptions) Pause the video. ```typescript await CapacitorVideoPlayer.pause({ playerId: 'player1' }); ``` ### getDuration(options) [Section titled “getDuration(options)”](#getdurationoptions) Get video duration in seconds. ```typescript const { value } = await CapacitorVideoPlayer.getDuration({ playerId: 'player1' }); console.log('Duration:', value, 'seconds'); ``` ### getCurrentTime(options) [Section titled “getCurrentTime(options)”](#getcurrenttimeoptions) Get current playback position in seconds. ```typescript const { value } = await CapacitorVideoPlayer.getCurrentTime({ playerId: 'player1' }); ``` ### setCurrentTime(options) [Section titled “setCurrentTime(options)”](#setcurrenttimeoptions) Seek to a specific time. ```typescript await CapacitorVideoPlayer.setCurrentTime({ playerId: 'player1', seektime: 60 // seconds }); ``` ### setVolume(options) [Section titled “setVolume(options)”](#setvolumeoptions) Set volume (0.0 to 1.0). ```typescript await CapacitorVideoPlayer.setVolume({ playerId: 'player1', volume: 0.8 }); ``` ### getVolume(options) [Section titled “getVolume(options)”](#getvolumeoptions) Get current volume. ```typescript const { value } = await CapacitorVideoPlayer.getVolume({ playerId: 'player1' }); ``` ### setMuted(options) [Section titled “setMuted(options)”](#setmutedoptions) Mute or unmute the video. ```typescript await CapacitorVideoPlayer.setMuted({ playerId: 'player1', muted: true }); ``` ### setRate(options) [Section titled “setRate(options)”](#setrateoptions) Set playback speed. ```typescript await CapacitorVideoPlayer.setRate({ playerId: 'player1', rate: 1.5 // 1.5x speed }); ``` ### stopAllPlayers() [Section titled “stopAllPlayers()”](#stopallplayers) Stop all active players. ```typescript await CapacitorVideoPlayer.stopAllPlayers(); ``` ### exitPlayer() [Section titled “exitPlayer()”](#exitplayer) Exit the video player. ```typescript await CapacitorVideoPlayer.exitPlayer(); ``` ## Complete Example [Section titled “Complete Example”](#complete-example) ```typescript import { CapacitorVideoPlayer } from '@capgo/capacitor-video-player'; export class VideoPlayerService { private playerId = 'mainPlayer'; async initializePlayer(videoUrl: string, subtitleUrl?: string) { try { const result = await CapacitorVideoPlayer.initPlayer({ mode: 'fullscreen', url: videoUrl, playerId: this.playerId, subtitle: subtitleUrl, language: 'en', rate: 1.0, exitOnEnd: true, loopOnEnd: false, pipEnabled: true, bkmodeEnabled: true, showControls: true, displayMode: 'all' }); console.log('Player initialized:', result); return result; } catch (error) { console.error('Failed to initialize player:', error); throw error; } } async togglePlayPause() { const { value: isPlaying } = await CapacitorVideoPlayer.isPlaying({ playerId: this.playerId }); if (isPlaying) { await CapacitorVideoPlayer.pause({ playerId: this.playerId }); } else { await CapacitorVideoPlayer.play({ playerId: this.playerId }); } } async seekForward(seconds: number = 10) { const { value: currentTime } = await CapacitorVideoPlayer.getCurrentTime({ playerId: this.playerId }); await CapacitorVideoPlayer.setCurrentTime({ playerId: this.playerId, seektime: currentTime + seconds }); } async seekBackward(seconds: number = 10) { const { value: currentTime } = await CapacitorVideoPlayer.getCurrentTime({ playerId: this.playerId }); await CapacitorVideoPlayer.setCurrentTime({ playerId: this.playerId, seektime: Math.max(0, currentTime - seconds) }); } async setPlaybackSpeed(speed: number) { await CapacitorVideoPlayer.setRate({ playerId: this.playerId, rate: speed }); } async getProgress() { const { value: currentTime } = await CapacitorVideoPlayer.getCurrentTime({ playerId: this.playerId }); const { value: duration } = await CapacitorVideoPlayer.getDuration({ playerId: this.playerId }); return { currentTime, duration, percentage: (currentTime / duration) * 100 }; } async cleanup() { await CapacitorVideoPlayer.stopAllPlayers(); } } ``` ## Platform Notes [Section titled “Platform Notes”](#platform-notes) ### iOS [Section titled “iOS”](#ios) * Requires iOS 10.0+ * Uses native AVPlayer * Supports Picture-in-Picture * Background playback supported ### Android [Section titled “Android”](#android) * Requires Android 5.0 (API 21)+ * Uses ExoPlayer * Supports Chromecast * Custom accent colors available ### Web [Section titled “Web”](#web) * Uses HTML5 video player * Embedded mode only * Limited native features ## Best Practices [Section titled “Best Practices”](#best-practices) 1. **Clean up players**: Always stop players when done 2. **Handle errors**: Wrap player calls in try-catch 3. **Unique player IDs**: Use unique IDs for multiple players 4. **Check playback status**: Verify state before operations 5. **Resource management**: Release players to free memory # @capgo/capacitor-video-thumbnails > Extract high-quality thumbnail images from local and remote video files with customizable quality and timestamp selection. Requires Capacitor 8+. Simple API Generate thumbnails with a single method call Cross-platform Works on iOS, Android, and Web platforms Remote Videos Extract thumbnails from both local files and remote URLs Quality Control Customize thumbnail quality and timestamp selection Comprehensive Documentation Check the [Documentation](/docs/plugins/video-thumbnails/getting-started/) to master the plugin in just a few minutes. # Getting Started > Learn how to install and use the Video Thumbnails plugin to generate thumbnails from videos in your Capacitor app. 1. **Install the package** * npm ```sh npm i @capgo/capacitor-video-thumbnails ``` * pnpm ```sh pnpm add @capgo/capacitor-video-thumbnails ``` * yarn ```sh yarn add @capgo/capacitor-video-thumbnails ``` * bun ```sh bun add @capgo/capacitor-video-thumbnails ``` 2. **Sync with native projects** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Usage [Section titled “Usage”](#usage) Import the plugin and use its methods to generate video thumbnails: ```typescript import { CapgoVideoThumbnails } from '@capgo/capacitor-video-thumbnails'; // Generate thumbnail from local video const generateFromLocal = async () => { const result = await CapgoVideoThumbnails.getThumbnail({ sourceUri: 'file:///path/to/video.mp4', time: 5000, // 5 seconds into the video quality: 0.8 }); console.log('Thumbnail URI:', result.uri); console.log('Dimensions:', result.width, 'x', result.height); }; // Generate thumbnail from remote video const generateFromRemote = async () => { const result = await CapgoVideoThumbnails.getThumbnail({ sourceUri: 'https://example.com/video.mp4', time: 0, // First frame quality: 1.0 }); return result.uri; }; ``` ## API Reference [Section titled “API Reference”](#api-reference) ### getThumbnail(options) [Section titled “getThumbnail(options)”](#getthumbnailoptions) Generate a thumbnail image from a video file. ```typescript interface VideoThumbnailsOptions { sourceUri: string; // Video file path or URL time?: number; // Position in milliseconds (default: 0) quality?: number; // 0.0 to 1.0 (default: 1.0) headers?: Record; // HTTP headers for remote URLs } interface VideoThumbnailsResult { uri: string; // Local path to generated thumbnail width: number; // Thumbnail width in pixels height: number; // Thumbnail height in pixels } const result = await CapgoVideoThumbnails.getThumbnail({ sourceUri: 'file:///path/to/video.mp4', time: 5000, quality: 0.8 }); ``` ### getPluginVersion() [Section titled “getPluginVersion()”](#getpluginversion) Get the current plugin version. ```typescript const { version } = await CapgoVideoThumbnails.getPluginVersion(); console.log('Plugin version:', version); ``` ## Complete Example [Section titled “Complete Example”](#complete-example) ```typescript import { CapgoVideoThumbnails } from '@capgo/capacitor-video-thumbnails'; export class VideoThumbnailService { /** * Generate a thumbnail from a video at a specific time */ async generateThumbnail( videoPath: string, timeMs: number = 0, quality: number = 0.8 ): Promise { try { const result = await CapgoVideoThumbnails.getThumbnail({ sourceUri: videoPath, time: timeMs, quality: quality }); return result.uri; } catch (error) { console.error('Failed to generate thumbnail:', error); throw error; } } /** * Generate thumbnail from authenticated remote video */ async generateFromAuthenticatedUrl( videoUrl: string, authToken: string ): Promise { const result = await CapgoVideoThumbnails.getThumbnail({ sourceUri: videoUrl, time: 0, quality: 1.0, headers: { 'Authorization': `Bearer ${authToken}` } }); return result.uri; } /** * Generate multiple thumbnails at different timestamps */ async generateThumbnailStrip( videoPath: string, count: number, videoDurationMs: number ): Promise { const thumbnails: string[] = []; const interval = videoDurationMs / (count + 1); for (let i = 1; i <= count; i++) { const time = Math.round(interval * i); const result = await CapgoVideoThumbnails.getThumbnail({ sourceUri: videoPath, time: time, quality: 0.6 // Lower quality for preview strip }); thumbnails.push(result.uri); } return thumbnails; } /** * Generate thumbnail with dimensions info */ async generateWithMetadata(videoPath: string) { const result = await CapgoVideoThumbnails.getThumbnail({ sourceUri: videoPath, time: 1000, // 1 second quality: 1.0 }); return { thumbnailUri: result.uri, aspectRatio: result.width / result.height, width: result.width, height: result.height }; } } // Usage in a React/Vue/Angular component const thumbnailService = new VideoThumbnailService(); async function displayVideoPreview(videoPath: string) { const thumbnailUri = await thumbnailService.generateThumbnail(videoPath, 2000); // Use in img tag // Video preview return thumbnailUri; } // Generate preview for video picker async function handleVideoSelected(videoUri: string) { const metadata = await thumbnailService.generateWithMetadata(videoUri); console.log(`Video aspect ratio: ${metadata.aspectRatio}`); console.log(`Thumbnail: ${metadata.thumbnailUri}`); return metadata; } ``` ## Best Practices [Section titled “Best Practices”](#best-practices) 1. **Use appropriate quality** * For thumbnails in lists: 0.5 - 0.7 * For preview images: 0.8 - 1.0 * Lower quality = smaller file size and faster generation 2. **Handle errors gracefully** ```typescript try { const result = await CapgoVideoThumbnails.getThumbnail({ sourceUri: videoPath, time: 5000 }); } catch (error) { console.error('Thumbnail generation failed:', error); // Show placeholder image } ``` 3. **Cache thumbnails** Store generated thumbnail URIs to avoid regenerating them: ```typescript const thumbnailCache = new Map(); async function getCachedThumbnail(videoPath: string): Promise { if (thumbnailCache.has(videoPath)) { return thumbnailCache.get(videoPath)!; } const result = await CapgoVideoThumbnails.getThumbnail({ sourceUri: videoPath }); thumbnailCache.set(videoPath, result.uri); return result.uri; } ``` 4. **Choose appropriate timestamps** * Avoid 0ms for videos that may have black intro frames * Use 1000-2000ms for better preview content ## Platform Notes [Section titled “Platform Notes”](#platform-notes) ### iOS [Section titled “iOS”](#ios) * Uses AVAssetImageGenerator for thumbnail extraction * Supports local files and remote URLs * Returns JPEG images ### Android [Section titled “Android”](#android) * Uses MediaMetadataRetriever for thumbnail extraction * Supports local files and remote URLs * Returns JPEG or PNG images ### Web [Section titled “Web”](#web) * Uses HTML5 video element with canvas capture * Requires CORS headers for remote videos * May have limitations with some video formats ## Requirements [Section titled “Requirements”](#requirements) * **Capacitor 8.0.0** or higher # @capgo/capacitor-volume-buttons > Detect and respond to volume up and down button presses on mobile devices for custom controls and interactions. Hardware Integration Listen to physical volume button presses Up/Down Detection Distinguish between volume up and down Simple API Easy event listener interface Comprehensive Documentation Check the [Documentation](/docs/plugins/volume-buttons/getting-started/) to start using volume buttons. # Getting Started > Learn how to detect volume button presses in your Capacitor app. 1. **Install the package** * npm ```sh npm i @capgo/capacitor-volume-buttons ``` * pnpm ```sh pnpm add @capgo/capacitor-volume-buttons ``` * yarn ```sh yarn add @capgo/capacitor-volume-buttons ``` * bun ```sh bun add @capgo/capacitor-volume-buttons ``` 2. **Sync with native projects** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Usage [Section titled “Usage”](#usage) ```typescript import { VolumeButtons } from '@capgo/capacitor-volume-buttons'; // Listen for volume button presses VolumeButtons.addListener('volumeButtonPressed', (event) => { console.log('Volume button pressed:', event.direction); if (event.direction === 'up') { console.log('Volume up pressed'); // Handle volume up } else if (event.direction === 'down') { console.log('Volume down pressed'); // Handle volume down } }); // Remove all listeners when done await VolumeButtons.removeAllListeners(); ``` ## API Reference [Section titled “API Reference”](#api-reference) ### addListener(‘volumeButtonPressed’, callback) [Section titled “addListener(‘volumeButtonPressed’, callback)”](#addlistenervolumebuttonpressed-callback) Listen for volume button press events. ```typescript const handle = VolumeButtons.addListener('volumeButtonPressed', (event) => { console.log('Direction:', event.direction); // 'up' or 'down' }); // Remove specific listener await handle.remove(); ``` **Event Data:** * `direction`: `'up' | 'down'` - Direction of the button press ### removeAllListeners() [Section titled “removeAllListeners()”](#removealllisteners) Remove all registered listeners. ```typescript await VolumeButtons.removeAllListeners(); ``` ### getPluginVersion() [Section titled “getPluginVersion()”](#getpluginversion) Get the plugin version. ```typescript const { version } = await VolumeButtons.getPluginVersion(); console.log('Plugin version:', version); ``` ## Complete Example [Section titled “Complete Example”](#complete-example) ```typescript import { VolumeButtons } from '@capgo/capacitor-volume-buttons'; export class VolumeButtonController { private listener: any; async initialize() { this.listener = VolumeButtons.addListener( 'volumeButtonPressed', this.handleVolumeButton.bind(this) ); console.log('Volume button listener initialized'); } private handleVolumeButton(event: { direction: 'up' | 'down' }) { switch (event.direction) { case 'up': this.handleVolumeUp(); break; case 'down': this.handleVolumeDown(); break; } } private handleVolumeUp() { console.log('Volume up pressed'); // Your custom logic for volume up // e.g., navigate forward, increase brightness, etc. } private handleVolumeDown() { console.log('Volume down pressed'); // Your custom logic for volume down // e.g., navigate backward, decrease brightness, etc. } async cleanup() { if (this.listener) { await this.listener.remove(); } await VolumeButtons.removeAllListeners(); console.log('Volume button listener cleaned up'); } } ``` ## Use Cases [Section titled “Use Cases”](#use-cases) ### Photo/Video Capture [Section titled “Photo/Video Capture”](#photovideo-capture) Use volume buttons as camera shutter triggers: ```typescript VolumeButtons.addListener('volumeButtonPressed', async (event) => { // Any volume button triggers camera await capturePhoto(); }); ``` ### Page Navigation [Section titled “Page Navigation”](#page-navigation) Navigate through content with volume buttons: ```typescript let currentPage = 0; VolumeButtons.addListener('volumeButtonPressed', (event) => { if (event.direction === 'up') { currentPage++; showPage(currentPage); } else { currentPage = Math.max(0, currentPage - 1); showPage(currentPage); } }); ``` ### Media Control [Section titled “Media Control”](#media-control) Control media playback: ```typescript VolumeButtons.addListener('volumeButtonPressed', (event) => { if (event.direction === 'up') { skipForward(); } else { skipBackward(); } }); ``` ### Game Controls [Section titled “Game Controls”](#game-controls) Use volume buttons for game actions: ```typescript VolumeButtons.addListener('volumeButtonPressed', (event) => { if (event.direction === 'up') { player.jump(); } else { player.crouch(); } }); ``` ## Best Practices [Section titled “Best Practices”](#best-practices) 1. **Clean up listeners** Always remove listeners when your component unmounts: ```typescript useEffect(() => { const listener = VolumeButtons.addListener('volumeButtonPressed', handler); return () => { listener.remove(); }; }, []); ``` 2. **Provide visual feedback** Show users that volume buttons are being used for custom controls: ```typescript VolumeButtons.addListener('volumeButtonPressed', (event) => { showNotification(`Volume ${event.direction} pressed`); performAction(event.direction); }); ``` 3. **Consider accessibility** Remember that some users rely on volume buttons for actual volume control. Provide alternative controls. 4. **Debounce rapid presses** Prevent accidental double-presses: ```typescript let lastPress = 0; const DEBOUNCE_MS = 300; VolumeButtons.addListener('volumeButtonPressed', (event) => { const now = Date.now(); if (now - lastPress < DEBOUNCE_MS) { return; // Ignore rapid presses } lastPress = now; handlePress(event.direction); }); ``` 5. **Document the behavior** Make it clear to users that volume buttons have custom functionality: ```typescript function showVolumeButtonHelp() { alert('Use Volume Up/Down buttons to navigate pages'); } ``` ## Platform Notes [Section titled “Platform Notes”](#platform-notes) ### iOS [Section titled “iOS”](#ios) * Works on iOS 10.0+ * Volume buttons detected reliably * System volume UI may still appear briefly ### Android [Section titled “Android”](#android) * Works on Android 5.0 (API 21)+ * Requires app to be in foreground * Some devices may have slight delay ### Web [Section titled “Web”](#web) * Not supported on web platform * Listeners will not fire ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) **Events not firing:** * Ensure app has focus * Check that listeners are properly registered * Verify plugin is installed and synced **System volume changes interfere:** * This is expected behavior - the plugin detects presses but doesn’t prevent system volume changes * Consider UX that works alongside volume changes **Multiple events firing:** * Implement debouncing to handle rapid presses * Check for duplicate listener registration # @capgo/capacitor-watch > Enable seamless two-way communication between your iPhone app and Apple Watch companion app. Bidirectional Messaging Send and receive messages between iPhone and Apple Watch in real-time Background Transfers Queue reliable data transfers that deliver even when the watch is offline Application Context Sync app state with latest-value-only semantics for efficient updates SwiftUI Ready Includes watch-side SDK with ObservableObject support for reactive SwiftUI apps ## Documentation [Section titled “Documentation”](#documentation) [Getting Started ](/docs/plugins/watch/getting-started/)Install the plugin and learn the basic API for iPhone-Watch communication. [Watch App Setup ](/docs/plugins/watch/watch-app-setup/)Complete step-by-step guide to creating a watchOS app in Xcode from scratch. [Examples & Use Cases ](/docs/plugins/watch/examples/)Real-world examples: fitness tracker, smart home remote, notification center, and data sync. # Examples & Use Cases > Real-world examples and patterns for building Apple Watch apps with Capacitor. This guide provides practical examples for common Apple Watch app patterns using `@capgo/capacitor-watch`. ## Example 1: Health & Fitness Tracker [Section titled “Example 1: Health & Fitness Tracker”](#example-1-health--fitness-tracker) Sync workout data between watch and phone. * Watch (SwiftUI) ```swift import SwiftUI import CapgoWatchSDK struct WorkoutView: View { @ObservedObject var connector = WatchConnector.shared @State private var heartRate: Double = 0 @State private var calories: Double = 0 @State private var isTracking = false var body: some View { VStack(spacing: 12) { // Heart Rate Display HStack { Image(systemName: "heart.fill") .foregroundColor(.red) Text("\(Int(heartRate)) BPM") .font(.title2) } // Calories HStack { Image(systemName: "flame.fill") .foregroundColor(.orange) Text("\(Int(calories)) cal") } // Start/Stop Button Button(isTracking ? "Stop" : "Start") { toggleTracking() } .buttonStyle(.borderedProminent) .tint(isTracking ? .red : .green) } .onReceive(Timer.publish(every: 1, on: .main, in: .common).autoconnect()) { _ in if isTracking { updateMetrics() } } } private func toggleTracking() { isTracking.toggle() if isTracking { // Notify phone that workout started connector.sendMessage([ "event": "workoutStarted", "timestamp": Date().timeIntervalSince1970 ]) } else { // Send final summary to phone do { try connector.updateApplicationContext([ "workoutComplete": true, "totalCalories": calories, "avgHeartRate": heartRate, "endTime": Date().timeIntervalSince1970 ]) } catch { print("Failed to update context: \(error)") } } } private func updateMetrics() { // Simulate heart rate (in real app, use HealthKit) heartRate = Double.random(in: 120...160) calories += Double.random(in: 0.1...0.3) // Send real-time updates if phone is reachable if connector.isReachable { connector.sendMessage([ "heartRate": heartRate, "calories": calories, "timestamp": Date().timeIntervalSince1970 ]) } } } ``` * Phone (Capacitor) ```typescript import { CapgoWatch } from '@capgo/capacitor-watch'; class WorkoutTracker { private heartRateData: number[] = []; private totalCalories = 0; async initialize() { // Listen for real-time workout updates await CapgoWatch.addListener('messageReceived', (event) => { const { heartRate, calories, timestamp } = event.message; if (heartRate) { this.heartRateData.push(heartRate); this.updateUI(heartRate, calories); } if (event.message.event === 'workoutStarted') { this.onWorkoutStarted(timestamp); } }); // Listen for workout completion via context await CapgoWatch.addListener('applicationContextReceived', (event) => { if (event.context.workoutComplete) { this.onWorkoutComplete(event.context); } }); } private onWorkoutStarted(timestamp: number) { console.log('Workout started at:', new Date(timestamp * 1000)); this.heartRateData = []; this.totalCalories = 0; } private onWorkoutComplete(data: any) { const avgHeartRate = data.avgHeartRate; const totalCalories = data.totalCalories; // Save to your backend this.saveWorkout({ avgHeartRate, totalCalories, endTime: data.endTime, heartRateHistory: this.heartRateData }); } private updateUI(heartRate: number, calories: number) { // Update your app's UI document.getElementById('heart-rate')!.textContent = `${Math.round(heartRate)} BPM`; document.getElementById('calories')!.textContent = `${Math.round(calories)} cal`; } private async saveWorkout(data: any) { // Save to your backend console.log('Saving workout:', data); } } const tracker = new WorkoutTracker(); tracker.initialize(); ``` ## Example 2: Smart Home Remote [Section titled “Example 2: Smart Home Remote”](#example-2-smart-home-remote) Control smart home devices from your watch. * Watch (SwiftUI) ```swift import SwiftUI import CapgoWatchSDK struct SmartHomeView: View { @ObservedObject var connector = WatchConnector.shared @State private var devices: [SmartDevice] = [] @State private var isLoading = false var body: some View { NavigationView { Group { if isLoading { ProgressView("Loading...") } else if devices.isEmpty { Text("No devices") .foregroundColor(.secondary) } else { List(devices) { device in DeviceRow(device: device, onToggle: { toggleDevice(device) }) } } } .navigationTitle("Home") .toolbar { Button(action: refreshDevices) { Image(systemName: "arrow.clockwise") } } } .onAppear { refreshDevices() } .onChange(of: connector.lastMessage) { message in handleMessage(message) } } private func refreshDevices() { guard connector.isReachable else { // Load from cached context loadFromContext() return } isLoading = true connector.sendMessage(["action": "getDevices"]) { reply in DispatchQueue.main.async { isLoading = false if let deviceList = reply["devices"] as? [[String: Any]] { devices = deviceList.compactMap { SmartDevice(from: $0) } } } } } private func toggleDevice(_ device: SmartDevice) { connector.sendMessage([ "action": "toggleDevice", "deviceId": device.id, "newState": !device.isOn ]) { reply in DispatchQueue.main.async { if reply["success"] as? Bool == true { // Update local state if let index = devices.firstIndex(where: { $0.id == device.id }) { devices[index].isOn.toggle() } } } } } private func loadFromContext() { if let deviceList = connector.applicationContext["devices"] as? [[String: Any]] { devices = deviceList.compactMap { SmartDevice(from: $0) } } } private func handleMessage(_ message: [String: Any]) { // Handle push updates from phone if let update = message["deviceUpdate"] as? [String: Any], let deviceId = update["id"] as? String, let isOn = update["isOn"] as? Bool { if let index = devices.firstIndex(where: { $0.id == deviceId }) { devices[index].isOn = isOn } } } } struct SmartDevice: Identifiable { let id: String let name: String let type: String var isOn: Bool init?(from dict: [String: Any]) { guard let id = dict["id"] as? String, let name = dict["name"] as? String, let type = dict["type"] as? String, let isOn = dict["isOn"] as? Bool else { return nil } self.id = id self.name = name self.type = type self.isOn = isOn } } struct DeviceRow: View { let device: SmartDevice let onToggle: () -> Void var body: some View { HStack { Image(systemName: iconName) .foregroundColor(device.isOn ? .yellow : .gray) Text(device.name) Spacer() Toggle("", isOn: .constant(device.isOn)) .labelsHidden() .onTapGesture { onToggle() } } } var iconName: String { switch device.type { case "light": return device.isOn ? "lightbulb.fill" : "lightbulb" case "thermostat": return "thermometer" case "lock": return device.isOn ? "lock.fill" : "lock.open" default: return "power" } } } ``` * Phone (Capacitor) ```typescript import { CapgoWatch } from '@capgo/capacitor-watch'; interface SmartDevice { id: string; name: string; type: string; isOn: boolean; } class SmartHomeController { private devices: SmartDevice[] = []; async initialize() { // Handle requests from watch await CapgoWatch.addListener('messageReceivedWithReply', async (event) => { const { action, deviceId, newState } = event.message; let response: any = { success: false }; switch (action) { case 'getDevices': response = { devices: this.devices }; break; case 'toggleDevice': const success = await this.toggleDevice(deviceId, newState); response = { success }; break; } await CapgoWatch.replyToMessage({ callbackId: event.callbackId, data: response }); }); // Load initial devices await this.loadDevices(); // Keep watch updated with device states await this.syncDevicesToWatch(); } private async loadDevices() { // Load from your smart home API this.devices = [ { id: '1', name: 'Living Room', type: 'light', isOn: true }, { id: '2', name: 'Bedroom', type: 'light', isOn: false }, { id: '3', name: 'Thermostat', type: 'thermostat', isOn: true }, { id: '4', name: 'Front Door', type: 'lock', isOn: true } ]; } private async toggleDevice(deviceId: string, newState: boolean): Promise { try { // Call your smart home API const device = this.devices.find(d => d.id === deviceId); if (device) { device.isOn = newState; // Push update to watch const info = await CapgoWatch.getInfo(); if (info.isReachable) { await CapgoWatch.sendMessage({ data: { deviceUpdate: { id: deviceId, isOn: newState } } }); } return true; } return false; } catch (error) { console.error('Failed to toggle device:', error); return false; } } private async syncDevicesToWatch() { // Update application context for offline access await CapgoWatch.updateApplicationContext({ context: { devices: this.devices } }); } // Call this when devices change on the phone async onDeviceStateChanged(deviceId: string, isOn: boolean) { const device = this.devices.find(d => d.id === deviceId); if (device) { device.isOn = isOn; await this.syncDevicesToWatch(); } } } const controller = new SmartHomeController(); controller.initialize(); ``` ## Example 3: Notification Center [Section titled “Example 3: Notification Center”](#example-3-notification-center) Send custom notifications to the watch. * Phone (Capacitor) ```typescript import { CapgoWatch } from '@capgo/capacitor-watch'; class NotificationService { async sendNotification(title: string, body: string, data?: any) { const info = await CapgoWatch.getInfo(); if (info.isReachable) { // Send immediately if watch is reachable await CapgoWatch.sendMessage({ data: { type: 'notification', title, body, data, timestamp: Date.now() } }); } else { // Queue for later delivery await CapgoWatch.transferUserInfo({ userInfo: { type: 'notification', title, body, data, timestamp: Date.now() } }); } } async sendUrgentAlert(message: string) { // Use sendMessage for urgent alerts (requires watch to be reachable) const info = await CapgoWatch.getInfo(); if (!info.isReachable) { console.warn('Watch not reachable for urgent alert'); return false; } await CapgoWatch.sendMessage({ data: { type: 'urgentAlert', message, haptic: 'warning', timestamp: Date.now() } }); return true; } } // Usage const notifications = new NotificationService(); // Regular notification (queued if watch unavailable) await notifications.sendNotification( 'Order Update', 'Your order has shipped!', { orderId: '12345' } ); // Urgent alert (requires watch to be active) const sent = await notifications.sendUrgentAlert('Security alert: New login detected'); ``` * Watch (SwiftUI) ```swift import SwiftUI import WatchKit import CapgoWatchSDK struct NotificationView: View { @ObservedObject var connector = WatchConnector.shared @State private var notifications: [WatchNotification] = [] @State private var showAlert = false @State private var alertMessage = "" var body: some View { NavigationView { List(notifications) { notification in NotificationRow(notification: notification) } .navigationTitle("Notifications") } .onReceive(connector.$lastMessage) { message in handleMessage(message) } .alert("Alert", isPresented: $showAlert) { Button("OK") { } } message: { Text(alertMessage) } } private func handleMessage(_ message: [String: Any]) { guard let type = message["type"] as? String else { return } switch type { case "notification": let notification = WatchNotification( id: UUID().uuidString, title: message["title"] as? String ?? "", body: message["body"] as? String ?? "", timestamp: Date(timeIntervalSince1970: (message["timestamp"] as? Double ?? 0) / 1000) ) notifications.insert(notification, at: 0) // Play haptic WKInterfaceDevice.current().play(.notification) case "urgentAlert": alertMessage = message["message"] as? String ?? "Alert" showAlert = true // Play strong haptic for urgent alerts if let hapticType = message["haptic"] as? String { switch hapticType { case "warning": WKInterfaceDevice.current().play(.notification) DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { WKInterfaceDevice.current().play(.notification) } default: WKInterfaceDevice.current().play(.notification) } } default: break } } } struct WatchNotification: Identifiable { let id: String let title: String let body: String let timestamp: Date } struct NotificationRow: View { let notification: WatchNotification var body: some View { VStack(alignment: .leading, spacing: 4) { Text(notification.title) .font(.headline) Text(notification.body) .font(.caption) .foregroundColor(.secondary) Text(notification.timestamp, style: .relative) .font(.caption2) .foregroundColor(.secondary) } .padding(.vertical, 4) } } ``` ## Example 4: Data Sync Service [Section titled “Example 4: Data Sync Service”](#example-4-data-sync-service) Keep data synchronized between phone and watch. ```typescript import { CapgoWatch } from '@capgo/capacitor-watch'; class DataSyncService { private syncQueue: any[] = []; private isSyncing = false; async initialize() { // Listen for sync requests from watch await CapgoWatch.addListener('messageReceivedWithReply', async (event) => { if (event.message.action === 'sync') { const data = await this.getDataForSync(event.message.lastSyncTime); await CapgoWatch.replyToMessage({ callbackId: event.callbackId, data: { items: data, syncTime: Date.now() } }); } }); // Listen for data from watch await CapgoWatch.addListener('userInfoReceived', (event) => { this.handleWatchData(event.userInfo); }); // Monitor connectivity for sync opportunities await CapgoWatch.addListener('reachabilityChanged', (event) => { if (event.isReachable && this.syncQueue.length > 0) { this.processSyncQueue(); } }); } // Push important data to watch (guaranteed delivery) async pushToWatch(data: any) { await CapgoWatch.transferUserInfo({ userInfo: { type: 'dataUpdate', data, timestamp: Date.now() } }); } // Update shared state (latest value only) async updateSharedState(state: any) { await CapgoWatch.updateApplicationContext({ context: { ...state, lastUpdated: Date.now() } }); } // Queue data for sync when watch becomes available queueForSync(data: any) { this.syncQueue.push(data); this.attemptSync(); } private async attemptSync() { const info = await CapgoWatch.getInfo(); if (info.isReachable) { await this.processSyncQueue(); } } private async processSyncQueue() { if (this.isSyncing || this.syncQueue.length === 0) return; this.isSyncing = true; while (this.syncQueue.length > 0) { const item = this.syncQueue.shift(); await CapgoWatch.transferUserInfo({ userInfo: item }); } this.isSyncing = false; } private async getDataForSync(lastSyncTime: number): Promise { // Return data modified since lastSyncTime // This would query your database/API return []; } private handleWatchData(data: any) { console.log('Received from watch:', data); // Process and save data from watch } } ``` ## Best Practices [Section titled “Best Practices”](#best-practices) ### 1. Choose the Right Transfer Method [Section titled “1. Choose the Right Transfer Method”](#1-choose-the-right-transfer-method) | Method | Use Case | Delivery | | -------------------------- | --------------------------- | -------------------------------- | | `sendMessage` | Interactive, time-sensitive | Immediate (fails if unreachable) | | `updateApplicationContext` | Current app state | Latest value only | | `transferUserInfo` | Important data | Queued, guaranteed | ### 2. Handle Offline Gracefully [Section titled “2. Handle Offline Gracefully”](#2-handle-offline-gracefully) ```typescript async function smartSend(data: any, important: boolean) { const info = await CapgoWatch.getInfo(); if (info.isReachable) { // Send immediately await CapgoWatch.sendMessage({ data }); } else if (important) { // Queue for later await CapgoWatch.transferUserInfo({ userInfo: data }); } else { // Update context (will be available when watch opens app) await CapgoWatch.updateApplicationContext({ context: data }); } } ``` ### 3. Minimize Data Size [Section titled “3. Minimize Data Size”](#3-minimize-data-size) ```swift // Bad - sending large objects connector.sendMessage(["user": fullUserObject]) // Good - send only what's needed connector.sendMessage([ "userId": user.id, "name": user.displayName, "avatarUrl": user.avatarUrl ]) ``` ### 4. Use Haptics Appropriately [Section titled “4. Use Haptics Appropriately”](#4-use-haptics-appropriately) ```swift import WatchKit // Success feedback WKInterfaceDevice.current().play(.success) // Failure feedback WKInterfaceDevice.current().play(.failure) // Navigation feedback WKInterfaceDevice.current().play(.click) // Notification WKInterfaceDevice.current().play(.notification) ``` ### 5. Cache on Watch [Section titled “5. Cache on Watch”](#5-cache-on-watch) ```swift class DataCache { private let defaults = UserDefaults.standard func save(_ data: [String: Any], forKey key: String) { if let encoded = try? JSONSerialization.data(withJSONObject: data) { defaults.set(encoded, forKey: key) } } func load(forKey key: String) -> [String: Any]? { guard let data = defaults.data(forKey: key), let dict = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else { return nil } return dict } } ``` ## Next Steps [Section titled “Next Steps”](#next-steps) * [Watch App Setup](/docs/plugins/watch/watch-app-setup/) - Create your first watch app * [API Reference](/docs/plugins/watch/getting-started/#api-reference) - Complete API documentation * [GitHub Repository](https://github.com/Cap-go/capacitor-watch) - Source code and issues # Getting Started > Learn how to install and configure the Capacitor Watch plugin for bidirectional communication between iPhone and Apple Watch. 1. **Install the package** * npm ```sh npm i @capgo/capacitor-watch ``` * pnpm ```sh pnpm add @capgo/capacitor-watch ``` * yarn ```sh yarn add @capgo/capacitor-watch ``` * bun ```sh bun add @capgo/capacitor-watch ``` 2. **Sync with native projects** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` 3. **Configure the plugin** **Basic Usage Example:** ```typescript import { CapgoWatch } from '@capgo/capacitor-watch'; // Check watch connectivity status const info = await CapgoWatch.getInfo(); console.log('Watch paired:', info.isPaired); console.log('Watch reachable:', info.isReachable); // Listen for messages from watch await CapgoWatch.addListener('messageReceived', (event) => { console.log('Message from watch:', event.message); }); ``` **Send a Message to Watch:** ```typescript // Check if watch is reachable first const info = await CapgoWatch.getInfo(); if (info.isReachable) { await CapgoWatch.sendMessage({ data: { action: 'refresh', timestamp: Date.now() } }); } ``` * iOS **Required iOS Setup:** 1. Add the WatchConnectivity capability to your iOS app in Xcode 2. Create a watchOS app target in your Xcode project 3. Implement WatchConnectivity in your watchOS app (see Watch App Implementation below) The plugin automatically activates the WCSession when the plugin loads. * Android Apple Watch is only supported on iOS. On Android, all methods will reject with “Apple Watch is only supported on iOS” error. The `getInfo()` method returns `isSupported: false`. 4. **Handle messages that require a reply** ```typescript // Listen for messages that need a response await CapgoWatch.addListener('messageReceivedWithReply', async (event) => { console.log('Request from watch:', event.message); // Process the request const result = await processWatchRequest(event.message); // Send reply back to watch await CapgoWatch.replyToMessage({ callbackId: event.callbackId, data: { result } }); }); ``` 5. **Sync application state** ```typescript // Update application context (latest value only) await CapgoWatch.updateApplicationContext({ context: { theme: 'dark', userId: '123', lastSync: Date.now() } }); // Listen for context updates from watch await CapgoWatch.addListener('applicationContextReceived', (event) => { console.log('Context from watch:', event.context); }); ``` 6. **Transfer user info reliably** ```typescript // Queue data for reliable delivery (even when watch is offline) await CapgoWatch.transferUserInfo({ userInfo: { recordId: '456', action: 'created', data: { name: 'Item 1' } } }); // Listen for user info transfers await CapgoWatch.addListener('userInfoReceived', (event) => { console.log('User info from watch:', event.userInfo); }); ``` 7. **Monitor connectivity** ```typescript // Track reachability changes await CapgoWatch.addListener('reachabilityChanged', (event) => { console.log('Watch reachable:', event.isReachable); if (event.isReachable) { // Watch is now available for interactive messaging } }); // Track session activation state await CapgoWatch.addListener('activationStateChanged', (event) => { // 0 = notActivated, 1 = inactive, 2 = activated console.log('Session state:', event.state); }); ``` ## Watch App Implementation [Section titled “Watch App Implementation”](#watch-app-implementation) Your watchOS app needs to implement WatchConnectivity. Here’s a SwiftUI example: ```swift import SwiftUI import WatchConnectivity @main struct MyWatchApp: App { init() { WatchViewModel.shared.activate() } var body: some Scene { WindowGroup { ContentView() } } } class WatchViewModel: NSObject, ObservableObject, WCSessionDelegate { static let shared = WatchViewModel() @Published var lastMessage: [String: Any] = [:] func activate() { guard WCSession.isSupported() else { return } WCSession.default.delegate = self WCSession.default.activate() } // Send message to iPhone func sendToPhone(_ data: [String: Any]) { guard WCSession.default.isReachable else { print("iPhone not reachable") return } WCSession.default.sendMessage(data, replyHandler: nil) } // Send message with reply func sendToPhoneWithReply(_ data: [String: Any], completion: @escaping ([String: Any]) -> Void) { guard WCSession.default.isReachable else { return } WCSession.default.sendMessage(data, replyHandler: completion) } // Receive message from iPhone func session(_ session: WCSession, didReceiveMessage message: [String: Any]) { DispatchQueue.main.async { self.lastMessage = message } } // Receive application context func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String: Any]) { DispatchQueue.main.async { self.lastMessage = applicationContext } } // Required delegate methods func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) { print("Watch session activated: \(activationState.rawValue)") } } ``` ## API Reference [Section titled “API Reference”](#api-reference) ### Methods [Section titled “Methods”](#methods) #### `sendMessage(options: SendMessageOptions)` [Section titled “sendMessage(options: SendMessageOptions)”](#sendmessageoptions-sendmessageoptions) Send an interactive message to the watch. Requires watch to be reachable. **Parameters:** * `data`: Object - The data to send to the watch #### `updateApplicationContext(options: UpdateContextOptions)` [Section titled “updateApplicationContext(options: UpdateContextOptions)”](#updateapplicationcontextoptions-updatecontextoptions) Update application context. Only latest value is kept. **Parameters:** * `context`: Object - The context data to sync #### `transferUserInfo(options: TransferUserInfoOptions)` [Section titled “transferUserInfo(options: TransferUserInfoOptions)”](#transferuserinfooptions-transferuserinfooptions) Queue user info for reliable delivery. **Parameters:** * `userInfo`: Object - The user info to transfer #### `replyToMessage(options: ReplyMessageOptions)` [Section titled “replyToMessage(options: ReplyMessageOptions)”](#replytomessageoptions-replymessageoptions) Reply to a message that requested a response. **Parameters:** * `callbackId`: string - The callback ID from messageReceivedWithReply event * `data`: Object - The reply data #### `getInfo()` [Section titled “getInfo()”](#getinfo) Get watch connectivity status. **Returns:** `WatchInfo` object with: * `isSupported`: boolean - Whether WatchConnectivity is available * `isPaired`: boolean - Whether a watch is paired * `isWatchAppInstalled`: boolean - Whether watch app is installed * `isReachable`: boolean - Whether watch is reachable * `activationState`: number - Session state (0/1/2) #### `getPluginVersion()` [Section titled “getPluginVersion()”](#getpluginversion) Get the native plugin version. ### Events [Section titled “Events”](#events) | Event | Description | | ---------------------------- | ----------------------------------------------- | | `messageReceived` | Simple message from watch | | `messageReceivedWithReply` | Message expecting a reply (includes callbackId) | | `applicationContextReceived` | Context update from watch | | `userInfoReceived` | User info transfer from watch | | `reachabilityChanged` | Watch connectivity changed | | `activationStateChanged` | Session activation state changed | ## Communication Patterns [Section titled “Communication Patterns”](#communication-patterns) ### Immediate Messaging (`sendMessage`) [Section titled “Immediate Messaging (sendMessage)”](#immediate-messaging-sendmessage) * Requires watch to be reachable * Best for interactive, time-sensitive communication * Fails immediately if watch is not available ### Application Context (`updateApplicationContext`) [Section titled “Application Context (updateApplicationContext)”](#application-context-updateapplicationcontext) * Latest value only - previous values are overwritten * Best for syncing current app state * Delivered when watch becomes available ### User Info Transfer (`transferUserInfo`) [Section titled “User Info Transfer (transferUserInfo)”](#user-info-transfer-transferuserinfo) * Queued and delivered in order * Best for important data that must be delivered * Works even when watch is temporarily unreachable ## Platform Notes [Section titled “Platform Notes”](#platform-notes) ### iOS [Section titled “iOS”](#ios) * Requires iOS 15.0 or later * Uses WatchConnectivity framework * Session automatically activates on plugin load * Supports background delivery for context and user info ### Android [Section titled “Android”](#android) * Not supported (Apple Watch is iOS-only) * All methods reject with appropriate error * `getInfo()` returns `isSupported: false` ### Web [Section titled “Web”](#web) * Not supported * All methods reject with unavailable error * `getInfo()` returns `isSupported: false` ## Common Use Cases [Section titled “Common Use Cases”](#common-use-cases) 1. **Data Sync**: Keep watch and phone data in sync 2. **Remote Control**: Control phone features from watch 3. **Notifications**: Send custom notifications to watch 4. **Health Data**: Share fitness and health metrics 5. **Media Control**: Control music playback from watch 6. **Smart Home**: Control devices from your wrist ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) **Watch not reachable:** * Ensure watch is within Bluetooth range * Check that both apps are running * Verify WCSession is activated on both sides **Messages not received:** * Check that listeners are registered before sending * Verify the watch app implements WCSessionDelegate * Use `transferUserInfo` for guaranteed delivery **Session not activating:** * Ensure WatchConnectivity capability is added in Xcode * Check that watch app has the companion bundle ID * Verify both apps target compatible OS versions # Creating a watchOS App > Complete step-by-step guide to creating an Apple Watch app for your Capacitor iOS project with bidirectional communication. This guide walks you through creating a watchOS companion app from scratch, including project setup in Xcode, integrating the CapgoWatchSDK, and building a functional watch app with SwiftUI. ## Prerequisites [Section titled “Prerequisites”](#prerequisites) Before you begin, ensure you have: * **Xcode 15 or later** (download from Mac App Store) * **macOS Sonoma or later** (for latest watchOS SDK) * **An existing Capacitor iOS project** (run `npx cap add ios` if you haven’t) * **Apple Developer account** (free account works for development) ## Project Structure Overview [Section titled “Project Structure Overview”](#project-structure-overview) After completing this guide, your project will have this structure: * ios/ * App/ * App/ (your main iOS app) * … * App.xcodeproj * App.xcworkspace (use this to open the project) * Podfile * MyWatch/ (new watch app) * MyWatch/ (watch app source) * MyWatchApp.swift * ContentView\.swift * Assets.xcassets/ * … * MyWatch.xcodeproj ## Step 1: Open Your iOS Project in Xcode [Section titled “Step 1: Open Your iOS Project in Xcode”](#step-1-open-your-ios-project-in-xcode) 1. Navigate to your Capacitor project’s `ios/App` folder 2. Open `App.xcworkspace` (not `.xcodeproj`) by double-clicking it 3. Wait for Xcode to index the project Caution Always open the `.xcworkspace` file, not `.xcodeproj`. The workspace includes CocoaPods dependencies that your project needs. ## Step 2: Add a watchOS Target [Section titled “Step 2: Add a watchOS Target”](#step-2-add-a-watchos-target) 1. In Xcode, go to **File → New → Target…** 2. In the template chooser: * Select **watchOS** tab at the top * Choose **App** * Click **Next** 3. Configure your watch app: * **Product Name**: `MyWatch` (or your preferred name) * **Team**: Select your Apple Developer team * **Organization Identifier**: Should match your iOS app (e.g., `app.capgo`) * **Bundle Identifier**: Will be auto-generated (e.g., `app.capgo.myapp.watchkitapp`) * **Language**: Swift * **User Interface**: SwiftUI * **Watch App Type**: App (not App for Existing iOS App) * Uncheck **Include Notification Scene** (unless you need it) * Uncheck **Include Complication** (unless you need it) 4. Click **Finish** 5. When prompted “Activate ‘MyWatch’ scheme?”, click **Activate** ## Step 3: Configure Watch App Settings [Section titled “Step 3: Configure Watch App Settings”](#step-3-configure-watch-app-settings) 1. In the Project Navigator (left sidebar), select your project (the blue icon at the top) 2. Select your watch target (e.g., “MyWatch”) from the targets list 3. Go to the **General** tab: * **Display Name**: The name shown under the app icon (e.g., “My App”) * **Bundle Identifier**: Should end with `.watchkitapp` * **Version**: Match your iOS app version * **Build**: Match your iOS app build number 4. Go to **Signing & Capabilities** tab: * Enable **Automatically manage signing** * Select your **Team** * Xcode will create provisioning profiles automatically 5. Set **Deployment Info**: * **Minimum Deployments**: watchOS 9.0 or later ## Step 4: Add CapgoWatchSDK via Swift Package Manager [Section titled “Step 4: Add CapgoWatchSDK via Swift Package Manager”](#step-4-add-capgowatchsdk-via-swift-package-manager) The CapgoWatchSDK provides a ready-to-use `WatchConnector` class for communication. 1. In Xcode, go to **File → Add Package Dependencies…** 2. In the search field, enter: ```plaintext https://github.com/Cap-go/capacitor-watch.git ``` 3. Press Enter and wait for Xcode to fetch the package 4. Configure the package: * **Dependency Rule**: “Up to Next Major Version” with “8.0.0” * Click **Add Package** 5. Choose which products to add: * **IMPORTANT**: Only select `CapgoWatchSDK` * Make sure it’s added to your **watch target** (e.g., “MyWatch”), not the iOS app * Click **Add Package** Tip The `CapgoWatchSDK` is specifically designed for watchOS apps. It provides `WatchConnector`, an ObservableObject that handles all WatchConnectivity complexities for you. ## Step 5: Implement the Watch App [Section titled “Step 5: Implement the Watch App”](#step-5-implement-the-watch-app) Now let’s create the watch app code. Replace the auto-generated files with the following: ### 5.1 Create the App Entry Point [Section titled “5.1 Create the App Entry Point”](#51-create-the-app-entry-point) Edit `MyWatch/MyWatchApp.swift`: ```swift import SwiftUI import CapgoWatchSDK @main struct MyWatchApp: App { init() { // Activate WatchConnectivity when app launches WatchConnector.shared.activate() } var body: some Scene { WindowGroup { ContentView() } } } ``` ### 5.2 Create the Main View [Section titled “5.2 Create the Main View”](#52-create-the-main-view) Edit `MyWatch/ContentView.swift`: ```swift import SwiftUI import CapgoWatchSDK struct ContentView: View { // Observe the WatchConnector for automatic UI updates @ObservedObject var connector = WatchConnector.shared // Local state @State private var messageText = "" @State private var statusMessage = "Ready" var body: some View { ScrollView { VStack(spacing: 16) { // Connection Status ConnectionStatusView(connector: connector) Divider() // Message Input TextField("Message", text: $messageText) .textFieldStyle(.roundedBorder) // Send Buttons HStack { Button("Send") { sendMessage() } .disabled(!connector.isReachable || messageText.isEmpty) Button("Request") { sendWithReply() } .disabled(!connector.isReachable || messageText.isEmpty) } Divider() // Status Text(statusMessage) .font(.caption) .foregroundColor(.secondary) // Last Received Message if !connector.lastMessage.isEmpty { VStack(alignment: .leading) { Text("Last Message:") .font(.caption) .foregroundColor(.secondary) Text(formatMessage(connector.lastMessage)) .font(.caption2) } .frame(maxWidth: .infinity, alignment: .leading) } } .padding() } } private func sendMessage() { connector.sendMessage(["text": messageText, "timestamp": Date().timeIntervalSince1970]) statusMessage = "Message sent" messageText = "" } private func sendWithReply() { connector.sendMessage(["text": messageText, "needsReply": true]) { reply in DispatchQueue.main.async { statusMessage = "Reply: \(formatMessage(reply))" } } messageText = "" } private func formatMessage(_ message: [String: Any]) -> String { message.map { "\($0.key): \($0.value)" }.joined(separator: ", ") } } // Separate view for connection status struct ConnectionStatusView: View { @ObservedObject var connector: WatchConnector var body: some View { HStack { Circle() .fill(connector.isReachable ? Color.green : Color.red) .frame(width: 12, height: 12) Text(connector.isReachable ? "Connected" : "Disconnected") .font(.headline) Spacer() if connector.isActivated { Image(systemName: "checkmark.circle.fill") .foregroundColor(.green) } } } } #Preview { ContentView() } ``` ## Step 6: Configure iOS App for WatchConnectivity [Section titled “Step 6: Configure iOS App for WatchConnectivity”](#step-6-configure-ios-app-for-watchconnectivity) Your iOS app also needs WatchConnectivity capability. 1. In the Project Navigator, select your project 2. Select your **iOS App target** (not the watch target) 3. Go to **Signing & Capabilities** tab 4. Click **+ Capability** 5. Search for and add **WatchConnectivity** (if available) or it may be added automatically 6. The Capacitor plugin handles the iOS side automatically, but ensure your Info.plist has: ```xml WKCompanionAppBundleIdentifier app.capgo.myapp.watchkitapp ``` ## Step 7: Build and Run [Section titled “Step 7: Build and Run”](#step-7-build-and-run) ### Run on Simulator [Section titled “Run on Simulator”](#run-on-simulator) 1. Select your watch scheme from the scheme selector (top of Xcode window) 2. Choose a watch simulator: * Click on the device selector next to the scheme * Select an Apple Watch simulator (e.g., “Apple Watch Series 9 (45mm)”) 3. Click the **Run** button (▶️) or press `Cmd + R` 4. The iOS Simulator will launch with both iPhone and Apple Watch ### Run on Physical Device [Section titled “Run on Physical Device”](#run-on-physical-device) 1. Connect your iPhone via USB 2. Ensure your Apple Watch is paired with that iPhone 3. Select your watch scheme 4. Select your physical Apple Watch from the device list 5. Click **Run** 6. First time: You may need to trust your computer on both devices Tip To debug both apps simultaneously: 1. Run the iOS app first 2. Then run the watch app with “Debug → Attach to Process” for the watch ## Step 8: Test Communication [Section titled “Step 8: Test Communication”](#step-8-test-communication) ### From iPhone (Capacitor) to Watch [Section titled “From iPhone (Capacitor) to Watch”](#from-iphone-capacitor-to-watch) In your Capacitor app: ```typescript import { CapgoWatch } from '@capgo/capacitor-watch'; // Check connection const info = await CapgoWatch.getInfo(); console.log('Watch reachable:', info.isReachable); // Send a message if (info.isReachable) { await CapgoWatch.sendMessage({ data: { action: 'update', value: 'Hello from iPhone!' } }); } ``` ### From Watch to iPhone [Section titled “From Watch to iPhone”](#from-watch-to-iphone) The watch app uses `WatchConnector`: ```swift // Send message (fire and forget) WatchConnector.shared.sendMessage(["action": "buttonTapped"]) // Send message with reply WatchConnector.shared.sendMessage(["request": "getData"]) { reply in print("Got reply: \(reply)") } ``` ### Handle Messages on iPhone [Section titled “Handle Messages on iPhone”](#handle-messages-on-iphone) ```typescript // Listen for messages from watch await CapgoWatch.addListener('messageReceived', (event) => { console.log('Message from watch:', event.message); // { action: 'buttonTapped' } }); // Handle messages that need a reply await CapgoWatch.addListener('messageReceivedWithReply', async (event) => { console.log('Request from watch:', event.message); // Send reply back await CapgoWatch.replyToMessage({ callbackId: event.callbackId, data: { status: 'success', items: ['item1', 'item2'] } }); }); ``` ## Advanced: Custom Delegate for More Control [Section titled “Advanced: Custom Delegate for More Control”](#advanced-custom-delegate-for-more-control) If you need more control, implement `WatchConnectorDelegate`: ```swift import SwiftUI import CapgoWatchSDK class WatchHandler: WatchConnectorDelegate { func didReceiveMessage(_ message: [String: Any]) { print("Received: \(message)") // Handle incoming message } func didReceiveMessageWithReply(_ message: [String: Any], replyHandler: @escaping ([String: Any]) -> Void) { print("Received request: \(message)") // Process and send reply replyHandler(["status": "processed"]) } func didReceiveApplicationContext(_ context: [String: Any]) { print("Context updated: \(context)") } func didReceiveUserInfo(_ userInfo: [String: Any]) { print("User info received: \(userInfo)") } func reachabilityDidChange(_ isReachable: Bool) { print("Reachability changed: \(isReachable)") } func activationDidComplete(with state: WCSessionActivationState) { print("Activation completed: \(state.rawValue)") } } // In your app setup: let handler = WatchHandler() WatchConnector.shared.delegate = handler WatchConnector.shared.activate() ``` ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) ### Watch App Not Appearing on Watch [Section titled “Watch App Not Appearing on Watch”](#watch-app-not-appearing-on-watch) 1. Ensure bundle IDs are correctly related (watch app bundle ID should be iOS app bundle ID + `.watchkitapp`) 2. Check that both apps are signed with the same team 3. On physical device: Open Watch app on iPhone → My Watch → scroll to find your app → toggle ON ### Messages Not Being Received [Section titled “Messages Not Being Received”](#messages-not-being-received) 1. Verify both apps have WCSession activated 2. Check `isReachable` before sending messages 3. For guaranteed delivery, use `transferUserInfo` instead of `sendMessage` 4. Ensure listeners are registered before the other device sends messages ### ”Session Not Activated” Error [Section titled “”Session Not Activated” Error”](#session-not-activated-error) 1. Call `WatchConnector.shared.activate()` early in app lifecycle 2. On iOS, the plugin activates automatically - ensure plugin is imported 3. Check that WatchConnectivity capability is added to iOS target ### Build Errors with CapgoWatchSDK [Section titled “Build Errors with CapgoWatchSDK”](#build-errors-with-capgowatchsdk) 1. Ensure the package is added to the **watch target**, not iOS target 2. Clean build folder: **Product → Clean Build Folder** (Cmd + Shift + K) 3. Reset package caches: **File → Packages → Reset Package Caches** ### Simulator Issues [Section titled “Simulator Issues”](#simulator-issues) 1. Reset the simulators: **Device → Erase All Content and Settings** 2. Ensure iOS and watchOS simulators are compatible pairs 3. Both simulators need to be running for communication to work ## Next Steps [Section titled “Next Steps”](#next-steps) * [API Reference](/docs/plugins/watch/getting-started/#api-reference) - Complete API documentation * [Communication Patterns](/docs/plugins/watch/getting-started/#communication-patterns) - When to use each method * [Example App](https://github.com/Cap-go/capacitor-watch/tree/main/example-app) - Full working example # @capgo/capacitor-webview-version-checker > Detect outdated Android WebView versions at runtime and guide users to updates with native UX. Runtime version check Check the active Android WebView provider version while the app is running. Native update prompt Show a native modal and redirect users to the correct Play Store destination. Event-driven API Listen for `webViewLatest`, `webViewOutdated`, and `statusChanged` to drive app logic. Soft enforcement Let users continue using the app while still pushing them to update their WebView. ## Platform docs [Section titled “Platform docs”](#platform-docs) * [Getting started](/docs/plugins/webview-version-checker/getting-started/) * [Android setup](/docs/plugins/webview-version-checker/android/) * [iOS notes](/docs/plugins/webview-version-checker/ios/) # Android Setup > Android-specific setup for @capgo/capacitor-webview-version-checker. This plugin is Android-first and performs native WebView provider version checks. ## Android support matrix [Section titled “Android support matrix”](#android-support-matrix) * API 22+ (Android 5.1 or later) * Android 5-6 and 10+: Android System WebView provider * Android 7-9: Google Chrome provider ## Default behavior (main use case) [Section titled “Default behavior (main use case)”](#default-behavior-main-use-case) The default mode is Browserslist-style compatibility: * `minimumDeviceSharePercent` defaults to `3` * dataset is bundled at build time from caniuse data * no runtime dataset URL call is required unless you set `versionShareApiUrl` ```ts import type { CapacitorConfig } from '@capacitor/cli'; const config: CapacitorConfig = { plugins: { WebviewVersionChecker: {}, }, }; export default config; ``` ## Basic plugin config [Section titled “Basic plugin config”](#basic-plugin-config) ```ts import type { CapacitorConfig } from '@capacitor/cli'; const config: CapacitorConfig = { plugins: { WebviewVersionChecker: { autoPromptOnOutdated: true, }, }, }; export default config; ``` ## Advanced threshold mode (custom dataset) [Section titled “Advanced threshold mode (custom dataset)”](#advanced-threshold-mode-custom-dataset) ```ts import type { CapacitorConfig } from '@capacitor/cli'; const config: CapacitorConfig = { plugins: { WebviewVersionChecker: { minimumDeviceSharePercent: 3, versionShareByMajor: { '137': 58.2, '136': 21.3, '135': 4.6, '134': 2.1, }, }, }, }; export default config; ``` `versionShareByMajor` explanation: * key = major WebView version (example `137`) * value = share percent (`0..100`) * `minimumDeviceSharePercent: 3` means “compatible only if current major version >= 3% share in the dataset” You can also provide the dataset through `versionShareApiUrl` with: * `{ "versionShareByMajor": { "137": 54.2, "136": 23.8 } }` * `{ "shareByMajor": { "137": 54.2, "136": 23.8 } }` * `{ "versions": [{ "major": 137, "share": 54.2 }, { "version": "136.0.0.0", "percent": 23.8 }] }` # Getting Started > Learn how to install and use WebView Version Checker in your Capacitor Android app. 1. **Install the package** * npm ```sh npm i @capgo/capacitor-webview-version-checker ``` * pnpm ```sh pnpm add @capgo/capacitor-webview-version-checker ``` * yarn ```sh yarn add @capgo/capacitor-webview-version-checker ``` * bun ```sh bun add @capgo/capacitor-webview-version-checker ``` 2. **Sync native projects** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` 3. **Optional: add plugin config** You can run with defaults (`WebviewVersionChecker: {}`) or customize prompt and threshold behavior in `capacitor.config.ts`. ## Default behavior (main use case) [Section titled “Default behavior (main use case)”](#default-behavior-main-use-case) By default, this plugin uses a Browserslist-style compatibility rule: * `minimumDeviceSharePercent` defaults to `3` * the share dataset is bundled at build time from caniuse data * no runtime dataset URL call is needed for the default flow ```ts import type { CapacitorConfig } from '@capacitor/cli'; const config: CapacitorConfig = { plugins: { WebviewVersionChecker: {}, }, }; export default config; ``` ## Simple config-only setup (show native prompt) [Section titled “Simple config-only setup (show native prompt)”](#simple-config-only-setup-show-native-prompt) ```ts import type { CapacitorConfig } from '@capacitor/cli'; const config: CapacitorConfig = { plugins: { WebviewVersionChecker: { autoPromptOnOutdated: true, }, }, }; export default config; ``` ## Advanced threshold mode (custom dataset) [Section titled “Advanced threshold mode (custom dataset)”](#advanced-threshold-mode-custom-dataset) ```ts import type { CapacitorConfig } from '@capacitor/cli'; const config: CapacitorConfig = { plugins: { WebviewVersionChecker: { minimumDeviceSharePercent: 3, versionShareByMajor: { '137': 58.2, '136': 21.3, '135': 4.6, '134': 2.1, }, autoPromptOnOutdated: true, }, }, }; export default config; ``` Use this only if you want compatibility based on real-world share instead of only a fixed version. * `minimumDeviceSharePercent: 3` means the installed major version must represent at least 3% in your dataset. * `versionShareByMajor` is your custom map: major version => percentage. * If you prefer remote data, use `versionShareApiUrl` with one of: * `{ "versionShareByMajor": { "137": 54.2, "136": 23.8 } }` * `{ "shareByMajor": { "137": 54.2, "136": 23.8 } }` * `{ "versions": [{ "major": 137, "share": 54.2 }, { "version": "136.0.0.0", "percent": 23.8 }] }` ## Advanced usage with JavaScript [Section titled “Advanced usage with JavaScript”](#advanced-usage-with-javascript) ```ts import { WebviewVersionChecker } from '@capgo/capacitor-webview-version-checker'; await WebviewVersionChecker.addListener('webViewOutdated', (status) => { console.log('Outdated WebView detected', status); }); await WebviewVersionChecker.check({ minimumMajorVersion: 124, showPromptOnOutdated: true, }); ``` ## Why use this plugin instead of only Capacitor config [Section titled “Why use this plugin instead of only Capacitor config”](#why-use-this-plugin-instead-of-only-capacitor-config) Capacitor supports static minimum checks: ```ts android: { minWebViewVersion: 124, }, server: { errorPath: 'unsupported-webview.html', } ``` This plugin adds runtime events and native prompt UX, so users can still open and use the app while being encouraged to update. Evaluation order: 1. Device-share threshold mode (`minimumDeviceSharePercent` + dataset), if provided 2. Latest version mode (`latestVersion` / `latestVersionApiUrl`) 3. Minimum major fallback (`minimumMajorVersion`) ## Android provider handling [Section titled “Android provider handling”](#android-provider-handling) The plugin supports both WebView provider models used by Capacitor on Android: * Android 5-6 and 10+: Android System WebView (`com.google.android.webview`) * Android 7-9: Google Chrome (`com.android.chrome`) # iOS > iOS support notes for @capgo/capacitor-webview-version-checker. `@capgo/capacitor-webview-version-checker` currently targets Android runtime checks. ## Why [Section titled “Why”](#why) The plugin is designed for Android WebView provider compatibility flows: * Detect active WebView provider package and version * Compare against latest/minimum rules or device-share threshold * Offer native prompt routing to provider update destination iOS does not use the same update-provider model, so this plugin does not expose equivalent native enforcement there. # @capgo/capacitor-wechat > Integrate WeChat SDK into your Capacitor app for seamless authentication, content sharing, payments, and mini-program access. OAuth Authentication Enable secure WeChat login for seamless user authentication 🔐 Content Sharing Share text, images, links, music, video, and mini-programs to WeChat 📤 WeChat Pay Accept payments through WeChat Pay for seamless transactions 💳 Mini-Programs Launch WeChat mini-programs directly from your app 🚀 Cross-platform Works on both iOS and Android with native WeChat SDK 📱 Comprehensive Documentation Check the [Documentation](/docs/plugins/wechat/getting-started/) to master the plugin in just a few minutes. # Getting Started > Learn how to install and use the WeChat plugin to integrate WeChat functionality in your Capacitor app. 1. **Install the package** * npm ```sh npm i @capgo/capacitor-wechat ``` * pnpm ```sh pnpm add @capgo/capacitor-wechat ``` * yarn ```sh yarn add @capgo/capacitor-wechat ``` * bun ```sh bun add @capgo/capacitor-wechat ``` 2. **Sync with native projects** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Configuration [Section titled “Configuration”](#configuration) ### WeChat App Registration [Section titled “WeChat App Registration”](#wechat-app-registration) Before using this plugin, you must register your app on the [WeChat Open Platform](https://open.weixin.qq.com/) to obtain an App ID. ### iOS Configuration [Section titled “iOS Configuration”](#ios-configuration) Add the following to your `Info.plist`: ```xml LSApplicationQueriesSchemes weixin weixinULAPI CFBundleURLTypes CFBundleTypeRole Editor CFBundleURLName weixin CFBundleURLSchemes YOUR_WECHAT_APP_ID ``` You’ll need to integrate the WeChat SDK into your iOS project. Add the WeChat SDK to your `Podfile` or download it from the [WeChat Open Platform](https://developers.weixin.qq.com/doc/oplatform/en/Mobile_App/Access_Guide/iOS.html). ### Android Configuration [Section titled “Android Configuration”](#android-configuration) Add the following to your `AndroidManifest.xml`: ```xml ``` You’ll need to integrate the WeChat SDK into your Android project. Add the WeChat SDK dependency to your `build.gradle` or download it from the [WeChat Open Platform](https://developers.weixin.qq.com/doc/oplatform/en/Mobile_App/Access_Guide/Android.html). ## Usage [Section titled “Usage”](#usage) ### Check WeChat Installation [Section titled “Check WeChat Installation”](#check-wechat-installation) ```typescript import { CapacitorWechat } from '@capgo/capacitor-wechat'; const checkWeChat = async () => { const { installed } = await CapacitorWechat.isInstalled(); if (installed) { console.log('WeChat is installed'); } else { console.log('WeChat is not installed'); } }; ``` ### Authentication [Section titled “Authentication”](#authentication) ```typescript const loginWithWeChat = async () => { try { const { code, state } = await CapacitorWechat.auth({ scope: 'snsapi_userinfo', // or 'snsapi_login' state: 'my_random_state' }); // Send code to your backend to exchange for access token const response = await fetch('https://yourapi.com/auth/wechat', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ code }) }); const { access_token, openid } = await response.json(); console.log('Logged in with WeChat:', openid); } catch (error) { console.error('WeChat auth failed:', error); } }; ``` ### Share Content [Section titled “Share Content”](#share-content) #### Share Text [Section titled “Share Text”](#share-text) ```typescript const shareText = async () => { await CapacitorWechat.share({ scene: 0, // 0 = Chat, 1 = Moments, 2 = Favorite type: 'text', text: 'Hello from Capacitor WeChat!' }); }; ``` #### Share Link [Section titled “Share Link”](#share-link) ```typescript const shareLink = async () => { await CapacitorWechat.share({ scene: 1, // Share to Moments type: 'link', title: 'Check out this awesome app!', description: 'Built with Capacitor and WeChat SDK', link: 'https://capacitorjs.com', thumbUrl: 'https://capacitorjs.com/icon.png' }); }; ``` #### Share Image [Section titled “Share Image”](#share-image) ```typescript const shareImage = async () => { await CapacitorWechat.share({ scene: 0, type: 'image', imageUrl: 'https://example.com/image.png', // or base64 data thumbUrl: 'https://example.com/thumb.png' }); }; ``` ### WeChat Pay [Section titled “WeChat Pay”](#wechat-pay) ```typescript const payWithWeChat = async () => { // First, get payment parameters from your server const paymentParams = await fetchPaymentParamsFromServer(); try { await CapacitorWechat.sendPaymentRequest({ partnerId: paymentParams.partnerId, prepayId: paymentParams.prepayId, nonceStr: paymentParams.nonceStr, timeStamp: paymentParams.timeStamp, package: paymentParams.package, // Usually 'Sign=WXPay' sign: paymentParams.sign }); console.log('Payment successful!'); } catch (error) { console.error('Payment failed:', error); } }; ``` ### Open Mini Program [Section titled “Open Mini Program”](#open-mini-program) ```typescript const openMiniProgram = async () => { await CapacitorWechat.openMiniProgram({ username: 'gh_xxxxxxxxxxxxx', // Mini program original ID path: 'pages/index/index', // Path within mini program type: 0 // 0 = Release, 1 = Test, 2 = Preview }); }; ``` ## API Reference [Section titled “API Reference”](#api-reference) ### isInstalled() [Section titled “isInstalled()”](#isinstalled) Check if WeChat app is installed on the device. ```typescript const result = await CapacitorWechat.isInstalled(); // Returns: { installed: boolean } ``` ### auth(options) [Section titled “auth(options)”](#authoptions) Authenticate user with WeChat OAuth. ```typescript interface WechatAuthOptions { scope: string; // 'snsapi_userinfo' or 'snsapi_login' state?: string; // Optional state parameter for CSRF protection } const result = await CapacitorWechat.auth(options); // Returns: { code: string, state?: string } ``` ### share(options) [Section titled “share(options)”](#shareoptions) Share content to WeChat. ```typescript interface WechatShareOptions { scene: number; // 0 = Session (chat), 1 = Timeline (moments), 2 = Favorite type: 'text' | 'image' | 'link' | 'music' | 'video' | 'miniprogram'; text?: string; // For type 'text' title?: string; // For type 'link', 'music', 'video', 'miniprogram' description?: string; // For type 'link', 'music', 'video', 'miniprogram' link?: string; // For type 'link' imageUrl?: string; // Image URL or base64 data thumbUrl?: string; // Thumbnail URL or base64 data mediaUrl?: string; // For type 'music' or 'video' miniProgramUsername?: string; // For type 'miniprogram' miniProgramPath?: string; // For type 'miniprogram' miniProgramType?: number; // For type 'miniprogram': 0 = Release, 1 = Test, 2 = Preview miniProgramWebPageUrl?: string; // Fallback URL for type 'miniprogram' } await CapacitorWechat.share(options); ``` ### sendPaymentRequest(options) [Section titled “sendPaymentRequest(options)”](#sendpaymentrequestoptions) Send payment request to WeChat Pay. ```typescript interface WechatPaymentOptions { partnerId: string; // Merchant ID prepayId: string; // Prepay ID from unified order API nonceStr: string; // Random string timeStamp: string; // Timestamp package: string; // Usually 'Sign=WXPay' sign: string; // Signature } await CapacitorWechat.sendPaymentRequest(options); ``` ### openMiniProgram(options) [Section titled “openMiniProgram(options)”](#openminiprogramoptions) Open WeChat mini-program. ```typescript interface WechatMiniProgramOptions { username: string; // Mini-program username (original ID) path?: string; // Path to open in mini-program type?: number; // 0 = Release, 1 = Test, 2 = Preview } await CapacitorWechat.openMiniProgram(options); ``` ### chooseInvoice(options) [Section titled “chooseInvoice(options)”](#chooseinvoiceoptions) Choose invoice from WeChat (for business apps). ```typescript interface WechatInvoiceOptions { appId: string; signType: string; cardSign: string; timeStamp: string; nonceStr: string; } const result = await CapacitorWechat.chooseInvoice(options); // Returns: { cards: string[] } ``` ### getPluginVersion() [Section titled “getPluginVersion()”](#getpluginversion) Get the native plugin version. ```typescript const result = await CapacitorWechat.getPluginVersion(); // Returns: { version: string } ``` ## Complete Example [Section titled “Complete Example”](#complete-example) ```typescript import { CapacitorWechat } from '@capgo/capacitor-wechat'; export class WeChatService { async init() { const { installed } = await CapacitorWechat.isInstalled(); if (!installed) { throw new Error('WeChat is not installed'); } } async login() { const { code } = await CapacitorWechat.auth({ scope: 'snsapi_userinfo', state: Math.random().toString(36).substring(7) }); // Exchange code for access token on your backend const response = await fetch('/api/auth/wechat', { method: 'POST', body: JSON.stringify({ code }) }); return response.json(); } async shareToChat(title: string, description: string, link: string, imageUrl: string) { await CapacitorWechat.share({ scene: 0, // Chat type: 'link', title, description, link, thumbUrl: imageUrl }); } async shareToMoments(title: string, description: string, link: string, imageUrl: string) { await CapacitorWechat.share({ scene: 1, // Moments type: 'link', title, description, link, thumbUrl: imageUrl }); } } ``` ## Important Notes [Section titled “Important Notes”](#important-notes) 1. **WeChat SDK Integration**: This plugin provides the Capacitor interface, but you need to integrate the official WeChat SDK into your native projects. 2. **App Registration**: You must register your app on the [WeChat Open Platform](https://open.weixin.qq.com/) to get an App ID. 3. **Universal Links (iOS)**: For iOS 13+, you need to configure Universal Links for WeChat callbacks. 4. **Backend Integration**: Authentication and payment features require backend integration to exchange codes for tokens and prepare payment parameters. 5. **Testing**: WeChat features require testing on physical devices - they will not work in simulators. ## Best Practices [Section titled “Best Practices”](#best-practices) 1. **Always check if WeChat is installed** before attempting to use features. 2. **Handle errors gracefully** with try-catch blocks. 3. **Use state parameter** in auth requests for CSRF protection. 4. **Validate payment responses** on your backend before granting access. 5. **Compress images** before sharing to reduce data usage. ## Platform Notes [Section titled “Platform Notes”](#platform-notes) ### iOS [Section titled “iOS”](#ios) * Requires iOS 10.0+ * Uses official WeChat SDK for iOS * Requires Universal Links configuration for iOS 13+ ### Android [Section titled “Android”](#android) * Requires Android 5.0 (API 21)+ * Uses official WeChat SDK for Android * Requires WXEntryActivity configuration ### Web [Section titled “Web”](#web) * Not supported on web platform # @capgo/capacitor-wifi > Manage WiFi connections, scan networks, and monitor connectivity status in your Capacitor app. Network Scanning Scan and discover available WiFi networks 📡 Connection Management Connect and disconnect from WiFi networks programmatically 🔌 Status Monitoring Monitor WiFi connection status in real-time 📊 Cross-platform Works on both iOS and Android devices 📱 Network Info Get detailed information about connected networks 🔍 Comprehensive Documentation Check the [Documentation](/docs/plugins/wifi/getting-started/) to master the plugin in just a few minutes. # Getting Started > Learn how to install and use the WiFi plugin to manage network connectivity in your Capacitor app. 1. **Install the package** * npm ```sh npm i @capgo/capacitor-wifi ``` * pnpm ```sh pnpm add @capgo/capacitor-wifi ``` * yarn ```sh yarn add @capgo/capacitor-wifi ``` * bun ```sh bun add @capgo/capacitor-wifi ``` 2. **Sync with native projects** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` 3. **Configure permissions** **Android**: Add to `AndroidManifest.xml`: ```xml ``` **iOS**: Add to `Info.plist`: ```xml NSLocationWhenInUseUsageDescription We need location access to scan WiFi networks ``` ## Usage [Section titled “Usage”](#usage) Import the plugin and manage WiFi connectivity: ```typescript import { CapacitorWifi } from '@capgo/capacitor-wifi'; // Get current WiFi connection info const getWifiInfo = async () => { const info = await CapacitorWifi.getWifiInfo(); console.log('Connected to:', info.ssid); console.log('IP Address:', info.ip); }; // Scan for available networks const scanNetworks = async () => { const { networks } = await CapacitorWifi.scan(); networks.forEach(network => { console.log(`SSID: ${network.ssid}, Signal: ${network.level}`); }); }; // Connect to a network const connectToWifi = async () => { await CapacitorWifi.connect({ ssid: 'MyNetwork', password: 'mypassword' }); }; ``` ## API Reference [Section titled “API Reference”](#api-reference) ### getWifiInfo() [Section titled “getWifiInfo()”](#getwifiinfo) Gets information about the currently connected WiFi network. ```typescript interface WifiInfo { ssid: string; // Network name bssid: string; // MAC address of access point ip: string; // Device IP address frequency: number; // Network frequency (MHz) linkSpeed: number; // Connection speed (Mbps) signalStrength: number; // Signal strength (0-100) } const info = await CapacitorWifi.getWifiInfo(); ``` ### scan() [Section titled “scan()”](#scan) Scans for available WiFi networks. ```typescript interface WifiNetwork { ssid: string; // Network name bssid: string; // MAC address level: number; // Signal level (dBm) frequency: number; // Network frequency capabilities: string; // Security capabilities } const { networks } = await CapacitorWifi.scan(); ``` ### connect(options) [Section titled “connect(options)”](#connectoptions) Connects to a WiFi network. ```typescript interface ConnectOptions { ssid: string; // Network name password?: string; // Network password (if secured) isHiddenSsid?: boolean; // Whether SSID is hidden } await CapacitorWifi.connect({ ssid: 'MyNetwork', password: 'mypassword' }); ``` ### disconnect() [Section titled “disconnect()”](#disconnect) Disconnects from the current WiFi network. ```typescript await CapacitorWifi.disconnect(); ``` ### getSSID() [Section titled “getSSID()”](#getssid) Gets the SSID of the currently connected network. ```typescript const { ssid } = await CapacitorWifi.getSSID(); console.log('Connected to:', ssid); ``` ### getIP() [Section titled “getIP()”](#getip) Gets the current device IP address. ```typescript const { ip } = await CapacitorWifi.getIP(); console.log('IP Address:', ip); ``` ## Complete Example [Section titled “Complete Example”](#complete-example) ```typescript import { CapacitorWifi } from '@capgo/capacitor-wifi'; export class WifiService { async getCurrentNetwork() { try { const info = await CapacitorWifi.getWifiInfo(); return { name: info.ssid, strength: this.getSignalQuality(info.signalStrength), speed: `${info.linkSpeed} Mbps`, ip: info.ip }; } catch (error) { console.error('Failed to get WiFi info:', error); return null; } } async scanAndConnect(targetSsid: string, password: string) { try { // Scan for networks const { networks } = await CapacitorWifi.scan(); // Find target network const targetNetwork = networks.find(n => n.ssid === targetSsid); if (!targetNetwork) { throw new Error(`Network ${targetSsid} not found`); } console.log(`Found network with signal: ${targetNetwork.level} dBm`); // Connect to network await CapacitorWifi.connect({ ssid: targetSsid, password: password }); console.log('Connected successfully!'); return true; } catch (error) { console.error('Connection failed:', error); return false; } } async findBestNetwork(preferredNetworks: string[]) { const { networks } = await CapacitorWifi.scan(); // Filter to preferred networks const available = networks.filter(n => preferredNetworks.includes(n.ssid) ); if (available.length === 0) { return null; } // Sort by signal strength available.sort((a, b) => b.level - a.level); return available[0]; } async monitorConnection(callback: (info: WifiInfo | null) => void) { const checkConnection = async () => { try { const info = await CapacitorWifi.getWifiInfo(); callback(info); } catch (error) { callback(null); } }; // Check every 5 seconds const interval = setInterval(checkConnection, 5000); // Initial check await checkConnection(); // Return cleanup function return () => clearInterval(interval); } private getSignalQuality(strength: number): string { if (strength >= 80) return 'Excellent'; if (strength >= 60) return 'Good'; if (strength >= 40) return 'Fair'; return 'Poor'; } async getNetworkSecurity(ssid: string): Promise { const { networks } = await CapacitorWifi.scan(); const network = networks.find(n => n.ssid === ssid); if (!network) { return 'Unknown'; } const caps = network.capabilities.toLowerCase(); if (caps.includes('wpa3')) return 'WPA3'; if (caps.includes('wpa2')) return 'WPA2'; if (caps.includes('wpa')) return 'WPA'; if (caps.includes('wep')) return 'WEP'; return 'Open'; } } ``` ## Advanced Usage [Section titled “Advanced Usage”](#advanced-usage) ### Network Quality Assessment [Section titled “Network Quality Assessment”](#network-quality-assessment) ```typescript const assessNetworkQuality = async () => { const info = await CapacitorWifi.getWifiInfo(); const quality = { signal: info.signalStrength >= 70 ? 'Excellent' : info.signalStrength >= 50 ? 'Good' : info.signalStrength >= 30 ? 'Fair' : 'Poor', speed: info.linkSpeed >= 100 ? 'Fast' : info.linkSpeed >= 50 ? 'Medium' : 'Slow', frequency: info.frequency >= 5000 ? '5GHz' : '2.4GHz' }; console.log('Network Quality:', quality); return quality; }; ``` ### Auto-Connect to Preferred Networks [Section titled “Auto-Connect to Preferred Networks”](#auto-connect-to-preferred-networks) ```typescript const autoConnect = async (preferredNetworks: Array<{ ssid: string, password: string }>) => { const { networks } = await CapacitorWifi.scan(); for (const preferred of preferredNetworks) { const found = networks.find(n => n.ssid === preferred.ssid); if (found) { try { await CapacitorWifi.connect({ ssid: preferred.ssid, password: preferred.password }); console.log(`Connected to ${preferred.ssid}`); return true; } catch (error) { console.error(`Failed to connect to ${preferred.ssid}`); } } } return false; }; ``` ### Network Change Detection [Section titled “Network Change Detection”](#network-change-detection) ```typescript class NetworkMonitor { private currentSsid: string | null = null; private listeners: Array<(ssid: string | null) => void> = []; async start() { setInterval(async () => { try { const { ssid } = await CapacitorWifi.getSSID(); if (ssid !== this.currentSsid) { this.currentSsid = ssid; this.notifyListeners(ssid); } } catch (error) { if (this.currentSsid !== null) { this.currentSsid = null; this.notifyListeners(null); } } }, 3000); } onNetworkChange(callback: (ssid: string | null) => void) { this.listeners.push(callback); } private notifyListeners(ssid: string | null) { this.listeners.forEach(listener => listener(ssid)); } } ``` ## Best Practices [Section titled “Best Practices”](#best-practices) 1. **Permissions**: Request location permissions before scanning networks 2. **Error Handling**: Always wrap WiFi operations in try-catch blocks 3. **User Feedback**: Show loading indicators during network operations 4. **Security**: Never store WiFi passwords in plain text 5. **Testing**: Test on real devices as WiFi APIs may not work in emulators ## Platform Differences [Section titled “Platform Differences”](#platform-differences) ### iOS [Section titled “iOS”](#ios) * Requires location permissions to scan networks * Cannot programmatically connect to networks (will open Settings) * Limited access to network details ### Android [Section titled “Android”](#android) * Full programmatic WiFi control * Requires location permissions for network scanning * Can connect/disconnect programmatically ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) ### Common Issues [Section titled “Common Issues”](#common-issues) **Scan returns empty**: Check location permissions are granted **Cannot connect to network**: Verify password is correct and network is in range **getWifiInfo fails**: Ensure device is connected to a WiFi network **Permission denied**: Add required permissions to platform configuration files # @capgo/capacitor-youtube-player > Integrate YouTube video playback with full control over player state, playlists, quality, and events in your Capacitor applications. Full YouTube API Complete access to YouTube Player API Playlist Support Load and control YouTube playlists Quality Control Adjust playback quality dynamically Event Listeners React to player state changes and events # Getting Started > Learn how to integrate YouTube video playback in your Capacitor app. 1. **Install the package** * npm ```sh npm i @capgo/capacitor-youtube-player ``` * pnpm ```sh pnpm add @capgo/capacitor-youtube-player ``` * yarn ```sh yarn add @capgo/capacitor-youtube-player ``` * bun ```sh bun add @capgo/capacitor-youtube-player ``` 2. **Sync with native projects** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Basic Usage [Section titled “Basic Usage”](#basic-usage) ```typescript import { YoutubePlayer } from '@capgo/capacitor-youtube-player'; // Initialize the player const result = await YoutubePlayer.initialize({ playerId: 'youtube-player', playerSize: { width: 640, height: 360 }, videoId: 'dQw4w9WgXcQ', fullscreen: false, playerVars: { autoplay: 1, controls: 1 } }); // Play video await YoutubePlayer.playVideo(result.player); // Pause video await YoutubePlayer.pauseVideo(result.player); // Listen to player events YoutubePlayer.addEventListener(result.player, 'onStateChange', (event) => { console.log('Player state:', event.data); }); ``` ## API Reference [Section titled “API Reference”](#api-reference) ### initialize(options) [Section titled “initialize(options)”](#initializeoptions) Initialize a YouTube player instance. ```typescript const { player, playerReady } = await YoutubePlayer.initialize({ playerId: 'my-player', playerSize: { width: 1280, height: 720 }, videoId: 'VIDEO_ID', fullscreen: false, playerVars: { autoplay: 0, controls: 1, rel: 0 } }); ``` ### Video Control Methods [Section titled “Video Control Methods”](#video-control-methods) ```typescript // Play await YoutubePlayer.playVideo(playerId); // Pause await YoutubePlayer.pauseVideo(playerId); // Stop await YoutubePlayer.stopVideo(playerId); // Seek to time await YoutubePlayer.seekTo(playerId, seconds, allowSeekAhead); // Load video by ID await YoutubePlayer.loadVideoById(playerId, { videoId: 'VIDEO_ID' }); // Cue video (load without playing) await YoutubePlayer.cueVideoById(playerId, { videoId: 'VIDEO_ID' }); ``` ### Playlist Control [Section titled “Playlist Control”](#playlist-control) ```typescript // Load playlist await YoutubePlayer.loadPlaylist(playerId, { listType: 'playlist', list: 'PLAYLIST_ID' }); // Cue playlist await YoutubePlayer.cuePlaylist(playerId, { playlist: ['VIDEO_ID_1', 'VIDEO_ID_2'], index: 0 }); // Navigate playlist await YoutubePlayer.nextVideo(playerId); await YoutubePlayer.previousVideo(playerId); await YoutubePlayer.playVideoAt(playerId, index); ``` ### Audio Control [Section titled “Audio Control”](#audio-control) ```typescript // Mute/Unmute await YoutubePlayer.mute(playerId); await YoutubePlayer.unMute(playerId); const { result } = await YoutubePlayer.isMuted(playerId); console.log('Muted:', result.value); // Volume (0-100) await YoutubePlayer.setVolume(playerId, 50); const { result } = await YoutubePlayer.getVolume(playerId); ``` ### Playback Control [Section titled “Playback Control”](#playback-control) ```typescript // Playback rate await YoutubePlayer.setPlaybackRate(playerId, 1.5); const { result } = await YoutubePlayer.getPlaybackRate(playerId); // Available rates const { result } = await YoutubePlayer.getAvailablePlaybackRates(playerId); console.log('Available rates:', result.value); // Loop and shuffle await YoutubePlayer.setLoop(playerId, true); await YoutubePlayer.setShuffle(playerId, true); ``` ### Quality Control [Section titled “Quality Control”](#quality-control) ```typescript // Set quality await YoutubePlayer.setPlaybackQuality(playerId, 'hd720'); // Get current quality const { result } = await YoutubePlayer.getPlaybackQuality(playerId); // Get available qualities const { result } = await YoutubePlayer.getAvailableQualityLevels(playerId); ``` ### Player Information [Section titled “Player Information”](#player-information) ```typescript // Duration const { result } = await YoutubePlayer.getDuration(playerId); // Current time const { result } = await YoutubePlayer.getCurrentTime(playerId); // Loaded fraction const { result } = await YoutubePlayer.getVideoLoadedFraction(playerId); // Player state const { result } = await YoutubePlayer.getPlayerState(playerId); // Video URL const { result } = await YoutubePlayer.getVideoUrl(playerId); ``` ### Event Listeners [Section titled “Event Listeners”](#event-listeners) ```typescript // State change YoutubePlayer.addEventListener(playerId, 'onStateChange', (event) => { console.log('State:', event.data); // -1, 0, 1, 2, 3, 5 }); // Ready YoutubePlayer.addEventListener(playerId, 'onReady', (event) => { console.log('Player ready'); }); // Error YoutubePlayer.addEventListener(playerId, 'onError', (event) => { console.error('Player error:', event.data); }); // Quality change YoutubePlayer.addEventListener(playerId, 'onPlaybackQualityChange', (event) => { console.log('Quality:', event.data); }); // Rate change YoutubePlayer.addEventListener(playerId, 'onPlaybackRateChange', (event) => { console.log('Rate:', event.data); }); // Remove listener YoutubePlayer.removeEventListener(playerId, 'onStateChange', callback); ``` ## Complete Example [Section titled “Complete Example”](#complete-example) ```typescript import { YoutubePlayer } from '@capgo/capacitor-youtube-player'; export class YouTubeService { private playerId: string | null = null; async initPlayer(videoId: string) { try { const { player, playerReady } = await YoutubePlayer.initialize({ playerId: 'main-player', playerSize: { width: 1280, height: 720 }, videoId, fullscreen: false, playerVars: { autoplay: 0, controls: 1, modestbranding: 1, rel: 0 } }); this.playerId = player; // Set up event listeners YoutubePlayer.addEventListener(player, 'onReady', () => { console.log('Player is ready'); }); YoutubePlayer.addEventListener(player, 'onStateChange', (event) => { this.handleStateChange(event.data); }); YoutubePlayer.addEventListener(player, 'onError', (event) => { console.error('YouTube error:', event.data); }); return player; } catch (error) { console.error('Failed to initialize player:', error); throw error; } } private handleStateChange(state: number) { switch (state) { case -1: console.log('Unstarted'); break; case 0: console.log('Ended'); break; case 1: console.log('Playing'); break; case 2: console.log('Paused'); break; case 3: console.log('Buffering'); break; case 5: console.log('Video cued'); break; } } async play() { if (!this.playerId) return; await YoutubePlayer.playVideo(this.playerId); } async pause() { if (!this.playerId) return; await YoutubePlayer.pauseVideo(this.playerId); } async loadVideo(videoId: string) { if (!this.playerId) return; await YoutubePlayer.loadVideoById(this.playerId, { videoId }); } async loadPlaylist(playlistId: string) { if (!this.playerId) return; await YoutubePlayer.loadPlaylist(this.playerId, { listType: 'playlist', list: playlistId, index: 0 }); } async setQuality(quality: 'small' | 'medium' | 'large' | 'hd720' | 'hd1080') { if (!this.playerId) return; await YoutubePlayer.setPlaybackQuality(this.playerId, quality); } async getProgress() { if (!this.playerId) return { current: 0, duration: 0 }; const current = await YoutubePlayer.getCurrentTime(this.playerId); const duration = await YoutubePlayer.getDuration(this.playerId); return { current: current.result.value, duration: duration.result.value }; } async destroy() { if (!this.playerId) return; await YoutubePlayer.destroy(this.playerId); this.playerId = null; } } ``` ## Player States [Section titled “Player States”](#player-states) | State | Value | Description | | --------- | ----- | ------------------ | | UNSTARTED | -1 | Video not started | | ENDED | 0 | Video has ended | | PLAYING | 1 | Video is playing | | PAUSED | 2 | Video is paused | | BUFFERING | 3 | Video is buffering | | CUED | 5 | Video is cued | ## Quality Levels [Section titled “Quality Levels”](#quality-levels) * `small`: 240p * `medium`: 360p * `large`: 480p * `hd720`: 720p * `hd1080`: 1080p * `highres`: >1080p * `default`: Auto quality ## Best Practices [Section titled “Best Practices”](#best-practices) 1. **Initialize once** Create player instance once and reuse it: ```typescript let player = await YoutubePlayer.initialize(options); // Reuse player for different videos await YoutubePlayer.loadVideoById(player, { videoId: 'NEW_ID' }); ``` 2. **Handle errors gracefully** ```typescript YoutubePlayer.addEventListener(playerId, 'onError', (event) => { switch (event.data) { case 2: console.error('Invalid parameter'); break; case 100: console.error('Video not found'); break; case 101: case 150: console.error('Embedding not allowed'); break; } }); ``` 3. **Clean up** Destroy player when component unmounts: ```typescript useEffect(() => { return () => { if (playerId) { YoutubePlayer.destroy(playerId); } }; }, []); ``` 4. **Respect user preferences** ```typescript // Don't autoplay unless user initiated playerVars: { autoplay: 0 } ``` 5. **Monitor playback** ```typescript setInterval(async () => { const time = await YoutubePlayer.getCurrentTime(playerId); updateProgressBar(time.result.value); }, 1000); ``` ## Platform Notes [Section titled “Platform Notes”](#platform-notes) ### iOS [Section titled “iOS”](#ios) * Works on iOS 9.0+ * Uses WKWebView with YouTube iframe API * Fullscreen supported ### Android [Section titled “Android”](#android) * Works on Android 5.0 (API 21)+ * Uses WebView with YouTube iframe API * Fullscreen supported ### Web [Section titled “Web”](#web) * Direct YouTube iframe integration * Full API support * Best performance ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) **Player not loading:** * Check video ID is valid * Verify internet connection * Ensure YouTube embedding is allowed for the video **Events not firing:** * Make sure listeners are added after initialization * Check player is ready before adding listeners **Quality issues:** * Check available quality levels first * Some videos don’t support all qualities * Quality changes may not be immediate # @capgo/capacitor-zip > Compress and extract ZIP archives effortlessly on iOS, Android, and Web platforms. Zip & Unzip Create and extract ZIP archives with ease 📦 Cross-platform Works seamlessly on iOS, Android, and Web 📱 Simple API Easy-to-use interface for compression operations 🚀 Free & Open Source Completely free with no limitations 💰 Progress Tracking Monitor compression and extraction progress ⚙️ Comprehensive Documentation Check the [Documentation](/docs/plugins/zip/getting-started/) to master the plugin in just a few minutes. # Getting Started > Learn how to install and use the Zip plugin to compress and extract files in your Capacitor app. 1. **Install the package** * npm ```sh npm i @capgo/capacitor-zip ``` * pnpm ```sh pnpm add @capgo/capacitor-zip ``` * yarn ```sh yarn add @capgo/capacitor-zip ``` * bun ```sh bun add @capgo/capacitor-zip ``` 2. **Sync with native projects** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Usage [Section titled “Usage”](#usage) Import the plugin and zip or unzip files: ```typescript import { CapacitorZip } from '@capgo/capacitor-zip'; // Zip a folder const zipFolder = async () => { await CapacitorZip.zip({ source: 'file:///path/to/folder', destination: 'file:///path/to/archive.zip' }); console.log('Folder zipped successfully!'); }; // Unzip an archive const unzipArchive = async () => { await CapacitorZip.unzip({ source: 'file:///path/to/archive.zip', destination: 'file:///path/to/output/folder' }); console.log('Archive extracted successfully!'); }; ``` ## API Reference [Section titled “API Reference”](#api-reference) ### zip(options) [Section titled “zip(options)”](#zipoptions) Creates a ZIP archive from a source folder. ```typescript interface ZipOptions { source: string; // Path to folder to compress destination: string; // Path for output ZIP file } await CapacitorZip.zip({ source: 'file:///path/to/folder', destination: 'file:///path/to/archive.zip' }); ``` ### unzip(options) [Section titled “unzip(options)”](#unzipoptions) Extracts files from a ZIP archive. ```typescript interface UnzipOptions { source: string; // Path to ZIP file destination: string; // Path to extract to } await CapacitorZip.unzip({ source: 'file:///path/to/archive.zip', destination: 'file:///path/to/output/folder' }); ``` ## Complete Example [Section titled “Complete Example”](#complete-example) ```typescript import { CapacitorZip } from '@capgo/capacitor-zip'; import { Filesystem, Directory } from '@capacitor/filesystem'; export class ZipService { async createBackup() { try { const timestamp = Date.now(); const sourceDir = `${Filesystem.Directory.Data}/userdata`; const destPath = `${Filesystem.Directory.Documents}/backup_${timestamp}.zip`; await CapacitorZip.zip({ source: sourceDir, destination: destPath }); console.log('Backup created:', destPath); return destPath; } catch (error) { console.error('Backup failed:', error); throw error; } } async restoreBackup(backupPath: string) { try { const destDir = `${Filesystem.Directory.Data}/userdata`; // Clear existing data await Filesystem.rmdir({ path: 'userdata', directory: Directory.Data, recursive: true }); // Extract backup await CapacitorZip.unzip({ source: backupPath, destination: destDir }); console.log('Backup restored successfully'); } catch (error) { console.error('Restore failed:', error); throw error; } } async compressFiles(filePaths: string[], outputPath: string) { try { // Create temporary directory const tempDir = `${Filesystem.Directory.Cache}/temp_zip_${Date.now()}`; await Filesystem.mkdir({ path: tempDir, directory: Directory.Cache, recursive: true }); // Copy files to temp directory for (const filePath of filePaths) { const fileName = filePath.split('/').pop(); await Filesystem.copy({ from: filePath, to: `${tempDir}/${fileName}` }); } // Zip the temp directory await CapacitorZip.zip({ source: tempDir, destination: outputPath }); // Clean up temp directory await Filesystem.rmdir({ path: tempDir, directory: Directory.Cache, recursive: true }); return outputPath; } catch (error) { console.error('Compression failed:', error); throw error; } } async extractSpecificFiles(zipPath: string, fileNames: string[]) { try { // Extract to temp location const tempDir = `${Filesystem.Directory.Cache}/temp_extract_${Date.now()}`; await CapacitorZip.unzip({ source: zipPath, destination: tempDir }); // Read only specific files const extractedFiles: { [key: string]: string } = {}; for (const fileName of fileNames) { const content = await Filesystem.readFile({ path: `${tempDir}/${fileName}`, directory: Directory.Cache }); extractedFiles[fileName] = content.data; } // Clean up await Filesystem.rmdir({ path: tempDir, directory: Directory.Cache, recursive: true }); return extractedFiles; } catch (error) { console.error('Extraction failed:', error); throw error; } } } ``` ## Advanced Usage [Section titled “Advanced Usage”](#advanced-usage) ### Creating Archives with Progress Tracking [Section titled “Creating Archives with Progress Tracking”](#creating-archives-with-progress-tracking) ```typescript const zipWithProgress = async (source: string, destination: string) => { console.log('Starting compression...'); try { await CapacitorZip.zip({ source, destination }); console.log('Compression complete!'); } catch (error) { console.error('Compression failed:', error); throw error; } }; ``` ### Batch Archive Operations [Section titled “Batch Archive Operations”](#batch-archive-operations) ```typescript const batchZip = async (folders: string[]) => { const results = []; for (const folder of folders) { const folderName = folder.split('/').pop(); const zipPath = `${folder}_${Date.now()}.zip`; try { await CapacitorZip.zip({ source: folder, destination: zipPath }); results.push({ folder, zipPath, success: true }); } catch (error) { results.push({ folder, error, success: false }); } } return results; }; ``` ### Secure Archive Creation [Section titled “Secure Archive Creation”](#secure-archive-creation) ```typescript const createSecureBackup = async (dataPath: string) => { // Zip the data const zipPath = `${Filesystem.Directory.Documents}/secure_backup.zip`; await CapacitorZip.zip({ source: dataPath, destination: zipPath }); // Optionally encrypt the zip file here using a crypto plugin return zipPath; }; ``` ### Archive Validation [Section titled “Archive Validation”](#archive-validation) ```typescript const validateArchive = async (zipPath: string): Promise => { try { const tempDir = `${Filesystem.Directory.Cache}/validate_${Date.now()}`; // Try to extract await CapacitorZip.unzip({ source: zipPath, destination: tempDir }); // Clean up await Filesystem.rmdir({ path: tempDir, directory: Directory.Cache, recursive: true }); return true; } catch (error) { console.error('Archive validation failed:', error); return false; } }; ``` ## Best Practices [Section titled “Best Practices”](#best-practices) 1. **Path Validation**: Always validate source and destination paths before operations 2. **Error Handling**: Wrap all zip/unzip operations in try-catch blocks 3. **Cleanup**: Remove temporary files and folders after operations 4. **Large Files**: Be cautious with large archives on mobile devices 5. **Permissions**: Ensure your app has necessary file system permissions ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) ### Common Issues [Section titled “Common Issues”](#common-issues) **Zip operation fails**: Ensure source path exists and is accessible **Unzip fails**: Check that ZIP file is valid and destination path is writable **Out of storage**: Monitor available disk space before operations **Permission denied**: Verify file system permissions in your app configuration # API Overview > Explore Capgo's public API for managing resources such as organizations, devices, channels, and bundles using RESTful HTTP methods and authentication. This is the documentation of the public API of Capgo cloud. The API allows you to programmatically manage your Capgo resources, including organizations, devices, channels, and bundles. It’s designed to be RESTful and uses standard HTTP methods. ## Authentication [Section titled “Authentication”](#authentication) All API endpoints require authentication. To authenticate your requests, add your API key in the `authorization` header. Example: ```bash curl -H "authorization: your-api-key" https://api.capgo.app/organization/ ``` [Get API key ](https://console.capgo.app/dashboard/apikeys/)Generate your API key in the Capgo dashboard ## Rate Limiting [Section titled “Rate Limiting”](#rate-limiting) The API implements rate limiting to ensure fair usage. Current limits are: * 100 requests per minute for standard accounts * 1000 requests per minute for enterprise accounts If you exceed these limits, you’ll receive a 429 (Too Many Requests) response. ## Response Format [Section titled “Response Format”](#response-format) All responses are in JSON format. Successful responses typically include either a `data` object or a `status` field. Error responses include an `error` field with a description of what went wrong. Example success response: ```json { "status": "ok", "data": { ... } } ``` Example error response: ```json { "error": "Invalid API key", "status": "KO" } ``` ## Available Endpoints [Section titled “Available Endpoints”](#available-endpoints) [Organizations ](/docs/public-api/organizations/)Create and manage organizations, update settings, and handle organization-level configurations [API Keys ](/docs/public-api/api-keys/)Generate, list, and revoke API keys for secure access to the Capgo API [Members ](/docs/public-api/members/)Manage organization members, roles, and permissions [Statistics ](/docs/public-api/statistics/)Access detailed analytics about app usage, storage, and bandwidth consumption [Channels ](/docs/public-api/channels/)Control app update channels, bundles (versions), and update policies [Devices ](/docs/public-api/devices/)Track and manage devices running your app, including bundle (version) and channel assignments [Bundles ](/docs/public-api/bundles/)Handle app bundles, including uploading, listing, and managing bundles (versions) ## Best Practices [Section titled “Best Practices”](#best-practices) 1. **Error Handling**: Always check for error responses and handle them appropriately 2. **Rate Limiting**: Implement exponential backoff when hitting rate limits 3. **Caching**: Cache responses when appropriate to reduce API calls 4. **Versioning**: Keep track of API changes through our changelog # API Keys > Comprehensive documentation for API Keys endpoint, detailing authentication, permissions, and management within Capgo API API keys are used to authenticate requests to the Capgo API. Each key can have different permissions (modes) to control access levels. Keys are organization-specific and should be managed carefully as they grant access to your Capgo resources. ## Key Modes [Section titled “Key Modes”](#key-modes) * **read**: Can only read data, no modifications allowed * **upload**: Can read, modify, and upload new bundles * **write**: Can read, modify data, and upload bundles * **all**: Full access to all operations Key modes follow a stepped/gradual schema. If you have an upload key, and then you create a write key, the write key will be able to do everything that the upload key could. Please take a look at the following diagram to better understand how API keys work. ![A diagram explaining how API key work](/capgo_apikeys_diagram.webp) ## Subkeys with Limited Rights [Section titled “Subkeys with Limited Rights”](#subkeys-with-limited-rights) You can create subkeys with limited access to specific apps or organizations. This is useful for restricting access to certain resources while still allowing operations on others. Use the `limited_to_apps` and `limited_to_orgs` parameters when creating a key to define these restrictions. ## Encrypted Keys [Section titled “Encrypted Keys”](#encrypted-keys) You can create **encrypted (hashed) API keys** for enhanced security. When creating an encrypted key: * The key is hashed using SHA-256 before being stored in the database * **The plain key is returned only once** in the creation response - save it immediately * The key cannot be retrieved or viewed again after creation * If you lose an encrypted key, you must create a new one Use the `hashed: true` parameter when creating a key to enable encryption. This is the recommended approach for production environments. ## Key Expiration [Section titled “Key Expiration”](#key-expiration) API keys can have an **expiration date** to limit the window of exposure if a key is compromised. When an expiration date is set: * The key stops working after the specified date and time * Requests using expired keys will be rejected with an error * Expired keys are automatically cleaned up after 30 days Organizations can enforce expiration policies: * **Require expiration**: All API keys must have an expiration date * **Maximum expiration period**: Limit how far in the future an expiration date can be set ## Security Best Practices [Section titled “Security Best Practices”](#security-best-practices) 1. **Principle of Least Privilege**: Always use the most restrictive mode that still allows your integration to function 2. **Use Encrypted Keys**: Create hashed keys for production to protect against database breaches 3. **Set Expiration Dates**: Use expiration dates to limit the lifetime of your keys 4. **Regular Rotation**: Rotate your API keys periodically 5. **Secure Storage**: Store API keys securely and never commit them to version control 6. **Monitoring**: Monitor API key usage and revoke any compromised keys immediately 7. **Limited Subkeys**: Use subkeys with limited rights for specific integrations to minimize risk ## Endpoints [Section titled “Endpoints”](#endpoints) ### GET [Section titled “GET”](#get) `https://api.capgo.app/apikey/` Retrieve all API keys associated with your account. #### Response Type [Section titled “Response Type”](#response-type) ```typescript interface ApiKey { created_at: string | null id: number key: string | null // null for encrypted keys mode: 'read' | 'write' | 'upload' | 'all' name: string updated_at: string | null user_id: string limited_to_apps?: string[] limited_to_orgs?: string[] expires_at?: string | null // ISO 8601 date, null means never expires } ``` > **Note**: For encrypted keys, the `key` field will be `null` in GET responses since the plain key is only shown once during creation. #### Example Request [Section titled “Example Request”](#example-request) ```bash curl -H "authorization: your-api-key" https://api.capgo.app/apikey/ ``` #### Example Response [Section titled “Example Response”](#example-response) ```json { "data": [ { "id": 1, "key": "ak_123...", "mode": "read", "name": "CI/CD Read Key", "created_at": "2024-01-01T00:00:00Z", "updated_at": "2024-01-01T00:00:00Z", "user_id": "user_123" }, { "id": 2, "key": "ak_456...", "mode": "upload", "name": "Deploy Bot", "created_at": "2024-01-02T00:00:00Z", "updated_at": "2024-01-02T00:00:00Z", "user_id": "user_123", "limited_to_apps": ["com.demo.app"] } ] } ``` ### POST [Section titled “POST”](#post) `https://api.capgo.app/apikey/` Create a new API key for a specific organization. #### Request Body [Section titled “Request Body”](#request-body) ```typescript interface ApiKeyCreate { name: string mode: 'read' | 'write' | 'upload' | 'all' limited_to_apps?: string[] limited_to_orgs?: string[] hashed?: boolean // Create an encrypted key (recommended for production) expires_at?: string // ISO 8601 date for key expiration } ``` #### Example Request (Standard Key) [Section titled “Example Request (Standard Key)”](#example-request-standard-key) ```bash curl -X POST \ -H "authorization: your-api-key" \ -H "Content-Type: application/json" \ -d '{ "name": "Limited Read Key", "mode": "read", "limited_to_apps": ["com.demo.app"] }' \ https://api.capgo.app/apikey/ ``` #### Example Request (Encrypted Key with Expiration) [Section titled “Example Request (Encrypted Key with Expiration)”](#example-request-encrypted-key-with-expiration) ```bash curl -X POST \ -H "authorization: your-api-key" \ -H "Content-Type: application/json" \ -d '{ "name": "Secure CI Key", "mode": "upload", "hashed": true, "expires_at": "2025-06-01T00:00:00Z" }' \ https://api.capgo.app/apikey/ ``` #### Example Response (Standard Key) [Section titled “Example Response (Standard Key)”](#example-response-standard-key) ```json { "apikey": { "id": 3, "key": "ak_789...", "mode": "read", "name": "Limited Read Key", "created_at": "2024-02-12T00:00:00Z", "user_id": "user_123", "limited_to_apps": ["com.demo.app"] } } ``` #### Example Response (Encrypted Key) [Section titled “Example Response (Encrypted Key)”](#example-response-encrypted-key) ```json { "apikey": { "id": 4, "key": "ak_abc123...", "mode": "upload", "name": "Secure CI Key", "created_at": "2024-02-12T00:00:00Z", "user_id": "user_123", "expires_at": "2025-06-01T00:00:00Z" } } ``` > **Important**: For encrypted keys, the `key` value in this response is the **only time** you will see the plain key. Save it immediately in a secure location. Subsequent GET requests will return `null` for the `key` field. ### DELETE [Section titled “DELETE”](#delete) `https://api.capgo.app/apikey/:id/` Delete an existing API key. Use this to revoke access immediately. #### Parameters [Section titled “Parameters”](#parameters) * `id`: The ID of the API key to delete (numeric identifier, not the key string itself) #### Example Request [Section titled “Example Request”](#example-request-1) ```bash curl -X DELETE -H "authorization: your-api-key" https://api.capgo.app/apikey/1/ ``` #### Success Response [Section titled “Success Response”](#success-response) ```json { "success": true } ``` ## Common Use Cases [Section titled “Common Use Cases”](#common-use-cases) 1. **CI/CD Integration**: Create read-only keys for CI pipelines to check deployment status 2. **Deployment Automation**: Use upload mode keys for automated deployment scripts 3. **Monitoring Tools**: Use read mode keys for external monitoring integrations 4. **Admin Access**: Use all mode keys sparingly for administrative tools 5. **Limited Access**: Create subkeys with limited rights to specific apps or organizations for third-party integrations ## Error Handling [Section titled “Error Handling”](#error-handling) Common error scenarios and their responses: ```json // Invalid mode { "error": "Invalid mode specified. Must be one of: read, write, upload, all", "status": "KO" } // Key not found { "error": "API key not found", "status": "KO" } // Permission denied { "error": "Insufficient permissions to manage API keys", "status": "KO" } // Expired API key used for authentication { "error": "API key has expired", "status": "KO" } // Invalid expiration date (in the past) { "error": "Expiration date must be in the future", "status": "KO" } // Organization requires expiration { "error": "Organization policy requires an expiration date for API keys", "status": "KO" } // Expiration exceeds organization limit { "error": "Expiration date exceeds organization maximum of 90 days", "status": "KO" } ``` # Apps > Detailed documentation for the Apps endpoint, providing comprehensive insights into managing and interacting with Capacitor applications through Capgo's platform. Apps are the foundational entities in Capgo. Each app represents a unique Capacitor application that you can manage and update through the platform. The Apps API allows you to create, retrieve, update, and delete app configurations. ## Understanding Apps [Section titled “Understanding Apps”](#understanding-apps) An app in Capgo represents your Capacitor application and includes: * **App ID**: Unique identifier for your application * **Name**: Human-readable name of your application * **Icons**: Visual identifiers for your app in the dashboard * **Configuration**: Settings that control how updates are delivered * **Ownership**: Organization and user access information * **Usage Statistics**: Metrics about installs and updates ## Best Practices [Section titled “Best Practices”](#best-practices) 1. **Naming Convention**: Use clear, identifiable names for your apps 2. **Security**: Protect your API keys and access credentials 3. **Organization**: Group related apps under the same organization 4. **Monitoring**: Regularly check app statistics and performance 5. **Backup**: Maintain configuration backups for critical apps ## Endpoints [Section titled “Endpoints”](#endpoints) ### GET [Section titled “GET”](#get) `https://api.capgo.app/app/` Retrieve information about your apps. #### Query Parameters [Section titled “Query Parameters”](#query-parameters) * `page`: Optional. Page number for pagination * `limit`: Optional. Number of results per page (default: 50) * `org_id`: Optional. Filter apps by organization ID. If not provided, returns apps from all organizations the user has access to For getting a specific app: * Use the app ID in the URL path: `https://api.capgo.app/app/:app_id` #### Response Type [Section titled “Response Type”](#response-type) Note: `last_version` refers to the last bundle (version) uploaded for the app. ```typescript interface App { app_id: string created_at: string | null default_upload_channel: string icon_url: string id: string | null last_version: string | null // last bundle (version) name name: string | null owner_org: string retention: number transfer_history: Json[] | null updated_at: string | null user_id: string | null } ``` #### Example Request [Section titled “Example Request”](#example-request) ```bash # Get all apps curl -H "authorization: your-api-key" \ "https://api.capgo.app/app/" # Get apps from a specific organization curl -H "authorization: your-api-key" \ "https://api.capgo.app/app/?org_id=046a36ac-e03c-4590-9257-bd6c9dba9ee8" # Get specific app curl -H "authorization: your-api-key" \ "https://api.capgo.app/app/com.demo.app" ``` #### Example Response [Section titled “Example Response”](#example-response) ```json { "data": [ { "app_id": "com.demo.app", "created_at": "2024-01-01T00:00:00Z", "default_upload_channel": "dev", "icon_url": "https://example.com/icon.png", "id": "550e8400-e29b-41d4-a716-446655440000", "last_version": "1.0.0", "name": "Demo App", "owner_org": "046a36ac-e03c-4590-9257-bd6c9dba9ee8", "retention": 2592000, "transfer_history": null, "updated_at": "2024-01-01T00:00:00Z", "user_id": "6aa76066-55ef-4238-ade6-0b32334a4097" } ] } ``` ### POST [Section titled “POST”](#post) `https://api.capgo.app/app/` Create a new app. #### Request Body [Section titled “Request Body”](#request-body) ```typescript interface CreateApp { app_id: string name: string icon?: string owner_org: string } ``` #### Example Request [Section titled “Example Request”](#example-request-1) ```bash # Create new app curl -X POST \ -H "authorization: your-api-key" \ -H "Content-Type: application/json" \ -d '{ "name": "My New App", "app_id": "com.demo.myapp", // this id is unique in Capgo This cannot be reused by any account. "icon": "https://example.com/icon.png", "owner_org": "046a36ac-e03c-4590-9257-bd6c9dba9ee8" }' \ https://api.capgo.app/app/ ``` #### Success Response [Section titled “Success Response”](#success-response) ```json { "app_id": "My New App", "created_at": "2024-01-01T00:00:00Z", "default_upload_channel": "dev", "icon_url": "https://example.com/icon.png", "id": "550e8400-e29b-41d4-a716-446655440000", "name": "My New App", "owner_org": "046a36ac-e03c-4590-9257-bd6c9dba9ee8", "retention": 2592000, "updated_at": "2024-01-01T00:00:00Z" } ``` ### PUT [Section titled “PUT”](#put) `https://api.capgo.app/app/:app_id` Update an existing app. The app ID is specified in the URL path. #### Request Body [Section titled “Request Body”](#request-body-1) ```typescript interface UpdateApp { name?: string icon?: string retention?: number } ``` #### Example Request [Section titled “Example Request”](#example-request-2) ```bash curl -X PUT \ -H "authorization: your-api-key" \ -H "Content-Type: application/json" \ -d '{ "name": "Updated App Name", "icon": "https://example.com/updated-icon.png", "retention": 45 }' \ https://api.capgo.app/app/com.demo.app ``` #### Success Response [Section titled “Success Response”](#success-response-1) ```json { "app_id": "com.demo.app", "created_at": "2024-01-01T00:00:00Z", "default_upload_channel": "dev", "icon_url": "https://example.com/updated-icon.png", "id": "550e8400-e29b-41d4-a716-446655440000", "name": "Updated App Name", "owner_org": "046a36ac-e03c-4590-9257-bd6c9dba9ee8", "retention": 45, "updated_at": "2024-01-01T00:00:00Z" } ``` ### DELETE [Section titled “DELETE”](#delete) `https://api.capgo.app/app/:app_id` Delete an app and all associated resources. The app ID is specified in the URL path. Use with extreme caution as this action cannot be undone. #### Example Request [Section titled “Example Request”](#example-request-3) ```bash curl -X DELETE \ -H "authorization: your-api-key" \ https://api.capgo.app/app/com.demo.app ``` #### Success Response [Section titled “Success Response”](#success-response-2) ```json { "status": "ok" } ``` ## Error Handling [Section titled “Error Handling”](#error-handling) Common error scenarios and their responses: ```json // App not found { "error": "App not found", "status": "KO" } // Duplicate custom ID { "error": "Custom ID already in use", "status": "KO" } // Invalid parameters { "error": "Invalid app configuration", "status": "KO" } // Permission denied { "error": "Insufficient permissions to manage app", "status": "KO" } // Organization access denied { "status": "You do not have access to this organization" } ``` ## Common Use Cases [Section titled “Common Use Cases”](#common-use-cases) 1. **Create New App** ```typescript // Set up a new app { "name": "Production App", "owner_org": "046a36ac-e03c-4590-9257-bd6c9dba9ee8" } ``` 2. **Update App Configuration** ```typescript // Change app name and icon { "name": "Rebranded App Name", "icon": "https://example.com/new-icon.png" } ``` 3. **Set Retention Policy** ```typescript // Configure automatic bundle cleanup { "retention": 30 // Keep bundles for 30 days } ``` 4. **Get Apps by Organization** ```bash # List all apps in a specific organization curl -H "authorization: your-api-key" \ "https://api.capgo.app/app/?org_id=046a36ac-e03c-4590-9257-bd6c9dba9ee8" ``` ## Resource Management [Section titled “Resource Management”](#resource-management) 1. **Storage Optimization**: Monitor storage usage and set appropriate retention policies 2. **Organization**: Group related apps under a single organization 3. **Access Control**: Manage which team members can modify app settings 4. **Backup Strategy**: Back up critical app configurations and settings # Bundles > Detailed guide for managing Capgo Bundles, covering listing, deleting, bundle (version) management, and storage optimization for app update packages. Bundles are the core update packages in Capgo. Each bundle contains the web assets (HTML, CSS, JS) that make up your app’s content. The Bundles API allows you to manage these update packages, including listing and deleting them. ## Understanding Bundles [Section titled “Understanding Bundles”](#understanding-bundles) A bundle represents a specific bundle (version) of your app’s web content and includes: * **Bundle (version)**: Semantic version number for the bundle * **Checksum**: Unique hash to verify bundle integrity * **Storage Info**: Details about where and how the bundle is stored * **Native Requirements**: Minimum native app version requirements * **Metadata**: Creation time, ownership, and other tracking information ## Manual Bundle Creation (Without CLI) [Section titled “Manual Bundle Creation (Without CLI)”](#manual-bundle-creation-without-cli) Here’s how to create and upload bundles manually without using the Capgo CLI: ### Step 1: Build Your App [Section titled “Step 1: Build Your App”](#step-1-build-your-app) First, build your app’s web assets: ```bash npm run build ``` ### Step 2: Create Bundle Zip Using Same Packages as Capgo CLI [Section titled “Step 2: Create Bundle Zip Using Same Packages as Capgo CLI”](#step-2-create-bundle-zip-using-same-packages-as-capgo-cli) **Important**: Use the exact same JavaScript packages that Capgo CLI uses internally to ensure compatibility. #### Install Required Packages [Section titled “Install Required Packages”](#install-required-packages) ```bash npm install adm-zip @tomasklaen/checksum ``` #### Create Zip Bundle with JavaScript (Same as Capgo CLI) [Section titled “Create Zip Bundle with JavaScript (Same as Capgo CLI)”](#create-zip-bundle-with-javascript-same-as-capgo-cli) Note: In the examples below, `version` refers to the bundle (version) name used by the API. ```javascript const fs = require('node:fs'); const path = require('node:path'); const os = require('node:os'); const AdmZip = require('adm-zip'); const { checksum: getChecksum } = require('@tomasklaen/checksum'); // Exact same implementation as Capgo CLI function zipFileUnix(filePath) { const zip = new AdmZip(); zip.addLocalFolder(filePath); return zip.toBuffer(); } async function zipFileWindows(filePath) { console.log('Zipping file windows mode'); const zip = new AdmZip(); const addToZip = (folderPath, zipPath) => { const items = fs.readdirSync(folderPath); for (const item of items) { const itemPath = path.join(folderPath, item); const stats = fs.statSync(itemPath); if (stats.isFile()) { const fileContent = fs.readFileSync(itemPath); zip.addFile(path.join(zipPath, item).split(path.sep).join('/'), fileContent); } else if (stats.isDirectory()) { addToZip(itemPath, path.join(zipPath, item)); } } }; addToZip(filePath, ''); return zip.toBuffer(); } // Main zipFile function (exact same logic as CLI) async function zipFile(filePath) { if (os.platform() === 'win32') { return zipFileWindows(filePath); } else { return zipFileUnix(filePath); } } async function createBundle(inputPath, outputPath, version) { // Create zip using exact same method as Capgo CLI const zipped = await zipFile(inputPath); // Write to file fs.writeFileSync(outputPath, zipped); // Calculate checksum using exact same package as CLI const checksum = await getChecksum(zipped, 'sha256'); return { filename: path.basename(outputPath), version: version, size: zipped.length, checksum: checksum }; } // Usage async function main() { try { const result = await createBundle('./dist', './my-app-1.2.3.zip', '1.2.3'); console.log('Bundle info:', JSON.stringify(result, null, 2)); } catch (error) { console.error('Error creating bundle:', error); } } main(); ``` ### Step 3: Calculate SHA256 Checksum Using Same Package as CLI [Section titled “Step 3: Calculate SHA256 Checksum Using Same Package as CLI”](#step-3-calculate-sha256-checksum-using-same-package-as-cli) ```javascript const fs = require('node:fs'); const { checksum: getChecksum } = require('@tomasklaen/checksum'); async function calculateChecksum(filePath) { const fileBuffer = fs.readFileSync(filePath); // Use exact same package and method as Capgo CLI const checksum = await getChecksum(fileBuffer, 'sha256'); return checksum; } // Usage async function main() { const checksum = await calculateChecksum('./my-app-1.2.3.zip'); console.log('Checksum:', checksum); } main(); ``` ### Step 4: Upload Bundle to Your Storage [Section titled “Step 4: Upload Bundle to Your Storage”](#step-4-upload-bundle-to-your-storage) Upload your zip file to any web-accessible storage: ```bash # Example: Upload to your server via scp scp my-app-1.2.3.zip user@your-server.com:/var/www/bundles/ # Example: Upload to S3 using AWS CLI aws s3 cp my-app-1.2.3.zip s3://your-bucket/bundles/ # Example: Upload via curl to a custom endpoint curl -X POST https://your-storage-api.com/upload \ -H "Authorization: Bearer YOUR_TOKEN" \ -F "file=@my-app-1.2.3.zip" ``` **Important**: Your bundle must be **publicly accessible** via HTTPS URL (no authentication required). Capgo’s servers need to download the bundle from this URL. Examples of valid public URLs: * `https://your-storage.com/bundles/my-app-1.2.3.zip` * `https://github.com/username/repo/releases/download/v1.2.3/bundle.zip` * `https://cdn.jsdelivr.net/gh/username/repo@v1.2.3/dist.zip` ### Step 5: Register Bundle with Capgo API [Section titled “Step 5: Register Bundle with Capgo API”](#step-5-register-bundle-with-capgo-api) Register the external bundle with Capgo using direct API calls: ```javascript async function registerWithCapgo(appId, version, bundleUrl, checksum, apiKey) { const fetch = require('node-fetch'); // Create bundle (version) const response = await fetch('https://api.capgo.app/bundle/', { method: 'POST', headers: { 'Content-Type': 'application/json', 'authorization': apiKey }, body: JSON.stringify({ app_id: appId, version: version, external_url: bundleUrl, checksum: checksum }) }); if (!response.ok) { throw new Error(`Failed to create bundle: ${response.statusText}`); } const data = await response.json(); console.log('Bundle created:', data); return data; } ``` #### API Parameters [Section titled “API Parameters”](#api-parameters) | Parameter | Description | Required | | -------------- | ----------------------------------------------------------------------------------- | -------- | | `app_id` | Your app identifier | Yes | | `version` | Bundle (version) semantic version (e.g., “1.2.3”) | Yes | | `external_url` | **Publicly accessible** HTTPS URL where bundle can be downloaded (no auth required) | Yes | | `checksum` | SHA256 checksum of the zip file | Yes | ## Bundle Structure Requirements [Section titled “Bundle Structure Requirements”](#bundle-structure-requirements) Your bundle zip must follow these requirements: 1. **Root Index File**: Must have `index.html` at the root level 2. **Capacitor Integration**: Must call `notifyAppReady()` in your app code 3. **Asset Paths**: Use relative paths for all assets ### Valid Bundle Structure [Section titled “Valid Bundle Structure”](#valid-bundle-structure) ```plaintext bundle.zip ├── index.html ├── assets/ │ ├── app.js │ └── styles.css └── images/ ``` ## Complete Manual Workflow Example [Section titled “Complete Manual Workflow Example”](#complete-manual-workflow-example) Simple Node.js script to zip, checksum, and upload to Capgo: ```javascript const fs = require('node:fs'); const os = require('node:os'); const AdmZip = require('adm-zip'); const { checksum: getChecksum } = require('@tomasklaen/checksum'); const fetch = require('node-fetch'); async function deployToCapgo() { const APP_ID = 'com.example.app'; const VERSION = '1.2.3'; const BUNDLE_URL = 'https://your-storage.com/bundles/app-1.2.3.zip'; const API_KEY = process.env.CAPGO_API_KEY; // 1. Create zip (same as Capgo CLI) const zip = new AdmZip(); zip.addLocalFolder('./dist'); const zipped = zip.toBuffer(); // 2. Calculate checksum (same as Capgo CLI) const checksum = await getChecksum(zipped, 'sha256'); console.log('Checksum:', checksum); // 3. Upload to your storage (replace with your upload logic) // fs.writeFileSync('./bundle.zip', zipped); // ... upload bundle.zip to your storage ... // 4. Register with Capgo API const response = await fetch('https://api.capgo.app/bundle/', { method: 'POST', headers: { 'Content-Type': 'application/json', 'authorization': API_KEY }, body: JSON.stringify({ app_id: APP_ID, version: VERSION, external_url: BUNDLE_URL, checksum: checksum }) }); if (!response.ok) { throw new Error(`Failed: ${response.statusText}`); } console.log('Bundle registered with Capgo!'); } deployToCapgo().catch(console.error); ``` Install dependencies: ```bash npm install adm-zip @tomasklaen/checksum node-fetch ``` ## Checksum Verification [Section titled “Checksum Verification”](#checksum-verification) ### JavaScript Checksum Calculation (Same as Capgo CLI) [Section titled “JavaScript Checksum Calculation (Same as Capgo CLI)”](#javascript-checksum-calculation-same-as-capgo-cli) Use the exact same package and method that Capgo CLI uses internally: ```javascript const fs = require('node:fs'); const { checksum: getChecksum } = require('@tomasklaen/checksum'); async function calculateChecksum(filePath) { const fileBuffer = fs.readFileSync(filePath); // Use exact same package and method as Capgo CLI const checksum = await getChecksum(fileBuffer, 'sha256'); return checksum; } // Verify checksum matches async function verifyChecksum(filePath, expectedChecksum) { const actualChecksum = await calculateChecksum(filePath); const isValid = actualChecksum === expectedChecksum; console.log(`File: ${filePath}`); console.log(`Expected: ${expectedChecksum}`); console.log(`Actual: ${actualChecksum}`); console.log(`Valid: ${isValid}`); return isValid; } // Usage async function main() { const bundleChecksum = await calculateChecksum('./my-app-1.2.3.zip'); console.log('SHA256 Checksum:', bundleChecksum); } main(); ``` ### Checksum Importance [Section titled “Checksum Importance”](#checksum-importance) * **Bundle Integrity**: Ensures the bundle hasn’t been corrupted during transfer * **API Verification**: Capgo verifies checksums before accepting bundles * **Plugin Verification**: The mobile plugin verifies checksums before applying updates ## Best Practices [Section titled “Best Practices”](#best-practices) 1. **Bundle (version) Management**: Use semantic versioning consistently 2. **Storage Optimization**: Remove unused bundles periodically 3. **Bundle (version) Compatibility**: Set appropriate minimum native version requirements 4. **Backup Strategy**: Maintain backups of critical bundles (versions) ## Endpoints [Section titled “Endpoints”](#endpoints) ### GET [Section titled “GET”](#get) `https://api.capgo.app/bundle/` Retrieve bundle information. Returns 50 bundles per page. #### Query Parameters [Section titled “Query Parameters”](#query-parameters) * `app_id`: Required. The ID of your app * `page`: Optional. Page number for pagination #### Response Type [Section titled “Response Type”](#response-type) ```typescript interface Bundle { app_id: string bucket_id: string | null checksum: string | null created_at: string | null deleted: boolean external_url: string | null id: number minUpdateVersion: string | null name: string native_packages: Json[] | null owner_org: string r2_path: string | null session_key: string | null storage_provider: string updated_at: string | null user_id: string | null } ``` #### Example Request [Section titled “Example Request”](#example-request) ```bash # Get all bundles curl -H "authorization: your-api-key" \ "https://api.capgo.app/bundle/?app_id=app_123" # Get next page curl -H "authorization: your-api-key" \ "https://api.capgo.app/bundle/?app_id=app_123&page=1" ``` #### Example Response [Section titled “Example Response”](#example-response) ```json { "data": [ { "id": 1, "app_id": "app_123", "name": "1.0.0", "checksum": "abc123...", "minUpdateVersion": "1.0.0", "storage_provider": "r2", "created_at": "2024-01-01T00:00:00Z", "updated_at": "2024-01-01T00:00:00Z", "deleted": false, "owner_org": "org_123", "user_id": "user_123" } ] } ``` ### DELETE [Section titled “DELETE”](#delete) `https://api.capgo.app/bundle/` Delete one or all bundles for an app. Use with caution as this action cannot be undone. #### Query Parameters [Section titled “Query Parameters”](#query-parameters-1) For deleting a specific bundle: ```typescript interface BundleDelete { app_id: string version: string } ``` For deleting all bundles: ```typescript interface BundleDeleteAll { app_id: string } ``` #### Example Requests [Section titled “Example Requests”](#example-requests) ```bash # Delete specific bundle curl -X DELETE \ -H "authorization: your-api-key" \ -H "Content-Type: application/json" \ -d '{ "app_id": "app_123", "version": "1.0.0" }' \ https://api.capgo.app/bundle/ # Delete all bundles curl -X DELETE \ -H "authorization: your-api-key" \ -H "Content-Type: application/json" \ -d '{ "app_id": "app_123" }' \ https://api.capgo.app/bundle/ ``` #### Success Response [Section titled “Success Response”](#success-response) ```json { "status": "ok" } ``` ### POST [Section titled “POST”](#post) `https://api.capgo.app/bundle/` Create a new bundle with external URL. #### Request Body [Section titled “Request Body”](#request-body) ```typescript interface CreateBundleBody { app_id: string version: string external_url: string // Must be publicly accessible HTTPS URL checksum: string } ``` #### Example Request [Section titled “Example Request”](#example-request-1) ```bash curl -X POST \ -H "authorization: your-api-key" \ -H "Content-Type: application/json" \ -d '{ "app_id": "com.example.app", "version": "1.2.3", "external_url": "https://your-storage.com/bundles/app-1.2.3.zip", "checksum": "a1b2c3d4e5f6789abcdef123456789abcdef123456789abcdef123456789abcd" }' \ https://api.capgo.app/bundle/ ``` #### Success Response [Section titled “Success Response”](#success-response-1) ```json { "status": "ok" } ``` ### POST (Metadata) [Section titled “POST (Metadata)”](#post-metadata) `https://api.capgo.app/bundle/metadata` Update bundle metadata such as link and comment information. #### Request Body [Section titled “Request Body”](#request-body-1) ```typescript interface UpdateMetadataBody { app_id: string version_id: number // bundle (version) id link?: string comment?: string } ``` #### Example Request [Section titled “Example Request”](#example-request-2) ```bash curl -X POST \ -H "authorization: your-api-key" \ -H "Content-Type: application/json" \ -d '{ "app_id": "app_123", "version_id": 456, "link": "https://github.com/myorg/myapp/releases/tag/v1.0.0", "comment": "Fixed critical bug in authentication" }' \ https://api.capgo.app/bundle/metadata ``` #### Success Response [Section titled “Success Response”](#success-response-2) ```json { "status": "success" } ``` ### PUT [Section titled “PUT”](#put) `https://api.capgo.app/bundle/` Set a bundle to a specific channel. This links a bundle (version) to a channel for distribution. #### Request Body [Section titled “Request Body”](#request-body-2) ```typescript interface SetChannelBody { app_id: string version_id: number // bundle (version) id channel_id: number } ``` #### Example Request [Section titled “Example Request”](#example-request-3) ```bash curl -X PUT \ -H "authorization: your-api-key" \ -H "Content-Type: application/json" \ -d '{ "app_id": "app_123", "version_id": 456, "channel_id": 789 }' \ https://api.capgo.app/bundle/ ``` #### Success Response [Section titled “Success Response”](#success-response-3) ```json { "status": "success", "message": "Bundle 1.0.0 set to channel production" } ``` ## Error Handling [Section titled “Error Handling”](#error-handling) Common error scenarios and their responses: ```json // Bundle not found { "error": "Bundle not found", "status": "KO" } // Invalid bundle (version) format { "error": "Invalid version format", "status": "KO" } // Storage error { "error": "Failed to delete bundle from storage", "status": "KO" } // Permission denied { "error": "Insufficient permissions to manage bundles", "status": "KO" } ``` ## Common Use Cases [Section titled “Common Use Cases”](#common-use-cases) 1. **Cleanup Old Bundles (Versions)** ```typescript // Delete outdated beta bundles (versions) { "app_id": "app_123", "version": "1.0.0-beta.1" } ``` 2. **App Reset** ```typescript // Remove all bundles to start fresh { "app_id": "app_123" } ``` ## Storage Considerations [Section titled “Storage Considerations”](#storage-considerations) 1. **Retention Policy**: Define how long to keep old bundles 2. **Size Management**: Monitor bundle sizes and storage usage 3. **Backup Strategy**: Consider backing up critical bundles (versions) 4. **Cost Optimization**: Remove unnecessary bundles to optimize storage costs # Channels > Guide to managing app update channels in Capgo, covering bundle (version) control, platform targeting, and update policies. Channels are the core mechanism for managing app updates in Capgo. They allow you to control how and when your users receive updates, enabling features like A/B testing, staged rollouts, and platform-specific updates. ## Understanding Channels [Section titled “Understanding Channels”](#understanding-channels) A channel represents a distribution track for your app updates. Each channel can be configured with specific rules and constraints: * **Bundle (version) Control**: Specify which bundle (version) users receive * **Platform Targeting**: Target specific platforms (iOS/Android/Electron) * **Update Policies**: Control how updates are delivered * **Device Restrictions**: Manage which devices can access updates ## Channel Configuration Options [Section titled “Channel Configuration Options”](#channel-configuration-options) * **public**: Set as default channel for new devices * **disableAutoUpdateUnderNative**: Prevent updates when the device’s native app version is newer than the update bundle (version) available in the channel (e.g., device is on native app version 1.2.3, but channel has bundle (version) 1.2.2) * **disableAutoUpdate**: Control update behavior (“major”, “minor”, “version\_number”, “none”) * **ios/android/electron**: Enable/disable for specific platforms * **allow\_device\_self\_set**: Let devices choose their channel * **allow\_emulator**: Allow updates on emulator devices * **allow\_dev**: Allow updates on development builds ## Best Practices [Section titled “Best Practices”](#best-practices) 1. **Testing Channel**: Maintain a testing channel for internal validation 2. **Staged Rollout**: Use multiple channels for gradual update deployment 3. **Platform Separation**: Create separate channels for iOS, Android, and Electron when needed 4. **Bundle (version) Control**: Use semantic versioning for clear update paths ## Endpoints [Section titled “Endpoints”](#endpoints) ### POST [Section titled “POST”](#post) `https://api.capgo.app/channel/` Create or update a channel configuration. #### Request Body [Section titled “Request Body”](#request-body) ```typescript type disable_update = "major" | "minor" | "version_number" | "none" interface ChannelSet { app_id: string channel: string version?: string // bundle (version) name public?: boolean disableAutoUpdateUnderNative?: boolean disableAutoUpdate?: disable_update ios?: boolean android?: boolean electron?: boolean allow_device_self_set?: boolean allow_emulator?: boolean allow_dev?: boolean } ``` #### Example Request [Section titled “Example Request”](#example-request) ```bash curl -X POST \ -H "authorization: your-api-key" \ -H "Content-Type: application/json" \ -d '{ "app_id": "app_123", "channel": "beta", "version": "1.2.0", "public": false, "disableAutoUpdate": "minor", "ios": true, "android": true, "electron": true, "allow_emulator": true }' \ https://api.capgo.app/channel/ ``` #### Success Response [Section titled “Success Response”](#success-response) ```json { "status": "ok" } ``` ### GET [Section titled “GET”](#get) `https://api.capgo.app/channel/` Retrieve channel information. Returns 50 channels per page. #### Query Parameters [Section titled “Query Parameters”](#query-parameters) * `app_id`: Required. The ID of your app * `page`: Optional. Page number for pagination * `channel`: Optional. Specific channel name to retrieve #### Example Requests [Section titled “Example Requests”](#example-requests) ```bash # Get all channels curl -H "authorization: your-api-key" \ "https://api.capgo.app/channel/?app_id=app_123" # Get specific channel curl -H "authorization: your-api-key" \ "https://api.capgo.app/channel/?app_id=app_123&channel=beta" # Get next page curl -H "authorization: your-api-key" \ "https://api.capgo.app/channel/?app_id=app_123&page=1" ``` #### Response Type [Section titled “Response Type”](#response-type) ```typescript interface Channel { id: number; created_at: string; name: string; app_id: string; version: { // bundle (version) assigned to the channel id: number, name: string }; created_by: string; updated_at: string; public: boolean; disableAutoUpdateUnderNative: boolean; disableAutoUpdate: boolean; allow_emulator: boolean; allow_dev: boolean; } ``` In the response below, `version` refers to the bundle (version) assigned to the channel. #### Example Response [Section titled “Example Response”](#example-response) ```json { "data": [ { "id": 1, "name": "production", "app_id": "app_123", "version": { "id": 1, "name": "1.0.0" }, "created_at": "2024-01-01T00:00:00Z", "updated_at": "2024-01-01T00:00:00Z", "created_by": "user_123", "public": true, "disableAutoUpdateUnderNative": false, "disableAutoUpdate": false, "allow_emulator": false, "allow_dev": false } ] } ``` ### DELETE [Section titled “DELETE”](#delete) `https://api.capgo.app/channel/` Delete a channel. Note that this will affect all devices using this channel. #### Query Parameters [Section titled “Query Parameters”](#query-parameters-1) ```typescript interface Channel { channel: string app_id: string } ``` #### Example Request [Section titled “Example Request”](#example-request-1) ```bash curl -X DELETE \ -H "authorization: your-api-key" \ -H "Content-Type: application/json" \ -d '{ "app_id": "app_123", "channel": "beta" }' \ https://api.capgo.app/channel/ ``` #### Success Response [Section titled “Success Response”](#success-response-1) ```json { "status": "ok" } ``` ## Error Handling [Section titled “Error Handling”](#error-handling) Common error scenarios and their responses: ```json // Channel not found { "error": "Channel not found", "status": "KO" } // Invalid bundle (version) format { "error": "Invalid version format. Use semantic versioning", "status": "KO" } // Invalid update policy { "error": "Invalid disableAutoUpdate value", "status": "KO" } // Permission denied { "error": "Insufficient permissions to manage channels", "status": "KO" } ``` ## Common Use Cases [Section titled “Common Use Cases”](#common-use-cases) 1. **Beta Testing** ```typescript { "app_id": "app_123", "channel": "beta", "version": "1.2.0-beta", "public": false, "allow_emulator": true, "allow_dev": true } ``` 2. **Production Rollout** ```typescript { "app_id": "app_123", "channel": "production", "version": "1.2.0", "public": true, "disableAutoUpdate": "minor" } ``` 3. **Platform-Specific Updates** ```typescript { "app_id": "app_123", "channel": "ios-hotfix", "version": "1.2.1", "ios": true, "android": false } ``` # Devices > Docs for managing devices via Capgo API, covering tracking, bundle (version) assignment, and channel management for app installations. Devices represent individual installations of your app that are managed by Capgo. The Devices API allows you to track and manage devices, including their bundles (versions), channels, and update status. Data Retention Device data is retained for **90 days** from the last device activity. Devices that haven’t connected to Capgo within this period will be automatically removed from the system. Active devices are continuously tracked as they check for updates. ## Understanding Devices [Section titled “Understanding Devices”](#understanding-devices) Each device has unique characteristics and states: * **Platform**: iOS, Android, or Electron * **Bundle (version)**: Current bundle (version) and native build version * **Environment**: Production or development, emulator or physical device * **Channel**: Current update channel assignment * **Custom ID**: Optional identifier for your own tracking purposes ## Best Practices [Section titled “Best Practices”](#best-practices) 1. **Bundle (version) Tracking**: Monitor device bundle (version) adoption to ensure update uptake 2. **Channel Management**: Assign devices to appropriate channels based on testing needs 3. **Environment Awareness**: Handle different environments (prod/dev/emulator) appropriately 4. **Custom Identification**: Use custom IDs to integrate with your existing systems ## Endpoints [Section titled “Endpoints”](#endpoints) ### POST [Section titled “POST”](#post) `https://api.capgo.app/device/` Link a device to a specific bundle (version) or channel. #### Request Body [Section titled “Request Body”](#request-body) ```typescript interface DeviceLink { app_id: string device_id: string version_id?: string // bundle (version) name channel?: string // channel name } ``` #### Example Request [Section titled “Example Request”](#example-request) ```bash curl -X POST \ -H "authorization: your-api-key" \ -H "Content-Type: application/json" \ -d '{ "app_id": "app_123", "device_id": "device_456", "channel": "beta" }' \ https://api.capgo.app/device/ ``` #### Success Response [Section titled “Success Response”](#success-response) ```json { "status": "ok" } ``` ### GET [Section titled “GET”](#get) `https://api.capgo.app/device/` Retrieve device information. Uses cursor-based pagination for efficient retrieval of large device lists. #### Query Parameters [Section titled “Query Parameters”](#query-parameters) * `app_id`: Required. The ID of your app * `device_id`: Optional. Specific device ID to retrieve a single device * `cursor`: Optional. Cursor from previous response for pagination * `limit`: Optional. Number of devices per page (default: 50) #### Example Requests [Section titled “Example Requests”](#example-requests) ```bash # Get all devices (first page) curl -H "authorization: your-api-key" \ "https://api.capgo.app/device/?app_id=app_123" # Get specific device curl -H "authorization: your-api-key" \ "https://api.capgo.app/device/?app_id=app_123&device_id=device_456" # Get next page using cursor curl -H "authorization: your-api-key" \ "https://api.capgo.app/device/?app_id=app_123&cursor=2024-01-01T00:00:00Z|device_456" ``` #### Response Type (List) [Section titled “Response Type (List)”](#response-type-list) When requesting multiple devices (no `device_id` parameter): ```typescript interface DeviceListResponse { data: Device[]; nextCursor?: string; // Pass this as 'cursor' param to get next page hasMore: boolean; // true if more pages available } interface Device { updated_at: string; device_id: string; custom_id: string; version?: number; // bundle (version) id version_name: string | null; // bundle (version) name channel?: string; app_id: string; platform: "ios" | "android" | "electron"; plugin_version: string; os_version: string; version_build: string; is_prod: boolean; is_emulator: boolean; key_id: string | null; // First 4 chars of encryption key (e.g., "MIIB") } ``` #### Response Type (Single Device) [Section titled “Response Type (Single Device)”](#response-type-single-device) When requesting a specific device with `device_id` parameter, returns the device object directly: ```typescript interface Device { updated_at: string; device_id: string; custom_id: string; version?: number; // bundle (version) id version_name: string | null; // bundle (version) name channel?: string; app_id: string; platform: "ios" | "android" | "electron"; plugin_version: string; os_version: string; version_build: string; is_prod: boolean; is_emulator: boolean; key_id: string | null; // First 4 chars of encryption key (e.g., "MIIB") } ``` #### Example Response (List) [Section titled “Example Response (List)”](#example-response-list) ```json { "data": [ { "device_id": "device_456", "custom_id": "test-device-1", "version": 1, "version_name": "1.0.0", "app_id": "app_123", "platform": "ios", "plugin_version": "5.0.0", "os_version": "17.0", "version_build": "1", "is_prod": true, "is_emulator": false, "updated_at": "2024-01-01T00:00:00Z" } ], "nextCursor": "2024-01-01T00:00:00Z|device_456", "hasMore": true } ``` #### Example Response (Single Device) [Section titled “Example Response (Single Device)”](#example-response-single-device) ```json { "device_id": "device_456", "custom_id": "test-device-1", "version": 1, "version_name": "1.0.0", "app_id": "app_123", "platform": "ios", "plugin_version": "5.0.0", "os_version": "17.0", "version_build": "1", "is_prod": true, "is_emulator": false, "updated_at": "2024-01-01T00:00:00Z", "channel": "production" } ``` ### DELETE [Section titled “DELETE”](#delete) `https://api.capgo.app/device/` Unlink a device from its channel override. This resets the device to use its default channel. Delete device Device data is retained for **90 days** from the last device activity. Devices that haven’t connected to Capgo within this period will be automatically removed from the system. Active devices are continuously tracked as they check for updates. You can’t delete a device with this endpoint, it only affect the channel override. #### Query Parameters [Section titled “Query Parameters”](#query-parameters-1) ```typescript interface Device { device_id: string app_id: string } ``` #### Example Request [Section titled “Example Request”](#example-request-1) ```bash curl -X DELETE \ -H "authorization: your-api-key" \ -H "Content-Type: application/json" \ -d '{ "app_id": "app_123", "device_id": "device_456" }' \ https://api.capgo.app/device/ ``` #### Success Response [Section titled “Success Response”](#success-response-1) ```json { "status": "ok" } ``` ## Error Handling [Section titled “Error Handling”](#error-handling) Common error scenarios and their responses: ```json // Device not found { "error": "Device not found", "status": "KO" } // Invalid bundle (version) { "error": "Version not found", "status": "KO" } // Invalid channel { "error": "Channel not found", "status": "KO" } // Permission denied { "error": "Insufficient permissions to manage devices", "status": "KO" } ``` ## Common Use Cases [Section titled “Common Use Cases”](#common-use-cases) 1. **Beta Device Registration** ```typescript { "app_id": "app_123", "device_id": "device_456", "channel": "beta" } ``` 2. **Version Override** ```typescript { "app_id": "app_123", "device_id": "device_456", "version_id": "1.1.0" } ``` 3. **Reset to Default Channel** ```typescript // Use DELETE endpoint to remove overrides ``` ## Tips for Device Management [Section titled “Tips for Device Management”](#tips-for-device-management) 1. **Monitoring**: Regularly check device status and bundle (version) distribution 2. **Testing**: Use custom IDs to identify test devices easily 3. **Troubleshooting**: Track device updates and channel assignments 4. **Native Version Control**: Monitor native app versions to ensure compatibility # Members > Comprehensive guide to managing organization members in Capgo, covering roles, permissions, and best practices for security and collaboration. Organization members are users who have access to your Capgo organization. Each member has a specific role that determines their permissions within the organization. Managing members effectively is crucial for maintaining security and collaboration in your team. ## Member Roles [Section titled “Member Roles”](#member-roles) ### Regular Roles [Section titled “Regular Roles”](#regular-roles) * **read**: Can view resources but cannot make changes * **upload**: Can upload new bundles and view resources * **write**: Can modify resources and upload bundles * **admin**: Can manage organization settings and members * **super\_admin**: Has full control over the organization ### Invite Roles [Section titled “Invite Roles”](#invite-roles) * **invite\_read**: Pending invitation for read access * **invite\_upload**: Pending invitation for upload access * **invite\_write**: Pending invitation for write access * **invite\_admin**: Pending invitation for admin access * **invite\_super\_admin**: Pending invitation for super admin access ## Best Practices [Section titled “Best Practices”](#best-practices) 1. **Role Assignment**: Follow the principle of least privilege when assigning roles 2. **Regular Audits**: Periodically review member access and remove unused accounts 3. **Onboarding**: Have a clear process for adding new members and assigning roles 4. **Offboarding**: Promptly remove access for members who leave the organization ## Endpoints [Section titled “Endpoints”](#endpoints) ### POST [Section titled “POST”](#post) `https://api.capgo.app/organization/members/` Add a new member to an organization or update an existing member’s role. Note that you can only invite users who already have a Capgo account - the email must correspond to an existing Capgo user. #### Request Body [Section titled “Request Body”](#request-body) ```typescript interface MemberCreate { orgId: string email: string role: "read" | "upload" | "write" | "admin" | "super_admin" } ``` #### Example Request [Section titled “Example Request”](#example-request) ```bash curl -X POST \ -H "authorization: your-api-key" \ -H "Content-Type: application/json" \ -d '{ "orgId": "org_123", "email": "newmember@example.com", "role": "write" }' \ https://api.capgo.app/organization/members/ ``` #### Success Response [Section titled “Success Response”](#success-response) ```json { "status": "OK", "data": { "uid": "user_789", "email": "newmember@example.com", "role": "invite_write", "image_url": null } } ``` Notes: * When adding a new member, they will receive an invitation email. Their role will be prefixed with “invite\_” until they accept the invitation. * The user must already have a Capgo account before they can be invited. If they don’t have an account, they should first create one at ### GET [Section titled “GET”](#get) `https://api.capgo.app/organization/members/` Retrieve all members of an organization. #### Query Parameters [Section titled “Query Parameters”](#query-parameters) ```typescript interface MemberQuery { orgId: string } ``` #### Response Type [Section titled “Response Type”](#response-type) ```typescript interface Member { uid: string; email: string; image_url: string; role: "invite_read" | "invite_upload" | "invite_write" | "invite_admin" | "invite_super_admin" | "read" | "upload" | "write" | "admin" | "super_admin"; } ``` #### Example Request [Section titled “Example Request”](#example-request-1) ```bash curl -H "authorization: your-api-key" \ "https://api.capgo.app/organization/members/?orgId=org_123" ``` #### Example Response [Section titled “Example Response”](#example-response) ```json { "data": [ { "uid": "user_123", "email": "john@example.com", "image_url": "https://example.com/avatar.png", "role": "admin" }, { "uid": "user_456", "email": "jane@example.com", "image_url": "https://example.com/avatar2.png", "role": "write" }, { "uid": "user_789", "email": "bob@example.com", "image_url": null, "role": "invite_read" } ] } ``` ### DELETE [Section titled “DELETE”](#delete) `https://api.capgo.app/organization/members/` Remove a member from an organization. This will immediately revoke their access. #### Request Body [Section titled “Request Body”](#request-body-1) ```typescript interface MemberDelete { orgId: string email: string } ``` #### Example Request [Section titled “Example Request”](#example-request-2) ```bash curl -X DELETE \ -H "authorization: your-api-key" \ -H "Content-Type: application/json" \ -d '{ "orgId": "org_123", "email": "user@example.com" }' \ https://api.capgo.app/organization/members/ ``` #### Success Response [Section titled “Success Response”](#success-response-1) ```json { "status": "OK" } ``` ## Error Handling [Section titled “Error Handling”](#error-handling) Common error scenarios and their responses: ```json // Member not found { "error": "Member not found", "status": "KO" } // Invalid role { "error": "Invalid role specified", "status": "KO" } // Permission denied { "error": "Insufficient permissions to manage members", "status": "KO" } // Cannot remove last admin { "error": "Cannot remove the last admin from the organization", "status": "KO" } // Invalid email { "error": "Invalid email format", "status": "KO" } // Member already exists { "error": "Member already exists in organization", "status": "KO" } ``` ## Common Use Cases [Section titled “Common Use Cases”](#common-use-cases) 1. **Team Expansion**: Adding new team members with appropriate roles 2. **Access Control**: Managing member permissions as responsibilities change 3. **Security Audit**: Reviewing member list and roles periodically 4. **Team Restructuring**: Updating roles during organizational changes # Organizations > Guide to managing organizations in Capgo, covering creation, settings, updates, and retrieval of organization details. Organizations are the top-level entities in Capgo. They allow you to group apps, team members, and resources under a single umbrella. Each organization can have multiple members with different roles and permissions. ## Common Use Cases [Section titled “Common Use Cases”](#common-use-cases) * Creating a new organization for your company * Managing organization settings * Updating organization information * Retrieving organization details ## Endpoints [Section titled “Endpoints”](#endpoints) ### GET [Section titled “GET”](#get) `https://api.capgo.app/organization/` Retrieve organization information. If `orgId` is provided in the parameters, returns a single organization. Otherwise, returns all accessible organizations. #### Query Parameters [Section titled “Query Parameters”](#query-parameters) * `orgId` (optional): The ID of the specific organization to retrieve #### Response Type [Section titled “Response Type”](#response-type) ```typescript interface Organization { id: string created_by: string created_at: string updated_at: string logo: string | null name: string management_email: string customer_id: string | null } ``` #### Example Request [Section titled “Example Request”](#example-request) ```bash # Get all organizations curl -H "authorization: your-api-key" https://api.capgo.app/organization/ # Get specific organization curl -H "authorization: your-api-key" https://api.capgo.app/organization/?orgId=org_123 ``` #### Example Response [Section titled “Example Response”](#example-response) ```json { "data": { "id": "org_123", "name": "My Company", "created_at": "2024-01-01T00:00:00Z", "updated_at": "2024-01-01T00:00:00Z", "logo": "https://example.com/logo.png", "management_email": "admin@example.com", "customer_id": "cus_123" } } ``` ### POST [Section titled “POST”](#post) `https://api.capgo.app/organization/` Create a new organization. #### Request Body [Section titled “Request Body”](#request-body) ```typescript interface OrganizationCreate { name: string } ``` #### Example Request [Section titled “Example Request”](#example-request-1) ```bash curl -X POST \ -H "authorization: your-api-key" \ -H "Content-Type: application/json" \ -d '{ "name": "New Organization" }' \ https://api.capgo.app/organization/ ``` #### Example Response [Section titled “Example Response”](#example-response-1) ```json { "status": "Organization created", "id": "org_456" } ``` ### PUT [Section titled “PUT”](#put) `https://api.capgo.app/organization/` Update an existing organization. Requires admin role. #### Request Body [Section titled “Request Body”](#request-body-1) ```typescript interface OrganizationUpdate { orgId: string logo?: string name?: string management_email?: string } ``` #### Example Request [Section titled “Example Request”](#example-request-2) ```bash curl -X PUT \ -H "authorization: your-api-key" \ -H "Content-Type: application/json" \ -d '{ "orgId": "org_123", "name": "New Company Name", "management_email": "newemail@example.com" }' \ https://api.capgo.app/organization/ ``` #### Example Response [Section titled “Example Response”](#example-response-2) ```json { "status": "Organization updated", "data": { "id": "org_123", "name": "New Company Name", "management_email": "newemail@example.com" } } ``` ### DELETE [Section titled “DELETE”](#delete) `https://api.capgo.app/organization/` Delete an existing organization. Requires admin role. This action is irreversible and will remove all associated apps, bundles (versions), and resources. #### Query Parameters [Section titled “Query Parameters”](#query-parameters-1) * `orgId`: The ID of the organization to delete #### Example Request [Section titled “Example Request”](#example-request-3) ```bash curl -X DELETE \ -H "authorization: your-api-key" \ https://api.capgo.app/organization/?orgId=org_123 ``` #### Example Response [Section titled “Example Response”](#example-response-3) ```json { "status": "Organization deleted", "id": "org_123" } ``` ## Error Handling [Section titled “Error Handling”](#error-handling) Common error scenarios and their responses: ```json // Invalid API key { "error": "Invalid API key", "status": "KO" } // Missing required field { "error": "Name is required", "status": "KO" } // Insufficient permissions { "error": "Admin role required", "status": "KO" } ``` ## Best Practices [Section titled “Best Practices”](#best-practices) 1. **Naming**: Use clear, descriptive names for organizations 2. **Roles**: Assign appropriate roles to team members 3. **Email**: Use a group email for management\_email to avoid issues with personal email changes 4. **Logo**: Host logos on a reliable CDN and use HTTPS URLs # Statistics > Detailed guide to Statistics API endpoints, providing insights into Monthly Active Users (MAU), storage, and bandwidth metrics for apps and organizations. The Statistics endpoints provide detailed analytics about your apps and organizations. You can track Monthly Active Users (MAU), storage usage, and bandwidth consumption across different time periods. This data is essential for monitoring app growth, resource usage, and planning capacity. ## Understanding the Metrics [Section titled “Understanding the Metrics”](#understanding-the-metrics) * **MAU (Monthly Active Users)**: Number of unique devices that accessed your app in the last 30 days * **Storage**: Total size of all bundles and resources stored in bytes * **Bandwidth**: Total data transfer for bundle downloads in bytes ## Best Practices [Section titled “Best Practices”](#best-practices) 1. **Regular Monitoring**: Check statistics periodically to track growth and usage patterns 2. **Resource Planning**: Use storage and bandwidth metrics for capacity planning 3. **User Engagement**: Track MAU to understand user engagement trends 4. **Cost Management**: Monitor resource usage to optimize costs ## Endpoints [Section titled “Endpoints”](#endpoints) ### GET /statistics/app/:app\_id/ [Section titled “GET /statistics/app/:app\_id/”](#get-statisticsappapp_id) Get statistics for a specific app. This endpoint is useful for monitoring individual app performance. #### Query Parameters [Section titled “Query Parameters”](#query-parameters) ```typescript interface StatsQuery { from: Date // Start date for the statistics (format: YYYY-MM-DD) to: Date // End date for the statistics (format: YYYY-MM-DD) } ``` #### Example Request [Section titled “Example Request”](#example-request) ```bash curl -H "authorization: your-api-key" \ "https://api.capgo.app/statistics/app/com.demo.app/?from=2024-01-01&to=2024-02-01" ``` #### Example Response [Section titled “Example Response”](#example-response) ```json [ { "date": "2024-01-01", "mau": 1500, "storage": 536870912, // 512MB in bytes "bandwidth": 1073741824 // 1GB in bytes }, { "date": "2024-01-02", "mau": 1550, "storage": 537919488, // 513MB in bytes "bandwidth": 1074790400 // 1.01GB in bytes } ] ``` ### GET /statistics/org/:org\_id/ [Section titled “GET /statistics/org/:org\_id/”](#get-statisticsorgorg_id) Get statistics for a specific organization. Useful for monitoring organization-level usage. #### Query Parameters [Section titled “Query Parameters”](#query-parameters-1) ```typescript interface StatsQuery { from: Date // Start date for the statistics (format: YYYY-MM-DD) to: Date // End date for the statistics (format: YYYY-MM-DD) breakdown: boolean // default false, optional if true it return the breakdown by app noAccumulate: boolean // default false, optional if true it will not accumulate data and just return day by day result } ``` #### Example Request [Section titled “Example Request”](#example-request-1) ```bash curl -H "authorization: your-api-key" \ "https://api.capgo.app/statistics/org/046a36ac-e03c-4590-9257-bd6c9dba9ee8/?from=2024-01-01&to=2024-02-01" ``` #### Example Response [Section titled “Example Response”](#example-response-1) ```json [ { "date": "2024-01-01", "mau": 10000, "storage": 536870912, // 512MB in bytes "bandwidth": 1073741824 // 1GB in bytes }, { "date": "2024-01-02", "mau": 10200, "storage": 537919488, // 513MB in bytes "bandwidth": 1074790400 // 1.01GB in bytes } ] ``` ### GET /statistics/user/ [Section titled “GET /statistics/user/”](#get-statisticsuser) Get aggregated statistics across all organizations you have access to. Perfect for overall usage monitoring. #### Query Parameters [Section titled “Query Parameters”](#query-parameters-2) ```typescript interface StatsQuery { from: Date // Start date for the statistics (format: YYYY-MM-DD) to: Date // End date for the statistics (format: YYYY-MM-DD) } ``` #### Example Request [Section titled “Example Request”](#example-request-2) ```bash curl -H "authorization: your-api-key" \ "https://api.capgo.app/statistics/user/?from=2024-01-01&to=2024-02-01" ``` #### Example Response [Section titled “Example Response”](#example-response-2) ```json [ { "date": "2024-01-01", "mau": 25000, "storage": 1073741824, // 1GB in bytes "bandwidth": 2147483648 // 2GB in bytes }, { "date": "2024-01-02", "mau": 25500, "storage": 1074790400, // 1.01GB in bytes "bandwidth": 2148532224 // 2.01GB in bytes } ] ``` ### GET /statistics/app/:app\_id/bundle\_usage [Section titled “GET /statistics/app/:app\_id/bundle\_usage”](#get-statisticsappapp_idbundle_usage) Get bundle usage statistics for a specific app, showing the distribution of bundles (versions) among users over a specified period. #### Query Parameters [Section titled “Query Parameters”](#query-parameters-3) ```typescript interface BundleUsageQuery { from: Date // Start date for the statistics (format: YYYY-MM-DD) to: Date // End date for the statistics (format: YYYY-MM-DD) } ``` #### Example Request [Section titled “Example Request”](#example-request-3) ```bash curl -H "authorization: your-api-key" \ "https://api.capgo.app/statistics/app/com.demo.app/bundle_usage?from=2024-01-01&to=2024-02-01" ``` #### Example Response [Section titled “Example Response”](#example-response-3) ```json { "labels": ["2024-01-01", "2024-01-02", "2024-01-03"], "datasets": [ { "label": "1.0.0", "data": [60.5, 58.2, 55.3] }, { "label": "1.0.1", "data": [39.5, 41.8, 44.7] } ] } ``` ## Error Handling [Section titled “Error Handling”](#error-handling) Common error scenarios and their responses: ```json // Invalid body { "status": "Invalid body", "error": "Invalid date format or missing parameters" } // Permission denied { "status": "You can't access this app", "error": "Insufficient permissions to access statistics" } // Permission denied for organization { "status": "You can't access this organization", "error": "Insufficient permissions to access organization statistics" } // No organizations found for user statistics { "status": "No organizations found", "error": "No organizations found" } // Internal server error { "status": "Cannot get app statistics", "error": "Internal server error message" } ``` ## Common Use Cases [Section titled “Common Use Cases”](#common-use-cases) 1. **Growth Tracking**: Monitor MAU growth over time 2. **Resource Optimization**: Track storage and bandwidth usage to optimize costs 3. **Capacity Planning**: Use trends to plan for future resource needs 4. **Usage Reports**: Generate periodic usage reports for stakeholders 5. **Bundle (version) Distribution Analysis**: Understand how users are distributed across different app bundles (versions) with bundle usage statistics ## Tips for Analysis [Section titled “Tips for Analysis”](#tips-for-analysis) 1. **Compare Periods**: Look at month-over-month or year-over-year trends 2. **Track Ratios**: Monitor bandwidth per user or storage per app 3. **Set Alerts**: Create alerts for unusual spikes in usage 4. **Regular Backups**: Export statistics regularly for historical analysis 5. **Bundle (version) Adoption**: Use bundle usage to track adoption rates of new bundles (versions) # Migrate from AppFlow to Capgo > Detailed walkthrough for moving an Ionic AppFlow project to Capgo. Each step maps to the standard AppFlow migration tasks so you can compare line by line—and see which ones Capgo already handles for you. > 🚦 Ionic announced that AppFlow’s commercial products—including Live Updates—are winding down. Existing projects can run until **31 December 2027**, but no new customers are accepted and no new features are planned. This guide walks you through the actions required to migrate to Capgo and highlights the native automation you gain. ## Migration overview [Section titled “Migration overview”](#migration-overview) Capgo handles channels, bundle retention, rollbacks, analytics, and CLI uploads for you. Migration boils down to installing the plugin, calling `CapacitorUpdater.notifyAppReady()`, and—if desired—configuring optional manual controls. The sections below walk through each task directly. Version Targeting Like AppFlow If you relied on AppFlow’s automatic version matching (delivering compatible updates based on native version), Capgo provides the same capability with enhanced control. See the [Version Targeting Guide](/docs/live-updates/version-targeting) for detailed strategies on managing updates across multiple app versions. ## Step 0 – Capture your current AppFlow setup [Section titled “Step 0 – Capture your current AppFlow setup”](#step0-capture-your-current-appflow-setup) * Note your AppFlow **App ID**, existing channels, and signing keys. * Export any bundle history you want to archive. * If you are using GitHub Actions or another CI provider, keep those pipelines—they will keep working with Capgo. ## Step 1 – Replace the AppFlow SDK with Capgo [Section titled “Step 1 – Replace the AppFlow SDK with Capgo”](#step1-replace-the-appflow-sdk-with-capgo) ```bash npm uninstall @capacitor/live-updates npm install @capgo/capacitor-updater npx cap sync ``` That’s it. Capgo bundles the native code for both iOS and Android; no extra JavaScript helpers are required. ## Step 2 – Minimal configuration (no manual fields) [Section titled “Step 2 – Minimal configuration (no manual fields)”](#step2-minimal-configuration-no-manual-fields) The existing configuration block is extensive. Capgo auto-detects your project and channels, so the minimal configuration is: capacitor.config.ts ```ts import { CapacitorConfig } from '@capacitor/cli' const config: CapacitorConfig = { plugins: { CapacitorUpdater: { autoUpdate: true, autoDeletePrevious: true, }, }, } export default config ``` ### Configuration quick reference [Section titled “Configuration quick reference”](#configuration-quick-reference) | Ionic AppFlow setting | Capgo equivalent | Do you need to set it? | | ---------------------------- | ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | | `appId` | Managed in the Capgo dashboard | Automatically supplied when you create the project | | `channel` / `defaultChannel` | Channel rules in the dashboard/API | Optional override; defaults come from the server. See [Version Targeting](/docs/live-updates/version-targeting) for multi-version strategies | | `autoUpdateMethod` | `autoUpdate: true` | Enabled by default | | `maxVersions` | Retention policy | Configured centrally (1 month default, 24 months max) | | `enabled` | Not required | Capgo toggles availability per channel | ## Step 3 – Call `notifyAppReady()` (the only required hook) [Section titled “Step 3 – Call notifyAppReady() (the only required hook)”](#step3-call-notifyappready-the-only-required-hook) In Ionic’s guide you wire `sync`, `download`, and `reload`, then hide the splash screen manually. Capgo performs those actions natively. You only need to confirm the app is ready: ```ts import { CapacitorUpdater } from '@capgo/capacitor-updater' CapacitorUpdater.notifyAppReady() ``` If the confirmation never arrives, Capgo rolls the bundle back automatically. **That’s it—Capgo handles the background checks, splash visibility, and rollbacks for you.** Optional: run logic before the splash screen hides ```ts import { CapacitorUpdater } from '@capgo/capacitor-updater' import { SplashScreen } from '@capacitor/splash-screen' CapacitorUpdater.addListener('appReady', () => { // Log diagnostics or run custom code if needed SplashScreen.hide() }) CapacitorUpdater.notifyAppReady() ``` ## Step 4 – Update strategies translated [Section titled “Step 4 – Update strategies translated”](#step4-update-strategies-translated) AppFlow documents three strategies. Here is how they map to Capgo: ### Background (default) [Section titled “Background (default)”](#background-default) * **AppFlow**: configure `autoUpdateMethod = background`, call `sync()` manually. * **Capgo**: enabled by default. No JavaScript required. ### Always latest [Section titled “Always latest”](#always-latest) * **AppFlow**: add an `App.addListener('resume')` handler that downloads and reloads. * **Capgo**: auto-update runs on resume already. Add the handler only if you want a custom timing window. Optional: manual resume check ```ts import { App } from '@capacitor/app' import { CapacitorUpdater } from '@capgo/capacitor-updater' App.addListener('resume', async () => { const bundle = await CapacitorUpdater.download() if (bundle) { await CapacitorUpdater.set({ id: bundle.id }) } }) ``` ### Force update [Section titled “Force update”](#force-update) * **AppFlow**: prompt the user and call `reload()`. * **Capgo**: mark the bundle as “mandatory” in the dashboard, then listen for the `majorAvailable` event (emitted after `notifyAppReady()`) to prompt or force users inside your app. ## Step 5 – Mapping API calls [Section titled “Step 5 – Mapping API calls”](#step5-mapping-api-calls) | AppFlow method | Capgo equivalent | Do you need it? | | -------------------------- | ----------------------------- | ---------------------------------------------------------- | | `LiveUpdates.sync()` | Handled automatically | Capgo’s native auto-update runs without a manual sync call | | `LiveUpdates.download()` | `CapacitorUpdater.download()` | Optional for custom flows | | `LiveUpdates.reload()` | `CapacitorUpdater.set()` | Optional; dashboard toggles handle forced updates | | `LiveUpdates.getVersion()` | `CapacitorUpdater.current()` | Optional diagnostics | ## Step 6 – Deploy using the Capgo CLI or API [Section titled “Step 6 – Deploy using the Capgo CLI or API”](#step6-deploy-using-the-capgo-cli-or-api) Finish the migration by uploading bundles with the Capgo CLI or API. The workflow mirrors what you may have scripted before, but now includes native safeguards: ```bash capgo login # authenticate once capgo bundle upload \ --path dist \ --channel production # automatically tags platform/version ``` ### Version-specific deployments (like AppFlow) [Section titled “Version-specific deployments (like AppFlow)”](#version-specific-deployments-like-appflow) If you need to target specific native versions (similar to AppFlow’s native version locking): ```bash # Only deliver to devices on native version 2.0.0 or higher capgo bundle upload \ --path dist \ --channel production \ --native-version "2.0.0" # Use channels for different major versions capgo bundle upload --channel v2 # for app version 2.x capgo bundle upload --channel v3 # for app version 3.x ``` See the [Version Targeting Guide](/docs/live-updates/version-targeting) for comprehensive strategies. Capgo automatically: * Keeps device-level audit logs for every install. * Sends proactive emails when you approach plan limits. * Provides burst credits so you are never blocked mid-release. * Publishes latency metrics for 18 global regions at [status.capgo.app/history](https://status.capgo.app/history). ## Ionic enterprise plugins [Section titled “Ionic enterprise plugins”](#ionic-enterprise-plugins) If your Ionic stack also uses enterprise plugins, follow the targeted migration docs below. Each guide recommends the Capgo replacement and the next steps. * [Migrate from Ionic Secure Storage](/docs/upgrade/from-ionic-secure-storage) * [Migrate from Ionic Auth Connect](/docs/upgrade/from-ionic-auth-connect) * [Migrate from Ionic Identity Vault](/docs/upgrade/from-ionic-identity-vault) ## Frequently asked questions [Section titled “Frequently asked questions”](#frequently-asked-questions) ### Why is AppFlow shutting down live updates? [Section titled “Why is AppFlow shutting down live updates?”](#why-is-appflow-shutting-down-live-updates) Ionic is discontinuing commercial products, including AppFlow, to focus on their open-source framework. Existing customers can continue using live updates until **31 December 2027**, but no new features or customers are accepted. Capgo fills that gap with a dedicated native OTA platform. ### How long does migration take? [Section titled “How long does migration take?”](#how-long-does-migration-take) Most teams complete the move in under a day. Concepts such as channels, deployments, and release rules map directly, and our team provides documentation plus hands-on support. In many cases you simply install the plugin, call `notifyAppReady()`, and upload your first bundle. ### Will we save money? [Section titled “Will we save money?”](#will-we-save-money) Yes. AppFlow live updates start at **$499/mo**. Capgo starts at **$14/mo** with usage-based pricing that drops to roughly **$0.001 per MAU**. You also gain encryption, automatic rollbacks, and worldwide latency monitoring. ### When should we migrate? [Section titled “When should we migrate?”](#when-should-we-migrate) Because AppFlow is now in maintenance mode, migrating sooner gives you access to ongoing Capgo innovation. We recommend switching when it fits your release schedule. Our engineering team will help you plan the changeover so your CI/CD and deployments keep running. ## Additional Resources [Section titled “Additional Resources”](#additional-resources) * **[Version Targeting Guide](/docs/live-updates/version-targeting)** - Deep dive into AppFlow-style version matching strategies * **[Breaking Changes](/docs/live-updates/breaking-changes)** - Managing major version updates with channels * **[Channel Management](/docs/live-updates/channels)** - Complete channel configuration reference ## Need help? [Section titled “Need help?”](#need-help) * Book a migration session: [cal.com/team/capgo/demo](https://cal.com/team/capgo/demo) * Join the community: [Capgo Discord](https://discord.gg/VCXxSVjefW) * Track issues / request features: [github.com/Cap-go/capacitor-updater](https://github.com/Cap-go/capacitor-updater) Capgo is engineered for enterprises that need native delta updates, encrypted bundles, and continuous innovation. Once you migrate you can delete the AppFlow glue code, rely on native automation, and keep shipping without interruption. # Migrate from Capawesome Cloud to Capgo > Step-by-step guide to move from Capawesome Cloud to Capgo while gaining native OTA safety, device-level observability, and full automation. > ⚡️ Capgo automates channels, bundle cleanup, rollbacks, analytics, and CLI uploads natively. Use this guide to perform the minimal steps required to migrate and optionally recreate any custom behaviour you still need. ## Overview [Section titled “Overview”](#overview) 1. Gather your existing Capawesome Cloud configuration (App ID, channels, signing keys, CLI tokens) so you can archive or audit it later. 2. Install the Capgo plugin, remove the Capawesome SDK, and call `CapacitorUpdater.notifyAppReady()`. 3. Configure optional behaviour (manual downloads, pinning bundles, reloads) if you rely on those flows today. With Capgo you only need to install our plugin and call `CapacitorUpdater.notifyAppReady()`. Everything else—channels, bundle cleanup, rollbacks, analytics, and CLI automation—is handled natively. The sections below walk through each task directly. ## Before you start [Section titled “Before you start”](#before-you-start) * Make sure your project is already using Capacitor 5 or later. * Install the Capgo CLI (`npm install -g @capgo/cli`) if you plan to push bundles from CI/CD. ## Step 1 – Install Capgo and remove the Capawesome SDK [Section titled “Step 1 – Install Capgo and remove the Capawesome SDK”](#step1-install-capgo-and-remove-the-capawesome-sdk) ```bash npm uninstall @capawesome/capacitor-live-update npm install @capgo/capacitor-updater npx cap sync ``` That is the only mandatory swap. Capgo’s native code ships with the plugin; no extra JavaScript helpers are required. ## Step 2 – Minimal configuration [Section titled “Step 2 – Minimal configuration”](#step2-minimal-configuration) The previous setup required mapping dozens of options in `capacitor.config`. Capgo recognises your project automatically, so the minimal configuration looks like this: capacitor.config.ts ```ts import { CapacitorConfig } from '@capacitor/cli' const config: CapacitorConfig = { plugins: { CapacitorUpdater: { autoUpdate: true, autoDeletePrevious: true, periodCheckDelay: 10 * 60 * 1000, // optional: check every 10 minutes }, }, } export default config ``` Everything Capawesome lists as manual flags (`defaultChannel`, `autoDeleteBundles`, retention policies, etc.) is managed through the Capgo dashboard or API. You only need to override these keys if you want behaviour that differs from Capgo’s defaults. ### Configuration quick reference [Section titled “Configuration quick reference”](#configuration-quick-reference) | Capawesome option | Capgo equivalent | Do you need to set it? | | ------------------------- | -------------------------------------------------------- | -------------------------------------------------------------- | | `appId` | Taken from the Capgo dashboard once you create a project | Only if you use multiple projects in one binary | | `defaultChannel` | Channel rules managed in the dashboard/API | Optional; most teams set this server-side | | `autoDeleteBundles` | `autoDeletePrevious: true` (default) | Already enabled | | `publicKey` | Managed in Capgo console | Only if you rotate keys manually | | `maxVersions` / retention | Bundle retention policy | Configured centrally in Capgo (1 month default, 24 months max) | ## Step 3 – Call `notifyAppReady()` (the only required hook) [Section titled “Step 3 – Call notifyAppReady() (the only required hook)”](#step3-call-notifyappready-the-only-required-hook) The old workflow introduced custom listeners (`checkForUpdates()`, `retryDownload()`, hiding the splash screen, etc.). Capgo performs those steps natively. The only API you must call is: ```ts import { CapacitorUpdater } from '@capgo/capacitor-updater' CapacitorUpdater.notifyAppReady() ``` This confirms the app booted successfully. If the confirmation never arrives, Capgo automatically rolls back the bundle—no extra JavaScript needed. **That’s it—Capgo handles background checks, splash visibility, and rollbacks natively.** Optional: run custom logic before the splash screen hides ```ts import { CapacitorUpdater } from '@capgo/capacitor-updater' import { SplashScreen } from '@capacitor/splash-screen' CapacitorUpdater.addListener('appReady', () => { // Run diagnostics or logging if you need to SplashScreen.hide() }) CapacitorUpdater.notifyAppReady() ``` ## Step 4 – Map API calls (mostly optional) [Section titled “Step 4 – Map API calls (mostly optional)”](#step4-map-api-calls-mostly-optional) In Capgo you normally let the auto-updater run; manual APIs remain available if you want full control. | Capawesome Cloud | Capgo equivalent | Do you need it? | | -------------------------------- | ------------------------------ | ------------------------------------------------------------------- | | `LiveUpdate.fetchLatestBundle()` | `CapacitorUpdater.getLatest()` | Only when implementing your own download workflow | | `LiveUpdate.downloadBundle()` | `CapacitorUpdater.download()` | Optional: native auto-update already downloads | | `LiveUpdate.setNextBundle()` | `CapacitorUpdater.next()` | Optional: dashboard pins bundles automatically | | `LiveUpdate.reload()` | `CapacitorUpdater.reload()` | Optional; Capgo enforces mandatory bundles after `notifyAppReady()` | | `LiveUpdate.getCurrentBundle()` | `CapacitorUpdater.current()` | Optional diagnostics | If you stick with the native auto-update behaviour you can delete the Capawesome JavaScript entirely. ### Manual control examples [Section titled “Manual control examples”](#manual-control-examples) **Download the latest bundle** Capgo ```ts import { CapacitorUpdater } from '@capgo/capacitor-updater' const downloadUpdate = async () => { const latest = await CapacitorUpdater.getLatest() if (latest?.url) { const bundle = await CapacitorUpdater.download({ url: latest.url, version: latest.version, }) console.log('Bundle downloaded', bundle?.id) } } ``` Capawesome Cloud ```ts import { LiveUpdate } from '@capawesome/capacitor-live-update' const downloadUpdate = async () => { const result = await LiveUpdate.fetchLatestBundle() if (result.downloadUrl) { await LiveUpdate.downloadBundle({ bundleId: result.bundleId, url: result.downloadUrl, }) console.log('Bundle downloaded') } } ``` **Set the next bundle** Capgo ```ts import { CapacitorUpdater } from '@capgo/capacitor-updater' const setNextBundle = async () => { await CapacitorUpdater.next({ id: 'bundle-id-123' }) } ``` Capawesome Cloud ```ts import { LiveUpdate } from '@capawesome/capacitor-live-update' const setNextBundle = async () => { await LiveUpdate.setNextBundle({ bundleId: 'bundle-id-123' }) } ``` **Apply the downloaded bundle immediately** Capgo ```ts import { CapacitorUpdater } from '@capgo/capacitor-updater' const applyUpdate = async () => { await CapacitorUpdater.reload() } ``` Capawesome Cloud ```ts import { LiveUpdate } from '@capawesome/capacitor-live-update' const applyUpdate = async () => { await LiveUpdate.reload() } ``` ## Step 5 – Update strategies: how Capgo handles them [Section titled “Step 5 – Update strategies: how Capgo handles them”](#step5-update-strategies-how-capgo-handles-them) Capawesome documents three strategies. Here’s how they translate: ### Background updates [Section titled “Background updates”](#background-updates) * **Previous workflow**: configure in code and schedule downloads manually. * **Capgo**: enabled by default (`autoUpdate: true`). No additional code required. ### Always latest [Section titled “Always latest”](#always-latest) * **Previous workflow**: add an `App.resume` listener, call `download`, then `set`. * **Capgo**: background auto-update already performs the check after resume. You only need the manual listener if you want a custom interval. Optional: manual resume check ```ts import { App } from '@capacitor/app' import { CapacitorUpdater } from '@capgo/capacitor-updater' App.addListener('resume', async () => { const latest = await CapacitorUpdater.getLatest() if (latest?.url) { const downloaded = await CapacitorUpdater.download({ url: latest.url, version: latest.version, }) if (downloaded) { await CapacitorUpdater.next({ id: downloaded.id }) } } }) ``` ### Force update [Section titled “Force update”](#force-update) * **Previous workflow**: wire prompt logic and enforce reload. * **Capgo**: mark the bundle as “mandatory” in the dashboard, then listen for the `majorAvailable` event (emitted after `notifyAppReady()`) to require users to upgrade inside your app. ## Step 6 – Deploying bundles [Section titled “Step 6 – Deploying bundles”](#step6-deploying-bundles) If you previously relied on `capawesome live-update deploy`, Capgo offers a similar CLI workflow, and you can also automate deployments entirely via API. ```bash # Authenticate once (stores a token in your CI environment) capgo login # Upload a new bundle (auto-detects platform/version) capgo bundle upload --path dist --channel production ``` Because Capgo tracks bundle health automatically, you also get: * Device-level audit logs for every install. * Automatic retention (one month by default) with configurable limits up to 24 months. * Real-time latency metrics at [status.capgo.app/history](https://status.capgo.app/history). ## Migration timeline [Section titled “Migration timeline”](#migration-timeline) * **Inventory & install**: 10 minutes (`npm install`, remove old plugin). * **Config & readiness**: 5 minutes (`notifyAppReady`). * **Sanity checks**: 15 minutes (optional manual tests or listeners). * **First deployment**: 10 minutes with Capgo CLI or CI integration. In practice teams finish in under an hour. If you provide Capawesome project details we can even import channels and device lists for you. ## Capgo support [Section titled “Capgo support”](#capgo-support) * **Migration concierge**: book a session at [cal.com/team/capgo/demo](https://cal.com/team/capgo/demo). * **Community**: join the [Capgo Discord](https://discord.gg/VCXxSVjefW). * **Issue tracker**: [github.com/Cap-go/capacitor-updater/issues](https://github.com/Cap-go/capacitor-updater/issues). Capgo is built for long-term reliability: native delta updates, encrypted bundles, automatic rollbacks, and analytics that do not require custom JavaScript. Once you migrate you can delete the maintenance-heavy glue and let the platform run updates automatically. # Migrate from Ionic Auth Connect > Move from Ionic Auth Connect to Capgo Social Login with OAuth2 support. Ionic Auth Connect handles enterprise OAuth and OIDC flows. Capgo’s Social Login plugin provides provider-native sign-in for Google, Apple, Facebook, Twitter, and generic OAuth2 providers. ## Capgo replacements at a glance [Section titled “Capgo replacements at a glance”](#capgo-replacements-at-a-glance) | Ionic enterprise plugin | Capgo replacement | Migration guide | | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- | | Secure Storage | [@capgo/capacitor-fast-sql](/docs/plugins/fast-sql/) or [@capgo/capacitor-data-storage-sqlite](/docs/plugins/data-storage-sqlite/) | [Secure Storage migration](/docs/upgrade/from-ionic-secure-storage) | | Auth Connect | [@capgo/capacitor-social-login](/docs/plugins/social-login/) | You are here | | Identity Vault | [@capgo/capacitor-native-biometric](/docs/plugins/native-biometric/) | [Identity Vault migration](/docs/upgrade/from-ionic-identity-vault) | ## Migration steps [Section titled “Migration steps”](#migration-steps) 1. **Inventory your providers** (Google, Apple, Facebook, Azure AD, Auth0, Okta, etc.) and the redirect URLs you currently use. 2. **Install Social Login** and sync native code. ```bash npm install @capgo/capacitor-social-login npx cap sync ``` 3. **Configure each provider** using the Social Login docs (client IDs, bundle IDs, OAuth redirect URIs). 4. **Update your auth flow** to call Social Login provider methods instead of Auth Connect. Store refresh tokens in a secure store (Fast SQL or Data Storage SQLite) if you rely on long-lived sessions. 5. **Remove Ionic Auth Connect** from your dependencies and native configuration. ## Next steps [Section titled “Next steps”](#next-steps) * [Social Login getting started](/docs/plugins/social-login/getting-started/) * [Secure Storage migration](/docs/upgrade/from-ionic-secure-storage) * [Identity Vault migration](/docs/upgrade/from-ionic-identity-vault) # Migrate from Ionic Identity Vault > Replace Ionic Identity Vault with Capgo Native Biometric and secure storage plugins. Ionic Identity Vault combines biometric gating with encrypted storage. Capgo replaces that stack with Native Biometric for user verification plus Fast SQL or Data Storage SQLite for encrypted persistence. ## Capgo replacements at a glance [Section titled “Capgo replacements at a glance”](#capgo-replacements-at-a-glance) | Ionic enterprise plugin | Capgo replacement | Migration guide | | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- | | Secure Storage | [@capgo/capacitor-fast-sql](/docs/plugins/fast-sql/) or [@capgo/capacitor-data-storage-sqlite](/docs/plugins/data-storage-sqlite/) | [Secure Storage migration](/docs/upgrade/from-ionic-secure-storage) | | Auth Connect | [@capgo/capacitor-social-login](/docs/plugins/social-login/) | [Auth Connect migration](/docs/upgrade/from-ionic-auth-connect) | | Identity Vault | [@capgo/capacitor-native-biometric](/docs/plugins/native-biometric/) | You are here | ## Migration steps [Section titled “Migration steps”](#migration-steps) 1. **Install Native Biometric** and sync native code. ```bash npm install @capgo/capacitor-native-biometric npx cap sync ``` 2. **Pick a secure storage plugin** (Fast SQL or Data Storage SQLite) to persist tokens or secrets. 3. **Gate sensitive reads/writes** with a biometric prompt before accessing the stored data. 4. **Migrate stored secrets** on first launch by reading from Identity Vault and writing them into the new SQLite store. 5. **Remove Ionic Identity Vault** from your dependencies and native configuration. ## Next steps [Section titled “Next steps”](#next-steps) * [Native Biometric getting started](/docs/plugins/native-biometric/getting-started/) * [Secure Storage migration](/docs/upgrade/from-ionic-secure-storage) * [Auth Connect migration](/docs/upgrade/from-ionic-auth-connect) # Migrate from Ionic Secure Storage > Replace Ionic Secure Storage with Capgo Fast SQL or Data Storage SQLite. Ionic Secure Storage is an enterprise plugin for encrypted key-value data. Capgo provides two SQLite-based options depending on your data model and performance needs. ## Capgo replacements at a glance [Section titled “Capgo replacements at a glance”](#capgo-replacements-at-a-glance) | Ionic enterprise plugin | Capgo replacement | Migration guide | | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- | | Secure Storage | [@capgo/capacitor-fast-sql](/docs/plugins/fast-sql/) or [@capgo/capacitor-data-storage-sqlite](/docs/plugins/data-storage-sqlite/) | You are here | | Auth Connect | [@capgo/capacitor-social-login](/docs/plugins/social-login/) | [Auth Connect migration](/docs/upgrade/from-ionic-auth-connect) | | Identity Vault | [@capgo/capacitor-native-biometric](/docs/plugins/native-biometric/) | [Identity Vault migration](/docs/upgrade/from-ionic-identity-vault) | ## Choose your storage plugin [Section titled “Choose your storage plugin”](#choose-your-storage-plugin) * **Fast SQL**: Best for large datasets, sync workflows, or when you want full SQL control and high throughput. * **Data Storage SQLite**: Best for simple key-value storage with optional encryption and minimal setup. If you are unsure, start with Data Storage SQLite for key-value secrets and move to Fast SQL when you need structured data or high-volume writes. ## Migration steps [Section titled “Migration steps”](#migration-steps) 1. **Pick the storage plugin** that matches your data model. 2. **Install the plugin** and sync native code. ```bash # Option A: Fast SQL npm install @capgo/capacitor-fast-sql # Option B: Data Storage SQLite npm install @capgo/capacitor-data-storage-sqlite npx cap sync ``` 3. **Update storage calls** to the new API surface. Use the Fast SQL or Data Storage SQLite docs for the exact read/write methods. 4. **Migrate existing data** on first launch by reading values from Ionic Secure Storage and writing them into the new SQLite store. 5. **Remove Ionic Secure Storage** from your dependencies and native configuration. ## Next steps [Section titled “Next steps”](#next-steps) * [Fast SQL getting started](/docs/plugins/fast-sql/getting-started/) * [Data Storage SQLite getting started](/docs/plugins/data-storage-sqlite/getting-started/) * [Auth Connect migration](/docs/upgrade/from-ionic-auth-connect) * [Identity Vault migration](/docs/upgrade/from-ionic-identity-vault) # From V2 to V3 > A comprehensive guide on transitioning from version 2 to version 3 of Capgo updater, detailing the necessary steps and considerations for a successful upgrade process This documentation will explain how to upgrade to the version 3 of auto-update. ## First migrate to the last tooling: [Section titled “First migrate to the last tooling:”](#first-migrate-to-the-last-tooling) ```bash npm remove -g capgo npm remove capacitor-updater npm i @capgo/cli npm i @capgo/capacitor-updater@3 npx cap sync ``` ## Remove all your previous config: [Section titled “Remove all your previous config:”](#remove-all-your-previous-config) ```json { CapacitorUpdater: { autoUpdateURL: "https...", ... }, } ``` to only let this: ```json { "CapacitorUpdater": { "autoUpdate": true } } ``` > ⚠️ If you were using your server, with `autoUpdateURL`, I will upgrade this guide soon for you. Meanwhile, take a look at the new upload option `external` which allows you to send only the link of your zip, not the code in Capgo cloud. This has been made for companies with strict privacy policies. In external mode, the code will never land on Capgo server, we just store the URL and send it to the device, which will directly download it. In the standard way, the code is zipped and stored in our server, but we will never open it or use it either. ## What change [Section titled “What change”](#what-change) All configurations become server-side for auto-update, to give you more control on how you send an update to users. That allows us to revert, even deploy just to one user with channels! These settings are added back to the web interface: * disable revert under native * disable update above major > ⚠️ They will become true by default for all channels This will also remove the need to update often the plugin, most updates will be done server side, and you will get it without any change in your side. > ⚠️ Reset when an update becomes the default, so if you prefer not to remove all download versions when updating from the store, do this: ```json { "CapacitorUpdater": { "autoUpdate": true, "resetWhenUpdate": false } } ``` ## Update your code [Section titled “Update your code”](#update-your-code) Lastly, update all your imports in JS from: ```plaintext import { CapacitorUpdater } from 'capacitor-updater' ``` to ```plaintext import { CapacitorUpdater } from '@capgo/capacitor-updater' ``` Then build your code again `npm run build` and copy assets once more `npx cap copy`. You should be able now to test the last auto-update system Send your version with: ```plaintext npx @capgo/cli@latest bundle upload ``` instead of ```plaintext npx capgo upload ``` ## Future evolution [Section titled “Future evolution”](#future-evolution) For now only the first public channel is in use, in the future, public will change for multi public channels, if more than one is set. ## Common problems: [Section titled “Common problems:”](#common-problems) * Build problem after upgrade: if you have already opened the source code of the plugin in Android studio or Xcode, sometimes the sync doesn’t remove them, that the cause of the issue. Open the native IDE and remove `capacitor-updater` by hands and do `npx cap sync` this should solve. # From V3 to V4 > How to upgrade from V3 to V4 of Capgo updater, understand what are the breaking changes and how to handle them ## Why this upgrade [Section titled “Why this upgrade”](#why-this-upgrade) After many talk in the discord community with you. I discovered the manual mode was very too manual and not safe to use, for example, auto-revert was not possible, so if you failed update in manual the user have to remove the app and install back, what is terrible UX. Meanwhile, I took this as an opportunity to give more freedom to you, and remove all bad code I made. ## Install [Section titled “Install”](#install) `npm i @capgo/capacitor-updater@4` ## Auto-update cloud [Section titled “Auto-update cloud”](#auto-update-cloud) If you use the basic example in your app, you are safe to migrate to the new version, enjoy! ## Auto-update self-hosted [Section titled “Auto-update self-hosted”](#auto-update-self-hosted) For you, still simple, the changes are: * The name of the setting from `autoUpdateUrl` in `updateUrl` * The Endpoint method changed from `GET` to POST ## Manual users [Section titled “Manual users”](#manual-users) For you, this is the most significant change, but for the best! You get tons of improvements, Read carefully. ## Changes [Section titled “Changes”](#changes) * `autoUpdateUrl` becomes `updateUrl` since this setting can be used in manual mode now too * Delete of `cancelDelay` and `delayUpdate` in favor of `setDelay` * No more `versionName` in set * Change `version` key, who was returned in most function to object `BundleInfo` ```typescript interface BundleInfo { id: string; version: string; downloaded: string; status: 'success' | 'error' | 'pending' | 'downloading' } ``` * Renamed of misleading names now (even to explain cannot be clear, but at usage is easy to understand the new one): * what was called a `version` is now referring to a `bundle` * `id` refer to the old `version` who was a random string of 10 char, this `id` is the only trustable and unique way to access to your bundles, example `7Dfcd2RedN`. * `version` refer now to the `versionName` you choose for a bundle, example `1.0.0` * `updateUrl` move from `get` to `post`, since custom headers were a problem for some of you and post is more logical, all previous headers go to the body and prefix `cap_` disappear. * `versionName` method is deleted, in favor of `getId` * list returns now a list of `BundleInfo` * Rename `getId` in `getDeviceId` * `autoUpdate` becomes true by default, if you use Manual mode, set it to false. ## News [Section titled “News”](#news) * Method `getLatest`, this method allows you to get from your server set with `updateUrl` the last version available. * Method `setDelay` who take `{`kind`:` “background” | “kill” | “nativeVersion” | “date”, value? : string`}` as argument to set delay to different modes. * Method `next`, to set the version in next backgrounding, in opposite to `set` who do it instantly. * Method `isAutoUpdateEnabled`, to let you know if you are in auto-update context * Event `downloadComplete` when download reach 100% * Added mandatory field `version` in download method * `notifyAppReady` become mandatory in manual mode too, if not call after 10 sec the app reverts to past version. ## Contributors [Section titled “Contributors”](#contributors) [@lincolnthree](https://github.com/lincolnthree/) Thank you so much for starting this work, it was impossible to make this update work without you. # From V4 to V5 > How to upgrade from V4 to V5, of Capgo updater what are the breaking change you should take care of, it's pretty simple ## Why this upgrade [Section titled “Why this upgrade”](#why-this-upgrade) This major version is here to follow Capacitor major version First follow the migration guide of Capacitor: [https://capacitorjs.com/docs/updating/5-0](https://capacitorjs.com/docs/updating/5-0/) ## Install [Section titled “Install”](#install) `npm i @capgo/capacitor-updater@5` `Then sync the native code update:` `npx cap sync` That it ! Pretty easy ! ## Manual mode [Section titled “Manual mode”](#manual-mode) If you were getting yourself the update with getLatest, there are a tiny change. Now if you are up-to-date already it will go into catch. Any response different than update available will do that. # From V5 to V6 > A detailed guide on transitioning from version 5 to version 6 of Capgo updater, outlining the necessary steps and considerations for a successful upgrade process, ensuring compatibility with the latest Capacitor features and improvements. ## Why this upgrade [Section titled “Why this upgrade”](#why-this-upgrade) This major version is here to follow Capacitor major version First follow the migration guide of Capacitor: [https://capacitorjs.com/docs/updating/6-0](https://capacitorjs.com/docs/updating/6-0/) ## Install [Section titled “Install”](#install) `npm i @capgo/capacitor-updater@6` `Then sync the native code update:` `npx cap sync` That it ! Pretty easy ! # From V6 to V7 > A detailed guide on transitioning from version 6 to version 7 of Capgo updater, outlining the necessary steps and considerations for a successful upgrade process, ensuring compatibility with the latest Capacitor features and improvements. ## Why this upgrade [Section titled “Why this upgrade”](#why-this-upgrade) This major version is here to follow Capacitor major version First follow the migration guide of Capacitor: [https://capacitorjs.com/docs/updating/7-0](https://capacitorjs.com/docs/updating/7-0/) ## Install [Section titled “Install”](#install) `npm i @capgo/capacitor-updater@7` `Then sync the native code update:` `npx cap sync` That it ! Pretty easy ! ## Encryption Migration [Section titled “Encryption Migration”](#encryption-migration) If you’re using the `key-v1` encryption method, you’ll need to migrate to the new encryption system as `key-v1` is no longer supported in V7. \[\[memory:96112]] Follow the encryption migration guide here: [Encryption Migration Guide](/docs/cli/migrations/encryption/) ## Configuration Changes [Section titled “Configuration Changes”](#configuration-changes) We recommend adding the following properties in your `capacitor.config` file: * `capacitorUpdater` * `appId` * `version` * `autoUpdate` These settings should help managed better the plugin’s and it’s behaviors. # From V7 to V8 > A detailed guide on transitioning from version 7 to version 8 of Capgo updater, outlining the necessary steps and considerations for a successful upgrade process, ensuring compatibility with Capacitor 8 features and improvements. ## Why this upgrade [Section titled “Why this upgrade”](#why-this-upgrade) This major version is here to follow Capacitor major version 8 First follow the migration guide of Capacitor: [https://capacitorjs.com/docs/updating/8-0](https://capacitorjs.com/docs/updating/8-0/) ## iOS Minimum Version Requirement [Section titled “iOS Minimum Version Requirement”](#ios-minimum-version-requirement) The iOS minimum deployment target has been bumped to **15** to ensure that iOS devices with [CVE-2022-36943](https://nvd.nist.gov/vuln/detail/CVE-2022-36943) are excluded. This is the minimum version of the iOS zip library that has the security fix implemented. ## Install [Section titled “Install”](#install) `npm i @capgo/capacitor-updater@8` Then sync the native code update: `npx cap sync` That’s it! Pretty easy! ## What’s New in V8 [Section titled “What’s New in V8”](#whats-new-in-v8) Version 8 of capacitor-updater brings full compatibility with Capacitor 8, ensuring your app can leverage the latest mobile OS features and improvements. ### Key Updates [Section titled “Key Updates”](#key-updates) * **Capacitor 8 Compatibility**: Full support for Capacitor 8’s enhanced native features * **Performance Improvements**: Optimized update delivery and installation process * **Enhanced Stability**: Bug fixes and stability improvements from v7 * **Maintained API Compatibility**: No breaking changes to the plugin API from v7 ## Configuration [Section titled “Configuration”](#configuration) The configuration remains the same as v7. Your existing `capacitor.config` settings will continue to work: ```typescript { plugins: { CapacitorUpdater: { appId: 'your-app-id', version: '1.0.0', autoUpdate: true, // ... other settings } } } ``` ## Migration Checklist [Section titled “Migration Checklist”](#migration-checklist) * [ ] Follow Capacitor’s v8 [migration guide](https://capacitorjs.com/docs/updating/8-0), check for breaking changes. * [ ] Bump iOS minimum deployment target to 15 (required for CVE-2022-36943 fix) * [ ] [Update](#install) @capgo/capacitor-updater to ^8.0.0 * [ ] [Run](#install) `npx cap sync` * [ ] Test your app thoroughly on both iOS and Android ## Need Help? [Section titled “Need Help?”](#need-help) If you encounter any issues during the migration, please: 1. Check our [documentation](/docs/live-updates/) 2. Visit our [Discord community](https://discord.com/invite/VnYRvBfgA6) 3. Open an issue on [GitHub](https://github.com/Cap-go/capacitor-updater/issues) # Introduction > Introduction to the Capgo webapp, learn how to use the web app of Capgo, to manage your updates and understand what happen with it ## What is this? [Section titled “What is this?”](#what-is-this) Capgo has an extensive webapp to help you manage your projects. This webapp is built with Vue.js and is open source. You can find the source code on [GitHub](https://github.com/Cap-go/capgo/tree/main/src/)\ The webapp is available [here](https://console.capgo.app/).\ This webapp allows you to [manage channels](/docs/webapp/channels/), [manage versions](/docs/webapp/bundles/), [manage devices](/docs/webapp/devices/), [inspect logs](/docs/webapp/logs/), [configure organization security](/docs/webapp/organization-security/), [manage billing](/docs/webapp/settings/) and [manage your account](/docs/webapp/settings/). ## How do I use it? [Section titled “How do I use it?”](#how-do-i-use-it) First, you need to create an account or log in. This is relatively simple and can be done by clicking the `Log in` button in the center of the screen. After you have logged in you will be redirected to the dashboard. This is the main page of the webapp. From here you can navigate to all the other pages. ![login page](/login.webp) ## Creating an account [Section titled “Creating an account”](#creating-an-account) You have to click on the `Create a free account` in the bottom part of the login form. From then it will be as simple as filling a form and following the instructions. ![create account](/create-account.webp) # 2FA Enforcement > Learn how to enforce Two-Factor Authentication (2FA) for all members of your organization to enhance security and protect your apps. Tip This page covers 2FA enforcement in detail. For an overview of all organization security features including password policies and API key controls, see [Organization Security](/docs/webapp/organization-security/). Two-Factor Authentication (2FA) enforcement allows organization administrators to require all members to have 2FA enabled on their accounts before accessing organization resources. This ensures a higher level of security for your apps and data. ## Overview [Section titled “Overview”](#overview) When 2FA enforcement is enabled for an organization: * All members must have 2FA enabled on their Capgo account * Members without 2FA will be denied access to the organization’s apps * Both the web dashboard and CLI will enforce this requirement * New members must enable 2FA before they can access organization resources Tip 2FA enforcement is particularly important for: * Enterprise organizations with strict security policies * Teams handling sensitive user data * Organizations in regulated industries (healthcare, finance, etc.) * Companies requiring SOC 2 or ISO 27001 compliance ## How It Works [Section titled “How It Works”](#how-it-works) ### Web Dashboard [Section titled “Web Dashboard”](#web-dashboard) When you try to access an organization that requires 2FA, and you don’t have it enabled: 1. You’ll see an access denied message 2. You’ll be directed to enable 2FA in your account settings 3. Once enabled, you can access the organization normally ### CLI Access [Section titled “CLI Access”](#cli-access) When using the Capgo CLI to interact with apps in an organization that requires 2FA: ```plaintext 🔐 Access Denied: Two-Factor Authentication Required ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ This organization requires all members to have 2FA enabled. To regain access: 1. Go to https://web.capgo.app/settings/account 2. Enable Two-Factor Authentication on your account 3. Try your command again ``` ## Enabling 2FA Enforcement [Section titled “Enabling 2FA Enforcement”](#enabling-2fa-enforcement) Caution Before enabling 2FA enforcement: * Ensure you have 2FA enabled on your own account * Notify all organization members to enable 2FA * Members without 2FA will immediately lose access ### Via Web Dashboard [Section titled “Via Web Dashboard”](#via-web-dashboard) 1. Navigate to your organization settings 2. Go to the **Security** section 3. Toggle **Require 2FA for all members** 4. Confirm the action ### Via CLI [Section titled “Via CLI”](#via-cli) You can enable 2FA enforcement using the Capgo CLI: ```shell # Enable 2FA enforcement for an organization npx @capgo/cli organization set YOUR_ORG_ID --enforce-2fa # Disable 2FA enforcement npx @capgo/cli organization set YOUR_ORG_ID --no-enforce-2fa ``` When enabling via CLI, you’ll be shown: * Which members don’t have 2FA enabled * A warning if you yourself don’t have 2FA enabled * A confirmation prompt before applying the change ## Checking Member 2FA Status [Section titled “Checking Member 2FA Status”](#checking-member-2fa-status) ### Via CLI [Section titled “Via CLI”](#via-cli-1) You can list all organization members and their 2FA status: ```shell npx @capgo/cli organization members YOUR_ORG_ID ``` This will display: * Member email and role * Whether they have 2FA enabled * A summary of how many members need to enable 2FA ### Via Web Dashboard [Section titled “Via Web Dashboard”](#via-web-dashboard-1) In your organization settings, you can see whether each member has 2FA enabled. ## Setting Up 2FA on Your Account [Section titled “Setting Up 2FA on Your Account”](#setting-up-2fa-on-your-account) If you need to enable 2FA on your account, see our [Two-Factor Authentication setup guide](/docs/webapp/mfa/). ## Best Practices [Section titled “Best Practices”](#best-practices) ### Before Enabling Enforcement [Section titled “Before Enabling Enforcement”](#before-enabling-enforcement) * **Communicate in advance**: Give members at least a week’s notice before enabling enforcement * **Provide support**: Share the [2FA setup guide](/docs/webapp/mfa/) with your team * **Check readiness**: Use `npx @capgo/cli organization members` to see who still needs to enable 2FA ### After Enabling Enforcement [Section titled “After Enabling Enforcement”](#after-enabling-enforcement) * **Monitor access issues**: Be available to help members who get locked out * **Keep backup codes**: Remind members to save their 2FA backup codes * **Review regularly**: Periodically check that all members maintain 2FA ### For CI/CD Pipelines [Section titled “For CI/CD Pipelines”](#for-cicd-pipelines) * **Use API keys**: CI/CD systems should use API keys, not user accounts * **API key owners**: Ensure the user who created CI/CD API keys has 2FA enabled * **Rotate keys**: Regularly rotate API keys used in automated systems ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) ### ”Access Denied: Two-Factor Authentication Required” [Section titled “”Access Denied: Two-Factor Authentication Required””](#access-denied-two-factor-authentication-required) **Problem**: You’re seeing this error when trying to access an organization. **Solution**: 1. Go to [Account Settings](https://web.capgo.app/settings/account) 2. Enable 2FA on your account 3. Try accessing the organization again ### ”Cannot enable 2FA enforcement” [Section titled “”Cannot enable 2FA enforcement””](#cannot-enable-2fa-enforcement) **Problem**: You can’t enable 2FA enforcement for your organization. **Solution**: * Ensure you have `super_admin` rights in the organization * Enable 2FA on your own account first * Contact support if the issue persists ### CLI Commands Failing [Section titled “CLI Commands Failing”](#cli-commands-failing) **Problem**: CLI commands fail with 2FA-related errors. **Solution**: * Verify your API key is valid: `npx @capgo/cli doctor` * Ensure the API key owner has 2FA enabled * Re-authenticate if using login-based auth: `npx @capgo/cli login` ## Compliance [Section titled “Compliance”](#compliance) 2FA enforcement helps your organization meet various compliance requirements: | Standard | Requirement | How 2FA Helps | | ------------- | ----------------------- | ---------------------------------------------- | | **SOC 2** | Access controls | Ensures strong authentication for all users | | **ISO 27001** | Information security | Adds a layer of identity verification | | **HIPAA** | Access management | Protects against unauthorized access | | **GDPR** | Data protection | Reduces risk of account compromise | | **PCI DSS** | Authentication controls | Meets multi-factor authentication requirements | ## Next Steps [Section titled “Next Steps”](#next-steps) * [Set up 2FA on your account](/docs/webapp/mfa/) * [Learn about organization management](/docs/webapp/organization-system/) * [Configure API keys for CI/CD](/docs/webapp/api-keys/) # Api keys > How to manage api keys for Capgo cloud, learn what they use for, how to create, edit or roll them to ensure security with your account ## What can I do with the api keys? [Section titled “What can I do with the api keys?”](#what-can-i-do-with-the-api-keys) An API key can be regenerated, or removed or a new one can be added. You can also set expiration dates and create encrypted keys for enhanced security. ## How to see all api keys? [Section titled “How to see all api keys?”](#how-to-see-all-api-keys) You need to go to [API keys page](https://console.capgo.app/dashboard/apikeys/) and there you will see all your api keys. ## How to add a new api key? [Section titled “How to add a new api key?”](#how-to-add-a-new-api-key) To add a new API key click on the little plus button ![add apikey](/apikeys-add.webp) and then select the permissions you want to give to the new API key. You can select: * Read - the API key will be able to read all the data * Upload - the API key will be able to upload new versions from the CLI and read * All - The API key will be able to do everything ![select perm](/apikeys-select-perm.webp) ## Encrypted API Keys [Section titled “Encrypted API Keys”](#encrypted-api-keys) When creating a new API key, you can choose to create an **encrypted key**. Encrypted keys provide enhanced security: * The key is hashed using SHA-256 before being stored in the database * **The plain key is only shown once** when you create it - make sure to copy and save it immediately * After creation, the key cannot be retrieved or viewed again * If you lose an encrypted key, you must create a new one This is the recommended approach for production environments as it ensures that even if the database is compromised, your actual API keys remain secure. ## API Key Expiration [Section titled “API Key Expiration”](#api-key-expiration) You can set an **expiration date** for your API keys. This is a security best practice that limits the window of exposure if a key is compromised. ### Setting an expiration date [Section titled “Setting an expiration date”](#setting-an-expiration-date) When creating or editing an API key, you can optionally set an expiration date. Once the expiration date passes: * The API key will no longer work for authentication * Any requests using the expired key will be rejected * Expired keys are automatically cleaned up after 30 days ### Organization policies [Section titled “Organization policies”](#organization-policies) Organizations can enforce expiration policies: * **Require expiration**: All API keys must have an expiration date * **Maximum expiration period**: Limit how far in the future an expiration date can be set (e.g., maximum 90 days) * **Enforce secure API keys**: Require all new API keys to be created as encrypted/hashed keys If your organization has these policies enabled, you will be required to set an appropriate expiration date when creating new keys. For more information on configuring organization-wide API key policies, see [Organization Security](/docs/webapp/organization-security/). ## How to remove an api key? [Section titled “How to remove an api key?”](#how-to-remove-an-api-key) To remove an API key click on the little trash button. ![remove apikey](/apikeys-remove.webp) And then confirm removing the API key. ## How to regenerate an api key? [Section titled “How to regenerate an api key?”](#how-to-regenerate-an-api-key) To regenerate an API key click on the little refresh button. ![generate apikey](/apikeys-regenerate.webp) And then confirm regenerating the API key. # Bundles > Learn how to manage bundles in Capgo. A bundle is a specific version of your application's code and assets. Discover how to view, link to channels, download, and delete bundles. ## Show all bundles [Section titled “Show all bundles”](#show-all-bundles) In Capgo, a bundle represents a specific version of your application’s code and assets, ready to be distributed to devices. First, let’s take a look at the bundles page. You can access it by [clicking on your app](/docs/webapp/main-page) and then [clicking on the bundles tab](/docs/webapp/main-app-page). ![bundle list](/bundles.webp) The bundles list displays: * **Name**: The bundle version number * **Created at**: When the bundle was uploaded * **Channel**: Which channel (if any) the bundle is currently linked to * **Size**: The bundle file size * **Action**: Quick actions including link to channel (gear icon) and delete (trash icon) You can use the **Reload** button to refresh the list, **+ Add** to upload a new bundle, and **Filters** to narrow down the displayed bundles. A search box allows you to find bundles by name. ## Delete a bundle [Section titled “Delete a bundle”](#delete-a-bundle) There are two ways a bundle can be deleted: * Normally * Unsafely The Unsafe way of deleting a bundle was added into Capgo on August 12, 2024. The difference between the two ways is the ability to reuse the version number after the deletion. For example, if you delete a version `1.0.0` the normal way and later try to upload a `1.0.0` version, it will fail. If you delete this version via the unsafe delete, you will be able to upload a `1.0.0` version. Danger Deleting a version unsafely and re-uploading it is REALLY dangerous. It can cause all sorts of bugs and unpredictable behaviour in the plugin. It should NEVER be used for bundles that have been used in a public channel. It is for this reason that deleting a version unsafely requires “super\_admin” privileges ## Managing a specific bundle [Section titled “Managing a specific bundle”](#managing-a-specific-bundle) Once you see the list of all bundles click on the one you want to manage. After you do that you should see something like this: ![bundle info](/bundle-info.webp) The bundle detail page has three tabs: 1. **Information**: Shows all bundle metadata and properties 2. **Dependencies**: Lists the dependencies included in this bundle 3. **History**: Shows the history of changes for this bundle ### Bundle Information [Section titled “Bundle Information”](#bundle-information) The Information tab displays the following details: * **Bundle number**: The version identifier (e.g., 12.87.1) * **ID**: The unique internal identifier for this bundle * **Created at**: When the bundle was first uploaded * **Updated at**: When the bundle was last modified * **Checksum**: The bundle’s integrity hash (click to copy) * **Channel**: The channel this bundle is linked to (click to open channel or change it) * **Encryption**: Whether the bundle is encrypted * **CLI version**: The version of Capgo CLI used to upload this bundle * **Zip app bundle**: The compressed bundle size (click to download) * **Manifest**: The manifest file size * **Status**: Whether the bundle is Active or Inactive (click trash icon to delete) ### Linking a bundle to a channel [Section titled “Linking a bundle to a channel”](#linking-a-bundle-to-a-channel) Click on the gear icon next to the Channel row or in the Action column to open the channel linking modal: ![bundle change](/bundle-change.webp) The modal provides: 1. **Search channel**: Filter available channels by name 2. **Current bundle**: Shows the bundle version being linked 3. **Available channels**: List of all channels with their details (ID, visibility, platforms, creation date). Channels marked as “current” indicate where this bundle is already linked. Public channels show a link icon. 4. **Set bundle to channel**: Confirms the selection and assigns this bundle to the selected channel **Set bundle to channel:** Assigns this bundle as the active version for a chosen channel. Devices subscribed to that channel will then receive this bundle. To open a channel’s dedicated page, click on the channel name link in the Channel row. To unlink a bundle from a channel, navigate to the channel’s configuration page. ### Downloading a bundle [Section titled “Downloading a bundle”](#downloading-a-bundle) To download a bundle, click on the **Zip app bundle** size value in the Information tab. A confirmation dialog will appear, and upon confirmation, the bundle will be downloaded directly to your device. # Channels > Channels are a way to manage your app's updates. Each channel can have one version. This allows you to have multiple versions of your app in production at the same time. ## How Capgo chooses a channel (precedence) [Section titled “How Capgo chooses a channel (precedence)”](#how-capgo-chooses-a-channel-precedence) When a device asks Capgo for an update, the channel it will use is decided in the following order (highest priority first): 1. **Forced device mapping**: If the device ID is explicitly forced to a channel (see the *Forced devices* list inside the channel settings), that channel always wins. 2. **Cloud override (created by `setChannel()` or Webapp action)**: Calling [`setChannel`](/docs/plugin/api/#setchannel) (or changing a device’s channel in the dashboard) writes a persistent override in the cloud tied to that device ID. That override is consulted after forced mapping but before any defaults. Re‑installing the app does **not** clear it; deleting the device entry does. 3. **Capacitor config `defaultChannel` (test build default)**: For internal / beta / test builds you can set `defaultChannel` (legacy key `channel`) in `capacitor.config.*` so test devices start on a pre-release channel (e.g. `beta`, `pr-123`). If absent, the device will proceed to the cloud default. Production builds usually leave this unset. 4. **Cloud Default Channel (primary strategy for \~99% of users)**: The main production channel virtually all real users land on. Any new device without a force, without an override, and without a config `defaultChannel` uses this. Changing it rolls out (or rolls back) for everyone in seconds—no new binary. Why the **cloud default** is the main path: * Instant rollout or rollback without rebuilding or re‑publishing native binaries. * One place to manage iOS, Android, and Electron behavior. * Safer: you can confirm bundles exist and settings are correct before switching default. * Auditable changes (team members can see who changed what in the UI / logs). Design principle: Layers above (force / override / config) are *exceptions* (debug single user, QA switching, test build defaults). Normal users flow to the cloud default. Changing the *cloud default* channel affects *new* normal devices that: * Are not forced * Do not already have a cloud override * Do not have an app-level `defaultChannel` defined If a test build ships with `defaultChannel: 'beta'` and you later change the cloud default to `production`, devices that started on `beta` via the config stay there until you: (a) override them with `setChannel()`, (b) force them, or (c) delete the device entry. Devices stay on their current channel unless you: * Force them to another channel. * Call `setChannel()` (creating/replacing the cloud override) or change it manually in the dashboard. * Remove / archive the channel they are on (then they will fall back through the precedence again at next check). If a channel is disabled for a platform (see iOS / Android / Electron toggles) and would otherwise have been selected, the selection skips it and falls back to the next rule. > Note: Setting `defaultChannel` means changing it requires a new binary; use it intentionally for test/QA, not for general production control. ### Capacitor config example [Section titled “Capacitor config example”](#capacitor-config-example) capacitor.config.ts ```ts // Example: a TestFlight or internal QA build defaults to the beta channel. const config = { plugins: { Capgo: { defaultChannel: 'beta', // Test build default. Omit in production so users attach to cloud default. // legacy key: channel }, }, }; export default config; ``` If you later change the dashboard default to `production`, devices already on another channel (via config, override, or force) will NOT move automatically; only fresh devices (or those whose override/force you clear) pick it up. *** ## Managing channels [Section titled “Managing channels”](#managing-channels) First, let’s take a look at the channels page. You can access it by [clicking on your app](/docs/webapp/main-page) and then [clicking on the channels tab](/docs/webapp/main-app-page). ![channel list](/channels.webp) ## Creating a channel [Section titled “Creating a channel”](#creating-a-channel) As you can see, there exists a plus button in the lower right corner. (`1` in the image) Clicking on it will open a modal where you can create a new channel. ![new channel](/new_channel_modal.webp) Then after you click on `Add` a new channel should appear in the list. ![after channel create](/post-channel-create.webp) ## What does misconfigured mean? [Section titled “What does misconfigured mean?”](#what-does-misconfigured-mean) Sometimes the configuration of a channel is not valid. In that case, you will get a big warning and the `Misconfigured` column will say `Yes` for one or more of the channels. You can learn more about it [here](/docs/cli/commands/#disable-updates-strategy) ## Deleting a channel [Section titled “Deleting a channel”](#deleting-a-channel) Deleting a channel is straight forward. Just click on the trash icon and confirm the deletion. (`2` in the image) ## Managing a channel [Section titled “Managing a channel”](#managing-a-channel) Clicking on the channel name will open a modal where you can manage the channel settings. (`3` in the image) ![Channel settings](/channel_settings.webp) The channel settings page contains all the configuration options for your channel. Let’s go through each setting. *** First the `Default channel` toggle. When enabled, this channel becomes the default for new devices. For a comprehensive explanation of how default channels work, including how to set up platform-specific defaults (one for iOS, one for Android, and one for Electron), see the [Default Channel Configuration](/docs/webapp/main-app-page/#default-channel-configuration) section. *** Second the `IOS` setting. This is relatively simple. If this is false then IOS devices will not be allowed to download updates from this channel. Third is the `Android` setting. This is similar to `IOS`. If this is false then Android devices will not be allowed to download updates from this channel. Fourth is the `Electron` setting. This is similar to `IOS` and `Android`. If this is false then Electron apps will not be allowed to download updates from this channel. Fifth is the `Disable auto downgrade under native` setting. If this is true then it will be impossible to downgrade from a native version. This means that if you have uploaded a `1.2.0` version to the app store or play store and try to set the channel version to `1.1.0` then the update (downgrade) will fail. Sixth is the `Disable auto update`. This setting is quite complex, and you can learn more about it [here](/docs/cli/commands/#disable-updates-strategy) As for `Allow development build`. If this is true then development builds will be allowed to download updates from this channel. If not then any update request that has the `prod` set to false will be rejected. This is mostly useful for testing purposes. Seventh is the `Allow Emulators`. If this is false then Capgo will disallow any update request that comes from an emulator. This is mostly useful for testing purposes. Eight is the `Allow devices to self associate`. If this is true then the [setChannel](/docs/plugin/api/#setchannel) method will be available. If this is set to false and you try to call the [setChannel](/docs/plugin/api/#setchannel) method with this channel then the call will fail. # Devices > A comprehensive guide on utilizing the devices page to manage and configure your devices effectively, including filtering options and detailed configuration settings for individual devices. Data Retention Device data is retained for **90 days** from the last device activity. Devices that haven’t connected to Capgo within this period will be automatically removed from the list. Active devices are continuously tracked as they check for updates. ## Show the list of all devices [Section titled “Show the list of all devices”](#show-the-list-of-all-devices) First, let’s take a look at the devices page. You can access it by [clicking on your app](/docs/webapp/main-page) and then [clicking on the devices tab](/docs/webapp/main-app-page). ![devices list](/devices.webp) 1. **Devices tab** - Click this tab in the navigation bar to access the devices page 2. **Filters** - Click to filter devices by: * **Override** - Show only devices that have a [custom channel](/docs/plugin/api/#setchannel) or a custom version * **Custom ID** - Show only devices with a custom identifier set ## Configuring a device [Section titled “Configuring a device”](#configuring-a-device) To configure a specific device click on it in the table and then you should see something like this: ![show one device](/device-specific.webp) 1. **Information** - View device details including Device ID, Last update, Platform, Plugin version, Version, Version builtin, OS version, and whether it’s a Production app 2. **Deployments** - View the deployment history for this device 3. **Logs** - View logs associated with this device 4. **Channel override** - Override the channel for this specific device. If set, this device will not use the public (default) channel and will only receive updates from the selected channel ### Custom ID [Section titled “Custom ID”](#custom-id) The `Custom ID` is a very useful field that helps you connect the device to an ID you can recognize for your own users. Use it to easily identify which device belongs to which user. Custom ID can only be set from the device itself using `CapacitorUpdater.setCustomId({ customId: user });` # Logs > Understand and utilize the application logs of Capgo to diagnostics and track the action of your devices with the updates. ## Understanding Application Logs [Section titled “Understanding Application Logs”](#understanding-application-logs) The Logs page provides a detailed history of update events and diagnostic information for your application. This is crucial for monitoring the update process, troubleshooting issues, and understanding how your devices interact with Capgo. You can access it by [clicking on your app](/docs/webapp/main-page) and then [clicking on the “Logs” tab (previously named “updates” in some older screenshots or documentation)](/docs/webapp/main-app-page). From there you should see a page similar to this, displaying a list of log entries: ![Logs page overview showing the main interface](/logs.webp) Logs Page Overview The logs page interface includes: 1. **Logs Tab** - The navigation tab to access the logs view 2. **Reload Button** - Refresh the logs list with the latest data 3. **Time Range & Actions Filters** - Filter logs by date range and action type (see sections below) Each row shows: * **Timestamp** (UTC) * **Device ID** * **Action code** (what happened) * **Version name** (bundle or `builtin`) Click a row to jump to the device detail page for the full history. ### Filtering by Date Range [Section titled “Filtering by Date Range”](#filtering-by-date-range) You can filter logs by a specific time period using the date picker: ![Date range picker for filtering logs](/logs_date_range.webp) Date Range Filter 1. **Quick Presets** - Select common time ranges: Last 1h, 3h, 6h, or 12h 2. **Start Time** - Set a custom start time for the range 3. **End Time** - Set a custom end time for the range 4. **Calendar** - Pick specific dates using the calendar view Click “Select” to apply your chosen date range, or “Cancel” to dismiss the picker. ### Filtering by Action Type [Section titled “Filtering by Action Type”](#filtering-by-action-type) The Actions dropdown lets you filter logs by specific event types: ![Actions filter dropdown for filtering logs by event type](/logs_filters.webp) Actions Filter Available action filters include: * **Device heartbeat** - Periodic health checks from devices * **Version deletion requested** - When a bundle version is deleted * **Reset to default version** - When a device reverts to the builtin bundle * **Version installed successfully** - Successful bundle installation * **New version sent to device** - When Capgo sends an update to a device * **Version installation failed** - When bundle installation fails Use the search box at the top to quickly find specific action types. You can select multiple actions to show logs matching any of the selected types. ### Sample log snippet (fake data) [Section titled “Sample log snippet (fake data)”](#sample-log-snippet-fake-data) | Time (UTC) | Device ID | Action | Version | What it tells you | | ------------------- | --------- | -------------------------- | ------- | --------------------------------------------------------- | | 2025-01-14 10:00:01 | `A1B2C3` | `get` | 2.4.1 | Device asked Capgo if an update is available | | 2025-01-14 10:00:03 | `A1B2C3` | `download_manifest_start` | 2.4.1 | Manifest fetch kicked off; SDK is about to download files | | 2025-01-14 10:00:07 | `A1B2C3` | `download_40` | 2.4.1 | Bundle download is 40% complete | | 2025-01-14 10:00:12 | `A1B2C3` | `download_zip_complete` | 2.4.1 | Zip finished downloading | | 2025-01-14 10:00:13 | `A1B2C3` | `set` | 2.4.1 | Bundle installed and marked as next to run | | 2025-01-14 10:05:00 | `B9C8D7` | `disableAutoUpdateToMajor` | 1.9.0 | Channel policy blocked a jump to 2.x | | 2025-01-14 10:05:05 | `B9C8D7` | `rateLimited` | builtin | Device hit the request limit; SDK backs off until restart | ## Example Log Scenarios [Section titled “Example Log Scenarios”](#example-log-scenarios) To help you understand what the logs tell you, here are example sequences showing real device update journeys: ### Successful Update Flow [Section titled “Successful Update Flow”](#successful-update-flow) This is what a healthy update looks like in your logs: | Time | Device ID | Action | Version | What it means | | -------- | ---------- | ------------------- | ------- | ---------------------------------------------------------- | | 10:00:01 | `a1b2c3d4` | `get` | 1.2.0 | Device checked for updates and received version 1.2.0 info | | 10:00:02 | `a1b2c3d4` | `download_10` | 1.2.0 | Download started, 10% complete | | 10:00:03 | `a1b2c3d4` | `download_50` | 1.2.0 | Download at 50% | | 10:00:05 | `a1b2c3d4` | `download_complete` | 1.2.0 | Download finished successfully | | 10:00:06 | `a1b2c3d4` | `set` | 1.2.0 | Bundle installed and activated | ### Device Already Up-to-Date [Section titled “Device Already Up-to-Date”](#device-already-up-to-date) When a device checks but already has the latest version: | Time | Device ID | Action | Version | What it means | | -------- | ---------- | ------- | ------- | --------------------------------------------------------- | | 14:30:00 | `e5f6g7h8` | `noNew` | 1.2.0 | Device is already on the latest version, no update needed | ### Failed Update with Rollback [Section titled “Failed Update with Rollback”](#failed-update-with-rollback) When an update fails and the device rolls back: | Time | Device ID | Action | Version | What it means | | -------- | ---------- | ------------------- | ------- | -------------------------------------------------------------------- | | 11:15:00 | `i9j0k1l2` | `get` | 1.3.0 | Device received update info | | 11:15:02 | `i9j0k1l2` | `download_complete` | 1.3.0 | Download completed | | 11:15:03 | `i9j0k1l2` | `set` | 1.3.0 | Bundle was set | | 11:15:10 | `i9j0k1l2` | `update_fail` | 1.3.0 | App crashed or `notifyAppReady()` wasn’t called - rollback triggered | | 11:15:11 | `i9j0k1l2` | `reset` | builtin | Device reverted to the built-in version | **Action needed**: Check that your app calls `notifyAppReady()` after successful initialization. See [the plugin documentation](/docs/plugin/api/#notifyappready) for details. ### Download Failure [Section titled “Download Failure”](#download-failure) When network issues prevent the download: | Time | Device ID | Action | Version | What it means | | -------- | ---------- | --------------- | ------- | -------------------------------------------------------- | | 09:45:00 | `m3n4o5p6` | `get` | 1.2.0 | Device received update info | | 09:45:01 | `m3n4o5p6` | `download_30` | 1.2.0 | Download started but… | | 09:45:15 | `m3n4o5p6` | `download_fail` | 1.2.0 | Download failed (network timeout, connection lost, etc.) | **Action needed**: The device will retry automatically on next app launch. No action required unless this happens frequently. ### Plan Limit Reached [Section titled “Plan Limit Reached”](#plan-limit-reached) When your account reaches its device limit: | Time | Device ID | Action | Version | What it means | | -------- | ---------- | ----------------- | ------- | ------------------------------------------------------------------------------- | | 16:00:00 | `q7r8s9t0` | `needPlanUpgrade` | - | This device won’t receive updates until you upgrade or the billing cycle resets | **Action needed**: [Upgrade your plan](/docs/webapp/settings/) or wait for the next billing cycle. ### Channel Configuration Blocking Updates [Section titled “Channel Configuration Blocking Updates”](#channel-configuration-blocking-updates) When channel settings prevent an update: | Time | Device ID | Action | Version | What it means | | -------- | ---------- | -------------------------- | ------- | --------------------------------------------------------------------- | | 12:00:00 | `u1v2w3x4` | `disableAutoUpdateToMajor` | 2.0.0 | Device on v1.x can’t auto-update to v2.x (major version jump blocked) | | 12:05:00 | `y5z6a7b8` | `disableEmulator` | 1.2.0 | Emulator detected, and channel blocks emulators | | 12:10:00 | `c9d0e1f2` | `disableDevBuild` | 1.2.0 | Dev build detected, and channel blocks dev builds | **Action needed**: These are intentional protections. If you want to allow these updates, modify your [channel settings](/docs/webapp/channels/). ## Log codes (Capgo backend enum) [Section titled “Log codes (Capgo backend enum)”](#log-codes-capgo-backend-enum) These codes come from the `stats_action` enum used by the dashboard API (`capgo/src/types/supabase.types.ts`). If you see a new code in the UI, it was emitted by the SDK or backend and validated against this list. **Happy path & lifecycle** | Code(s) | Meaning | | ------------------------------------------------------- | -------------------------------------------------------------------- | | `get` | Device asked Capgo for the current channel manifest | | `download_manifest_start`, `download_manifest_complete` | Manifest download began / finished (for delta or multi-file bundles) | | `download_zip_start`, `download_zip_complete` | Zip archive download began / finished | | `download_10` … `download_90` | Download progress milestones | | `download_complete` | Entire bundle downloaded | | `set` | Bundle staged for next launch | | `reset` | Device reverted to the builtin bundle | | `delete` | Bundle removed from local storage | | `uninstall` | App uninstall detected | | `app_moved_to_foreground`, `app_moved_to_background` | App lifecycle events recorded by SDK | | `ping` | Health/heartbeat check from device | | `setChannel`, `getChannel` | Channel overridden or fetched via SDK call | **Configuration or policy blocks** | Code(s) | Why the update was blocked | | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------- | | `disableAutoUpdate`, `disableAutoUpdateToMajor`, `disableAutoUpdateToMinor`, `disableAutoUpdateToPatch`, `disableAutoUpdateMetadata`, `disableAutoUpdateUnderNative` | Channel strategy forbids this semver jump | | `disablePlatformIos`, `disablePlatformAndroid` | Platform is disabled on the channel | | `disableDevBuild`, `disableEmulator` | Dev builds or emulators not allowed | | `cannotUpdateViaPrivateChannel`, `NoChannelOrOverride`, `channelMisconfigured` | Channel selection or override failed | | `missingBundle`, `cannotGetBundle` | Manifest refers to a bundle Capgo cannot serve | | `needPlanUpgrade` | Org hit its plan/device limit | | `rateLimited` | Too many requests; SDK throttles until restart | | `blocked_by_server_url`, `backend_refusal`, `InvalidIp` | Server-side rule blocked the request | **Download / integrity / install failures** | Code(s) | Meaning | | ------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | | `download_fail` | Bundle download failed (network or response error) | | `download_manifest_file_fail`, `download_manifest_checksum_fail`, `download_manifest_brotli_fail` | Manifest file couldn’t be retrieved or validated | | `checksum_fail`, `checksum_required` | Integrity check failed or checksum missing | | `unzip_fail`, `directory_path_fail`, `canonical_path_fail`, `windows_path_fail` | File system or unzip validation failed | | `decrypt_fail` | Decryption failed (encrypted bundle) | | `update_fail` | Bundle installed but app never called `notifyAppReady()`; rollback triggered | | `download_zip_*` with no subsequent `set` | Download finished but install phase never completed | ➡️ Need deeper per-code guidance? See **[Full Log Code Reference and Debugging Guide](/docs/plugins/updater/debugging/#understanding-cloud-logs)**. ## Getting More Details About a Log [Section titled “Getting More Details About a Log”](#getting-more-details-about-a-log) If you click on a specific log entry, it will typically take you to the [device’s page](/docs/webapp/devices/). This allows you to see the full history for that particular device, which can be very helpful for diagnosing device-specific issues or understanding its update journey. # App Page > What does the app page of the Capgo webapp shows? And what you can do with it. ## What does the app page shows? [Section titled “What does the app page shows?”](#what-does-the-app-page-shows) First, let’s take a look at the main page of the app: In Capgo, an app represents your mobile application integrated with Capgo’s live update system. It allows you to manage updates, channels, and devices seamlessly. ![Main page screenshot](/main-app-page.webp) Let’s take a closer look at this. The main app page is divided into several key areas: 1. **Top Navigation Bar:** Provides access to different sections of your app management: * **Dashboard (1):** The current view, displaying key metrics and summaries. * **Information (2):** Displays your app’s core settings and details (see “App Information” section below). * **[Bundles (3)](/docs/webapp/bundles/):** Manage your app’s versions and releases. * **[Channels (4)](/docs/webapp/channels/):** Configure and manage different update channels (e.g., production, beta). * **[Devices (5)](/docs/webapp/devices/):** View and manage registered devices, including setting specific overrides. * **[Logs (6)](/docs/webapp/logs/):** Access detailed logs and error reports for your app. * **Builds (7):** View and manage your app builds. 2. **Statistics Display:** Visualizes important metrics for the last billing period (data reflects usage from your billing day, not the 1st of the month): * **Monthly Active Users (8):** Tracks the number of unique active users over time. * **Storage (9):** Shows the current storage consumption. * **Bandwidth (10):** Displays the bandwidth usage. * **Active Bundle (11):** Shows the distribution of active devices across different app bundles. 3. **Summary Cards:** Offers a quick overview of key counts: * **Bundle Uploads (12):** Total number of app bundles uploaded. * **Updates Statistics (13):** Total number of updates performed, including requests, installs, and failures. * **Deployment Statistics (14):** Total number of deployments. 4. **Display Options (15-18):** Controls for filtering and viewing the statistics: * **Daily (15):** View statistics on a daily basis. * **Cumulative (16):** View cumulative statistics over time. * **Billing Period (17):** View statistics for the current billing period. * **Date Range (18):** Select a custom date range (e.g., last 30 days). ## App Information [Section titled “App Information”](#app-information) This section corresponds to the “Information” tab (1) in the top navigation bar. Here you can view and manage crucial details and settings for your application. ![App Information Page](/app_info.webp) App Information Page Here’s a breakdown of the available fields and actions: * **App Icon (2):** Displays your application’s icon. You can click the “Change” button to upload a new icon. * **App ID (3):** A unique identifier for your application within Capgo. This ID is not editable. * **App Name (4):** The display name for your application. You can modify this as needed. * **Default Upload Channel (5):** Specifies the default channel to which new bundles will be uploaded. You can click the edit icon to select a different default channel. * **Default Download Channel (6):** Specifies the default channel from which devices will download updates. You can click the edit icon to select a different default channel. See the [Default Channel Configuration](#default-channel-configuration) section below for detailed information. * **Auto Delete Bundles Not Used (after x seconds) (7):** This setting allows you to automatically delete old bundles that haven’t been used for a specified duration (in seconds). Set to `0` to disable auto-deletion. This helps manage storage and keep your bundle list clean. * **Expose Bundle Metadata to Plugin (8):** When enabled, bundle link and comment fields will be sent to the Capacitor Updater plugin. This feature requires plugin version 7.35.0 or higher. * **Transfer App Ownership (9):** This section provides an option to initiate the transfer of your application to a different organization you are a part of. * **Delete App Button:** Permanently deletes your application from Capgo. This action is irreversible and will remove all associated data, bundles, channels, and devices. * **Update Button:** Saves any changes you’ve made to the editable fields on this page (e.g., App Name, Default Upload Channel, Auto Delete Bundles setting). ## Default Channel Configuration [Section titled “Default Channel Configuration”](#default-channel-configuration) The **Default Download Channel** is one of the most important settings for your app. It determines which channel new devices will receive updates from when they first connect to Capgo. ### How Default Channels Work [Section titled “How Default Channels Work”](#how-default-channels-work) When a device requests an update from Capgo, the system determines which channel to use based on the following precedence (highest priority first): 1. **Forced device mapping**: If the device ID is explicitly forced to a channel in the channel settings, that channel always wins. 2. **Cloud override**: If the device has been assigned to a channel via `setChannel()` or manually in the dashboard, that override is used. 3. **Capacitor config `defaultChannel`**: If set in your `capacitor.config.*` file, this is used for test/beta builds. 4. **Default Download Channel**: The setting configured here—this is what \~99% of your production users will use. ### Platform-Specific Default Channels [Section titled “Platform-Specific Default Channels”](#platform-specific-default-channels) You can configure platform-specific default channels—for example one for iOS, one for Android, and one for Electron. This is useful when: * You want to roll out updates to one platform before the other * You need different update strategies per platform * You’re testing a new version on one platform while keeping the other stable To set up platform-specific defaults: 1. Create separate channels for each platform (for example `production-ios`, `production-android`, and `production-electron`) 2. In each channel’s settings, enable only the relevant platform (iOS, Android, or Electron toggle) 3. Mark both channels as “Default” - Capgo allows this when channels target different platforms When a device requests an update: * iOS devices will receive updates from the iOS-enabled default channel * Android devices will receive updates from the Android-enabled default channel ### Single Default Channel (Recommended for Most Apps) [Section titled “Single Default Channel (Recommended for Most Apps)”](#single-default-channel-recommended-for-most-apps) For most applications, a single default channel that supports all three core platforms is the simplest approach: 1. Create one channel (e.g., `production`) 2. Ensure iOS, Android, and Electron toggles are enabled 3. Mark it as the default channel This ensures consistent behavior across all platforms and simplifies your release workflow. ### Changing the Default Channel [Section titled “Changing the Default Channel”](#changing-the-default-channel) When you change the default channel: * **New devices** will immediately start receiving updates from the new default * **Existing devices** that already have a channel assignment (via override or force) will NOT automatically switch * To move existing devices, you need to either: * Use `setChannel()` to override them programmatically * Force them to the new channel in the dashboard * Delete their device entries (they’ll re-register with the new default) > **Tip**: Always test your new default channel with a small group of forced devices before making it the default for all users. # Home > What does the home page of the Capgo webapp shows? And what you can do with it. ## What does the main page shows? [Section titled “What does the main page shows?”](#what-does-the-main-page-shows) First, let’s take a look at the main page: ![Main page screenshot](/main-page.webp) Let’s start at the beginning. The first thing you see is the **stats**. It contains app-specific statistics. Those updates depend on the usage of the app The second thing you see is the list of all available apps. This will change depending on how many apps have you created. You can click on each app to see more details about it. The third thing you see is the **API keys** button. This button will take you to the [API keys](/docs/webapp/api-keys/) page. The next thing you see is the **your name and picture** button. This button will change depending on your first and second name and it will take you to the [settings](/docs/webapp/settings/) or support page. # Two-factor authentication > Managing two-factor authentication to better protect your capgo account. This doc will help you to make your account the most secure possible, and prevent bad actor sending unwanted bundle to your user ## What is 2FA? [Section titled “What is 2FA?”](#what-is-2fa) 2FA is a security measure designed to prevent a bad actor from taking over your capgo account.\ It achieves it by requiring an additional code. This code is stored on your mobile phone or a hardware 2FA key. It changes every 30 seconds, making it impossible to guess ## Requirements for this guide [Section titled “Requirements for this guide”](#requirements-for-this-guide) * An Android or IOS phone * Internet connection ## What will this guide cover? [Section titled “What will this guide cover?”](#what-will-this-guide-cover) This guide will show how to setup 2FA using `Google Authenticator`. Other apps exist to serve a similar purpose, however I cannot cover the entire topic of 2FA in this guide. ## How to setup 2FA? [Section titled “How to setup 2FA?”](#how-to-setup-2fa) First download the `Google Authenticator` app. If you are on Android,, you can download it from the [play store](https://play.google.com/store/apps/details/?id=com.google.android.apps.authenticator2). On IOS you can download it from the [App store](https://apps.apple.com/us/app/google-authenticator/id388497605/) Second, please go to [capgo’s account settings](https://console.capgo.app/dashboard/settings/account/). There you should see a green button that should look like this ![Button MFA](/button-mfa.webp) Click it, and once you do, a QR code should appear ![Enable MFA](/enable-mfa.webp) Danger ⚠️ Important Note:\ Never share this QR code with anyone, or you could get logged out of your account Next, please open the `Google Authenticator` on your phone. Once you do, follow these steps: Click the plus button ![MFA auth plus](/mfa-auth-plus.webp) After that, please click the camera button ![MFA scan QR](/mfa-scan-qr.webp) That will open the camera preview. Please scan the QR code shown on your PC. After you do that, you should see something like this: ![MFA final code](/mfa-final-code.webp) Then please go back to the PC and click the verify button ![Verify MFA](/enable-mfa-verify.webp) This will open a window to type our 2FA code. In my case, this code is `095101` but it will be different for you.\ After you type this code, please click on the `verify` button ![MFA verify code final](/mfa-verify-final-code.webp) If you entered the correct code and pressed `verify` you should see a pop-up like this ![MFA enabled](/mfa-enabled.webp) Congrats!\ You enabled 2FA authentication 🎉 ## How to login using 2FA [Section titled “How to login using 2FA”](#how-to-login-using-2fa) Next time you login to your account, you will see a window like this ![MFA required](/mfa-required.webp) Please open the authenticator and copy your auth code ![MFA login](/mfa-login.webp) Danger ⚠️ Important Note:\ Press `verify` before the code refresh, otherwise the code will change, and the old one will no longer be valid Then input the code, into the login form and pres the `verify` ![MFA login](/mfa-final-login-verify.webp) If you entered the correct code you should see the capgo dashboard ## How to disable 2FA [Section titled “How to disable 2FA”](#how-to-disable-2fa) To disable 2FA please go to the [capgo’s account settings](https://console.capgo.app/dashboard/settings/account/). There you should see a red button that should look like this: ![MFA disable button](/mfa-disable-button.webp) Click it, and and you should see a screen like this ![MFA disabled](/mfa-disable-2.webp) Just press the `disable` button, and that’s it. # Organization Security > Configure comprehensive security policies for your organization including 2FA enforcement, password policies, and API key controls to protect your apps and data. Capgo provides comprehensive security controls that allow organization administrators to enforce security policies across all members. These features help you meet compliance requirements, protect sensitive data, and maintain a strong security posture. ## Overview [Section titled “Overview”](#overview) The Organization Security settings allow super admins to configure: * **Two-Factor Authentication (2FA) Enforcement** - Require all members to enable 2FA * **Password Policy** - Set password complexity requirements * **API Key Security** - Enforce secure API keys and expiration policies ![Organization Security Settings](/org_security.webp) The Security page is organized into clearly labeled sections: 1. **Security Tab** - Access all security settings from the Organization settings sidebar 2. **2FA Enforcement** - Toggle and status display for two-factor authentication requirements 3. **Password Policy** - Configure password complexity rules for organization members 4. **API Key Policy** - Settings for secure API keys and expiration requirements 5. **API Key Expiration** - Control whether API keys must have expiration dates Note Only users with **super\_admin** role can configure organization security settings. Other members can view their compliance status but cannot modify the policies. ## Accessing Security Settings [Section titled “Accessing Security Settings”](#accessing-security-settings) 1. Navigate to your organization settings by clicking on **Settings** in the sidebar 2. Click on the **Organization** tab at the top of the settings page 3. Select the **Security** tab from the organization navigation bar (highlighted with a shield icon) ## Two-Factor Authentication (2FA) Enforcement [Section titled “Two-Factor Authentication (2FA) Enforcement”](#two-factor-authentication-2fa-enforcement) 2FA enforcement requires all organization members to have two-factor authentication enabled on their accounts. This adds a critical layer of security by requiring both a password and a verification code. ### What Happens When 2FA is Enforced [Section titled “What Happens When 2FA is Enforced”](#what-happens-when-2fa-is-enforced) * Members without 2FA are **immediately blocked** from accessing organization apps * Both the web dashboard and CLI enforce this requirement * New members must enable 2FA before they can access organization resources * The system tracks which members have 2FA enabled in real-time ### Understanding the 2FA Status Panel [Section titled “Understanding the 2FA Status Panel”](#understanding-the-2fa-status-panel) The Security page displays a comprehensive **Members 2FA Status** panel that shows: * **Total Members** - The total number of members in your organization * **2FA Enabled** (green indicator) - Members who have successfully enabled two-factor authentication * **2FA Not Enabled** (orange warning indicator) - Members who still need to set up 2FA When members don’t have 2FA enabled, they appear in a **Members Without 2FA** warning box. This box shows: * Each member’s email address and their role in the organization * A **Copy Email List** button to quickly copy all affected email addresses for communication ### Enabling 2FA Enforcement [Section titled “Enabling 2FA Enforcement”](#enabling-2fa-enforcement) 1. Navigate to **Organization Settings > Security** 2. Locate the **Require 2FA for All Members** section at the top of the page 3. Review the **Members 2FA Status** panel to see which members will be affected 4. If there are members without 2FA, use the **Copy Email List** button to notify them before enabling 5. Toggle the switch next to **Require 2FA for All Members** to enable enforcement 6. The toggle will show **Disabled** or **Enabled** status on the right side Caution Before enabling 2FA enforcement: * Notify affected members in advance and share the [2FA setup guide](/docs/webapp/mfa/) * Give them time to enable 2FA on their accounts * Members without 2FA will lose access immediately when you enable enforcement * Consider reaching out to members listed in the “Members Without 2FA” warning box ### CLI Support for 2FA Enforcement [Section titled “CLI Support for 2FA Enforcement”](#cli-support-for-2fa-enforcement) You can also manage 2FA enforcement via the CLI: ```shell # Enable 2FA enforcement npx @capgo/cli organization set YOUR_ORG_ID --enforce-2fa # Disable 2FA enforcement npx @capgo/cli organization set YOUR_ORG_ID --no-enforce-2fa # Check member 2FA status npx @capgo/cli organization members YOUR_ORG_ID ``` For detailed information about 2FA enforcement, see the [2FA Enforcement guide](/docs/webapp/2fa-enforcement/). ## Password Policy [Section titled “Password Policy”](#password-policy) Password policies allow you to enforce password complexity requirements for all organization members. When a member’s password doesn’t meet the policy requirements, they must update their password before accessing organization resources. The Password Policy section (marked with indicator **3** in the overview image) provides a simple toggle to enforce password requirements across your organization. ### How Password Policy Works [Section titled “How Password Policy Works”](#how-password-policy-works) When you enable the password policy: * All organization members must meet the password complexity requirements * Users who don’t meet the requirements will be locked out until they update their password * The policy applies to all members regardless of their role ### Enabling Password Policy [Section titled “Enabling Password Policy”](#enabling-password-policy) 1. Go to **Organization Settings > Security** 2. Scroll down to find the **Password Policy** section 3. Read the description: “Require organization members to use passwords that meet specific complexity requirements” 4. Toggle the **Enforce Password Policy** switch to enable it 5. The toggle description states: “When enabled, all organization members must meet the password requirements to access the organization” ### Available Password Requirements [Section titled “Available Password Requirements”](#available-password-requirements) | Setting | Description | Range | | ----------------------------- | ---------------------------------------------------------------------- | ---------------- | | **Minimum Length** | Minimum number of characters required | 6-128 characters | | **Require Uppercase** | Password must contain at least one uppercase letter (A-Z) | On/Off | | **Require Number** | Password must contain at least one digit (0-9) | On/Off | | **Require Special Character** | Password must contain at least one special character (!@#$%^&\*, etc.) | On/Off | ### Member Compliance Tracking [Section titled “Member Compliance Tracking”](#member-compliance-tracking) When a password policy is active, you can monitor compliance: * **Total Members**: Number of members in your organization * **Compliant**: Members whose passwords meet the policy requirements * **Non-Compliant**: Members who need to update their passwords Non-compliant members are listed with their email addresses. You can copy the email list to notify them about the policy and required password changes. Tip When enabling a password policy for the first time, members with non-compliant passwords will be prompted to change their password on their next login. They won’t be immediately locked out, but their access may be restricted until they comply. ### Best Practices for Password Policies [Section titled “Best Practices for Password Policies”](#best-practices-for-password-policies) * **Start with reasonable requirements**: A minimum of 10-12 characters with mixed case and numbers provides good security without being overly restrictive * **Communicate changes**: Notify your team before enabling new password requirements * **Allow transition time**: Give members time to update their passwords * **Consider password managers**: Recommend that team members use password managers to generate and store strong passwords ## API Key Security [Section titled “API Key Security”](#api-key-security) Capgo provides two security controls for API keys: enforcing secure (hashed) API keys and requiring expiration dates. The API Key Policy section (marked with indicator **4** in the overview image) is identified by a key icon. ### Enforce Secure API Keys [Section titled “Enforce Secure API Keys”](#enforce-secure-api-keys) The first option in the API Key Policy section is **Enforce Secure API Keys**. When enabled, this setting requires all API keys in your organization to be created using the secure/hashed format. Hashed API keys are more secure because: * The actual key value is never stored on our servers * Only you (and your systems) have access to the full key * Even if our database were compromised, your keys couldn’t be used The toggle description states: “When enabled, only secure (hashed) API keys can access this organization. Plain-text API keys will be rejected.” Note Existing legacy (non-hashed) API keys will continue to work, but members won’t be able to create new non-hashed keys when this policy is enabled. ### Enabling Secure API Keys [Section titled “Enabling Secure API Keys”](#enabling-secure-api-keys) 1. Go to **Organization Settings > Security** 2. Scroll down to find the **API Key Policy** section (look for the key icon) 3. Locate the **Enforce Secure API Keys** toggle 4. Toggle the switch to enable secure API key enforcement 5. Existing keys are not affected; the policy applies to new key creation ### API Key Expiration Policy [Section titled “API Key Expiration Policy”](#api-key-expiration-policy) The second option (marked with indicator **5** in the overview image) is **Require API Key Expiration**. You can require all API keys to have an expiration date, limiting their validity period. This is a security best practice that: * Limits the window of exposure if a key is compromised * Ensures regular key rotation * Helps meet compliance requirements for credential management The toggle description states: “When enabled, all API keys for this organization must have an expiration date” ### Configuring Expiration Policy [Section titled “Configuring Expiration Policy”](#configuring-expiration-policy) 1. Go to **Organization Settings > Security** 2. Find the **API Key Policy** section 3. Locate the **Require API Key Expiration** toggle (below Enforce Secure API Keys) 4. Toggle the switch to enable the expiration requirement 5. Once enabled, set the **Maximum expiration days** (1-365 days) * This limits how far in the future expiration dates can be set * Example: Setting 90 days means keys can expire at most 90 days from creation Caution When you enable the expiration requirement: * New API keys must have an expiration date * Existing keys without expiration continue to work * Consider auditing existing keys and rotating those without expiration ### Recommended API Key Policies [Section titled “Recommended API Key Policies”](#recommended-api-key-policies) | Use Case | Secure Keys | Expiration | Max Days | | ------------------------- | ----------- | ---------- | -------- | | **Development** | Recommended | Optional | 30-90 | | **CI/CD Pipelines** | Required | Required | 90-180 | | **Production** | Required | Required | 30-90 | | **Enterprise/Compliance** | Required | Required | 30-60 | ## Compliance and Auditing [Section titled “Compliance and Auditing”](#compliance-and-auditing) Organization security features help you meet various compliance requirements: | Standard | Relevant Features | | ------------- | ----------------------------------------------------- | | **SOC 2** | 2FA enforcement, password policies, API key controls | | **ISO 27001** | All security features help demonstrate access control | | **HIPAA** | Strong authentication and access management | | **GDPR** | Data protection through access controls | | **PCI DSS** | Multi-factor authentication, strong passwords | ### Monitoring Compliance Status [Section titled “Monitoring Compliance Status”](#monitoring-compliance-status) The Security dashboard provides real-time visibility into: * How many members have 2FA enabled * Password policy compliance across your organization * API key security adoption Use the “Copy email list” feature to easily export lists of non-compliant members for targeted communication. ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) ### ”Access Denied: Security policy not met” [Section titled “”Access Denied: Security policy not met””](#access-denied-security-policy-not-met) **Problem**: A member cannot access the organization. **Solutions**: 1. Check if 2FA is enforced - member needs to [enable 2FA](/docs/webapp/mfa/) 2. Check if password policy is active - member needs to update their password 3. Verify the member’s compliance status in the Security dashboard ### Cannot enable security features [Section titled “Cannot enable security features”](#cannot-enable-security-features) **Problem**: Security toggles are disabled or not responding. **Solutions**: * Ensure you have **super\_admin** role in the organization * Check your network connection * Try refreshing the page * Contact support if the issue persists ### API key creation fails [Section titled “API key creation fails”](#api-key-creation-fails) **Problem**: Cannot create new API keys. **Solutions**: * If secure keys are enforced, ensure you’re using the secure key creation flow * If expiration is required, set an expiration date within the allowed range * Check the maximum expiration days setting ## Next Steps [Section titled “Next Steps”](#next-steps) * [Set up 2FA on your account](/docs/webapp/mfa/) * [Learn about 2FA enforcement details](/docs/webapp/2fa-enforcement/) * [Manage API keys](/docs/webapp/api-keys/) * [Organization management](/docs/webapp/organization-system/) # Organization system > A detailed guide on managing and organizing your teams and applications within your Capgo account, ensuring secure collaboration and efficient access control for all members involved. ## What is the organization system? [Section titled “What is the organization system?”](#what-is-the-organization-system) The organization system is a system in Capgo that allows you to securely share your apps with members of your team. ## Organization Settings Overview [Section titled “Organization Settings Overview”](#organization-settings-overview) ![Organization Settings](/orgs-settings.webp) | Number | Element | Description | | ------ | --------------------- | -------------------------------------------------------------- | | 1 | Settings | Access settings from the sidebar menu | | 2 | Organization Tab | Switch to organization settings (vs personal Account settings) | | 3 | General | View and edit organization name, logo, and management email | | 4 | Members | Manage team members and their permissions | | 5 | Security | Configure security policies (2FA, password requirements) | | 6 | Audit Logs | View activity logs for your organization | | 7 | Plans | View and manage your subscription plan | | 8 | Usage | Monitor your organization’s usage statistics | | 9 | Credits | View and purchase additional credits | | 10 | Webhooks | Configure webhook integrations | | 11 | Billing | Manage billing information and invoices | | 12 | Organization Selector | Switch between different organizations | ### How to access organization settings [Section titled “How to access organization settings”](#how-to-access-organization-settings) 1. Click on **Settings** (1) in the left sidebar 2. Select the **Organization** tab (2) at the top 3. Use the tabs (3-11) to navigate between different settings sections ### How to switch organizations [Section titled “How to switch organizations”](#how-to-switch-organizations) Click on the **Organization Selector** (12) dropdown near the top left of the sidebar to view and switch between organizations you have access to. ## Managing Members [Section titled “Managing Members”](#managing-members) ![Organization Members](/org-show-members.webp) The Members section displays all users who have access to your organization with their: * **Member**: User avatar, name, and email * **Role**: Current permission level (read, upload, write, admin, super admin) * **Actions**: Edit or remove members ### How to invite a new member [Section titled “How to invite a new member”](#how-to-invite-a-new-member) ![Invite Member](/orgs-add-member.webp) 1. Click the **+ Add** button (1) 2. Enter the user’s email address in the modal (2) 3. Click **Invite** ### Selecting permissions [Section titled “Selecting permissions”](#selecting-permissions) ![Select Permissions](/select-perm-orgs.webp) After entering the email, select the permission level for the invited user: * **Read** - View-only access * **Upload** - Can upload new versions * **Write** - Can modify settings and content * **Admin** - Full access except billing and deletion * **Super admin** - Complete organization control ### Complete the invitation [Section titled “Complete the invitation”](#complete-the-invitation) ![Invite Form](/invite_org_user.webp) Fill in the remaining details: * **Email** - Pre-filled from previous step * **Role** - Shows the selected permission level * **First name** - Optional: invitee’s first name * **Last name** - Optional: invitee’s last name * **Captcha** - Verify you are human Click **Send invitation** to complete the process. ## Permission Breakdown [Section titled “Permission Breakdown”](#permission-breakdown) | Permission | Read | Upload | Write | Admin | Super Admin | | -------------------------------- | ---- | ------ | ----- | ----- | ----------- | | Show app stats | ✅ | ✅ | ✅ | ✅ | ✅ | | Show app channels | ✅ | ✅ | ✅ | ✅ | ✅ | | Show devices | ✅ | ✅ | ✅ | ✅ | ✅ | | Show logs | ✅ | ✅ | ✅ | ✅ | ✅ | | Show bundles | ✅ | ✅ | ✅ | ✅ | ✅ | | Delete app | ❌ | ❌ | ❌ | ❌ | ✅ | | Delete channel | ❌ | ❌ | ❌ | ✅ | ✅ | | Delete version | ❌ | ❌ | ✅ | ✅ | ✅ | | Change org’s settings | ❌ | ❌ | ❌ | ✅ | ✅ | | Manage org users | ❌ | ❌ | ❌ | ✅ | ✅ | | Alter channel settings | ❌ | ❌ | ✅ | ✅ | ✅ | | Upload new version | ❌ | ✅ | ✅ | ✅ | ✅ | | Alter devices | ❌ | ❌ | ✅ | ✅ | ✅ | | Change channel’s current version | ❌ | ❌ | ✅ | ✅ | ✅ | | Create new channel | ❌ | ❌ | ❌ | ✅ | ✅ | | Alter version (metadata) | ❌ | ❌ | ✅ | ✅ | ✅ | | Manage billing | ❌ | ❌ | ❌ | ❌ | ✅ | | Delete a version unsafely | ❌ | ❌ | ❌ | ❌ | ✅ | ## Billing [Section titled “Billing”](#billing) Anyone with a **super admin** permission can manage the billing for a given organization. Plans are linked to an organization and not to your personal account. Danger ⚠️ Buying a plan will ONLY affect the organization that you have currently selected ## FAQ [Section titled “FAQ”](#faq) ### Can I create more than one organization? [Section titled “Can I create more than one organization?”](#can-i-create-more-than-one-organization) No, not yet. ### How can I configure security policies for my organization? [Section titled “How can I configure security policies for my organization?”](#how-can-i-configure-security-policies-for-my-organization) Super admins can configure comprehensive security policies including: * **2FA Enforcement** - Require all members to enable two-factor authentication * **Password Policy** - Set password complexity requirements (length, uppercase, numbers, special characters) * **API Key Security** - Enforce secure/hashed API keys and expiration policies See the [Organization Security](/docs/webapp/organization-security/) documentation for detailed configuration instructions. # Payment system > Managing payments in your Capgo account. In this section we show you how to manage your Card, plan, and credits for Capgo ## What is this about? [Section titled “What is this about?”](#what-is-this-about) This page aims to answer some questions about the payment system in capgo. ## Plans [Section titled “Plans”](#plans) #### Q: How can I upgrade my capgo plan? [Section titled “Q: How can I upgrade my capgo plan?”](#q-how-can-i-upgrade-my-capgo-plan) A: You can upgrade your capgo plan by going to [the settings](/docs/webapp/settings/#how-to-get-to-the-settings-page) and clicking on the **Plans** tab (1). ![Plans page](/plans-subscribe.webp) On this page you can: * Toggle between **Monthly Plan** and **Yearly** billing (with a 20% discount for yearly) * Choose from four plans: **Solo**, **Maker**, **Team**, or **Enterprise** * Click **Subscribe** (2) on the plan that best fits your needs Each plan shows: * Monthly price and annual billing total * Monthly active users included and overage pricing * Storage and bandwidth allowances * Build minutes/hours * Features like dedicated support, custom domain, and SOC II compliance If you don’t want to commit to a plan, you can use **credits** instead - click “Learn about credits” to see how pay-as-you-go works. After clicking Subscribe, a Stripe checkout page will open where you can enter your payment information securely. ## Credits [Section titled “Credits”](#credits) Credits provide a flexible alternative to upgrading your plan. They’re useful when: * **Temporary spikes** - You’re temporarily going above your plan limits (e.g., a viral moment, seasonal traffic) and don’t want to upgrade permanently * **Plan mismatch** - The next plan tier doesn’t fit your needs for other reasons (maybe you need more bandwidth but not more MAU) * **Pay-as-you-go preference** - You prefer paying only for what you use beyond your base plan #### Q: How do I access the Credits page? [Section titled “Q: How do I access the Credits page?”](#q-how-do-i-access-the-credits-page) A: Go to [the settings](/docs/webapp/settings/#how-to-get-to-the-settings-page) and click on the **Credits** tab (1). ![Credits page](/buy_credits.webp) The Credits page shows: 1. **Credits tab** - Navigate here from the Organization settings 2. **Credits Balance** - Shows your available credits (e.g., 0.00 / 0.00) and credits used in the current period 3. **Credits Used (USD)** - The estimated dollar value of credits consumed during the current billing period, plus available credits remaining 4. **Need more credits?** - Purchase additional credits instantly to keep your users receiving updates without disruption. Choose from preset amounts ($50, $100, $500, $5000) or enter a custom amount. The estimated total including taxes is shown before checkout 5. **Credit pricing** - Expand this section to see the pricing tiers for different usage types (MAU, storage, bandwidth) 6. **Credit transactions** - View your credit purchase and usage history #### Q: How do I buy credits? [Section titled “Q: How do I buy credits?”](#q-how-do-i-buy-credits) A: On the Credits page, select the amount you want to purchase (or use the dropdown for custom amounts), then click **Buy credits**. You’ll be redirected to Stripe checkout where you can adjust the quantity and complete payment. #### Q: When are credits used? [Section titled “Q: When are credits used?”](#q-when-are-credits-used) A: Credits are automatically consumed when you exceed your plan limits for monthly active users, storage, or bandwidth. They provide a flexible way to handle overages without service interruption #### Q: Are payments secure? [Section titled “Q: Are payments secure?”](#q-are-payments-secure) A: Yes, payments are fully managed by stripe. Capgo never gets access to your credit card details. Stripe takes security very seriously. [Learn more about stripe security policy](https://stripe.com/docs/security/) #### Q: Will capgo automatically upgrade my plan when I exceed the limit? [Section titled “Q: Will capgo automatically upgrade my plan when I exceed the limit?”](#q-will-capgo-automatically-upgrade-my-plan-when-i-exceed-the-limit) A: No, capgo will never change your plan. #### Q: Will cagpo send me an email when my plan is near its limits? [Section titled “Q: Will cagpo send me an email when my plan is near its limits?”](#q-will-cagpo-send-me-an-email-when-my-plan-is-near-its-limits) A: Yes, capgo will send you an email informing you about the usage. #### Q: Will the plan I purchase affect the organizations I am invited to? [Section titled “Q: Will the plan I purchase affect the organizations I am invited to?”](#q-will-the-plan-i-purchase-affect-the-organizations-i-am-invited-to) A: No, the plan will only affect the organization you have currently selected. Please refeer to [the organization documentation](/docs/webapp/organization-system/#q-how-is-billing-done-within-orgs). #### Q: What if I need a more tailored plan? [Section titled “Q: What if I need a more tailored plan?”](#q-what-if-i-need-a-more-tailored-plan) A: [Please contact capgo’s support directly](/docs/getting-help#support-by-chat) #### Q: What is the refund policy for capgo? [Section titled “Q: What is the refund policy for capgo?”](#q-what-is-the-refund-policy-for-capgo) A: A refund policy can be found [here](https://capgo.app/return/) # Settings > How to change the user settings, manage your info, name and email change your picture and more from your Capgo account ## How to get to the settings page [Section titled “How to get to the settings page”](#how-to-get-to-the-settings-page) First click on **your name and picture**. Then click on **Settings**. ![open settings](/settings-go.webp) ## Changing the user settings [Section titled “Changing the user settings”](#changing-the-user-settings) To change any user setting you can just fill out the form in the account and then click on **Update**. ![save change in account](/account-save.webp) Then a confirmation should appear. ![account updated](/account-updated.webp) ## Changing the password [Section titled “Changing the password”](#changing-the-password) To change the password go to the settings page and click on **Password**. Then fill in the form and click on **Update**. ![update password](/update-passwd.webp) When the password does not follow the capgo password security rules then you will get an error message. ![wrong password](/passwd-error.webp) # Bienvenido a Capgo > Capgo es una plataforma de código abierto de actualizaciones en tiempo real para Capacitor y equipos de desarrollo móvil, que permite entregar actualizaciones a los usuarios en minutos en lugar de días El poder de la Actualización en Vivo Entrega de manera fluida actualizaciones de aplicaciones en vivo, correcciones críticas de errores, cambios de contenido, funciones beta y más para brindar a tus usuarios la mejor experiencia posible Fácil de integrar El Plugin toma 3 pasos para integrarse en tu código base y te permite enviar actualizaciones a tus usuarios en cuestión de minutos en lugar de días Instalación Ejecuta `npx @capgo/cli@latest init [APIKEY]` para comenzar Documentación extensa Aprende en la [Documentación](/docs/getting-started/quickstart/) cómo dominar el Plugin en 5 minutos con nuestro video de introducción # Compilaciones Android > Configura y compila aplicaciones Android con Capgo Construcción en la nube Compila y envía aplicaciones Android a Google Play Store usando la infraestructura de nube segura de Capgo. ## Antes de Compilar [Section titled “Antes de Compilar”](#antes-de-compilar) ⚠️ Configura las Credenciales de Android Primero **Requerido:** Debes guardar tus credenciales de Android antes de compilar aplicaciones de lanzamiento. [Configurar Credenciales de Android →](/docs/cli/cloud-build/credentials/#saving-android-credentials) ## Cómo Funcionan las Compilaciones Android [Section titled “Cómo Funcionan las Compilaciones Android”](#cómo-funcionan-las-compilaciones-android) Las compilaciones Android se ejecutan en sandboxes seguros de Cloudflare: * **Infraestructura**: Cloudflare Workers con Android SDK en contenedores * **Herramienta de Compilación**: Gradle con herramientas de compilación de Android * **Ejecución**: Entorno sandbox aislado por compilación * **Limpieza**: Eliminación instantánea después de completar la compilación * **Seguridad**: Sin almacenamiento persistente, aislamiento completo entre compilaciones ## Prerequisitos [Section titled “Prerequisitos”](#prerequisitos) Antes de compilar para Android, necesitas: ### 1. Entorno de Desarrollo (Pruebas Locales) [Section titled “1. Entorno de Desarrollo (Pruebas Locales)”](#1-entorno-de-desarrollo-pruebas-locales) * Android Studio instalado localmente (para configuración inicial de keystore) * Tu aplicación compilándose exitosamente con `npx cap open android` * Java JDK 17 o superior ### 2. Keystore de Firma [Section titled “2. Keystore de Firma”](#2-keystore-de-firma) Para compilaciones de lanzamiento, necesitas un keystore de firma: | Tipo de Compilación | Keystore Requerido | Propósito | | ------------------- | ------------------ | -------------------------- | | **Depuración** | No | Solo pruebas, autogenerado | | **Lanzamiento** | Sí | Envío a Play Store | ## Crear un Keystore [Section titled “Crear un Keystore”](#crear-un-keystore) Tip Para guías detalladas paso a paso con capturas de pantalla, visita: * [Guía de Certificados Android](https://capgo.app/cloud/native-builds/certificates/android/) * [Blog: Compilaciones Automáticas de Android con GitHub Actions](https://capgo.app/blog/automatic-capacitor-android-build-github-action/) Si aún no tienes un keystore, créalo: ```bash keytool -genkey -v \ -keystore my-release-key.keystore \ -alias my-key-alias \ -keyalg RSA \ -keysize 2048 \ -validity 10000 ``` Responde las preguntas: * **Contraseña**: Elige una contraseña fuerte (¡guárdala de forma segura!) * **Nombre**: Tu nombre o nombre de la empresa * **Organización**: Nombre de tu empresa * **Ubicación**: Tu ciudad, estado, país Caution **Importante**: ¡Mantén tu archivo keystore y contraseñas seguras! Si las pierdes, no podrás actualizar tu aplicación en Play Store. ### Entendiendo los Componentes del Keystore [Section titled “Entendiendo los Componentes del Keystore”](#entendiendo-los-componentes-del-keystore) Al crear un keystore, necesitarás recordar: 1. **Contraseña del Keystore** (`KEYSTORE_STORE_PASSWORD`): La contraseña para el archivo keystore en sí 2. **Alias de la Clave** (`KEYSTORE_KEY_ALIAS`): El nombre/identificador para tu clave de firma dentro del keystore 3. **Contraseña de la Clave** (`KEYSTORE_KEY_PASSWORD`): La contraseña para la clave específica (puede ser la misma que la contraseña del almacén) **Flujo de trabajo de ejemplo:** ```bash # Lista los alias en tu keystore para verificar keytool -list -keystore my-release-key.keystore # Ver información detallada sobre tu clave keytool -list -v -keystore my-release-key.keystore -alias my-key-alias ``` ## Configuración de Compilación [Section titled “Configuración de Compilación”](#configuración-de-compilación) ### Variables de Entorno Requeridas [Section titled “Variables de Entorno Requeridas”](#variables-de-entorno-requeridas) Para **compilaciones de lanzamiento**, configura estas credenciales: ```bash # Firma de Android (Requerido para lanzamiento) ANDROID_KEYSTORE_FILE="" KEYSTORE_KEY_ALIAS="my-key-alias" KEYSTORE_KEY_PASSWORD="" KEYSTORE_STORE_PASSWORD="" # Publicación en Play Store (Opcional, para envío automático) PLAY_CONFIG_JSON="" ``` ### Generando Credenciales en Base64 [Section titled “Generando Credenciales en Base64”](#generando-credenciales-en-base64) **Archivo Keystore:** ```bash base64 -i my-release-key.keystore | pbcopy ``` **JSON de Cuenta de Servicio de Play Store:** ```bash base64 -i play-store-service-account.json | pbcopy ``` La cadena base64 ahora está en tu portapapeles. ### Configuración de Cuenta de Servicio de Play Store [Section titled “Configuración de Cuenta de Servicio de Play Store”](#configuración-de-cuenta-de-servicio-de-play-store) Para habilitar las cargas automáticas a Play Store, necesitas crear una cuenta de servicio de Google Cloud con los permisos apropiados. Tip Para instrucciones detalladas con capturas de pantalla, consulta la [Guía de Certificados Android](https://capgo.app/cloud/native-builds/certificates/android/). 1. **Crear Cuenta de Servicio en Google Cloud** * Ve a [Google Play Console](https://play.google.com/console) → Configuración → Acceso a la API * Haz clic en “Crear nueva cuenta de servicio” * Sigue el enlace a Google Cloud Console * Haz clic en “Crear Cuenta de Servicio” * Ingresa un nombre (ej., “Capgo CI/CD”) * Otorga el rol “Usuario de Cuenta de Servicio” * Haz clic en “Listo” 2. **Crear Clave JSON** * En Google Cloud Console, encuentra tu cuenta de servicio * Haz clic en el correo electrónico de la cuenta de servicio * Ve a la pestaña “Claves” * Haz clic en “Agregar Clave” → “Crear nueva clave” * Elige formato “JSON” * Descarga el archivo JSON (¡manténlo seguro!) 3. **Otorgar Permisos en Play Console** * Vuelve a Google Play Console → Configuración → Acceso a la API * Encuentra tu cuenta de servicio en la lista * Haz clic en “Otorgar acceso” * En “Permisos de aplicación”, selecciona tu aplicación * En “Permisos de cuenta”, otorga: * **Lanzamientos**: “Ver información de la aplicación y descargar informes masivos (solo lectura)” * **Lanzamientos**: “Crear, editar y eliminar lanzamientos borrador” * **Lanzamientos**: “Lanzar a producción, excluir dispositivos y usar Play Aplicación Signing” * Haz clic en “Invitar usuario” 4. **Aceptar la Invitación** * La cuenta de servicio recibirá un correo de invitación * Acepta la invitación para activar los permisos Caution El archivo JSON de la cuenta de servicio contiene credenciales sensibles. Nunca lo confirmes en control de versiones ni lo compartas públicamente. ## Compilando para Android [Section titled “Compilando para Android”](#compilando-para-android) ### Compilación de Depuración [Section titled “Compilación de Depuración”](#compilación-de-depuración) Perfecto para pruebas sin firma: ```bash npx @capgo/cli@latest build com.example.app \ --platform android \ --build-mode debug ``` Esto crea un APK de Depuración que se puede instalar en cualquier dispositivo para pruebas. ### Compilación de Lanzamiento [Section titled “Compilación de Lanzamiento”](#compilación-de-lanzamiento) Para envío a Play Store: ```bash npx @capgo/cli@latest build com.example.app \ --platform android \ --build-mode release ``` Requiere que las credenciales de firma estén configuradas como Variables de entorno. ## Integración CI/CD [Section titled “Integración CI/CD”](#integración-cicd) ### Ejemplo de GitHub Actions [Section titled “Ejemplo de GitHub Actions”](#ejemplo-de-github-actions) ```yaml name: Build Android App on: push: branches: [main] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - name: Setup Node.js uses: actions/setup-node@v6 with: node-version: '24' - name: Install dependencies run: npm ci - name: Build web assets run: npm run build - name: Sync Capacitor run: npx cap sync android - name: Build Android app env: CAPGO_TOKEN: ${{ secrets.CAPGO_TOKEN }} ANDROID_KEYSTORE_FILE: ${{ secrets.ANDROID_KEYSTORE }} KEYSTORE_KEY_ALIAS: ${{ secrets.KEYSTORE_ALIAS }} KEYSTORE_KEY_PASSWORD: ${{ secrets.KEYSTORE_KEY_PASSWORD }} KEYSTORE_STORE_PASSWORD: ${{ secrets.KEYSTORE_STORE_PASSWORD }} PLAY_CONFIG_JSON: ${{ secrets.PLAY_STORE_CONFIG }} run: | npx @capgo/cli@latest build ${{ secrets.APP_ID }} \ --platform android \ --build-mode release ``` ## Detalles del Proceso de Compilación [Section titled “Detalles del Proceso de Compilación”](#detalles-del-proceso-de-compilación) ### Qué Sucede Durante una Compilación Android [Section titled “Qué Sucede Durante una Compilación Android”](#qué-sucede-durante-una-compilación-android) 1. **Inicialización del Sandbox** (\~5 segundos) * Contenedor seguro iniciado * Android SDK y Gradle cargados * Sistema de archivos aislado creado 2. **Configuración del Proyecto** (\~20 segundos) * Zip del proyecto descargado desde R2 * Extraído al directorio de compilación * Credenciales de firma inyectadas 3. **Compilación Gradle** (2-4 minutos) * Dependencias descargadas * Compilación APK/AAB * Optimización ProGuard/R8 (modo Lanzamiento) * Firma de código aplicada 4. **Carga a Play Store** (30 segundos, si está configurado) * AAB subido a Play Console * Canal de lanzamiento configurado * Envío iniciado 5. **Limpieza** (inmediata) * Todos los archivos eliminados * Contenedor destruido * Sin artefactos retenidos ### Pila de Compilación [Section titled “Pila de Compilación”](#pila-de-compilación) Nuestro entorno de compilación Android incluye: * **Java**: OpenJDK 17 * **Android SDK**: Última versión estable * **Gradle**: 8.x * **Construir Tools**: 34.x * **Node.js**: 18.x (LTS) * **npm**: Última versión estable ## Salida de Compilación [Section titled “Salida de Compilación”](#salida-de-compilación) ### APK vs AAB [Section titled “APK vs AAB”](#apk-vs-aab) * **APK** (Paquete Android): Archivo instalable para instalación directa * **AAB** (Android Aplicación Paquete): Formato recomendado por Google Play (descargas más pequeñas para usuarios) Por defecto, las compilaciones de Capgo crean: * **Modo Depuración**: APK * **Modo Lanzamiento**: AAB (optimizado para Play Store) ## Tiempos de Compilación [Section titled “Tiempos de Compilación”](#tiempos-de-compilación) Tiempos típicos de compilación Android: | Tipo de Compilación | Tiempo Promedio | | -------------------------- | --------------- | | Depuración | 2-3 minutos | | Lanzamiento (sin ProGuard) | 3-4 minutos | | Lanzamiento (con ProGuard) | 4-6 minutos | Note Las compilaciones Android cuestan **1× la tarifa base** (la mitad del costo de las compilaciones iOS). ## Variantes de Compilación [Section titled “Variantes de Compilación”](#variantes-de-compilación) ### Variantes de Compilación Personalizadas [Section titled “Variantes de Compilación Personalizadas”](#variantes-de-compilación-personalizadas) Si tu aplicación tiene variantes de compilación personalizadas (ej., `staging`, `production`), usa `build-config`: ```bash npx @capgo/cli@latest build com.example.app \ --platform android \ --build-mode release \ --build-config '{"variant":"staging"}' ``` Esto compilará la variante `stagingRelease`. ### Dimensiones de Sabor [Section titled “Dimensiones de Sabor”](#dimensiones-de-sabor) Para aplicaciones con dimensiones de sabor: ```bash --build-config '{"flavor":"premium","variant":"production"}' ``` Esto compila la variante `premiumProductionRelease`. ## Solución de Problemas [Section titled “Solución de Problemas”](#solución-de-problemas) ### Problemas Comunes [Section titled “Problemas Comunes”](#problemas-comunes) **“Contraseña de keystore incorrecta”** * Verifica que KEYSTORE\_STORE\_PASSWORD coincida con tu keystore * Asegúrate de que KEYSTORE\_KEY\_PASSWORD coincida con la contraseña de tu alias de clave * Verifica espacios extra o caracteres especiales **“Alias de clave no encontrado”** * Verifica que KEYSTORE\_KEY\_ALIAS coincida exactamente (sensible a mayúsculas) * Lista los alias: `keytool -list -keystore my-release-key.keystore` **“Compilación Gradle falló”** * Revisa los registros de compilación para el Error específico * Asegúrate de que tu aplicación compile localmente con `./gradlew assembleRelease` * Verifica que todas las dependencias nativas estén en `build.gradle` **“Carga a Play Store falló”** * Verifica que el JSON de la cuenta de servicio sea válido * Asegúrate de que la cuenta de servicio tenga los permisos correctos en Play Console * Verifica que la aplicación esté configurada correctamente en Play Console **“Tiempo de espera de compilación agotado”** * Las aplicaciones grandes pueden necesitar optimización * Verifica si se pueden eliminar dependencias innecesarias * Contacta soporte si las compilaciones consistentemente agotan el tiempo ### Registros de Depuración [Section titled “Registros de Depuración”](#registros-de-depuración) Busca estas fases clave en los registros de compilación: ```plaintext → Descargando dependencias... → Ejecutando Gradle assembleRelease... → Firmando APK/AAB... → Subiendo a Play Store... ✔ Compilación exitosa ``` Si una compilación falla, el Error específico de Gradle se mostrará en los registros. ## Mejores Prácticas [Section titled “Mejores Prácticas”](#mejores-prácticas) ### 1. Probar Localmente Primero [Section titled “1. Probar Localmente Primero”](#1-probar-localmente-primero) Siempre asegúrate de que tu compilación Android funcione localmente: ```bash cd android ./gradlew assembleRelease # o ./gradlew bundleRelease ``` ### 2. Asegurar tu Keystore [Section titled “2. Asegurar tu Keystore”](#2-asegurar-tu-keystore) * Nunca confirmes keystores en control de versiones * Almacena en gestión de secretos segura (1Password, AWS Secrets Manager, etc.) * Mantén copias de respaldo en múltiples ubicaciones seguras * Documenta contraseñas en un gestor de contraseñas seguro ### 3. Gestión de Versiones [Section titled “3. Gestión de Versiones”](#3-gestión-de-versiones) Capgo lee la versión de tu `capacitor.config.json`: ```json { "appId": "com.example.app", "appName": "My App", "version": "1.0.0", "build": "1" } ``` Incrementa el número de `build` para cada lanzamiento. ### 4. Configuración de ProGuard [Section titled “4. Configuración de ProGuard”](#4-configuración-de-proguard) Para compilaciones de lanzamiento, asegúrate de que las reglas de ProGuard estén configuradas correctamente: android/app/proguard-rules.pro ```plaintext -keep class com.getcapacitor.** { *; } -keep @com.getcapacitor.annotation.CapacitorPlugin public class * { @com.getcapacitor.annotation.PluginMethod public ; } ``` ### 5. Monitorear Tamaño de Compilación [Section titled “5. Monitorear Tamaño de Compilación”](#5-monitorear-tamaño-de-compilación) Mantén un ojo en el tamaño APK/AAB para asegurar que esté optimizado: ```plaintext El CLI muestra el tamaño final: → Tamaño APK: 12.4 MB ``` Si tu aplicación es grande (>50 MB), considera: * Habilitar ProGuard/R8 * Usar formato AAB (entrega dinámica) * Optimizar imágenes y recursos ## Envío a Play Store [Section titled “Envío a Play Store”](#envío-a-play-store) ### Envío Automático [Section titled “Envío Automático”](#envío-automático) Con PLAY\_CONFIG\_JSON configurado, las compilaciones se cargan automáticamente al canal de prueba interna de Play Console. ### Envío Manual [Section titled “Envío Manual”](#envío-manual) Si prefieres el envío manual: 1. Ejecuta la compilación sin PLAY\_CONFIG\_JSON 2. Descarga el AAB de los artefactos de compilación (si está configurado) 3. Sube manualmente a Play Console ## Próximos Pasos [Section titled “Próximos Pasos”](#próximos-pasos) * [Compilaciones iOS](/docs/cli/cloud-build/ios/) - Configurar compilaciones iOS * [Solución de Problemas](/docs/cli/cloud-build/troubleshooting/) - Problemas comunes de compilación * [Referencia CLI](/docs/cli/reference/build/) - Documentación completa de comandos ## ¿Necesitas Ayuda? [Section titled “¿Necesitas Ayuda?”](#necesitas-ayuda) * [Guía de Solución de Problemas](/docs/cli/cloud-build/troubleshooting/) * [Comunidad Discord](https://discord.com/invite/VnYRvBfgA6) * Email: # Gestión de Credenciales > Guarda y gestiona credenciales de compilación localmente para compilaciones iOS y Android Gestiona tus credenciales de compilación de iOS y Android localmente para compilaciones en la nube convenientes. ## Descripción General [Section titled “Descripción General”](#descripción-general) Capgo CLI te permite guardar credenciales de compilación localmente en tu máquina en la carpeta `.capgo-credentials`. Cuando ejecutas una compilación, estas credenciales se usan automáticamente y se envían de forma segura a los servidores de compilación de Capgo. ¿Necesitas Ayuda para Obtener Credenciales? Si aún no tienes tus certificados y credenciales, consulta estas guías completas: **iOS:** * [Cómo Obtener Certificados iOS](/docs/cli/cloud-build/ios/#how-to-get-ios-certificates-and-provisioning-profiles) - Guía paso a paso * [Guía de Certificados iOS (Capgo)](https://capgo.app/cloud/native-builds/certificates/ios/) - Tutorial detallado con capturas de pantalla * [Blog: Compilaciones Automáticas iOS](https://capgo.app/blog/automatic-capacitor-ios-build-github-action/) - Configuración completa de CI/CD **Android:** * [Crear un Keystore](/docs/cli/cloud-build/android/#creating-a-keystore) - Guía paso a paso * [Guía de Certificados Android (Capgo)](https://capgo.app/cloud/native-builds/certificates/android/) - Tutorial detallado con capturas de pantalla * [Blog: Compilaciones Automáticas Android](https://capgo.app/blog/automatic-capacitor-android-build-github-action/) - Configuración completa de CI/CD Garantía de Seguridad **Tus credenciales NUNCA se almacenan permanentemente en los servidores de Capgo:** * ✅ Usadas SOLO durante el proceso de compilación activo * ✅ Eliminadas automáticamente después de completar la compilación * ✅ Retención máxima: 24 horas (incluso si la compilación falla) * ✅ Las aplicaciones se envían directamente a Aplicación Store/Play Store - no almacenamos NADA * ✅ Transmitidas de forma segura sobre HTTPS ## Comandos [Section titled “Comandos”](#comandos) ### Guardar Credenciales [Section titled “Guardar Credenciales”](#guardar-credenciales) Almacena tus credenciales de compilación localmente para uso automático: ```bash npx @capgo/cli build credentials save --platform [opciones] ``` ### Listar Credenciales [Section titled “Listar Credenciales”](#listar-credenciales) Ver credenciales guardadas actualmente (las contraseñas están enmascaradas): ```bash npx @capgo/cli build credentials list ``` ### Limpiar Credenciales [Section titled “Limpiar Credenciales”](#limpiar-credenciales) Eliminar credenciales guardadas de tu máquina local: ```bash # Limpiar todas las credenciales npx @capgo/cli build credentials clear # Limpiar solo credenciales iOS npx @capgo/cli build credentials clear --platform ios # Limpiar solo credenciales Android npx @capgo/cli build credentials clear --platform android ``` ## Guardar Credenciales iOS [Section titled “Guardar Credenciales iOS”](#guardar-credenciales-ios) Note **¿Aún no tienes certificados iOS?** Consulta la [guía de Compilaciones iOS](/docs/cli/cloud-build/ios/#how-to-get-ios-certificates-and-provisioning-profiles) para instrucciones sobre cómo crear certificados y perfiles de aprovisionamiento. ### Ejemplo Completo [Section titled “Ejemplo Completo”](#ejemplo-completo) ```bash npx @capgo/cli build credentials save \ --platform ios \ --certificate ./cert.p12 \ --p12-password "TuContraseñaP12" \ --provisioning-profile ./profile.mobileprovision \ --apple-key ./AuthKey_ABC1234567.p8 \ --apple-key-id "ABC1234567" \ --apple-issuer-id "00000000-0000-0000-0000-000000000000" \ --apple-team-id "TEAM123456" ``` ### Opciones iOS [Section titled “Opciones iOS”](#opciones-ios) | Opción | Descripción | Requerido | | ------------------------------------ | ------------------------------------------------------------------ | ---------------- | | `--certificate ` | Ruta al archivo de certificado .p12 | Sí (lanzamiento) | | `--p12-password ` | Contraseña para el certificado .p12 | Sí (lanzamiento) | | `--provisioning-profile ` | Ruta al archivo .mobileprovision | Sí (lanzamiento) | | `--provisioning-profile-prod ` | Perfil de aprovisionamiento de producción (opcional) | No | | `--apple-key ` | Ruta a la clave API .p8 de Aplicación Store Connect | Sí (envío) | | `--apple-key-id ` | ID de clave API de Aplicación Store Connect | Sí (envío) | | `--apple-issuer-id ` | ID de emisor API de Aplicación Store Connect (UUID) | Sí (envío) | | `--apple-team-id ` | ID de equipo de Aplicación Store Connect | Sí (envío) | | `--apple-id ` | Email de Apple ID (autenticación alternativa) | No | | `--apple-app-password ` | Contraseña específica de la aplicación (autenticación alternativa) | No | ### Qué se Almacena [Section titled “Qué se Almacena”](#qué-se-almacena) Cuando guardas credenciales iOS, el CLI: 1. Lee los archivos de certificado y perfil de aprovisionamiento 2. Los convierte a codificación base64 3. Guarda las credenciales en la carpeta `.capgo-credentials` 4. Almacena contraseñas e IDs como texto plano (solo archivos locales) La estructura de archivos almacenada: ```json { "ios": { "BUILD_CERTIFICATE_BASE64": "...", "BUILD_PROVISION_PROFILE_BASE64": "...", "APPLE_KEY_CONTENT": "...", "P12_PASSWORD": "...", "APPLE_KEY_ID": "ABC1234567", "APPLE_ISSUER_ID": "...", "APP_STORE_CONNECT_TEAM_ID": "TEAM123456" } } ``` ## Guardar Credenciales Android [Section titled “Guardar Credenciales Android”](#guardar-credenciales-android) Note **¿Aún no tienes un keystore?** Consulta la [guía de Compilaciones Android](/docs/cli/cloud-build/android/#creating-a-keystore) para instrucciones sobre cómo crear un keystore y configurar credenciales de Play Store. ### Ejemplo Completo [Section titled “Ejemplo Completo”](#ejemplo-completo-1) ```bash npx @capgo/cli build credentials save \ --platform android \ --keystore ./release.keystore \ --keystore-alias "mi-alias-de-clave" \ --keystore-key-password "ContraseñaClave123" \ --keystore-store-password "ContraseñaAlmacén123" \ --play-config ./play-store-service-account.json ``` ### Opciones Android [Section titled “Opciones Android”](#opciones-android) | Opción | Descripción | Requerido | | ---------------------------------------- | --------------------------------------------------- | ---------------- | | `--keystore ` | Ruta al archivo .keystore o .jks | Sí (lanzamiento) | | `--keystore-alias ` | Alias de la clave en el keystore | Sí (lanzamiento) | | `--keystore-key-password ` | Contraseña para el alias de la clave | Sí (lanzamiento) | | `--keystore-store-password ` | Contraseña para el keystore | Sí (lanzamiento) | | `--play-config ` | Ruta al JSON de la cuenta de servicio de Play Store | Sí (envío) | ### Qué se Almacena [Section titled “Qué se Almacena”](#qué-se-almacena-1) Cuando guardas credenciales Android, el CLI: 1. Lee los archivos keystore y JSON de la cuenta de servicio 2. Los convierte a codificación base64 3. Guarda las credenciales en la carpeta `.capgo-credentials` 4. Almacena contraseñas y alias como texto plano (solo archivos locales) La estructura de archivos almacenada: ```json { "android": { "ANDROID_KEYSTORE_FILE": "...", "PLAY_CONFIG_JSON": "...", "KEYSTORE_KEY_ALIAS": "mi-alias-de-clave", "KEYSTORE_KEY_PASSWORD": "...", "KEYSTORE_STORE_PASSWORD": "..." } } } ``` ## Usar Credenciales Guardadas [Section titled “Usar Credenciales Guardadas”](#usar-credenciales-guardadas) Una vez que hayas guardado las credenciales, se usan automáticamente cuando compiles: ```bash # Las credenciales se cargan automáticamente desde la carpeta .capgo-credentials npx @capgo/cli build com.example.app --platform ios ``` También puedes sobrescribir las credenciales guardadas usando Variables de entorno: ```bash # Las variables de entorno tienen prioridad sobre las credenciales guardadas BUILD_CERTIFICATE_BASE64="..." \ P12_PASSWORD="contraseña-diferente" \ npx @capgo/cli build com.example.app --platform ios ``` **Orden de precedencia:** 1. Variables de entorno (prioridad más alta) 2. Credenciales guardadas en la carpeta `.capgo-credentials` 3. Sin credenciales (prioridad más baja) ## Ver Credenciales Guardadas [Section titled “Ver Credenciales Guardadas”](#ver-credenciales-guardadas) Lista qué credenciales tienes guardadas: ```bash npx @capgo/cli build credentials list ``` Ejemplo de salida: ```plaintext 📋 Credenciales de Compilación Guardadas: Credenciales iOS: ✓ Certificado (base64) ✓ Perfil de Aprovisionamiento (base64) ✓ Contenido de Clave Apple (base64) ✓ Contraseña P12: ******** ✓ ID de Clave Apple: ABC1234567 ✓ ID de Emisor Apple: 00000000-0000-0000-0000-000000000000 ✓ ID de Equipo: TEAM123456 Credenciales Android: ✓ Keystore (base64) ✓ Configuración de Play Store (base64) ✓ Alias de Keystore: mi-alias-de-clave ✓ Contraseña de Clave: ******** ✓ Contraseña de Almacén: ******** Ubicación: .capgo-credentials/ 🔒 Estas credenciales se almacenan localmente solo en tu máquina. Al compilar, se envían a Capgo pero NUNCA se almacenan allí. Se eliminan automáticamente después de completar la compilación (máx. 24 horas). ``` ## Mejores Prácticas de Seguridad [Section titled “Mejores Prácticas de Seguridad”](#mejores-prácticas-de-seguridad) ### Seguridad de Almacenamiento Local [Section titled “Seguridad de Almacenamiento Local”](#seguridad-de-almacenamiento-local) 1. **Permisos de Archivo** ```bash # Asegurar que la carpeta de credenciales no sea legible por otros chmod 700 .capgo-credentials chmod 600 .capgo-credentials/* ``` 2. **Nunca Confirmar Credenciales** ```bash # Agregar a .gitignore echo ".capgo-credentials/" >> .gitignore ``` 3. **Separar Credenciales** * Usa diferentes credenciales para desarrollo local vs CI/CD * Rota las credenciales regularmente * No compartas credenciales entre miembros del equipo ### Uso en CI/CD [Section titled “Uso en CI/CD”](#uso-en-cicd) Para entornos CI/CD, **prefiere Variables de entorno** sobre credenciales guardadas: ```yaml # GitHub Actions env: BUILD_CERTIFICATE_BASE64: ${{ secrets.IOS_CERTIFICATE }} P12_PASSWORD: ${{ secrets.P12_PASSWORD }} # ... otros secretos ``` Esto es más seguro porque: * Los secretos son gestionados por tu plataforma CI/CD * No hay archivos de credenciales en los runners * Fácil rotación y control de acceso * Pistas de auditoría para uso de secretos ### Rotación de Credenciales [Section titled “Rotación de Credenciales”](#rotación-de-credenciales) Rota tus credenciales regularmente: 1. **iOS**: Genera nuevos certificados y claves API anualmente 2. **Android**: Cambia las contraseñas del keystore anualmente 3. **Después de cambios de equipo**: Rota cuando los miembros del equipo se vayan Actualizar credenciales guardadas: ```bash # Vuelve a ejecutar el comando save con nuevas credenciales npx @capgo/cli build credentials save --platform ios --certificate ./nuevo-cert.p12 ... ``` ## Solución de Problemas [Section titled “Solución de Problemas”](#solución-de-problemas) ### ”No se encontraron credenciales” [Section titled “”No se encontraron credenciales””](#no-se-encontraron-credenciales) Si la compilación dice que no se encontraron credenciales: 1. **Verifica si las credenciales están guardadas**: ```bash npx @capgo/cli build credentials list ``` 2. **Guarda las credenciales si faltan**: ```bash npx @capgo/cli build credentials save --platform ios ... ``` 3. **Verifica que la carpeta de credenciales exista**: ```bash ls -la .capgo-credentials/ ``` ### “Permiso denegado” al leer credenciales [Section titled ““Permiso denegado” al leer credenciales”](#permiso-denegado-al-leer-credenciales) Corrige los permisos de archivo: ```bash chmod 700 .capgo-credentials chmod 600 .capgo-credentials/* ``` ### Las credenciales no se están usando [Section titled “Las credenciales no se están usando”](#las-credenciales-no-se-están-usando) Verifica que se especifique la plataforma correcta: ```bash # Asegúrate de que --platform coincida con las credenciales guardadas npx @capgo/cli build com.example.app --platform ios # Usa credenciales ios npx @capgo/cli build com.example.app --platform android # Usa credenciales android ``` ### Limpiar y volver a guardar credenciales [Section titled “Limpiar y volver a guardar credenciales”](#limpiar-y-volver-a-guardar-credenciales) Si las credenciales parecen corruptas: ```bash # Limpiar todas las credenciales npx @capgo/cli build credentials clear # Guardar nuevamente npx @capgo/cli build credentials save --platform ios ... ``` ## Migración desde Variables de Entorno [Section titled “Migración desde Variables de Entorno”](#migración-desde-variables-de-entorno) Si actualmente estás usando Variables de entorno, puedes migrar a credenciales guardadas: 1. **Extrae tus Variables de entorno actuales** ```bash echo $BUILD_CERTIFICATE_BASE64 # Verifica que existan ``` 2. **Decodifica los archivos base64 a archivos originales** (si es necesario) ```bash echo "$BUILD_CERTIFICATE_BASE64" | base64 -d > cert.p12 echo "$BUILD_PROVISION_PROFILE_BASE64" | base64 -d > profile.mobileprovision ``` 3. **Guarda usando el CLI** ```bash npx @capgo/cli build credentials save \ --platform ios \ --certificate ./cert.p12 \ --provisioning-profile ./profile.mobileprovision \ --p12-password "$P12_PASSWORD" \ --apple-key-id "$APPLE_KEY_ID" \ --apple-issuer-id "$APPLE_ISSUER_ID" \ --apple-team-id "$APP_STORE_CONNECT_TEAM_ID" ``` 4. **Prueba la compilación** ```bash npx @capgo/cli build com.example.app --platform ios ``` 5. **Elimina las Variables de entorno** (opcional) ```bash unset BUILD_CERTIFICATE_BASE64 BUILD_PROVISION_PROFILE_BASE64 ``` ## Ubicación de Archivo [Section titled “Ubicación de Archivo”](#ubicación-de-archivo) Las credenciales se almacenan en la carpeta `.capgo-credentials`: * **macOS/Linux**: `.capgo-credentials/` (en la raíz de tu proyecto o directorio home) * **Windows**: `.capgo-credentials\` (en la raíz de tu proyecto o directorio home) La carpeta se crea automáticamente cuando guardas credenciales por primera vez. ## Próximos Pasos [Section titled “Próximos Pasos”](#próximos-pasos) * [Comenzar](/docs/cli/cloud-build/getting-started/) - Crea tu primera compilación * [Compilaciones iOS](/docs/cli/cloud-build/ios/) - Configuración de compilación específica para iOS * [Compilaciones Android](/docs/cli/cloud-build/android/) - Configuración de compilación específica para Android * [Solución de Problemas](/docs/cli/cloud-build/troubleshooting/) - Problemas comunes y soluciones ## ¿Necesitas Ayuda? [Section titled “¿Necesitas Ayuda?”](#necesitas-ayuda) * 📚 [Guía de solución de problemas](/docs/cli/cloud-build/troubleshooting/) * 💬 [Comunidad Discord](https://discord.com/invite/VnYRvBfgA6) * 📧 Email: # Comenzar > Crea tu primera compilación nativa con Capgo Construcción en la nube Comienza con Capgo Construcción en la nube y crea tu primera compilación nativa iOS o Android en minutos. ## Lo Que Necesitarás [Section titled “Lo Que Necesitarás”](#lo-que-necesitarás) Antes de comenzar, asegúrate de tener: * Una aplicación Capacitor que compile exitosamente localmente * Node.js 20 o superior instalado * Una cuenta Capgo con una suscripción activa * Tu aplicación ya registrada en Capgo (ejecuta `npx @capgo/cli@latest app add` si no lo está) * **Credenciales de compilación configuradas** (certificados, keystores) - ver abajo ## Antes de Tu Primera Compilación [Section titled “Antes de Tu Primera Compilación”](#antes-de-tu-primera-compilación) ⚠️ Configura las Credenciales Primero **Requerido antes de compilar:** Debes configurar tus credenciales de compilación (certificados para iOS, keystores para Android). [Configurar Credenciales →](/docs/cli/cloud-build/credentials/) ## Inicio Rápido [Section titled “Inicio Rápido”](#inicio-rápido) 1. **Configurar Credenciales de Compilación** Antes de poder compilar, necesitas guardar tus credenciales localmente: **Para iOS:** ```bash npx @capgo/cli build credentials save \ --platform ios \ --certificate ./cert.p12 \ --p12-password "password" \ --provisioning-profile ./profile.mobileprovision \ --apple-key ./AuthKey.p8 \ --apple-key-id "KEY123" \ --apple-issuer-id "issuer-uuid" \ --apple-team-id "team-id" ``` **Para Android:** ```bash npx @capgo/cli build credentials save \ --platform android \ --keystore ./release.keystore \ --keystore-alias "my-key" \ --keystore-key-password "key-pass" \ --keystore-store-password "store-pass" ``` Consulta la [guía completa de credenciales](/docs/cli/cloud-build/credentials/) para más detalles. 2. **Verificar Compilación Local** Primero, asegúrate de que tu aplicación compile localmente sin errores: ```bash # Compila tus recursos web npm run build # Sincroniza con Capacitor npx cap sync # Prueba compilación local (opcional pero recomendado) npx cap open ios # Para iOS npx cap open android # Para Android ``` 3. **Autenticarse con Capgo** Configura tu clave API de Capgo (si no está ya configurada): ```bash npx @capgo/cli@latest login ``` O configura la Variable de entorno: ```bash export CAPGO_TOKEN=tu_clave_api_aquí ``` 4. **Ejecutar Tu Primera Compilación** Comienza con una compilación de Depuración de Android (la más rápida para probar): ```bash npx @capgo/cli@latest build com.example.app \ --platform android \ --build-mode debug ``` Verás registros en tiempo real a medida que progresa tu compilación: ```plaintext ✔ Creando trabajo de compilación... ✔ Subiendo proyecto (15.2 MB)... ✔ Compilación iniciada 📝 Registros de compilación: → Instalando dependencias... → Ejecutando compilación Gradle... → Firmando APK... ✔ Compilación exitosa en 3m 42s ``` 5. **Verificar Estado de Compilación** El CLI automáticamente encuestará y mostrará el estado de compilación. Una vez completado, verás: * Tiempo de compilación * Estado de éxito/fallo * Aplicación enviada a Aplicación Store/Play Store (si las credenciales están configuradas) ## Entendiendo el Proceso de Compilación [Section titled “Entendiendo el Proceso de Compilación”](#entendiendo-el-proceso-de-compilación) Cuando ejecutas el comando de compilación, esto es lo que sucede: ```mermaid graph LR A[Tu Máquina] -->|1. Comprimir Proyecto| B[Temp Local] B -->|2. Subir| C[Capgo Cloud] C -->|3. Compilar| D[Servidor de Compilación] D -->|4. Stream de Registros| A D -->|5. Limpieza| E[Auto Eliminar] ``` 1. **Preparación Local** - Tu proyecto se comprime (excluyendo `node_modules` y archivos ocultos) 2. **Subida** - El zip se sube a almacenamiento en la nube seguro (Cloudflare R2) 3. **Ejecución de Compilación** - Tu aplicación se compila en infraestructura dedicada 4. **Streaming de Registros** - Los registros en tiempo real se transmiten a tu terminal vía Server-Sent Events 5. **Limpieza Automática** - Los artefactos de compilación se eliminan (Android: instantáneo, iOS: 24 horas) ## Tu Primera Compilación de Producción [Section titled “Tu Primera Compilación de Producción”](#tu-primera-compilación-de-producción) Una vez que hayas verificado que el proceso funciona, crea una compilación de producción: ### Android [Section titled “Android”](#android) ```bash npx @capgo/cli@latest build com.example.app \ --platform android \ --build-mode release ``` Necesitarás configurar primero las credenciales de firma. Consulta [Configuración de Compilación Android](/docs/cli/cloud-build/android/). ### iOS [Section titled “iOS”](#ios) ```bash npx @capgo/cli@latest build com.example.app \ --platform ios \ --build-mode release ``` Las compilaciones iOS requieren certificados de firma y perfiles de aprovisionamiento. Consulta [Configuración de Compilación iOS](/docs/cli/cloud-build/ios/). ## Qué se Compila [Section titled “Qué se Compila”](#qué-se-compila) **Importante:** Capgo Construcción en la nube solo compila las **partes nativas** de tu aplicación (código nativo iOS y Android). Tú eres responsable de: * Compilar tus recursos web (`npm run build`) * Ejecutar `npx cap sync` antes de la compilación * Asegurar que todas las dependencias estén en `package.json` Nosotros nos encargamos de: * Compilación nativa iOS (Xcode, Fastlane) * Compilación nativa Android (Gradle) * Firma de código * Envío a tiendas de aplicaciones (si está configurado) ## Tiempo y Costos de Compilación [Section titled “Tiempo y Costos de Compilación”](#tiempo-y-costos-de-compilación) El tiempo de compilación se mide desde el inicio hasta la finalización: * **Android**: Típicamente 3-5 minutos (multiplicador de facturación 1×) * **iOS**: Típicamente 5-10 minutos (multiplicador de facturación 2× debido a costos de hardware Mac) Solo pagas por el tiempo de compilación real usado. Sin cargos ocultos. ## Casos de Uso Comunes [Section titled “Casos de Uso Comunes”](#casos-de-uso-comunes) ### Integración CI/CD [Section titled “Integración CI/CD”](#integración-cicd) Agregar a tu flujo de trabajo de GitHub Actions: ```yaml - name: Build native app env: CAPGO_TOKEN: ${{ secrets.CAPGO_TOKEN }} run: | npm run build npx cap sync npx @capgo/cli@latest build ${{ secrets.APP_ID }} \ --platform both \ --build-mode release ``` ### Desarrollo Local [Section titled “Desarrollo Local”](#desarrollo-local) Probar compilaciones localmente antes de confirmar: ```bash # Compilación de debug rápida para pruebas npm run build && npx cap sync npx @capgo/cli@latest build com.example.app \ --platform android \ --build-mode debug ``` ### Compilaciones Multi-Plataforma [Section titled “Compilaciones Multi-Plataforma”](#compilaciones-multi-plataforma) Compilar para ambas plataformas simultáneamente: ```bash npx @capgo/cli@latest build com.example.app \ --platform both \ --build-mode release ``` ## Próximos Pasos [Section titled “Próximos Pasos”](#próximos-pasos) Ahora que has creado tu primera compilación: * [Configurar compilaciones iOS](/docs/cli/cloud-build/ios/) - Configurar certificados y perfiles * [Configurar compilaciones Android](/docs/cli/cloud-build/android/) - Configurar keystores y Play Store * [Solución de problemas](/docs/cli/cloud-build/troubleshooting/) - Problemas comunes y soluciones * [Referencia CLI](/docs/cli/reference/build/) - Documentación completa de comandos ## ¿Necesitas Ayuda? [Section titled “¿Necesitas Ayuda?”](#necesitas-ayuda) * Consulta la [guía de solución de problemas](/docs/cli/cloud-build/troubleshooting/) * Únete a nuestra [comunidad Discord](https://discord.com/invite/VnYRvBfgA6) * Envía un correo a soporte en # Compilaciones iOS > Configura y compila aplicaciones iOS con Capgo Construcción en la nube Compila y envía aplicaciones iOS a TestFlight y Aplicación Store usando la infraestructura Mac dedicada de Capgo. ## Antes de Compilar [Section titled “Antes de Compilar”](#antes-de-compilar) ⚠️ Configura las Credenciales iOS Primero **Requerido:** Debes guardar tus credenciales iOS antes de compilar. [Configurar Credenciales iOS →](/docs/cli/cloud-build/credentials/#saving-ios-credentials) ## Cómo Funcionan las Compilaciones iOS [Section titled “Cómo Funcionan las Compilaciones iOS”](#cómo-funcionan-las-compilaciones-ios) Las compilaciones iOS se ejecutan en máquinas Mac dedicadas (Scaleway Mac minis M4) aprovisionadas bajo demanda: * **Hardware**: Mac minis de Apple Silicon con macOS 15 * **Herramienta de Compilación**: Xcode con Fastlane (configuración personalizada de Capgo) * **Aislamiento**: Cada compilación se ejecuta como una cuenta de usuario macOS separada * **Duración**: Las máquinas tienen arrendamientos de 24 horas y se limpian automáticamente * **Seguridad**: Todos los archivos y cuentas de usuario se eliminan después de liberar la máquina ## Prerequisitos [Section titled “Prerequisitos”](#prerequisitos) Antes de compilar para iOS, necesitas: ### 1. Entorno de Desarrollo [Section titled “1. Entorno de Desarrollo”](#1-entorno-de-desarrollo) * Computadora Mac con Xcode instalado (para configuración inicial de certificado) * Cuenta de desarrollador de Apple válida ($99/año) * Tu aplicación compilándose exitosamente localmente con `npx cap open ios` ### 2. Certificados de Firma de Código [Section titled “2. Certificados de Firma de Código”](#2-certificados-de-firma-de-código) Necesitarás uno de estos tipos de certificados dependiendo de tu compilación: | Tipo de Compilación | Certificado Requerido | Perfil de Aprovisionamiento | | -------------------- | --------------------- | --------------------------- | | **Development** | Apple Development | Perfil de Desarrollo | | **Ad Hoc** | Apple Distribution | Perfil Ad Hoc | | **Aplicación Store** | Apple Distribution | Perfil Aplicación Store | #### Cómo Obtener Certificados iOS y Perfiles de Aprovisionamiento [Section titled “Cómo Obtener Certificados iOS y Perfiles de Aprovisionamiento”](#cómo-obtener-certificados-ios-y-perfiles-de-aprovisionamiento) Tip Para guías detalladas paso a paso con capturas de pantalla, visita: * [Guía de Certificados iOS](https://capgo.app/cloud/native-builds/certificates/ios/) * [Blog: Compilaciones Automáticas iOS con GitHub Actions](https://capgo.app/blog/automatic-capacitor-ios-build-github-action/) **Resumen Rápido:** 1. **Crear una Solicitud de Firma de Certificado (CSR)** * Abre Acceso a Llaveros en tu Mac * Ve a Acceso a Llaveros → Asistente de Certificados → Solicitar un Certificado de una Autoridad Certificadora * Ingresa tu correo electrónico y nombre, selecciona “Guardado en disco” * Guarda el archivo `.certSigningRequest` 2. **Generar Certificado en el Portal de Desarrollador de Apple** * Ve a [Certificados de Desarrollador de Apple](https://developer.apple.com/account/resources/certificates) * Haz clic en ”+” para crear un nuevo certificado * Elige el tipo de certificado (Distribución iOS para compilaciones de Aplicación Store) * Sube tu archivo CSR * Descarga el certificado (archivo `.cer`) 3. **Exportar Certificado como .p12** * Haz doble clic en el archivo `.cer` descargado para agregarlo a Llaveros * En Acceso a Llaveros, encuentra tu certificado bajo “Mis Certificados” * Haz clic derecho → Exportar “Apple Distribution…” * Guarda en formato `.p12` y establece una contraseña (¡guarda esta contraseña!) 4. **Crear Perfil de Aprovisionamiento** * Ve a [Perfiles de Desarrollador de Apple](https://developer.apple.com/account/resources/profiles) * Haz clic en ”+” para crear un nuevo perfil * Elige el tipo de perfil (Aplicación Store para compilaciones de producción) * Selecciona tu ID de aplicación * Selecciona el certificado que acabas de crear * Descarga el archivo `.mobileprovision` ### 3. Clave API de Aplicación Store Connect (Recomendado) [Section titled “3. Clave API de Aplicación Store Connect (Recomendado)”](#3-clave-api-de-aplicación-store-connect-recomendado) Para envío automático a TestFlight, crea una clave API: 1. Ve a [App Store Connect](https://appstoreconnect.apple.com/) → Usuarios y Acceso → Claves 2. Haz clic en el botón ”+” para crear una nueva clave 3. Dale un nombre (ej., “Capgo CI”) y selecciona el rol “Developer” 4. Descarga el archivo `.p8` (¡solo puedes descargarlo una vez!) 5. Anota el **ID de Clave** y el **ID de Emisor** Note También puedes encontrar tu **ID de Equipo** en tu cuenta de desarrollador de Apple bajo los detalles de Membresía. ## Configuración de Compilación [Section titled “Configuración de Compilación”](#configuración-de-compilación) ### Variables de Entorno Requeridas [Section titled “Variables de Entorno Requeridas”](#variables-de-entorno-requeridas) Configura estas credenciales antes de compilar: ```bash # Firma iOS (Requerido) BUILD_CERTIFICATE_BASE64="" BUILD_PROVISION_PROFILE_BASE64="" P12_PASSWORD="" # API de App Store Connect (para envío) APPLE_KEY_ID="ABC1234567" APPLE_ISSUER_ID="00000000-0000-0000-0000-000000000000" APPLE_KEY_CONTENT="" # Configuración Adicional APP_STORE_CONNECT_TEAM_ID="1234567890" APPLE_PROFILE_NAME="App Store com.example.app" ``` ### Generando Credenciales en Base64 [Section titled “Generando Credenciales en Base64”](#generando-credenciales-en-base64) Tip Usa estos comandos para codificar tus certificados para Capgo: **Certificado (.p12):** ```bash base64 -i TuCertificado.p12 | pbcopy ``` **Perfil de Aprovisionamiento (.mobileprovision):** ```bash base64 -i TuPerfil.mobileprovision | pbcopy ``` **Clave de Aplicación Store Connect (.p8):** ```bash base64 -i AuthKey_ABC1234567.p8 | pbcopy ``` La cadena base64 ahora está en tu portapapeles - pégala en tu Variable de entorno o secretos de CI/CD. ## Compilando para iOS [Section titled “Compilando para iOS”](#compilando-para-ios) ### Compilación de Depuración (Development) [Section titled “Compilación de Depuración (Development)”](#compilación-de-depuración-development) ```bash npx @capgo/cli@latest build com.example.app \ --platform ios \ --build-mode debug ``` Esto crea una compilación de desarrollo que se puede instalar en dispositivos registrados. ### Compilación de Lanzamiento (Aplicación Store) [Section titled “Compilación de Lanzamiento (Aplicación Store)”](#compilación-de-lanzamiento-aplicación-store) ```bash npx @capgo/cli@latest build com.example.app \ --platform ios \ --build-mode release ``` Esto crea una compilación de Aplicación Store y automáticamente la envía a TestFlight si tienes configuradas las credenciales de la API de Aplicación Store Connect. ## Integración CI/CD [Section titled “Integración CI/CD”](#integración-cicd) ### Ejemplo de GitHub Actions [Section titled “Ejemplo de GitHub Actions”](#ejemplo-de-github-actions) ```yaml name: Build iOS App on: push: branches: [main] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - name: Setup Node.js uses: actions/setup-node@v6 with: node-version: '24' - name: Install dependencies run: npm ci - name: Build web assets run: npm run build - name: Sync Capacitor run: npx cap sync ios - name: Build iOS app env: CAPGO_TOKEN: ${{ secrets.CAPGO_TOKEN }} BUILD_CERTIFICATE_BASE64: ${{ secrets.IOS_CERTIFICATE }} BUILD_PROVISION_PROFILE_BASE64: ${{ secrets.IOS_PROVISION_PROFILE }} P12_PASSWORD: ${{ secrets.P12_PASSWORD }} APPLE_KEY_ID: ${{ secrets.APPLE_KEY_ID }} APPLE_ISSUER_ID: ${{ secrets.APPLE_ISSUER_ID }} APPLE_KEY_CONTENT: ${{ secrets.APPLE_KEY_CONTENT }} APP_STORE_CONNECT_TEAM_ID: ${{ secrets.TEAM_ID }} run: | npx @capgo/cli@latest build ${{ secrets.APP_ID }} \ --platform ios \ --build-mode release ``` ## Detalles del Proceso de Compilación [Section titled “Detalles del Proceso de Compilación”](#detalles-del-proceso-de-compilación) ### Qué Sucede Durante una Compilación iOS [Section titled “Qué Sucede Durante una Compilación iOS”](#qué-sucede-durante-una-compilación-ios) 1. **Aprovisionamiento de Máquina** (1-2 minutos) * Se aprovisiona o asigna un Mac mini de Scaleway * macOS 15 con Xcode preinstalado * Se ejecutan scripts de arranque (si es el primer uso) 2. **Aislamiento de Usuario** (\~10 segundos) * Se crea usuario macOS único: `job-` * Directorio home dedicado: `/Users/job-` * Se crea espacio de trabajo aislado 3. **Configuración de Proyecto** (\~30 segundos) * Zip del proyecto descargado desde R2 * Extraído al espacio de trabajo * Credenciales inyectadas como Variables de entorno 4. **Compilación Fastlane** (3-8 minutos) * Llavero creado con certificado de firma * Perfil de aprovisionamiento instalado * Comando de compilación Xcode ejecutado * Archivo IPA generado 5. **Envío a Aplicación Store** (1-2 minutos, si está configurado) * IPA subido a Aplicación Store Connect * Enviado a TestFlight * El procesamiento comienza del lado de Apple 6. **Limpieza** (inmediata) * Cuenta de usuario eliminada y destruida * Archivos del espacio de trabajo eliminados * Archivos temporales borrados 7. **Liberación de Máquina** (después de 24 horas) * La máquina Mac es destruida * Todos los datos eliminados permanentemente ### Pila de Compilación [Section titled “Pila de Compilación”](#pila-de-compilación) Nuestro entorno de compilación iOS incluye: * **macOS**: 15 (última versión estable) * **Xcode**: Última versión estable * **Fastlane**: Última versión estable * **CocoaPods**: Última versión estable * **Node.js**: 18.x (LTS) * **Ruby**: Ruby del sistema con bundler ## Tiempos de Compilación [Section titled “Tiempos de Compilación”](#tiempos-de-compilación) Tiempos típicos de compilación iOS: | Tipo de Compilación | Primera Compilación | Compilaciones Subsecuentes\* | | ------------------- | ------------------- | ---------------------------- | | Depuración | 5-7 minutos | 4-6 minutos | | Lanzamiento | 7-10 minutos | 5-8 minutos | \*Las compilaciones subsecuentes pueden ser más rápidas si se reutiliza la misma máquina dentro de la ventana de 24 horas. Note Las compilaciones iOS cuestan **2× la tarifa base** debido a los requisitos de hardware Mac dedicado. ## Solución de Problemas [Section titled “Solución de Problemas”](#solución-de-problemas) ### Problemas Comunes [Section titled “Problemas Comunes”](#problemas-comunes) **“Firma de código falló”** * Verifica que tu certificado sea para el tipo de distribución correcto * Asegúrate de que el perfil de aprovisionamiento coincida con tu ID de aplicación * Verifica que P12\_PASSWORD sea correcta **“El perfil de aprovisionamiento no incluye el certificado de firma”** * Regenera tu perfil de aprovisionamiento incluyendo el certificado * Vuelve a descargar y vuelve a codificar el perfil **“Autenticación de Aplicación Store Connect falló”** * Verifica APPLE\_KEY\_ID, APPLE\_ISSUER\_ID y APPLE\_KEY\_CONTENT * Asegúrate de que la clave API no haya sido revocada * Verifica que la clave tenga el rol “Developer” o superior **“Tiempo de espera de compilación agotado después de 10 minutos”** * Verifica si tu aplicación tiene dependencias nativas grandes * Considera optimizar tu Podfile * Contacta soporte si las compilaciones consistentemente agotan el tiempo ### Registros de Depuración [Section titled “Registros de Depuración”](#registros-de-depuración) Todos los registros de compilación se transmiten en tiempo real. Busca estas fases clave: ```plaintext ✔ Máquina asignada: m-abc123 → Creando usuario: job-abc123 → Instalando dependencias CocoaPods... → Compilando aplicación iOS... → Firmando código con certificado... → Subiendo a App Store Connect... ✔ Compilación exitosa ``` Si una compilación falla, el Error se mostrará claramente en los registros con el mensaje de Error específico de Fastlane/Xcode. ## Mejores Prácticas [Section titled “Mejores Prácticas”](#mejores-prácticas) ### 1. Probar Localmente Primero [Section titled “1. Probar Localmente Primero”](#1-probar-localmente-primero) Siempre asegúrate de que tu compilación iOS funcione localmente antes de usar la compilación en la nube: ```bash npx cap open ios # Compilar en Xcode ``` ### 2. Usar Variables de Entorno [Section titled “2. Usar Variables de Entorno”](#2-usar-variables-de-entorno) Nunca confirmes certificados o claves en tu repositorio. Siempre usa: * Secretos de CI/CD (GitHub, GitLab) * Variables de entorno * Gestión de secretos segura ### 3. Cachear Dependencias [Section titled “3. Cachear Dependencias”](#3-cachear-dependencias) Para compilaciones más rápidas, asegúrate de que tu `package.json` y `Podfile.lock` estén confirmados en control de versiones. ### 4. Monitorear Tiempo de Compilación [Section titled “4. Monitorear Tiempo de Compilación”](#4-monitorear-tiempo-de-compilación) Mantén un ojo en la duración de la compilación para optimizar costos: ```bash # El CLI muestra el tiempo de compilación al final Compilación exitosa en 6m 42s (13.4 minutos de facturación a tarifa 2×) ``` ## Próximos Pasos [Section titled “Próximos Pasos”](#próximos-pasos) * [Compilaciones Android](/docs/cli/cloud-build/android/) - Configurar compilaciones Android * [Solución de problemas](/docs/cli/cloud-build/troubleshooting/) - Problemas comunes de compilación * [Referencia CLI](/docs/cli/reference/build/) - Documentación completa de comandos ## ¿Necesitas Ayuda? [Section titled “¿Necesitas Ayuda?”](#necesitas-ayuda) * [Guía de Solución de Problemas](/docs/cli/cloud-build/troubleshooting/) * [Comunidad Discord](https://discord.com/invite/VnYRvBfgA6) * Email: # Solución de Problemas > Problemas comunes y soluciones para Capgo Construcción en la nube Soluciones a problemas comunes al compilar aplicaciones nativas con Capgo Construcción en la nube. ## Fallos de Compilación [Section titled “Fallos de Compilación”](#fallos-de-compilación) ### ”Subida fallida” o “Tiempo de conexión agotado” [Section titled “”Subida fallida” o “Tiempo de conexión agotado””](#subida-fallida-o-tiempo-de-conexión-agotado) **Síntomas:** * La compilación falla durante la subida del proyecto * Errores de tiempo de espera después de 60 segundos **Soluciones:** 1. **Verifica tu conexión a internet** ```bash # Prueba la conexión a Capgo curl -I https://api.capgo.app ``` 2. **Reduce el tamaño del proyecto** * Asegúrate de que `node_modules/` no se esté subiendo (debería estar auto-excluido) * Busca archivos grandes en tu proyecto: ```bash find . -type f -size +10M ``` 3. **Verifica la expiración de la URL de subida** * Las URLs de subida expiran después de 1 hora * Si obtienes un Error de URL expirada, vuelve a ejecutar el comando de compilación Tip Los proyectos grandes (>200MB) pueden alcanzar los límites de tiempo de espera. Contacta soporte para opciones empresariales. ### ”Tiempo de espera de compilación agotado después de 10 minutos” [Section titled “”Tiempo de espera de compilación agotado después de 10 minutos””](#tiempo-de-espera-de-compilación-agotado-después-de-10-minutos) **Síntomas:** * La compilación excede el tiempo máximo permitido * El estado muestra `timeout` **Soluciones:** 1. **Optimiza las dependencias** * Elimina paquetes npm no utilizados * Usa `npm prune --production` antes de compilar 2. **Verifica problemas de red en la compilación** * Algunas dependencias pueden descargar archivos grandes durante la compilación * Considera pre-cachear con un archivo de bloqueo 3. **Revisa las dependencias nativas** ```bash # iOS - verifica Podfile para dependencias pesadas cat ios/App/Podfile # Android - verifica build.gradle cat android/app/build.gradle ``` 4. **Contacta soporte** * Si tu aplicación legítimamente necesita más tiempo * Podemos ajustar los límites para casos de uso específicos ## Problemas de Autenticación [Section titled “Problemas de Autenticación”](#problemas-de-autenticación) ### ”Clave API inválida” o “No autorizado” [Section titled “”Clave API inválida” o “No autorizado””](#clave-api-inválida-o-no-autorizado) **Síntomas:** * La compilación falla inmediatamente con Error de autenticación * Errores 401 o 403 **Soluciones:** 1. **Verifica que la clave API sea correcta** ```bash # Prueba con un comando simple npx @capgo/cli@latest app list ``` 2. **Verifica los permisos de la clave API** * La clave debe tener permisos `write` o `all` * Verifica en el panel de Capgo bajo Claves API 3. **Asegúrate de que se esté leyendo la clave API** ```bash # Verifica la variable de entorno echo $CAPGO_TOKEN # O verifica el archivo .capgo local cat .capgo ``` 4. **Re-autentícate** ```bash npx @capgo/cli@latest login ``` ### ”Aplicación no encontrada” o “Sin permiso para esta aplicación” [Section titled “”Aplicación no encontrada” o “Sin permiso para esta aplicación””](#aplicación-no-encontrada-o-sin-permiso-para-esta-aplicación) **Síntomas:** * La autenticación funciona pero hay Error específico de la aplicación **Soluciones:** 1. **Verifica que la aplicación esté registrada** ```bash npx @capgo/cli@latest app list ``` 2. **Verifica que el ID de la aplicación coincida** * Verifica el appId en `capacitor.config.json` * Asegúrate de que el comando Usar el ID de aplicación correcto 3. **Verifica el acceso a la organización** * Verifica que estés en la organización correcta * La clave API debe tener acceso a la organización de la aplicación ## Problemas de Compilación iOS [Section titled “Problemas de Compilación iOS”](#problemas-de-compilación-ios) ### ”Firma de código falló” [Section titled “”Firma de código falló””](#firma-de-código-falló) **Síntomas:** * La compilación falla durante la fase de firma de código * Errores de Xcode sobre certificados o perfiles **Soluciones:** 1. **Verifica que el tipo de certificado coincida con el tipo de compilación** * Las compilaciones de desarrollo necesitan certificados de Desarrollo * Las compilaciones de Aplicación Store necesitan certificados de Distribución 2. **Verifica que el certificado y el perfil coincidan** ```bash # Decodifica e inspecciona tu certificado echo $BUILD_CERTIFICATE_BASE64 | base64 -d > cert.p12 openssl pkcs12 -in cert.p12 -nokeys -passin pass:$P12_PASSWORD | openssl x509 -noout -subject ``` 3. **Asegúrate de que el perfil de aprovisionamiento sea válido** * Verifica la fecha de expiración * Verifica que incluya tu ID de aplicación * Confirma que incluya el certificado 4. **Regenera las credenciales** * Elimina el certificado/perfil antiguo * Crea nuevos en el portal de desarrollador de Apple * Vuelve a codificar y actualiza las Variables de entorno ### ”El perfil de aprovisionamiento no incluye el certificado de firma” [Section titled “”El perfil de aprovisionamiento no incluye el certificado de firma””](#el-perfil-de-aprovisionamiento-no-incluye-el-certificado-de-firma) **Síntomas:** * Xcode no puede encontrar el certificado en el perfil **Soluciones:** 1. **Descarga el perfil más reciente de Apple** * Ve a Apple Developer → Certificados, IDs y Perfiles * Descarga el perfil de aprovisionamiento * Asegúrate de que incluya tu certificado 2. **Verifica que el certificado esté en el perfil** ```bash # Extrae el perfil echo $BUILD_PROVISION_PROFILE_BASE64 | base64 -d > profile.mobileprovision # Ver contenido del perfil security cms -D -i profile.mobileprovision ``` 3. **Recrea el perfil con el certificado correcto** * En el portal de desarrollador de Apple, edita el perfil * Asegúrate de que tu certificado de distribución esté seleccionado * Descarga y vuelve a codificar ### ”Autenticación de Aplicación Store Connect falló” [Section titled “”Autenticación de Aplicación Store Connect falló””](#autenticación-de-aplicación-store-connect-falló) **Síntomas:** * La subida a TestFlight falla * Errores de clave API **Soluciones:** 1. **Verifica las credenciales de la clave API** * Verifica APPLE\_KEY\_ID (debería tener 10 caracteres) * Verifica APPLE\_ISSUER\_ID (debería estar en formato UUID) * Verifica que APPLE\_KEY\_CONTENT esté correctamente codificado en base64 2. **Prueba la clave API localmente** ```bash # Decodifica la clave echo $APPLE_KEY_CONTENT | base64 -d > AuthKey.p8 # Prueba con fastlane (si está instalado) fastlane pilot list ``` 3. **Verifica los permisos de la clave API** * La clave necesita el rol “Developer” o superior * Verifica en Aplicación Store Connect → Usuarios y Acceso → Claves 4. **Asegúrate de que la clave no haya sido revocada** * Verifica en Aplicación Store Connect * Genera una nueva clave si es necesario ### ”La instalación de Pod falló” [Section titled “”La instalación de Pod falló””](#la-instalación-de-pod-falló) **Síntomas:** * La compilación falla durante la instalación de CocoaPods * Errores de Podfile **Soluciones:** 1. **Verifica que Podfile.lock esté confirmado** ```bash git status ios/App/Podfile.lock ``` 2. **Prueba pod Instalar localmente** ```bash cd ios/App pod install ``` 3. **Verifica pods incompatibles** * Revisa el Podfile para conflictos de versión * Asegúrate de que todos los pods soporten tu objetivo de implementación iOS 4. **Limpia el caché de pods** ```bash cd ios/App rm -rf Pods rm Podfile.lock pod install # Luego confirma el nuevo Podfile.lock ``` ## Problemas de Compilación Android [Section titled “Problemas de Compilación Android”](#problemas-de-compilación-android) ### ”Contraseña de keystore incorrecta” [Section titled “”Contraseña de keystore incorrecta””](#contraseña-de-keystore-incorrecta) **Síntomas:** * La compilación falla durante la firma * Errores de Gradle sobre el keystore **Soluciones:** 1. **Verifica la contraseña del keystore** ```bash # Prueba el keystore localmente keytool -list -keystore my-release-key.keystore # Ingresa la contraseña cuando se solicite ``` 2. **Verifica las Variables de entorno** ```bash # Asegúrate de que no haya espacios extra o caracteres especiales echo "$KEYSTORE_STORE_PASSWORD" | cat -A echo "$KEYSTORE_KEY_PASSWORD" | cat -A ``` 3. **Verifica la codificación base64** ```bash # Decodifica y prueba echo $ANDROID_KEYSTORE_FILE | base64 -d > test.keystore keytool -list -keystore test.keystore ``` ### ”Alias de clave no encontrado” [Section titled “”Alias de clave no encontrado””](#alias-de-clave-no-encontrado) **Síntomas:** * La firma falla con Error de alias **Soluciones:** 1. **Lista los alias del keystore** ```bash keytool -list -keystore my-release-key.keystore ``` 2. **Verifica que el alias coincida exactamente** * El alias distingue mayúsculas de minúsculas * Verifica errores tipográficos en KEYSTORE\_KEY\_ALIAS 3. **Usa el alias correcto del keystore** ```bash # Actualiza la variable de entorno para que coincida export KEYSTORE_KEY_ALIAS="el-nombre-exacto-del-alias" ``` ### ”Compilación Gradle falló” [Section titled “”Compilación Gradle falló””](#compilación-gradle-falló) **Síntomas:** * Errores genéricos de Gradle * Problemas de compilación o dependencias **Soluciones:** 1. **Prueba la compilación localmente primero** ```bash cd android ./gradlew clean ./gradlew assembleRelease ``` 2. **Verifica las dependencias faltantes** * Revisa los archivos Construir.gradle * Asegúrate de que todos los Plugins estén listados en las dependencias 3. **Verifica la compatibilidad de la versión de Gradle** ```bash # Verifica la versión de gradle cat android/gradle/wrapper/gradle-wrapper.properties ``` 4. **Limpia el caché de Gradle** ```bash cd android ./gradlew clean rm -rf .gradle build ``` ### ”Subida a Play Store falló” [Section titled “”Subida a Play Store falló””](#subida-a-play-store-falló) **Síntomas:** * La compilación tiene éxito pero la subida falla * Errores de cuenta de servicio **Soluciones:** 1. **Verifica el JSON de la cuenta de servicio** ```bash # Decodifica y verifica el formato echo $PLAY_CONFIG_JSON | base64 -d | jq . ``` 2. **Verifica los permisos de la cuenta de servicio** * Ve a Play Console → Configuración → Acceso a la API * Asegúrate de que la cuenta de servicio tenga acceso a tu aplicación * Otorga el permiso “Lanzar a canales de prueba” 3. **Verifica que la aplicación esté configurada en Play Console** * La aplicación debe crearse primero en Play Console * Al menos un APK debe subirse manualmente inicialmente 4. **Verifica que la API esté habilitada** * La API de Google Play Developer debe estar habilitada * Verifica en Google Cloud Console ## Problemas Generales [Section titled “Problemas Generales”](#problemas-generales) ### ”Trabajo no encontrado” o “Estado de compilación no disponible” [Section titled “”Trabajo no encontrado” o “Estado de compilación no disponible””](#trabajo-no-encontrado-o-estado-de-compilación-no-disponible) **Síntomas:** * No se puede verificar el estado de compilación * Errores de ID de trabajo **Soluciones:** 1. **Espera un momento y reintenta** * Los trabajos de compilación pueden tardar unos segundos en inicializarse 2. **Verifica que el ID de trabajo sea correcto** * Verifica el ID de trabajo de la respuesta inicial de compilación 3. **Verifica que la compilación no haya expirado** * Los datos de compilación están disponibles durante 24 horas ### ”Sincronización de proyecto falló” [Section titled “”Sincronización de proyecto falló””](#sincronización-de-proyecto-falló) **Síntomas:** * La compilación falla antes de que comience la compilación * Errores de archivos faltantes **Soluciones:** 1. **Ejecuta la sincronización de Capacitor localmente** ```bash npx cap sync ``` 2. **Asegúrate de que todos los archivos nativos estén confirmados** ```bash git status ios/ android/ ``` 3. **Verifica archivos nativos ignorados por Git** * Revisa .gitignore * Asegúrate de que los archivos de configuración importantes no estén ignorados ### ”Compilación exitosa pero no veo la salida” [Section titled “”Compilación exitosa pero no veo la salida””](#compilación-exitosa-pero-no-veo-la-salida) **Síntomas:** * La compilación muestra éxito pero no hay enlace de descarga **Soluciones:** 1. **Verifica la configuración de compilación** * El almacenamiento de artefactos puede no estar configurado * Para beta pública, contacta soporte sobre acceso a artefactos 2. **Para envío a TestFlight de iOS** * Verifica Aplicación Store Connect * El procesamiento puede tardar 5-30 minutos después de la subida 3. **Para Play Store de Android** * Verifica Play Console → Pruebas → Prueba interna * El procesamiento puede tardar unos minutos ## Problemas Específicos de CI/CD [Section titled “Problemas Específicos de CI/CD”](#problemas-específicos-de-cicd) ### GitHub Actions: “Comando no encontrado” [Section titled “GitHub Actions: “Comando no encontrado””](#github-actions-comando-no-encontrado) **Síntomas:** * `npx @capgo/cli` falla en CI **Soluciones:** 1. **Asegúrate de que Node.js esté instalado** ```yaml - uses: actions/setup-node@v6 with: node-version: '24' ``` 2. **Instala el CLI explícitamente** ```yaml - run: npm install -g @capgo/cli ``` ### GitHub Actions: “Secretos no encontrados” [Section titled “GitHub Actions: “Secretos no encontrados””](#github-actions-secretos-no-encontrados) **Síntomas:** * Variables de entorno vacías en la compilación **Soluciones:** 1. **Verifica que los secretos estén configurados** * Ve a Configuración del repositorio → Secretos y Variables → Actions * Agrega todos los secretos requeridos 2. **Usa la sintaxis correcta** ```yaml env: CAPGO_TOKEN: ${{ secrets.CAPGO_TOKEN }} ``` 3. **Verifica que los nombres de secretos coincidan** * Los nombres distinguen mayúsculas de minúsculas * Sin errores tipográficos en las referencias de secretos ## Obtener Más Ayuda [Section titled “Obtener Más Ayuda”](#obtener-más-ayuda) ### Habilitar Registro Detallado [Section titled “Habilitar Registro Detallado”](#habilitar-registro-detallado) ```bash # Agrega la bandera debug (cuando esté disponible) npx @capgo/cli@latest build com.example.app --verbose ``` ### Recopilar Información de Compilación [Section titled “Recopilar Información de Compilación”](#recopilar-información-de-compilación) Al contactar soporte, incluye: 1. **Comando de compilación usado** ```bash npx @capgo/cli@latest build com.example.app --platform ios ``` 2. **Mensaje de Error** (salida completa) 3. **ID de trabajo** (de la salida de compilación) 4. **Registros de compilación** (copia la salida completa del terminal) 5. **Información del entorno** ```bash node --version npm --version npx @capgo/cli --version ``` ### Contactar Soporte [Section titled “Contactar Soporte”](#contactar-soporte) * **Discord**: [Únete a nuestra comunidad](https://discord.com/invite/VnYRvBfgA6) * **Email**: * **Documentación**: [Documentos de Capgo](/docs/) ### Limitaciones Conocidas [Section titled “Limitaciones Conocidas”](#limitaciones-conocidas) Limitaciones actuales durante la beta pública: * Tiempo máximo de compilación: 10 minutos * Tamaño máximo de subida: \~500MB * Las compilaciones iOS requieren arrendamientos de Mac de 24 horas, la compilación en Mac se pondrá en cola para asegurar un uso óptimo * La descarga de artefactos de compilación puede no estar disponible Estas limitaciones pueden ajustarse según los comentarios. ## Recursos Adicionales [Section titled “Recursos Adicionales”](#recursos-adicionales) * [Comenzar](/docs/cli/cloud-build/getting-started/) - Guía de configuración inicial * [Compilaciones iOS](/docs/cli/cloud-build/ios/) - Configuración específica de iOS * [Compilaciones Android](/docs/cli/cloud-build/android/) - Configuración específica de Android * [Referencia CLI](/docs/cli/reference/build/) - Documentación completa de comandos # Commands > Capgo CLI documentación, cómo utilizarla y para qué sirve ### Uso [Section titled “Uso”](#uso) Todos los comandos deben ejecutarse en la carpeta de su aplicación con el proyecto del condensador encendido correctamente. [Capacitor Tiempo de ejecución nativo multiplataforma para aplicaciones web ](https://capacitorjs.com/docs/getting-started/) ### **Inicio** [Section titled “Inicio”](#inicio) `npx @capgo/cli@latest init [apikey]` Este método está aquí para incorporarlo paso a paso. Agregará su aplicación a Capgo. Agregará el código a su aplicación para validar la actualización. Asimismo, construirá su aplicación. Además, cargará su aplicación en Capgo. Y te ayudará a comprobar si la actualización funciona. ### **Iniciar sesión** [Section titled “Iniciar sesión”](#iniciar-sesión) `npx @capgo/cli login [apikey]` Este método está aquí para recordar el `apikey` por usted. Note use `--apikey=********` en cualquier comando para anularlo **Opcionalmente puedes regalar:** `--local` Esto almacenará su **apikey** en el repositorio local y la ignorará. ## **Médico** [Section titled “Médico”](#médico) `npx @capgo/cli doctor` Comando para verificar si está actualizado con los paquetes Capgo. Este comando también será útil para informar errores. ## Aplicación [Section titled “Aplicación”](#aplicación) ### **Agregar** [Section titled “Agregar”](#agregar) `npx @capgo/cli app add [appId]` `[appId]` el ID de su aplicación, el formato `com.test.app` se explica [aquí](https://capacitorjs.com/docs/cli/commands/init/). > 💡 Todas las opciones se adivinarán en su configuración si no se proporcionan. Opcionalmente puedes dar: * `--icon [/path/to/my/icon]` para mostrar un ícono personalizado en la aplicación web Capgo. * `--name [test]` para tener un nombre personalizado en la lista. * `--apikey [key]` API clave para vincular a su cuenta. * `--retention [retention]` período de retención del paquete de aplicaciones en días, 0 de forma predeterminada = infinito. Ejemplo de `capacitor.config.json` para appId y AppName, el ícono se adivina en la carpeta de recursos ```json { "appId": "ee.forgr.capacitor_go", "appName": "Capgo", "webDir": "dist" } ``` ### **Establecer** [Section titled “Establecer”](#establecer) `npx @capgo/cli app set [appId]` `[appId]` es el ID de su aplicación, el formato se explica [aquí](https://capacitorjs.com/docs/cli/commands/init/). Opcionalmente puedes dar: * `--icon [/path/to/my/icon]` para mostrar un ícono personalizado en la aplicación web Capgo. * `--name [test]` para tener un nombre personalizado en la lista. * `--retention [retention]` período de retención del paquete de aplicaciones en días, 0 de forma predeterminada = infinito. * `--apikey [key]` API clave para vincular a su cuenta. ### **Lista** [Section titled “Lista”](#lista) `npx @capgo/cli app list [appId]` `[appId]` el ID de su aplicación, el formato `com.test.app` se explica [aquí](https://capacitorjs.com/docs/cli/commands/init/). Opcionalmente puedes dar: * `--apikey [key]` API clave para vincular a su cuenta. ### **Eliminar** [Section titled “Eliminar”](#eliminar) `npx @capgo/cli app delete [appId]` `[appId]` el ID de su aplicación, el formato `com.test.app` se explica [aquí](https://capacitorjs.com/docs/cli/commands/init/). Opcionalmente puedes dar: * `--apikey [key]` API clave para vincular a su cuenta. * `--bundle` con el número de versión solo eliminará esta versión. ### Depurar [Section titled “Depurar”](#depurar) `npx @capgo/cli app debug [appId]` `[appId]` el ID de su aplicación, el formato `com.test.app` se explica [aquí](https://capacitorjs.com/docs/cli/commands/init/). Opcionalmente puedes dar: * `--apikey [key]` API clave para vincular a su cuenta. * `--device` con el dispositivo específico que desea depurar ### Configuración [Section titled “Configuración”](#configuración) `npx @capgo/cli app setting [path]` Edite la configuración Capacitor. `[path]`: ruta de la configuración que desea cambiar. Por ejemplo, para cambiar `appId`, proporcione `appId`. Si desea deshabilitar la actualización automática en `capacitor-updater`, proporcione `plugins.CapacitorUpdater.autoUpdate` ¡DEBE proporcionar `--string` o `--bool`! Opciones: * `--string ` - establece la configuración en una cadena * `--bool ` - establece la configuración en booleano ## Paquete### Subir [Section titled “Paquete### Subir”](#paquete-subir) `npx @capgo/cli bundle upload [appId]` `[appId]` es el ID de su aplicación, el formato se explica [aquí](https://capacitorjs.com/docs/cli/commands/init/). Opcionalmente puedes dar: * `--apikey ` API clave para vincular a su cuenta. * `--path ` Ruta de la carpeta a cargar. * `--channel ` Canal al que vincular. * `--external ` Enlace a una URL externa en lugar de cargarlo en la nube Capgo. * `--iv-session-key ` Establezca el IV y la clave de sesión para la URL del paquete externo. * `--s3-endpoint ` URL del punto final de S3. No funciona con cargas delta ni con la opción externa. * `--s3-region ` Región para su depósito S3. * `--s3-apikey ` API clave para su punto final S3. * `--s3-apisecret ` API secreto para su punto final S3. * `--s3-bucket-name ` Nombre de su depósito de AWS S3. * `--s3-port ` Puerto para su punto final S3. * `--no-s3-ssl` Deshabilitar SSL para carga S3. * `--key ` Ruta personalizada para la clave de firma pública (sistema v1). * `--key-data ` Clave de firma pública (sistema v1). * `--key-v2 ` Ruta personalizada para la clave de firma privada (sistema v2). * `--key-data-v2 ` Clave de firma privada (sistema v2). * `--bundle-url` Imprime la URL del paquete en la salida estándar. * `--no-key` Ignorar la clave de firma y enviar una actualización clara. * `--no-code-check` Ignore la verificación si se llama a notifyAppReady() en el código fuente y el índice presente en la carpeta raíz. * `--display-iv-session` Muestra en la consola el IV y la clave de sesión utilizados para cifrar la actualización. * `--bundle ` Número de versión del paquete que se va a cargar. * `--min-update-version ` Versión mínima requerida para actualizar a esta versión. Se usa solo si la desactivación de la actualización automática está configurada en metadatos en el canal. * `--auto-min-update-version` Establece la versión mínima de actualización según los paquetes nativos. * `--ignore-metadata-check` Ignora la verificación de metadatos (node\_modules) al cargar. * `--ignore-checksum-check` Ignora la verificación de suma de verificación al cargar. * `--timeout ` Tiempo de espera para el proceso de carga en segundos. * `--delta` Carga archivos Delta (manifiesto) junto con el paquete completo. * `--delta-only` Carga solo actualizaciones Delta (manifiesto), omitiendo el paquete completo. * `--no-delta` Deshabilita las cargas Delta (manifiesto) (útil si `directUpdate` está habilitado pero desea un paquete completo). * `--tus` Sube el paquete usando el protocolo tus. * `--multipart` Utiliza un protocolo multiparte para cargar datos en S3. En desuso, utilice TUS en su lugar. * `--encrypted-checksum ` Una suma de comprobación cifrada (firma). Se utiliza solo al cargar un paquete externo. * `--package-json ` Una ruta al paquete.json. Útil para monorepos. * `--auto-set-bundle` Configure el paquete en capacitor.config.json. * `--node-modules ` Una lista de rutas a node\_modules. Útil para monorepos (separados por comas, por ejemplo: ../../node\_modules,./node\_modules) > ⭐️ External option helps to unlock 2 cases: corporate with privacy concern, don’t send the code to a third part and app bigger than 200 MB. Con esta configuración, Capgo almacena solo el enlace al zip y envía el enlace a todas las aplicaciones. > 👀 Capgo la nube nunca mira lo que hay en el enlace (para opción externa) o en el código cuando se almacena. > 🔑 You can add a second layer of security by using encryption, then Capgo will not be able to look or modify anything, it becomes “trustless”.Ejemplo de `package.json` para la versión ```json { "version": "1.0.2" } ``` > ⛔ La versión debe ser mayor que “0.0.0”. > 💡 Don’t forget to update the version number each time you send one, version number cannot be overridden, or reused after deletion for security reason. ### **Lista** [Section titled “Lista”](#lista-1) `npx @capgo/cli bundle list [appId]` `[appId]` el ID de su aplicación, el formato `com.test.app` se explica [aquí](https://capacitorjs.com/docs/cli/commands/init/). Opcionalmente puedes dar: * `--apikey [key]` API clave para vincular a su cuenta. ### **Eliminar** [Section titled “Eliminar”](#eliminar-1) `npx @capgo/cli bundle delete [appId]` `[appId]` el ID de su aplicación, el formato `com.test.app` se explica [aquí](https://capacitorjs.com/docs/cli/commands/init/). Opcionalmente puedes dar: * `--apikey [key]` API clave para vincular a su cuenta. * `--bundle` con el número de versión solo eliminará esta versión. ### Limpieza [Section titled “Limpieza”](#limpieza) en un rango SemVer para una versión mayor a Cloud `npx @capgo/cli bundle cleanup [appId] --bundle=[majorVersion] --keep=[numberToKeep]` `[appId]` el ID de su aplicación, el formato `com.test.app` se explica [aquí](https://capacitorjs.com/docs/cli/commands/init/). Opcionalmente puedes dar: * `--apikey [key]` API clave para vincular a su cuenta. * `--bundle [majorVersion]` una versión para la que desea eliminar paquetes anteriores, mantendrá el último + `numberToKeep`. * `--keep [numberToKeep]` la cantidad de paquetes que desea conservar (predeterminado 4). Por ejemplo: si tiene 10 versiones de 10.0.1 a 10.0.11 y usa `npx @capgo/cli cleanup [appId] --bundle=10.0.0`, eliminará 10.0.1 a 10.0.6. Se mantendrán del 10.0.7 al 10.0.11. If you have 20 versions in total, and you don’t provide a bundle number like this: `npx @capgo/cli cleanup [appId] --keep=2` It will remove 18 versions, and keep the last 2. > Este comando pedirá confirmación, muestra una tabla de lo que se mantendrá y eliminará. Note Este comando ignorará los paquetes que estén actualmente en uso en cualquier canal. ### **Cifrar** [Section titled “Cifrar”](#cifrar) > **Advertencia**: este comando está obsoleto y se eliminará en la próxima versión principal. Utilice el nuevo sistema de cifrado. `npx @capgo/cli bundle encrypt [path/to/zip]` Este comando se utiliza cuando utiliza una fuente externa para almacenar su código o con fines de prueba. Opcionalmente puedes dar: `--key [/path/to/my/private_key]` la ruta de su clave privada. `--key-data [privateKey]` los datos de la clave privada, si desea utilizarlos en línea. El comando imprimirá su `ivSessionKey`y y generará un zip cifrado, para usarlo con el comando de carga o el comando decrit. ### **Cifrar V2** [Section titled “Cifrar V2”](#cifrar-v2) `npx @capgo/cli bundle encrypt [path/to/zip] [checksum]` Este comando se utiliza cuando utiliza una fuente externa para almacenar su código o con fines de prueba. La suma de comprobación es el sha256 del paquete (generado por —key-v2), se utiliza para verificar la integridad del archivo después del descifrado. Se cifrará con la clave privada y se enviará junto con el paquete. En el cifrado v2, la suma de comprobación se actualiza para convertirse en una “firma” del paquete. Opcionalmente puedes dar: `--key [/path/to/my/private_key]` la ruta de su clave privada. `--key-data [privateKey]` los datos de la clave privada, si desea utilizarlos en línea. `--json` para generar información como json. El comando imprimirá su `ivSessionKey`y y generará un zip cifrado, para usarlo con el comando de carga o el comando decrit. ### **Descifrar** [Section titled “Descifrar”](#descifrar) `npx @capgo/cli bundle decrypt [path/to/zip] [ivSessionKey]` Opcionalmente puedes dar: `--key [/path/to/my/private_key]` la ruta de su clave privada.`--key-data [privateKey]` los datos de la clave privada, si desea utilizarlos en línea. Este comando se utiliza principalmente con fines de prueba, descifrará el zip e imprimirá la clave de sesión descifrada en base64 en la consola. ### **Descifrar V2** [Section titled “Descifrar V2”](#descifrar-v2) `npx @capgo/cli bundle decryptV2 [path/to/zip] [ivSessionKey]` Opcionalmente puedes dar: `--key [/path/to/my/private_key]` la ruta de su clave privada. `--key-data [privateKey]` los datos de la clave privada, si desea utilizarlos en línea. Este comando se utiliza principalmente con fines de prueba, descifrará el zip e imprimirá la clave de sesión descifrada en base64 en la consola. `--checksum [checksum]` la suma de verificación del archivo, verificará la suma de verificación después del descifrado. ### **Código postal** [Section titled “Código postal”](#código-postal) `npx @capgo/cli bundle zip [appId]` `[appId]` es el ID de su aplicación, el formato se explica [aquí](https://capacitorjs.com/docs/cli/commands/init/). Opcionalmente puedes dar: * `--path [/path/to/my/bundle]` para cargar una carpeta específica. * `--bundle [1.0.0]` para establecer el número de versión del paquete del nombre de archivo. * `--name [myapp]` para anular el nombre del archivo. * `--json` para generar información como json. * `--no-code-check` para ignorar la verificación del código y enviar el paquete de todos modos. * `--key-v2` para utilizar el nuevo sistema de cifrado. Esto es necesario ya que el nuevo sistema de cifrado utiliza mejores sumas de verificación para verificar la integridad del archivo. ### **Compatibilidad** [Section titled “Compatibilidad”](#compatibilidad) `npx @capgo/cli bundle compatibility [appId] -c [channelId]` `[appId]` es el ID de su aplicación, el formato se explica [aquí](https://capacitorjs.com/docs/cli/commands/init/). `[channelId]` el nombre de tu nuevo canal. Opcionalmente puedes dar: * `--apikey [key]` API clave para vincular a su cuenta. * `--text` usa texto en lugar de emojis en la tabla * `--channel [channel]` el canal para verificar la compatibilidad. * `--package-json ` Una ruta al paquete.json. Útil para monorepos * `--node-modules ` Una lista de rutas a node\_modules. Útil para monorepos (separados por comas, por ejemplo: ../../node\_modules,./node\_modules) ## Canal [Section titled “Canal”](#canal) ### **Agregar** [Section titled “Agregar”](#agregar-1) `npx @capgo/cli channel add [channelId] [appId]` `[channelId]` el nombre de tu nuevo canal. `[appId]` el ID de su aplicación, el formato `com.test.app` se explica [aquí](https://capacitorjs.com/docs/cli/commands/init/). ### **Eliminar** [Section titled “Eliminar”](#eliminar-2) `npx @capgo/cli channel delete [channelId] [appId]` `[channelId]` el nombre de tu canal que deseas eliminar. `[appId]` el ID de su aplicación, el formato `com.test.app` se explica [aquí](https://capacitorjs.com/docs/cli/commands/init/). ### **Lista** [Section titled “Lista”](#lista-2) `npx @capgo/cli channel list [appId]` `[appId]` el ID de su aplicación, el formato `com.test.app` se explica [aquí](https://capacitorjs.com/docs/cli/commands/init/). Opcionalmente puedes dar: * `--apikey [key]` API clave para vincular a su cuenta. ### **Establecer** [Section titled “Establecer”](#establecer-1) `npx @capgo/cli channel set [channelId] [appId]` `[appId]` es el ID de su aplicación, el formato se explica [aquí](https://capacitorjs.com/docs/cli/commands/init/). Opcionalmente puedes dar:\* `--bundle [1.2.3]` tu paquete de aplicaciones ya enviado a la nube, para vincularlo a un canal. * `--latest` obtiene la versión del paquete de `package.json:version`, no se puede usar con `--bundle`. * `--state [ normal | default ]` establece el estado del canal, puede ser `normal` o `default`. Un canal debe ser `default`. * `--downgrade` permite que el canal envíe una versión anterior a los dispositivos. * `--no-downgrade` no permite que el canal envíe una versión degradada a los dispositivos. * `--upgrade` permite que el canal envíe una versión de actualización (principal) a los dispositivos. * `--no-upgrade` no permite que el canal envíe una versión de actualización (principal) a los dispositivos. * `--ios` permite que el canal envíe la versión a los dispositivos iOS. * `--no-ios` no permite que el canal envíe la versión a los dispositivos iOS. * `--android` permite que el canal envíe la versión a dispositivos Android. * `--no-android` no permite que el canal envíe la versión a dispositivos Android. * `--self-assign` permite que los dispositivos se autoasignen a este canal. * `--no-self-assign` no permite que los dispositivos se autoasignen a este canal. * `--disable-auto-update STRATEGY` Deshabilita la estrategia de actualización automática para este canal. Las opciones posibles son: mayor, menor, metadatos, ninguno. * `--apikey [key]` API clave para vincular a su cuenta. ## Deshabilitar la estrategia de actualizaciones [Section titled “Deshabilitar la estrategia de actualizaciones”](#deshabilitar-la-estrategia-de-actualizaciones) Hay algunas formas de gestionar la desactivación de actualizaciones para versiones demasiado antiguas.\ Capgo no puede actualizar el código nativo, por lo que no debería ser posible actualizar desde una versión con el código nativo antiguo a una versión con el código nativo actualizado. Hay un par de formas de lograrlo. Primero, la estrategia `major`. Impide una actualización de `0.0.0` -> `1.0.0`. El mayor es el número resaltado (**1**.0.0 y **0**.0.0).\ En segundo lugar está la estrategia `minor`. Impide una actualización de `0.0.0` -> `1.1.0` o una actualización de `1.1.0` a `1.2.0`. **TENGA EN CUENTA** esta estrategia no impide una actualización de `0.1.0` -> `1.1.0` En tercer lugar, la estrategia `patch`. Se agregó a capgo como un modo muy estricto. No se recomienda su uso a menos que comprenda completamente cómo funciona. Para que acepte una actualización se deben cumplir las siguientes condiciones: * La versión principal es la misma entre la versión nueva y la antigua. * El menor es el mismo entre la versión nueva y la antigua. * El parche de la nueva versión es mayor que el parche de la versión anterior. A continuación se muestra un ejemplo de en qué escenarios se permite o deniega la actualización. * 0.0.311 —> 0.0.314 ✅ * 0.0.0 —> 0.0.314 ✅ * 0.0.316 -> 0.0.314 ❌ * 0.1.312 -> 0.0.314 ❌ * 1.0.312 -> 0.0.314 ❌ Por último la estrategia más complicada. La estrategia `metadata`.\ Primero debe saber que inicialmente, después de habilitarlo, las actualizaciones **FALLARÁN** ya que el canal carece de los metadatos necesarios.\ Si al canal le faltan metadatos, verá un mensaje como este: ![No puedo encontrar metadatos](/fail-metadata.webp) Si ve algo como esto, sabrá que debe ir al paquete actual del canal que falla y configurar los metadatos.\ Primero, averigüe qué canal está fallando. Puedes hacerlo mirando la columna `misconfigured` ![Tabla mal configurada](/misconfigured-table.webp)Luego vaya al canal que falla y haga clic en “Número de paquete”. Esto debería llevarlo a la página del paquete. ![Localizar el canal defectuoso](/fail-channel-show.webp) Una vez allí, complete el campo “Versión de actualización mínima”. Esto debería ser un [semver](https://devhints.io/semver/).\ Si el valor que pasas no es un semver obtendrás un error, pero si todo va correctamente deberías ver algo como esto: ![Establecer versión mínima](/set-min-update-version.webp) Ahora bien, probablemente no quieras configurar estos datos manualmente cada vez que actualices. Afortunadamente, CLI evitará que envíes una actualización sin estos metadatos. ![CLI falla sin metadatos](/cli-fail-no-metadata.webp) Para cargar correctamente un paquete cuando se utiliza la opción `metadata`, debe pasar `--min-update-version` con el semver válido. Algo como esto: ![CLI subir con metadatos](/cli-upload-with-metadata.webp) El `--min-update-version` no es la ÚNICA forma de lograr compatibilidad. También existe el `--auto-min-update-version`. Así es como funciona. Primero, echa un vistazo a la versión subida actualmente al canal. Comprueba la compatibilidad de la misma manera que lo haría el comando “compatibilidad de paquete”. En segundo lugar, si la nueva versión es 100% compatible, reutiliza el `min_update_version` de la última versión del canal. De lo contrario, establece `min_update_version` en el número de paquete de la versión recién cargada. Siempre obtendrá información sobre cuál es `min_update_version` al utilizar esta opción. Se verá así: ![Versión mínima de actualización](/min_update_version_info.webp) Si la nueva versión no es compatible, debería verse así ![Versión mínima de actualización no compatible](/min_update_version_not_compatible.webp) ## Cifrado de extremo a extremo (sin confianza) [Section titled “Cifrado de extremo a extremo (sin confianza)”](#cifrado-de-extremo-a-extremo-sin-confianza) Capgo admite el cifrado de extremo a extremo, esto significa que su paquete (código) se cifra antes de enviarlo a la nube y descifrarlo en el dispositivo. Para eso, necesita generar un par de claves RSA; puede usar el siguiente comando para generarlo. El sistema de cifrado es una combinación de RSA y AES, la clave RSA se utiliza para cifrar la clave AES y la clave AES se utiliza para cifrar el archivo. Consulte a continuación para obtener más información sobre el sistema de cifrado. ![Cómo funcionan las criptomonedas](/crypto_explained.webp) Esquema de cifrado ### Crea una clave para tu aplicación [Section titled “Crea una clave para tu aplicación”](#crea-una-clave-para-tu-aplicación) `npx @capgo/cli key create` Opcionalmente, puede proporcionar: `--force` para sobrescribir la clave existente. Este comando creará para usted un par de claves en su aplicación y le pedirá que guarde la clave privada en un lugar seguro. Se recomienda no confirmar la clave privada y no compartirla con nadie. > Después de su prueba local, elimine la clave del archivo de configuración y agréguela en el paso CI con “guardar clave” ### Guardar clave en la configuración de tu aplicación [Section titled “Guardar clave en la configuración de tu aplicación”](#guardar-clave-en-la-configuración-de-tu-aplicación) `npx @capgo/cli key save` Opcionalmente puedes dar: `--key [/path/to/my/public_key]` la ruta de su archivo de clave pública. `--key-data [publicKey]` los datos de clave pública, si desea utilizarlos en línea. Este comando es útil si siguió la recomendación y no confirmó la clave en la configuración de su aplicación. ## Integración Ci [Section titled “Integración Ci”](#integración-ci) Para automatizar su trabajo, le recomiendo que haga que la acción GitHub haga el trabajo de enviar a nuestro servidor. [GitHub tutorial de acción](https://capgo.app/blog/automatic-build-and-release-with-github-actions/) ## Nuestra aplicación de demostración [Section titled “Nuestra aplicación de demostración”](#nuestra-aplicación-de-demostración) [GitHub - Cap-go/aplicación de demostración](https://github.com/Cap-go/demo-app/) No olvide configurar la variable de entorno CI con su clave API # CLI de 0.x a 1.x > Cómo actualizar de 0.x a 1.x No hay cambios significativos en la CLI El cambio disruptivo es principalmente el cambio de nombre del argumento `--version` a `--bundle` para evitar conflictos y seguir la nueva nomenclatura en todas partes # Encriptación > Cómo cifrar tus datos con el nuevo cifrado Esta documentación explicará cómo cifrar tus datos con el nuevo sistema de cifrado y eliminar el anterior Aprende más sobre el nuevo sistema de cifrado en el [blog post](/blog/introducing-end-to-end-security-to-capacitor-updater-with-code-signing) *** Primero, crea un nuevo par de claves con el siguiente comando: ```bash npx @capgo/cli key create ``` Este comando creará un nuevo par de claves en tu aplicación; es imperativo almacenar la clave privada en un lugar seguro. Nunca se debe confirmar la clave privada en el control de código fuente ni compartirla con terceros no confiables Este comando también eliminará la clave antigua de tu configuración de Capacitor, pero no eliminará los archivos de la clave antigua. El CLI los mantiene para permitirte continuar enviando actualizaciones en vivo para las aplicaciones que no han recibido una actualización de la tienda de aplicaciones y que todavía utilizan el Plugin anterior. Esto facilita la migración Cuando la migración te pregunte “¿Quieres configurar el cifrado con el nuevo canal para admitir aplicaciones antiguas y facilitar la migración?”, por favor acepta. Esto agregará una nueva opción “defaultChannel” a tu configuración de Capacitor. Esto hará que tu aplicación Usar el canal “encryption\_v2”. Esto asegurará que el nuevo cifrado sea utilizado solo por aplicaciones que lo soporten. Las aplicaciones que no han recibido una actualización de la tienda de aplicaciones continuarán usando el canal predeterminado anterior *** Ahora, necesitas construir tu paquete JS y subirlo al nuevo canal. Por favor, ejecuta el siguiente comando: ```bash npx @capgo/cli bundle upload --channel encryption_v2 ``` *** Luego, ejecuta este comando para permitir que las aplicaciones se auto-asignen al canal “encryption\_v2” :::Precaución Esto es necesario para que la nueva opción “defaultChannel” funcione ::: ```bash npx @capgo/cli channel set encryption_v2 --self-assign ``` *** Ahora puedes ejecutar la aplicación; utilizará el nuevo sistema de cifrado Para subir el nuevo paquete JS al canal antiguo, solo necesitas ejecutar el siguiente comando: ```bash npx @capgo/cli bundle upload --channel production ``` *** No necesitas preocuparte por la configuración de Capacitor, nunca se sube a Capgo Cuando todos los usuarios hayan actualizado sus aplicaciones (puede tomar hasta 3/4 meses), puedes eliminar el “defaultChannel” de tu configuración de Capacitor Y luego, puedes eliminar el canal antiguo con el siguiente comando: ```bash npx @capgo/cli channel delete encryption_v2 ``` *** Después de eliminar el canal “encryption\_v2”, todas las aplicaciones que lo usen como predeterminado comenzarán a usar el canal “production” # Descripción general Usar la función de Actualizaciones en Vivo de Capgo para actualizar los paquetes JavaScript de su aplicación de forma remota y en tiempo real. Envíe actualizaciones JS directamente a sus usuarios sin pasar por el proceso de revisión de la tienda de aplicaciones para corregir errores y lanzar nuevas funciones instantáneamente. Note Las Actualizaciones en Vivo están limitadas a cambios en los paquetes JavaScript. Si necesita actualizar código nativo, como agregar o eliminar un Plugin o cambiar la configuración del proyecto nativo, deberá enviar una nueva compilación binaria nativa a las tiendas de aplicaciones. ## Cómo funcionan las Actualizaciones en Vivo [Section titled “Cómo funcionan las Actualizaciones en Vivo”](#cómo-funcionan-las-actualizaciones-en-vivo) El sistema de Actualización en Vivo de Capgo tiene dos componentes clave: 1. El SDK de Capgo, que instala en su aplicación. El SDK verifica las actualizaciones disponibles y las descarga en segundo plano. 2. Canales, que le permiten dirigir actualizaciones a grupos específicos de usuarios. Puede usar canales para gestionar diferentes pistas de lanzamiento, como `Production`, `Staging` y `Dev`. Cuando sube un nuevo paquete JS a Capgo y lo asigna a un canal, el SDK de Capgo en las aplicaciones configuradas para ese canal detectará la actualización y la descargará. La próxima vez que la aplicación se reinicie, se cargará el nuevo paquete. ## Primeros pasos [Section titled “Primeros pasos”](#primeros-pasos) Para comenzar a usar las Actualizaciones en Vivo, siga estos pasos: 1. Completo el [Inicio rápido de Capgo](/docs/getting-started/quickstart) para configurar su aplicación en Capgo e instalar el SDK de Capgo. 2. En el código de su aplicación, llame a `CapacitorUpdater.notifyAppReady()` después de que su aplicación haya terminado de inicializarse. Esto le indica al SDK de Capgo que su aplicación está lista para recibir actualizaciones. 3. Compile su paquete JS y súbalo a Capgo: ```shell npm run build npx @capgo/cli@latest bundle upload --channel=production ``` 4. Abra su aplicación y espere a que se descargue la actualización. Puede verificar el estado con: ```shell npx @capgo/cli@latest app debug ``` 5. Una vez que se descargue la actualización, cierre y vuelva a abrir su aplicación para cargar el nuevo paquete. Consulte la guía [Implementación de Actualizaciones en Vivo](/docs/getting-started/deploy) para más detalles. ## La CLI de Capgo [Section titled “La CLI de Capgo”](#la-cli-de-capgo) La CLI de Capgo es una herramienta poderosa que permite a los desarrolladores interactuar con los servicios de Capgo desde sus propios pipelines de CI/CD. Con la CLI, tiene control granular sobre cuándo se producen y despliegan las compilaciones, permitiéndole integrar Capgo en sus flujos de trabajo empresariales existentes. ### ¿Para qué sirve la CLI de Capgo? [Section titled “¿Para qué sirve la CLI de Capgo?”](#para-qué-sirve-la-cli-de-capgo) La CLI de Capgo está diseñada para desarrolladores y equipos que necesitan más control y flexibilidad en sus flujos de trabajo de actualización en vivo. Al usar la CLI en sus pipelines de CI/CD, puede: * Decidir exactamente cuándo construir y desplegar actualizaciones, en lugar de depender de la automatización integrada de Capgo * Insertar sus propios procesos, como firma de código, pruebas de QA o aprobaciones de gerentes, entre los pasos de compilación y despliegue * Integrar Capgo en sus herramientas y flujos de trabajo DevOps existentes ### Autenticación [Section titled “Autenticación”](#autenticación) Para usar la CLI de Capgo, necesitará autenticarse con su clave API. Puede generar una clave API en la configuración de su cuenta de Capgo. Para iniciar sesión y almacenar de forma segura su clave API, ejecute: ```shell npx @capgo/cli@latest login [API_KEY] ``` Este comando se guardará para uso futuro. No necesitará proporcionar su clave API con cada comando después de iniciar sesión. ### Diferencias clave con otras herramientas CLI [Section titled “Diferencias clave con otras herramientas CLI”](#diferencias-clave-con-otras-herramientas-cli) Si está familiarizado con otras herramientas CLI de actualización en vivo, hay algunas cosas clave que debe tener en cuenta sobre la CLI de Capgo: * Capgo utiliza una única CLI tanto para casos de uso de desarrollo como de CI/CD, ya que Capgo se centra únicamente en el conjunto de funciones de actualización en vivo * La CLI de Capgo no requiere un paso de instalación separado. Está incluida en el paquete `@capgo/cli` y se puede ejecutar directamente usando `npx` * La CLI de Capgo está diseñada específicamente para el flujo de trabajo de actualización en vivo, por lo que puede no incluir algunas características o comandos que se encuentran en herramientas CLI de propósito más general ## Siguientes pasos [Section titled “Siguientes pasos”](#siguientes-pasos) [ Canales](/docs/live-Actualizaciones/Canales/) [Aprenda cómo usar canales para gestionar diferentes pistas de lanzamiento y dirigir actualizaciones a usuarios específicos](/docs/live-Actualizaciones/Canales/) [ Reversiones](/docs/live-Actualizaciones/Reversiones/) [Descubra cómo revertir a una versión anterior del paquete JS si una actualización causa problemas](/docs/live-Actualizaciones/Reversiones/) [ Comportamiento de actualización](/docs/live-Actualizaciones/Actualizar-behavior/) [Personalice cómo y cuándo se descargan y aplican las actualizaciones en su aplicación](/docs/live-Actualizaciones/Actualizar-behavior/) [ Actualizaciones rápidas](/docs/live-Actualizaciones/differentials/) [Aprenda cómo usar actualizaciones rápidas para acelerar el proceso de actualización](/docs/live-Actualizaciones/differentials/) # 概要 > Documentación detallada para los comandos de la CLI de Capgo La CLI de Capgo proporciona un conjunto de comandos para gestionar tus aplicaciones y despliegues de Capgo. Esta referencia proporciona información detallada sobre cada comando disponible, incluyendo sus opciones y ejemplos de uso ## Comandos [Section titled “Comandos”](#comandos) [ init](/docs/CLI/Referencia/init/) [Inicializar una nueva aplicación de Capgo](/docs/CLI/Referencia/init/) [ login](/login/) [Autenticarse con el servicio de Capgo](/login/) [ doctor](/docs/CLI/Referencia/doctor/) [Verificar tu configuración de Capgo en busca de posibles problemas](/docs/CLI/Referencia/doctor/) [ Aplicación](/docs/CLI/Referencia/Aplicación/) [Gestionar tus aplicaciones de Capgo](/docs/CLI/Referencia/Aplicación/) [ Paquete](/docs/CLI/Referencia/Paquete/) [Gestionar tus paquetes de aplicaciones](/docs/CLI/Referencia/Paquete/) [ Canal](/docs/CLI/Referencia/Canal/) [Gestionar tus canales de lanzamiento](/docs/CLI/Referencia/Canal/) [ key](/docs/CLI/Referencia/key/) [Gestionar tus claves de firma de aplicaciones](/docs/CLI/Referencia/key/) ## Integración CI [Section titled “Integración CI”](#integración-ci) Para automatizar tu trabajo, recomendamos usar GitHub Actions para enviar tus actualizaciones a Capgo. Consulta nuestro [tutorial de GitHub Actions](https://capgo.app/blog/automatic-build-and-release-with-github-actions/) para más información No olvides configurar las Variables de entorno de tu CI con tu clave API de Capgo ## Aplicación Demo [Section titled “Aplicación Demo”](#aplicación-demo) Para un ejemplo completo de una aplicación Capgo con integración CI, consulta nuestra [aplicación demo en GitHub](https://github.com/Cap-go/demo-app/) # cuenta El comando `account` te permite gestionar tu cuenta de Capgo ### id [Section titled “id”](#id) `npx @capgo/cli account id` Obtiene tu ID de cuenta Opciones: * `-a, --apikey `: Clave API para enlazar a tu cuenta # aplicación El comando `app` te permite gestionar tus aplicaciones de Capgo ### Agregar [Section titled “Agregar”](#agregar) `npx @capgo/cli app add [appId]` Agrega una nueva aplicación a tu cuenta de Capgo `[appId]` es el ID de tu aplicación en el formato `com.example.app`. Consulta la [documentación de Capacitor](https://capacitorjs.com/docs/cli/commands/init/) para más información > 💡 Todas las opciones se obtendrán de tu `capacitor.config.json` si no se proporcionan Opciones: * `--icon [path]`: Ruta a un ícono personalizado para mostrar en la aplicación web de Capgo * `--name [name]`: Nombre personalizado para mostrar en la lista de aplicaciones * `--apikey [key]`: Clave API para vincular a tu cuenta * `--retention [days]`: Período de retención para los paquetes de la aplicación en días (predeterminado: 0 = infinito) ### Establecer [Section titled “Establecer”](#establecer) `npx @capgo/cli app set [appId]` Actualiza una aplicación existente en tu cuenta de Capgo Opciones: * `--icon [path]`: Ruta a un ícono personalizado para mostrar en la aplicación web de Capgo * `--name [name]`: Nombre personalizado para mostrar en la lista de aplicaciones * `--retention [days]`: Período de retención para los paquetes de la aplicación en días (predeterminado: 0 = infinito) * `--apikey [key]`: Clave API para vincular a tu cuenta ### list [Section titled “list”](#list) `npx @capgo/cli app list [appId]` Lista todas las aplicaciones en tu cuenta de Capgo Opciones: * `--apikey [key]`: Clave API para vincular a tu cuenta ### Eliminar [Section titled “Eliminar”](#eliminar) `npx @capgo/cli app delete [appId]` Elimina una aplicación de tu cuenta de Capgo Opciones: * `--apikey [key]`: Clave API para vincular a tu cuenta * `--bundle`: Eliminar solo una versión específica del paquete ### Depuración [Section titled “Depuración”](#depuración) `npx @capgo/cli app debug [appId]` Muestra información de depuración para una aplicación Opciones: * `--apikey [key]`: Clave API para vincular a tu cuenta * `--device`: Depurar un dispositivo específico ### setting [Section titled “setting”](#setting) `npx @capgo/cli app setting [path]` Edita la configuración de Capacitor para una aplicación `[path]` es la ruta a la configuración que deseas cambiar (por ejemplo, `appId` o `plugins.CapacitorUpdater.autoUpdate`) Debes proporcionar ya sea `--string` o `--bool`: * `--string `: Establece la configuración a un valor de cadena * `--bool `: Establece la configuración a un valor booleano # 🏗️ Construir > Compila aplicaciones móviles nativas en la nube y envíalas a Aplicación Store y Play Store directamente desde tu CLI. 🏗️ Compila aplicaciones móviles nativas en la nube y envíalas automáticamente a Aplicación Store y Play Store. ## Descripción General [Section titled “Descripción General”](#descripción-general) El comando `build` te permite compilar tu aplicación Capacitor para iOS y Android en la infraestructura en la nube de Capgo, similar a cómo Expo maneja las compilaciones. Tu aplicación se compila en infraestructura dedicada y puede enviarse automáticamente a las tiendas de aplicaciones. **Estado Actual:** Beta Pública ## ¿Por Qué Usar Construcción en la nube? [Section titled “¿Por Qué Usar Construcción en la nube?”](#por-qué-usar-construcción-en-la-nube) Compilar aplicaciones móviles nativas tradicionalmente requiere: * **Hardware Mac** para compilaciones iOS ($1000+ o costosos minutos de CI) * **Configuración compleja de CI/CD** con caché y credenciales * **Sobrecarga de mantenimiento** para Xcode, Android Studio y SDKs Con Capgo Construcción en la nube, obtienes: * ✅ **No se requiere Mac** - Compila aplicaciones iOS desde cualquier máquina * ✅ **Sin configuración** - No se necesita configuración de CI/CD * ✅ **Probado en batalla** - Construido sobre 3 años de uso interno en producción * ✅ **Seguro** - Sin almacenamiento de registros, limpieza automática * ✅ **Solo nativo** - Tu JavaScript se mantiene privado ## Inicio Rápido [Section titled “Inicio Rápido”](#inicio-rápido) ```bash npx @capgo/cli@latest build com.example.app ``` ¡Eso es todo! Tu aplicación se compilará en la nube y verás registros en tiempo real. ## Secciones de Documentación [Section titled “Secciones de Documentación”](#secciones-de-documentación) ⚠️ Configura las Credenciales PRIMERO **Requerido antes de compilar:** Guarda tus credenciales iOS/Android localmente. [Configurar Credenciales →](/docs/cli/cloud-build/credentials/) Comenzar Crea tu primera compilación nativa en minutos. [Leer guía →](/docs/cli/cloud-build/getting-started/) Compilaciones iOS Configura certificados y compila para Aplicación Store. [Configurar iOS →](/docs/cli/cloud-build/ios/) Compilaciones Android Configura keystores y compila para Play Store. [Configurar Android →](/docs/cli/cloud-build/android/) Solución de Problemas Soluciones a problemas comunes de compilación. [Obtener ayuda →](/docs/cli/cloud-build/troubleshooting/) ## Referencia de Comandos [Section titled “Referencia de Comandos”](#referencia-de-comandos) ### Uso Básico [Section titled “Uso Básico”](#uso-básico) ```bash npx @capgo/cli@latest build [appId] [opciones] ``` ### Ejemplos [Section titled “Ejemplos”](#ejemplos) Compilar para ambas plataformas: ```bash npx @capgo/cli@latest build com.example.app ``` Compilar solo para iOS: ```bash npx @capgo/cli@latest build com.example.app --platform ios ``` Compilar para Android en modo Depuración: ```bash npx @capgo/cli@latest build com.example.app --platform android --build-mode debug ``` Compilar desde un directorio específico: ```bash npx @capgo/cli@latest build com.example.app --path ./my-app ``` ### Opciones [Section titled “Opciones”](#opciones) | Opción | Tipo | Por Defecto | Descripción | | ------------------------- | ------ | ----------------------- | ------------------------------------------------------- | | `appId` | string | Capacitor.config | ID de Aplicación (ej., com.Ejemplo.Aplicación) | | `--path ` | string | Directorio actual | Ruta al directorio de tu proyecto | | `--platform ` | string | both | Plataforma objetivo: `ios`, `android`, o `both` | | `--build-mode ` | string | Lanzamiento | Modo de compilación: `debug` o `release` | | `--build-config ` | string | - | Configuración adicional de compilación como cadena JSON | | `-a, --apikey ` | string | - | Clave API (o usa la Variable de entorno `CAPGO_TOKEN`) | | `--supa-host ` | string | | URL personalizada del host Supabase | | `--supa-anon ` | string | - | Clave anon personalizada de Supabase | ## Cómo Funciona [Section titled “Cómo Funciona”](#cómo-funciona) ```mermaid sequenceDiagram participant Dev as Desarrollador participant CLI as Capgo CLI participant Cloud as Capgo Cloud participant iOS as Constructor Mac participant Android as Constructor Android Dev->>CLI: build com.example.app CLI->>CLI: Comprimir proyecto localmente CLI->>Cloud: Subir a R2 Cloud->>iOS: Aprovisionar Mac (iOS) Cloud->>Android: Iniciar sandbox (Android) iOS-->>CLI: Stream de registros (SSE) Android-->>CLI: Stream de registros (SSE) iOS->>Cloud: Compilación completa Android->>Cloud: Compilación completa Cloud->>iOS: Limpieza (24h después) Cloud->>Android: Limpieza (instantánea) CLI->>Dev: Compilación exitosa ``` ### Proceso de Compilación [Section titled “Proceso de Compilación”](#proceso-de-compilación) 1. **Preparación Local** - Tu proyecto se comprime localmente (excluyendo `node_modules`, archivos ocultos) 2. **Subida** - El archivo zip se sube a almacenamiento en la nube seguro (Cloudflare R2) 3. **Ejecución de Compilación**: * **iOS**: Se aprovisiona máquina Mac dedicada, Fastlane compila y firma * **Android**: Se crea sandbox seguro, Gradle compila y firma 4. **Streaming de Registros** - Registros en tiempo real vía Server-Sent Events (¡no se almacenan!) 5. **Limpieza Automática**: * **iOS**: Archivos eliminados después de 24 horas cuando se libera la máquina * **Android**: Todo se elimina instantáneamente después de la compilación ## Nuestra Experiencia [Section titled “Nuestra Experiencia”](#nuestra-experiencia) Capgo Construcción en la nube no es infraestructura nueva - la hemos estado usando internamente durante **3 años**: * ✅ **Fastlane Personalizado** - Construido específicamente para aplicaciones Capacitor * ✅ **Miles de compilaciones** - Probado en batalla en producción * ✅ **Expertos en Capacitor** - Conocimiento profundo de lo que importa * ✅ **Enfoque solo en nativo** - Tu JavaScript nunca toca nuestros servidores ## Seguridad y Privacidad [Section titled “Seguridad y Privacidad”](#seguridad-y-privacidad) * **Sin almacenamiento de registros** - Los registros solo se transmiten a tu terminal, nunca se guardan * **Sin almacenamiento de artefactos** - Las aplicaciones se envían directamente a Aplicación Store/Play Store, no guardamos nada * **Credenciales auto-eliminadas** - Solo se usan durante la compilación, eliminadas después (máx. 24h) * **Compilaciones aisladas** - Cada compilación se ejecuta en aislamiento * **Tu código sigue siendo tuyo** - Solo compilamos partes nativas, JavaScript permanece local ## Integración CI/CD [Section titled “Integración CI/CD”](#integración-cicd) Funciona en todas partes - GitHub Actions, GitLab CI, o cualquier plataforma CI/CD: ```yaml - name: Build native app env: CAPGO_TOKEN: ${{ secrets.CAPGO_TOKEN }} run: | npm run build npx cap sync npx @capgo/cli@latest build com.example.app \ --platform both \ --build-mode release ``` No necesitas: * Runners Mac * Instalación de Android SDK * Instalación de Xcode * Caché complejo * Gestión de credenciales ## Precios [Section titled “Precios”](#precios) El tiempo de compilación se factura según el uso real: * **Android**: Multiplicador 1× (\~$0.XX por minuto) * **iOS**: Multiplicador 2× (\~$0.XX por minuto, debido al hardware Mac) **Costos típicos:** * Depuración Android: 3 min × 1× = \~$X.XX * Lanzamiento iOS: 7 min × 2× = \~$X.XX Solo pagas por lo que usas. Sin mínimos, sin sorpresas. ## Comparación con Otras Soluciones [Section titled “Comparación con Otras Soluciones”](#comparación-con-otras-soluciones) | Característica | Capgo Construcción en la nube | GitHub Actions (Mac) | Expo EAS | | -------------------------------- | ----------------------------- | -------------------- | --------------------- | | **Mac requerido localmente** | ❌ No | ✅ Sí | ❌ No | | **Complejidad de configuración** | ⭐ Comando único | ⭐⭐⭐ YAML complejo | ⭐⭐ Archivos de config | | **Nativo Capacitor** | ✅ Optimizado | ⚠️ Genérico | ❌ Solo Expo | | **Privacidad de tu código** | ✅ Nunca almacenado | ⚠️ En runners | ⚠️ Subido | | **Costo (iOS)** | 💰 2× base | 💰💰💰 10× costoso | 💰💰 Premium | ## Qué se Compila [Section titled “Qué se Compila”](#qué-se-compila) **Importante:** Capgo compila **solo partes nativas**. ✅ **Nosotros compilamos:** * Código nativo iOS (Swift, Objective-C, proyectos Xcode) * Código nativo Android (Java, Kotlin, proyectos Gradle) * Firma de código y envío a tiendas de aplicaciones ❌ **Tú compilas (localmente):** * JavaScript, HTML, CSS (`npm run build`) * Sincronización Capacitor (`npx cap sync`) * Tus recursos web Esta separación asegura: * **Mejor seguridad** - La lógica de tu aplicación permanece privada * **Compilaciones más rápidas** - Sin compilaciones web duplicadas * **Responsabilidad clara** - Tú controlas tu código ## Limitaciones [Section titled “Limitaciones”](#limitaciones) Limitaciones actuales durante la beta pública: * **Tiempo de espera de compilación**: Máximo 10 minutos * **Tiempo de espera de subida**: 1 hora para URL de subida * **Máquina iOS**: Requisito de arrendamiento de 24 horas, la compilación en Mac se pondrá en cola para asegurar un uso óptimo * **Acceso**: Solo beta pública ## Comenzar [Section titled “Comenzar”](#comenzar) ¿Listo para compilar sin complicaciones? Crea Tu Primera Compilación Guía paso a paso para tu primera compilación en la nube. [Comenzar →](/docs/cli/cloud-build/getting-started/) Únete a la Beta Pública Construcción en la nube está en beta pública. Únete a nuestra comunidad para obtener acceso. [Únete a Discord →](https://discord.com/invite/VnYRvBfgA6) ## Aprende Más [Section titled “Aprende Más”](#aprende-más) * [Guía de Comenzar](/docs/cli/cloud-build/getting-started/) - Crea tu primera compilación * [Configuración iOS](/docs/cli/cloud-build/ios/) - Configura compilaciones iOS * [Configuración Android](/docs/cli/cloud-build/android/) - Configura compilaciones Android * [Solución de Problemas](/docs/cli/cloud-build/troubleshooting/) - Problemas comunes y soluciones * [Blog: Presentamos Cloud Build](/blog/introducing-capgo-cloud-build/) - Anuncio de característica ## ¿Necesitas Ayuda? [Section titled “¿Necesitas Ayuda?”](#necesitas-ayuda) * 📚 [Guía de solución de problemas](/docs/cli/cloud-build/troubleshooting/) * 💬 [Comunidad Discord](https://discord.com/invite/VnYRvBfgA6) * 📧 Email: # paquete El comando `bundle` te permite gestionar los paquetes de tu aplicación ### Subir [Section titled “Subir”](#subir) `npx @capgo/cli bundle upload [appId]` Sube un nuevo paquete para una aplicación Opciones: * `-a, --apikey `: Clave API para vincular a tu cuenta * `-p, --path `: Ruta a la carpeta a subir (por defecto el `webDir` en `capacitorconfig`) * `-c, --channel `: Canal al que vincular el paquete * `-e, --external `: Enlace a una URL externa en lugar de subir a Capgo Cloud * `--iv-session-key `: Establecer la IV y clave de sesión para una URL de paquete externo * `--s3-region `: Región para tu bucket S3 * `--s3-apikey `: Clave API para tu endpoint S3 * `--s3-apisecret `: Secreto API para tu endpoint S3 * `--s3-endpoint `: URL del endpoint S3 * `--s3-bucket-name `: Nombre de tu bucket S3 * `--s3-port `: Puerto para tu endpoint S3 * `--no-s3-ssl`: Deshabilitar SSL para subidas S3 * `--key `: Ruta personalizada para la clave de firma pública (sistema v1) * `--key-data `: Datos de la clave de firma pública (sistema v1) * `--key-v2 `: Ruta personalizada para la clave de firma privada (sistema v2) * `--key-data-v2 `: Datos de la clave de firma privada (sistema v2) * `--bundle-url`: Imprimir la URL del paquete en stdout * `--no-key`: Ignorar la clave de firma y enviar una actualización sin firmar * `--no-code-check`: Omitir la verificación de `notifyAppReady()` en el código fuente y `indexhtml` en la carpeta raíz * `--display-iv-session`: Mostrar la IV y clave de sesión usada para encriptar la actualización * `-b, --bundle `: Número de versión del paquete a subir * `--min-update-version `: Versión mínima de la Aplicación requerida para aplicar esta actualización (solo se usa si la auto-actualización está deshabilitada vía metadata) * `--auto-min-update-version`: Establecer automáticamente la versión mínima de actualización basada en versiones nativas del paquete * `--ignore-metadata-check`: Ignorar la verificación de metadata (node\_modules) al subir * `--ignore-checksum-check`: Ignorar la verificación del checksum al subir * `--timeout `: Tiempo límite para el proceso de subida en segundos * `--multipart`: Usar el protocolo multipart para subir datos a S3 (obsoleto, usar `--tus` en su lugar) * `--tus`: Subir el paquete usando el protocolo tus * `--tus-chunk-size `: Tamaño del chunk para la subida tus * `--partial`: Subir solo archivos modificados a Capgo Cloud * `--partial-only`: Subir solo archivos parciales a Capgo Cloud, omitiendo el archivo comprimido (útil para paquetes grandes) * `--encrypted-checksum `: Checksum encriptado (firma) para un paquete externo * `--auto-set-bundle`: Establecer automáticamente la versión del paquete en `capacitorconfigjson` * `--dry-upload`: Realizar una prueba de subida sin subir realmente los archivos (útil para pruebas) * `--package-json `: Lista separada por comas de rutas a archivos `packagejson` (útil para monorepos) * `--node-modules `: Lista separada por comas de rutas a directorios `node_modules` (útil para monorepos) * `--encrypt-partial`: Encriptar los archivos de actualización parcial * `--delete-linked-bundle-on-upload`: Eliminar el paquete actualmente vinculado en el canal destino antes de subir ### compatibility [Section titled “compatibility”](#compatibility) `npx @capgo/cli bundle compatibility [appId]` Verifica la compatibilidad de un paquete con un canal específico Opciones: * `-a, --apikey `: Clave API para vincular a tu cuenta * `-c, --channel `: Canal para verificar compatibilidad * `--text`: Mostrar resultados como texto en lugar de emojis * `--package-json `: Lista separada por comas de rutas a archivos `packagejson` (útil para monorepos) * `--node-modules `: Lista separada por comas de rutas a directorios `node_modules` (útil para monorepos) ### Eliminar [Section titled “Eliminar”](#eliminar) `npx @capgo/cli bundle delete [bundleId] [appId]` Elimina un paquete de una aplicación Opciones: * `-a, --apikey `: Clave API para vincular a tu cuenta ### list [Section titled “list”](#list) `npx @capgo/cli bundle list [appId]` Lista todos los paquetes de una aplicación Opciones: * `-a, --apikey `: Clave API para vincular a tu cuenta ### cleanup [Section titled “cleanup”](#cleanup) `npx @capgo/cli bundle cleanup [appId]` Limpia paquetes antiguos para una versión mayor, manteniendo el número especificado de paquetes más recientes Opciones: * `-b, --bundle `: Número de versión mayor para limpiar * `-a, --apikey `: Clave API para vincular a tu cuenta * `-k, --keep `: Número de paquetes a mantener (por defecto: 4) * `-f, --force`: Forzar eliminación sin confirmación ### decrypt [Section titled “decrypt”](#decrypt) `npx @capgo/cli bundle decrypt [zipPath] [sessionKey]` Desencripta un paquete zip firmado Opciones: * `--key `: Ruta personalizada para la clave de firma privada * `--key-data `: Datos de la clave de firma privada ### encrypt [Section titled “encrypt”](#encrypt) `npx @capgo/cli bundle encrypt [zipPath]` Encripta un paquete zip Opciones: * `--key `: Ruta personalizada para la clave de firma privada * `--key-data `: Datos de la clave de firma privada ### encryptV2 [Section titled “encryptV2”](#encryptv2) `npx @capgo/cli bundle encryptV2 [zipPath] [checksum]` Encripta un paquete zip usando el nuevo método de encriptación Opciones: * `--key `: Ruta personalizada para la clave de firma privada * `--key-data `: Datos de la clave de firma privada * `-j, --json`: Mostrar resultados como JSON ### decryptV2 [Section titled “decryptV2”](#decryptv2) `npx @capgo/cli bundle decryptV2 [zipPath] [checksum]` Desencripta un paquete zip usando el nuevo método de encriptación Opciones: * `--key `: Ruta personalizada para la clave de firma privada * `--key-data `: Datos de la clave de firma privada * `--checksum `: Checksum del paquete para verificar integridad ### zip [Section titled “zip”](#zip) `npx @capgo/cli bundle zip [appId]` Genera un archivo zip para un paquete Opciones: * `-p, --path `: Ruta a la carpeta a comprimir (por defecto el `webDir` en `capacitorconfig`) * `-b, --bundle `: Número de versión del paquete a usar en el nombre del archivo * `-n, --name `: Nombre personalizado para el zip * `-j, --json`: Mostrar resultados como JSON * `--no-code-check`: Omitir la verificación de `notifyAppReady()` en el código fuente y `indexhtml` en la carpeta raíz * `--key-v2`: Usar el nuevo método de encriptación (v2) * `--package-json `: Lista separada por comas de rutas a archivos `packagejson` (útil para monorepos) # canal El comando `channel` te permite gestionar tus canales de lanzamiento ### Agregar [Section titled “Agregar”](#agregar) `npx @capgo/cli channel add [channelId] [appId]` Crea un nuevo canal para una aplicación Opciones: * `-d, --default`: Establece el nuevo canal como canal predeterminado * `-a, --apikey `: Clave API para vincular a tu cuenta ### Eliminar [Section titled “Eliminar”](#eliminar) `npx @capgo/cli channel delete [channelId] [appId]` Elimina un canal de una aplicación Opciones: * `-a, --apikey `: Clave API para vincular a tu cuenta * `--delete-bundle`: Elimina el paquete asociado al canal ### list [Section titled “list”](#list) `npx @capgo/cli channel list [appId]` Lista todos los canales de una aplicación Opciones: * `-a, --apikey `: Clave API para vincular a tu cuenta ### currentBundle [Section titled “currentBundle”](#currentbundle) `npx @capgo/cli channel currentBundle [channel] [appId]` Obtiene el paquete actual para un canal específico Opciones: * `-c, --channel `: Canal del cual obtener el paquete actual * `-a, --apikey `: Clave API para vincular a tu cuenta * `--quiet`: Solo muestra la versión del paquete ### Establecer [Section titled “Establecer”](#establecer) `npx @capgo/cli channel set [channelId] [appId]` Establece las propiedades de un canal Opciones: * `-a, --apikey `: Clave API para vincular a tu cuenta * `-b, --bundle `: Número de versión del paquete a establecer para el canal * `-s, --state `: Establece el estado del canal (`default` o `normal`) * `--latest`: Usa la última versión de `packagejson` como versión del paquete * `--downgrade`: Permite degradar a versiones inferiores a la versión nativa * `--no-downgrade`: Deshabilita las degradaciones a versiones inferiores a la versión nativa * `--upgrade`: Permite actualizaciones a versiones superiores a la versión nativa * `--no-upgrade`: Deshabilita las actualizaciones a versiones superiores a la versión nativa * `--ios`: Permite enviar actualizaciones a dispositivos iOS * `--no-ios`: Deshabilita el envío de actualizaciones a dispositivos iOS * `--android`: Permite enviar actualizaciones a dispositivos Android * `--no-android`: Deshabilita el envío de actualizaciones a dispositivos Android * `--self-assign`: Permite que los dispositivos se auto-asignen a este canal * `--no-self-assign`: Deshabilita la auto-asignación de dispositivos a este canal * `--disable-auto-update `: Deshabilita la estrategia de actualización automática para este canal (opciones: `major`, `minor`, `metadata`, `patch`, `none`) * `--dev`: Permite enviar actualizaciones a dispositivos de desarrollo * `--no-dev`: Deshabilita el envío de actualizaciones a dispositivos de desarrollo * `--emulator`: Permite enviar actualizaciones a dispositivos emuladores * `--no-emulator`: Deshabilita el envío de actualizaciones a dispositivos emuladores * `--package-json `: Lista separada por comas de rutas a archivos `packagejson` (útil para monorepos) # médico `npx @capgo/cli doctor` Este comando verifica si estás usando la última versión de los paquetes de Capgo También es útil para reportar errores # iniciar `npx @capgo/cli@latest init [apikey]` Este comando te guía paso a paso para: * Agregar tu aplicación a Capgo * Agregar el código a tu aplicación para validar la actualización * Construir tu aplicación * Subir tu aplicación a Capgo * Ayudarte a verificar si la actualización funciona # llave El comando `key` te permite gestionar las claves de firma de tu aplicación ### Guardar [Section titled “Guardar”](#guardar) `npx @capgo/cli key save` Guarda una clave de cifrado codificada en base64 en la configuración de Capacitor (útil para CI) Opciones: * `-f, --force`: Forzar la generación de una nueva clave * `--key`: Ruta al archivo de clave para guardar en la configuración de Capacitor * `--key-data`: Datos de clave para guardar directamente en la configuración de Capacitor ### Crear [Section titled “Crear”](#crear) `npx @capgo/cli key create` Crea una nueva clave de cifrado Opciones: * `-f, --force`: Forzar la generación de una nueva clave ### delete\_old [Section titled “delete\_old”](#delete_old) `npx @capgo/cli key delete_old` Elimina la clave de cifrado antigua # iniciar sesión `npx @capgo/cli login [apikey]` Este comando almacena tu clave API de Capgo para uso futuro Puedes sobrescribir la clave API almacenada pasando `--apikey=` a cualquier comando Opciones: * `--local`: Almacena la clave API en el repositorio local y lo agrega a `gitignore` # 🔹 organisation > 🏢 Gestiona tus organizaciones en Capgo Cloud para colaboración en equipo y gestión de aplicaciones. 🏢 Gestiona tus organizaciones en Capgo Cloud para colaboración en equipo y gestión de aplicaciones. ### []()📋 **List** [Section titled “ 📋 List”](#--list) **Alias:** `l` ```bash npx @capgo/cli@latest organisation list ``` 📋 Lista todas las organizaciones a las que tienes acceso en Capgo Cloud. **Ejemplo:** ```bash npx @capgo/cli@latest organisation list ``` **Opciones:** | Param | Type | Description | | -------------- | -------- | --------------------------------------------------------------------------------- | | **-a,** | `string` | Clave API para vincular a tu cuenta | | **—supa-host** | `string` | URL personalizada del host Supabase (para auto-alojamiento o desarrollo de Capgo) | | **—supa-anon** | `string` | Clave anon personalizada de Supabase (para auto-alojamiento) | ### []()➕ **Agregar** [Section titled “ ➕ Agregar”](#--agregar) **Alias:** `a` ```bash npx @capgo/cli@latest organisation add ``` ➕ Crea una nueva organización en Capgo Cloud para colaboración en equipo. **Ejemplo:** ```bash npx @capgo/cli@latest organisation add --name "Mi Empresa" --email admin@miempresa.com ``` **Opciones:** | Param | Type | Description | | -------------- | -------- | --------------------------------------------------------------------------------- | | **-n,** | `string` | Nombre de la organización | | **-e,** | `string` | Email de gestión para la organización | | **-a,** | `string` | Clave API para vincular a tu cuenta | | **—supa-host** | `string` | URL personalizada del host Supabase (para auto-alojamiento o desarrollo de Capgo) | | **—supa-anon** | `string` | Clave anon personalizada de Supabase (para auto-alojamiento) | ### []()⚙️ **Establecer** [Section titled “ ⚙️ Establecer”](#-️-establecer) **Alias:** `s` ```bash npx @capgo/cli@latest organisation set ``` ⚙️ Actualiza la configuración de la organización como el nombre y el email de gestión. **Ejemplo:** ```bash npx @capgo/cli@latest organisation set ORG_ID --name "Nombre de Empresa Actualizado" ``` **Opciones:** | Param | Type | Description | | -------------- | -------- | --------------------------------------------------------------------------------- | | **-n,** | `string` | Nombre de la organización | | **-e,** | `string` | Email de gestión para la organización | | **-a,** | `string` | Clave API para vincular a tu cuenta | | **—supa-host** | `string` | URL personalizada del host Supabase (para auto-alojamiento o desarrollo de Capgo) | | **—supa-anon** | `string` | Clave anon personalizada de Supabase (para auto-alojamiento) | ### []()🗑️ **Eliminar** [Section titled “ 🗑️ Eliminar”](#-️-eliminar) **Alias:** `d` ```bash npx @capgo/cli@latest organisation delete ``` 🗑️ Elimina una organización de Capgo Cloud. Esta acción no se puede deshacer. Solo los propietarios de organizaciones pueden eliminar organizaciones. **Ejemplo:** ```bash npx @capgo/cli@latest organisation delete ORG_ID ``` **Opciones:** | Param | Type | Description | | -------------- | -------- | --------------------------------------------------------------------------------- | | **-a,** | `string` | Clave API para vincular a tu cuenta | | **—supa-host** | `string` | URL personalizada del host Supabase (para auto-alojamiento o desarrollo de Capgo) | | **—supa-anon** | `string` | Clave anon personalizada de Supabase (para auto-alojamiento) | # Agregar o actualizar complementos > Guía completa para contribuyentes y agentes sobre cómo agregar nuevos complementos o actualizar complementos existentes en la documentación Capgo. Esta guía explica cómo agregar nuevos complementos Capacitor al sitio web Capgo o actualizar la documentación del complemento existente. Esto es útil para los contribuyentes, mantenedores y agentes de IA que ayudan a mantener la documentación. ## Descripción general [Section titled “Descripción general”](#descripción-general) Al agregar un nuevo complemento al ecosistema Capgo, debe actualizar varios archivos y ubicaciones en el sitio web para garantizar que el complemento aparezca correctamente en todos los lugares relevantes: 1. **Configuración de la lista de complementos**: agregue metadatos de complementos a la lista maestra 2. **Página de índice de complementos**: agregue el complemento a la página de listado de complementos categorizados 3. **Navegación en la barra lateral**: agregue un complemento a la barra lateral de documentación 4. **Documentación del complemento**: cree páginas de descripción general y de introducción 5. **Tutorial del complemento**: cree un tutorial completo ## Ubicaciones de archivos [Section titled “Ubicaciones de archivos”](#ubicaciones-de-archivos) ### Archivos clave para actualizar [Section titled “Archivos clave para actualizar”](#archivos-clave-para-actualizar) | Archivo | Propósito | | ----------------------------------------------- | ----------------------------------------------- | | `/src/config/plugins.ts` | Lista maestra de complementos con metadatos | | `/src/content/docs/docs/plugins/index.mdx` | Página de índice de complementos con categorías | | `/astro.config.mjs` | Configuración de navegación de la barra lateral | | `/src/content/docs/docs/plugins/[plugin-name]/` | Directorio de documentación de complementos | | `/src/content/plugins-tutorials/en/` | Archivos de tutoriales en inglés | ## Guía paso a paso [Section titled “Guía paso a paso”](#guía-paso-a-paso) 1. ### Agregar complemento a la lista maestra [Section titled “Agregar complemento a la lista maestra”](#agregar-complemento-a-la-lista-maestra) Abra `/src/config/plugins.ts` y agregue su complemento a la matriz `actions`: ```typescript // First, import an appropriate Heroicon import YourIconName from 'astro-heroicons/mini/IconName.astro' // Then add to the actions array { name: '@capgo/your-plugin-name', author: 'github.com/Cap-go', description: 'Brief description of what the plugin does', href: 'https://github.com/Cap-go/your-plugin-name/', title: 'Display Name', icon: YourIconName, } ``` **Iconos disponibles**: consulte `/node_modules/astro-heroicons/mini/` para ver los íconos disponibles. 2. ### Agregar complemento a la página de índice [Section titled “Agregar complemento a la página de índice”](#agregar-complemento-a-la-página-de-índice) Abra `/src/content/docs/docs/plugins/index.mdx` y agregue su complemento en la categoría apropiada: ```mdx ``` **Categorías**: * ⭐ Complementos destacados * 📱 Complementos de dispositivo y sistema * 🎥 Complementos de cámara y medios * 🛠️ Complementos de utilidad * 🤖 IA y medios avanzados * 📍 Servicios de ubicación y antecedentes * 📞 Comunicación y análisis * 🔐 Seguridad y sistema * 📊 Android-Características específicas * 📥 Descarga y navegación 3. ### Agregar a la navegación de la barra lateral [Section titled “Agregar a la navegación de la barra lateral”](#agregar-a-la-navegación-de-la-barra-lateral) Abra `/astro.config.mjs` y agregue su complemento a la configuración de la barra lateral (alrededor de la línea 540): ```javascript { label: 'Your Plugin Name', items: [ { label: 'Overview', link: '/docs/plugins/your-plugin-name/' }, { label: 'Getting started', link: '/docs/plugins/your-plugin-name/getting-started' }, ], collapsed: true, } ``` Los complementos se enumeran alfabéticamente en la barra lateral. 4. ### Crear directorio de documentación de complementos [Section titled “Crear directorio de documentación de complementos”](#crear-directorio-de-documentación-de-complementos) Cree un nuevo directorio para la documentación de su complemento: ```bash mkdir -p /src/content/docs/docs/plugins/your-plugin-name/ ``` 5. ### Crear página de descripción general del complemento [Section titled “Crear página de descripción general del complemento”](#crear-página-de-descripción-general-del-complemento) Cree `/src/content/docs/docs/plugins/your-plugin-name/index.mdx`: ```mdx --- title: "@capgo/your-plugin-name" description: Brief description of the plugin's purpose tableOfContents: false next: false prev: false sidebar: order: 1 label: "Introduction" hero: tagline: Detailed tagline explaining what the plugin does image: file: ~public/your-plugin-icon.svg actions: - text: Get started link: /docs/plugins/your-plugin-name/getting-started/ icon: right-arrow variant: primary - text: Github link: https://github.com/Cap-go/your-plugin-name/ icon: external variant: minimal --- import { Card, CardGrid } from '@astrojs/starlight/components'; Description of first key feature Description of second key feature Works on both iOS and Android 📱 Check the [Documentation](/docs/plugins/your-plugin-name/getting-started/) to master the plugin. ``` 6. ### Crear una guía de introducción [Section titled “Crear una guía de introducción”](#crear-una-guía-de-introducción) Cree `/src/content/docs/docs/plugins/your-plugin-name/getting-started.mdx`: ```mdx --- title: Getting Started description: Learn how to install and use the plugin in your Capacitor app. sidebar: order: 2 --- import { Steps } from '@astrojs/starlight/components'; import { PackageManagers } from 'starlight-package-managers' 1. **Install the package** 2. **Sync with native projects** ## Configuration ### iOS Configuration [iOS-specific setup instructions] ### Android Configuration [Android-specific setup instructions] ## Usage [Basic usage examples] ## API Reference [Detailed API documentation] ## Complete Example [Full working example] ## Best Practices [Recommended practices and tips] ## Platform Notes [Platform-specific notes and limitations] ``` 7. ### Crear archivo tutorial [Section titled “Crear archivo tutorial”](#crear-archivo-tutorial) Cree `/src/content/plugins-tutorials/en/your-plugin-name.md`: ```markdown --- locale: en --- # Using @capgo/your-plugin-name Package The `@capgo/your-plugin-name` package [brief description]. In this tutorial, we will guide you through the installation, configuration, and usage of this package in your Ionic Capacitor app. ## Installation [Installation steps] ## Configuration [Configuration steps for iOS and Android] ## API Usage [Detailed API usage examples] ## Complete Example [Full working example] ## Best Practices [Tips and best practices] ## Troubleshooting [Common issues and solutions] ## Conclusion [Summary and links to additional resources] ``` ## Estructura de documentación del complemento [Section titled “Estructura de documentación del complemento”](#estructura-de-documentación-del-complemento) ### Archivos requeridos [Section titled “Archivos requeridos”](#archivos-requeridos) ```plaintext src/content/docs/docs/plugins/your-plugin-name/ ├── index.mdx # Overview page with hero and feature cards └── getting-started.mdx # Installation and usage guide src/content/plugins-tutorials/en/ └── your-plugin-name.md # Comprehensive tutorial ``` ### Archivos opcionales [Section titled “Archivos opcionales”](#archivos-opcionales) Para complementos complejos, puede agregar páginas de documentación adicionales: ```plaintext src/content/docs/docs/plugins/your-plugin-name/ ├── index.mdx ├── getting-started.mdx ├── api-reference.mdx # Detailed API documentation ├── examples.mdx # Additional examples ├── troubleshooting.mdx # Troubleshooting guide └── migrations.mdx # Migration guides ``` ## Pautas de contenido [Section titled “Pautas de contenido”](#pautas-de-contenido) ### Escribir descripciones de complementos [Section titled “Escribir descripciones de complementos”](#escribir-descripciones-de-complementos) * **Sea conciso**: mantenga las descripciones de menos de 100 caracteres * **Sea específico**: explique qué hace el complemento, no qué es * **Use palabras de acción**: comience con verbos como “Control”, “Integrar”, “Habilitar” **Buenos ejemplos**: * “Dispositivo de control de linterna y antorcha con simple interruptor de encendido/apagado” * “Integre el chat en vivo de Crisp y la atención al cliente en su aplicación” * “Habilite la autenticación segura usando Face ID y Touch ID” **Malos ejemplos**: * “Un complemento para flash” * “Este es un complemento Crisp” * “Complemento biométrico” ### Redacción de documentación1. **Comience con la instalación**: comience siempre con pasos de instalación claros [Section titled “Redacción de documentación1. Comience con la instalación: comience siempre con pasos de instalación claros”](#redacción-de-documentación1-comience-con-la-instalación-comience-siempre-con-pasos-de-instalación-claros) 2. **Proporcionar configuración**: incluya requisitos de configuración específicos de la plataforma 3. **Mostrar ejemplos de uso**: proporcione ejemplos de código de trabajo 4. **Incluir referencia API**: documentar todos los métodos y parámetros 5. **Agregar ejemplos completos**: mostrar patrones de uso en el mundo real 6. **Enumere las mejores prácticas**: comparta sugerencias para un uso óptimo 7. **Diferencias en la plataforma de documentos**: Aclare el comportamiento de iOS frente a Android 8. **Agregar solución de problemas**: solucione problemas comunes ### Ejemplos de código [Section titled “Ejemplos de código”](#ejemplos-de-código) * Utilice TypeScript para todos los ejemplos de código * Incluir importaciones en la parte superior. * Agregar comentarios que expliquen los pasos clave. * Mostrar manejo de errores * Demostrar el uso tanto básico como avanzado. ## Lista de verificación [Section titled “Lista de verificación”](#lista-de-verificación) Utilice esta lista de verificación cuando agregue un nuevo complemento: * \[] Complemento agregado a `/src/config/plugins.ts` * \[] Icono apropiado seleccionado de Heroicons * \[] Se agregó complemento a `/src/content/docs/docs/plugins/index.mdx` en la categoría correcta * \[] Se agregó una entrada en la barra lateral en `/astro.config.mjs` * \[] Directorio de documentación del complemento creado * \[] Se creó la página de descripción general `index.mdx` * \[] Se creó la guía `getting-started.mdx` * \[] Tutorial creado en `/src/content/plugins-tutorials/en/` * \[] Instrucciones de instalación incluidas * \[] Configuración documentada iOS * \[] Configuración documentada Android * \[] Ejemplos de uso proporcionados * \[] Se agregó la referencia API * \[] Incluye ejemplo de trabajo completo * \[] Mejores prácticas enumeradas * \[] Se agregaron notas específicas de la plataforma. * \[] Probado que todos los enlaces funcionan correctamente ## Referencia de icono [Section titled “Referencia de icono”](#referencia-de-icono) Iconos comunes utilizados para complementos (de `astro-heroicons/mini/`): | Icono | Caso de uso | | ------------------------ | ------------------------------------------------ | | `BoltIcon` | Flash, potencia, energía | | `CameraIcon` | Cámara, fotografía, vídeo | | `ChatBubbleLeftIcon` | Chat, mensajería, comunicación | | `FingerPrintIcon` | Biométrica, seguridad, autenticación | | `MapPinIcon` | Ubicación, geolocalización, mapas | | `SpeakerWaveIcon` | Audio, sonido, música | | `VideoCameraIcon` | Vídeo, grabación, streaming | | `CreditCardIcon` | Pagos, compras | | `PlayCircleIcon` | Reproductores multimedia, reproductores de vídeo | | `SignalIcon` | Conectividad, red, baliza | | `RadioIcon` | Baliza de radiodifusión inalámbrica | | `ChatBubbleOvalLeftIcon` | Redes sociales, WeChat | ## Actualización de complementos existentes [Section titled “Actualización de complementos existentes”](#actualización-de-complementos-existentes) Al actualizar un complemento existente: 1. **Actualizar números de versión** en la documentación 2. **Agregue guías de migración** si existen cambios importantes 3. **Actualizar referencia API** con nuevos métodos 4. **Agregue nuevos ejemplos** para nuevas funciones 5. **Actualizar los requisitos de la plataforma** si se modifica 6. **Revisar las mejores prácticas** según las nuevas funciones 7. **Mantenga el tutorial actualizado** con la última versión API ## Soporte en varios idiomas [Section titled “Soporte en varios idiomas”](#soporte-en-varios-idiomas) El sitio web admite varios idiomas. Después de crear la documentación en inglés: 1. Ejecute el script de traducción: ```bash bun run plugins:translate_all ``` 2. Revisar las traducciones generadas en: * `/src/content/plugins-tutorials/de/` (alemán) * `/src/content/plugins-tutorials/es/` (español) * `/src/content/plugins-tutorials/fr/` (francés) * `/src/content/plugins-tutorials/it/` (italiano) * `/src/content/plugins-tutorials/ja/` (japonés) * `/src/content/plugins-tutorials/ko/` (coreano) * `/src/content/plugins-tutorials/id/` (indonesio) ## Probando tus cambios [Section titled “Probando tus cambios”](#probando-tus-cambios) Después de agregar o actualizar la documentación del complemento: 1. **Construya el sitio localmente**: ````bash npm run build ```2. **Compruebe si hay errores**: - Verificar que todos los enlaces funcionen. - Asegúrese de que las imágenes se carguen correctamente - Confirmar que los ejemplos de códigos son válidos - La navegación de prueba funciona. ```` 2. **Vista previa del sitio**: ```bash npm run dev ``` 3. **Verifica que aparezca tu complemento**: * Verifique la página de listado de complementos * Verificar la navegación de la barra lateral * Pruebe todas las páginas de documentación. * Confirmar que la página del tutorial funciona. ## Errores comunes [Section titled “Errores comunes”](#errores-comunes) Caution **Evite estos errores comunes:** 1. **Olvidar sincronizar**: ejecute siempre `npx cap sync` en los ejemplos 2. **Nombres inconsistentes**: use el mismo nombre de complemento en todas partes 3. **Falta configuración de plataforma**: documente la configuración de iOS y Android 4. **Enlaces rotos**: utilice enlaces relativos y verifique que funcionen 5. **Sin manejo de errores**: muestra siempre try-catch en los ejemplos 6. **Importaciones faltantes**: incluya todas las importaciones necesarias en los ejemplos 7. **Descripciones poco claras**: Sea específico sobre lo que hace el complemento ## Obtener ayuda [Section titled “Obtener ayuda”](#obtener-ayuda) Si necesita ayuda para agregar o actualizar la documentación del complemento: * **Discord**: Únete a nuestra [comunidad de Discord](https://discord.capgo.app) * **GitHub**: abrir una incidencia en el [repositorio del sitio web](https://github.com/Cap-go/website) * **Correo electrónico**: póngase en contacto con el equipo en ## Ejemplos [Section titled “Ejemplos”](#ejemplos) Como referencia, consulte estos complementos bien documentados: * **Actualizador**: `/src/content/docs/docs/plugins/updater/` (complemento complejo con varias páginas) * **Flash**: `/src/content/docs/docs/plugins/flash/` (complemento simple, buen ejemplo inicial) * **Inicio de sesión social**: `/src/content/docs/docs/plugins/social-login/` (complemento con subpáginas) ## Resumen [Section titled “Resumen”](#resumen) Agregar un complemento a la documentación Capgo implica: 1. Agregar metadatos a la configuración maestra 2. Agregar el complemento a la página de índice categorizada 3. Configurar la navegación de la barra lateral 4. Creación de páginas de documentación completas. 5. Escribir un tutorial detallado 6. Probar todos los cambios localmente Si sigue esta guía, se asegurará de que los complementos estén documentados de forma coherente y sean fácilmente detectables por los usuarios. # FAQ > Preguntas frecuentes sobre Capgo, cómo solucionar el problema más común en Capgo o con el Updater, qué es OTA y cómo gestionarlas Si tiene preguntas que no han sido respondidas aquí, ¡pregunte! Tanto presentar un problema como preguntar sobre [Discord](https://discord.capgo.app) funcionan. ### ¿Qué es “code push”?[](https://capgo.app/docs/#what-is-code-push "Enlace directo a ¿Qué es \"code push\"?") [Section titled “¿Qué es “code push”?”](#qué-es-code-push) Code push, también conocido como “actualizaciones inalámbricas” (OTA), es un servicio en la nube que permite a los desarrolladores de Capacitor implementar actualizaciones en sus aplicaciones en producción. Capgo actualmente funciona en Android, iOS y Electron. “Code Push” es una referencia al nombre de una función de implementación utilizada por la comunidad React Native de [Microsoft](https://appcenter.ms/) y [Expo](https://expo.dev/), ninguna de las cuales admite Capacitor. ### ¿Cuál es la diferencia entre un paquete y una versión?[](https://capgo.app/docs/faq/#what-is-the-difference-between-a-bundle-and-a-release "Enlace directo a ¿Cuál es la diferencia entre un paquete y una versión?") [Section titled “¿Cuál es la diferencia entre un paquete y una versión?”](#cuál-es-la-diferencia-entre-un-paquete-y-una-versión) Usamos el término “lanzamiento” para referirnos a la preparación de un binario para las tiendas de aplicaciones. Para poder generar posteriormente un paquete, Capgo necesita saber el binario exacto que se envió a las tiendas de aplicaciones. Usamos el término “paquete” para referirnos a un parche que se puede aplicar a una versión para actualizarla con un código nuevo. El comando `npx @capgo/cli@latest bundle upload` se utiliza para generar un paquete a partir de su nuevo código local que luego se envía a sus usuarios. ### ¿Cuál es la hoja de ruta?[](https://capgo.app/docs/faq/#what-is-the-roadmap "Enlace directo a ¿Qué es la hoja de ruta?") [Section titled “¿Cuál es la hoja de ruta?”](#cuál-es-la-hoja-de-ruta) Nuestros tableros de proyectos también son públicos y se encuentran en: [https://github.com/orgs/Cap-go/projects](https://github.com/orgs/Cap-go/projects/) Nuestro equipo también opera en público, por lo que puedes ver en qué estamos trabajando en cualquier momento. Estaremos encantados de responder cualquier pregunta que tenga sobre nuestra hoja de ruta o prioridades a través de problemas de Github o [Discord](https://discord.capgo.app). ### ¿Puedo usar Capgo con mi equipo?[](https://capgo.app/docs/faq/#can-i-use-capgo-with-my-team "Enlace directo a ¿Puedo usar Capgo con mi equipo?") [Section titled “¿Puedo usar Capgo con mi equipo?”](#puedo-usar-capgo-con-mi-equipo) ¡Sí! Todos los planes admiten desarrolladores ilimitados. Solo limitamos las métricas de las aplicaciones (MAU, almacenamiento y ancho de banda) a cada organización. Consulte [Equipos](https://capgo.app/pricing/) para obtener más información. ### ¿Capgo almacena mi código fuente?[](https://capgo.app/docs/faq/#does-capgo-store-my-source-code "Enlace directo a ¿Capgo almacena mi código fuente?") [Section titled “¿Capgo almacena mi código fuente?”](#capgo-almacena-mi-código-fuente) No. Los servidores Capgo nunca ven su código fuente. Cuando ejecuta `npx @capgo/cli@latest bundle upload`, Capgo almacena un archivo zip del código minificado/compilado: el mismo código que recibiría un navegador, no su código fuente. Para mayor seguridad, tienes dos opciones: * **Cifrado de extremo a extremo**: cifra tu paquete antes de cargarlo, lo que hace imposible que Capgo lea o modifique el contenido. * **Carga de URL externa**: almacene el paquete en su propio servidor y solo proporcione a Capgo el enlace de descarga con la opción `--external ` Consulte también nuestra política de privacidad: [https://capgo.app/privacy](https://capgo.app/privacy/) ### ¿Puedo usar Capgo desde mi sistema CI?[](https://capgo.app/docs/faq/#can-i-use-capgo-from-my-ci-system "Enlace directo a ¿Puedo usar Capgo desde mi sistema CI?") [Section titled “¿Puedo usar Capgo desde mi sistema CI?”](#puedo-usar-capgo-desde-mi-sistema-ci) Sí. Capgo está diseñado para usarse desde sistemas CI. Hemos publicado una guía para [Android y Github Actions](https://capgo.app/blog/automatic-capacitor-android-build-github-action/) y [iOS](https://capgo.app/blog/automatic-capacitor-ios-build-github-action/), y para [GitLab](https://capgo.app/blog/setup-ci-and-cd-gitlab/). Otros sistemas de CI deberían ser similares. No dude en comunicarse con GitHub problemas o en Discord si encuentra algún problema.### ¿Cómo se relaciona esto con Firebase Remote Config o Launch Darkly?[](https://capgo.app/docs/faq/#how-does-this-relate-to-firebase-remote-config-or-launch-darkly "Enlace directo a ¿Cómo se relaciona esto con Firebase Remote Config o Launch Darkly?") La inserción de código permite agregar código nuevo/reemplazar código en el dispositivo. Firebase Remote Config y Launch Darkly son ambos sistemas de configuración. Le permiten cambiar la configuración de su aplicación sin tener que enviar una nueva versión. No están destinados a reemplazar el código. ### ¿Qué tamaño de huella de dependencia agrega esto?[](https://capgo.app/docs/faq/#how-big-of-a-dependency-footprint-does-this-add "Enlace directo a ¿Qué tamaño de huella de dependencia agrega esto?") [Section titled “¿Qué tamaño de huella de dependencia agrega esto?”](#qué-tamaño-de-huella-de-dependencia-agrega-esto) No lo he medido recientemente, pero espero que la biblioteca de inserción de código agregue menos de un megabyte a las aplicaciones Capacitor. Conocemos formas en que podemos reducir esto cuando se convierta en una prioridad. Si el tamaño es un obstáculo para usted, ¡háganoslo saber! ### ¿Capgo funciona en el simulador iOS 18.4?[](https://capgo.app/docs/faq/#does-capgo-work-on-the-ios-18-4-simulator "Enlace directo a ¿Funciona Capgo en el simulador iOS 18.4?") [Section titled “¿Capgo funciona en el simulador iOS 18.4?”](#capgo-funciona-en-el-simulador-ios-184) No. Debido a un problema ascendente que afecta al simulador iOS 18.4, Capgo no se ejecuta de manera confiable allí. Pruebe en un dispositivo real o utilice una versión diferente del simulador iOS. Ver detalles en el problema de React Native: [facebook/react-native#50510](https://github.com/facebook/react-native/issues/50510) ### ¿La inserción de código funciona con aplicaciones grandes?[](https://capgo.app/docs/faq/#does-code-push-work-with-large-applications "Enlace directo a ¿Funciona la inserción de código con aplicaciones grandes?") [Section titled “¿La inserción de código funciona con aplicaciones grandes?”](#la-inserción-de-código-funciona-con-aplicaciones-grandes) Sí. No hay límite en el tamaño de la aplicación que se puede actualizar mediante inserción de código. Como se indica [a continuación](https://capgo.app/docs/faq/#what-types-of-changes-does-capgo-code-push-support), Capgo puede cambiar cualquier código JS en su aplicación independientemente del tamaño. Nota: un tamaño mayor dificulta que los usuarios descarguen actualizaciones. We recommend keeping your app as small as possible. ### What can I use Capgo code push for?[](https://capgo.app/docs/faq/#what-can-i-use-capgo-code-push-for "Direct link to What can I use Capgo code push for?") [Section titled “What can I use Capgo code push for?”](#what-can-i-use-capgo-code-push-for) Hemos visto una variedad de usos, que incluyen: * Correcciones de emergencia para aplicaciones de producción. * Envío de correcciones de errores a usuarios de versiones anteriores de su aplicación. * Envío constantemente (por ejemplo, cada hora). Note that most app stores prohibit shipping code that changes the behavior of the app in a significant way. Consulte [a continuación](https://capgo.app/docs/faq/#how-does-this-relate-to-the-appplay-store-review-process-or-policies) para obtener más información. ### ¿Qué cuenta como “MAU” para Capgo?[](https://capgo.app/docs/faq/#what-counts-as-a-mau-for-capgo "Enlace directo a ¿Qué cuenta como \"MAU\" para Capgo?") [Section titled “¿Qué cuenta como “MAU” para Capgo?”](#qué-cuenta-como-mau-para-capgo) Una MAU es un “usuario activo mensual”. En el contexto de Capgo, esto en realidad se refiere a un Dispositivo Activo Mensual. We count a MAU as any device that has contacted our servers in the last 30 days. No contamos los dispositivos que no se han comunicado con nuestros servidores en los últimos 30 días. **Importante**: A partir de la versión del complemento **v5.10.0**, **v6.25.0** y **v7.25.0**, el ID del dispositivo ahora persiste en las reinstalaciones de la aplicación. Antes de estas versiones, cada reinstalación de la aplicación generaba un nuevo ID de dispositivo y contaba como una nueva MAU.Con las versiones actuales: * DeviceID persiste durante las reinstalaciones de aplicaciones (almacenado de forma segura en Keychain en iOS y EncryptedSharedPreferences en Android) * La actualización de la aplicación no crea una nueva ID de dispositivo * Durante el desarrollo, si está utilizando una versión anterior del complemento (< v5.10.0 / v6.25.0 / v7.25.0), each reinstall still creates a new MAU Note: TestFlight downloads and channel switches in Android may still generate new device registrations depending on your configuration. > ), recomendamos después de la primera configuración, deshabilitar los dispositivos de desarrollo y los emuladores para reducir la cantidad de dispositivos duplicados. ### ¿Para qué no podemos usar el código push Capgo?[](https://capgo.app/docs/faq/#what-cant-we-use-capgo-code-push-for "Enlace directo a ¿Para qué no podemos usar el código push Capgo?") [Section titled “¿Para qué no podemos usar el código push Capgo?”](#para-qué-no-podemos-usar-el-código-push-capgo) Como se indicó anteriormente, Capgo no debe usarse para violar las políticas de la tienda de aplicaciones. Consulte [a continuación](https://capgo.app/docs/faq/#does-capgo-comply-with-play-store-guidelines) para obtener más información. Además, Capgo no admite el cambio de código nativo (por ejemplo, Java/Kotlin en Android u Objective-C/Swift en iOS). La herramienta le avisará durante un intento de actualización si ha cambiado el código nativo. ### ¿Puedo actualizar los cambios de capacitor.config.ts a través de Capgo?[](https://capgo.app/docs/faq/#can-i-update-capacitorconfigts-changes-via-capgo "Enlace directo a ¿Puedo actualizar los cambios de capacitor.config.ts a través de Capgo?") [Section titled “¿Puedo actualizar los cambios de capacitor.config.ts a través de Capgo?”](#puedo-actualizar-los-cambios-de-capacitorconfigts-a-través-de-capgo) No. Los cambios en `capacitor.config.ts` no se pueden enviar a través de Capgo actualizaciones en vivo. El archivo de configuración Capacitor se lee en el momento de la compilación nativa y se compila en el binario de la aplicación nativa. Esto significa que cualquier cambio en `capacitor.config.ts` (como configuraciones de complementos, ID de aplicación, configuración del servidor u opciones de complementos nativos) requiere una nueva versión nativa a través de App Store o Google Play. Capgo solo puede actualizar recursos web (HTML, CSS, JavaScript) que se cargan en tiempo de ejecución. Si necesita cambiar su configuración Capacitor, debe: 1. Actualizar `capacitor.config.ts` localmente 2. Reconstruya su aplicación nativa (`npx cap sync` seguida de una compilación nativa) 3. Envíe el nuevo binario a las tiendas de aplicaciones. ### ¿Capgo envía a las tiendas por mí?[](https://capgo.app/docs/faq/#does-capgo-submit-to-the-stores-for-me "Enlace directo a ¿Capgo envía a las tiendas por mí?") [Section titled “¿Capgo envía a las tiendas por mí?”](#capgo-envía-a-las-tiendas-por-mí) Actualmente, Capgo no admite el envío a las tiendas de aplicaciones en su nombre. Tenemos planes de agregar esto en el futuro, pero por ahora deberá continuar usando sus procesos existentes para enviar a las tiendas de aplicaciones. Puede utilizar nuestra [guía de CI Android](https://capgo.app/blog/automatic-capacitor-android-build-github-action/) para automatizar este proceso y nuestra [guía de CI iOS](https://capgo.app/blog/automatic-capacitor-ios-build-github-action/). ### ¿Qué almacena Capgo en el disco y dónde?[](https://capgo.app/docs/faq/#what-does-capgo-store-on-disk-and-where "Enlace directo a ¿Qué almacena Capgo en el disco y dónde?") [Section titled “¿Qué almacena Capgo en el disco y dónde?”](#qué-almacena-capgo-en-el-disco-y-dónde) El actualizador Capgo (incluido en su aplicación cuando crea su aplicación) almacena en caché el último paquete descargado en el único directorio que permite cargar código. En Android, esto se encuentra en `/data/user/0/com.example.app/code_cache/capgo_updater` aunque la base de esa ruta la proporciona el sistema Android y puede cambiar dinámicamente en tiempo de ejecución. En los dispositivos iOS, los datos se almacenan en `Library/Application Support/capgo`. Las herramientas de línea de comandos Capgo (por ejemplo, `npx @capgo/cli@latest bundle upload`) se instalan en el disco en cachés npm, sus inicios de sesión se almacenan en su directorio de inicio en `~/.capgo`.### ¿Cómo se relaciona esto con Capacitor Hot Reload?[](https://capgo.app/docs/faq/#how-does-this-relate-to-capacitor-hot-reload "Enlace directo a ¿Cómo se relaciona esto con Capacitor Hot Reload?") La recarga en caliente de Capacitor es una característica solo en tiempo de desarrollo. La inserción de código es para producción. La recarga en caliente es una característica de Capacitor que le permite cambiar el código en el dispositivo durante el desarrollo. Requiere crear la aplicación Capacitor con un proxy para conectarse a su máquina local. La inserción de código es una función que le permite cambiar el código en el dispositivo en producción. Usaremos una variedad de técnicas diferentes para hacer esto posible dependiendo de la plataforma. ### ¿Qué tipos de cambios admite la inserción de código Capgo?[](https://capgo.app/docs/faq/#what-types-of-changes-does-capgo-code-push-support "Enlace directo a ¿Qué tipos de cambios admite la inserción de código Capgo?") [Section titled “¿Qué tipos de cambios admite la inserción de código Capgo?”](#qué-tipos-de-cambios-admite-la-inserción-de-código-capgo) Capgo puede cambiar cualquier código JS en su aplicación. Esto incluye el código de la aplicación y el código generado. También puede actualizar las dependencias en `package.json` siempre que no requieran cambios en el código nativo. No tenemos planes para admitir el cambio de código nativo (por ejemplo, Java/Kotlin en Android u Objective-C/Swift en iOS), y la herramienta le avisará si detecta que ha cambiado el código nativo, ya que no se incluirá en el paquete. ### ¿Esto es compatible con Web?[](https://capgo.app/docs/faq/#does-this-support-web "Enlace directo a ¿Esto es compatible con Web?") [Section titled “¿Esto es compatible con Web?”](#esto-es-compatible-con-web) No es necesario insertar código para la web, ya que la web ya funciona de esta manera. Cuando un usuario abre una aplicación web, descarga la última versión del servidor si es necesario. Si tiene un caso de uso para la inserción de código con la web, ¡nos encantaría saberlo! ### ¿Funcionará esto en iOS, Android, Mac, Windows, Linux, etc.?[](https://capgo.app/docs/faq/#will-this-work-on-ios-android-mac-windows-linux-etc "Enlace directo a ¿Funcionará esto en iOS, Android, Mac, Windows, Linux, etc.?") [Section titled “¿Funcionará esto en iOS, Android, Mac, Windows, Linux, etc.?”](#funcionará-esto-en-ios-android-mac-windows-linux-etc) Sí. Hasta ahora nos hemos centrado en el soporte de Android, iOS y Electron, y el envío de código está listo para producción en los tres. ### ¿Qué versiones de sistema operativo admite Capgo?[](https://capgo.app/docs/faq/#what-os-versions-does-capgo-support "Enlace directo a ¿Qué versiones de sistema operativo admite Capgo?") [Section titled “¿Qué versiones de sistema operativo admite Capgo?”](#qué-versiones-de-sistema-operativo-admite-capgo) Capgo admite las mismas versiones de Android que admite Capacitor. Capacitor actualmente admite Android API nivel 22+ y iOS 13.0+: [https://capacitorjs.com/docs/main/reference/support-policy](https://capacitorjs.com/docs/main/reference/support-policy/) ### ¿Qué versiones de Capacitor admite Capgo?[](https://capgo.app/docs/faq/#what-versions-of-capacitor-does-capgo-support "Enlace directo a ¿Qué versiones de Capacitor admite Capgo?") [Section titled “¿Qué versiones de Capacitor admite Capgo?”](#qué-versiones-de-capacitor-admite-capgo) Capgo actualmente solo admite versiones estables recientes de Capacitor. También podríamos admitir versiones anteriores de Capacitor, pero no hemos creado la infraestructura necesaria para mantenerlas a lo largo del tiempo. Tenemos la intención de admitir más versiones de Capacitor en el futuro, incluida cualquier versión para nuestros clientes empresariales. [https://github.com/Cap-go/capgo/issues/1100](https://github.com/Cap-go/capgo/issues/1100/) Capgo rastrea Capacitor estable y generalmente se actualiza a las pocas horas de cualquier lanzamiento estable. Nuestro sistema para realizar estas actualizaciones es automatizado y tarda unos minutos en ejecutarse. Luego realizamos un paso de verificación manual adicional antes de publicar en nuestros servidores.### ¿Cómo se relaciona esto con el proceso o las políticas de revisión de la aplicación/Play Store?[](https://capgo.app/docs/faq/#how-does-this-relate-to-the-appplay-store-review-process-or-policies "Enlace directo a ¿Cómo se relaciona esto con el proceso o las políticas de revisión de la aplicación/Play Store?") Los desarrolladores están sujetos a sus acuerdos con los proveedores de tiendas cuando deciden utilizar esas tiendas. La inserción de código está diseñada para permitir a los desarrolladores actualizar sus aplicaciones y seguir cumpliendo con las políticas de la tienda en los canales de entrega iOS, Android y Electron. Similar a la variedad de productos comerciales disponibles para hacerlo con React Native (por ejemplo, [Microsoft](https://appcenter.ms/), [Expo](https://expo.dev/)). Microsoft también publica una guía sobre cómo su solución cumple con las tiendas de aplicaciones: [https://github.com/microsoft/react-native-code-push#store-guideline-compliance](https://github.com/microsoft/react-native-code-push/#store-guideline-compliance) La inserción de código es una técnica ampliamente utilizada en las tiendas de aplicaciones. Todas las aplicaciones grandes que conozco utilizan código push. La principal política a tener en cuenta es no cambiar el comportamiento de la aplicación de manera significativa. Consulte [a continuación](https://capgo.app/docs/faq/#does-capgo-comply-with-play-store-guidelines) para obtener más información. ### ¿Capgo cumple con las pautas Play Store?[](https://capgo.app/docs/faq/#does-capgo-comply-with-play-store-guidelines "Enlace directo a ¿Cumple Capgo con las pautas Play Store?") [Section titled “¿Capgo cumple con las pautas Play Store?”](#capgo-cumple-con-las-pautas-play-store) Sí. El Play Store ofrece dos restricciones relacionadas con las herramientas de actualización. 1. Las actualizaciones deben utilizar un intérprete o una máquina virtual (Capgo usa JavaScript en un WebView). [https://support.google.com/googleplay/android-developer/answer/9888379?hl=en](https://support.google.com/googleplay/android-developer/answer/9888379/?hl=en) ```plaintext An app distributed via Google Play may not modify, replace, or update itself using any method other than Google Play's update mechanism. Likewise, an app may not download executable code (such as dex, JAR, .so files) from a source other than Google Play. *This restriction does not apply to code that runs in a virtual machine or an interpreter* where either provides indirect access to Android APIs (such as JavaScript in a webview or browser). Apps or third-party code, like SDKs, with interpreted languages (JavaScript, Python, Lua, etc.) loaded at run time (for example, not packaged with the app) must not allow potential violations of Google Play policies. ``` 2. Los cambios en la aplicación no deben ser engañosos (por ejemplo, cambiar el propósito de la aplicación mediante una actualización). [https://support.google.com/googleplay/android-developer/answer/9888077](https://support.google.com/googleplay/android-developer/answer/9888077/) Sea claro con sus usuarios sobre lo que proporciona con su aplicación y no viole sus expectativas con cambios de comportamiento significativos mediante el uso de Capgo. Capgo está diseñado para ser compatible con las pautas Play Store. Sin embargo, Capgo es una herramienta y, como ocurre con cualquier herramienta, se puede abusar de ella. Abusar deliberadamente de Capgo para violar las pautas de Play Store es una violación de los Capgo [Términos de servicio](https://capgo.app/tos/) y puede resultar en la cancelación de su cuenta. Finalmente, los servicios de inserción de código se utilizan ampliamente en la industria (todas las aplicaciones grandes que conozco los usan) y hay muchos otros servicios de inserción de código disponibles públicamente (por ejemplo, expo.dev y appcenter.ms). Este es un camino muy transitado. Microsoft también publica una guía sobre cómo su biblioteca nativa “codepush” de reacción cumple con las tiendas de aplicaciones: [https://github.com/microsoft/react-native-code-push#store-guideline-compliance](https://github.com/microsoft/react-native-code-push/#store-guideline-compliance) ### ¿Capgo cumple con las pautas App Store?[](https://capgo.app/docs/faq/#does-capgo-comply-with-app-store-guidelines "Enlace directo a ¿Cumple Capgo con las pautas App Store?") [Section titled “¿Capgo cumple con las pautas App Store?”](#capgo-cumple-con-las-pautas-app-store) Sí. Al igual que el Play Store, el App Store ofrece restricciones técnicas y de políticas. ```plaintext 3.2.2 ... interpreted code may be downloaded to an Application but only so long as such code: (a) does not change the primary purpose of the Application by providing features or functionality that are inconsistent with the intended and advertised purpose of the Application as submitted to the App Store, (b) does not create a store or storefront for other code or applications, and (c) does not bypass signing, sandbox, or other security features of the OS. ``` Capgo usa JavaScript en un WebView para cumplir con la restricción de solo intérprete para actualizaciones en iOS. Siempre que su aplicación no tenga un comportamiento engañoso a través de actualizaciones (por ejemplo, cambiar el propósito de la aplicación mediante una actualización), la actualización a través de Capgo (o cualquier otra solución de inserción de código) es una práctica estándar de la industria y cumple con las pautas App Store.Deliberately abusing Capgo to violate App Store guidelines is in violation of the Capgo [Terms of Service](https://capgo.app/tos/) and can result in termination of your account. Microsoft también publica una guía sobre cómo su biblioteca nativa “codepush” de reacción cumple con las tiendas de aplicaciones: [https://github.com/microsoft/react-native-code-push#store-guideline-compliance](https://github.com/microsoft/react-native-code-push/#store-guideline-compliance) ### ¿Puedo usar Capgo en mi país?[](https://capgo.app/docs/faq/#can-i-use-capgo-in-my-country "Enlace directo a ¿Puedo usar Capgo en mi país?") [Section titled “¿Puedo usar Capgo en mi país?”](#puedo-usar-capgo-en-mi-país) No hemos intentado restringir el acceso a Capgo desde ningún país. Reconocemos que algunos países tienen restricciones sobre las URL a las que se puede acceder desde dentro del país. Capgo actualmente utiliza Cloudflare Cloud para alojamiento, incluidos R2 Storage y los trabajadores de Cloudflare. Capgo utiliza las siguientes URL: * [https://api.capgo.app](https://api.capgo.app/) — used by the `npx @capgo/cli` command line tools to interact with the Capgo servers as well as the Capgo updater on users’ devices to check for updates. * [https://\*.r2.cloudflarestorage.com](https://*.r2.cloudflarestorage.com/) — utilizado por la herramienta de línea de comando `npx @capgo/cli` para cargar y descargar paquetes Si se puede acceder a todas esas URL desde su país, entonces Capgo debería funcionar. Si su región requiere bloquear el acceso a cualquiera de esas URL, háganoslo saber y podremos trabajar con usted para encontrar una solución. Los servidores proxy son una opción. ### ¿Puedo autohospedar Capgo?[](https://capgo.app/docs/faq/#can-i-self-host-capgo "Enlace directo a ¿Puedo autohospedar Capgo?") [Section titled “¿Puedo autohospedar Capgo?”](#puedo-autohospedar-capgo) Sí, puedes autohospedar Capgo. La guía aún no está escrita, pero el código es de código abierto y está disponible en [https://github.com/cap-go/capgo](https://github.com/cap-go/capgo/) ### ¿La inserción de código requiere Internet para funcionar?[](https://capgo.app/docs/faq/#does-code-push-require-the-internet-to-work "Enlace directo a ¿La inserción de código requiere Internet para funcionar?") [Section titled “¿La inserción de código requiere Internet para funcionar?”](#la-inserción-de-código-requiere-internet-para-funcionar) Sí. One could imagine running a server to distribute the updates separately from the general internet, but some form of network connectivity is required to transport updates to the devices. ### How is Capgo affected by lack of network connectivity?[](https://capgo.app/docs/faq/#how-is-capgo-affected-by-lack-of-network-connectivity "Direct link to How is Capgo affected by lack of network connectivity?") [Section titled “How is Capgo affected by lack of network connectivity?”](#how-is-capgo-affected-by-lack-of-network-connectivity) Capgo updater (included in your application when you build your app with Capgo) is designed to be resilient to network connectivity issues. In the default update behavior, when the application launches it alerts the Capgo updater, which spawns a separate thread to make a network request to Capgo’s servers and ask for an update. Usamos intencionalmente un hilo separado para evitar afectar el bloqueo de cualquier otra cosa que la aplicación pueda estar haciendo. Si la solicitud de red falla o se agota el tiempo de espera, el actualizador simplemente intentará verificar nuevamente la próxima vez que se inicie la aplicación. Las herramientas de línea de comandos Capgo (por ejemplo, `npx @capgo/cli@latest bundle upload`) requieren conectividad de red para funcionar. Si está utilizando Capgo para distribuir su aplicación, debe asegurarse de que su sistema CI tenga conectividad de red. ### What happens if a user doesn’t update for a long time and misses an update?[](https://capgo.app/docs/faq/#what-happens-if-a-user-doesnt-update-for-a-long-time-and-misses-an-update "Direct link to What happens if a user doesn't update for a long time and misses an update?")Our implementation always sends an update specifically tailored for the device that is requesting it updating the requestor always to the latest version available. Thus if a user doesn’t update for a while they will “miss” intermediate updates. [Section titled “What happens if a user doesn’t update for a long time and misses an update?Our implementation always sends an update specifically tailored for the device that is requesting it updating the requestor always to the latest version available. Thus if a user doesn’t update for a while they will “miss” intermediate updates.”](#what-happens-if-a-user-doesnt-update-for-a-long-time-and-misses-an-updateour-implementation-always-sends-an-update-specifically-tailored-for-the-device-that-is-requesting-it-updating-the-requestor-always-to-the-latest-version-available-thus-if-a-user-doesnt-update-for-a-while-they-will-miss-intermediate-updates) The update server could be changed to support responding with either the next incremental version or the latest version depending on your application’s needs. Háganos saber si los comportamientos de actualización alternativos son importantes para usted. ### How does Capgo relate to Capacitor?[](https://capgo.app/docs/faq/#how-does-capgo-relate-to-capacitor "Direct link to How does Capgo relate to Capacitor?") [Section titled “How does Capgo relate to Capacitor?”](#how-does-capgo-relate-to-capacitor) Capgo es un complemento para Capacitor que agrega inserción de código. Capgo no reemplaza a Capacitor. Puede seguir utilizando las herramientas Capacitor que ya conoce y ama. We track the latest stable release of Capacitor and update our code push plugin to work with it. ### ¿Cuándo ocurren las actualizaciones?[](https://capgo.app/docs/faq/#when-do-updates-happen "Enlace directo a ¿Cuándo ocurren las actualizaciones?") [Section titled “¿Cuándo ocurren las actualizaciones?”](#cuándo-ocurren-las-actualizaciones) De forma predeterminada, el actualizador Capgo busca actualizaciones al iniciar la aplicación. It runs on a background thread and does not block the UI thread. Todas las actualizaciones se instalarán mientras el usuario esté usando la aplicación y se aplicarán la próxima vez que se reinicie la aplicación. It is also possible to run the Capgo updater manually using the `@capgo/capacitor-updater` package, through which it is possible to trigger updates at any time, including via a push notification. The Capgo updater is designed such that when the network is not available, or the server is down or otherwise unreachable, the app will continue to run as normal. Si alguna vez decide eliminar una actualización de nuestros servidores, todos sus clientes seguirán funcionando normalmente. Hemos agregado la capacidad de deshacer parches. Lo más sencillo es simplemente adjuntar un paquete anterior a tu canal para deshacerlo. ### ¿Necesito mantener mi app\_id en secreto?[](https://capgo.app/docs/faq/#do-i-need-to-keep-my-app_id-secret "Enlace directo a ¿Necesito mantener mi app_id en secreto?") [Section titled “¿Necesito mantener mi app\_id en secreto?”](#necesito-mantener-mi-app_id-en-secreto) No. El `app_id` está incluido en su aplicación y es seguro que sea público. Puedes registrarlo en el control de versiones (incluso públicamente) y no preocuparte de que alguien más acceda a él. Someone who has your `app_id` can fetch the latest version of your app from Capgo servers, but they cannot push updates to your app or access any other aspect of your Capgo account. ### What information is sent to Capgo servers?[](https://capgo.app/docs/faq/#what-information-is-sent-to-capgo-servers "Direct link to What information is sent to Capgo servers?") [Section titled “What information is sent to Capgo servers?”](#what-information-is-sent-to-capgo-servers) Aunque Capgo se conecta a la red, no envía ninguna información de identificación personal. Incluir Capgo no debería afectar sus declaraciones para Play Store o App Store. Las solicitudes enviadas desde la aplicación a los servidores Capgo incluyen:- aplicación\_id (especificado `capacitor.config.json`) * canal (opcional en `capacitor.config.json`) * lanzamiento\_versión (nombre de versión de AndroidManifest.xml o CFBundleShortVersionString de Info.plist o `capacitor.config.json` si está configurado en [`CapacitorUpdater.version`](/docs/plugin/settings/#version)) * versión\_número (generado como parte de `npx @capgo/cli@latest bundle upload`) * os\_version (por ejemplo, ‘11.2.1’) * plataforma (por ejemplo, ‘android’, necesaria para enviar el parche correcto) Eso es todo. El código para esto está en `updater/library/src/network.rs` * dispositivo\_id (generado en el dispositivo en la primera ejecución, utilizado para eliminar duplicados de instalaciones por dispositivo y permitirnos cobrar en función de los usuarios instalados (por ejemplo, usuarios activos mensuales), en lugar del total de parches o de instalaciones de parches) * custom\_id (opcional, configurado en tiempo de ejecución por el desarrollador, usado para vincular un dispositivo a un usuario en su sistema) ### ¿Qué plataformas admite Capgo?[](https://capgo.app/docs/faq/#what-platforms-does-capgo-support "Enlace directo a ¿Qué plataformas admite Capgo?") [Section titled “¿Qué plataformas admite Capgo?”](#qué-plataformas-admite-capgo) Actualmente, Capgo admite Android, iOS y Electron. Todos están listos para producción. El uso de Capgo para iOS, Android o Electron pueden ser decisiones independientes. Puede configurar su estrategia de canal para Android y un ipa integrado para App Store, o canales Electron, según sea necesario. Capgo se puede hacer (con relativa facilidad) para que admita objetivos integrados o de escritorio. Si son importantes para usted, háganoslo saber. ### ¿Cómo interactúa Capgo con Play Testing Tracks o Apple TestFlight?[](https://capgo.app/docs/faq/#how-does-capgo-interact-with-play-testing-tracks-or-apple-testflight "Enlace directo a ¿Cómo interactúa Capgo con Play Testing Tracks o Apple TestFlight?") [Section titled “¿Cómo interactúa Capgo con Play Testing Tracks o Apple TestFlight?”](#cómo-interactúa-capgo-con-play-testing-tracks-o-apple-testflight) Cada una de las tiendas de aplicaciones tiene mecanismos separados para distribuir aplicaciones a grupos limitados de usuarios (por ejemplo, “pruebas internas”, “beta cerrada”, etc.). Todos estos son mecanismos para segmentar a sus usuarios en grupos y distribuir versiones específicas de sus aplicaciones a cada uno. Desafortunadamente, no todos estos mecanismos permiten a terceros detectar cuándo se instalan aplicaciones en una pista de prueba específica o mediante TestFlight. Por lo tanto, no tenemos una visibilidad confiable de la composición de estos grupos y no podemos controlar de manera confiable el acceso a los parches Capgo basados ​​en estos grupos. [https://stackoverflow.com/questions/53291007/can-an-android-application-identify-the-test-track-within-google-play](https://stackoverflow.com/questions/53291007/can-an-android-application-identify-the-test-track-within-google-play/) [https://stackoverflow.com/questions/26081543/how-to-tell-at-runtime-whether-an-ios-app-is-running-through-a-testflight-beta-i](https://stackoverflow.com/questions/26081543/how-to-tell-at-runtime-whether-an-ios-app-is-running-through-a-testflight-beta-i/) Si desea segmentar la disponibilidad del paquete Capgo, existen 4 opciones potenciales:1. Utilice un canal separado para cada grupo. Este es el enfoque más sencillo, pero requiere que administres múltiples canales. Es posible que ya tengas canales de desarrollo y canales de producción con diferente disponibilidad. De este modo, puede actualizar sus canales de desarrollo, verificarlos y luego actualizar por separado sus canales de producción. Recomendamos utilizar ramas/etiquetas en su control de versiones para ayudar a realizar un seguimiento de las fuentes asociadas con cada versión. 2. Realice un seguimiento de su propio conjunto de usuarios registrados, desactive las actualizaciones automáticas y active actualizaciones solo para ciertos usuarios a través del paquete `@capgo/capacitor-updater`. Esto funciona hoy, pero requiere que usted administre su propia lista de suscripción. 3. Capgo permite crear su propio mecanismo de suscripción por dispositivo (similar a Test Tracks o TestFlight, solo que es independiente de la plataforma). Esto le permite a su equipo de control de calidad optar por el paquete antes de promocionarlo al público en general. 4. Capgo tiene implementaciones basadas en porcentajes. Esto no le permite elegir a qué dispositivos enviar, pero puede ayudarle a implementar de forma incremental y retroceder ante cualquier problema. ## Facturación[](https://capgo.app/docs/faq/#billing "Enlace directo a Facturación") [Section titled “Facturación”](#facturación) ### ¿Cómo actualizo o degrade mi plan?[](https://capgo.app/docs/faq/#how-do-i-upgrade-or-downgrade-my-plan "Enlace directo a ¿Cómo actualizo o degrade mi plan?") [Section titled “¿Cómo actualizo o degrade mi plan?”](#cómo-actualizo-o-degrade-mi-plan) Puede actualizar o degradar su plan en cualquier momento en su panel de control: [https://console.capgo.app/settings/organization/plans](https://console.capgo.app/settings/organization/plans/) ### ¿Cuándo se restablece mi período de facturación?[](https://capgo.app/docs/faq/#when-does-my-billing-period-reset "Enlace directo a ¿Cuándo se restablece mi período de facturación?") [Section titled “¿Cuándo se restablece mi período de facturación?”](#cuándo-se-restablece-mi-período-de-facturación) Los períodos de facturación se restablecen automáticamente cada mes en el mes en que se suscribió por primera vez a Capgo. Por ejemplo, si se suscribió el día 15 del mes, su período de facturación se restablecerá el día 15 de cada mes. ### ¿Cómo cancelo mi suscripción?[](https://capgo.app/docs/faq/#how-do-i-cancel-my-subscription "Enlace directo a ¿Cómo cancelo mi suscripción?") [Section titled “¿Cómo cancelo mi suscripción?”](#cómo-cancelo-mi-suscripción) Puedes cancelar tu suscripción en cualquier momento en tu panel de control: [https://console.capgo.app/settings/organization/plans](https://console.capgo.app/settings/organization/plans/) ### ¿Puedo pagar con un año de anticipación?[](https://capgo.app/docs/faq/#can-i-pay-for-a-year-in-advance "Enlace directo a ¿Puedo pagar con un año de anticipación?") [Section titled “¿Puedo pagar con un año de anticipación?”](#puedo-pagar-con-un-año-de-anticipación) Sí, puedes hacerlo en cualquier momento en tu panel de control: [https://console.capgo.app/settings/organization/plans](https://console.capgo.app/settings/organization/plans/) ### Estadísticas y análisis[](https://capgo.app/docs/faq/#stats-and-analytics "Enlace directo a Estadísticas y análisis") [Section titled “Estadísticas y análisis”](#estadísticas-y-análisis) Las estadísticas en su panel se actualizan cada medianoche UTC. Las estadísticas se calculan en función de la cantidad de [MAU](https://capgo.app/docs/faq/#what-is-the-difference-between-a-bundle-and-a-release "Enlace directo a ¿Cuál es la diferencia entre un paquete y una versión?") que se han instalado en sus dispositivos. ## Cómo se genera la ID del dispositivo[](https://capgo.app/docs/faq/#how-device-id-is-generated "Enlace directo a Cómo se genera la ID del dispositivo") [Section titled “Cómo se genera la ID del dispositivo”](#cómo-se-genera-la-id-del-dispositivo) La ID del dispositivo se genera en el dispositivo en la primera ejecución y se utiliza para eliminar duplicados de instalaciones por dispositivo y permitirnos cobrar en función de los usuarios instalados (por ejemplo, usuarios activos mensuales), en lugar del total de parches o instalaciones de parches totales. MAU es una mejor solución que el número de instalaciones para fijar el precio Capgo, ya que es más preciso y refleja el costo real de Capgo por dispositivo.**Persistencia de ID de dispositivo (actualizado en v6.25.0 y v7.25.0)**: * **Comportamiento actual**: el ID del dispositivo ahora persiste durante las reinstalaciones de la aplicación. Se almacena de forma segura en el llavero del dispositivo (iOS) o EncryptedSharedPreferences (Android), lo que nos permite rastrear el mismo dispositivo incluso después de desinstalarlo/reinstalarlo. * **Comportamiento anterior** (antes de v6.25.0/v7.25.0): por razones de privacidad relacionadas con las políticas de tienda Apple y Google, el ID del dispositivo se restablecía en cada reinstalación de la aplicación, lo que hacía imposible rastrear el mismo dispositivo en todas las reinstalaciones. Las reglas de privacidad son aplicadas por Apple y Google, y la implementación de Capgo cumple con sus mejores prácticas para la identificación de dispositivos. La ID del dispositivo no aparecerá en su lista de dispositivos hasta que instalen el primer parche. ## ¿Por qué mi número de dispositivo es diferente a mi MAU?[](https://capgo.app/docs/faq/#why-my-device-number-is-different-than-my-mau "Enlace directo a ¿Por qué mi número de dispositivo es diferente a mi MAU?") [Section titled “¿Por qué mi número de dispositivo es diferente a mi MAU?”](#por-qué-mi-número-de-dispositivo-es-diferente-a-mi-mau) Actualmente, la lista de dispositivos no se actualiza con tanta frecuencia como la MAU. La lista de dispositivos se actualiza solo cuando un dispositivo instala una actualización. Mientras que la MAU se actualiza en cada inicio de la aplicación. Esta es una limitación actual de la plataforma. Nuestra plataforma de análisis no admite actualizaciones sin procesar, por lo que utilizamos una base de datos convencional para la lista de dispositivos. Para limitar la cantidad de consultas a la base de datos, actualizamos la fila solo cuando se actualiza la aplicación. Esta limitación se eliminará en el futuro. ## ¿Cómo tener una actualización diferente por plataforma?[](https://capgo.app/docs/faq/#how-to-have-different-update-by-platform "Enlace directo a ¿Cómo tener una actualización diferente por plataforma?") [Section titled “¿Cómo tener una actualización diferente por plataforma?”](#cómo-tener-una-actualización-diferente-por-plataforma) Puedes crear un canal para cada plataforma. y deshabilite las actualizaciones específicas de la plataforma en cada canal. En el canal ios, deshabilite las actualizaciones de Android y en el canal de Android, deshabilite las actualizaciones de ios. Luego cargue un paquete en cada canal para tener una actualización diferente para cada plataforma. Si necesita tener la misma actualización para ambas plataformas, puede vincular un paquete a varios canales. No es necesario duplicar el paquete. # Tech Support für Capgo > Cómo obtener soporte técnico para Capgo ## Soporte por Discord [Section titled “Soporte por Discord”](#soporte-por-discord) Capgo tiene un [servidor de Discord](https://discordcom/invite/VnYRvBfgA6) oficial. Obtener soporte técnico allí es probablemente una de las formas más rápidas de obtener una respuesta. Aquí hay un curso rápido: 1. * ve al canal `questions` ![Ask on discord](/discord-questions.webp) 2. * crea tu hilo ![Crear a question on discord](/discord-newquestion.webp) 3. * Describe tu problema y selecciona las etiquetas relevantes ![Crear a post on discord](/discord-new-post.webp) 4. * Comparte tu ID de cuenta segura (opcional) Esto permitirá al personal de Capgo revisar tu cuenta. Compartir este ID es seguro, ya que fue diseñado para ser compartido públicamente. Para compartirlo, por favor ve a la [configuración de Capgo](https://console.capgo.app/dashboard/settings/account/). Allí, haz clic en `copy account id` ![Share your id without leaking your Información](/share-secure-id.webp) Esto copiará el ID seguro de la cuenta al portapapeles. Por favor, incluye eso en tu publicación de Discord. ## Soporte por correo electrónico [Section titled “Soporte por correo electrónico”](#soporte-por-correo-electrónico) Esta es la forma más lenta de obtener soporte. Por favor, utiliza primero el servidor de Discord. Si necesitas contactarnos por correo electrónico, por favor envía un correo a . # Añadir una Aplicación > Añade una aplicación a tu cuenta de Capgo e instala el Plugin en tu aplicación ## Requisitos [Section titled “Requisitos”](#requisitos) Antes de comenzar con Capgo, asegúrate de tener: * Una aplicación Capacitor instalada y configurada. [Aprende cómo configurar Capacitor](https://capacitorjs.com/docs/getting-started/) * Node.js 20 o posterior instalado * Uno de los siguientes entornos de desarrollo: * **macOS** con Xcode (para desarrollo iOS) y/o Android Studio (para desarrollo Android) * **Linux** con Android Studio (para desarrollo Android) * **Windows** con Android Studio (para desarrollo Android) ## Introducción a Capgo [Section titled “Introducción a Capgo”](#introducción-a-capgo) [Play](https://youtube.com/watch?v=NzXXKoyhTIo) ## Las actualizaciones en vivo están a 3 pasos de distancia [Section titled “Las actualizaciones en vivo están a 3 pasos de distancia”](#las-actualizaciones-en-vivo-están-a-3-pasos-de-distancia) ### Configuración guiada [Section titled “Configuración guiada”](#configuración-guiada) 1. Crea tu cuenta en . ![captura de pantalla de registro](/signup.webp "captura de pantalla de registro") 2. Usa el comando Init para comenzar ```bash npx @capgo/cli@latest init [APIKEY] ``` Se te presentará una serie de preguntas. Proporciona las respuestas necesarias para completar la configuración automatizada. 3. Implementa una actualización en vivo Tip Siguiendo estos pasos, estarás en funcionamiento en poco tiempo. Si necesitas más asistencia durante el proceso, nuestro equipo de soporte está [aquí para ayudar](https://support.capgo.app). ¡Feliz incorporación! [Guía Detallada de Incorporación ](/docs/getting-started/onboarding/)Vea la guía completa paso a paso para el proceso de incorporación CLI [Implementar una actualización en vivo ](/docs/getting-started/Desplegar/)Aprende cómo implementar una actualización en vivo en tu aplicación ### Configuración manual [Section titled “Configuración manual”](#configuración-manual) En caso de que el comando init no funcione para ti, puedes añadir manualmente una aplicación. 1. Conecta la CLI a tu cuenta: ```bash npx @capgo/cli@latest login [APIKEY] ``` 2. Añade la aplicación a tu cuenta con este comando: ```bash npx @capgo/cli@latest app add [APP_NAME] ``` 3. Instala el Plugin en tu aplicación: ```bash npm i @capgo/capacitor-updater ``` 4. Configura el Plugin en tu `capacitor.config` ```JSON { "plugins": { CapacitorUpdater: { "appId": "Your appID", "autoUpdate": true, "version": "1.0.0" } } } ``` [Ver todas las opciones disponibles](/docs/plugin/settings/). Esta información se inferirá si no se proporciona. 5. Llama al método init lo más temprano posible en tu aplicación: ```TypeScript import { CapacitorUpdater } from '@capgo/capacitor-updater'; CapacitorUpdater.notifyAppReady(); ``` 6. Implementa una actualización en vivo Instalación para versiones anteriores de Capacitor El comando anterior instala la última versión (v8.x) para Capacitor 8. Para versiones anteriores de Capacitor, usa la etiqueta npm apropiada: ```bash # Capacitor 7 npm i @capgo/capacitor-updater@lts-v7 # Capacitor 6 npm i @capgo/capacitor-updater@lts-v6 # Capacitor 5 npm i @capgo/capacitor-updater@lts-v5 ``` Cada versión mayor del plugin corresponde a la versión mayor de Capacitor (v8 → Capacitor 8, v7 → Capacitor 7, v6 → Capacitor 6, v5 → Capacitor 5). Las versiones menores comparten el mismo conjunto de características en todas las versiones mayores (ej., 5.34.0, 6.34.0, 7.34.0, y 8.34.0 incluyen las mismas características). # Integración CI/CD > Integrar Capgo en tu pipeline de CI/CD te permite automatizar completamente el proceso de construir e implementar actualizaciones en tu aplicación. Al aprovechar la CLI de Capgo y semantic-Lanzamiento, puedes asegurar implementaciones consistentes y confiables y habilitar iteración rápida. Integrar Capgo en tu pipeline de CI/CD te permite automatizar completamente el proceso de construir e implementar actualizaciones en tu aplicación. Al aprovechar la CLI de Capgo y semantic-Lanzamiento, puedes asegurar implementaciones consistentes y confiables y habilitar iteración rápida. ## Beneficios de la Integración CI/CD [Section titled “Beneficios de la Integración CI/CD”](#beneficios-de-la-integración-cicd) * **Automatización**: No más pasos manuales ni espacio para errores humanos. Todo tu proceso de construcción, prueba e implementación puede ser automatizado de principio a fin. * **Consistencia**: Cada implementación sigue el mismo conjunto de pasos, asegurando un proceso predecible y repetible. Esto es especialmente valioso cuando tienes múltiples miembros del equipo contribuyendo código. * **Iteraciones más rápidas**: Con implementaciones automatizadas, puedes enviar actualizaciones más frecuentemente y con confianza. No más espera para QA manual o aprobaciones de lanzamiento. ## CLI de Capgo [Section titled “CLI de Capgo”](#cli-de-capgo) La CLI de Capgo es la clave para integrar Capgo en tu flujo de trabajo CI/CD. Proporciona comandos para subir nuevas versiones de Paquetes, gestionar canales y más. El comando más importante para la integración CI/CD es `bundle upload`: ```shell npx @capgo/cli@latest bundle upload --channel Production --apikey YOUR_API_KEY ``` Si usas encriptación debes proporcionarla de una de estas formas: **Usando una ruta de archivo de clave privada:** ```shell npx @capgo/cli@latest bundle upload --channel Production --apikey YOUR_API_KEY --key-v2 PRIVATE_KEY_PATH ``` **Usando el contenido de la clave privada directamente (recomendado para CI/CD):** ```shell npx @capgo/cli@latest bundle upload --channel Production --apikey YOUR_API_KEY --key-data-v2 PRIVATE_KEY_CONTENT ``` **Usando Variables de entorno (mejor práctica para CI/CD):** ```shell npx @capgo/cli@latest bundle upload --channel Production --apikey YOUR_API_KEY --key-data-v2 "$CAPGO_PRIVATE_KEY" ``` ### Configurar Variables de Entorno para Encriptación [Section titled “Configurar Variables de Entorno para Encriptación”](#configurar-variables-de-entorno-para-encriptación) Para entornos CI/CD, se recomienda almacenar tu clave privada como una Variable de entorno en lugar de un archivo. Aquí está cómo configurarlo: 1. **Obtén el contenido de tu clave privada:** ```shell cat .capgo_key_v2 | pbcopy ``` Esto copia el contenido de la clave a tu portapapeles. 2. **Agrégalo a tu entorno CI/CD:** * **GitHub Actions**: Agrega `CAPGO_PRIVATE_KEY` a los secretos de tu repositorio * **GitLab CI**: Agrégalo como una Variable enmascarada en la configuración de tu proyecto * **CircleCI**: Agrégalo como una Variable de entorno en la configuración de tu proyecto * **Jenkins**: Agrégalo como una credencial de texto secreto 3. **Úsalo en tu pipeline:** ```yaml - run: npx @capgo/cli@latest bundle upload --channel=production --apikey=${{ secrets.CAPGO_API_KEY }} --key-data-v2 "${{ secrets.CAPGO_PRIVATE_KEY }}" ``` **Nota**: La bandera `--key-data-v2` te permite pasar el contenido de la clave privada directamente como una cadena, haciéndolo perfecto para Variables de entorno en pipelines CI/CD donde no quieres crear archivos temporales. Este comando sube la construcción web actual al canal especificado. Típicamente ejecutarás esto como el último paso en tu pipeline CI/CD, después de que tu construcción web se haya completado exitosamente. ## Configurar Capgo en tu Pipeline CI/CD [Section titled “Configurar Capgo en tu Pipeline CI/CD”](#configurar-capgo-en-tu-pipeline-cicd) Aunque los pasos exactos variarán dependiendo de tu herramienta CI/CD de elección, el proceso general para integrar Capgo se ve así: 1. **Generar una clave API**: Inicia sesión en el panel de Capgo y crea una nueva clave API. Esta clave se usará para autenticar la CLI en tu entorno CI/CD. ¡Manténla secreta y nunca la comprometas en tu repositorio! 2. **Configurar el comando `bundle upload`**: Agrega un paso a tu configuración CI/CD que ejecute el comando `bundle upload` con los argumentos apropiados: Subir.yml ```YAML - run: npx @capgo/cli@latest bundle upload --channel=production --apikey=${{ secrets.CAPGO_API_KEY }} ``` \n Reemplaza `Production` con el canal al que quieres implementar, `${{ secrets.CAPGO_API_KEY }}` con la Variable de entorno que contiene tu clave API, y agrega `--key-data-v2 "${{ secrets.CAPGO_PRIVATE_KEY }}"` si usas encriptación. 3. **Agregar el paso `upload` después de tu construcción web**: Asegúrate de que el paso `upload` venga después de que tu construcción web se haya completado exitosamente. Esto asegura que siempre estés implementando tu código más reciente.\n Aquí hay una configuración de ejemplo para GitHub Actions:\n Subir.yml ```YAML name: Deploy to Capgo on: push: branches: [main] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - uses: actions/setup-node@v6 with: node-version: '24' - run: npm ci - run: npm run build - run: npm install -g @capgo/cli - run: npx @capgo/cli@latest bundle upload --channel=production --apikey=${{ secrets.CAPGO_API_KEY }} --key-data-v2 "${{ secrets.CAPGO_PRIVATE_KEY }}" ``` ## Gestión de Versiones con Semantic-Lanzamiento [Section titled “Gestión de Versiones con Semantic-Lanzamiento”](#gestión-de-versiones-con-semantic-lanzamiento) La forma recomendada de manejar el versionado con Capgo es establecer la versión en tu archivo `capacitor.config.ts` importándola desde `package.json`: ```ts import pkg from './package.json' const config: CapacitorConfig = { // ... otra configuración plugins: { CapacitorUpdater: { version: pkg.version, } } } ``` Este enfoque te permite: 1. Usar semantic-Lanzamiento (o cualquier otra herramienta) para actualizar la versión de `package.json` 2. Construir tu aplicación con la versión actualizada incluida automáticamente 3. Subir el Paquete con la versión correcta Tu flujo de trabajo CI/CD se vería así: ```yaml - run: npm ci - run: npx semantic-release # Actualiza la versión de package.json - run: npm run build # Construye con la nueva versión de capacitor.config - run: npx @capgo/cli@latest bundle upload --channel=production --apikey=${{ secrets.CAPGO_API_KEY }} ``` Aquí hay un archivo de configuración `.releaserc` de ejemplo para semantic-Lanzamiento: ```json { "branches": [ "main", { "name": "beta", "prerelease": true } ], "plugins": [ "@semantic-release/commit-analyzer", "@semantic-release/release-notes-generator", "@semantic-release/changelog", [ "@semantic-release/git", { "assets": ["CHANGELOG.md", "package.json"], "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}" } ] ] } ``` Esta configuración hace lo siguiente: 1. Analiza los mensajes de commit para determinar el próximo número de versión, siguiendo la especificación de Conventional Commits. 2. Genera notas de lanzamiento basadas en los commits desde el último lanzamiento. 3. Actualiza el archivo `CHANGELOG.md` con las nuevas notas de lanzamiento. 4. Actualiza la versión de `package.json`, que será recogida por tu Capacitor.config. 5. Hace commit de los archivos actualizados `CHANGELOG.md`, `package.json` y cualquier otro archivo cambiado de vuelta al repositorio. Asegúrate de ejecutar semantic-Lanzamiento antes de construir tu aplicación para que la versión actualizada de `package.json` se incluya en tu construcción a través del Capacitor.config. ## Solución de Problemas [Section titled “Solución de Problemas”](#solución-de-problemas) Si encuentras problemas con tu integración CI/CD de Capgo, aquí hay algunas cosas para verificar: * **Clave API**: Asegúrate de que tu clave API sea válida y tenga los permisos necesarios. Si usas una Variable de entorno, verifica dos veces que esté configurada correctamente. * **Versión de CLI**: Asegúrate de que estés usando la última versión de la CLI de Capgo. Las versiones antiguas pueden tener problemas de compatibilidad o carecer de ciertas características. * **Artefactos de construcción**: Confirma que tu construcción web esté generando los archivos de salida esperados. La CLI de Capgo necesita una construcción web válida para crear un Paquete. * **Conectividad de red**: Verifica que tu entorno CI/CD tenga acceso de red a los servidores de Capgo. Los problemas de firewall o proxy a veces pueden interferir con el comando `upload`. Si aún tienes problemas, contacta al soporte de Capgo para asistencia. Pueden ayudar a solucionar cualquier problema con tu configuración específica. ## Conclusión [Section titled “Conclusión”](#conclusión) Integrar Capgo en tu pipeline de CI/CD con gestión de versiones adecuada puede agilizar enormemente tu flujo de trabajo de desarrollo. Al automatizar tus implementaciones y versionado a través del enfoque de Capacitor.config, puedes enviar actualizaciones más rápido y con más confianza. El enfoque recomendado de establecer la versión en tu archivo `capacitor.config.ts` y usar semantic-Lanzamiento para actualizar `package.json` proporciona un proceso de implementación robusto y confiable que te permite enfocarte en construir grandes características en lugar de preocuparte por pasos de lanzamiento manuales. Para más detalles sobre los comandos y opciones de la CLI de Capgo, consulta la [referencia CLI](/docs/cli/overview). Y para una inmersión más profunda en la configuración de semantic-Lanzamiento, ve los [documentos de semantic-release](https://github.com/semantic-release/semantic-release). ¡Felices implementaciones! # Implementar una Actualización en Vivo > Aprende cómo implementar una actualización en vivo en tu aplicación usando la función de Actualizaciones en Vivo de Capgo, permitiendo actualizaciones de UI y lógica en tiempo real sin reenvío a la tienda de aplicaciones. Utiliza la función de Actualizaciones en Vivo de Capgo para actualizar la interfaz de usuario y la lógica de negocio de tu aplicación de forma remota y en tiempo real. Envía actualizaciones de Paquetes JS directamente a tus usuarios sin pasar por la tienda de aplicaciones para corregir errores al instante y lanzar nuevas funciones. Esta guía asume que has completado el [Inicio Rápido de Capgo](/docs/getting-started/quickstart) y ya has: 1. Instalado el SDK `@capgo/capacitor-updater` en tu aplicación Capacitor 2. Configurado tu ID de aplicación y canal de actualización en `capacitor.config.ts` 3. Añadido en tu código el método `CapacitorUpdater.notifyAppReady()` Si aún no has completado estos pasos, por favor regresa y completa primero el inicio rápido. [Añadir una aplicación ](/docs/getting-started/Agregar-an-Aplicación/)Añade una aplicación a tu cuenta Capgo e instala el Plugin en tu aplicación ## Subir un Paquete [Section titled “Subir un Paquete”](#subir-un-paquete) Con el SDK de Capgo instalado y configurado, estás listo para subir tu primer Paquete de actualización en vivo: 1. Compila tus activos web: ```shell npm run build ``` 2. Sube el Paquete a Capgo: * Consola ```shell npx @capgo/cli@latest bundle upload --channel=production ``` * GitHub Actions .github/workflows/build\_and\_deploy.yml ```yml name: Build source code and send to Capgo concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true on: push: branches: - main jobs: deploy_to_capgo: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v6 - uses: actions/setup-node@v6 with: node-version: '24' - name: Install dependencies run: npm install - name: Build run: npm run build - name: Deploy to Capgo run: npx @capgo/cli@latest bundle upload -a ${{ secrets.CAPGO_TOKEN }} --channel ${{ env.CHANNEL }} env: CAPGO_TOKEN: ${{ secrets.CAPGO_TOKEN }} ``` * GitLab .gitlab-ci.yml ```yml stages: - build build: stage: build image: node:18 cache: - key: files: - package-lock.json paths: - .node_modules/ script: - npm install - npm run build - npx @capgo/cli@latest bundle upload -a $CAPGO_TOKEN --channel $CAPGO_CHANNEL artifacts: paths: - node_modules/ - dist/ only: - master ``` Esto subirá una nueva versión de Paquete al canal especificado en el comando. ### Solución de Problemas de Subida [Section titled “Solución de Problemas de Subida”](#solución-de-problemas-de-subida) Si tu subida falla, verifica dos veces: * Tu ID de aplicación en `capacitor.config.ts` coincide con tu aplicación en el panel de Capgo * Estás ejecutando el comando de subida desde la raíz de tu proyecto Capacitor * Tus activos web están compilados y actualizados Si aún tienes problemas, ve a la sección de [Solución de Problemas](/docs/getting-started/troubleshooting/). ## Recibir una Actualización en un Dispositivo [Section titled “Recibir una Actualización en un Dispositivo”](#recibir-una-actualización-en-un-dispositivo) Una vez que tu Paquete está subido, puedes probar la actualización en vivo en un dispositivo: 1. Sincroniza tu aplicación con el dispositivo: ```shell npx cap sync ios ``` 2. Abre otra terminal y ejecuta el siguiente comando para verificar el estado de la actualización: ```shell npx @capgo/cli@latest app debug ``` 3. Ejecuta tu aplicación localmente: ```shell npx cap run ios ``` O abre el proyecto iOS/Android en Xcode/Android Studio y haz una ejecución nativa. 4. Mantén la aplicación abierta durante aproximadamente 30 segundos para permitir que la actualización se descargue en segundo plano. 5. Los Registros tardarán unos segundos en actualizarse y mostrar el estado de la actualización. 6. Cierra y vuelve a abrir la aplicación. ¡Deberías ver tu actualización en vivo aplicada! Consulta el [Inicio Rápido de Capgo](/docs/getting-started/quickstart#receiving-a-live-update-on-a-device) para más detalles sobre cómo probar actualizaciones en vivo. ## Próximos Pasos [Section titled “Próximos Pasos”](#próximos-pasos) ¡Felicidades por implementar tu primera actualización en vivo con Capgo! 🎉 Para aprender más, revisa el resto de la [documentación de Actualizaciones en Vivo de Capgo](/docs/live-updates). Algunos temas clave para revisar a continuación: * [Dirigir Actualizaciones con Canales](/docs/live-updates/channels) * [Personalizar Comportamiento de Actualización](/docs/live-updates/update-behavior) * [Reversiones de Actualizaciones en Vivo](/docs/live-updates/rollbacks) # Guía de Incorporación CLI > Guía completa paso a paso para incorporar tu aplicación con Capgo usando la CLI interactiva ## Resumen Rápido [Section titled “Resumen Rápido”](#resumen-rápido) La CLI de Capgo proporciona una incorporación interactiva que configura actualizaciones en vivo para tu aplicación Capacitor. Podrás: 1. ✅ Registrar tu aplicación en Capgo 2. 🔌 Instalar y configurar el plugin de actualización 3. 🚀 Implementar tu primera actualización en vivo 4. 📱 Probar la actualización en tu dispositivo **Tiempo estimado:** 10-20 minutos (varía según tu velocidad de internet y tiempo de compilación) Tip La incorporación es totalmente reanudable: sal en cualquier momento y continúa más tarde desde donde lo dejaste. ## Iniciar la Incorporación [Section titled “Iniciar la Incorporación”](#iniciar-la-incorporación) Ejecuta el comando de incorporación con tu clave API: ```bash npx @capgo/cli@latest init [APIKEY] ``` Verás el mensaje de bienvenida: ```plaintext Capgo onboarding 🛫 ``` ## Qué Sucede Durante la Incorporación [Section titled “Qué Sucede Durante la Incorporación”](#qué-sucede-durante-la-incorporación) La CLI te guiará a través de 13 pasos interactivos: **Fase de Configuración (Pasos 1-6):** * Verificar tu entorno de desarrollo (Xcode/Android Studio) * Agregar tu aplicación a Capgo y crear un canal de producción * Instalar el plugin `@capgo/capacitor-updater` * Inyectar el código requerido en tu aplicación * Opcionalmente habilitar cifrado de extremo a extremo * Elegir una plataforma para pruebas (iOS o Android) **Fase de Pruebas (Pasos 7-12):** * Compilar tu aplicación y ejecutarla en un dispositivo/simulador * Hacer un cambio de código visible (automático o manual) * Subir el bundle actualizado a Capgo * Ver la actualización en vivo aparecer en tu dispositivo en tiempo real **Finalización (Paso 13):** * ¡Tu aplicación está lista para actualizaciones en vivo! 🎉 ## El Proceso de Incorporación de 13 Pasos [Section titled “El Proceso de Incorporación de 13 Pasos”](#el-proceso-de-incorporación-de-13-pasos) ### Paso 1: Verificar Requisitos Previos [Section titled “Paso 1: Verificar Requisitos Previos”](#paso-1-verificar-requisitos-previos) La CLI verifica tu entorno de desarrollo para asegurar que tienes las herramientas necesarias instaladas. **Qué se verifica:** * **Xcode** (solo macOS) - para desarrollo iOS * **Android SDK** - para desarrollo Android **Resultados posibles:** ✅ **Ambos entornos encontrados:** ```plaintext ✅ Xcode detected - iOS development ready ✅ Android SDK detected - Android development ready ``` ⚠️ **Ningún entorno encontrado:** ```plaintext ⚠️ Xcode not found ⚠️ Android SDK not found ❌ No development environment detected 📱 To develop mobile apps with Capacitor, you need: • For iOS: Xcode (macOS only) - https://developer.apple.com/xcode/ • For Android: Android Studio - https://developer.android.com/studio ``` Caution Si no se detecta ningún entorno de desarrollo, se te preguntará si deseas continuar. Se recomienda instalar al menos una plataforma antes de continuar. ### Paso 2: Agregar Tu Aplicación [Section titled “Paso 2: Agregar Tu Aplicación”](#paso-2-agregar-tu-aplicación) La CLI te conectará a Capgo y agregará tu aplicación a tu cuenta. ```plaintext (spinner) Running: npm @capgo/cli@latest login *** Login Done ✅ ❓ Add {appId} in Capgo? ``` **Si tu ID de aplicación ya está en uso:** La CLI sugerirá alternativas: ```plaintext ❌ App ID "com.example.app" is already taken 💡 Here are some suggestions: 1. com.example.app2 2. com.example.app3 3. com.example.app.new 4. com.example.app.app ❓ What would you like to do? ``` Puedes elegir una sugerencia o ingresar un ID de aplicación personalizado. Note Los IDs de aplicación deben seguir la notación de dominio inverso (ej., `com.example.myapp`) ### Paso 3: Crear Canal de Producción [Section titled “Paso 3: Crear Canal de Producción”](#paso-3-crear-canal-de-producción) Los canales te permiten gestionar diferentes flujos de actualización para tu aplicación. ```plaintext ❓ Create default channel production for {appId} in Capgo? ``` Tip **¡No te preocupes!** Esto es solo para pruebas locales durante la incorporación. Crear un canal de “producción” no significa que tus actualizaciones se enviarán inmediatamente a los clientes. Tienes control total sobre cuándo se implementan las actualizaciones. Selecciona **Sí** a menos que tengas requisitos específicos de canal. **Si seleccionas Sí:** ```plaintext (spinner) Running: npm @capgo/cli@latest channel add production {appId} --default Channel add Done ✅ (or "Channel already added ✅") ``` Se creará un canal de producción y se establecerá como predeterminado. Esta es la opción recomendada para la mayoría de los usuarios. **Si seleccionas No:** ```plaintext If you change your mind, run it for yourself with: "npm @capgo/cli@latest channel add production {appId} --default" ``` Necesitarás crear y configurar canales manualmente más tarde. Alternativamente, puedes: * Establecer el canal en tu archivo `capacitor.config.ts` * Usar el método JavaScript `setChannel()` para establecer el canal dinámicamente * Configurar canales más tarde desde la consola web de Capgo ### Paso 4: Instalar Plugin de Actualización [Section titled “Paso 4: Instalar Plugin de Actualización”](#paso-4-instalar-plugin-de-actualización) La CLI instalará el plugin `@capgo/capacitor-updater` compatible con tu versión de Capacitor. ```plaintext ❓ Automatic Install "@capgo/capacitor-updater" dependency in {appId}? ``` **Compatibilidad de versiones:** * **Capacitor 5**: Instala `@capgo/capacitor-updater` v5 * **Capacitor 6**: Instala `@capgo/capacitor-updater` v6 * **Capacitor 7**: Instala `@capgo/capacitor-updater` v7 * **Capacitor 8+**: Instala la última versión Caution Capgo solo soporta Capacitor v5 y superior. Si estás usando una versión anterior, necesitarás actualizar primero. **Opción de actualizaciones instantáneas:** Después de la instalación, se te preguntará: ```plaintext ❓ Do you want to set instant updates in {appId}? Read more: https://capgo.app/docs/live-updates/update-behavior/#applying-updates-immediately ``` Tip **¿Qué son las actualizaciones instantáneas?** Con las actualizaciones instantáneas habilitadas, tu aplicación aplica actualizaciones inmediatamente cuando se pone en segundo plano y se vuelve a abrir. Esto funciona sin problemas porque Capgo puede distribuir actualizaciones en todo el mundo en menos de 300ms. Sin esto (modo estándar), las actualizaciones se descargan en segundo plano y se aplican en el próximo reinicio de la aplicación. Las actualizaciones instantáneas son excelentes para iteración más rápida durante el desarrollo y correcciones críticas de errores en producción. **Si seleccionas Sí:** * Las actualizaciones se configurarán para aplicarse inmediatamente cuando la aplicación se ponga en segundo plano y se vuelva a abrir * `directUpdate: 'always'` y `autoSplashscreen: true` se agregarán a tu configuración * Tu `capacitor.config.ts` se actualizará automáticamente * **Actualizaciones Delta** se habilitarán automáticamente - esto envía solo los archivos que cambiaron entre actualizaciones en lugar del bundle completo, haciendo las actualizaciones mucho más rápidas **Si seleccionas No:** * Las actualizaciones usarán el comportamiento estándar (descarga en segundo plano, aplicación en el próximo reinicio) * Siempre puedes habilitar actualizaciones instantáneas más tarde modificando tu `capacitor.config.ts` ### Paso 5: Agregar Código de Integración [Section titled “Paso 5: Agregar Código de Integración”](#paso-5-agregar-código-de-integración) La CLI inyectará automáticamente el código requerido en tu archivo principal de aplicación. ```plaintext ❓ Automatic Add "CapacitorUpdater.notifyAppReady()" code and import in {appId}? ``` **Qué se agrega:** ```typescript import { CapacitorUpdater } from '@capgo/capacitor-updater' CapacitorUpdater.notifyAppReady() ``` **Detección de tipo de proyecto:** * **Nuxt.js**: Crea `plugins/capacitorUpdater.client.ts` * **Otros frameworks**: Agrega a tu archivo de entrada principal Tip **Si la inyección automática falla**, puedes agregar el código manualmente a tu archivo principal de aplicación: **Para Nuxt.js:** Crea `plugins/capacitorUpdater.client.ts`: ```typescript import { CapacitorUpdater } from '@capgo/capacitor-updater' export default defineNuxtPlugin(() => { CapacitorUpdater.notifyAppReady() }) ``` **Para otros frameworks:** Agrega a tu archivo de entrada principal (ej., `main.ts`, `index.js`, `App.tsx`): ```typescript import { CapacitorUpdater } from '@capgo/capacitor-updater' CapacitorUpdater.notifyAppReady() ``` Coloca este código después de tus importaciones y antes de la inicialización de tu aplicación. Para más detalles, consulta la [guía Agregar una Aplicación](/docs/getting-started/add-an-app/). ### Paso 6: Configurar Cifrado (Opcional) [Section titled “Paso 6: Configurar Cifrado (Opcional)”](#paso-6-configurar-cifrado-opcional) El cifrado de extremo a extremo agrega una capa adicional de seguridad para tus actualizaciones. ```plaintext 🔐 End-to-end encryption ✅ Use this for: Banking, healthcare, or apps with legal encryption requirements ⚠️ Note: Makes debugging harder - skip if you don't need it ❓ Enable end-to-end encryption for {appId} updates? ``` Note El cifrado solo está disponible para Capacitor v6 y superior. Si habilitas el cifrado, la CLI: 1. Generará claves de cifrado 2. Ofrecerá sincronizar tu configuración de Capacitor ### Paso 7: Seleccionar Plataforma [Section titled “Paso 7: Seleccionar Plataforma”](#paso-7-seleccionar-plataforma) Elige con qué plataforma probar durante la incorporación. ```plaintext 📱 Platform selection for onboarding This is just for testing during onboarding - your app will work on all platforms ❓ Which platform do you want to test with during this onboarding? Options: - iOS - Android ``` Tip Esto solo afecta el proceso de incorporación. Tu aplicación final soportará todas las plataformas. ### Paso 8: Compilar Tu Proyecto [Section titled “Paso 8: Compilar Tu Proyecto”](#paso-8-compilar-tu-proyecto) La CLI compilará tu aplicación y la sincronizará con Capacitor. ```plaintext ❓ Automatic build {appId} with "npm run build"? ``` **Qué sucede:** 1. Detecta tu tipo de proyecto 2. Ejecuta tu script de compilación 3. Ejecuta `npx cap sync {platform}` **Si falta el script de compilación:** Se te preguntará si deseas omitir la compilación o agregar un script de compilación a tu `package.json`. ### Paso 9: Ejecutar en Dispositivo [Section titled “Paso 9: Ejecutar en Dispositivo”](#paso-9-ejecutar-en-dispositivo) Prueba la versión inicial de tu aplicación en un dispositivo o simulador. ```plaintext ❓ Run {appId} on {PLATFORM} device now to test the initial version? ``` Si seleccionas **Sí**: ```plaintext (spinner) Running: npx cap run {platform} (device picker appears) App started ✅ 📱 Your app should now be running on your {platform} device with Capgo integrated 🔄 This is your baseline version - we'll create an update next ``` ### Paso 10: Hacer un Cambio de Prueba [Section titled “Paso 10: Hacer un Cambio de Prueba”](#paso-10-hacer-un-cambio-de-prueba) Ahora es momento de probar el sistema de actualización de Capgo haciendo un cambio visible. ```plaintext 🎯 Now let's test Capgo by making a visible change and deploying an update! ❓ How would you like to test the update? Options: - Auto: Let Capgo CLI make a visible change for you - Manual: I'll make changes myself ``` **Modo automático:** La CLI modificará automáticamente tus archivos para agregar un banner de prueba visible o un cambio. **Modo manual:** Haces tus propios cambios (ej., cambiar texto, colores o agregar elementos). **Manejo de versión:** ```plaintext ❓ How do you want to handle the version for this update? Options: - Auto: Bump patch version ({currentVersion} → {nextVersion}) - Manual: I'll provide the version number ``` **Compilar con cambios:** ```plaintext ❓ Build {appId} with changes before uploading? ``` Tip Si necesitas compilar manualmente en otra terminal, selecciona “No” y compila tú mismo, luego continúa. ### Paso 11: Subir Bundle [Section titled “Paso 11: Subir Bundle”](#paso-11-subir-bundle) Sube tu bundle de aplicación actualizado a Capgo. ```plaintext ❓ Upload the updated {appId} bundle (v{version}) to Capgo? ``` La CLI ejecuta: ```bash npx @capgo/cli@latest bundle upload ``` Tip **Actualizaciones Delta con Direct Update:** Si habilitaste actualizaciones instantáneas (Direct Update) en el Paso 4, la CLI preguntará automáticamente si deseas habilitar actualizaciones delta. Las actualizaciones delta envían solo los archivos que cambiaron entre versiones en lugar del bundle completo. Como normalmente solo cambian algunos archivos entre actualizaciones, esto hace las descargas mucho más rápidas. Selecciona **Sí** para la mejor experiencia con actualizaciones instantáneas. **Solicitud de actualizaciones delta (si Direct Update está habilitado):** ```plaintext 💡 Direct Update (instant updates) is enabled in your config Delta updates send only changed files instead of the full bundle ❓ Enable delta updates for this upload? (Recommended with Direct Update) ``` Caution Para monorepos, es posible que necesites proporcionar rutas adicionales a tu `package.json` y `node_modules`. **Éxito:** ```plaintext ✅ Update v{version} uploaded successfully! 🎉 Your updated bundle is now available on Capgo ``` ### Paso 12: Probar Actualización en Dispositivo [Section titled “Paso 12: Probar Actualización en Dispositivo”](#paso-12-probar-actualización-en-dispositivo) ¡Hora de ver la actualización en acción! ```plaintext 🧪 Time to test the Capgo update system! 📱 Go to your device where the app is running ``` **Para actualizaciones instantáneas:** ```plaintext 🔄 IMPORTANT: Background your app (swipe up/press home button) and then reopen it ⏱️ The update should be downloaded and applied automatically ``` **Para actualizaciones estándar:** ```plaintext 📱 With standard updates, you will need to: 1. Background the app (swipe up/press home button) to start download 2. Wait a few seconds for download to complete 3. Background and foreground again to see the update ``` **Monitorear registros:** ```plaintext ❓ Monitor Capgo logs to verify the update worked? ``` Si seleccionas **Sí**, verás registros en vivo desde tu dispositivo mostrando el proceso de actualización. ### Paso 13: Finalización [Section titled “Paso 13: Finalización”](#paso-13-finalización) ```plaintext Welcome onboard ✈️! ``` ¡Felicitaciones! Has configurado exitosamente las actualizaciones en vivo de Capgo para tu aplicación. ## Lo Que Has Logrado [Section titled “Lo Que Has Logrado”](#lo-que-has-logrado) Después de completar la incorporación, tienes: ✅ Aplicación Registrada Tu aplicación está registrada en Capgo con un canal de producción ✅ Plugin Instalado El plugin Capacitor Updater está instalado y configurado ✅ Código Integrado El código de integración está agregado a tu aplicación ✅ Actualización Probada Has implementado y recibido exitosamente una actualización en vivo ## Flujo de Trabajo Diario [Section titled “Flujo de Trabajo Diario”](#flujo-de-trabajo-diario) Para actualizaciones posteriores, usa: ```bash npm run build npx @capgo/cli@latest bundle upload --channel=production ``` Para más opciones de implementación, consulta [Implementar una Actualización en Vivo](/docs/getting-started/deploy/). ## Reanudar la Incorporación [Section titled “Reanudar la Incorporación”](#reanudar-la-incorporación) Si sales del proceso de incorporación, puedes reanudarlo en cualquier momento: ```bash npx @capgo/cli@latest init [APIKEY] ``` Verás: ```plaintext You have already got to the step {stepNumber}/13 in the previous session ❓ Would you like to continue from where you left off? ``` Tip El progreso se guarda localmente, por lo que puedes salir y reanudar el proceso de incorporación de forma segura. ## Solución de Problemas [Section titled “Solución de Problemas”](#solución-de-problemas) ### Sin Entorno de Desarrollo [Section titled “Sin Entorno de Desarrollo”](#sin-entorno-de-desarrollo) **Problema:** No se detecta ni Xcode ni Android SDK. **Solución:** * **Para iOS**: Instala [Xcode](https://developer.apple.com/xcode/) (solo macOS) * **Para Android**: Instala [Android Studio](https://developer.android.com/studio) ### ID de Aplicación Ya en Uso [Section titled “ID de Aplicación Ya en Uso”](#id-de-aplicación-ya-en-uso) **Problema:** Tu ID de aplicación ya está registrado. **Solución:** Elige una de las alternativas sugeridas o ingresa un ID de aplicación personalizado en notación de dominio inverso. ### Falta Script de Compilación [Section titled “Falta Script de Compilación”](#falta-script-de-compilación) **Problema:** No se encuentra script de compilación en `package.json`. **Solución:** Agrega un script de compilación a tu `package.json`: ```json { "scripts": { "build": "your-build-command" } } ``` ### Inyección Automática Falló [Section titled “Inyección Automática Falló”](#inyección-automática-falló) **Problema:** La CLI no puede inyectar automáticamente el código de integración. **Solución:** Agrega el código manualmente a tu archivo principal: ```typescript import { CapacitorUpdater } from '@capgo/capacitor-updater' CapacitorUpdater.notifyAppReady() ``` ### Versión de Capacitor Muy Antigua [Section titled “Versión de Capacitor Muy Antigua”](#versión-de-capacitor-muy-antigua) **Problema:** Tu versión de Capacitor está por debajo de v5. **Solución:** Actualiza Capacitor a v5 o superior: * [Actualizar a Capacitor 5](https://capacitorjs.com/docs/updating/5-0) * [Actualizar a Capacitor 6](https://capacitorjs.com/docs/updating/6-0) * [Actualizar a Capacitor 7](https://capacitorjs.com/docs/updating/7-0) ## Próximos Pasos [Section titled “Próximos Pasos”](#próximos-pasos) Ahora que has completado la incorporación, explora estos temas: [ Implementar Actualizaciones](/docs/getting-started/deploy/) [Aprende cómo implementar actualizaciones desde el panel de Capgo](/docs/getting-started/deploy/) [ Tipos de Actualización](/docs/live-updates/update-types/) [Referencia de todos los tipos de actualización OTA: momento de aplicación, condiciones de retraso, bloqueo de versión y entrega](/docs/live-updates/update-types/) [ Integración CI/CD](/docs/getting-started/cicd-integration/) [Automatiza tus implementaciones de actualización con CI/CD](/docs/getting-started/cicd-integration/) [ Canales](/docs/live-updates/channels/) [Gestiona múltiples flujos de actualización con canales](/docs/live-updates/channels/) [ Cifrado](/docs/live-updates/encryption/) [Asegura tus actualizaciones con cifrado de extremo a extremo](/docs/live-updates/encryption/) [ Comportamiento de Actualización](/docs/live-updates/update-behavior/) [Personaliza cuándo y cómo se aplican las actualizaciones (direct, delta, etc.)](/docs/live-updates/update-behavior/) ## Obtener Ayuda [Section titled “Obtener Ayuda”](#obtener-ayuda) Si encuentras problemas durante la incorporación: * Consulta la [Guía de Solución de Problemas](/docs/getting-started/troubleshooting/) * Únete a la [Comunidad Discord](https://discord.capgo.app) * Revisa las [Preguntas Frecuentes](/docs/faq/) * Contacta al [Soporte](/docs/getting-help/) # Descripción General > Comienza con Capgo aprendiendo los conceptos clave y los pasos para integrar e implementar actualizaciones en vivo en tu aplicación. El Tutorial de inicio rápido te guiará a través de los conceptos clave de Capgo. Los conceptos que se explorarán incluyen: 1. Agregar una aplicación a tu cuenta de Capgo 2. Integrar Capgo con tu CI/CD 3. Activar la carga de Paquetes en Capgo al enviar commits 4. Configurar y personalizar la publicación de Paquetes de Capgo 5. Configurar tu aplicación para habilitar actualizaciones en vivo a través de Capgo 6. Implementar actualizaciones en vivo en tu aplicación desde Capgo Simplemente sigue la guía paso a paso, o navega directamente a la documentación del componente que te interese. [ Comenzar el Tutorial](/docs/getting-started/Agregar-an-Aplicación/) [¡Sigue el Tutorial de inicio rápido y comienza a usar Capgo en poco tiempo!](/docs/getting-started/Agregar-an-Aplicación/) [ Guía de Incorporación CLI](/docs/getting-started/onboarding/) [Guía completa paso a paso para el proceso de incorporación CLI interactivo.](/docs/getting-started/onboarding/) [ Enviar actualizaciones](/docs/getting-started/Desplegar/) [Envía actualizaciones a tu aplicación desde el panel de Capgo.](/docs/getting-started/Desplegar/) [ Automatizar actualizaciones](/docs/getting-started/cicd-Integración/) [Integra Capgo con tu CI/CD y activa cargas de Paquetes en Capgo al enviar commits.](/docs/getting-started/cicd-Integración/) [ Solución de Problemas]() [Problemas comunes y cómo resolverlos.]() [ Conclusión](/docs/getting-started/wrapping-up/) [Finaliza el Tutorial y obtén una visión rápida de lo que has aprendido.](/docs/getting-started/wrapping-up/) Tip La función de actualización Over-the-Air (OTA) solo es aplicable para modificaciones realizadas en archivos HTML, CSS y JavaScript. Si realizas cambios en el código nativo, como actualizaciones de Plugins de Capacitor, es obligatorio volver a enviar la aplicación a la tienda de aplicaciones para su aprobación. ## Únete a la Comunidad de Discord [Section titled “Únete a la Comunidad de Discord”](#únete-a-la-comunidad-de-discord) [¡Únete al Servidor de Discord de Capgo!](https://discord.capgo.app) ## Mantenimiento [Section titled “Mantenimiento”](#mantenimiento) | Versión del Plugin | Compatibilidad con Capacitor | Mantenimiento | | ------------------ | ---------------------------- | --------------------------------------------------------------------- | | v7.\*.\* (≥7.25.0) | v7.\*.\* | ✅ Totalmente compatible | | v6.\*.\* (≥6.25.0) | v6.\*.\* | ✅ Totalmente compatible | | v5.\*.\* (≥5.10.0) | v5.\*.\* | ✅ Totalmente compatible | | v5.\*.\* (<5.10.0) | v5.\*.\* | ⚠️ Obsoleto | | v4.\*.\* | v4.\*.\* | ❌ Ya no es compatible | | v3.\*.\* | v3.\*.\* | ❌ Ya no es compatible | | >= 8 | v4.\*.\* | ⚠️ Obsoleto debido a problemas de versionado en nuestro proceso de CI | ## Cumplimiento de las Directrices de las Tiendas [Section titled “Cumplimiento de las Directrices de las Tiendas”](#cumplimiento-de-las-directrices-de-las-tiendas) Google Play de Android y Aplicación Store de iOS tienen directrices correspondientes con reglas que debes conocer antes de integrar la solución Capacitor-updater en tu aplicación. ### Google Play [Section titled “Google Play”](#google-play) El tercer párrafo del tema [Abuso de Dispositivos y Red](https://support.google.com/googleplay/android-developer/answer/9888379/?hl=en) describe que está restringida la actualización del código fuente por cualquier método que no sea el mecanismo de actualización de Google Play. Pero esta restricción no se aplica a la actualización de Paquetes JavaScript. > Esta restricción no se aplica al código que se ejecuta en una máquina virtual y tiene acceso limitado a las API de Android (como JavaScript en un webview o navegador). Esto permite completamente Capacitor-updater ya que solo actualiza los Paquetes JS y no actualizará código nativo. ### Aplicación Store [Section titled “Aplicación Store”](#aplicación-store) El párrafo **3.3.2**, desde 2015 en el [Acuerdo de Licencia del Programa de Desarrollador de Apple](https://developer.apple.com/programs/ios/information/) permite completamente realizar actualizaciones over-the-air de JavaScript y activos - y en su última versión (20170605) [descargable aquí](https://developer.apple.com/terms/) esta normativa es incluso más amplia: > El código interpretado puede descargarse en una Aplicación solo mientras dicho código: (a) no cambie el propósito principal de la Aplicación proporcionando características o funcionalidades que sean inconsistentes con el propósito previsto y anunciado de la Aplicación tal como se envió a la Aplicación Store, (b) no cree una tienda o punto de venta para otro código o aplicaciones, y (c) no eluda la firma, el sandbox u otras características de seguridad del SO. Capacitor Updater te permite seguir estas reglas en total cumplimiento siempre que la actualización que envíes no se desvíe significativamente de la intención original de tu producto aprobada por la Aplicación Store. Para mantener aún más el cumplimiento de las directrices de Apple, sugerimos que las aplicaciones distribuidas en la Aplicación Store no habiliten el escenario de `Actualización forzada`, ya que en las [Directrices de Revisión de la App Store](https://developer.apple.com/app-store/review/guidelines/) se establece que: > Las aplicaciones no deben forzar a los usuarios a calificar la aplicación, revisar la aplicación, descargar otras aplicaciones u otras acciones similares para acceder a la funcionalidad, contenido o uso de la aplicación. Esto no es un problema para el comportamiento predeterminado de actualización en segundo plano, ya que no forzará al usuario a aplicar la nueva versión hasta la próxima vez que cierre la aplicación, pero al menos debes ser consciente de ese rol si decides mostrarlo. ## Código abierto [Section titled “Código abierto”](#código-abierto) El Plugin está bajo la Licencia LGPL-3.0 y el back-end está bajo la Licencia AGPL-3.0. > 💡 LGPL-3.0 significa que si alguien modifica el código del Plugin, es obligatorio publicarlo, en código abierto con la misma licencia. Si usas el código sin modificación, eso no te concierne. Para más detalles, consulta el problema a continuación, revisa el enlace 👇 [¿Licenciamiento? ](https://GitHub.com/Cap-go/Capacitor-updater/Problemas/7) [Prueba GPTS Capgo para obtener ayuda en lugar de leer la documentación ](https://chat.openai.com/g/g-3dMwHbF2w-Capgo-doc-gpt) > Puedes incluirlo en tu aplicación sin preocupaciones ## Notas Finales [Section titled “Notas Finales”](#notas-finales) Si autohostas y encuentras útil esta herramienta, considera apoyar mi trabajo convirtiéndote en un [patrocinador de GitHub](https://github.com/sponsors/riderx/). Aposté por hacer código abierto todo el código que construí aquí en lugar de ponerlo detrás de un muro de pago. Al abrirlo en lugar de luchar y ocultarlo, creo que podemos hacer del mundo un lugar mejor. Para hacer esto posible, es necesario que todos hagamos nuestra parte, incluyéndote a ti 🥹. Si Capgo cloud no satisface tus necesidades, puedes respaldar a un creador independiente [aquí](https://github.com/sponsors/riderx/) en tus propios términos. ## Matemáticas Simples [Section titled “Matemáticas Simples”](#matemáticas-simples) El precio del plan básico: $14\*12 = $168 al año. Mientras que el promedio dev/hora = $60. Eso significa que 3 horas perdidas de tiempo de desarrollo en autohospedaje te permiten pagar un año completo, si gastas más de 3 horas estás perdiendo dinero ^^ # Solución de Problemas > Resuelve problemas comunes encontrados al usar Capgo con pasos detallados de solución de problemas y opciones avanzadas para carga y depuración. Aquí hay algunos problemas comunes que puedes encontrar mientras usas Capgo y cómo resolverlos. 🚀 ¿Necesitas Ayuda Experta? ¿Atascado con un problema complejo? ¡Nuestro equipo experto está aquí para ayudar! Obtén soporte personalizado, revisiones de código y soluciones personalizadas adaptadas a tus necesidades específicas. [Obtener Soporte Profesional](/consulting/) ### Fallos en la Carga [Section titled “Fallos en la Carga”](#fallos-en-la-carga) Si tu carga de Paquete falla, verifica dos veces: * Tu ID de aplicación en `capacitor.config.ts` coincide con tu aplicación en el panel de Capgo * Estás ejecutando el comando de carga desde la raíz de tu proyecto Capacitor * Tus activos web están compilados y actualizados #### Opciones avanzadas de carga [Section titled “Opciones avanzadas de carga”](#opciones-avanzadas-de-carga) La CLI de Capgo proporciona algunas banderas adicionales para ayudar con problemas comunes de carga: * `--tus`: Usa el [protocolo de carga reanudable tus](https://tus.io/) para cargas más confiables de Paquetes grandes o en conexiones de red deficientes. Si tu Paquete es mayor a 10MB o estás en una conexión irregular, considera usar `--tus`: ```shell npx @capgo/cli@latest bundle upload --tus ``` * `--package-json` y `--node-modules`: Indica a Capgo dónde encontrar tu `package.json` raíz y `node_modules` si tu aplicación usa una estructura no estándar como un monorepo o espacio de trabajo npm. Pasa la ruta al `package.json` raíz y la ruta `--node_modules`: ```shell npx @capgo/cli@latest bundle upload --package-json=path/to/package.json --node_modules=path/to/node_modules ``` Capgo necesita esta información para empaquetar correctamente las dependencias de tu aplicación. Puedes combinar estas banderas con otras opciones como `--channel` según sea necesario. Consulta los [documentos de CLI de Capgo](/docs/cli/overview/) para detalles completos sobre las opciones de carga disponibles. Si aún tienes problemas con las cargas, contacta al [soporte de Capgo](https://support.capgo.app) para obtener más asistencia. ### Depuración de Actualizaciones [Section titled “Depuración de Actualizaciones”](#depuración-de-actualizaciones) Si encuentras problemas con las actualizaciones en vivo, el comando de depuración de Capgo es una herramienta útil para solucionar problemas. Para usarlo: 1. Ejecuta el siguiente comando en tu directorio de proyecto: ```shell npx @capgo/cli@latest app debug ``` 2. Lanza tu aplicación en un dispositivo o emulador y realiza la acción que debería activar una actualización (por ejemplo, reabrir la aplicación después de subir un nuevo Paquete). 3. Observa la salida del comando de depuración. Registrará información sobre el proceso de actualización, incluyendo: * Cuándo la aplicación verifica una actualización * Si se encuentra una actualización y qué versión es * Progreso de descarga e instalación para la actualización * Cualquier Error que ocurra durante el proceso de actualización 4. Usa los registros de depuración para identificar dónde está ocurriendo el problema. Por ejemplo: * Si no se encuentra ninguna actualización, verifica dos veces que tu Paquete se subió exitosamente y que la aplicación está configurada para usar el canal correcto. * Si la actualización se descarga pero no se instala, asegúrate de haber llamado a `CapacitorUpdater.notifyAppReady()` y que la aplicación se cerró completamente y se volvió a abrir. * Si ves un mensaje de Error, busca ese Error específico en los documentos de Capgo o contacta al soporte para obtener ayuda. El comando de depuración es especialmente útil para identificar problemas con el proceso de descarga e instalación de actualizaciones. Si los registros muestran que se encontró la versión de actualización esperada pero no se aplicó finalmente, enfoca tu solución de problemas en los pasos después de la descarga. ### Depuración con Registros Nativos [Section titled “Depuración con Registros Nativos”](#depuración-con-registros-nativos) Además del comando de depuración de Capgo, los registros nativos en Android e iOS pueden proporcionar información valiosa para la solución de problemas, especialmente para problemas en el lado nativo del proceso de actualización. #### Registros de Android [Section titled “Registros de Android”](#registros-de-android) Para acceder a los registros de Android: 1. Conecta tu dispositivo o inicia tu emulador 2. Abre Android Studio y selecciona “View > Tool Windows > Logcat” 3. En la ventana Logcat, filtra los registros solo al proceso de tu aplicación seleccionándolo del menú desplegable en la parte superior 4. Busca cualquier línea que incluya `Capgo` para encontrar los registros del SDK Alternativamente, puedes usar el comando `adb logcat` y grep para `Capgo` para filtrar los registros. El SDK de Capgo registrará eventos clave durante el proceso de actualización, tales como: * Cuándo se inicia una verificación de actualización * Si se encuentra una actualización y qué versión es * Cuándo la descarga de actualización comienza y se completa * Cuándo se activa la instalación de actualización * Cualquier Error que ocurra durante los pasos de actualización nativos Problemas comunes específicos de Android que podrías ver en los registros incluyen: * Problemas de conectividad de red que impiden la descarga de la actualización * Errores de permisos de archivo al guardar o leer el Paquete de actualización * Falta de espacio de almacenamiento para el Paquete de actualización * Fallo al reiniciar la aplicación después de instalar la actualización #### Registros de iOS [Section titled “Registros de iOS”](#registros-de-ios) Para acceder a los registros de iOS: 1. Conecta tu dispositivo o inicia tu simulador 2. Abre Xcode y ve a “Window > Dispositivos and Simulators” 3. Selecciona tu dispositivo y haz clic en “Open Console” 4. En la salida de la consola, busca cualquier línea que incluya `Capgo` para encontrar los registros del SDK También puedes usar el comando `log stream` en la terminal y grep para `Capgo` para filtrar los registros. Similar a Android, el SDK de Capgo registrará eventos clave del lado de iOS: * Inicio y resultado de verificación de actualización * Inicio, progreso y finalización de descarga * Activación y resultado de instalación * Cualquier Error durante el proceso de actualización nativo Problemas específicos de iOS que podrías identificar en los registros incluyen: * Problemas de certificado SSL al descargar la actualización * Seguridad de transporte de aplicaciones bloqueando la descarga de actualización * Espacio de almacenamiento insuficiente para el Paquete de actualización * Fallo al extraer o aplicar correctamente el Paquete de actualización En ambas plataformas, los registros nativos proporcionan una vista de nivel inferior del proceso de actualización, con más detalles sobre la implementación nativa. Son especialmente útiles para identificar problemas que ocurren fuera de la capa JavaScript de Capgo. Al solucionar un problema complicado de actualización en vivo, es una buena idea capturar tanto los registros de depuración de Capgo como los registros nativos para obtener una imagen completa de lo que está sucediendo. Los dos registros juntos te darán la mejor oportunidad de identificar y resolver el problema. ### Actualizaciones que no se aplican [Section titled “Actualizaciones que no se aplican”](#actualizaciones-que-no-se-aplican) Si has subido un Paquete pero no ves los cambios en tu dispositivo: * Asegúrate de haber llamado a `CapacitorUpdater.notifyAppReady()` en el código de tu aplicación como se muestra en el [inicio rápido](/docs/getting-started/quickstart) * Verifica que tu dispositivo esté conectado a internet y que los registros de depuración de Capgo muestren que la actualización se descargó * Intenta cerrar completamente y volver a abrir la aplicación, ya que las actualizaciones solo se aplican en un lanzamiento nuevo * Busca cualquier Error en los registros nativos que pueda indicar un problema al aplicar la actualización Consulta la guía de [implementación de actualizaciones en vivo](/docs/getting-started/deploy) para más detalles sobre el proceso de actualización. Si aún estás atascado, usa el comando `npx @capgo/cli@latest app debug` y los registros nativos para obtener más visibilidad sobre lo que está sucediendo. ## Instalación del SDK [Section titled “Instalación del SDK”](#instalación-del-sdk) Si tienes problemas para instalar el SDK de Capgo, asegúrate de que: * Tu aplicación esté usando una versión compatible de Capacitor (4.0 o más reciente) * Hayas seguido los pasos del [inicio rápido](/docs/getting-started/quickstart) en orden, incluyendo sincronizar tu aplicación después de instalar el SDK ## Integración CI/CD [Section titled “Integración CI/CD”](#integración-cicd) Para problemas con la activación de cargas de Capgo desde tu pipeline de CI/CD: * Verifica dos veces que tu token de autenticación de Capgo esté configurado correctamente * Asegúrate de que estés ejecutando el comando de carga después de que tus activos web se hayan compilado * Verifica que el comando de carga esté usando el nombre de canal correcto para tu entorno objetivo Consulta los documentos de [integración CI/CD](/docs/getting-started/cicd-integration/) para más consejos de solución de problemas. También puedes usar el comando `npx @capgo/cli@latest app debug` para confirmar si tus actualizaciones activadas por CI/CD están siendo recibidas por la aplicación. # Conclusión > Concluye tu viaje con Capgo con una visión general concisa de los conceptos clave y próximos pasos, asegurando una base sólida para la exploración futura y el dominio. Ahora que has completado la guía de inicio rápido, debes tener una comprensión básica de los conceptos clave de Capgo. Los conceptos clave que has aprendido en esta guía son: 1. Añadir una aplicación a tu cuenta de Capgo 2. Integrar Capgo con tu pipeline de CI/CD 3. Activar cargas de Paquetes a Capgo en nuevos commits 4. Configurar tu aplicación para habilitar actualizaciones en vivo con el SDK de Capgo 5. Implementar actualizaciones en vivo en tu aplicación desde el panel de Capgo ¡Pero aún hay más que aprender sobre Capgo! Continúa explorando los documentos o revisa algunos de estos temas clave: [ Integración CI/CD](/docs/getting-started/cicd-Integración/) [¿Ya tienes un pipeline de CI/CD? Aprende cómo incorporar Capgo en tu flujo de trabajo existente.](/docs/getting-started/cicd-Integración/) [ Actualizaciones en Vivo](/docs/live-Actualizaciones/) [Profundiza en las características y mejores prácticas de actualizaciones en vivo de Capgo.](/docs/live-Actualizaciones/) [ Preguntas Frecuentes]() [Encuentra respuestas a preguntas comunes sobre Capgo.]() [ Solución de Problemas]() [Obtén ayuda con problemas comunes que pueden surgir al usar Capgo.]() # Cara Melakukan > Cómo usar Capgo, tutoriales, consejos y trucos [Cómo funciona la versión en Capgo ](https://capgo.app/blog/how-version-work-in-capgo/)Capgo.Aplicación [Cómo lanzar una versión mayor en Capgo ](https://capgo.app/blog/how-to-release-major-version-in-capgo/)Capgo.Aplicación [Cómo enviar una actualización específica a un usuario o grupo ](https://capgo.app/blog/how-to-send-specific-version-to-users/)Capgo.Aplicación ## CI / CD [Section titled “CI / CD”](#ci--cd) [Construcción y lanzamiento automático con GitHub Actions ](https://capgo.app/blog/how-version-work-in-capgo/)Capgo.Aplicación [Gestionar construcciones de desarrollo y producción con GitHub Actions ](https://capgo.app/blog/automatic-build-and-release-with-github-actions/)Capgo.Aplicación ## Contribuir [Section titled “Contribuir”](#contribuir) [Contribuir al código abierto de Capgo ](https://github.com/Cap-go/Capgo/blob/main/CONTRIBUTING.md)githubcom # Visión General > Descubre cómo las Actualizaciones en Vivo de Capgo permiten actualizaciones fluidas de Paquetes JavaScript, permitiéndote enviar cambios directamente a los usuarios sin demoras de la tienda de aplicaciones. Usa la función de Actualizaciones en Vivo de Capgo para actualizar los Paquetes JavaScript de tu aplicación de forma remota y en tiempo real. Envía actualizaciones JS directamente a tus usuarios sin pasar por el proceso de revisión de la tienda de aplicaciones para corregir errores al instante y lanzar nuevas funciones. Note Las Actualizaciones en Vivo están limitadas a cambios en el Paquete JavaScript. Si necesitas actualizar código nativo, como agregar o eliminar un Plugin o cambiar la configuración del proyecto nativo, necesitarás enviar una nueva compilación binaria nativa a las tiendas de aplicaciones. ## Cómo Funcionan las Actualizaciones en Vivo [Section titled “Cómo Funcionan las Actualizaciones en Vivo”](#cómo-funcionan-las-actualizaciones-en-vivo) El sistema de Actualizaciones en Vivo de Capgo tiene dos componentes clave: 1. El SDK de Capgo, que instalas en tu aplicación. El SDK verifica las actualizaciones disponibles y las descarga en segundo plano. 2. Canales, que te permiten dirigir actualizaciones a grupos específicos de usuarios. Puedes usar canales para gestionar diferentes pistas de lanzamiento, como `Production`, `Staging` y `Dev`. Cuando subes un nuevo Paquete JS a Capgo y lo asignas a un canal, el SDK de Capgo en aplicaciones configuradas para ese canal detectará la actualización y la descargará. La próxima vez que la aplicación se reinicie, se cargará el nuevo Paquete. ## Por Qué Importan los Registros de Capgo (vista de marketing) [Section titled “Por Qué Importan los Registros de Capgo (vista de marketing)”](#por-qué-importan-los-registros-de-capgo-vista-de-marketing) * **Radiografía instantánea de cada despliegue**: Las líneas de tiempo por dispositivo muestran verificaciones, descargas, instalaciones, bloqueos de políticas y reversiones, para que sepas exactamente qué sucedió—sin conjeturas ni debates de “funciona en mi teléfono”. * **Respuesta más rápida a incidentes**: Los códigos tipo alerta (por ejemplo, límites de tasa, fallas de checksum, pérdidas de notifyAppReady) aparecen antes de que los usuarios comiencen a inundar el soporte, permitiéndote enviar una corrección o reversión en minutos. * **Prueba de política de canal**: Los registros verifican que las barreras (bloquear mayores, deshabilitar emuladores/compilaciones de desarrollo, límites de plataforma) estén protegiendo activamente la producción. * **Protección de ingresos y reputación**: Ve cuándo las actualizaciones se detienen en redes deficientes o alcanzan límites del plan, para que puedas intervenir antes de que caigan conversiones, sesiones o reseñas. * **Fuente única de verdad**: Producto, QA y Soporte comparten la misma transmisión de registros en la nube—sin rebuscar en Xcode/Android Studio o enviar DM a ingenieros para registros nativos. ## Comenzar [Section titled “Comenzar”](#comenzar) Para empezar a usar Actualizaciones en Vivo, sigue estos pasos: 1. Completa el [Inicio Rápido de Capgo](/docs/getting-started/quickstart) para configurar tu aplicación en Capgo e instalar el SDK de Capgo. 2. En el código de tu aplicación, llama a `CapacitorUpdater.notifyAppReady()` después de que tu aplicación haya terminado de inicializarse. Esto le dice al SDK de Capgo que tu aplicación está lista para recibir actualizaciones. 3. Compila tu Paquete JS y súbelo a Capgo: ```shell npm run build npx @capgo/cli@latest bundle upload --channel=production ``` 4. Abre tu aplicación y espera a que se descargue la actualización. Puedes verificar el estado con: ```shell npx @capgo/cli@latest app debug ``` 5. Una vez que se descarga la actualización, cierra y vuelve a abrir tu aplicación para cargar el nuevo Paquete. Consulta la guía de [Implementación de Actualizaciones en Vivo](/docs/getting-started/deploy) para más detalles. ## Próximos Pasos [Section titled “Próximos Pasos”](#próximos-pasos) [ Tipos de Actualización](/docs/live-updates/update-types/) [Referencia de todos los tipos de actualización OTA: tiempo de aplicación, condiciones de retraso, bloqueo de versiones y entrega.](/docs/live-updates/update-types/) [ Canales](/docs/live-Actualizaciones/Canales/) [Aprende cómo usar canales para gestionar diferentes pistas de lanzamiento y dirigir actualizaciones a usuarios específicos.](/docs/live-Actualizaciones/Canales/) [ Reversiones](/docs/live-Actualizaciones/Reversiones/) [Descubre cómo revertir a una versión anterior del Paquete JS si una actualización causa problemas.](/docs/live-Actualizaciones/Reversiones/) [ Comportamiento de Actualización](/docs/live-Actualizaciones/Actualizar-behavior/) [Personaliza cómo y cuándo se descargan y aplican las actualizaciones en tu aplicación.](/docs/live-Actualizaciones/Actualizar-behavior/) [ Actualizaciones Rápidas](/docs/live-Actualizaciones/differentials/) [Aprende cómo usar actualizaciones rápidas para acelerar el proceso de actualización.](/docs/live-Actualizaciones/differentials/) # Cambios Incompatibles > Cómo manejar cambios incompatibles con canales versionados Esta documentación explica cómo manejar cambios incompatibles en tu aplicación usando canales versionados. Este enfoque te permite mantener diferentes versiones de tu aplicación mientras aseguras que los usuarios reciban actualizaciones compatibles. ## Escenario de Ejemplo [Section titled “Escenario de Ejemplo”](#escenario-de-ejemplo) Digamos que tienes: * Versión de aplicación 1.2.3 (versión antigua) - usa canal production * Versión de aplicación 2.0.0 (nueva versión con cambios incompatibles) - usa canal v2 * Actualización en vivo 1.2.4 (compatible con 1.2.3) * Actualización en vivo 2.0.1 (compatible con 2.0.0) ## Estrategia: Siempre Usa defaultChannel para Versiones Mayores [Section titled “Estrategia: Siempre Usa defaultChannel para Versiones Mayores”](#estrategia-siempre-usa-defaultchannel-para-versiones-mayores) **Enfoque recomendado:** Establece un `defaultChannel` para cada versión mayor. Esto asegura que siempre puedas enviar actualizaciones a grupos específicos de usuarios sin depender de asignación dinámica de canales. ```ts // Lanzamientos versión 1.x defaultChannel: 'v1' // Lanzamientos versión 2.x defaultChannel: 'v2' // Lanzamientos versión 3.x (futuro) defaultChannel: 'v3' ``` **Beneficios de este enfoque:** * **Siempre tienes control** sobre qué usuarios reciben actualizaciones * **No se necesita cambio dinámico de canal** en el código de tu aplicación * **Separación clara** entre diferentes versiones de aplicación * **Flexibilidad** para enviar actualizaciones a cualquier grupo de versión específico ## 1. Crear Canal para Nueva Versión [Section titled “1. Crear Canal para Nueva Versión”](#1-crear-canal-para-nueva-versión) ```bash # Crear canal para versión 2.x npx @capgo/cli channel create v2 ``` ## 2. Actualizar Configuración de Capacitor para Versión 2.0.0 [Section titled “2. Actualizar Configuración de Capacitor para Versión 2.0.0”](#2-actualizar-configuración-de-capacitor-para-versión-200) Actualiza tu configuración de Capacitor antes de compilar la versión 2.0.0 para la tienda de aplicaciones: capacitor.config.ts ```ts import { CapacitorConfig } from '@capacitor/cli'; const config: CapacitorConfig = { appId: 'com.example.app', appName: 'Example App', plugins: { CapacitorUpdater: { // ... otras opciones defaultChannel: 'v2' // Todos los usuarios de 2.0.0 usarán el canal v2 } } }; export default config; ``` **Para versión 1.x:** Si no estableciste un `defaultChannel` inicialmente, los usuarios de la versión 1.x están en el canal `production`. Para futuras versiones mayores, siempre establece un canal específico como `v3`, `v4`, etc. ## 3. Gestionar Ramas de Código Separadas [Section titled “3. Gestionar Ramas de Código Separadas”](#3-gestionar-ramas-de-código-separadas) Crea ramas Git separadas para mantener compatibilidad entre versiones de aplicación: ```bash # Crear y mantener una rama para actualizaciones de versión 1.x git checkout -b v1-maintenance git push origin v1-maintenance # Tu rama principal continúa con desarrollo de versión 2.x git checkout main ``` **Crítico:** Nunca envíes Paquetes JavaScript a aplicaciones antiguas que esperan código/APIs nativas que no tienen. Siempre compila actualizaciones desde la rama apropiada: * **rama v1-maintenance**: Para actualizaciones a aplicaciones 1.x (canal production) * **rama main**: Para actualizaciones a aplicaciones 2.x (canal v2) ## 4. Subir Paquetes a Canales Respectivos [Section titled “4. Subir Paquetes a Canales Respectivos”](#4-subir-paquetes-a-canales-respectivos) ```bash # Para actualizaciones 1.x: Compila desde rama v1-maintenance git checkout v1-maintenance # Haz tus cambios compatibles con 1.x aquí npx @capgo/cli bundle upload --channel production # Para actualizaciones 2.x: Compila desde rama main git checkout main # Haz tus cambios para 2.x aquí npx @capgo/cli bundle upload --channel v2 ``` ## 5. Habilitar Auto-Asignación [Section titled “5. Habilitar Auto-Asignación”](#5-habilitar-auto-asignación) ```bash # Permitir que las aplicaciones se auto-asignen al canal v2 npx @capgo/cli channel set v2 --self-assign ``` ## 6. Desplegar en la Tienda de Aplicaciones [Section titled “6. Desplegar en la Tienda de Aplicaciones”](#6-desplegar-en-la-tienda-de-aplicaciones) Compila y despliega la versión 2.0.0 en la tienda de aplicaciones. Todos los usuarios que descarguen esta versión (ya sean usuarios nuevos o usuarios existentes actualizando) automáticamente usarán el canal v2 porque está configurado en el Paquete de la aplicación. **¡No se necesitan cambios de código!** Como `defaultChannel: 'v2'` está incluido en la versión de la tienda de aplicaciones, todos los usuarios que descarguen la versión 2.0.0 usarán automáticamente el canal correcto. ## Escalar a Versiones Futuras [Section titled “Escalar a Versiones Futuras”](#escalar-a-versiones-futuras) Cuando lances la versión 3.0.0 con más cambios incompatibles: ```bash # Crear canal para versión 3.x npx @capgo/cli channel create v3 ``` ```ts // capacitor.config.ts para versión 3.0.0 const config: CapacitorConfig = { // ... plugins: { CapacitorUpdater: { defaultChannel: 'v3' // Usuarios de versión 3.x } } }; ``` Ahora puedes enviar actualizaciones a cualquier versión: * Canal `production` → Usuarios de versión 1.x * Canal `v2` → Usuarios de versión 2.x * Canal `v3` → Usuarios de versión 3.x ## 7. Limpieza (Después de la Migración) [Section titled “7. Limpieza (Después de la Migración)”](#7-limpieza-después-de-la-migración) Una vez que todos los usuarios hayan migrado a la versión 2.x (cuenta 3-4 meses): 1. Elimina `defaultChannel` de tu configuración de Capacitor 2. Elimina el canal v2: ```bash npx @capgo/cli channel delete v2 ``` 3. Elimina la rama v1-maintenance: ```bash git branch -d v1-maintenance git push origin --delete v1-maintenance ``` Este enfoque asegura que los usuarios solo reciban actualizaciones compatibles con su versión de aplicación Siempre prueba las actualizaciones a fondo en cada canal antes del despliegue Puedes eliminar con seguridad el canal v2 en Capgo incluso si algunos usuarios todavía tienen la anulación de canal. Automáticamente recibirán actualizaciones del canal production en su lugar. ## Mantener Actualizaciones de Versión 1.x [Section titled “Mantener Actualizaciones de Versión 1.x”](#mantener-actualizaciones-de-versión-1x) Para enviar actualizaciones compatibles con la versión 1.x: 1. Cambia a la rama v1-maintenance: ```bash git checkout v1-maintenance ``` 2. Haz tus cambios y confírmalos: ```bash # Haz cambios compatibles con 1.x git add . git commit -m "Fix para v1.x" git push origin v1-maintenance ``` 3. Compila y sube al canal production: ```bash npx @capgo/cli bundle upload --channel production ``` Mantén tu rama v1-maintenance actualizada con correcciones de errores que sean compatibles con la versión 1.x, pero nunca fusiones cambios incompatibles desde main # Canales > Aprende cómo gestionar y configurar canales de Actualización en Vivo en Capgo, permitiendo actualizaciones de aplicaciones sin problemas dirigiendo compilaciones específicas de Paquetes JS a dispositivos configurados para esos canales. Un canal de Actualización en Vivo apunta a una compilación específica del Paquete JS de tu aplicación que se compartirá con cualquier dispositivo configurado para escuchar ese canal para actualizaciones. Cuando [instalas el SDK de Actualizaciones en Vivo de Capgo](/docs/getting-started/quickstart/) en tu aplicación, cualquier binario nativo configurado para ese canal verificará las actualizaciones disponibles cada vez que se inicie la aplicación. Puedes cambiar la compilación a la que apunta un canal en cualquier momento y también puedes revertir a compilaciones anteriores si es necesario. ## Cómo un dispositivo elige un canal (precedencia) [Section titled “Cómo un dispositivo elige un canal (precedencia)”](#cómo-un-dispositivo-elige-un-canal-precedencia) Cuando un dispositivo verifica una actualización, Capgo decide qué canal usar en este orden estricto (prioridad más alta primero): 1. **Mapeo forzado de dispositivo (Panel)** – Ancla manualmente un ID de dispositivo específico a un canal. Úsalo para depuración urgente o pruebas controladas con un solo usuario real. Esto siempre gana. 2. **Anulación en la nube (por dispositivo) vía Panel o API** – Creado cuando cambias el canal del dispositivo en el panel o vía API. Úsalo para usuarios de QA cambiando entre canales de características / PR o para reproducir un problema de usuario. Reinstalar el binario no lo borra; eliminar la entrada del dispositivo sí lo hace. Cambio Instantáneo de Canal con setChannel() **A partir de la versión del plugin 5.34.0, 6.34.0, 7.34.0, o 8.0.0** (dependiendo de tu versión mayor), `setChannel()` funciona de manera diferente: contacta al backend para **validar** que el canal está permitido (verificando si la auto-asignación está habilitada para ese canal), luego almacena el canal **localmente en el dispositivo** como `defaultChannel`. Esto significa que el nuevo canal toma efecto **instantáneamente** para la próxima verificación de actualización—sin esperar replicación. Anteriormente, `setChannel()` guardaba la anulación del canal en la base de datos del backend (igual que los cambios del Panel o API). Los dispositivos tenían que esperar la replicación del backend (hasta 2 minutos) antes de que el nuevo canal fuera reconocido. El nuevo comportamiento solo lee del backend (para validación) y almacena localmente, haciendo los cambios de canal instantáneos. **Nota de seguridad:** Incluso si un canal se vuelve no permitido después de configurarse localmente, el backend aún validará el canal durante las verificaciones de actualización, asegurando que la seguridad se mantenga. **Importante:** Cuando los cambios de canal se realizan vía el Panel o API, todavía hay un retraso de replicación de hasta 2 minutos. Para cambio instantáneo de canal, usa `setChannel()` desde el código de tu aplicación—valida con el backend, luego establece el canal localmente para efecto inmediato. 3. **Config de Capacitor `defaultChannel` (predeterminado de compilación de prueba)** – Si está presente en `capacitor.config.*` y no existe forzado/anulación, la aplicación comienza en este canal (por ejemplo, `beta`, `qa`, `pr-123`). Destinado para TestFlight / compilaciones internas para que los evaluadores aterricen automáticamente en un canal de pre-lanzamiento. Las compilaciones de producción típicamente dejan esto sin configurar. 4. **Canal Predeterminado en la Nube (ruta principal \~99% de usuarios)** – Si marcas un canal predeterminado en el panel, todos los usuarios finales normales (sin forzado, sin anulación, sin config defaultChannel) se conectan aquí. Cámbialo para desplegar o revertir al instante—sin nuevo binario. Si tienes predeterminados específicos de plataforma (uno solo para iOS, uno solo para Android), cada dispositivo aterriza en el predeterminado que coincida con su plataforma. Dejar el predeterminado en la nube sin configurar está permitido; en ese caso, el dispositivo debe coincidir en los pasos 1–3 para recibir actualizaciones. Mejor práctica: * Trata 1–3 como capas de excepción / prueba; cuando establezcas un predeterminado en la nube, los usuarios reales deberían fluir hacia él. Si eliges no establecer uno, sé deliberado sobre cómo los usuarios se conectan (típicamente vía `defaultChannel` en la configuración o anulaciones por dispositivo). * Solo configura `defaultChannel` en binarios que explícitamente envíes a evaluadores. Dejarlo sin configurar mantiene la lógica de producción centralizada en el panel. * Usa `setChannel()` con moderación en producción—principalmente para QA o diagnósticos dirigidos. Si un canal está deshabilitado para la plataforma (interruptores iOS/Android) cuando de otro modo sería elegido, el proceso de selección lo omite y continúa en la lista. > Resumen: Forzado > Anulación > Config `defaultChannel` > Predeterminado en la Nube. ## Comportamiento del Canal Predeterminado [Section titled “Comportamiento del Canal Predeterminado”](#comportamiento-del-canal-predeterminado) Establecer un predeterminado en la nube es opcional, pero generalmente sirve como la ruta general para nuevos dispositivos. Sin uno, solo los dispositivos que coincidan en mapeos forzados, anulaciones o un `defaultChannel` en la configuración de Capacitor recibirán actualizaciones. Cuando elijas marcar predeterminados, ten en cuenta estos patrones: * **Predeterminado único (más común)** – Si el canal tiene tanto iOS como Android habilitados, se convierte en el único predeterminado; cualquier dispositivo sin anulaciones se conectará aquí. * **Predeterminados específicos de plataforma** – Si divides canales por plataforma (por ejemplo, `ios-production` con solo iOS habilitado y `android-production` con solo Android habilitado), marca cada uno como el predeterminado para su plataforma. Los dispositivos iOS van al predeterminado de iOS, los dispositivos Android van al predeterminado de Android. Recuerda que el predeterminado en la nube y `defaultChannel` en `capacitor.config.*` ocupan la misma capa de decisión. Si estableces un predeterminado en la nube, no necesitas duplicar el valor en tu configuración de Capacitor—deja `defaultChannel` vacío para compilaciones de producción. Reserva `defaultChannel` para binarios que intencionalmente envíes a evaluadores o QA cuando quieras que comiencen en un canal que no sea de producción incluso si el predeterminado en la nube es diferente. Puedes cambiar los predeterminados en cualquier momento en el panel. Cuando cambias un predeterminado, los nuevos dispositivos obedecen el nuevo enrutamiento inmediatamente y los dispositivos existentes siguen las reglas normales de precedencia la próxima vez que se registren. ## Configurar un Canal [Section titled “Configurar un Canal”](#configurar-un-canal) Durante la incorporación creas el primer canal (la mayoría de los equipos lo nombran “Production”), pero nada está bloqueado—puedes renombrar o eliminar cualquier canal en cualquier momento. Para agregar canales adicionales más tarde: 1. Ve a la sección “Canales” del panel de Capgo 2. Haz clic en el botón “New Canal” 3. Ingresa un nombre para el canal y haz clic en “Crear” Los nombres de los canales pueden ser lo que desees. Una estrategia común es hacer coincidir los canales con tus etapas de desarrollo, tales como: * `Development` - para probar actualizaciones en vivo en dispositivos locales o emuladores * `QA` - para que tu equipo de QA verifique las actualizaciones antes de un lanzamiento más amplio * `Staging` - para pruebas finales en un entorno similar a producción * `Production` - para la versión de tu aplicación que los usuarios finales reciben de las tiendas de aplicaciones ## Configurar el Canal en Tu Aplicación [Section titled “Configurar el Canal en Tu Aplicación”](#configurar-el-canal-en-tu-aplicación) Con tus canales creados, necesitas configurar tu aplicación para escuchar el canal apropiado. En este ejemplo, usaremos el canal `Development`. Abre tu archivo `capacitor.config.ts` (o `capacitor.config.json`). Bajo la sección `plugins`, opcionalmente establece `defaultChannel` para **compilaciones de prueba** (internas / QA). Para compilaciones de producción, prefiere omitirlo para que los dispositivos usen el Predeterminado en la Nube a menos que se anule explícitamente. ```ts import { CapacitorConfig } from '@capacitor/cli'; const config: CapacitorConfig = { plugins: { CapacitorUpdater: { // Para una compilación QA/TestFlight – los evaluadores comienzan en el canal Development automáticamente. defaultChannel: 'Development', // Las compilaciones de producción generalmente omiten esto para que los usuarios se conecten al canal Predeterminado en la Nube. }, }, }; ``` A continuación, compila tu aplicación web y ejecuta `npx cap sync` para copiar el archivo de configuración actualizado a tus proyectos iOS y Android. Si omites este paso de sincronización, tus proyectos nativos continuarán usando el canal para el que estaban previamente configurados. Caution Orden de selección de canal: Forzado > Anulación (`setChannel` / panel) > Config `defaultChannel` > Predeterminado en la Nube. Usa `defaultChannel` solo en compilaciones de prueba/internas; déjalo fuera para producción para que los usuarios sigan el Predeterminado en la Nube (cuando esté establecido) en lugar de duplicar el enrutamiento en la configuración nativa. Aún puedes forzar (anclar) un dispositivo o aplicar una anulación más tarde—esos superan inmediatamente el valor de configuración. > Los nombres de los canales distinguen entre mayúsculas y minúsculas. ## Opciones y Estrategias de Canales [Section titled “Opciones y Estrategias de Canales”](#opciones-y-estrategias-de-canales) Los canales tienen varias opciones que controlan quién puede recibir actualizaciones y cómo se entregan las actualizaciones. Las más importantes están a continuación. Puedes configurarlas desde la aplicación web, la CLI o la API Pública. * Canal predeterminado: Opcionalmente marca el canal o canales específicos de plataforma a los que se conectan los nuevos dispositivos. Ve “Comportamiento del Canal Predeterminado” para escenarios de enrutamiento. * Filtros de plataforma: Habilita o deshabilita la entrega a dispositivos `iOS` y/o `Android` por canal. * Deshabilitar degradación automática bajo nativo: Previene enviar una actualización cuando la versión de la aplicación nativa del dispositivo es más nueva que el Paquete del canal (por ejemplo, dispositivo en 1.2.3 mientras el canal tiene 1.2.2). * Permitir compilaciones de desarrollo: Permite actualizaciones a compilaciones de desarrollo (útil para pruebas). * Permitir dispositivos emuladores: Permite actualizaciones a emuladores/simuladores (útil para pruebas). * Permitir auto-asignación de dispositivo: Permite a la aplicación cambiar a este canal en tiempo de ejecución usando `setChannel`. Si está deshabilitado, `setChannel` fallará para este canal. ### Estrategias de Deshabilitar Actualización Automática [Section titled “Estrategias de Deshabilitar Actualización Automática”](#estrategias-de-deshabilitar-actualización-automática) Usa esto para restringir qué tipos de actualizaciones el canal entregará automáticamente. Opciones: * major: Bloquea actualizaciones entre versiones mayores (0.0.0 → 1.0.0). Actualizaciones menores y de parche aún permitidas. * minor: Bloquea actualizaciones entre versiones menores (por ejemplo, 1.1.0 → 1.2.0) y mayores. Actualizaciones de parche aún permitidas. Nota: no bloquea 0.1.0 → 1.1.0. * patch: Muy estricto. Permite solo versiones de parche crecientes dentro de la misma versión mayor y menor. Ejemplos: 0.0.311 → 0.0.314 ✅, 0.1.312 → 0.0.314 ❌, 1.0.312 → 0.0.314 ❌. * metadata: Requiere metadatos de versión mínima de actualización en cada Paquete. Configura vía CLI usando `--min-update-version` o `--auto-min-update-version`. Si falta, el canal se marca como mal configurado y las actualizaciones serán rechazadas hasta que se establezca. * none: Permite todas las actualizaciones según compatibilidad semver. Aprende más detalles y ejemplos en Estrategia de deshabilitar actualizaciones en /docs/CLI/Comandos/#Deshabilitar-Actualizaciones-strategy. Ejemplo (CLI): ```bash # Bloquear actualizaciones mayores en el canal Production npx @capgo/cli@latest channel set production com.example.app \ --disable-auto-update major # Permitir que los dispositivos se auto-asignen al canal Beta npx @capgo/cli@latest channel set beta com.example.app --self-assign ``` ## Asignar un Paquete a un Canal [Section titled “Asignar un Paquete a un Canal”](#asignar-un-paquete-a-un-canal) Para implementar una actualización en vivo, necesitas subir una nueva compilación de Paquete JS y asignarla a un canal. Puedes hacer esto en un solo paso con la CLI de Capgo: ```shell npx @capgo/cli@latest bundle upload --channel=Development ``` Esto subirá tus activos web compilados y establecerá el nuevo Paquete como la compilación activa para el canal `Development`. Cualquier aplicación configurada para escuchar ese canal recibirá la actualización la próxima vez que verifique una. También puedes asignar compilaciones a canales desde la sección “Paquetes” del panel de Capgo. Haz clic en el ícono de menú junto a una compilación y selecciona “Assign A Canal” para elegir el canal para esa compilación. ## Versionado de Paquetes y Canales [Section titled “Versionado de Paquetes y Canales”](#versionado-de-paquetes-y-canales) Es importante notar que los Paquetes en Capgo son globales para tu aplicación, no específicos de canales individuales. El mismo Paquete puede ser asignado a múltiples canales. Al versionar tus Paquetes, recomendamos usar versionado semántico [semver](https://semver.org/) con identificadores de pre-lanzamiento para compilaciones específicas de canales. Por ejemplo, un lanzamiento beta podría versionarse como `1.2.3-beta.1`. Este enfoque tiene varios beneficios: * Comunica claramente la relación entre compilaciones. `1.2.3-beta.1` es obviamente un pre-lanzamiento de `1.2.3`. * Permite reutilizar números de versión entre canales, reduciendo la confusión. * Habilita rutas de reversión claras. Si necesitas revertir desde `1.2.3`, sabes que `1.2.2` es la versión estable anterior. Aquí hay un ejemplo de cómo podrías alinear las versiones de tus Paquetes con una configuración típica de canales: * Canal `Development`: `1.2.3-dev.1`, `1.2.3-dev.2`, etc. * Canal `QA`: `1.2.3-qa.1`, `1.2.3-qa.2`, etc. * Canal `Staging`: `1.2.3-rc.1`, `1.2.3-rc.2`, etc. * Canal `Production`: `1.2.3`, `1.2.4`, etc. Usar semver con identificadores de pre-lanzamiento es un enfoque recomendado, pero no estrictamente requerido. La clave es encontrar un esquema de versionado que comunique claramente las relaciones entre tus compilaciones y se alinee con el proceso de desarrollo de tu equipo. ## Revertir una Actualización en Vivo [Section titled “Revertir una Actualización en Vivo”](#revertir-una-actualización-en-vivo) Si implementas una actualización en vivo que introduce un Error o necesita ser revertida, puedes fácilmente revertir a una compilación anterior. Desde la sección “Canales” del panel: 1. Haz clic en el nombre del canal que quieres revertir 2. Encuentra la compilación a la que quieres revertir y haz clic en el ícono de corona ![Revertir compilación](/select_bundle.webp) 3. Confirma la acción La compilación seleccionada se convertirá inmediatamente en la compilación activa para ese canal nuevamente. Las aplicaciones recibirán la versión revertida la próxima vez que verifiquen una actualización. ## Automatizar Implementaciones [Section titled “Automatizar Implementaciones”](#automatizar-implementaciones) Para flujos de trabajo más avanzados, puedes automatizar tus implementaciones de actualización en vivo como parte de tu pipeline de CI/CD. Al integrar Capgo en tu proceso de compilación, puedes subir automáticamente nuevos Paquetes y asignarlos a canales cada vez que hagas push a ciertas ramas o crees nuevos lanzamientos. Consulta los documentos de [Integración CI/CD](/docs/getting-started/cicd-integration/) para aprender más sobre automatizar actualizaciones en vivo de Capgo. ## Implementar en un Dispositivo [Section titled “Implementar en un Dispositivo”](#implementar-en-un-dispositivo) Ahora que entiendes los canales, estás listo para comenzar a implementar actualizaciones en vivo en dispositivos reales. El proceso básico es: 1. Instalar el SDK de Capgo en tu aplicación 2. Configurar la aplicación para escuchar tu canal deseado 3. Subir una compilación y asignarla a ese canal 4. ¡Lanzar la aplicación y esperar la actualización! Para un recorrido más detallado, consulta la guía de [Implementación de Actualizaciones en Vivo](/docs/getting-started/deploy/). ¡Felices actualizaciones! ## Uso Avanzado de Canales: Segmentación de Usuarios [Section titled “Uso Avanzado de Canales: Segmentación de Usuarios”](#uso-avanzado-de-canales-segmentación-de-usuarios) Los canales pueden usarse para más que solo etapas de desarrollo. Son una herramienta poderosa para la segmentación de usuarios, habilitando características como: * Banderas de características para diferentes niveles de usuario * Pruebas A/B * Despliegues graduales de características * Programas de pruebas beta Aprende cómo implementar estos casos de uso avanzados en nuestra guía: [Cómo Segmentar Usuarios por Plan y Canales para Banderas de Características y Pruebas A/B](/blog/how-to-segment-users-by-plan-and-channels/). # Usar Capgo en China > Aprende cómo configurar Capgo Actualizaciones en vivo para funcionar en China usando URLs OST regionales para rendimiento y confiabilidad óptimos. Si estás desplegando tu aplicación para usuarios en China, necesitarás configurar Capgo para usar URLs OST (Tecnología de Almacenamiento de Objetos) regionales para asegurar actualizaciones confiables y rápidas. ## ¿Por Qué Usar URLs Específicas de China? [Section titled “¿Por Qué Usar URLs Específicas de China?”](#por-qué-usar-urls-específicas-de-china) Debido a la infraestructura de red y regulaciones en China (el Gran Cortafuegos), las conexiones directas a servidores internacionales pueden ser lentas o poco confiables. Capgo proporciona URLs OST dedicadas con datos ubicados en Hong Kong para minimizar la latencia y asegurar que tus usuarios reciban actualizaciones lo más rápido y confiable posible. ## Configuración [Section titled “Configuración”](#configuración) Para configurar Capgo para China, necesitas establecer tres URLs específicas en tu archivo de configuración de Capacitor. Estas URLs apuntan a la infraestructura basada en Hong Kong de Capgo. 1. Abre tu archivo `capacitor.config.ts` 2. Agrega la siguiente configuración a la sección del Plugin `CapacitorUpdater`: ```typescript import { CapacitorConfig } from '@capacitor/cli'; const config: CapacitorConfig = { plugins: { CapacitorUpdater: { autoUpdate: true, updateUrl: 'https://updater.capgo.com.cn/updates', statsUrl: 'https://updater.capgo.com.cn/stats', channelUrl: 'https://updater.capgo.com.cn/channel_self', }, }, }; export default config; ``` 3. Reconstruye tu aplicación para aplicar los cambios: ```shell npm run build npx cap sync ``` ## Detalles de Configuración [Section titled “Detalles de Configuración”](#detalles-de-configuración) Esto es lo que hace cada URL: * **updateUrl**: `https://updater.capgo.com.cn/updates` - Usada para verificar y descargar actualizaciones disponibles para tu aplicación * **statsUrl**: `https://updater.capgo.com.cn/stats` - Usada para reportar analíticas y estadísticas de uso de vuelta a Capgo * **channelUrl**: `https://updater.capgo.com.cn/channel_self` - Usada para recuperar configuración de canal y determinar qué actualizaciones aplicar Tip Las tres URLs deben configurarse juntas para asegurar funcionalidad completa del actualizador de Capgo en China. ## Configuraciones Recomendadas para China [Section titled “Configuraciones Recomendadas para China”](#configuraciones-recomendadas-para-china) Debido a las limitaciones de rendimiento de red causadas por el Gran Cortafuegos de China, tenemos recomendaciones específicas para aplicaciones desplegadas en China continental: ### Deshabilitar Actualizaciones Directas [Section titled “Deshabilitar Actualizaciones Directas”](#deshabilitar-actualizaciones-directas) **Recomendamos encarecidamente deshabilitar `directUpdate`** para aplicaciones en China. La conectividad de red en China es menos eficiente que en otras regiones, y las actualizaciones directas (que se aplican inmediatamente) pueden llevar a una mala experiencia de usuario si las descargas se interrumpen o son lentas. En su lugar, usa el comportamiento de actualización predeterminado donde las actualizaciones se descargan en segundo plano y se aplican cuando la aplicación pasa a segundo plano o se reinicia. Esto proporciona una experiencia más confiable para tus usuarios. ```typescript const config: CapacitorConfig = { plugins: { CapacitorUpdater: { autoUpdate: true, directUpdate: false, // Recomendado para China updateUrl: 'https://updater.capgo.com.cn/updates', statsUrl: 'https://updater.capgo.com.cn/stats', channelUrl: 'https://updater.capgo.com.cn/channel_self', }, }, }; ``` Caution Aunque nuestra infraestructura basada en Hong Kong ayuda a minimizar la latencia y mejorar la confiabilidad, el rendimiento de red hacia China continental aún puede verse afectado por el Gran Cortafuegos. Deshabilitar `directUpdate` ayuda a asegurar que las actualizaciones se completen exitosamente sin interrumpir la experiencia del usuario. ## Ejemplo de Configuración Completa [Section titled “Ejemplo de Configuración Completa”](#ejemplo-de-configuración-completa) Aquí hay un ejemplo completo con configuraciones recomendadas para aplicaciones desplegadas en China: ```typescript import { CapacitorConfig } from '@capacitor/cli'; const config: CapacitorConfig = { appId: 'com.example.app', appName: 'My App', webDir: 'dist', plugins: { CapacitorUpdater: { autoUpdate: true, directUpdate: false, // Recomendado: deshabilitar para mejor confiabilidad en China updateUrl: 'https://updater.capgo.com.cn/updates', statsUrl: 'https://updater.capgo.com.cn/stats', channelUrl: 'https://updater.capgo.com.cn/channel_self', }, }, }; export default config; ``` ## Probar Tu Configuración [Section titled “Probar Tu Configuración”](#probar-tu-configuración) Después de configurar las URLs específicas de China, puedes verificar que las actualizaciones funcionen correctamente: 1. Sube un nuevo Paquete a Capgo: ```shell npx @capgo/cli@latest bundle upload --channel=production ``` 2. Instala tu aplicación en un dispositivo de prueba en China 3. Monitorea el proceso de actualización: ```shell npx @capgo/cli@latest app debug ``` 4. Verifica que las actualizaciones se estén descargando desde las URLs OST de China Note El comportamiento y tiempo de actualización permanecen igual que con la configuración estándar de Capgo. Consulta la documentación de [Comportamiento de Actualización](/docs/live-updates/update-behavior/) para detalles sobre cómo y cuándo se aplican las actualizaciones. ## Despliegue Multi-Región [Section titled “Despliegue Multi-Región”](#despliegue-multi-región) Si tu aplicación sirve a usuarios tanto dentro como fuera de China, puedes usar la configuración de dominio chino para todos los usuarios a nivel mundial. El dominio `updater.capgo.com.cn` se resuelve globalmente gracias a la infraestructura DNS de Alibaba, haciéndolo accesible tanto dentro de China como en todo el mundo. ### Usar Dominios Chinos Globalmente [Section titled “Usar Dominios Chinos Globalmente”](#usar-dominios-chinos-globalmente) Las URLs del dominio chino funcionan sin problemas para aplicaciones multi-región: ```typescript const config: CapacitorConfig = { plugins: { CapacitorUpdater: { autoUpdate: true, directUpdate: false, // Recomendado para usuarios de China updateUrl: 'https://updater.capgo.com.cn/updates', statsUrl: 'https://updater.capgo.com.cn/stats', channelUrl: 'https://updater.capgo.com.cn/channel_self', }, }, }; ``` Esta configuración única funcionará para: * Usuarios en China continental (usando infraestructura basada en Hong Kong) * Usuarios fuera de China (accediendo a la misma infraestructura vía DNS de Alibaba) **Consideraciones de Rendimiento:** Aunque el dominio `.cn` se resuelve globalmente a través del DNS de Alibaba y funciona en todas partes, es ligeramente menos eficiente para usuarios fuera de China comparado con el dominio estándar (`api.capgo.app`), que se resuelve directamente por Cloudflare donde está alojado nuestro backend. Sin embargo, la resolución DNS es rápida, por lo que la diferencia de rendimiento es mínima y no afectará significativamente la experiencia del usuario. Tip Usar el dominio `.cn` para todos los usuarios simplifica tu despliegue y asegura comportamiento de actualización consistente en todas las regiones. No necesitas compilaciones separadas o configuraciones basadas en entorno. El pequeño compromiso de rendimiento fuera de China típicamente vale la pena por el despliegue simplificado. ### Alternativa: Configuraciones Específicas por Región [Section titled “Alternativa: Configuraciones Específicas por Región”](#alternativa-configuraciones-específicas-por-región) Si prefieres optimizar de manera diferente para cada región, también puedes considerar: * Compilar variantes de aplicación separadas con diferentes configuraciones * Usar configuración basada en entorno para establecer dinámicamente las URLs * Crear diferentes canales de lanzamiento para diferentes regiones Si necesitas asistencia con estrategias de despliegue multi-región, por favor contáctanos en o únete a nuestra [comunidad Discord](https://discord.capgo.app) para obtener ayuda. ## Solución de Problemas [Section titled “Solución de Problemas”](#solución-de-problemas) Si experimentas problemas con actualizaciones en China: 1. **Verifica tu configuración** - Revisa que las tres URLs estén correctamente establecidas en tu `capacitor.config.ts` 2. **Verifica conectividad de red** - Asegúrate de que tu dispositivo pueda alcanzar el dominio `updater.capgo.com.cn` 3. **Revisa registros** - Usa `npx @capgo/cli@latest app debug` para verificar mensajes de Error 4. **Prueba actualizaciones** - Intenta subir un nuevo Paquete y monitorear el proceso de descarga 5. **Contacta soporte** - Si los problemas persisten, contáctanos en o únete a nuestra [comunidad Discord](https://discord.capgo.app) para asistencia Caution Asegúrate de usar el dominio `.cn` (`updater.capgo.com.cn`) y no el dominio internacional estándar al configurar para China. ## Próximos Pasos [Section titled “Próximos Pasos”](#próximos-pasos) * Aprende sobre [Comportamiento de Actualización](/docs/live-updates/update-behavior/) para personalizar cuándo se aplican las actualizaciones * Explora [Canales](/docs/live-updates/channels/) para gestionar diferentes pistas de lanzamiento * Revisa [Encriptación](/docs/live-updates/encryption/) para asegurar tus actualizaciones # Cumplimiento > Conozca las prácticas de privacidad de Capgo, la recopilación de datos, el cumplimiento de seguridad y cómo protegemos la información de sus usuarios durante las actualizaciones en vivo. Capgo está diseñado con la privacidad, la seguridad y el cumplimiento en mente. Este documento explica qué datos se recopilan, cómo se utilizan y qué medidas se implementan para proteger la privacidad de sus usuarios y garantizar el cumplimiento normativo al usar el servicio de actualización en vivo de Capgo. ## Descripción General de la Recopilación de Datos [Section titled “Descripción General de la Recopilación de Datos”](#descripción-general-de-la-recopilación-de-datos) Capgo recopila los datos mínimos necesarios para proporcionar el servicio de actualización en vivo de manera efectiva. La recopilación de datos se centra en los requisitos operativos en lugar del seguimiento de usuarios o análisis. ### Qué Datos se Recopilan [Section titled “Qué Datos se Recopilan”](#qué-datos-se-recopilan) Capgo recopila solo los datos necesarios para proporcionar la función de actualizaciones en vivo. Cuando su aplicación verifica actualizaciones o descarga nuevos paquetes, se recopila la siguiente información: * **ID de Aplicación**: Un identificador único para su aplicación que se utiliza para asociar la aplicación con la cuenta correcta * **Código de Versión de la Aplicación**: El código de versión de la aplicación que se utiliza para determinar qué actualizaciones son compatibles con la aplicación * **Nombre de Versión de la Aplicación**: El nombre de versión de la aplicación que se utiliza para propósitos de visualización * **Plataforma**: La plataforma (iOS, Android) de la aplicación que se utiliza para determinar qué actualizaciones son compatibles con la aplicación * **ID de Dispositivo**: Un identificador único para el dispositivo que se utiliza para entregar actualizaciones a un dispositivo específico y para propósitos de facturación. Este identificador es una cadena aleatoria que se crea cuando la aplicación se inicia por primera vez. **A partir de las versiones v5.10.0, v6.25.0 y v7.25.0 del Plugin**, el ID del dispositivo ahora persiste a través de las reinstalaciones de la aplicación (almacenado de forma segura en Keychain en iOS y EncryptedSharedPreferences en Android) para proporcionar un mejor seguimiento del dispositivo mientras se mantiene el cumplimiento con las directrices de las tiendas de aplicaciones. Antes de estas versiones, el ID del dispositivo se restablecía con cada instalación de la aplicación * **ID de Paquete**: El identificador único para el paquete que está actualmente instalado en el dispositivo * **Nombre del Canal**: El nombre del canal que está seleccionado para recibir actualizaciones * **Versión del SO**: La versión del sistema operativo que se utiliza para determinar qué actualizaciones son compatibles con el dispositivo * **Versión del Plugin**: La versión del Plugin @Capgo/Capacitor-updater que se utiliza para entregar actualizaciones al dispositivo **Datos Técnicos Adicionales:** * Marcas de tiempo de verificación de actualizaciones * Estado de éxito/fallo de descarga * Estado de instalación del paquete * Eventos de reversión y razones * Dirección IP (para geolocalización y optimización de CDN) Note Puede verificar los datos que se recopilan inspeccionando el código fuente del Plugin @Capgo/Capacitor-updater, que es de código abierto y está disponible en [GitHub](https://github.com/Cap-go/capacitor-updater). Capgo no recopila información de identificación personal (PII) como nombres, direcciones de correo electrónico, números de teléfono o identificadores de dispositivos persistentes que puedan usarse para rastrear usuarios individuales entre aplicaciones. ### Qué Datos NO se Recopilan [Section titled “Qué Datos NO se Recopilan”](#qué-datos-no-se-recopilan) Capgo explícitamente no recopila: * Información personal del usuario o credenciales * Análisis de uso de la aplicación o datos de comportamiento del usuario * Contenido de su aplicación o datos generados por el usuario * Datos de ubicación más allá de la región geográfica general * Identificadores persistentes de dispositivos para seguimiento * Datos biométricos o personales sensibles ## Uso y Propósito de los Datos [Section titled “Uso y Propósito de los Datos”](#uso-y-propósito-de-los-datos) Los datos recopilados por Capgo se utilizan exclusivamente para: ### Operación del Servicio [Section titled “Operación del Servicio”](#operación-del-servicio) * Determinar qué actualizaciones están disponibles para versiones específicas de la aplicación * Optimizar la entrega de contenido a través de la selección geográfica de CDN * Garantizar la compatibilidad entre actualizaciones y capacidades del dispositivo * Gestionar despliegues de actualizaciones y asignaciones de canales ### Mejora del Servicio [Section titled “Mejora del Servicio”](#mejora-del-servicio) * Monitorear tasas de éxito de actualizaciones e identificar problemas * Optimizar el rendimiento y la fiabilidad de las descargas * Mejorar el sistema general de entrega de actualizaciones * Depurar y solucionar fallos de actualización ### Seguridad e Integridad [Section titled “Seguridad e Integridad”](#seguridad-e-integridad) * Prevenir abusos y garantizar la disponibilidad del servicio * Validar la autenticidad e integridad de las actualizaciones * Proteger contra actualizaciones maliciosas o corruptas * Mantener la seguridad y estabilidad del servicio ## Almacenamiento y Retención de Datos [Section titled “Almacenamiento y Retención de Datos”](#almacenamiento-y-retención-de-datos) ### Ubicación de Almacenamiento [Section titled “Ubicación de Almacenamiento”](#ubicación-de-almacenamiento) * Los paquetes de actualización y metadatos se almacenan en infraestructura en la nube segura * Los datos se distribuyen en múltiples regiones geográficas para rendimiento * Toda la transmisión de datos está cifrada usando protocolos estándar de la industria (HTTPS/TLS) ### Retención de Datos [Section titled “Retención de Datos”](#retención-de-datos) * Los registros de verificación de actualizaciones se retienen para propósitos operativos (típicamente 30-90 días) * Los archivos de paquetes se retienen mientras estén asignados a canales activos * Las métricas agregadas y no personales pueden retenerse más tiempo para mejora del servicio * Los datos personales, si los hay, se eliminan según las leyes aplicables de protección de datos ### Seguridad de Datos [Section titled “Seguridad de Datos”](#seguridad-de-datos) * Todos los datos están cifrados en tránsito y en reposo * El acceso a los datos está restringido solo a personal autorizado * Se realizan auditorías de seguridad y monitoreo regulares * Se siguen prácticas de seguridad estándar de la industria * **Certificación SOC 2**: Capgo está actualmente certificado SOC 2 Type II, garantizando los más altos estándares de seguridad, disponibilidad y confidencialidad. Vea nuestro estado de cumplimiento en [trust.capgo.app](https://trust.capgo.app) * **Auditoría Continua de Código**: Cada commit es auditado automáticamente por [SonarCloud](https://sonarcloud.io/summary/overall?id=Cap-go_capacitor-updater\&branch=main) para el [plugin](https://sonarcloud.io/summary/overall?id=Cap-go_capgo\&branch=main) y el [backend](https://sonarcloud.io/summary/overall?id=Cap-go_capgo\&branch=main), asegurando la calidad del código, detección de vulnerabilidades de seguridad y mantenibilidad * **Escaneo de Vulnerabilidades**: Se realiza escaneo de seguridad adicional mediante [Snyk](https://snyk.io/test/github/Cap-go/capgo) para detectar y remediar vulnerabilidades de seguridad en las dependencias * **Seguridad de Infraestructura**: Nuestra infraestructura de alojamiento es monitoreada y verificada continuamente a través de [verificaciones de seguridad de hosting](https://hosting-checker.net/websites/api.capgo.app) * **Revisión de Código con IA**: Cada solicitud de extracción es revisada por CodeRabbit AI para detectar posibles problemas, preocupaciones de seguridad y mantener los estándares de calidad del código ## Controles de Privacidad [Section titled “Controles de Privacidad”](#controles-de-privacidad) ### Para Desarrolladores de Aplicaciones [Section titled “Para Desarrolladores de Aplicaciones”](#para-desarrolladores-de-aplicaciones) Como usuario de Capgo, usted tiene control sobre: * **Gestión de Canales**: Controle qué actualizaciones se distribuyen a qué usuarios * **Minimización de Datos**: Configurar qué información del dispositivo se comparte * **Controles Geográficos**: Gestione dónde se distribuyen sus actualizaciones * **Configuración de Retención**: Controle cuánto tiempo se retienen los datos de actualización ### Para Usuarios Finales [Section titled “Para Usuarios Finales”](#para-usuarios-finales) Los usuarios de su aplicación se benefician de: * **Recopilación Mínima de Datos**: Solo se recopilan datos esenciales para la entrega de actualizaciones * **Sin Seguimiento**: Sin seguimiento entre aplicaciones o seguimiento persistente de usuarios * **Transparencia**: Esta política de privacidad explica exactamente qué datos se recopilan * **Seguridad**: Toda la transmisión de datos está cifrada y es segura ## Cumplimiento y Legal [Section titled “Cumplimiento y Legal”](#cumplimiento-y-legal) ### Regulaciones de Protección de Datos [Section titled “Regulaciones de Protección de Datos”](#regulaciones-de-protección-de-datos) Capgo está diseñado para cumplir con las principales regulaciones de protección de datos, incluyendo: * **GDPR** (Reglamento General de Protección de Datos) * **CCPA** (Ley de Privacidad del Consumidor de California) * **COPPA** (Ley de Protección de la Privacidad en Línea de los Niños) * Otras leyes regionales de privacidad aplicables ### Cumplimiento de las Tiendas de Aplicaciones [Section titled “Cumplimiento de las Tiendas de Aplicaciones”](#cumplimiento-de-las-tiendas-de-aplicaciones) Capgo se adhiere estrictamente a las directrices y políticas de las tiendas de aplicaciones: * **Apple Aplicación Store**: Cumple con las [Directrices de Revisión de App Store](https://developer.apple.com/app-store/review/guidelines/) sección 3.3.2, asegurando que las actualizaciones en vivo solo modifican el comportamiento de la aplicación de formas que son consistentes con la aplicación enviada * **Google Play Store**: Sigue los requisitos de la [Política de Desarrolladores de Google Play](https://play.google.com/about/developer-content-policy/) para carga dinámica de código y actualizaciones de aplicaciones * **Restricciones de Contenido**: Las actualizaciones en vivo no pueden introducir funcionalidad que no estaba presente en el envío original de la aplicación o violar políticas de contenido específicas de la plataforma * **Requisitos de Seguridad**: Todas las actualizaciones mantienen la misma postura de seguridad y permisos que la aplicación original ### Sus Responsabilidades [Section titled “Sus Responsabilidades”](#sus-responsabilidades) Como desarrollador de aplicaciones que usa Capgo, usted debe: * Incluir divulgaciones de privacidad apropiadas en la política de privacidad de su aplicación * Informar a los usuarios sobre el uso de servicios de actualización en vivo * Garantizar el cumplimiento con las leyes aplicables en su jurisdicción * Implementar mecanismos de consentimiento apropiados si es necesario Tip **No se Requiere Implementación Adicional de Privacidad**: Capgo está diseñado para cumplir con la privacidad por defecto. No necesita implementar controles de privacidad adicionales ni mecanismos de manejo de datos en el código de su aplicación. El enfoque de recopilación mínima de datos y privacidad por diseño significa que integrar Capgo típicamente no requiere cambios en sus declaraciones o políticas de privacidad existentes. ## Privacidad por Diseño [Section titled “Privacidad por Diseño”](#privacidad-por-diseño) Capgo sigue los principios de privacidad por diseño: ### Minimización de Datos [Section titled “Minimización de Datos”](#minimización-de-datos) * Solo recopilar datos que sean absolutamente necesarios para la operación del servicio * Evitar recopilar información personal o sensible * Usar datos agregados y anonimizados cuando sea posible ### Limitación de Propósito [Section titled “Limitación de Propósito”](#limitación-de-propósito) * Usar los datos recopilados solo para los propósitos declarados * No reutilizar datos para actividades no relacionadas * Mantener límites claros sobre el uso de datos ### Transparencia [Section titled “Transparencia”](#transparencia) * Proporcionar información clara sobre la recopilación y uso de datos * Hacer que las prácticas de privacidad sean fácilmente accesibles y comprensibles * Actualizar regularmente la documentación de privacidad ## Contacto y Preguntas [Section titled “Contacto y Preguntas”](#contacto-y-preguntas) Si tiene preguntas sobre las prácticas de privacidad de Capgo o necesita reportar una preocupación de privacidad: * Revise nuestra Política de Privacidad completa en [capgo.app/privacy](https://capgo.app/privacy) * Vea nuestro estado de seguridad y cumplimiento en [capgo.app/trust](https://capgo.app/trust) * Contacte a nuestro equipo de privacidad a través de los canales de soporte * Reporte cualquier problema relacionado con la privacidad a través de nuestro contacto de seguridad Tip Recuerde actualizar la política de privacidad de su propia aplicación para reflejar el uso del servicio de actualización en vivo de Capgo y cualquier recopilación de datos que pueda ocurrir como parte del proceso de actualización. ## Mejores Prácticas para la Privacidad [Section titled “Mejores Prácticas para la Privacidad”](#mejores-prácticas-para-la-privacidad) Al implementar Capgo en su aplicación: 1. **Sea Transparente**: Informe a los usuarios sobre la funcionalidad de actualización en vivo 2. **Minimice los Datos**: Solo habilite las funciones de recopilación de datos que realmente necesita 3. **Implementación Segura**: Siga las mejores prácticas de seguridad en su integración 4. **Revisiones Regulares**: Revise periódicamente sus prácticas de privacidad y actualice las políticas 5. **Control del Usuario**: Considere proporcionar a los usuarios opciones para controlar el comportamiento de las actualizaciones Al seguir estas prácticas y comprender el enfoque de privacidad de Capgo, puede proporcionar a sus usuarios una experiencia de actualización en vivo segura y que respeta la privacidad. # Almacenamiento Personalizado > Aprenda cómo usar soluciones de almacenamiento personalizadas con Capgo Actualizaciones en vivo, incluyendo URLs externas, integración S3 y cifrado de paquetes para despliegues seguros. Capgo admite soluciones de almacenamiento personalizadas para sus paquetes de aplicación, lo que le permite alojar sus actualizaciones en su propia infraestructura o servicios de almacenamiento de terceros. Esto es particularmente útil para organizaciones con requisitos de seguridad específicos, necesidades de cumplimiento o infraestructura de almacenamiento existente. ## Descripción General [Section titled “Descripción General”](#descripción-general) El almacenamiento personalizado en Capgo funciona cargando su paquete en una ubicación externa y proporcionando a Capgo la URL para acceder a él. El SDK de Capgo descargará las actualizaciones directamente desde su ubicación de almacenamiento personalizada en lugar del almacenamiento en la nube predeterminado de Capgo. Tip El almacenamiento personalizado es ideal para: * Organizaciones con requisitos estrictos de residencia de datos * Equipos con infraestructura CDN o de almacenamiento existente * Aplicaciones que requieren capas de seguridad adicionales * Optimización de costos para tamaños de paquete grandes ## Carga de URL Externa [Section titled “Carga de URL Externa”](#carga-de-url-externa) La forma más simple de usar almacenamiento personalizado es cargando su paquete en cualquier URL públicamente accesible y proporcionando esa URL a Capgo. ### Carga Básica de URL Externa [Section titled “Carga Básica de URL Externa”](#carga-básica-de-url-externa) ```shell npx @capgo/cli@latest bundle upload --external https://your-domain.com/bundles/v1.2.3.zip ``` Este comando le dice a Capgo que haga referencia al paquete en la URL especificada en lugar de cargarlo al almacenamiento en la nube de Capgo. ### Con Cifrado [Section titled “Con Cifrado”](#con-cifrado) Para almacenamiento externo seguro, puede cifrar su paquete y proporcionar las claves de descifrado: ```shell npx @capgo/cli@latest bundle upload --external https://your-domain.com/bundles/v1.2.3.zip --iv-session-key YOUR_IV_SESSION_KEY ``` ## Integración S3 [Section titled “Integración S3”](#integración-s3) Capgo proporciona soporte integrado para Amazon S3 y servicios de almacenamiento compatibles con S3. El CLI puede cargar automáticamente su paquete a S3 y configurar Capgo para usar la URL de S3. ### Opciones de Carga S3 [Section titled “Opciones de Carga S3”](#opciones-de-carga-s3) ```shell npx @capgo/cli@latest bundle upload \ --s3-region us-east-1 \ --s3-apikey YOUR_ACCESS_KEY \ --s3-apisecret YOUR_SECRET_KEY \ --s3-bucket-name your-bucket-name ``` ### Configuración Completa de S3 [Section titled “Configuración Completa de S3”](#configuración-completa-de-s3) Para servicios compatibles con S3 o endpoints personalizados: ```shell npx @capgo/cli@latest bundle upload \ --s3-region us-east-1 \ --s3-apikey YOUR_ACCESS_KEY \ --s3-apisecret YOUR_SECRET_KEY \ --s3-endpoint https://s3.your-provider.com \ --s3-bucket-name your-bucket-name \ --s3-port 443 \ --no-s3-ssl # Solo si su endpoint no soporta SSL ``` ### Parámetros de Configuración S3 [Section titled “Parámetros de Configuración S3”](#parámetros-de-configuración-s3) | Parámetro | Descripción | Requerido | | ------------------ | -------------------------------- | --------- | | `--s3-region` | Región AWS para su bucket S3 | Sí | | `--s3-apikey` | ID de clave de acceso S3 | Sí | | `--s3-apisecret` | Clave de acceso secreta S3 | Sí | | `--s3-bucket-name` | Nombre de su bucket S3 | Sí | | `--s3-endpoint` | URL de endpoint S3 personalizado | No | | `--s3-port` | Puerto para endpoint S3 | No | | `--no-s3-ssl` | Deshabilitar SSL para carga S3 | No | ## Preparación y Cifrado de Paquetes [Section titled “Preparación y Cifrado de Paquetes”](#preparación-y-cifrado-de-paquetes) Al usar almacenamiento personalizado, especialmente con cifrado, necesita preparar sus paquetes correctamente. Esto implica crear un archivo zip y opcionalmente cifrarlo. ### Paso 1: Crear un Paquete Zip [Section titled “Paso 1: Crear un Paquete Zip”](#paso-1-crear-un-paquete-zip) Primero, cree un archivo zip de su paquete de aplicación: ```shell npx @capgo/cli@latest bundle zip com.example.app --path ./dist ``` El comando zip devolverá el checksum del archivo zip. Puede usar este checksum para cifrar el archivo zip si es necesario. Usar la opción `--json` para obtener salida estructurada incluyendo el checksum. #### Opciones del Comando Zip [Section titled “Opciones del Comando Zip”](#opciones-del-comando-zip) ```shell npx @capgo/cli@latest bundle zip [appId] \ --path ./dist \ --bundle 1.2.3 \ --name myapp-v1.2.3 \ --json \ --no-code-check \ --key-v2 \ --package-json ../../package.json,./package.json ``` | Opción | Descripción | | ----------------- | ------------------------------------------------------------------------- | | `--path` | Ruta a la carpeta a comprimir (por defecto es webDir de Capacitor.config) | | `--bundle` | Número de versión del paquete para nombrar el archivo zip | | `--name` | Nombre personalizado para el archivo zip | | `--json` | Salida de resultados en formato JSON (incluye checksum) | | `--no-code-check` | Omitir la verificación de llamada a notifyAppReady() y archivo index | | `--key-v2` | Usar cifrado v2 | | `--package-json` | Rutas a archivos Paquete.JSON para monorepos (separados por comas) | ### Paso 2: Cifrar el Paquete (Opcional) [Section titled “Paso 2: Cifrar el Paquete (Opcional)”](#paso-2-cifrar-el-paquete-opcional) Para mayor seguridad, cifre su paquete zip antes de cargarlo: ```shell # Usando clave local predeterminada npx @capgo/cli@latest bundle encrypt ./myapp.zip CHECKSUM # Usando archivo de clave personalizado npx @capgo/cli@latest bundle encrypt ./myapp.zip CHECKSUM --key ./path/to/.capgo_key_v2 # Usando datos de clave directamente npx @capgo/cli@latest bundle encrypt ./myapp.zip CHECKSUM --key-data "PRIVATE_KEY_CONTENT" ``` El parámetro `CHECKSUM` es requerido y debe ser el checksum de su archivo zip. Puede obtener el checksum de la salida del comando zip (Usar la opción `--json` para salida estructurada). Por defecto, el comando encrypt usará su clave de firma privada local. Puede especificar una clave personalizada usando las opciones `--key` o `--key-data`. El comando encrypt devolverá el `ivSessionKey` necesario para la carga o descifrado. #### Opciones del Comando de Cifrado [Section titled “Opciones del Comando de Cifrado”](#opciones-del-comando-de-cifrado) | Opción | Descripción | | ------------ | ----------------------------------------------------------------------------------------- | | `zipPath` | Ruta al archivo zip a cifrar (requerido) | | `checksum` | Checksum del archivo zip (requerido) - obténgalo del comando zip | | `--key` | Ruta personalizada para la clave de firma privada (opcional, usa clave local por defecto) | | `--key-data` | Datos de clave de firma privada directamente (opcional) | | `--json` | Salida de resultados en formato JSON | Caution El comando encrypt generará un `ivSessionKey` que necesitará proporcionar al cargar con la opción `--iv-session-key`. ## Ejemplos de Flujo de Trabajo Completo [Section titled “Ejemplos de Flujo de Trabajo Completo”](#ejemplos-de-flujo-de-trabajo-completo) ### Ejemplo 1: URL Externa con Cifrado [Section titled “Ejemplo 1: URL Externa con Cifrado”](#ejemplo-1-url-externa-con-cifrado) 1. **Compile su aplicación:** ```shell npm run build ``` 2. **Cree un paquete zip:** ```shell npx @capgo/cli@latest bundle zip com.example.app --path ./dist --bundle 1.2.3 ``` Anote el checksum devuelto por este comando. 3. **Cifre el paquete:** ```shell npx @capgo/cli@latest bundle encrypt ./com.example.app-1.2.3.zip CHECKSUM_FROM_STEP_2 ``` Anote el `ivSessionKey` de la salida. 4. **Cargue a su almacenamiento:** Cargue el archivo zip cifrado a su servicio de alojamiento. 5. **Registre con Capgo:** ```shell npx @capgo/cli@latest bundle upload \ --external https://your-cdn.com/bundles/com.example.app-1.2.3.zip \ --iv-session-key IV_SESSION_KEY_FROM_STEP_3 ``` ### Ejemplo 2: Carga Directa a S3 [Section titled “Ejemplo 2: Carga Directa a S3”](#ejemplo-2-carga-directa-a-s3) 1. **Compile su aplicación:** ```shell npm run build ``` 2. **Cargue directamente a S3:** ```shell npx @capgo/cli@latest bundle upload \ --s3-region us-west-2 \ --s3-apikey YOUR_ACCESS_KEY \ --s3-apisecret YOUR_SECRET_KEY \ --s3-bucket-name your-app-bundles \ --channel Production ``` ### Ejemplo 3: S3 con Cifrado [Section titled “Ejemplo 3: S3 con Cifrado”](#ejemplo-3-s3-con-cifrado) 1. **Compile y comprima:** ```shell npm run build npx @capgo/cli@latest bundle zip com.example.app --path ./dist --key-v2 ``` 2. **Cifre el paquete:** ```shell npx @capgo/cli@latest bundle encrypt ./com.example.app.zip CHECKSUM ``` 3. **Cargue a S3 con cifrado:** ```shell npx @capgo/cli@latest bundle upload \ --s3-region us-west-2 \ --s3-apikey YOUR_ACCESS_KEY \ --s3-apisecret YOUR_SECRET_KEY \ --s3-bucket-name your-app-bundles \ --iv-session-key IV_SESSION_KEY_FROM_STEP_2 \ --channel Production ``` ## Consideraciones de Seguridad [Section titled “Consideraciones de Seguridad”](#consideraciones-de-seguridad) Al usar almacenamiento personalizado, considere estas mejores prácticas de seguridad: ### Control de Acceso [Section titled “Control de Acceso”](#control-de-acceso) * Asegúrese de que sus URLs de almacenamiento sean accesibles para los usuarios de su aplicación pero no públicamente descubribles * Usar URLs firmadas o autenticación basada en tokens cuando sea posible * Implemente encabezados CORS apropiados para aplicaciones basadas en web ### Cifrado [Section titled “Cifrado”](#cifrado) * Siempre cifre paquetes sensibles usando las herramientas de cifrado de Capgo * Almacene las claves de cifrado de forma segura y rótelas regularmente * Usar HTTPS para todas las URLs de paquetes (requerido para iOS y Android) ### Monitoreo [Section titled “Monitoreo”](#monitoreo) * Monitoree los registros de acceso para detectar patrones de descarga inusuales * Configurar alertas para descargas de paquetes fallidas * Audite regularmente los permisos de su almacenamiento ## Solución de Problemas [Section titled “Solución de Problemas”](#solución-de-problemas) ### Problemas Comunes [Section titled “Problemas Comunes”](#problemas-comunes) **El paquete no se descarga:** * Verifique que la URL sea públicamente accesible y Usar HTTPS (requerido para iOS y Android) * Verifique los encabezados CORS para aplicaciones web * Asegúrese de que el formato del paquete sea correcto **Errores de cifrado:** * Verifique que el `ivSessionKey` coincida con el paquete cifrado * Verifique que el paquete fue cifrado con la clave correcta * Asegúrese de que se Usar el cifrado v2 para nuevos paquetes **Fallos de carga S3:** * Verifique sus credenciales y permisos de S3 * Verifique las políticas de bucket y la configuración de CORS * Asegúrese de que la región especificada sea correcta ### Comandos de Depuración [Section titled “Comandos de Depuración”](#comandos-de-depuración) Verifique el estado del paquete: ```shell npx @capgo/cli@latest app debug ``` Verifique la integridad del paquete: ```shell npx @capgo/cli@latest bundle list ``` ## Próximos Pasos [Section titled “Próximos Pasos”](#próximos-pasos) * Conozca sobre [Canales](/docs/live-updates/channels/) para gestionar diferentes entornos de despliegue * Explore [Comportamiento de Actualización](/docs/live-updates/update-behavior/) para personalizar cómo se aplican las actualizaciones * Configurar [Integración CI/CD](/docs/getting-started/cicd-integration/) para automatizar su flujo de trabajo de almacenamiento personalizado # Actualizaciones Delta > Aprende cómo las actualizaciones diferenciales de Capgo optimizan la transferencia de datos enviando solo archivos modificados, mejorando el rendimiento en redes más lentas. El sistema de Actualización en Vivo de Capgo puede entregar actualizaciones más rápido y eficientemente enviando solo los archivos modificados, en lugar del Paquete JS completo. Esto es especialmente beneficioso para usuarios en conexiones de red más lentas o con datos limitados, ya que minimiza la cantidad de datos que necesitan descargarse. Un segundo beneficio es cuando la aplicación tiene activos grandes que cambian raramente, como imágenes o videos, en comparación con archivos JS comprimidos se descargarán solo una vez. ## Cómo Funcionan las Actualizaciones Diferenciales [Section titled “Cómo Funcionan las Actualizaciones Diferenciales”](#cómo-funcionan-las-actualizaciones-diferenciales) Las actualizaciones diferenciales en Capgo son manejadas por el Plugin de Capgo instalado en tu aplicación. Cuando subes una nueva versión de tu aplicación usando la bandera `--partial`, Capgo hace lo siguiente: 1. Cada archivo en tu compilación se sube individualmente 2. Se generan checksums para cada archivo 3. Se crea un nuevo manifiesto JSON, listando todos los archivos y sus checksums 4. Este manifiesto se sube a la base de datos de Capgo Cuando un dispositivo ejecutando tu aplicación verifica una actualización, el Plugin de Capgo recibe el nuevo manifiesto del servidor. Lo compara con el manifiesto que tiene actualmente, identificando qué archivos han cambiado basándose en los checksums y la ruta de archivos. El Plugin luego descarga solo los archivos modificados, en lugar del Paquete JS completo. Reconstruye la nueva versión de la aplicación combinando estos archivos descargados con los archivos sin cambios que ya tiene. Manifiesto En caso de actualizaciones diferenciales, el dispositivo almacena todos los archivos descargados en un caché común, Capgo nunca lo limpiará pero el SO puede hacerlo en cualquier momento. ## Habilitar Actualizaciones Diferenciales [Section titled “Habilitar Actualizaciones Diferenciales”](#habilitar-actualizaciones-diferenciales) Para habilitar actualizaciones diferenciales para tu aplicación Capgo, simplemente usa la bandera `--partial` al subir una nueva versión: ## Forzar Actualizaciones Diferenciales [Section titled “Forzar Actualizaciones Diferenciales”](#forzar-actualizaciones-diferenciales) Si quieres asegurar que todas las cargas sean actualizaciones diferenciales y prevenir cargas accidentales de Paquetes completos, puedes usar la bandera `--partial-only`: ```shell npx @capgo/cli@latest bundle upload --partial-only ``` Cuando se usa `--partial-only`, Capgo solo subirá archivos individuales y generará un manifiesto. Cualquier dispositivo que no soporte parcial no podrá descargar la actualización. Podrías querer usar `--partial-only` si: * Siempre quieres usar actualizaciones diferenciales y nunca quieres permitir cargas de Paquetes completos * Estás configurando un pipeline de CI/CD y quieres asegurar que todas las cargas automatizadas sean diferenciales * Tu aplicación es grande y el ancho de banda está limitado, por lo que necesitas minimizar los tamaños de carga/descarga Si necesitas hacer una carga de Paquete completo mientras `--partial-only` está establecido, simplemente ejecuta el comando de carga sin `--partial-only`. Esto anulará la configuración para esa única carga, permitiéndote enviar un Paquete completo cuando sea necesario. ## Solución de Problemas [Section titled “Solución de Problemas”](#solución-de-problemas) Si las actualizaciones diferenciales no parecen estar funcionando (es decir, los dispositivos siempre están descargando el Paquete JS completo incluso para pequeños cambios), verifica dos veces que: * Estés usando la bandera `--partial` cada vez que subes una nueva versión * Si usas `--partial-only`, asegúrate de no haber omitido accidentalmente la bandera `--partial` * Tu dispositivo esté ejecutando la última versión del Plugin de Capgo * Tu dispositivo tenga una conexión de red estable y pueda alcanzar los servidores de Capgo También puedes usar la aplicación web de Capgo para verificar los detalles de tu última carga: 1. Ve a la [aplicación web](https://app.capgo.io) 2. Haz clic en tu aplicación 3. Haz clic en el número de Paquetes de la barra de estadísticas. 4. Selecciona el último Paquete 5. Verifica el campo `Partial` ![tipo de bundle](/bundle_type.webp) Si continúas teniendo problemas, contacta al soporte de Capgo para más asistencia. Pueden verificar los registros del servidor para confirmar que tus cargas parciales se están procesando correctamente y que los dispositivos están recibiendo los manifiestos actualizados. ¡Eso es todo! La bandera `--partial` le dice a Capgo que realice las cargas de archivos individuales y la generación de manifiesto necesaria para actualizaciones diferenciales. Ten en cuenta que necesitas usar `--partial` cada vez que subes una nueva versión que quieres que se entregue como una actualización diferencial. Si omites la bandera, Capgo subirá el Paquete JS completo como un solo archivo, y los dispositivos descargarán todo el Paquete incluso si solo una pequeña parte ha cambiado. # Cifrado > Aprenda cómo el cifrado de extremo a extremo de Capgo asegura sus paquetes de aplicación durante la transmisión y almacenamiento, protegiendo su código y datos de usuario. Capgo proporciona un cifrado de extremo a extremo robusto para sus paquetes de aplicación, asegurando que su código JavaScript y activos estén protegidos durante la transmisión y almacenamiento. Este sistema de cifrado está diseñado para darle control completo sobre la seguridad de su aplicación mientras mantiene la conveniencia de las actualizaciones en vivo. ## Descripción General [Section titled “Descripción General”](#descripción-general) El sistema de cifrado de Capgo utiliza métodos criptográficos estándar de la industria para proteger sus paquetes del acceso no autorizado. Cuando el cifrado está habilitado, sus paquetes se cifran antes de salir de su entorno de desarrollo y permanecen cifrados hasta que son descifrados por su aplicación en el dispositivo del usuario. **Cifrado Verdadero de Extremo a Extremo**: A diferencia de otras plataformas de actualización OTA que solo firman actualizaciones (dejando el código públicamente legible), Capgo proporciona cifrado verdadero de extremo a extremo. Esto significa que solo sus usuarios pueden descifrar sus actualizaciones - nadie más, incluido Capgo mismo. El contenido de su paquete permanece completamente privado e ilegible durante todo el proceso de entrega. Tip El cifrado es particularmente importante para: * Aplicaciones que manejan datos sensibles o lógica de negocio * Aplicaciones empresariales con requisitos de cumplimiento * Aplicaciones desplegadas en industrias reguladas * Organizaciones con políticas de seguridad estrictas ## Cómo Funciona el Cifrado [Section titled “Cómo Funciona el Cifrado”](#cómo-funciona-el-cifrado) Capgo utiliza un enfoque de cifrado híbrido que combina cifrado RSA y AES para seguridad y rendimiento óptimos: ![Flujo de Cifrado de Capgo](/encryption_flow.webp) ### 1. Generación de Claves [Section titled “1. Generación de Claves”](#1-generación-de-claves) * **Clave Privada**: Generada y almacenada de forma segura en su entorno de desarrollo (usada para cifrado) * **Clave Pública**: Derivada de su clave privada y almacenada en la configuración de Capacitor de su aplicación (usada para descifrado) * **Claves de Sesión**: Claves AES aleatorias generadas para cada carga de paquete ### 2. Proceso de Cifrado [Section titled “2. Proceso de Cifrado”](#2-proceso-de-cifrado) 1. Se genera una clave de sesión AES aleatoria para cada carga de paquete 2. Su paquete se cifra usando la clave de sesión AES 3. Se calcula el checksum del paquete 4. Tanto la clave de sesión AES como el checksum se cifran juntos usando su clave privada RSA (creando la “firma”) 5. Se almacenan el paquete cifrado y la firma cifrada El checksum se cifra junto con la clave AES para prevenir manipulación. Dado que solo su clave privada RSA puede crear esta firma, y solo la clave pública correspondiente puede descifrarla, esto asegura que tanto la clave de sesión AES como el checksum esperado son auténticos y no han sido modificados por un atacante. ### 3. Proceso de Descifrado [Section titled “3. Proceso de Descifrado”](#3-proceso-de-descifrado) 1. Su aplicación descarga el paquete cifrado y la firma cifrada 2. El SDK de Capgo usa su clave pública RSA (almacenada en la aplicación) para descifrar la firma 3. Esto revela la clave de sesión AES y el checksum original 4. La clave de sesión AES se usa para descifrar el paquete 5. Se calcula un checksum del paquete descifrado y se compara con el checksum original para verificación de integridad Este proceso asegura que incluso si un atacante intercepta el paquete cifrado, no puede modificar la clave de sesión AES ni proporcionar un checksum falso, porque necesitaría su clave privada para crear una firma válida que la clave pública pueda descifrar. Tip RSA no puede cifrar grandes cantidades de datos eficientemente, por lo que AES se usa para el cifrado del paquete real mientras que RSA asegura la clave AES y proporciona verificación de integridad a través de la firma del checksum. ## Capgo vs Otras Plataformas [Section titled “Capgo vs Otras Plataformas”](#capgo-vs-otras-plataformas) | Característica | Capgo | Otras Plataformas OTA | | ------------------------- | --------------------------------------------------------- | --------------------------------------- | | **Contenido del Paquete** | Completamente cifrado (ilegible) | Públicamente legible | | **Método de Seguridad** | Cifrado verdadero de extremo a extremo | Solo firma de código | | **Nivel de Privacidad** | Conocimiento cero (incluso Capgo no puede leer su código) | La plataforma puede acceder a su código | | **Protección** | Contenido + integridad + autenticidad | Solo integridad + autenticidad | **Por Qué Esto Importa:** * **La firma de código** solo verifica que las actualizaciones no han sido manipuladas y provienen de la fuente correcta * **El cifrado de extremo a extremo** asegura que el contenido real de su código permanezca privado e ilegible durante la transmisión y almacenamiento * Con el cifrado verdadero de extremo a extremo de Capgo, solo sus usuarios pueden descifrar actualizaciones - nadie más, incluido Capgo mismo ## Métodos de Cifrado [Section titled “Métodos de Cifrado”](#métodos-de-cifrado) Capgo usa Cifrado V2 como el método de cifrado estándar: ### Cifrado V2 (Estándar Actual) [Section titled “Cifrado V2 (Estándar Actual)”](#cifrado-v2-estándar-actual) * Usa RSA-4096 para seguridad mejorada * AES-256-GCM para cifrado autenticado * Proporciona verificación de integridad * Mejor rendimiento y seguridad ### Cifrado V1 (Obsoleto) [Section titled “Cifrado V1 (Obsoleto)”](#cifrado-v1-obsoleto) * Usa RSA-2048 para cifrado de claves * AES-256-CBC para cifrado de paquetes * **Ya no está disponible en el CLI actual** * Las aplicaciones heredadas que usan V1 deben migrar a V2 Danger El Cifrado V1 ya no es compatible con el CLI de Capgo actual. Si está usando cifrado V1, debe migrar a V2. Consulte la [guía de migración](/docs/upgrade/encryption-v1-to-v2/) para instrucciones detalladas. ## Configuración del Cifrado [Section titled “Configuración del Cifrado”](#configuración-del-cifrado) ### Paso 1: Generar Claves de Cifrado [Section titled “Paso 1: Generar Claves de Cifrado”](#paso-1-generar-claves-de-cifrado) Primero, genere sus claves de cifrado usando el CLI de Capgo: ```shell # Generar nuevas claves de cifrado (crea archivos en el directorio actual) npx @capgo/cli@latest key create ``` Esto crea: * `.capgo_key_v2`: Su clave privada (¡manténgala segura!) * `.capgo_key_v2.pub`: Su clave pública (usada por su aplicación) Estos archivos se crean en el directorio actual donde ejecuta el comando. Caution **Notas Importantes de Almacenamiento:** * **Clave Privada (`.capgo_key_v2`)**: Nunca confirme esto en control de versiones. Este archivo debe mantenerse seguro y usarse solo para cifrado durante las cargas de paquetes. * **Clave Pública (`.capgo_key_v2.pub`)**: Es seguro confirmar esto en control de versiones ya que es una copia de seguridad de su clave pública. * **Ubicación del Archivo**: Las claves se crean en el directorio actual donde ejecuta el comando `key create`. * **Clave Pública en Config**: Debe ejecutar `key save` para almacenar la clave pública en su configuración de Capacitor para que la aplicación móvil la Usar. Para uso en producción, almacene la clave privada de forma segura (Variables de entorno, servicios de gestión de claves) y elimínela de su proyecto local después de la configuración. ### Paso 2: Guardar su Clave Pública en la Configuración de Capacitor (Requerido) [Section titled “Paso 2: Guardar su Clave Pública en la Configuración de Capacitor (Requerido)”](#paso-2-guardar-su-clave-pública-en-la-configuración-de-capacitor-requerido) **Debe** guardar su clave pública en la configuración de Capacitor para que su aplicación móvil pueda descifrar paquetes: ```shell # Guardar clave pública desde archivo a configuración de Capacitor (requerido) npx @capgo/cli@latest key save --key ./.capgo_key_v2.pub # O guardar datos de clave pública directamente npx @capgo/cli@latest key save --key-data "$CAPGO_PUBLIC_KEY" ``` ### Paso 3: Sincronizar Plataforma Capacitor (Requerido) [Section titled “Paso 3: Sincronizar Plataforma Capacitor (Requerido)”](#paso-3-sincronizar-plataforma-capacitor-requerido) Después de guardar la clave pública, **debe** sincronizar la plataforma Capacitor para copiar la configuración actualizada a la capa nativa: ```shell # Sincronizar la plataforma para copiar config a nativo npx cap sync ``` Caution **Pasos Requeridos**: 1. El comando `key save` almacena la clave pública en su configuración de Capacitor 2. `npx cap sync` copia esta configuración a la capa nativa donde la aplicación móvil puede acceder a ella 3. Sin ambos pasos, su aplicación no podrá descifrar actualizaciones cifradas ## Cifrado de Paquetes [Section titled “Cifrado de Paquetes”](#cifrado-de-paquetes) ### Método 1: Cifrar Durante la Carga [Section titled “Método 1: Cifrar Durante la Carga”](#método-1-cifrar-durante-la-carga) La forma más simple es cifrar durante el proceso de carga: ```shell # Cargar con cifrado automático npx @capgo/cli@latest bundle upload --key-v2 # Para almacenamiento externo, debe cifrar primero (ver Flujo de Trabajo de Cifrado Manual abajo) ``` ### Método 2: Flujo de Trabajo de Cifrado Manual [Section titled “Método 2: Flujo de Trabajo de Cifrado Manual”](#método-2-flujo-de-trabajo-de-cifrado-manual) Para más control, puede cifrar paquetes manualmente: 1. **Crear un paquete zip:** ```shell npx @capgo/cli@latest bundle zip com.example.app --path ./dist --key-v2 ``` 2. **Cifrar el paquete:** ```shell npx @capgo/cli@latest bundle encrypt ./com.example.app.zip CHECKSUM_FROM_STEP_1 ``` 3. **Cargar a su almacenamiento (ej., S3) y registrar con Capgo:** ```shell # Primero cargar el paquete cifrado a su almacenamiento (ej., AWS S3) aws s3 cp ./encrypted-bundle.zip s3://your-bucket/encrypted-bundle.zip # Luego registrar con Capgo usando la URL externa npx @capgo/cli@latest bundle upload --external https://your-storage.com/encrypted-bundle.zip --iv-session-key IV_SESSION_KEY_FROM_STEP_2 ``` ## Gestión de Claves [Section titled “Gestión de Claves”](#gestión-de-claves) ### Almacenamiento Seguro de Claves [Section titled “Almacenamiento Seguro de Claves”](#almacenamiento-seguro-de-claves) **Opciones de Clave Privada:** 1. **Basada en archivo (desarrollo local):** ```shell # Clave almacenada como archivo .capgo_key_v2 en la raíz del proyecto npx @capgo/cli@latest bundle upload --key-v2 ``` 2. **Variable de entorno (CI/CD):** ```shell # Almacenar en variable de entorno para CI export CAPGO_PRIVATE_KEY="$(cat .capgo_key_v2)" npx @capgo/cli@latest bundle upload --key-data-v2 "$CAPGO_PRIVATE_KEY" ``` **Configuración de Clave Pública (Requerido):** ```shell # Debe guardar clave pública en configuración de Capacitor para aplicación móvil npx @capgo/cli@latest key save --key ./.capgo_key_v2.pub ``` **Entorno de Producción:** * Almacene claves privadas en servicios de gestión de claves seguros (AWS KMS, Azure Key Vault, etc.) * Usar gestión de secretos de CI/CD para claves privadas * Nunca confirme claves privadas en control de versiones **Uso de Claves:** * **Clave Privada**: Usada por CLI para cifrado durante la carga de paquetes (mantener segura) * **Clave Pública**: Almacenada en configuración de la aplicación para descifrado en dispositivo (seguro confirmar) ### Rotación de Claves [Section titled “Rotación de Claves”](#rotación-de-claves) Rote regularmente sus claves de cifrado para mayor seguridad: 1. **Generar nuevas claves:** ```shell # Navegar al directorio deseado primero, luego crear claves mkdir ./new-keys && cd ./new-keys npx @capgo/cli@latest key create ``` 2. **Guardar la nueva clave pública en configuración de Capacitor:** ```shell npx @capgo/cli@latest key save --key ./new-keys/.capgo_key_v2.pub ``` 3. **Actualizar la configuración de su aplicación** con la nueva clave pública 4. **Desplegar la aplicación actualizada** antes de cargar paquetes cifrados con la nueva clave ## Mejores Prácticas de Seguridad [Section titled “Mejores Prácticas de Seguridad”](#mejores-prácticas-de-seguridad) ### Seguridad de Claves [Section titled “Seguridad de Claves”](#seguridad-de-claves) * **Nunca comparta claves privadas** entre entornos o miembros del equipo * **Usar claves diferentes** para diferentes entornos (dev, staging, producción) * **Rote claves regularmente** (recomendado: cada 6-12 meses) * **Almacene claves de forma segura** usando sistemas de gestión de claves apropiados ### Seguridad de Paquetes [Section titled “Seguridad de Paquetes”](#seguridad-de-paquetes) * **Siempre verifique** la integridad del paquete después del descifrado * **Monitoree** patrones de descarga inusuales o fallos * **Usar HTTPS** para todas las URLs de paquetes (requerido para aplicaciones móviles) * **Implemente** manejo de errores apropiado para fallos de descifrado ### Control de Acceso [Section titled “Control de Acceso”](#control-de-acceso) * **Limite el acceso** a claves de cifrado solo a personal autorizado * **Usar acceso basado en roles** para operaciones de gestión de claves * **Audite** el uso y acceso de claves regularmente * **Implemente** procedimientos apropiados de respaldo y recuperación ## Solución de Problemas de Cifrado [Section titled “Solución de Problemas de Cifrado”](#solución-de-problemas-de-cifrado) ### Problemas Comunes [Section titled “Problemas Comunes”](#problemas-comunes) **Fallos de descifrado:** * Verifique que la clave privada coincida con la clave pública usada para cifrado * Verifique que el `ivSessionKey` sea correcto * Asegúrese de que está usando Cifrado V2 (V1 ya no es compatible) **Errores relacionados con claves:** * Confirme que el formato de clave privada sea correcto (formato PEM) * Verifique que la clave no se haya corrompido durante almacenamiento/transferencia * Verifique que la clave tenga permisos apropiados en la configuración de su aplicación **Problemas de rendimiento:** * Los paquetes grandes pueden tardar más en cifrar/descifrar * Considere usar actualizaciones diferenciales para reducir tamaños de paquetes * Monitoree el rendimiento del dispositivo durante el descifrado ### Comandos de Depuración [Section titled “Comandos de Depuración”](#comandos-de-depuración) Verificar estado de cifrado: ```shell npx @capgo/cli@latest app debug ``` Probar flujo de trabajo de cifrado/descifrado: ```shell # Probar el flujo de trabajo completo: zip → cifrar → descifrar → descomprimir npx @capgo/cli@latest bundle zip com.example.app --key-v2 npx @capgo/cli@latest bundle encrypt ./com.example.app.zip CHECKSUM --json npx @capgo/cli@latest bundle decrypt ./encrypted-bundle.zip IV_SESSION_KEY ``` ## Cumplimiento y Estándares [Section titled “Cumplimiento y Estándares”](#cumplimiento-y-estándares) La implementación de cifrado de Capgo sigue estándares de la industria: * **AES-256**: Algoritmo de cifrado aprobado por FIPS 140-2 * **RSA-4096**: Cifrado asimétrico fuerte para protección de claves * **Modo GCM**: Proporciona confidencialidad y autenticidad * **Aleatorio Seguro**: Generación de números aleatorios criptográficamente seguros Esto hace que Capgo sea adecuado para aplicaciones que requieren cumplimiento con: * GDPR (Reglamento General de Protección de Datos) * HIPAA (Ley de Portabilidad y Responsabilidad de Seguros de Salud) * SOC 2 (Service Organización Control 2) * ISO 27001 (Gestión de Seguridad de la Información) ## Consideraciones de Rendimiento [Section titled “Consideraciones de Rendimiento”](#consideraciones-de-rendimiento) ### Sobrecarga de Cifrado [Section titled “Sobrecarga de Cifrado”](#sobrecarga-de-cifrado) * **Tamaño del paquete**: Los paquetes cifrados son ligeramente más grandes (\~1-2% de sobrecarga) * **Tiempo de procesamiento**: El cifrado/descifrado agrega latencia mínima * **Uso de memoria**: Aumento temporal durante operaciones de cifrado/descifrado ### Consejos de Optimización [Section titled “Consejos de Optimización”](#consejos-de-optimización) * Usar actualizaciones diferenciales para minimizar transferencia de datos cifrados * Optimice el tamaño de su paquete convirtiendo imágenes a formato WebP * Minimice archivos JavaScript y CSS antes de empaquetar * Elimine dependencias y código no utilizados * Monitoree el rendimiento del dispositivo en dispositivos más antiguos/lentos ## Próximos Pasos [Section titled “Próximos Pasos”](#próximos-pasos) * Conozca sobre [Almacenamiento Personalizado](/docs/live-updates/custom-storage/) para usar cifrado con su propia infraestructura * Explore [Canales](/docs/live-updates/channels/) para gestionar paquetes cifrados en diferentes entornos * Configurar [Integración CI/CD](/docs/getting-started/cicd-integration/) para automatizar despliegues cifrados # Funciones > Referencia completa de todas las funciones de Capgo Live Update, desde el sistema central de actualizaciones hasta controles de implementación avanzados, análisis y funciones de colaboración en equipo. Esta página ofrece una visión general completa de todas las características disponibles en Capgo Live Updates. Cada función incluye una breve descripción y enlaces a la documentación detallada. ## Core Update System [Section titled “Core Update System”](#core-update-system) ### Actualizaciones Over-the-Air (OTA) [Section titled “Actualizaciones Over-the-Air (OTA)”](#actualizaciones-over-the-air-ota) Despliegue de actualizaciones de JavaScript, HTML, CSS y activos directamente a los usuarios sin aprobación de la tienda de aplicaciones. Las actualizaciones se descargan en segundo plano y se aplican en el próximo reinicio de la aplicación. **Capacidades clave:** * Descargas en segundo plano * Instalación automática * Sin interrupciones para el usuario * Soporte multiplataforma (iOS, Android, Electron) [Más información sobre el comportamiento de las actualizaciones →](/docs/live-updates/update-behavior/) *** ### Delta Updates (Actualizaciones diferenciales) [Section titled “Delta Updates (Actualizaciones diferenciales)”](#delta-updates-actualizaciones-diferenciales) Solo descargue los archivos que han cambiado entre versiones, reduciendo el uso de ancho de banda hasta un 95% y acelerando la entrega de actualizaciones. **Capacidades clave:** * Diferenciación automática a nivel de archivo * Verificación basada en sumas de verificación * Comparación de manifiestos * Conmutación inteligente a actualizaciones completas cuando sea necesario [Más información sobre actualizaciones diferenciales →](/docs/live-updates/differentials/) *** ### Reversión automática [Section titled “Reversión automática”](#reversión-automática) Si una actualización falla al cargarse o provoca fallos, el sistema revierte automáticamente a la última versión que funcionaba. **Capacidades clave:** * Detección de fallos * Detección de tiempo de espera * Reversión automática * No se requiere intervención del usuario [Más información sobre reversiones →](/docs/live-updates/rollbacks/) *** ### Validación de sumas de verificación y reversión [Section titled “Validación de sumas de verificación y reversión”](#validación-de-sumas-de-verificación-y-reversión) Verifica la integridad del bundle mediante sumas de verificación y, automáticamente, recae a la última versión que funcionaba si se detecta corrupción. **Capacidades clave:** * Validación de sumas de verificación al descargar * Detección de corrupción * Reversión automática a la última bundle que funcionaba * Herramientas de recuperación manual disponibles *** ### Detección de actualizaciones incompatibles [Section titled “Detección de actualizaciones incompatibles”](#detección-de-actualizaciones-incompatibles) Previene que se apliquen actualizaciones incompatibles a dispositivos con versiones de código nativo más antiguas. **Capacidades clave:** * Verificación de compatibilidad de versiones nativas * Validación de dependencias de plugins * Bloqueo automático de actualizaciones incompatibles * Mensajes de error claros [Más información sobre la focalización de versiones →](/docs/live-updates/version-targeting/) *** ## Deployment Control [Section titled “Deployment Control”](#deployment-control) ### Sistema de canales [Section titled “Sistema de canales”](#sistema-de-canales) Organiza y gestiona actualizaciones a través de diferentes entornos y segmentos de usuarios con configuraciones de canales flexibles. **Capacidades clave:** * Canales personalizados ilimitados (producción, staging, beta, etc.) * Asignaciones de bundles por canal * Reglas de segmentación específicas por canal * Autoasignación de dispositivos * Sobrescritura de canal por dispositivo [Más información sobre canales →](/docs/live-updates/channels/) *** ### Segmentación de dispositivos [Section titled “Segmentación de dispositivos”](#segmentación-de-dispositivos) Apunta a dispositivos específicos, versiones o segmentos de usuarios para lanzamientos por fases y despliegues controlados. **Capacidades clave:** * Segmentación basada en versiones * Sobrescrituras específicas por dispositivo * Filtrado por plataforma (iOS, Android) * Filtrado por metadatos personalizados * Bloqueo de compilaciones de emulador/desarrollo *** ### Políticas de canal [Section titled “Políticas de canal”](#políticas-de-canal) Configura reglas y restricciones sobre cómo se entregan las actualizaciones en cada canal. **Capacidades clave:** * Desactivar actualizaciones automáticas * Bloquear actualizaciones de versiones principales * Desactivar actualizaciones en emuladores * Desactivar actualizaciones en compilaciones de desarrollo * Políticas específicas por plataforma (solo iOS, solo Android) [Más información sobre políticas de canal →](/docs/live-updates/channels/#channel-policies) *** ## Developer Tools [Section titled “Developer Tools”](#developer-tools) ### Vista previa de bundles [Section titled “Vista previa de bundles”](#vista-previa-de-bundles) Vista previa de bundles en un entorno web en vivo antes de desplegarlos a los dispositivos, accesible desde el panel web. **Ubicación:** Web Dashboard → App → Bundle → Vista previa *** ### Depuración en vivo [Section titled “Depuración en vivo”](#depuración-en-vivo) Monitoreo en tiempo real de los eventos de actualización para dispositivos específicos vía CLI, mostrando eventos de verificación, descarga, instalación y errores. **Uso:** ```bash npx @capgo/cli app debug [appId] ``` **Muestra:** * Verificaciones de actualización * Progreso de descarga * Estado de instalación * Mensajes de error * Bloqueos de políticas *** ### Visor de manifiesto de bundle [Section titled “Visor de manifiesto de bundle”](#visor-de-manifiesto-de-bundle) Inspecciona el manifiesto completo de cualquier bundle, incluida la lista de archivos, sumas de verificación y metadatos. **Ubicación:** Web Dashboard → App → Bundle → Manifest **Muestra:** * Lista de archivos con sumas de verificación * Metadatos del bundle * Compatibilidad de versiones nativas * Dependencias de plugins *** ### Dependencias de plugins nativos de Capacitor [Section titled “Dependencias de plugins nativos de Capacitor”](#dependencias-de-plugins-nativos-de-capacitor) Ver todas las plugins nativas de Capacitor incluidas en cada bundle para rastrear cambios de dependencias entre versiones. **Ubicación:** Web Dashboard → App → Bundle → Dependencies **Muestra:** * Nombres y versiones de plugins * Adiciones/remociones de dependencias * Advertencias de compatibilidad *** ### Integración con CLI [Section titled “Integración con CLI”](#integración-con-cli) Interfaz de línea de comandos integral para implementaciones automatizadas e integración CI/CD. **Comandos clave:** * `bundle upload` - Subir nuevos bundles * `bundle list` - Listar todos los bundles * `bundle delete` - Eliminar bundles * `bundle cleanup` - Limpiar bundles antiguos * `channel set` - Configurar canales * `app debug` - Depuración en vivo [Ver referencia completa de CLI →](/docs/cli/reference/) *** ### Cifrado de bundles [Section titled “Cifrado de bundles”](#cifrado-de-bundles) Cifrado de extremo a extremo para bundles con cifrado AES-256, protegiendo tu código en tránsito y en reposo. **Capacidades clave:** * Generación de par de claves RSA * Cifrado de bundles AES-256 * Verificación de firmas de código * Gestión de claves de cifrado [Más información sobre cifrado →](/docs/live-updates/encryption/) *** ### Limpieza y retención de bundles [Section titled “Limpieza y retención de bundles”](#limpieza-y-retención-de-bundles) Eliminación automática de bundles antiguos basada en políticas de retención para gestionar el uso del almacenamiento. **Capacidades clave:** * Número de retención configurable * Limpieza automática vía CLI * Trabajos de limpieza programados * Seguimiento del uso del almacenamiento **Uso:** ```bash npx @capgo/cli bundle cleanup --keep=10 ``` *** ## Analytics & Monitoring [Section titled “Analytics & Monitoring”](#analytics--monitoring) ### Estadísticas de actualizaciones [Section titled “Estadísticas de actualizaciones”](#estadísticas-de-actualizaciones) Rastrea tasas de adopción de actualizaciones, tasas de éxito y progreso de implementación entre tus usuarios. **Métricas disponibles:** * Tasa de éxito de descarga * Tasa de éxito de instalación * Tasas de errores por tipo * Adopción de actualizaciones a lo largo del tiempo * Distribución de versiones **Ubicación:** Web Dashboard → App → Estadísticas *** ### Registros de dispositivos [Section titled “Registros de dispositivos”](#registros-de-dispositivos) Registros de eventos por dispositivo que muestran el ciclo de vida completo de la actualización, desde la verificación hasta la instalación. **Tipos de eventos:** * Verificaciones de actualización * Inicio/completado/fallo de descarga * Inicio/completado/fallo de instalación * Eventos de reversión * Bloqueos de políticas **Ubicación:** * Web Dashboard → App → Device → Logs * Web Dashboard → App → Logs (todos los dispositivos) [Más información sobre logs →](/docs/webapp/logs/) *** ### Analítica de uso de bundles [Section titled “Analítica de uso de bundles”](#analítica-de-uso-de-bundles) Analíticas detalladas sobre qué bundles están activos, recuentos de descargas y uso de almacenamiento. **Métricas:** * Instalaciones activas por bundle * Recuentos de descargas * Uso de almacenamiento por bundle * Uso de ancho de banda *** ### Estadísticas de canal [Section titled “Estadísticas de canal”](#estadísticas-de-canal) Rastrea métricas de rendimiento y adopción por canal. **Métricas:** * Dispositivos por canal * Tasas de éxito de actualización por canal * Historial de implementación * Tasas de error por canal **Ubicación:** Web Dashboard → App → Channel → Estadísticas *** ### Historial de implementación [Section titled “Historial de implementación”](#historial-de-implementación) Rastreo completo de todas las implementaciones de bundles, asignaciones de canales y cambios de configuración. **Eventos rastreados:** * Subidas de bundles * Asignaciones de canales * Cambios de políticas * Anulaciones de dispositivo **Ubicación:** Web Dashboard → App → Channel → History *** ## Security & Compliance [Section titled “Security & Compliance”](#security--compliance) ### Cifrado de extremo a extremo [Section titled “Cifrado de extremo a extremo”](#cifrado-de-extremo-a-extremo) Cifra bundles en reposo y en tránsito con cifrado AES-256 de estándar de la industria. [Más información sobre cifrado →](/docs/live-updates/encryption/) *** ### Firma de código [Section titled “Firma de código”](#firma-de-código) Verifica la integridad del bundle con firmas criptográficas para evitar manipulaciones. *** ### Cumplimiento SOC 2 Tipo II [Section titled “Cumplimiento SOC 2 Tipo II”](#cumplimiento-soc-2-tipo-ii) Infraestructura y procesos certificados conforme a SOC 2 Tipo II para la seguridad empresarial. *** ### Cumplimiento de la App Store [Section titled “Cumplimiento de la App Store”](#cumplimiento-de-la-app-store) Totalmente compatible con las políticas de Apple App Store y Google Play Store para actualizaciones OTA. [Más información sobre cumplimiento →](/docs/live-updates/compliance/) *** ### Implementación de 2FA (a nivel de organización) [Section titled “Implementación de 2FA (a nivel de organización)”](#implementación-de-2fa-a-nivel-de-organización) Requiere autenticación de dos factores para todos los miembros de la organización para acceder al panel y a la API. **Ubicación:** Web Dashboard → Organization → Security [Más información sobre 2FA →](/docs/webapp/2fa-enforcement/) *** ### Aplicación de bundles cifrados [Section titled “Aplicación de bundles cifrados”](#aplicación-de-bundles-cifrados) Requiere que todos los bundles estén cifrados a nivel de la organización.Ubicación: Panel de control web → Organización → Seguridad *** ## Colaboración en equipo [Section titled “Colaboración en equipo”](#colaboración-en-equipo) ### Control de Acceso Basado en Roles (RBAC) [Section titled “Control de Acceso Basado en Roles (RBAC)”](#control-de-acceso-basado-en-roles-rbac) Permisos granulares para el control de acceso a nivel de organización y de aplicación **Roles de la organización:** 1. `super_admin` - Acceso completo 2. `admin` - Acceso de administrador 3. `read` - Acceso de solo lectura 4. `upload` - Acceso solo de subida **Roles de la aplicación:** 1. `app_developer` - Acceso completo a la app 2. `app_uploader` - Solo subida de bundles 3. `app_reader` - Acceso de solo lectura **Ubicación:** * Web Dashboard → Organización → Miembros * Web Dashboard → App → Acceso [Learn more about RBAC →](/docs/webapp/organization-system/#roles-and-permissions) *** ### Registros de auditoría [Section titled “Registros de auditoría”](#registros-de-auditoría) Rastreo completo de todas las actividades de la organización y la app para cumplimiento y seguridad. **Eventos registrados:** 1. Acciones de usuario (inicio de sesión, cierre de sesión, cambios de permisos) 2. Operaciones de bundles (subir, eliminar, asignar) 3. Operaciones de canales (crear, actualizar, eliminar) 4. Cambios en la organización (configuración, miembros) **Ubicación:** Panel de control web → Organización → Registros de auditoría *** ### Webhooks [Section titled “Webhooks”](#webhooks) Reciba notificaciones en tiempo real sobre eventos en sus apps mediante webhooks HTTP. **Eventos admitidos:** 1. `apps` - App creada/actualizada/eliminada 2. `app_versions` - Bundle subido/eliminado 3. `channels` - Canal creado/actualizado/eliminado 4. `org_users` - Miembro agregado/eliminado 5. `orgs` - Organización actualizada **Características:** 1. URLs de webhook personalizadas 2. Filtrado de eventos 3. Registros de entrega 4. Mecanismo de reintento 5. Funcionalidad de prueba **Ubicación:** Web Dashboard → Organización → Webhooks *** ### Colaboración entre múltiples usuarios [Section titled “Colaboración entre múltiples usuarios”](#colaboración-entre-múltiples-usuarios) Invita a miembros del equipo a tu organización con roles y permisos específicos. **Características:** 1. Invitaciones por correo electrónico 2. Asignación de roles 3. Gestión de miembros 4. Revocación de acceso **Ubicación:** Web Dashboard → Organización → Miembros *** ### Gestión de claves API [Section titled “Gestión de claves API”](#gestión-de-claves-api) Crear, gestionar y revocar claves API con fechas de expiración opcionales y almacenamiento hash. **Capacidades de las claves:** 1. Claves por aplicación o por organización 2. Fechas de expiración opcionales 3. Almacenamiento hasheado (irreversible) 4. Soporte para rotación de claves **Ubicación:** Web Dashboard → Claves API [Learn more about API keys →](/docs/public-api/#authentication) *** ### Políticas de contraseña [Section titled “Políticas de contraseña”](#políticas-de-contraseña) Requisitos de contraseña a nivel de organización para garantizar estándares de seguridad. **Políticas configurables:** 1. Longitud mínima 2. Requiere mayúsculas 3. Requiere números 4. Requiere caracteres especiales **Ubicación:** Web Dashboard → Organización → Seguridad *** ## Soporte de plataforma [Section titled “Soporte de plataforma”](#soporte-de-plataforma) ### Soporte multiplataforma [Section titled “Soporte multiplataforma”](#soporte-multiplataforma) Soporte para iOS, Android y aplicaciones Electron con un único SDK. **Plataformas compatibles:** * iOS (Capacitor 5, 6, 7, 8) * Android (Capacitor 5, 6, 7, 8) * Electron (NUEVO en 2025) *** ### Soporte a largo plazo [Section titled “Soporte a largo plazo”](#soporte-a-largo-plazo) Soporte continuo para versiones anteriores de Capacitor para mantener la compatibilidad con aplicaciones heredadas. **Actualmente compatibles:** * Capacitor 8 (la más reciente) * Capacitor 7 * Capacitor 6 * Capacitor 5 *** ### Backends de almacenamiento personalizados [Section titled “Backends de almacenamiento personalizados”](#backends-de-almacenamiento-personalizados) Utilice su propia infraestructura de almacenamiento (S3, R2, etc.) en lugar del almacenamiento predeterminado de Capgo. [Learn more about custom storage →](/docs/live-updates/custom-storage/) *** ### Configuración de China [Section titled “Configuración de China”](#configuración-de-china) Configuración especial para aplicaciones distribuidas en China continental para cumplir con las regulaciones locales. [Learn more about China configuration →](/docs/live-updates/china-configuration/) *** ## Funciones avanzadas [Section titled “Funciones avanzadas”](#funciones-avanzadas) ### Comportamiento de actualización personalizado [Section titled “Comportamiento de actualización personalizado”](#comportamiento-de-actualización-personalizado) Configure cuándo y cómo se verifican e impulsan actualizaciones a través del SDK. **Opciones configurables:** 1. Intervalo de verificación (`periodCheckDelay` - mínimo 600 segundos) 2. Temporización de actualización directa (`directUpdate` - atInstall, onLaunch, always) 3. Activar/desactivar actualizaciones automáticas (`autoUpdate`) 4. Requisitos de red (Android solo - vía WorkManager) [Learn more about update behavior →](/docs/live-updates/update-behavior/) *** ### Tipos de actualización [Section titled “Tipos de actualización”](#tipos-de-actualización) Diferentes tipos de actualización para distintos usos, desde actualizaciones instantáneas hasta instalaciones controladas por el usuario. **Tipos disponibles:** 1. Actualizaciones en segundo plano (predeterminado) 2. Actualizaciones inmediatas 3. Actualizaciones solicitadas por el usuario 4. Actualizaciones condicionadas [Learn more about update types →](/docs/live-updates/update-types/) *** ### Sistema de créditos [Section titled “Sistema de créditos”](#sistema-de-créditos) Facturación basada en el uso con créditos para ancho de banda, almacenamiento y otros recursos. **Características:** * Seguimiento del uso de créditos * Alertas de uso * Recarga vía Stripe * Libro mayor de créditos **Ubicación:** Web Dashboard → Organización → Créditos *** ## Comenzando [Section titled “Comenzando”](#comenzando) ¿Listo para empezar a usar estas funciones? Siga nuestra [Quickstart Guide](/docs/getting-started/quickstart/) para configurar su primera aplicación con Capgo Live Updates. ## ¿Necesita ayuda? [Section titled “¿Necesita ayuda?”](#necesita-ayuda) * [Join our Discord](https://discord.capgo.app) para soporte comunitario * [Check the FAQ](/docs/faq/) para preguntas comunes * [Browse API documentation](/docs/public-api/) para la integración de API * [Contact support](https://capgo.app/consulting/) para asistencia empresarial . # Integraciones de CI/CD > Integre las Actualizaciones en Vivo de Capgo con su plataforma de CI/CD favorita para flujos de trabajo de despliegue automatizados. Automatice su proceso de despliegue de Actualizaciones en Vivo de Capgo integrándose con plataformas populares de CI/CD. Estas integraciones le permiten desplegar automáticamente las actualizaciones de aplicaciones cada vez que realice cambios en el código, probar ramas de características y gestionar múltiples entornos de despliegue. ## Integraciones disponibles [Section titled “Integraciones disponibles”](#integraciones-disponibles) Elija su plataforma de CI/CD para comenzar con despliegues automatizados: [Azure DevOps ](/docs/live-Actualizaciones/Integraciones/Azure-devops/)Integre con Azure DevOps Pipelines para flujos de trabajo automatizados de construcción, pruebas y despliegue. [GitLab CI/CD ](/docs/live-Actualizaciones/Integraciones/GitLab-ci/)Configurar pipelines de GitLab CI/CD para desplegar automáticamente las actualizaciones de su aplicación con gestión completa de entornos. [GitHub Actions ](/docs/live-Actualizaciones/Integraciones/GitHub-actions/)Usar GitHub Actions para automatización poderosa con despliegues multicanal y protección de entorno. [Bitbucket Pipelines ](/docs/live-Actualizaciones/Integraciones/Bitbucket-pipeline/)Despliegue con Bitbucket Pipelines usando configuraciones simples o avanzadas para múltiples entornos. ## Lo que obtendrá [Section titled “Lo que obtendrá”](#lo-que-obtendrá) Todas las guías de integración incluyen: * **Configuración simple**: Configuración básica para comenzar rápidamente * **Flujos de trabajo avanzados**: Despliegues multi-entorno con staging y producción * **Prueba de ramas de características**: Despliegue automático de ramas de características a canales de prueba * **Mejores prácticas de seguridad**: Gestión segura de secretos y protección de entorno * **Monitoreo**: Notificaciones y registro para el estado del despliegue Tip **¿Nuevo en CI/CD?** Comience con la configuración simple para su plataforma, luego agregue gradualmente características más avanzadas como despliegues multicanal y pruebas automatizadas a medida que crezcan sus necesidades. ## Características comunes [Section titled “Características comunes”](#características-comunes) Cada integración admite: * **Construcciones automatizadas**: Activar despliegues en cambios de código * **Soporte multicanal**: Desplegar a diferentes canales (desarrollo, staging, producción) * **Pruebas de Solicitud de extracción/Merge Request**: Probar cambios en entornos aislados * **Soporte de cifrado**: Despliegues seguros con la función de cifrado de Capgo * **Protección de entorno**: Aprobaciones manuales y acceso restringido para producción * **Notificaciones**: Integraciones de Slack, correo electrónico y otras notificaciones ## Requisitos previos [Section titled “Requisitos previos”](#requisitos-previos) Antes de configurar cualquier integración, asegúrese de tener: * Una cuenta de Capgo con una aplicación configurada * El código fuente de su aplicación en un repositorio Git * Un token de API de Capgo desde [console.capgo.app/apikeys](https://console.capgo.app/apikeys) * Node.js y npm/yarn configurados en su proyecto ## Documentación relacionada [Section titled “Documentación relacionada”](#documentación-relacionada) * [Canales](/docs/live-updates/channels/) - Aprenda cómo gestionar diferentes entornos de despliegue * [Cifrado](/docs/live-updates/encryption/) - Asegure sus despliegues con cifrado de extremo a extremo * [Comportamiento de actualización](/docs/live-updates/update-behavior/) - Personalice cómo se aplican las actualizaciones a sus aplicaciones ¡Elija su plataforma de CI/CD arriba para comenzar a automatizar sus despliegues de Capgo! # Integración con Azure DevOps > Aprenda cómo integrar las Actualizaciones en Vivo de Capgo con Azure DevOps Pipelines para el despliegue automatizado de las actualizaciones de su aplicación. Integre las Actualizaciones en Vivo de Capgo con Azure DevOps Pipelines para desplegar automáticamente las actualizaciones de su aplicación cada vez que realice cambios en el código. Esta guía cubre la configuración de flujos de trabajo automatizados de construcción, pruebas y despliegue. ## Requisitos previos [Section titled “Requisitos previos”](#requisitos-previos) Antes de configurar la integración con Azure DevOps, asegúrese de tener: * Una organización y proyecto de Azure DevOps * Una cuenta de Capgo con una aplicación configurada * El código fuente de su aplicación en un repositorio Git de Azure Repos * Node.js y npm/yarn configurados en su proyecto ## Configuración de Azure DevOps Pipeline [Section titled “Configuración de Azure DevOps Pipeline”](#configuración-de-azure-devops-pipeline) ### Paso 1: Crear Variables de Pipeline [Section titled “Paso 1: Crear Variables de Pipeline”](#paso-1-crear-variables-de-pipeline) Primero, Configurar las Variables necesarias en su proyecto de Azure DevOps: 1. Navegue a su proyecto de Azure DevOps 2. Vaya a **Pipelines** → **Library** → **Variable groups** 3. Cree un nuevo grupo de Variables llamado `Capgo-Variables` 4. Agregue las siguientes Variables: | Nombre de Variable | Valor | Segura | | ------------------ | ------------------------ | ------ | | `CAPGO_TOKEN` | Su token de API de Capgo | ✅ Sí | Tip Obtenga su token de API de Capgo desde [console.capgo.app/apikeys](https://console.capgo.app/apikeys). El ID de su aplicación ya está configurado en su archivo `capacitor.config.ts`. ## Simple [Section titled “Simple”](#simple) Configuración básica que despliega a producción en cada push a la rama principal: ```yaml # Simple Azure DevOps Pipeline para Actualizaciones en Vivo de Capgo trigger: branches: include: - main variables: - group: Capgo-Variables jobs: - job: BuildAndDeploy displayName: 'Construir y Desplegar a Capgo' pool: vmImage: 'ubuntu-latest' steps: - task: NodeTool@0 displayName: 'Configurar Node.js' inputs: versionSpec: '22.x' - script: | npm ci npm run test npm run build displayName: 'Instalar, probar y construir' - script: | npm install -g @capgo/cli npx @capgo/cli bundle upload --apikey $(CAPGO_TOKEN) --channel production displayName: 'Desplegar a Capgo' ``` ## Avanzado [Section titled “Avanzado”](#avanzado) ### Despliegues de ramas de características [Section titled “Despliegues de ramas de características”](#despliegues-de-ramas-de-características) Despliegue ramas de características a canales de prueba para revisión y Pruebas: ```yaml # Despliegue de rama de características trigger: branches: include: - feature/* variables: - group: Capgo-Variables jobs: - job: DeployFeature displayName: 'Desplegar rama de características' pool: vmImage: 'ubuntu-latest' condition: startsWith(variables['Build.SourceBranch'], 'refs/heads/feature/') steps: - task: NodeTool@0 inputs: versionSpec: '22.x' - script: | npm ci npm run test npm run build displayName: 'Instalar, probar y construir' - script: | BRANCH_NAME=$(echo "$(Build.SourceBranchName)" | sed 's/[^a-zA-Z0-9-]/-/g') CHANNEL_NAME="feature-$BRANCH_NAME" npm install -g @capgo/cli npx @capgo/cli channel create $CHANNEL_NAME --apikey $(CAPGO_TOKEN) || true npx @capgo/cli bundle upload --apikey $(CAPGO_TOKEN) --channel $CHANNEL_NAME displayName: 'Desplegar a canal de características' ``` Tip **Pruebas con Canales**: Después de desplegar a un canal de características, puede probar la actualización en su aplicación configurándola para usar ese canal específico. Aprenda más sobre [configurar canales en su aplicación](/docs/live-updates/channels/#configuring-the-channel-in-your-app). ### Uso de cifrado [Section titled “Uso de cifrado”](#uso-de-cifrado) Si está usando la [función de cifrado de Capgo](/docs/live-updates/encryption/), deberá almacenar su clave privada de forma segura en su entorno de CI/CD. Después de [configurar las claves de cifrado](/docs/live-updates/encryption/#setting-up-encryption) localmente, agregue su clave privada a las Variables de Azure DevOps: ```shell # Mostrar el contenido de su clave privada (copiar esta salida) cat .capgo_key_v2 ``` Agregue este contenido como `CAPGO_PRIVATE_KEY` en su grupo de Variables de Azure DevOps (márquelo como secreto), luego úselo en pipelines: ```yaml # Desplegar con cifrado - script: | npm install -g @capgo/cli npx @capgo/cli bundle upload --apikey $(CAPGO_TOKEN) --key-data-v2 "$(CAPGO_PRIVATE_KEY)" --channel production displayName: 'Desplegar a Capgo con cifrado' ``` Caution **Mejores prácticas de seguridad:** * Nunca confirme el archivo `.capgo_key_v2` en el control de versiones * Almacene la clave privada solo en la gestión segura de secretos de CI/CD * Usar claves diferentes para diferentes entornos ### Configuración multicanal [Section titled “Configuración multicanal”](#configuración-multicanal) Para obtener información completa sobre la configuración y gestión de múltiples canales de despliegue, consulte la [documentación de Canales](/docs/live-updates/channels/). Configuración completa con múltiples entornos y despliegues de Solicitud de extracción: ```yaml # Azure DevOps Pipeline avanzado con múltiples canales trigger: branches: include: - main - develop pr: branches: include: - main - develop variables: - group: Capgo-Variables stages: # Etapa de construcción - stage: Build jobs: - job: BuildApp pool: vmImage: 'ubuntu-latest' steps: - task: NodeTool@0 inputs: versionSpec: '22.x' - script: | npm ci npm run test npm run build displayName: 'Instalar, probar y construir' - task: PublishBuildArtifacts@1 inputs: pathToPublish: 'dist' artifactName: 'app-build' # Desplegar a desarrollo - stage: DeployDev condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/develop')) jobs: - deployment: DeployDevelopment environment: development pool: vmImage: 'ubuntu-latest' strategy: runOnce: deploy: steps: - task: NodeTool@0 inputs: versionSpec: '22.x' - task: DownloadBuildArtifacts@0 inputs: artifactName: 'app-build' downloadPath: '$(Pipeline.Workspace)' - script: | npm install -g @capgo/cli npx @capgo/cli bundle upload --apikey $(CAPGO_TOKEN) --channel development --path $(Pipeline.Workspace)/app-build displayName: 'Desplegar a desarrollo' # Desplegar PR a canal de prueba - stage: DeployPR condition: and(succeeded(), eq(variables['Build.Reason'], 'PullRequest')) jobs: - job: DeployPRChannel pool: vmImage: 'ubuntu-latest' steps: - task: NodeTool@0 inputs: versionSpec: '22.x' - task: DownloadBuildArtifacts@0 inputs: artifactName: 'app-build' downloadPath: '$(Pipeline.Workspace)' - script: | CHANNEL_NAME="pr-$(System.PullRequest.PullRequestNumber)" npm install -g @capgo/cli npx @capgo/cli channel create $CHANNEL_NAME --apikey $(CAPGO_TOKEN) || true npx @capgo/cli bundle upload --apikey $(CAPGO_TOKEN) --channel $CHANNEL_NAME --path $(Pipeline.Workspace)/app-build displayName: 'Desplegar a canal de PR' # Desplegar a producción - stage: DeployProd condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main')) jobs: - deployment: DeployProduction environment: production pool: vmImage: 'ubuntu-latest' strategy: runOnce: deploy: steps: - task: NodeTool@0 inputs: versionSpec: '22.x' - task: DownloadBuildArtifacts@0 inputs: artifactName: 'app-build' downloadPath: '$(Pipeline.Workspace)' - script: | npm install -g @capgo/cli npx @capgo/cli bundle upload --apikey $(CAPGO_TOKEN) --channel production --path $(Pipeline.Workspace)/app-build displayName: 'Desplegar a producción' ``` ### Despliegue multi-entorno [Section titled “Despliegue multi-entorno”](#despliegue-multi-entorno) Para escenarios complejos con múltiples entornos: ```yaml # Pipeline extendido con múltiples entornos parameters: - name: deployEnvironment displayName: 'Entorno de despliegue' type: string default: 'staging' values: - staging - production variables: - group: Capgo-Variables - name: channelName ${{ if eq(parameters.deployEnvironment, 'production') }}: value: 'production' ${{ else }}: value: 'staging' stages: # Etapa de construcción - stage: Build jobs: - job: BuildApp pool: vmImage: 'ubuntu-latest' steps: - task: NodeTool@0 inputs: versionSpec: '22.x' - script: | npm ci npm run test npm run build displayName: 'Instalar, probar y construir' - task: PublishBuildArtifacts@1 inputs: pathToPublish: 'dist' artifactName: 'app-build' - stage: DeployStaging displayName: 'Desplegar a Staging' dependsOn: Build condition: and(succeeded(), eq('${{ parameters.deployEnvironment }}', 'staging')) jobs: - deployment: DeployStaging displayName: 'Desplegar a canal de staging' pool: vmImage: 'ubuntu-latest' environment: 'staging' strategy: runOnce: deploy: steps: - template: deploy-steps.yml parameters: channel: 'staging' - stage: DeployProduction displayName: 'Desplegar a producción' dependsOn: Build condition: and(succeeded(), eq('${{ parameters.deployEnvironment }}', 'production')) jobs: - deployment: DeployProduction displayName: 'Desplegar a canal de producción' pool: vmImage: 'ubuntu-latest' environment: 'production' strategy: runOnce: deploy: steps: - template: deploy-steps.yml parameters: channel: 'production' ``` ### Plantilla de despliegue (Desplegar-Pasos.yml) [Section titled “Plantilla de despliegue (Desplegar-Pasos.yml)”](#plantilla-de-despliegue-desplegar-pasosyml) Cree un archivo de plantilla reutilizable `deploy-steps.yml`: deploy-steps.yml ```yaml parameters: - name: channel type: string steps: - task: NodeTool@0 displayName: 'Instalar Node.js' inputs: versionSpec: '22.x' - task: DownloadBuildArtifacts@0 displayName: 'Descargar artefactos de construcción' inputs: artifactName: 'app-build' downloadPath: '$(System.ArtifactsDirectory)' - script: | npm install -g @capgo/cli displayName: 'Instalar Capgo CLI' - script: | npx @capgo/cli bundle upload \ --apikey $(CAPGO_TOKEN) \ --channel ${{ parameters.channel }} \ --path $(System.ArtifactsDirectory)/app-build displayName: 'Subir a Capgo (${{ parameters.channel }})' ``` ### Estrategia de despliegue basada en ramas [Section titled “Estrategia de despliegue basada en ramas”](#estrategia-de-despliegue-basada-en-ramas) Configurar diferentes estrategias de despliegue según las ramas de Git: ```yaml trigger: branches: include: - main - develop - feature/* variables: - group: Capgo-Variables - name: targetChannel ${{ if eq(variables['Build.SourceBranch'], 'refs/heads/main') }}: value: 'production' ${{ elseif eq(variables['Build.SourceBranch'], 'refs/heads/develop') }}: value: 'staging' ${{ else }}: value: 'development' stages: - stage: Build jobs: - job: BuildApp pool: vmImage: 'ubuntu-latest' steps: - task: NodeTool@0 inputs: versionSpec: '22.x' - script: | npm ci npm run test npm run build displayName: 'Instalar, probar y construir' - task: PublishBuildArtifacts@1 inputs: pathToPublish: 'dist' artifactName: 'app-build' - stage: Deploy displayName: 'Desplegar a $(targetChannel)' dependsOn: Build condition: succeeded() jobs: - deployment: DeployJob displayName: 'Desplegar a canal $(targetChannel)' pool: vmImage: 'ubuntu-latest' environment: '$(targetChannel)' strategy: runOnce: deploy: steps: - template: deploy-steps.yml parameters: channel: '$(targetChannel)' ``` ## Mejores prácticas de seguridad [Section titled “Mejores prácticas de seguridad”](#mejores-prácticas-de-seguridad) ### Gestión segura de Variables [Section titled “Gestión segura de Variables”](#gestión-segura-de-variables) 1. **Usar grupos de Variables**: Almacene datos sensibles en grupos de Variables de Azure DevOps 2. **Marque como secreto**: Siempre marque tokens de API y claves como Variables secretas 3. **Acceso de ámbito**: Limite el acceso del grupo de Variables a pipelines y usuarios específicos 4. **Rote claves**: Rote regularmente sus tokens de API de Capgo ## Monitoreo y notificaciones [Section titled “Monitoreo y notificaciones”](#monitoreo-y-notificaciones) ### Integración con Teams [Section titled “Integración con Teams”](#integración-con-teams) Agregue notificaciones de Microsoft Teams a su pipeline: ```yaml - task: ms-teams-deploy-card@1.4.1 displayName: 'Notificar Teams en caso de éxito' condition: succeeded() inputs: webhookUri: '$(TEAMS_WEBHOOK_URL)' title: 'Despliegue de Capgo exitoso' text: 'Aplicación desplegada al canal $(targetChannel)' themeColor: '00FF00' - task: ms-teams-deploy-card@1.4.1 displayName: 'Notificar Teams en caso de fallo' condition: failed() inputs: webhookUri: '$(TEAMS_WEBHOOK_URL)' title: 'Despliegue de Capgo fallido' text: 'Fallo en el despliegue a $(targetChannel)' themeColor: 'FF0000' ``` ### Notificaciones por correo electrónico [Section titled “Notificaciones por correo electrónico”](#notificaciones-por-correo-electrónico) Configurar notificaciones por correo electrónico para el estado del despliegue: ```yaml - task: EmailReport@1 displayName: 'Enviar informe por correo' condition: always() inputs: sendMailConditionConfig: 'Always' subject: 'Informe de despliegue de Capgo - $(Build.BuildNumber)' to: 'team@yourcompany.com' body: | Estado de despliegue: $(Agent.JobStatus) Canal: $(targetChannel) Build: $(Build.BuildNumber) Commit: $(Build.SourceVersion) ``` ## Solución de problemas [Section titled “Solución de problemas”](#solución-de-problemas) ### Problemas comunes [Section titled “Problemas comunes”](#problemas-comunes) **El pipeline falla con “Capgo CLI no encontrado”:** ```yaml # Asegúrese de la instalación global - script: | npm install -g @capgo/cli which capgo || echo "Capgo CLI no encontrado en PATH" displayName: 'Instalar y verificar Capgo CLI' ``` **Errores de autenticación:** ```yaml # Verificar que el token esté configurado correctamente - script: | echo "Longitud del token: ${#CAPGO_TOKEN}" if [ -z "$CAPGO_TOKEN" ]; then echo "CAPGO_TOKEN no está configurado" exit 1 fi displayName: 'Verificar token de Capgo' env: CAPGO_TOKEN: $(CAPGO_TOKEN) ``` **Artefactos de construcción no encontrados:** ```yaml # Listar artefactos disponibles para depuración - script: | ls -la $(System.ArtifactsDirectory) find $(System.ArtifactsDirectory) -name "*.js" -o -name "*.html" displayName: 'Depurar artefactos' ``` ### Depurar pipeline [Section titled “Depurar pipeline”](#depurar-pipeline) Agregue pasos de depuración para solucionar problemas: ```yaml - script: | echo "Build.SourceBranch: $(Build.SourceBranch)" echo "Build.BuildNumber: $(Build.BuildNumber)" echo "Canal objetivo: $(targetChannel)" displayName: 'Depurar variables de pipeline' - script: | npx @capgo/cli app debug --apikey $(CAPGO_TOKEN) displayName: 'Depurar estado de aplicación de Capgo' ``` ## Próximos pasos [Section titled “Próximos pasos”](#próximos-pasos) * Aprenda sobre [Canales](/docs/live-updates/channels/) para gestionar diferentes entornos de despliegue * Explore [Almacenamiento personalizado](/docs/live-updates/custom-storage/) para escenarios de despliegue avanzados * Configurar [Cifrado](/docs/live-updates/encryption/) para despliegues seguros * Configurar [Comportamiento de actualización](/docs/live-updates/update-behavior/) para personalizar cómo se aplican las actualizaciones Con la integración de Azure DevOps, puede automatizar sus despliegues de Capgo y garantizar actualizaciones consistentes y confiables para los usuarios de su aplicación móvil. # Integración con Bitbucket Pipelines > Aprenda cómo integrar las Actualizaciones en Vivo de Capgo con Bitbucket Pipelines para el despliegue automatizado de las actualizaciones de su aplicación. Integre las Actualizaciones en Vivo de Capgo con Bitbucket Pipelines para desplegar automáticamente las actualizaciones de su aplicación cada vez que realice cambios en el código. Esta guía cubre la configuración de flujos de trabajo automatizados de construcción, pruebas y despliegue. ## Requisitos previos [Section titled “Requisitos previos”](#requisitos-previos) Antes de configurar la integración con Bitbucket Pipelines, asegúrese de tener: * Una cuenta de Bitbucket con un repositorio * Una cuenta de Capgo con una aplicación configurada * Node.js y npm/yarn configurados en su proyecto ## Configuración de Bitbucket Pipelines [Section titled “Configuración de Bitbucket Pipelines”](#configuración-de-bitbucket-pipelines) ### Paso 1: Configurar Variables del repositorio [Section titled “Paso 1: Configurar Variables del repositorio”](#paso-1-configurar-variables-del-repositorio) Primero, Configurar las Variables necesarias en su repositorio de Bitbucket: 1. Navegue a su repositorio de Bitbucket 2. Vaya a **Repositorio Configuración** → **Pipelines** → **Repositorio Variables** 3. Agregue las siguientes Variables: | Nombre de Variable | Valor | Asegurada | | ------------------ | ------------------------ | --------- | | `CAPGO_TOKEN` | Su token de API de Capgo | ✅ Sí | Tip Obtenga su token de API de Capgo desde [console.capgo.app/apikeys](https://console.capgo.app/apikeys). El ID de su aplicación ya está configurado en su archivo `capacitor.config.ts`. ## Simple [Section titled “Simple”](#simple) Configuración básica que despliega a producción en cada push a la rama principal: ```yaml # bitbucket-pipelines.yml - Configuración simple image: node:22 pipelines: branches: main: - step: name: Construir y desplegar a producción script: - npm ci - npm run test - npm run build - npm install -g @capgo/cli - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel production artifacts: - dist/** ``` ## Avanzado [Section titled “Avanzado”](#avanzado) ### Despliegues de ramas de características [Section titled “Despliegues de ramas de características”](#despliegues-de-ramas-de-características) Despliegue ramas de características a canales de prueba para revisión y Pruebas: ```yaml # Despliegue de rama de características pipelines: branches: feature/*: - step: name: Desplegar rama de características script: - npm ci - npm run test - npm run build - BRANCH_NAME=$(echo $BITBUCKET_BRANCH | sed 's/[^a-zA-Z0-9-]/-/g') - CHANNEL_NAME="feature-$BRANCH_NAME" - npm install -g @capgo/cli - npx @capgo/cli channel create $CHANNEL_NAME --apikey $CAPGO_TOKEN || true - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel $CHANNEL_NAME artifacts: - dist/** ``` Tip **Pruebas con Canales**: Después de desplegar a un canal de características, puede probar la actualización en su aplicación configurándola para usar ese canal específico. Aprenda más sobre [configurar canales en su aplicación](/docs/live-updates/channels/#configuring-the-channel-in-your-app). ### Uso de cifrado [Section titled “Uso de cifrado”](#uso-de-cifrado) Si está usando la [función de cifrado de Capgo](/docs/live-updates/encryption/), deberá almacenar su clave privada de forma segura en su entorno de CI/CD. Después de [configurar las claves de cifrado](/docs/live-updates/encryption/#setting-up-encryption) localmente, agregue su clave privada a las Variables de Bitbucket: ```shell # Mostrar el contenido de su clave privada (copiar esta salida) cat .capgo_key_v2 ``` Agregue este contenido como `CAPGO_PRIVATE_KEY` en las Variables del repositorio de Bitbucket (marque como asegurada), luego úselo en pipelines: ```yaml # Desplegar con cifrado - step: name: Desplegar a Capgo con cifrado script: - npm install -g @capgo/cli - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --key-data-v2 "$CAPGO_PRIVATE_KEY" --channel production ``` Caution **Mejores prácticas de seguridad:** * Nunca confirme el archivo `.capgo_key_v2` en el control de versiones * Almacene la clave privada solo en la gestión segura de secretos de CI/CD * Usar claves diferentes para diferentes entornos ### Configuración multicanal [Section titled “Configuración multicanal”](#configuración-multicanal) Para obtener información completa sobre la configuración y gestión de múltiples canales de despliegue, consulte la [documentación de Canales](/docs/live-updates/channels/). Configuración completa con múltiples entornos y despliegues de Solicitud de extracción: ```yaml # bitbucket-pipelines.yml - Configuración avanzada multicanal image: node:22 definitions: steps: - step: &build-step name: Construir aplicación script: - npm ci - npm run test - npm run build artifacts: - dist/** - step: &deploy-step name: Desplegar a Capgo script: - npm install -g @capgo/cli - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel $CHANNEL_NAME pipelines: branches: main: - step: <<: *build-step - step: <<: *deploy-step name: Desplegar a producción deployment: production trigger: manual script: - export CHANNEL_NAME=production - npm install -g @capgo/cli - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel $CHANNEL_NAME develop: - step: <<: *build-step - step: <<: *deploy-step name: Desplegar a desarrollo deployment: development script: - export CHANNEL_NAME=development - npm install -g @capgo/cli - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel $CHANNEL_NAME pull-requests: '**': - step: <<: *build-step - step: name: Desplegar PR a canal de prueba script: - CHANNEL_NAME="pr-$BITBUCKET_PR_ID" - npm install -g @capgo/cli - npx @capgo/cli channel create $CHANNEL_NAME --apikey $CAPGO_TOKEN || true - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel $CHANNEL_NAME artifacts: - dist/** ``` ### Pipeline multi-entorno [Section titled “Pipeline multi-entorno”](#pipeline-multi-entorno) Para escenarios de despliegue complejos con entornos de staging y producción: ```yaml # Pipeline multi-entorno image: node:22 pipelines: branches: main: - step: name: Construir script: - npm ci - npm run test - npm run build artifacts: - dist/** - step: name: Desplegar a Staging deployment: staging script: - npm install -g @capgo/cli - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel staging - step: name: Desplegar a producción deployment: production trigger: manual script: - npm install -g @capgo/cli - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel production develop: - step: name: Construir y desplegar a desarrollo script: - npm ci - npm run test - npm run build - npm install -g @capgo/cli - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel development artifacts: - dist/** ``` ### Estrategia de despliegue basada en ramas [Section titled “Estrategia de despliegue basada en ramas”](#estrategia-de-despliegue-basada-en-ramas) Despliegue automáticamente diferentes ramas a los canales apropiados: ```yaml # Despliegue dinámico de canal image: node:22 definitions: scripts: - script: &determine-channel | if [ "$BITBUCKET_BRANCH" = "main" ]; then export CHANNEL_NAME="production" elif [ "$BITBUCKET_BRANCH" = "develop" ]; then export CHANNEL_NAME="staging" else export CHANNEL_NAME="development" fi echo "Desplegando al canal: $CHANNEL_NAME" pipelines: default: - step: name: Construir y desplegar script: - npm ci - npm run test - npm run build - *determine-channel - npm install -g @capgo/cli - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel $CHANNEL_NAME artifacts: - dist/** ``` ### Ejecución de pipeline en paralelo [Section titled “Ejecución de pipeline en paralelo”](#ejecución-de-pipeline-en-paralelo) Optimice los tiempos de construcción con pasos paralelos: ```yaml # Pipeline de ejecución paralela image: node:22 pipelines: branches: main: - parallel: - step: name: Ejecutar pruebas script: - npm ci - npm run test - step: name: Analizar código script: - npm ci - npm run lint - step: name: Construir aplicación script: - npm ci - npm run build artifacts: - dist/** - step: name: Desplegar a producción deployment: production script: - npm install -g @capgo/cli - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel production ``` ## Mejores prácticas de seguridad [Section titled “Mejores prácticas de seguridad”](#mejores-prácticas-de-seguridad) ### Variables del repositorio [Section titled “Variables del repositorio”](#variables-del-repositorio) 1. **Variables aseguradas**: Siempre marque los tokens de API como asegurados 2. **Variables de entorno**: Usar Variables específicas del despliegue cuando sea necesario 3. **Control de acceso**: Limite el acceso al repositorio a los miembros autorizados del equipo 4. **Rotación de tokens**: Rote regularmente sus tokens de API de Capgo ### Entornos de despliegue [Section titled “Entornos de despliegue”](#entornos-de-despliegue) Configurar entornos de despliegue para mayor seguridad: ```yaml # Despliegue con restricciones de entorno pipelines: branches: main: - step: name: Desplegar a producción deployment: production trigger: manual script: - npm install -g @capgo/cli - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel production ``` ## Monitoreo y notificaciones [Section titled “Monitoreo y notificaciones”](#monitoreo-y-notificaciones) ### Integración con Slack [Section titled “Integración con Slack”](#integración-con-slack) Agregue notificaciones de Slack a su pipeline: ```yaml # Pipeline con notificaciones de Slack pipelines: branches: main: - step: name: Construir y desplegar script: - npm ci - npm run test - npm run build - npm install -g @capgo/cli - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel production after-script: - | if [ $BITBUCKET_EXIT_CODE -eq 0 ]; then curl -X POST -H 'Content-type: application/json' \ --data '{"text":"✅ Despliegue de Capgo exitoso para '$BITBUCKET_BRANCH'"}' \ $SLACK_WEBHOOK_URL else curl -X POST -H 'Content-type: application/json' \ --data '{"text":"❌ Despliegue de Capgo fallido para '$BITBUCKET_BRANCH'"}' \ $SLACK_WEBHOOK_URL fi ``` ### Notificaciones por correo electrónico [Section titled “Notificaciones por correo electrónico”](#notificaciones-por-correo-electrónico) Configurar notificaciones por correo electrónico a través de las funciones integradas de Bitbucket o usando servicios externos: ```yaml # Paso de notificación por correo electrónico - step: name: Enviar notificación script: - | curl -X POST \ -H "Content-Type: application/json" \ -d '{ "to": "team@yourcompany.com", "subject": "Estado de despliegue de Capgo", "body": "Despliegue de '$BITBUCKET_BRANCH' completado con estado: '$BITBUCKET_EXIT_CODE'" }' \ $EMAIL_SERVICE_URL condition: result: [successful, failed] ``` ## Solución de problemas [Section titled “Solución de problemas”](#solución-de-problemas) ### Problemas comunes [Section titled “Problemas comunes”](#problemas-comunes) **El pipeline falla con “Capgo CLI no encontrado”:** ```yaml # Depurar instalación de CLI - step: name: Depurar CLI script: - npm install -g @capgo/cli - which capgo || echo "Capgo CLI no encontrado" - npx @capgo/cli --version ``` **Errores de autenticación:** ```yaml # Verificar configuración de token - step: name: Depurar autenticación script: - | if [ -z "$CAPGO_TOKEN" ]; then echo "CAPGO_TOKEN no está configurado" exit 1 fi echo "Longitud del token: ${#CAPGO_TOKEN}" ``` **Artefactos de construcción no encontrados:** ```yaml # Listar salidas de construcción - step: name: Depurar construcción script: - ls -la dist/ - find dist/ -type f -name "*.js" -o -name "*.html" ``` ### Depurar pipeline [Section titled “Depurar pipeline”](#depurar-pipeline) Agregue información de depuración para solucionar problemas: ```yaml # Pipeline de depuración pipelines: branches: main: - step: name: Información de depuración script: - echo "Rama: $BITBUCKET_BRANCH" - echo "Commit: $BITBUCKET_COMMIT" - echo "Build: $BITBUCKET_BUILD_NUMBER" - env | grep BITBUCKET_ | sort - step: name: Construir y desplegar script: - npm ci - npm run test - npm run build - npm install -g @capgo/cli - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel production ``` ### Validación de pipeline [Section titled “Validación de pipeline”](#validación-de-pipeline) Habilite la validación de pipeline para detectar errores de configuración: ```yaml # Habilitar validación de pipeline options: docker: true size: 2x pipelines: branches: main: - step: name: Validar pipeline script: - echo "Validación de pipeline exitosa" - step: name: Construir y desplegar script: # ... pasos de despliegue ``` ## Próximos pasos [Section titled “Próximos pasos”](#próximos-pasos) * Aprenda sobre [Canales](/docs/live-updates/channels/) para gestionar diferentes entornos de despliegue * Explore [Almacenamiento personalizado](/docs/live-updates/custom-storage/) para escenarios de despliegue avanzados * Configurar [Cifrado](/docs/live-updates/encryption/) para despliegues seguros * Configurar [Comportamiento de actualización](/docs/live-updates/update-behavior/) para personalizar cómo se aplican las actualizaciones Con la integración de Bitbucket Pipelines, puede automatizar sus despliegues de Capgo y garantizar actualizaciones consistentes y confiables para los usuarios de su aplicación móvil. # GitHub Integración de acciones > Aprenda cómo integrar Capgo Live Updates con GitHub Actions para la implementación automatizada de las actualizaciones de su aplicación. Integre Capgo Actualizaciones en vivo con GitHub Acciones para implementar automáticamente las actualizaciones de su aplicación cada vez que realice cambios en el código. Esta guía cubre la configuración de flujos de trabajo de compilación, prueba e implementación automatizados utilizando la poderosa plataforma CI/CD de GitHub. ## Requisitos previos [Section titled “Requisitos previos”](#requisitos-previos) Antes de configurar la integración de GitHub Acciones, asegúrese de tener: * Un repositorio GitHub con el código fuente de tu aplicación * Una cuenta Capgo con una aplicación configurada * Node.js y npm/hilo configurado en su proyecto * GitHub Acciones habilitadas para tu repositorio ## Configuración de secretos GitHub [Section titled “Configuración de secretos GitHub”](#configuración-de-secretos-github) ### Paso 1: Configurar los secretos del repositorio [Section titled “Paso 1: Configurar los secretos del repositorio”](#paso-1-configurar-los-secretos-del-repositorio) Configure los secretos necesarios en su repositorio GitHub: 1. Navegue a su repositorio GitHub 2. Vaya a **Configuración** → **Secretos y variables** → **Acciones** 3. Haga clic en **Nuevo secreto del repositorio** y agregue lo siguiente: | Nombre secreto | Valor | | -------------- | ------------------ | | `CAPGO_TOKEN` | Su token Capgo API | Tip Obtenga su token Capgo API de [console.capgo.app/apikeys](https://console.capgo.app/apikeys). Su ID de aplicación ya está configurada en su archivo `capacitor.config.ts`. ## Implementación de producción simple [Section titled “Implementación de producción simple”](#implementación-de-producción-simple) Comience con esta configuración básica que se implementa en producción en cada envío a la rama principal: ```yaml # Simple GitHub Actions Workflow for Capgo Live Updates name: Deploy to Capgo on: push: branches: - main jobs: deploy: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v6 - name: Setup Node.js uses: actions/setup-node@v6 with: node-version: '24' cache: 'npm' - name: Install, test and build run: | npm ci npm run test npm run build - name: Deploy to Capgo run: | npm install -g @capgo/cli npx @capgo/cli bundle upload --channel production env: CAPGO_TOKEN: ${{ secrets.CAPGO_TOKEN }} # For encrypted uploads, add: --key-data-v2 "${{ secrets.CAPGO_PRIVATE_KEY }}" ``` ## Configuración multicanal avanzada [Section titled “Configuración multicanal avanzada”](#configuración-multicanal-avanzada) ### Implementaciones de ramas de funciones [Section titled “Implementaciones de ramas de funciones”](#implementaciones-de-ramas-de-funciones) Deploy feature branches to temporary channels for testing: ```yaml # Feature branch deployment name: Deploy Feature Branch to Capgo on: push: branches: - 'feature/**' jobs: deploy-feature: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - uses: actions/setup-node@v6 with: node-version: '24' cache: 'npm' - run: | npm ci npm run test npm run build - name: Deploy to feature channel run: | CHANNEL_NAME=$(echo "${{ github.ref_name }}" | sed 's/[^a-zA-Z0-9]/-/g' | tr '[:upper:]' '[:lower:]') npm install -g @capgo/cli npx @capgo/cli channel create $CHANNEL_NAME --apikey ${{ secrets.CAPGO_TOKEN }} || true npx @capgo/cli bundle upload --apikey ${{ secrets.CAPGO_TOKEN }} --channel $CHANNEL_NAME ``` ### Usando cifrado [Section titled “Usando cifrado”](#usando-cifrado) Si está utilizando la [función de cifrado de Capgo](/docs/live-updates/encryption/), deberá almacenar su clave privada de forma segura en su entorno CI/CD. After [setting up encryption keys](/docs/live-updates/encryption/#setting-up-encryption) locally, add your private key to GitHub secrets: ```shell # Display your private key content (copy this output) cat .capgo_key_v2 ``` Add this content as `CAPGO_PRIVATE_KEY` in your GitHub repository secrets, then use it in workflows: ```yaml # Deploy with encryption - name: Deploy to Capgo with Encryption run: | npm install -g @capgo/cli npx @capgo/cli bundle upload --apikey ${{ secrets.CAPGO_TOKEN }} --key-data-v2 "${{ secrets.CAPGO_PRIVATE_KEY }}" --channel production ``` Caution **Mejores prácticas de seguridad:** * Never commit the `.capgo_key_v2` file to version control * Store the private key only in secure CI/CD secret management * Utilice diferentes claves para diferentes entornos. ### Configuración multicanal [Section titled “Configuración multicanal”](#configuración-multicanal) For comprehensive information about setting up and managing multiple deployment channels, see the [Channels documentation](/docs/live-updates/channels/). Complete workflow with development, pull requests, and production deployments: ```yaml # Complete multi-environment workflow name: Deploy to Capgo on: push: branches: [main, develop] pull_request: branches: [main, develop] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - uses: actions/setup-node@v6 with: node-version: '24' cache: 'npm' - run: | npm ci npm run test npm run build - uses: actions/upload-artifact@v4 with: name: dist path: dist/ deploy-development: if: github.ref == 'refs/heads/develop' needs: build runs-on: ubuntu-latest environment: development steps: - uses: actions/setup-node@v6 with: node-version: '24' - uses: actions/download-artifact@v4 with: name: dist path: dist/ - run: | npm install -g @capgo/cli npx @capgo/cli bundle upload --apikey ${{ secrets.CAPGO_TOKEN }} --channel development deploy-pr: if: github.event_name == 'pull_request' needs: build runs-on: ubuntu-latest steps: - uses: actions/setup-node@v6 with: node-version: '24' - uses: actions/download-artifact@v4 with: name: dist path: dist/ - name: Deploy to PR channel run: | CHANNEL_NAME="pr-${{ github.event.number }}" npm install -g @capgo/cli npx @capgo/cli channel create $CHANNEL_NAME --apikey ${{ secrets.CAPGO_TOKEN }} || true npx @capgo/cli bundle upload --apikey ${{ secrets.CAPGO_TOKEN }} --channel $CHANNEL_NAME - name: Comment PR uses: actions/github-script@v7 with: script: | github.rest.issues.createComment({ issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo, body: `🚀 This PR has been deployed to Capgo channel: \`pr-${{ github.event.number }}\`\n\nTo test this update in your app, configure it to use this channel. [Learn how to configure channels →](/docs/live-updates/channels/#configuring-the-channel-in-your-app)` }) deploy-production: if: github.ref == 'refs/heads/main' needs: build runs-on: ubuntu-latest environment: production steps: - uses: actions/setup-node@v6 with: node-version: '24' - uses: actions/download-artifact@v4 with: name: dist path: dist/ - run: | npm install -g @capgo/cli npx @capgo/cli bundle upload --apikey ${{ secrets.CAPGO_TOKEN }} --channel production ``` Tip **Prueba con canales**: después de implementarla en un canal de desarrollo o relaciones públicas, puede probar la actualización en su aplicación configurándola para usar ese canal específico. Learn more about [configuring channels in your app](/docs/live-updates/channels/#configuring-the-channel-in-your-app). ### Canales de funciones de limpieza [Section titled “Canales de funciones de limpieza”](#canales-de-funciones-de-limpieza) Automatically clean up feature channels when branches are deleted: ```yaml name: Cleanup Feature Channels on: delete: jobs: cleanup: runs-on: ubuntu-latest if: github.event.ref_type == 'branch' && startsWith(github.event.ref, 'feature/') steps: - uses: actions/setup-node@v6 with: node-version: '24' - name: Delete Capgo channel run: | CHANNEL_NAME=$(echo "${{ github.event.ref }}" | sed 's/[^a-zA-Z0-9]/-/g' | tr '[:upper:]' '[:lower:]') npm install -g @capgo/cli npx @capgo/cli channel delete $CHANNEL_NAME --apikey ${{ secrets.CAPGO_TOKEN }} || true ``` ## Seguridad y mejores prácticas [Section titled “Seguridad y mejores prácticas”](#seguridad-y-mejores-prácticas) ### Reglas de protección del medio ambiente [Section titled “Reglas de protección del medio ambiente”](#reglas-de-protección-del-medio-ambiente) Set up environment protection rules in GitHub: 1. Go to **Settings** → **Environments** in your repository 2. Create environments: `development`, `staging`, `production` 3. Para el entorno de producción, agregue: * **Required reviewers**: Add team members who must approve deployments * **Wait timer**: Add a delay before deployment (optional) * **Deployment branches**: Restrict to `main` branch only ### Gestión segura de secretos [Section titled “Gestión segura de secretos”](#gestión-segura-de-secretos) Utilice secretos específicos del entorno: ```yaml # Use different secrets per environment deploy-production: environment: production steps: - name: Deploy to Production run: | npx @capgo/cli bundle upload \ --apikey ${{ secrets.CAPGO_PROD_TOKEN }} \ --app ${{ secrets.CAPGO_PROD_APP_ID }} \ --channel production ``` ## Monitoreo y Notificaciones [Section titled “Monitoreo y Notificaciones”](#monitoreo-y-notificaciones) ### Integración floja [Section titled “Integración floja”](#integración-floja) Agrega notificaciones de Slack a tu flujo de trabajo: ```yaml name: Deploy with Notifications jobs: deploy: runs-on: ubuntu-latest steps: # ... deployment steps - name: Notify Slack on Success if: success() uses: 8398a7/action-slack@v3 with: status: success text: '✅ Capgo deployment successful!' fields: repo,message,commit,author,action,eventName,ref,workflow env: SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} - name: Notify Slack on Failure if: failure() uses: 8398a7/action-slack@v3 with: status: failure text: '❌ Capgo deployment failed!' fields: repo,message,commit,author,action,eventName,ref,workflow env: SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} ``` ### Integración de discordia [Section titled “Integración de discordia”](#integración-de-discordia) Enviar notificaciones a Discord: ```yaml - name: Discord notification if: always() uses: Ilshidur/action-discord@master with: args: | Capgo deployment ${{ job.status }}! App: ${{ secrets.CAPGO_APP_ID }} Channel: ${{ github.ref_name }} Commit: ${{ github.sha }} env: DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }} ``` ### Notificaciones por correo electrónico [Section titled “Notificaciones por correo electrónico”](#notificaciones-por-correo-electrónico) Configurar notificaciones por correo electrónico: ```yaml - name: Send email notification if: failure() uses: dawidd6/action-send-mail@v3 with: server_address: smtp.gmail.com server_port: 465 username: ${{ secrets.EMAIL_USERNAME }} password: ${{ secrets.EMAIL_PASSWORD }} subject: 'Capgo Deployment Failed - ${{ github.repository }}' to: team@yourcompany.com from: ci-cd@yourcompany.com body: | Deployment failed for ${{ github.repository }} Branch: ${{ github.ref_name }} Commit: ${{ github.sha }} Workflow: ${{ github.workflow }} ``` ## Solución de problemas [Section titled “Solución de problemas”](#solución-de-problemas) ### Flujo de trabajo de depuración [Section titled “Flujo de trabajo de depuración”](#flujo-de-trabajo-de-depuración) Agregue pasos de depuración para solucionar problemas: ```yaml - name: Debug environment run: | echo "Node version: $(node --version)" echo "NPM version: $(npm --version)" echo "Working directory: $(pwd)" echo "Files in dist/: $(ls -la dist/ || echo 'No dist directory')" echo "Environment variables:" env | grep -E "(GITHUB_|CAPGO_)" | sort - name: Test Capgo CLI run: | npx @capgo/cli --version npx @capgo/cli app debug --apikey ${{ secrets.CAPGO_TOKEN }} --app ${{ secrets.CAPGO_APP_ID }} ``` ### Problemas comunes y soluciones [Section titled “Problemas comunes y soluciones”](#problemas-comunes-y-soluciones) **El flujo de trabajo falla con “CAPGO\_TOKEN no encontrado”:** ```yaml - name: Verify secrets run: | if [ -z "${{ secrets.CAPGO_TOKEN }}" ]; then echo "ERROR: CAPGO_TOKEN secret is not set" exit 1 fi echo "CAPGO_TOKEN is set (length: ${#CAPGO_TOKEN})" env: CAPGO_TOKEN: ${{ secrets.CAPGO_TOKEN }} ``` **Artefactos de compilación no encontrados:** ```yaml - name: Debug artifacts run: | echo "Checking for build artifacts..." ls -la dist/ || echo "No dist directory found" find . -name "*.js" -o -name "*.html" | head -10 ``` **Problemas de conectividad de red:** ```yaml - name: Test connectivity run: | ping -c 3 api.capgo.io || echo "Ping failed" curl -I https://api.capgo.io/health || echo "Health check failed" ``` ## Flujos de trabajo reutilizables [Section titled “Flujos de trabajo reutilizables”](#flujos-de-trabajo-reutilizables) Create reusable workflows for consistency across projects: .github/workflows/reusable-capgo-deploy.yml ```yaml name: Reusable Capgo Deploy on: workflow_call: inputs: environment: required: true type: string channel: required: true type: string secrets: CAPGO_TOKEN: required: true CAPGO_APP_ID: required: true jobs: deploy: runs-on: ubuntu-latest environment: ${{ inputs.environment }} steps: - uses: actions/checkout@v6 - name: Setup Node.js uses: actions/setup-node@v6 with: node-version: '24' cache: 'npm' - name: Install and build run: | npm ci npm run build - name: Deploy to Capgo run: | npm install -g @capgo/cli npx @capgo/cli bundle upload \ --apikey ${{ secrets.CAPGO_TOKEN }} \ --app ${{ secrets.CAPGO_APP_ID }} \ --channel ${{ inputs.channel }} ``` Utilice el flujo de trabajo reutilizable: .github/workflows/deploy.yml ```yaml name: Deploy App on: push: branches: [main, develop] jobs: deploy-dev: if: github.ref == 'refs/heads/develop' uses: ./.github/workflows/reusable-capgo-deploy.yml with: environment: development channel: development secrets: CAPGO_TOKEN: ${{ secrets.CAPGO_TOKEN }} CAPGO_APP_ID: ${{ secrets.CAPGO_APP_ID }} deploy-prod: if: github.ref == 'refs/heads/main' uses: ./.github/workflows/reusable-capgo-deploy.yml with: environment: production channel: production secrets: CAPGO_TOKEN: ${{ secrets.CAPGO_TOKEN }} CAPGO_APP_ID: ${{ secrets.CAPGO_APP_ID }} ``` ## Próximos pasos [Section titled “Próximos pasos”](#próximos-pasos) * Learn about [Channels](/docs/live-updates/channels/) to manage different deployment environments * Explore [Custom Storage](/docs/live-updates/custom-storage/) for advanced deployment scenarios * Set up [Encryption](/docs/live-updates/encryption/) for secure deployments * Configure [Update Behavior](/docs/live-updates/update-behavior/) to customize how updates are applied Con la integración de acciones de GitHub, puede aprovechar la poderosa plataforma CI/CD de GitHub para crear flujos de trabajo de implementación sofisticados con funciones integradas de seguridad, monitoreo y colaboración para sus actualizaciones en vivo de Capgo. # Integración con GitLab CI/CD > Aprenda cómo integrar las Actualizaciones en Vivo de Capgo con GitLab CI/CD para el despliegue automatizado de las actualizaciones de su aplicación. Integre las Actualizaciones en Vivo de Capgo con GitLab CI/CD para desplegar automáticamente las actualizaciones de su aplicación cada vez que realice cambios en el código. Esta guía cubre la configuración de flujos de trabajo automatizados de construcción, pruebas y despliegue. ## Requisitos previos [Section titled “Requisitos previos”](#requisitos-previos) Antes de configurar la integración con GitLab CI/CD, asegúrese de tener: * Una cuenta de GitLab con un repositorio de proyecto * Una cuenta de Capgo con una aplicación configurada * Node.js y npm/yarn configurados en su proyecto ## Configuración de GitLab CI/CD [Section titled “Configuración de GitLab CI/CD”](#configuración-de-gitlab-cicd) ### Paso 1: Configurar Variables de entorno [Section titled “Paso 1: Configurar Variables de entorno”](#paso-1-configurar-variables-de-entorno) Primero, Configurar las Variables necesarias en su proyecto de GitLab: 1. Navegue a su proyecto de GitLab 2. Vaya a **Configuración** → **CI/CD** → **Variables** 3. Agregue las siguientes Variables: | Nombre de Variable | Valor | Protegida | Enmascarada | | ------------------ | ------------------------ | --------- | ----------- | | `CAPGO_TOKEN` | Su token de API de Capgo | ✅ Sí | ✅ Sí | Tip Obtenga su token de API de Capgo desde [console.capgo.app/apikeys](https://console.capgo.app/apikeys). El ID de su aplicación ya está configurado en su archivo `capacitor.config.ts`. ## Simple [Section titled “Simple”](#simple) Configuración básica que despliega a producción en cada push a la rama principal: ```yaml # .gitlab-ci.yml - Configuración simple image: node:22 stages: - build - deploy variables: npm_config_cache: "$CI_PROJECT_DIR/.npm" build: stage: build script: - npm ci - npm run test - npm run build artifacts: paths: - dist/ expire_in: 1 hour only: - main deploy_production: stage: deploy script: - npm install -g @capgo/cli - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel production # Para cargas cifradas, agregar: --key-data-v2 "$CAPGO_PRIVATE_KEY" dependencies: - build only: - main ``` ## Avanzado [Section titled “Avanzado”](#avanzado) ### Despliegues de ramas de características [Section titled “Despliegues de ramas de características”](#despliegues-de-ramas-de-características) Despliegue ramas de características a canales de prueba para revisión y Pruebas: ```yaml # Despliegue de rama de características deploy_feature: stage: deploy script: - npm install -g @capgo/cli - CHANNEL_NAME="feature-$(echo $CI_COMMIT_REF_NAME | sed 's/[^a-zA-Z0-9-]/-/g')" - npx @capgo/cli channel create $CHANNEL_NAME --apikey $CAPGO_TOKEN || true - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel $CHANNEL_NAME dependencies: - build only: - /^feature\/.*$/ environment: name: feature/$CI_COMMIT_REF_NAME url: https://your-app.com/channels/$CHANNEL_NAME ``` Tip **Pruebas con Canales**: Después de desplegar a un canal de características, puede probar la actualización en su aplicación configurándola para usar ese canal específico. Aprenda más sobre [configurar canales en su aplicación](/docs/live-updates/channels/#configuring-the-channel-in-your-app). ### Uso de cifrado [Section titled “Uso de cifrado”](#uso-de-cifrado) Si está usando la [función de cifrado de Capgo](/docs/live-updates/encryption/), deberá almacenar su clave privada de forma segura en su entorno de CI/CD. Después de [configurar las claves de cifrado](/docs/live-updates/encryption/#setting-up-encryption) localmente, agregue su clave privada a las Variables de GitLab: ```shell # Mostrar el contenido de su clave privada (copiar esta salida) cat .capgo_key_v2 ``` Agregue este contenido como `CAPGO_PRIVATE_KEY` en las Variables del proyecto de GitLab (marque como protegida y enmascarada), luego úselo en pipelines: ```yaml # Desplegar con cifrado deploy_production: script: - npm install -g @capgo/cli - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --key-data-v2 "$CAPGO_PRIVATE_KEY" --channel production ``` Caution **Mejores prácticas de seguridad:** * Nunca confirme el archivo `.capgo_key_v2` en el control de versiones * Almacene la clave privada solo en la gestión segura de secretos de CI/CD * Usar claves diferentes para diferentes entornos ### Configuración multicanal [Section titled “Configuración multicanal”](#configuración-multicanal) Para obtener información completa sobre la configuración y gestión de múltiples canales de despliegue, consulte la [documentación de Canales](/docs/live-updates/channels/). Configuración completa con múltiples entornos y despliegues de merge request: ```yaml # .gitlab-ci.yml - Configuración avanzada multicanal image: node:22 stages: - build - deploy variables: npm_config_cache: "$CI_PROJECT_DIR/.npm" # Etapa de construcción build: stage: build script: - npm ci - npm run test - npm run build artifacts: paths: - dist/ expire_in: 24 hours # Desplegar a canal de desarrollo deploy_development: stage: deploy script: - npm install -g @capgo/cli - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel development dependencies: - build only: - develop environment: name: development # Desplegar merge requests a canales de prueba deploy_mr: stage: deploy script: - npm install -g @capgo/cli - CHANNEL_NAME="mr-$CI_MERGE_REQUEST_IID" - npx @capgo/cli channel create $CHANNEL_NAME --apikey $CAPGO_TOKEN || true - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel $CHANNEL_NAME dependencies: - build only: - merge_requests environment: name: review/$CI_MERGE_REQUEST_IID url: https://your-app.com/channels/mr-$CI_MERGE_REQUEST_IID on_stop: cleanup_mr # Limpiar canales de MR cuando se cierra el MR cleanup_mr: stage: deploy script: - npm install -g @capgo/cli - npx @capgo/cli channel delete mr-$CI_MERGE_REQUEST_IID --apikey $CAPGO_TOKEN || true when: manual environment: name: review/$CI_MERGE_REQUEST_IID action: stop only: - merge_requests # Desplegar a staging deploy_staging: stage: deploy script: - npm install -g @capgo/cli - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel staging dependencies: - build only: - develop environment: name: staging # Desplegar a producción deploy_production: stage: deploy script: - npm install -g @capgo/cli - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel production dependencies: - build only: - main environment: name: production ``` ### Multi-entorno con aprobación manual [Section titled “Multi-entorno con aprobación manual”](#multi-entorno-con-aprobación-manual) Para despliegues de producción que requieren aprobación manual: ```yaml deploy_production: stage: deploy script: - npm install -g @capgo/cli - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel production dependencies: - build only: - main when: manual environment: name: production ``` ### Estrategia de despliegue basada en ramas [Section titled “Estrategia de despliegue basada en ramas”](#estrategia-de-despliegue-basada-en-ramas) Despliegue diferentes ramas a los canales apropiados automáticamente: ```yaml # Despliegue dinámico de canal basado en rama deploy: stage: deploy script: - npm install -g @capgo/cli - | if [ "$CI_COMMIT_REF_NAME" = "main" ]; then CHANNEL="production" elif [ "$CI_COMMIT_REF_NAME" = "develop" ]; then CHANNEL="staging" else CHANNEL="development" fi - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel $CHANNEL dependencies: - build environment: name: $CHANNEL ``` ## Mejores prácticas de seguridad [Section titled “Mejores prácticas de seguridad”](#mejores-prácticas-de-seguridad) ### Variables protegidas [Section titled “Variables protegidas”](#variables-protegidas) 1. **Marcar Variables sensibles**: Siempre marque los tokens de API como protegidos y enmascarados 2. **Protección de rama**: Usar Variables protegidas para despliegues de producción 3. **Control de acceso**: Limite el acceso de Variables solo a mantenedores 4. **Rotación regular**: Rote los tokens de API regularmente ### Configuración segura de pipeline [Section titled “Configuración segura de pipeline”](#configuración-segura-de-pipeline) ```yaml # Usar variables protegidas para producción deploy_production: stage: deploy script: - npm install -g @capgo/cli - npx @capgo/cli bundle upload --apikey $CAPGO_TOKEN --channel production only: refs: - main variables: - $CI_COMMIT_REF_PROTECTED == "true" ``` ## Monitoreo y notificaciones [Section titled “Monitoreo y notificaciones”](#monitoreo-y-notificaciones) ### Integración con Slack [Section titled “Integración con Slack”](#integración-con-slack) Agregue notificaciones de Slack a su pipeline: ```yaml notify_success: stage: .post image: alpine:latest before_script: - apk add --no-cache curl script: - | curl -X POST -H 'Content-type: application/json' \ --data '{"text":"✅ Despliegue de Capgo exitoso para '"$CI_COMMIT_REF_NAME"'"}' \ $SLACK_WEBHOOK_URL when: on_success notify_failure: stage: .post image: alpine:latest before_script: - apk add --no-cache curl script: - | curl -X POST -H 'Content-type: application/json' \ --data '{"text":"❌ Despliegue de Capgo fallido para '"$CI_COMMIT_REF_NAME"'"}' \ $SLACK_WEBHOOK_URL when: on_failure ``` ### Notificaciones por correo electrónico [Section titled “Notificaciones por correo electrónico”](#notificaciones-por-correo-electrónico) Configurar notificaciones por correo electrónico en la configuración de su proyecto de GitLab o Usar la API: ```yaml notify_email: stage: .post script: - | curl --request POST \ --header "PRIVATE-TOKEN: $GITLAB_API_TOKEN" \ --form "to=team@yourcompany.com" \ --form "subject=Estado de despliegue de Capgo" \ --form "body=Despliegue de $CI_COMMIT_REF_NAME completado con estado: $CI_JOB_STATUS" \ "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/emails" when: always ``` ## Solución de problemas [Section titled “Solución de problemas”](#solución-de-problemas) ### Problemas comunes [Section titled “Problemas comunes”](#problemas-comunes) **El pipeline falla con “Capgo CLI no encontrado”:** ```yaml # Depurar instalación de CLI debug_cli: script: - npm install -g @capgo/cli - which capgo || echo "Capgo CLI no encontrado" - npx @capgo/cli --version ``` **Errores de autenticación:** ```yaml # Verificar configuración de token debug_auth: script: - | if [ -z "$CAPGO_TOKEN" ]; then echo "CAPGO_TOKEN no está configurado" exit 1 fi echo "Longitud del token: ${#CAPGO_TOKEN}" ``` **Artefactos de construcción no encontrados:** ```yaml # Listar salidas de construcción debug_build: script: - ls -la dist/ - find dist/ -type f -name "*.js" -o -name "*.html" ``` ### Depurar pipeline [Section titled “Depurar pipeline”](#depurar-pipeline) Agregue información de depuración para solucionar problemas: ```yaml debug: stage: build script: - echo "Rama: $CI_COMMIT_REF_NAME" - echo "Commit: $CI_COMMIT_SHA" - echo "Build: $CI_PIPELINE_ID" - env | grep CI_ | sort only: - branches ``` ## Próximos pasos [Section titled “Próximos pasos”](#próximos-pasos) * Aprenda sobre [Canales](/docs/live-updates/channels/) para gestionar diferentes entornos de despliegue * Explore [Almacenamiento personalizado](/docs/live-updates/custom-storage/) para escenarios de despliegue avanzados * Configurar [Cifrado](/docs/live-updates/encryption/) para despliegues seguros * Configurar [Comportamiento de actualización](/docs/live-updates/update-behavior/) para personalizar cómo se aplican las actualizaciones Con la integración de GitLab CI/CD, puede automatizar sus despliegues de Capgo y garantizar actualizaciones consistentes y confiables para los usuarios de su aplicación móvil. # Reversiones > Aprende cómo gestionar reversiones en Capgo, permitiéndote revertir a versiones anteriores de la aplicación sin problemas cuando sea necesario. Mientras que las actualizaciones en vivo de Capgo te permiten entregar mejoras y correcciones rápidamente a tus usuarios, puede haber situaciones en las que necesites revertir a una versión anterior de tu aplicación. Quizás una nueva actualización introdujo un problema crítico inesperado, o tal vez quieres revertir un cambio específico mientras trabajas en una corrección. Capgo proporciona varias formas de gestionar las compilaciones de un canal y controlar la versión de tu aplicación que los usuarios reciben, incluyendo tanto opciones de reversión manual como mecanismos de seguridad automáticos. ## Protección de Reversión Automática [Section titled “Protección de Reversión Automática”](#protección-de-reversión-automática) Capgo incluye un mecanismo de seguridad incorporado para proteger a tus usuarios de actualizaciones rotas. Si ocurre un Error de JavaScript antes de que se llame al método `notifyAppReady()`, el Plugin revertirá automáticamente a la versión anterior que funciona. ### Cómo Funciona la Reversión Automática [Section titled “Cómo Funciona la Reversión Automática”](#cómo-funciona-la-reversión-automática) Cuando se descarga y aplica una nueva actualización, Capgo espera que tu aplicación llame a `notifyAppReady()` dentro de un período de tiempo configurable para confirmar que la actualización se cargó exitosamente. Este método señala que: * El Paquete JavaScript se cargó sin errores críticos * La funcionalidad principal de tu aplicación está funcionando * La actualización es segura de mantener Si `notifyAppReady()` no se llama debido a un fallo de JavaScript o Error crítico, Capgo: 1. Detectará que la actualización falló al inicializarse correctamente 2. Revertirá automáticamente al Paquete anterior que funciona 3. Marcará la actualización problemática como fallida para prevenir que se aplique nuevamente Tip Asegúrate de llamar a `notifyAppReady()` en el código de inicialización de tu aplicación después de que tus componentes principales se hayan cargado exitosamente. Esto asegura que la protección de reversión automática funcione como se pretende. ```javascript import { CapacitorUpdater } from '@capgo/capacitor-updater' // Llama a esto después de que tu aplicación se haya inicializado exitosamente await CapacitorUpdater.notifyAppReady() ``` Esta protección automática ayuda a asegurar que incluso si accidentalmente envías una actualización rota, tus usuarios no quedarán atascados con una aplicación no funcional. ### Configurar el Tiempo de Espera [Section titled “Configurar el Tiempo de Espera”](#configurar-el-tiempo-de-espera) Puedes configurar cuánto tiempo espera Capgo a que se llame `notifyAppReady()` estableciendo el `appReadyTimeout` en tu configuración de Capacitor: ```json { "plugins": { "CapacitorUpdater": { "appReadyTimeout": 10000 } } } ``` El valor de `appReadyTimeout` se especifica en milisegundos. El tiempo de espera predeterminado es típicamente 10 segundos, pero puedes ajustarlo según los requisitos de inicialización de tu aplicación. Si tu aplicación tarda más en cargar debido a procesos de inicialización complejos, podrías querer aumentar este valor. ## Revertir a un Paquete Anterior [Section titled “Revertir a un Paquete Anterior”](#revertir-a-un-paquete-anterior) Cada vez que subes una nueva compilación y la asignas a un canal, Capgo mantiene un historial de esas compilaciones. Si necesitas revertir una actualización específica, puedes seleccionar una de estas compilaciones anteriores para volver a implementar en el canal. ![Interfaz de reversión](/rollback_ui.webp) La forma principal de revertir es a través de la interfaz de reversión, que se encuentra en la 4ta pestaña (Historial) cuando visualizas un canal en el Panel de Capgo. Esta pestaña proporciona una vista completa de todas las compilaciones disponibles para el canal, permitiéndote seleccionar y revertir fácilmente a cualquier versión anterior. Para revertir usando la pestaña Historial: 1. Inicia sesión en el [Panel de Capgo](https://app.capgo.io). 2. Navega a la sección “Canales”. 3. Haz clic en el nombre del canal que quieres revertir. 4. Ve a la 4ta pestaña (Historial) en la vista del canal. 5. Encuentra la compilación a la que quieres revertir en el historial de compilaciones. 6. Selecciona esa compilación para hacerla la compilación activa para el canal. 7. Confirma que quieres revertir a esta compilación. ### Método Alternativo: Usar el Ícono de Corona [Section titled “Método Alternativo: Usar el Ícono de Corona”](#método-alternativo-usar-el-ícono-de-corona) Como segunda forma, también puedes revertir directamente desde la primera pestaña haciendo clic en el ícono de corona junto a cualquier compilación en el historial de compilaciones del canal: 1. En la primera pestaña de la vista del canal, encuentra la compilación a la que quieres revertir. 2. Haz clic en el ícono de corona junto a esa compilación para hacerla la compilación activa para el canal. ![Opciones de gestión de canal](/select_bundle.webp) 3. Confirma que quieres revertir a esta compilación. Note Revertir a una compilación anterior solo afecta al canal seleccionado. Si tienes múltiples canales (por ejemplo, Production, Staging, etc.), necesitarás repetir el proceso de reversión para cada canal afectado. Después de revertir, los dispositivos configurados para escuchar el canal actualizado recibirán la compilación anterior la próxima vez que verifiquen una actualización. La compilación revertida se tratará como una nueva actualización, por lo que se aplican el flujo y condiciones de actualización usuales. ## Desvincular un Canal [Section titled “Desvincular un Canal”](#desvincular-un-canal) Si quieres detener temporalmente las actualizaciones en un canal mientras investigas un problema, puedes desvincular el canal de su compilación actual. Para desvincular un canal: 1. Navega al canal en el Panel de Capgo. 2. Haz clic en el botón “Unlink” junto a la compilación actual. 3. Confirma que quieres desvincular el canal. Una vez que un canal está desvinculado, no distribuirá ninguna nueva actualización. Los dispositivos configurados para ese canal permanecerán en su compilación actual hasta que el canal se vincule a una compilación nuevamente. Esto es útil si has identificado un problema con una actualización pero aún no estás seguro de a qué compilación quieres revertir. Desvincular el canal te da tiempo para investigar sin enviar más actualizaciones. ## Forzar el Paquete Incorporado [Section titled “Forzar el Paquete Incorporado”](#forzar-el-paquete-incorporado) En situaciones más severas, podrías querer revertir todos los dispositivos en un canal de vuelta a la compilación web que originalmente se empaquetó con el binario nativo de tu aplicación. Esto se conoce como el “Paquete incorporado”. Para forzar el Paquete incorporado en un canal: 1. Navega al canal en el Panel de Capgo. 2. Haz clic en el botón “Built-in Paquete”. 3. Confirma que quieres forzar el Paquete incorporado. Cuando fuerzas el Paquete incorporado, todos los dispositivos configurados para ese canal revertirán de vuelta a la compilación web empaquetada original en su próxima verificación de actualización. Esto sucede independientemente de en qué compilación estén actualmente. Esta es una opción de reversión más agresiva que revertir a una compilación anterior específica, ya que descarta todas las actualizaciones en vivo lanzadas desde que la aplicación se publicó por última vez en las tiendas de aplicaciones. Caution Ten cuidado al forzar el Paquete incorporado, ya que afectará a todos los dispositivos en el canal. Asegúrate de haber considerado el impacto y tener un plan para avanzar antes de tomar esta acción. ## Monitorear y Responder a Problemas [Section titled “Monitorear y Responder a Problemas”](#monitorear-y-responder-a-problemas) Para detectar problemas rápidamente y minimizar el impacto de actualizaciones problemáticas, es importante tener un plan para monitorear tus lanzamientos y responder a problemas. Algunas estrategias incluyen: * Monitorear reportes de fallas y comentarios de usuarios inmediatamente después de lanzar una actualización * Usar lanzamientos escalonados o un sistema de canales por etapas para probar actualizaciones en un grupo más pequeño antes del lanzamiento amplio * Tener un proceso de decisión claro para cuándo revertir, desvincular o forzar el Paquete incorporado, y quién tiene la autoridad para hacerlo * Comunicar a los usuarios sobre el problema y la resolución, si es apropiado Al combinar un monitoreo cuidadoso con la capacidad de gestionar rápidamente actualizaciones problemáticas, puedes entregar una experiencia de aplicación en mejora continua mientras minimizas interrupciones para tus usuarios. # Comportamiento de Actualización Cuando lanzas una actualización de tu aplicación Capgo, probablemente quieres que tus usuarios reciban esa actualización lo antes posible. Pero también no quieres interrumpir su experiencia forzándolos a esperar una descarga o reiniciar la aplicación en medio de una sesión. El comportamiento de actualización de Capgo está diseñado para lograr un equilibrio entre entregar actualizaciones rápidamente y minimizar la interrupción para tus usuarios. ## Flujo de Actualización Predeterminado [Section titled “Flujo de Actualización Predeterminado”](#flujo-de-actualización-predeterminado) Por defecto, así es como Capgo maneja las actualizaciones de la aplicación: 1. Al iniciar la aplicación, el Plugin de Capgo verifica si hay una nueva actualización disponible 2. Si se encuentra una actualización, se descarga en segundo plano mientras el usuario continúa usando la versión actual de la aplicación 3. Una vez que se completa la descarga, Capgo espera a que el usuario envíe la aplicación a segundo plano o la cierre por completo 4. Cuando el usuario vuelva a iniciar la aplicación, estará ejecutando la versión actualizada Este flujo asegura que los usuarios siempre estén ejecutando la última versión de tu aplicación, sin ser interrumpidos por avisos de actualización o forzados a esperar descargas. Tip Capgo también busca actualizaciones cuando la aplicación se reanuda desde segundo plano, por lo que los usuarios recibirán actualizaciones incluso si no cierran completamente la aplicación ## ¿Por qué Este Enfoque? [Section titled “¿Por qué Este Enfoque?”](#por-qué-este-enfoque) Aplicar actualizaciones en un evento de segundo plano o cierre tiene varios beneficios clave para la experiencia del usuario: * Los usuarios no son interrumpidos por avisos de actualización ni forzados a esperar descargas en medio de una sesión * Las actualizaciones se aplican sin problemas entre sesiones, por lo que la experiencia de iniciar la aplicación siempre es nueva * Puedes entregar actualizaciones frecuentemente sin preocuparte por interrumpir a los usuarios activos La principal desventaja es que si un usuario envía a segundo plano y reanuda rápidamente tu aplicación, puede perder cualquier estado no guardado ya que la actualización se aplicó entre esas acciones. Para mitigar esto, recomendamos: * Guardar el estado frecuentemente y restaurarlo correctamente cuando la aplicación se reanude * Evitar actualizaciones muy frecuentes que modifiquen grandes partes del estado de la aplicación * Considerar personalizar el comportamiento de actualización para flujos sensibles (ver abajo) ## Personalizando Cuándo se Aplican las Actualizaciones [Section titled “Personalizando Cuándo se Aplican las Actualizaciones”](#personalizando-cuándo-se-aplican-las-actualizaciones) En algunos casos, es posible que desees más control sobre exactamente cuándo se aplica una actualización. Por ejemplo, podrías querer asegurarte de que un usuario Completo un flujo en progreso antes de actualizar, o coordinar una actualización de la aplicación con un cambio del lado del servidor. Capgo proporciona una función `setDelay` que te permite especificar condiciones que deben cumplirse antes de que se instale una actualización: ```typescript import { CapacitorUpdater } from '@capgo/capacitor-updater'; await CapacitorUpdater.setMultiDelay({ delayConditions: [ { kind: 'date', value: '2023-06-01T00:00:00.000Z', }, { kind: 'background', value: '60000', }, ], }); ``` Este ejemplo retrasaría la instalación de una actualización hasta después del 1 de junio de 2023 Y la aplicación haya estado en segundo plano durante al menos 60 segundos. Las condiciones de retraso disponibles son: * `date`: Esperar hasta después de una fecha/hora específica para aplicar la actualización * `background`: Esperar una duración mínima después de que la aplicación esté en segundo plano para aplicar la actualización * `nativeVersion`: Esperar a que se instale un binario nativo con una versión mínima antes de aplicar la actualización * `kill`: Esperar hasta el próximo evento de cierre de la aplicación para aplicar la actualización Puedes mezclar y combinar estas condiciones para controlar precisamente cuándo se instala una actualización. Danger Ten en cuenta que la condición `kill` actualmente activa la actualización después del primer evento de cierre, no el próximo evento de segundo plano como las otras condiciones. Esta inconsistencia se corregirá en una versión futura ## Aplicando Actualizaciones Inmediatamente [Section titled “Aplicando Actualizaciones Inmediatamente”](#aplicando-actualizaciones-inmediatamente) Para actualizaciones críticas o aplicaciones con estado muy simple, es posible que desees aplicar una actualización tan pronto como se descargue, sin esperar un evento de segundo plano o cierre. Capgo admite esto a través de la opción de configuración `directUpdate`. Recomendado: Usar Delta Actualizaciones con Direct Actualizar Al usar `directUpdate`, **recomendamos encarecidamente** habilitar [Delta Updates](/docs/live-updates/differentials/) para minimizar los tiempos de descarga y mejorar la experiencia del usuario. Las Delta Actualizaciones solo descargan archivos modificados en lugar del Paquete completo, lo cual es especialmente importante cuando las actualizaciones se aplican inmediatamente mientras los usuarios están usando activamente tu aplicación. **Por qué esto es importante para Direct Actualizaciones:** * **Actualizaciones más rápidas**: Descargas más pequeñas significan que las actualizaciones se completan rápidamente, reduciendo el tiempo en que los usuarios ven pantallas de carga * **Mejor experiencia móvil**: Los usuarios en redes celulares o conexiones más lentas no enfrentarán largos tiempos de espera * **Menor uso de ancho de banda**: Solo se descargan archivos modificados, ahorrando datos tanto para ti como para tus usuarios Para habilitar Delta Actualizaciones, simplemente usa el flag `--partial` al subir Paquetes: ```shell npx @capgo/cli@latest bundle upload --partial ``` Aprende más en la [documentación de Delta Updates](/docs/live-updates/differentials/). `directUpdate` se establece en tu archivo `capacitor.config.ts`, no en código JavaScript: ```typescript import { CapacitorConfig } from '@capacitor/cli'; const config: CapacitorConfig = { plugins: { CapacitorUpdater: { autoUpdate: true, directUpdate: true, keepUrlPathAfterReload: true, }, }, }; export default config; ``` Con `directUpdate` habilitado, Capgo aplicará inmediatamente una actualización tan pronto como se Completo la descarga, incluso si el usuario está usando activamente la aplicación. Ten en cuenta que debido a que `directUpdate` es una configuración nativa, requiere un manejo adicional en tu código JavaScript. Cuando uses `directUpdate`, necesitas escuchar el evento `appReady` y ocultar la pantalla de inicio de tu aplicación en respuesta: ```js import { CapacitorUpdater } from '@capgo/capacitor-updater'; import { SplashScreen } from '@capacitor/splash-screen'; CapacitorUpdater.addListener('appReady', () => { // Ocultar pantalla de inicio SplashScreen.hide(); }); CapacitorUpdater.notifyAppReady(); ``` El evento `appReady` se dispara una vez que la aplicación ha terminado de inicializarse y aplicar cualquier actualización pendiente. Este es el punto en el que es seguro mostrar la interfaz de usuario de tu aplicación, ya que asegura que el usuario verá la última versión. Además de manejar el evento `appReady`, recomendamos establecer la opción de configuración `keepUrlPathAfterReload` en `true` cuando se Usar `directUpdate`. Esto preserva la ruta URL actual cuando la aplicación se recarga debido a una actualización, ayudando a mantener la ubicación del usuario en la aplicación y reduciendo la desorientación. Si no manejas el evento `appReady` y estableces `keepUrlPathAfterReload` cuando uses `directUpdate`, el usuario puede ver brevemente una versión obsoleta de la aplicación, ser llevado de vuelta a la ruta inicial, o ver un parpadeo mientras se aplica la actualización. Usar `directUpdate` puede ser útil para entregar correcciones de errores críticos o parches de seguridad, pero viene con algunos compromisos: * El usuario puede ver un breve parpadeo o estado de carga mientras se aplica la actualización si no manejas correctamente el evento `appReady` * Si la actualización modifica el estado o la interfaz de usuario de la aplicación, el usuario puede ver un cambio disruptivo en medio de una sesión * La ubicación del usuario en la aplicación puede perderse si `keepUrlPathAfterReload` no está establecido, potencialmente desorientándolo * Necesitarás manejar cuidadosamente el guardado y restauración del estado para asegurar una transición suave Si habilitas `directUpdate`, recomendamos: * Manejar el evento `appReady` para controlar cuándo se muestra la interfaz de usuario de tu aplicación * Establecer `keepUrlPathAfterReload` en `true` para preservar la ubicación del usuario en la aplicación * Guardar y restaurar el estado de la aplicación según sea necesario para evitar perder el progreso del usuario * Probar exhaustivamente el comportamiento de actualización de tu aplicación para asegurar que no haya transiciones bruscas, pérdida de estado o cambios de ubicación desorientadores En la mayoría de los casos, el comportamiento de actualización predeterminado proporciona el mejor equilibrio entre entregar actualizaciones rápidamente y minimizar la interrupción. Pero para aplicaciones con necesidades específicas, Capgo proporciona la flexibilidad para personalizar cuándo y cómo se aplican las actualizaciones. # Tipos de Actualización > Una referencia completa de todos los tipos de actualización OTA que proporciona Capgo: momento de aplicación, condiciones de retraso, bloqueo de versiones y métodos de entrega. Capgo admite varios tipos de actualizaciones por aire (OTA). Esta página enumera y explica todos ellos para que puedas elegir la combinación adecuada para tu aplicación. ## Momento de Aplicación [Section titled “Momento de Aplicación”](#momento-de-aplicación) Controla **cuándo** se aplica una actualización después de ser descargada. | Tipo | Descripción | Caso de Uso | | ----------------------------- | ---------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | | **Predeterminado** | Descargar en segundo plano, aplicar cuando el usuario pone la app en segundo plano o la cierra | La mayoría de las apps; mínima interrupción | | **directUpdate: `atInstall`** | Aplicar inmediatamente solo en instalación nueva o actualización desde la tienda de apps | Los nuevos usuarios obtienen la última versión; los usuarios existentes usan el flujo predeterminado | | **directUpdate: `onLaunch`** | Aplicar inmediatamente en instalación, actualización de tienda o después de cerrar la app | Balance entre frescura y estabilidad de sesión | | **directUpdate: `always`** | Aplicar inmediatamente cuando se descarga una actualización (incluso al reanudar) | Correcciones críticas, apps con estado simple | Configurar en `capacitor.config.ts`: ```typescript plugins: { CapacitorUpdater: { directUpdate: false, // default // or: 'atInstall' | 'onLaunch' | 'always' } } ``` Tip Para detalles completos y manejo de pantalla de inicio, consulta [Comportamiento de Actualización](/docs/live-updates/update-behavior/). ## Condiciones de Retraso [Section titled “Condiciones de Retraso”](#condiciones-de-retraso) Condiciones que deben cumplirse **antes** de que se instale una actualización. Usa `setMultiDelay` para combinarlas (todas las condiciones deben satisfacerse). | Condición | Descripción | Ejemplo | | ----------------- | --------------------------------------------------------------------------- | ------------------------------------------------------ | | **date** | Esperar hasta después de una fecha/hora específica | Coordinar con lanzamiento del lado del servidor | | **background** | Esperar una duración mínima (ms) después de que la app pase a segundo plano | Evitar aplicar durante cambios rápidos de app | | **nativeVersion** | Requerir una versión binaria nativa mínima | Bloquear actualizaciones en código nativo incompatible | | **kill** | Esperar hasta el siguiente evento de cierre de app | Aplicar solo en reinicio completo | ```typescript import { CapacitorUpdater } from '@capgo/capacitor-updater'; await CapacitorUpdater.setMultiDelay({ delayConditions: [ { kind: 'date', value: '2023-06-01T00:00:00.000Z' }, { kind: 'background', value: '60000' }, ], }); ``` Danger La condición `kill` se activa después del primer evento de cierre, no en el siguiente segundo plano como las demás. Esto se corregirá en una versión futura. ## Bloqueo de Versiones (Política de Canal) [Section titled “Bloqueo de Versiones (Política de Canal)”](#bloqueo-de-versiones-política-de-canal) Controla qué **actualizaciones semver** entregará automáticamente un canal. Se configura mediante `--disable-auto-update` en los canales. | Estrategia | Bloquea | Permite | Caso de Uso | | ------------ | -------------------------------------------- | ---------------------------------------------------------- | ------------------------------------------------------------ | | **none** | Nada | Todas las actualizaciones | Predeterminado; actualización automática completa | | **major** | 0.0.0 → 1.0.0 | Mismo major (ej. 1.x → 1.y) | Prevenir cambios incompatibles en versiones nativas antiguas | | **minor** | 0.0.0 → 1.1.0, 1.1.0 → 1.2.0 | Mismo minor (ej. 1.2.x → 1.2.y) | Control más estricto dentro del major | | **patch** | Cualquier cambio excepto incremento de patch | Solo 0.0.311 → 0.0.314 | Muy estricto; solo actualizaciones de patch | | **metadata** | Actualizaciones sin `min_update_version` | Actualizaciones con metadatos de compatibilidad explícitos | Reglas de compatibilidad personalizadas por bundle | ```bash npx @capgo/cli channel set production --disable-auto-update major ``` Caution `patch` y `metadata` requieren configuración cuidadosa. Consulta [comandos CLI](/docs/cli/commands/#disable-updates-strategy) y [Segmentación de Versiones](/docs/live-updates/version-targeting/) para más detalles. ## Tipos de Entrega [Section titled “Tipos de Entrega”](#tipos-de-entrega) Cómo se **transfiere el bundle** al dispositivo. | Tipo | Descripción | Cuándo Usar | | -------------------- | ------------------------------------------ | ------------------------------------------------------------------------------ | | **Bundle completo** | Se descarga el bundle JS completo | Primera instalación, cambios grandes o cuando delta no está disponible | | **Delta (manifest)** | Solo se descargan los archivos modificados | La mayoría de las actualizaciones; más rápido y amigable con el ancho de banda | ```bash # Bundle completo (predeterminado) npx @capgo/cli bundle upload --channel production # Actualizaciones delta npx @capgo/cli bundle upload --channel production --delta ``` Tip Cuando uses `directUpdate`, habilita [Actualizaciones Delta](/docs/live-updates/differentials/) para minimizar el tiempo de descarga y mejorar la UX. ## Referencia Rápida [Section titled “Referencia Rápida”](#referencia-rápida) | Categoría | Tipos | | -------------------------- | ------------------------------------------------- | | **Momento de aplicación** | Predeterminado, `atInstall`, `onLaunch`, `always` | | **Condiciones de retraso** | `date`, `background`, `nativeVersion`, `kill` | | **Bloqueo de versiones** | `none`, `major`, `minor`, `patch`, `metadata` | | **Entrega** | Bundle completo, Delta (manifest) | ## Relacionado [Section titled “Relacionado”](#relacionado) * [Comportamiento de Actualización](/docs/live-updates/update-behavior/) — Configurar momento de aplicación y retrasos * [Segmentación de Versiones](/docs/live-updates/version-targeting/) — Enrutamiento de versiones basado en canales * [Actualizaciones Delta (manifest)](/docs/live-updates/differentials/) — Habilitar descargas parciales * [Canales](/docs/live-updates/channels/) — Configuración y precedencia de canales # Targeting de Versiones > Entregue automáticamente actualizaciones compatibles a los usuarios en función de su versión de aplicación nativa Esta guía explica cómo entregar automáticamente el último bundle compatible a los usuarios en función de su versión de aplicación nativa, **similar al enfoque de Ionic AppFlow**. Esto garantiza una gestión simplificada de actualizaciones y lanzamientos más rápidos mientras se evitan problemas de compatibilidad. ¿Migrando desde Ionic AppFlow? Si está migrando desde Ionic AppFlow, esta guía es especialmente importante para usted. AppFlow asociaba automáticamente las actualizaciones con versiones nativas, y Capgo proporciona la misma capacidad con aún más control y flexibilidad. Consulte la [Guía de migración de AppFlow](/docs/upgrade/from-appflow-to-capgo) para obtener instrucciones paso a paso sobre la migración. ## Descripción General [Section titled “Descripción General”](#descripción-general) El sistema de targeting de versiones de Capgo le permite: * **Entregar automáticamente actualizaciones compatibles** a los usuarios en función de su versión de aplicación nativa * **Prevenir cambios bruscos** de llegar a versiones de aplicación incompatibles * **Gestionar múltiples versiones de aplicaciones** simultáneamente sin lógica compleja * **Lanzar actualizaciones sin problemas** para segmentos específicos de usuarios ### Por Qué el Targeting de Versiones es Importante (Especialmente para Usuarios de AppFlow) [Section titled “Por Qué el Targeting de Versiones es Importante (Especialmente para Usuarios de AppFlow)”](#por-qué-el-targeting-de-versiones-es-importante-especialmente-para-usuarios-de-appflow) Si está familiarizado con **Ionic AppFlow**, sabe lo crítico que es asegurar que los usuarios reciban solo actualizaciones compatibles. AppFlow asociaba automáticamente bundles de actualización en vivo con versiones de aplicaciones nativas, evitando que JavaScript incompatible se entregara a código nativo más antiguo. **Capgo proporciona las mismas garantías de seguridad**, con características adicionales: * Control más granular sobre la coincidencia de versiones * Múltiples estrategias (canales, semver, restricciones nativas) * Mejor visibilidad en la distribución de versiones * Control de API y CLI junto con administración de panel Este enfoque es particularmente útil cuando: * Tiene usuarios en diferentes versiones principales de su aplicación (p. ej., v1.x, v2.x, v3.x) * Necesita mantener compatibilidad con versiones anteriores mientras implementa cambios bruscos * Desea evitar que bundles más nuevos rompan código nativo más antiguo * Está migrando gradualmente a los usuarios de una versión a otra * **Está migrando desde AppFlow** y desea mantener la misma seguridad de actualización ## Cómo Funciona [Section titled “Cómo Funciona”](#cómo-funciona) Capgo utiliza un enfoque de múltiples capas para hacer coincidir a los usuarios con actualizaciones compatibles: 1. **Restricciones de Versión Nativa**: Evite que los bundles se entreguen a versiones nativas incompatibles 2. **Enrutamiento Basado en Canales**: Enrute diferentes versiones de aplicaciones a diferentes canales de actualización 3. **Controles de Versión Semántica**: Bloquee automáticamente actualizaciones a través de límites principales/menores/parche 4. **Anulaciones a Nivel de Dispositivo**: Usuarios específicos de destino o grupos de usuarios ### Flujo de Coincidencia de Versiones [Section titled “Flujo de Coincidencia de Versiones”](#flujo-de-coincidencia-de-versiones) ```mermaid graph TD A[User Opens App] --> B{Check Device Override} B -->|Override Set| C[Use Override Channel] B -->|No Override| D{Check defaultChannel in App} D -->|Has defaultChannel| E[Use App's defaultChannel] D -->|No defaultChannel| F[Use Cloud Default Channel] C --> G{Check Version Constraints} E --> G F --> G G -->|Compatible| H[Deliver Update] G -->|Incompatible| I[Skip Update] ``` ## Estrategia 1: Enrutamiento de Versiones Basado en Canales [Section titled “Estrategia 1: Enrutamiento de Versiones Basado en Canales”](#estrategia-1-enrutamiento-de-versiones-basado-en-canales) Este es el **enfoque recomendado** para gestionar cambios bruscos y actualizaciones de versiones principales. Es similar al modelo de entrega de AppFlow. ### Escenario de Ejemplo [Section titled “Escenario de Ejemplo”](#escenario-de-ejemplo) * **App v1.x** (100.000 usuarios) → canal `production` * **App v2.x** (50.000 usuarios con cambios bruscos) → canal `v2` * **App v3.x** (10.000 usuarios beta) → canal `v3` ### Implementación [Section titled “Implementación”](#implementación) #### Paso 1: Configurar Canales para Cada Versión Principal [Section titled “Paso 1: Configurar Canales para Cada Versión Principal”](#paso-1-configurar-canales-para-cada-versión-principal) ```typescript // capacitor.config.ts para compilaciones de versión 1.x import { CapacitorConfig } from '@capacitor/cli'; const config: CapacitorConfig = { appId: 'com.example.app', appName: 'Example App', plugins: { CapacitorUpdater: { autoUpdate: true, defaultChannel: 'production', // u omitir para predeterminado } } }; export default config; ``` ```typescript // capacitor.config.ts para compilaciones de versión 2.x const config: CapacitorConfig = { appId: 'com.example.app', appName: 'Example App', plugins: { CapacitorUpdater: { autoUpdate: true, defaultChannel: 'v2', // Enruta a los usuarios de v2 automáticamente } } }; ``` ```typescript // capacitor.config.ts para compilaciones de versión 3.x const config: CapacitorConfig = { appId: 'com.example.app', appName: 'Example App', plugins: { CapacitorUpdater: { autoUpdate: true, defaultChannel: 'v3', // Enruta a los usuarios de v3 automáticamente } } }; ``` #### Paso 2: Crear Canales [Section titled “Paso 2: Crear Canales”](#paso-2-crear-canales) ```bash # Crear canales para cada versión principal npx @capgo/cli channel create production npx @capgo/cli channel create v2 npx @capgo/cli channel create v3 # Habilitar la asignación automática para que las aplicaciones cambien de canal npx @capgo/cli channel set production --self-assign npx @capgo/cli channel set v2 --self-assign npx @capgo/cli channel set v3 --self-assign ``` #### Paso 3: Cargar Bundles Específicos de Versión [Section titled “Paso 3: Cargar Bundles Específicos de Versión”](#paso-3-cargar-bundles-específicos-de-versión) ```bash # Para usuarios de v1.x (desde rama v1-maintenance) git checkout v1-maintenance npm run build npx @capgo/cli bundle upload --channel production # Para usuarios de v2.x (desde rama v2-maintenance o main) git checkout main npm run build npx @capgo/cli bundle upload --channel v2 # Para usuarios de v3.x (desde rama beta/v3) git checkout beta npm run build npx @capgo/cli bundle upload --channel v3 ``` Enrutamiento Automático Cuando los usuarios abren la aplicación, se conectan automáticamente a su canal designado en función del `defaultChannel` en su bundle de aplicación instalado. ¡No se requieren cambios de código JavaScript! ### Beneficios [Section titled “Beneficios”](#beneficios) * **Cero cambios de código** - El enrutamiento de canales ocurre automáticamente * **Separación clara** - Cada versión tiene su propio pipeline de actualización * **Targeting flexible** - Empuje actualizaciones a grupos de versiones específicos * **Lanzamientos seguros** - Los cambios bruscos nunca llegan a versiones incompatibles ## Estrategia 2: Controles de Versión Semántica [Section titled “Estrategia 2: Controles de Versión Semántica”](#estrategia-2-controles-de-versión-semántica) Utilice los controles de versión semántica integrados de Capgo para evitar actualizaciones a través de límites de versión. ### Deshabilitar Auto-Actualización a Través de Versiones Principales [Section titled “Deshabilitar Auto-Actualización a Través de Versiones Principales”](#deshabilitar-auto-actualización-a-través-de-versiones-principales) ```bash # Crear un canal que bloquee actualizaciones de versión principal npx @capgo/cli channel create stable --disable-auto-update major ``` Esta configuración significa: * Los usuarios en la versión de aplicación **1.2.3** recibirán actualizaciones hasta **1.9.9** * Los usuarios **NO** recibirán la versión **2.0.0** automáticamente * Evita que cambios bruscos lleguen a código nativo más antiguo ### Opciones de Control Granular [Section titled “Opciones de Control Granular”](#opciones-de-control-granular) ```bash # Bloquear actualizaciones de versión menor (1.2.x no obtendrá 1.3.0) npx @capgo/cli channel set stable --disable-auto-update minor # Bloquear actualizaciones de parche (1.2.3 no obtendrá 1.2.4) npx @capgo/cli channel set stable --disable-auto-update patch # Permitir todas las actualizaciones npx @capgo/cli channel set stable --disable-auto-update none ``` Versión Semántica Requerida Esta estrategia solo funciona si sigue versión semántica (semver) para sus versiones de aplicación. Asegúrese de que sus números de versión sigan el formato `MAJOR.MINOR.PATCH`. ## Estrategia 3: Restricciones de Versión Nativa [Section titled “Estrategia 3: Restricciones de Versión Nativa”](#estrategia-3-restricciones-de-versión-nativa) Especifique requisitos de versión nativa mínimos para los bundles para evitar la entrega a dispositivos incompatibles. ### Usando Condición de Retraso de nativeVersion [Section titled “Usando Condición de Retraso de nativeVersion”](#usando-condición-de-retraso-de-nativeversion) Al cargar un bundle, puede especificar una versión nativa mínima: ```bash # Este bundle requiere versión nativa 2.0.0 o superior npx @capgo/cli bundle upload \ --channel production \ --native-version "2.0.0" ``` Cómo Funciona Los dispositivos en la versión nativa 1.x NO recibirán este bundle. Solo los dispositivos en 2.0.0+ lo obtendrán. Esto es perfecto para actualizaciones que requieren nuevas APIs o complementos nativos. ### Casos de Uso [Section titled “Casos de Uso”](#casos-de-uso) 1. **Nuevo Complemento Nativo Requerido** ```bash # El bundle necesita complemento de cámara añadido en v2.0.0 npx @capgo/cli bundle upload --native-version "2.0.0" ``` 2. **Cambios de API Nativa de Ruptura** ```bash # El bundle utiliza nuevas APIs de Capacitor 6 npx @capgo/cli bundle upload --native-version "3.0.0" ``` 3. **Migración Gradual** ```bash # Pruebe bundle solo en la última versión nativa npx @capgo/cli bundle upload \ --channel beta \ --native-version "2.5.0" ``` ## Estrategia 4: Prevención de Degradación Automática [Section titled “Estrategia 4: Prevención de Degradación Automática”](#estrategia-4-prevención-de-degradación-automática) Evite que los usuarios reciban bundles más antiguos que su versión nativa actual. ### Habilitar en Configuración de Canal [Section titled “Habilitar en Configuración de Canal”](#habilitar-en-configuración-de-canal) En el panel de control de Capgo: 1. Vaya a **Canales** → Seleccione su canal 2. Habilite **“Deshabilitar degradación automática bajo nativa”** 3. Guardar cambios O a través de CLI: ```bash npx @capgo/cli channel set production --disable-downgrade ``` ### Ejemplo [Section titled “Ejemplo”](#ejemplo) * Versión del dispositivo del usuario: Versión nativa **1.2.5** * Bundle de canal: Versión **1.2.3** * **Resultado**: La actualización se bloquea (sería una degradación) Esto es útil cuando: * Los usuarios instalaron manualmente una versión más nueva desde la tienda de aplicaciones * Necesita asegurar que los usuarios siempre tengan los últimos parches de seguridad * Desea evitar errores de regresión ## Estrategia 5: Targeting a Nivel de Dispositivo [Section titled “Estrategia 5: Targeting a Nivel de Dispositivo”](#estrategia-5-targeting-a-nivel-de-dispositivo) Anule la asignación de canal para dispositivos específicos o grupos de usuarios. ### Forzar Versión Específica para Pruebas [Section titled “Forzar Versión Específica para Pruebas”](#forzar-versión-específica-para-pruebas) ```typescript import { CapacitorUpdater } from '@capgo/capacitor-updater' // Forzar a los probadores beta a usar el canal v3 async function assignBetaTesters() { const deviceId = await CapacitorUpdater.getDeviceId() // Verificar si el usuario es probador beta if (isBetaTester(userId)) { await CapacitorUpdater.setChannel({ channel: 'v3' }) } } ``` ### Anulación de Dispositivo de Panel [Section titled “Anulación de Dispositivo de Panel”](#anulación-de-dispositivo-de-panel) En el panel de control de Capgo: 1. Vaya a **Dispositivos** → Busque dispositivo 2. Haga clic en **Establecer canal** o **Establecer versión** 3. Anule con canal o versión de bundle específico 4. El dispositivo recibirá actualizaciones de la fuente anulada Prueba de Actualizaciones Utilice anulaciones de dispositivos para probar actualizaciones en su propio dispositivo antes de implementar para todos los usuarios. ## Flujo de Trabajo Completo en Estilo AppFlow [Section titled “Flujo de Trabajo Completo en Estilo AppFlow”](#flujo-de-trabajo-completo-en-estilo-appflow) Aquí hay un ejemplo completo que combina todas las estrategias: ### 1. Configuración Inicial (App v1.0.0) [Section titled “1. Configuración Inicial (App v1.0.0)”](#1-configuración-inicial-app-v100) ```bash # Crear canal de producción con controles semver npx @capgo/cli channel create production \ --disable-auto-update major \ --disable-downgrade ``` capacitor.config.ts ```typescript const config: CapacitorConfig = { plugins: { CapacitorUpdater: { autoUpdate: true, defaultChannel: 'production', } } }; ``` ### 2. Lanzar Cambio Brusco (App v2.0.0) [Section titled “2. Lanzar Cambio Brusco (App v2.0.0)”](#2-lanzar-cambio-brusco-app-v200) ```bash # Crear canal v2 para nueva versión npx @capgo/cli channel create v2 \ --disable-auto-update major \ --disable-downgrade \ --self-assign # Crear rama git para mantenimiento v1 git checkout -b v1-maintenance git push origin v1-maintenance ``` ```typescript // capacitor.config.ts para v2.0.0 const config: CapacitorConfig = { plugins: { CapacitorUpdater: { autoUpdate: true, defaultChannel: 'v2', // Los nuevos usuarios obtienen el canal v2 } } }; ``` ### 3. Empujar Actualizaciones a Ambas Versiones [Section titled “3. Empujar Actualizaciones a Ambas Versiones”](#3-empujar-actualizaciones-a-ambas-versiones) ```bash # Actualizar usuarios de v1.x (corrección de error) git checkout v1-maintenance # Hacer cambios npx @capgo/cli bundle upload \ --channel production \ --native-version "1.0.0" # Actualizar usuarios de v2.x (nueva característica) git checkout main # Hacer cambios npx @capgo/cli bundle upload \ --channel v2 \ --native-version "2.0.0" ``` ### 4. Monitorear Distribución de Versiones [Section titled “4. Monitorear Distribución de Versiones”](#4-monitorear-distribución-de-versiones) Utilice el panel de control de Capgo para rastrear: * Cuántos usuarios están en v1 vs v2 * Tasas de adopción de bundles por versión * Errores o fallos por versión ### 5. Deprecar Versión Anterior [Section titled “5. Deprecar Versión Anterior”](#5-deprecar-versión-anterior) Una vez que el uso de v1 cae por debajo del umbral: ```bash # Deja de cargar en el canal de producción # Opcional: Eliminar rama de mantenimiento v1 git branch -d v1-maintenance # Mover todos los usuarios restantes al predeterminado # (Necesitarán actualizar a través de la tienda de aplicaciones) ``` ## Precedencia de Canal [Section titled “Precedencia de Canal”](#precedencia-de-canal) Cuando existen múltiples configuraciones de canal, Capgo utiliza este orden de precedencia: 1. **Anulación de dispositivo** (Panel o API) - Mayor prioridad 2. **Anulación de nube** a través de llamada `setChannel()` 3. **defaultChannel** en capacitor.config.ts 4. **Canal predeterminado** (Configuración en la nube) - Menor prioridad Ejemplo de Precedencia Si la aplicación de un usuario tiene `defaultChannel: 'v2'` pero anula su dispositivo a `'beta'` en el panel, recibirá actualizaciones del canal `'beta'`. ## Mejores Prácticas [Section titled “Mejores Prácticas”](#mejores-prácticas) ### 1. Siempre Establecer defaultChannel para Versiones Principales [Section titled “1. Siempre Establecer defaultChannel para Versiones Principales”](#1-siempre-establecer-defaultchannel-para-versiones-principales) ```typescript // ✅ Bien: Cada versión principal tiene canal explícito // v1.x → production // v2.x → v2 // v3.x → v3 // ❌ Mal: Depender del cambio de canal dinámico // Todas las versiones → production, cambiar manualmente ``` ### 2. Usar Versión Semántica [Section titled “2. Usar Versión Semántica”](#2-usar-versión-semántica) ```bash # ✅ Bien 1.0.0 → 1.0.1 → 1.1.0 → 2.0.0 # ❌ Mal 1.0 → 1.1 → 2 → 2.5 ``` ### 3. Mantener Ramas Separadas [Section titled “3. Mantener Ramas Separadas”](#3-mantener-ramas-separadas) ```bash # ✅ Bien: Ramas separadas por versión principal main (v3.x) v2-maintenance (v2.x) v1-maintenance (v1.x) # ❌ Mal: Rama única para todas las versiones ``` ### 4. Probar Antes del Lanzamiento [Section titled “4. Probar Antes del Lanzamiento”](#4-probar-antes-del-lanzamiento) ```bash # Probar primero en canal beta npx @capgo/cli bundle upload --channel beta # Monitorear problemas, luego promover a producción npx @capgo/cli bundle upload --channel production ``` ### 5. Monitorear Distribución de Versiones [Section titled “5. Monitorear Distribución de Versiones”](#5-monitorear-distribución-de-versiones) Verifique regularmente su panel: * ¿Están los usuarios actualizando a versiones nativas más nuevas? * ¿Las versiones antiguas aún reciben mucho tráfico? * ¿Debería deprecar canales antiguos? ## Comparación con Ionic AppFlow [Section titled “Comparación con Ionic AppFlow”](#comparación-con-ionic-appflow) Para equipos que migran desde **Ionic AppFlow**, aquí hay una comparación del targeting de versiones de Capgo: | Característica | Ionic AppFlow | Capgo | | ---------------------------------------- | --------------------------------------- | -------------------------------------------------------- | | **Enrutamiento basado en versión** | Automático basado en versión nativa | Automático vía `defaultChannel` + múltiples estrategias | | **Versión semántica** | Soporte básico | Avanzado con `--disable-auto-update` (major/minor/patch) | | **Restricciones de versión nativa** | Configuración manual en panel AppFlow | Indicador `--native-version` integrado en CLI | | **Gestión de canales** | UI web + CLI | UI web + CLI + API | | **Anulaciones de dispositivo** | Control limitado a nivel de dispositivo | Control completo vía Panel/API | | **Prevención de degradación automática** | Sí | Sí vía `--disable-downgrade` | | **Mantenimiento de múltiples versiones** | Gestión manual de rama/canal | Automatizado con precedencia de canal | | **Auto-hospedaje** | No | Sí (control total) | | **Análisis de versiones** | Básico | Métricas detalladas por versión | Paridad AppFlow y Más Allá Capgo proporciona **todas las capacidades de targeting de versiones** que AppFlow ofreció, más mecanismos de control adicionales. Si confiaba en la coincidencia automática de versiones de AppFlow, encontrará que Capgo es igualmente seguro con más flexibilidad. ## Solución de Problemas [Section titled “Solución de Problemas”](#solución-de-problemas) ### Los Usuarios No Reciben Actualizaciones [Section titled “Los Usuarios No Reciben Actualizaciones”](#los-usuarios-no-reciben-actualizaciones) Verifique lo siguiente: 1. **Asignación de canal**: Verifique que el dispositivo esté en el canal correcto ```typescript const channel = await CapacitorUpdater.getChannel() console.log('Current channel:', channel) ``` 2. **Restricciones de versión**: Verifique si el bundle tiene requisitos de versión nativa * Panel → Bundles → Verifique columna “Versión nativa” 3. **Configuración de Semver**: Verifique la configuración `disable-auto-update` del canal ```bash npx @capgo/cli channel list ``` 4. **Anulación de dispositivo**: Verifique si el dispositivo tiene anulación manual * Panel → Dispositivos → Busque dispositivo → Verifique canal/versión ### Bundle Entregado a Versión Incorrecta [Section titled “Bundle Entregado a Versión Incorrecta”](#bundle-entregado-a-versión-incorrecta) 1. **Revisar defaultChannel**: Asegúrese de canal correcto en `capacitor.config.ts` 2. **Verificar carga de bundle**: Verifique que el bundle se cargó en el canal deseado 3. **Inspeccionar versión nativa**: Confirme que el indicador `--native-version` se utilizó correctamente ### Cambios Bruscos Afectando Versiones Antiguas [Section titled “Cambios Bruscos Afectando Versiones Antiguas”](#cambios-bruscos-afectando-versiones-antiguas) 1. **Corrección inmediata**: Anule dispositivos afectados a bundle seguro * Panel → Dispositivos → Seleccionar múltiples → Establecer versión 2. **Corrección a largo plazo**: Crear canales versionados y mantener ramas separadas 3. **Prevención**: Siempre pruebe actualizaciones en dispositivos representativos antes del lanzamiento ## Migración desde Ionic AppFlow [Section titled “Migración desde Ionic AppFlow”](#migración-desde-ionic-appflow) Si está migrando desde **Ionic AppFlow**, el targeting de versiones funciona de manera muy similar en Capgo, con mayor flexibilidad: ### Mapeo de Conceptos [Section titled “Mapeo de Conceptos”](#mapeo-de-conceptos) | Concepto AppFlow | Equivalente Capgo | Notas | | ---------------------------------------- | ------------------------------------------------- | ---------------------------------- | | **Canal de implementación** | Canal Capgo | Mismo concepto, más poderoso | | **Bloqueo de versión nativa** | indicador `--native-version` | Control más granular | | **Prioridad de canal** | Precedencia de canal (override → cloud → default) | Precedencia más transparente | | **Objetivo de implementación** | Canal + controles semver | Múltiples estrategias disponibles | | **Canal de producción** | canal `production` (o cualquier nombre) | Nombre flexible | | **Implementación basada en git** | Carga de bundle CLI desde rama | Mismo flujo de trabajo | | **Coincidencia automática de versiones** | `defaultChannel` + restricciones de versión | Mejorado con múltiples estrategias | ### Diferencias Clave para Usuarios de AppFlow [Section titled “Diferencias Clave para Usuarios de AppFlow”](#diferencias-clave-para-usuarios-de-appflow) 1. **Más control**: Capgo le proporciona múltiples estrategias (canales, semver, versión nativa) que se pueden combinar 2. **Mejor visibilidad**: El panel muestra distribución de versiones y problemas de compatibilidad 3. **Acceso de API**: Control programático completo sobre targeting de versiones 4. **Auto-hospedaje**: Opción para ejecutar su propio servidor de actualización con la misma lógica de versión ### Pasos de Migración [Section titled “Pasos de Migración”](#pasos-de-migración) 1. **Asigne sus canales de AppFlow** a canales de Capgo (generalmente 1:1) 2. **Establezca `defaultChannel`** en `capacitor.config.ts` para cada versión principal 3. **Configure reglas semver** si desea bloqueo automático en límites de versión 4. **Cargue bundles específicos de versión** utilizando el indicador `--native-version` 5. **Monitoree distribución de versiones** en el panel de Capgo Guía Completa de Migración Para instrucciones completas de migración incluyendo reemplazo de SDK y mapeo de API, consulte la [Guía de migración de AppFlow a Capgo](/docs/upgrade/from-appflow-to-capgo). ## Patrones Avanzados [Section titled “Patrones Avanzados”](#patrones-avanzados) ### Lanzamiento Gradual por Versión [Section titled “Lanzamiento Gradual por Versión”](#lanzamiento-gradual-por-versión) ```typescript // Migrar gradualmente usuarios de v1 a v2 async function migrateUsers() { const deviceId = await CapacitorUpdater.getDeviceId() const rolloutPercentage = 10 // Comenzar con 10% // Hash ID de dispositivo para obtener porcentaje determinista const hash = hashCode(deviceId) % 100 if (hash < rolloutPercentage) { // El usuario está en grupo de lanzamiento - Migrar a v2 await CapacitorUpdater.setChannel({ channel: 'v2' }) } } ``` ### Indicadores de Característica por Versión [Section titled “Indicadores de Característica por Versión”](#indicadores-de-característica-por-versión) ```typescript // Habilitar características basadas en versión nativa async function checkFeatureAvailability() { const info = await CapacitorUpdater.getDeviceId() const nativeVersion = info.nativeVersion if (compareVersions(nativeVersion, '2.0.0') >= 0) { // Habilitar características que requieren v2.0.0+ enableNewCameraFeature() } } ``` ### Prueba A/B Entre Versiones [Section titled “Prueba A/B Entre Versiones”](#prueba-ab-entre-versiones) ```typescript // Ejecutar pruebas A/B dentro de la misma versión nativa async function assignABTest() { const nativeVersion = await getNativeVersion() if (nativeVersion.startsWith('2.')) { // Solo prueba A/B en usuarios de v2 const variant = Math.random() < 0.5 ? 'v2-test-a' : 'v2-test-b' await CapacitorUpdater.setChannel({ channel: variant }) } } ``` ## Resumen [Section titled “Resumen”](#resumen) Capgo proporciona múltiples estrategias para entrega de actualización específica de versión: 1. **Enrutamiento basado en canales**: Separación automática de versiones vía `defaultChannel` 2. **Versión semántica**: Prevenir actualizaciones a través de límites principales/menores/parche 3. **Restricciones de versión nativa**: Requerir versión nativa mínima para bundles 4. **Prevención de degradación automática**: Nunca entregar bundles más antiguos a versiones nativas más nuevas 5. **Anulaciones de dispositivo**: Control manual para pruebas y targeting Al combinar estas estrategias, puede lograr entrega de actualización automática en estilo AppFlow con aún más flexibilidad y control. Elija el enfoque que mejor se adapte a su flujo de trabajo de versionado e implementación de aplicaciones. Para más detalles sobre características específicas: * [Guía de Cambios Bruscos](/docs/live-updates/breaking-changes) - Estrategia de versionado de canal detallada * [Gestión de Canales](/docs/live-updates/channels) - Referencia de configuración de canal completa * [Comportamiento de Actualización](/docs/live-updates/update-behavior) - Retrasos de versión nativa y condiciones # Funciones and Configuración > All available Método and Configuración of El Plugin # Updater Plugin Config [Section titled “Updater Plugin Config”](#updater-plugin-config) CapacitorUpdater Puede be configured with these Opciones: | Prop | Type | Description | Predeterminado | Since | | ----------------------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ | ------------ | | **`appReadyTimeout`** | `number` | Configurar El number of milliseconds El native Plugin should wait before considering an Actualizar ‘Falló’. Only available for Android and iOS. | `10000 // (10 seconds)` | | | **`responseTimeout`** | `number` | Configurar El number of seconds El native Plugin should wait before considering API timeout. Only available for Android and iOS. | `20 // (20 second)` | | | **`autoDeleteFailed`** | `boolean` | Configurar whether El Plugin should Usar automatically Eliminar Falló Paquetes. Only available for Android and iOS. | `true` | | | **`autoDeletePrevious`** | `boolean` | Configurar whether El Plugin should Usar automatically Eliminar previous Paquetes after a Exitoso Actualizar. Only available for Android and iOS. | `true` | | | **`autoUpdate`** | `boolean` | Configurar whether El Plugin should Usar Auto Actualización via an Actualizar server. Only available for Android and iOS. | `true` | | | **`resetWhenUpdate`** | `boolean` | Automatically Eliminar previous downloaded Paquetes when a newer native Aplicación Paquete is installed A El Dispositivo. Only available for Android and iOS. | `true` | | | **`updateUrl`** | `string` | Configurar El URL / endpoint A which Actualizar checks are sent. Only available for Android and iOS. | `https://plugin.capgo.app/updates` | | | **`channelUrl`** | `string` | Configurar El URL / endpoint for Canal operations. Only available for Android and iOS. | `https://plugin.capgo.app/channel_self` | | | **`statsUrl`** | `string` | Configurar El URL / endpoint A which Actualizar Estadísticas are sent. Only available for Android and iOS. Establecer A "" Para deshabilitar stats reporting. | `https://plugin.capgo.app/stats` | | | **`publicKey`** | `string` | Configurar El public key for end A end live Actualizar Cifrado Versión 2 Only available for Android and iOS. | `undefined` | 6.2.0 | | **`version`** | `string` | Configurar El current Versión of El Aplicación. This Va a be used for El first Actualizar request. If not Establecer, El Plugin Va a get El Versión De El native code. Only available for Android and iOS. | `undefined` | 4.17.48 | | **`directUpdate`** | \`boolean | ’always' | 'atInstall' | 'onLaunch’\` | | **`autoSplashscreen`** | `boolean` | Automatically handle splashscreen hiding when using directUpdate. When Habilitado, El Plugin Va a automatically hide El splashscreen after Actualizaciones are applied or when no Actualizar is needed. This removes El Necesita A manually listen for appReady events and call SplashScreen.hide(). Only works when directUpdate is Establecer A “atInstall”, “always”, “onLaunch”, or true. Requires El @Capacitor/splash-screen Plugin A be installed and configured with launchAutoHide: false. Requires autoUpdate and directUpdate A be Habilitado. Only available for Android and iOS. | `false` | 7.6.0 | | **`autoSplashscreenLoader`** | `boolean` | Display a native loading indicator on top of El splashscreen while automatic direct Actualizaciones are Ejecutando. Only takes effect when {@Enlace autoSplashscreen} is Habilitado. Requires El @Capacitor/splash-screen Plugin A be installed and configured with launchAutoHide: false. Only available for Android and iOS. | `false` | 7.19.0 | | **`autoSplashscreenTimeout`** | `number` | Automatically hide El splashscreen after El specified number of milliseconds when using automatic direct Actualizaciones. If El timeout elapses, El Actualizar continues A Descargar in El background while El splashscreen is dismissed. Establecer A `0` (zero) Para deshabilitar El timeout. When El timeout fires, El direct Actualizar flow is skipped and El downloaded Paquete is installed on El next background/launch. Requires {@Enlace autoSplashscreen} A be Habilitado. Only available for Android and iOS. | `10000 // (10 seconds)` | 7.19.0 | | **`periodCheckDelay`** | `number` | Configurar El delay period for period Actualizar Verificar. El unit is in seconds. Only available for Android and iOS. Cannot be less than 600 seconds (10 minutes). | `0 (disabled)` | | | **`localS3`** | `boolean` | Configurar El CLI Para usar a local server for Pruebas or self-hosted Actualizar server. | `undefined` | 4.17.48 | | **`localHost`** | `string` | Configurar El CLI Para usar a local server for Pruebas or self-hosted Actualizar server. | `undefined` | 4.17.48 | | **`localWebHost`** | `string` | Configurar El CLI Para usar a local server for Pruebas or self-hosted Actualizar server. | `undefined` | 4.17.48 | | **`localSupa`** | `string` | Configurar El CLI Para usar a local server for Pruebas or self-hosted Actualizar server. | `undefined` | 4.17.48 | | **`localSupaAnon`** | `string` | Configurar El CLI Para usar a local server for Pruebas. | `undefined` | 4.17.48 | | **`localApi`** | `string` | Configurar El CLI Para usar a local API for Pruebas. | `undefined` | 6.3.3 | | **`localApiFiles`** | `string` | Configurar El CLI Para usar a local file API for Pruebas. | `undefined` | 6.3.3 | | **`allowModifyUrl`** | `boolean` | Allow El Plugin A modify El updateUrl, statsUrl and channelUrl dynamically De El JavaScript side. | `false` | 5.4.0 | | **`allowModifyAppId`** | `boolean` | Allow El Plugin A modify El appId dynamically De El JavaScript side. | `false` | 7.14.0 | | **`allowManualBundleError`** | `boolean` | Allow marking Paquetes as errored De JavaScript while using manual Actualizar flows. When Habilitado, {@Enlace CapacitorUpdaterPlugin.setBundleError} Puede change a Paquete Estado A `error`. | `false` | 7.20.0 | | **`persistCustomId`** | `boolean` | Persist El customId Establecer through {@Enlace CapacitorUpdaterPlugin.setCustomId} across Aplicación restarts. Only available for Android and iOS. | `false (will be true by default in a future major release v8.x.x)` | 7.17.3 | | **`persistModifyUrl`** | `boolean` | Persist El updateUrl, statsUrl and channelUrl Establecer through {@Enlace CapacitorUpdaterPlugin.setUpdateUrl}, {@Enlace CapacitorUpdaterPlugin.setStatsUrl} and {@Enlace CapacitorUpdaterPlugin.setChannelUrl} across Aplicación restarts. Only available for Android and iOS. | `false` | 7.20.0 | | **`allowSetDefaultChannel`** | `boolean` | Allow or disallow El {@Enlace CapacitorUpdaterPlugin.setChannel} Método A modify El defaultChannel. When Establecer A `false`, calling `setChannel()` Va a return an Error with code `disabled_by_config`. | `true` | 7.34.0 | | **`defaultChannel`** | `string` | Establecer El Predeterminado Canal for El Aplicación in El config. Case sensitive. This setting will override El Predeterminado Canal Establecer in El cloud, but Va a still respect overrides made in El cloud. This requires El Canal A allow Dispositivos A self dissociate/associate in El Canal Configuración. | `undefined` | 5.5.0 | | **`appId`** | `string` | Configurar El Aplicación id for El Aplicación in El config. | `undefined` | 6.0.0 | | **`keepUrlPathAfterReload`** | `boolean` | Configurar El Plugin A keep El URL path after a reload. Advertencia: When a reload is triggered, ‘window\.history’ Va a be cleared. | `false` | 6.8.0 | | **`disableJSLogging`** | `boolean` | Deshabilitar El JavaScript logging of El Plugin. if true, El Plugin Va a not Registro A El JavaScript console. only El native Registro Va a be done | `false` | 7.3.0 | | **`shakeMenu`** | `boolean` | Habilitar shake gesture A show Actualizar menu for Depuración/Pruebas purposes | `false` | 7.5.0 | ## API Referencia [Section titled “API Referencia”](#api-referencia) * [`notifyAppReady`](#notifyappready) * [`setUpdateUrl`](#setupdateurl) * [`setStatsUrl`](#setstatsurl) * [`setChannelUrl`](#setchannelurl) * [`download`](#download) * [`next`](#next) * [`set`](#set) * [`delete`](#delete) * [`setBundleError`](#setbundleerror) * [`list`](#list) * [`reset`](#reset) * [`current`](#current) * [`reload`](#reload) * [`setMultiDelay`](#setmultidelay) * [`cancelDelay`](#canceldelay) * [`getLatest`](#getlatest) * [`setChannel`](#setchannel) * [`unsetChannel`](#unsetchannel) * [`getChannel`](#getchannel) * [`listChannels`](#listchannels) * [`setCustomId`](#setcustomid) * [`getBuiltinVersion`](#getbuiltinversion) * [`getDeviceId`](#getdeviceid) * [`getPluginVersion`](#getpluginversion) * [`isAutoUpdateEnabled`](#isautoupdateenabled) * [`removeAllListeners`](#removealllisteners) * [`addListener('download')`](#addlistenerdownload-) * [`addListener('noNeedUpdate')`](#addlistenernoneedupdate-) * [`addListener('updateAvailable')`](#addlistenerupdateavailable-) * [`addListener('downloadComplete')`](#addlistenerdownloadcomplete-) * [`addListener('breakingAvailable')`](#addlistenerbreakingavailable-) * [`addListener('majorAvailable')`](#addlistenermajoravailable-) * [`addListener('updateFailed')`](#addlistenerupdatefailed-) * [`addListener('downloadFailed')`](#addlistenerdownloadfailed-) * [`addListener('appReloaded')`](#addlistenerappreloaded-) * [`addListener('appReady')`](#addlistenerappready-) * [`addListener('channelPrivate')`](#addlistenerchannelprivate-) * [`isAutoUpdateAvailable`](#isautoupdateavailable) * [`getNextBundle`](#getnextbundle) * [`getFailedUpdate`](#getfailedupdate) * [`setShakeMenu`](#setshakemenu) * [`isShakeMenuEnabled`](#isshakemenuenabled) * [`getAppId`](#getappid) * [`setAppId`](#setappid) ### notifyAppReady [Section titled “notifyAppReady”](#notifyappready) ```typescript notifyAppReady() => Promise ``` Notify El native layer that JavaScript initialized successfully. **CRITICAL: Debe call this Método on every Aplicación launch A prevent automatic Reversión.** Esto es simple notification A confirm that your Paquete’s JavaScript loaded and executed. El native web server successfully served El Paquete files and your JS runtime started. That’s all it checks - nothing more complex. **What triggers Reversión:** * NOT calling this Método within El timeout (Predeterminado: 10 seconds) * Completo JavaScript failure (Paquete won’t load at all) **What does NOT trigger Reversión:** * Runtime errors after initialization (API failures, crashes, etc.) * Network request failures * Application logic errors **Importante: Call this BEFORE any network requests.** Don’t wait for APIs, data loading, or async operations. Call it as soon as your JavaScript Paquete starts executing A confirm El Paquete itself is valid. Best practices: * Call immediately in your Aplicación entry point (main.js, Aplicación component mount, etc.) * Don’t put it after network calls or heavy initialization * Don’t wrap it in try/catch with conditions * Adjust {@Enlace PluginsConfig.CapacitorUpdater.appReadyTimeout} if Necesita more time **Returns** `Promise` — Always resolves successfully with current Paquete Información. This Método never fails. *** ### setUpdateUrl [Section titled “setUpdateUrl”](#setupdateurl) ```typescript setUpdateUrl(options: UpdateUrl) => Promise ``` Establecer El Actualizar URL for El Aplicación dynamically at runtime. This overrides El {@Enlace PluginsConfig.CapacitorUpdater.updateUrl} config value. Requires {@Enlace PluginsConfig.CapacitorUpdater.allowModifyUrl} A be Establecer A `true`. Usar {@Enlace PluginsConfig.CapacitorUpdater.persistModifyUrl} A persist this value across Aplicación restarts. Otherwise, El URL Va a reset A El config value on next Aplicación launch. **Parámetros** | Name | Type | Description | | --------- | ----------- | ----------------------------------------------------------- | | `options` | `UpdateUrl` | Contains El URL Para usar for checking for Actualizaciones. | **Returns** `Promise` — Resolves when El URL is successfully Actualizado. **Since:** 5.4.0 **Throws:** {Error} If `allowModifyUrl` is false or if El operation fails. *** ### setStatsUrl [Section titled “setStatsUrl”](#setstatsurl) ```typescript setStatsUrl(options: StatsUrl) => Promise ``` Establecer El Estadísticas URL for El Aplicación dynamically at runtime. This overrides El {@Enlace PluginsConfig.CapacitorUpdater.statsUrl} config value. Requires {@Enlace PluginsConfig.CapacitorUpdater.allowModifyUrl} A be Establecer A `true`. Pass an empty string Para deshabilitar Estadísticas gathering entirely. Usar {@Enlace PluginsConfig.CapacitorUpdater.persistModifyUrl} A persist this value across Aplicación restarts. **Parámetros** | Name | Type | Description | | --------- | ---------- | ----------------------------------------------------------------------------------------- | | `options` | `StatsUrl` | Contains El URL Para usar for sending Estadísticas, or an empty string Para deshabilitar. | **Returns** `Promise` — Resolves when El URL is successfully Actualizado. **Since:** 5.4.0 **Throws:** {Error} If `allowModifyUrl` is false or if El operation fails. *** ### setChannelUrl [Section titled “setChannelUrl”](#setchannelurl) ```typescript setChannelUrl(options: ChannelUrl) => Promise ``` Establecer El Canal URL for El Aplicación dynamically at runtime. This overrides El {@Enlace PluginsConfig.CapacitorUpdater.channelUrl} config value. Requires {@Enlace PluginsConfig.CapacitorUpdater.allowModifyUrl} A be Establecer A `true`. Usar {@Enlace PluginsConfig.CapacitorUpdater.persistModifyUrl} A persist this value across Aplicación restarts. Otherwise, El URL Va a reset A El config value on next Aplicación launch. **Parámetros** | Name | Type | Description | | --------- | ------------ | ----------------------------------------------- | | `options` | `ChannelUrl` | Contains El URL Para usar for Canal operations. | **Returns** `Promise` — Resolves when El URL is successfully Actualizado. **Since:** 5.4.0 **Throws:** {Error} If `allowModifyUrl` is false or if El operation fails. *** ### Descargar [Section titled “Descargar”](#descargar) ```typescript download(options: DownloadOptions) => Promise ``` Descargar a new Paquete De El provided URL for later Instalación. El downloaded Paquete is stored locally but not activated. Para usar it: * Call {@Enlace next} A Establecer it for Instalación on next Aplicación backgrounding/restart * Call {@Enlace Establecer} A activate it immediately (destroys current JavaScript context) El URL should point A a zip file containing either: * Your Aplicación files directly in El zip root, or * A single folder containing all your Aplicación files El Paquete Debe include an `index.html` file at El root level. For encrypted Paquetes, provide El `sessionKey` and `checksum` Parámetros. For multi-file partial Actualizaciones, provide El `manifest` array. **Parámetros** | Name | Type | Description | | --------- | ----------------- | --------------------------------------------------------------- | | `options` | `DownloadOptions` | El {@Enlace DownloadOptions} for downloading a new Paquete zip. | **Returns** `Promise` — El {@Enlace BundleInfo} for El downloaded Paquete. **Throws:** {Error} If El Descargar fails or El Paquete is invalid. **Ejemplo** ```ts const bundle = await CapacitorUpdater.download({ url: `https://example.com/versions/${version}/dist.zip`, version: version }); // Bundle is downloaded but not active yet await CapacitorUpdater.next({ id: bundle.id }); // Will activate on next background ``` *** ### next [Section titled “next”](#next) ```typescript next(options: BundleId) => Promise ``` Establecer El next Paquete A be activated when El Aplicación backgrounds or restarts. Esto es El recommended way A apply Actualizaciones as it doesn’t interrupt El user’s current session. El Paquete Va a be activated when: * El Aplicación is backgrounded (user switches away), or * El Aplicación is killed and relaunched, or * {@Enlace reload} is called manually Unlike {@Enlace Establecer}, this Método does NOT destroy El current JavaScript context immediately. Your Aplicación continues Ejecutando normally until one of El above events occurs. Usar {@Enlace setMultiDelay} A Agregar additional conditions before El Actualizar is applied. **Parámetros** | Name | Type | Description | | --------- | ---------- | ------------------------------------------------------------------------------------------------------------ | | `options` | `BundleId` | Contains El ID of El Paquete A Establecer as next. Usar {@Enlace BundleInfo.id’} from ‘a downloaded Paquete. | **Returns** `Promise` — El {@Enlace BundleInfo} for El specified Paquete. **Throws:** {Error} When there is no index.HTML file inside El Paquete folder or El Paquete doesn’t exist. *** ### Establecer [Section titled “Establecer”](#establecer) ```typescript set(options: BundleId) => Promise ``` Establecer El current Paquete and immediately reloads El Aplicación. **Importante: Esto es terminal operation that destroys El current JavaScript context.** When you call this Método: * El entire JavaScript context is immediately destroyed * El Aplicación reloads De a different folder with different files * NO code after this call Va a execute * NO promises Va a resolve * NO callbacks Va a fire * Event listeners registered after this call are unreliable and may never fire El reload happens automatically - you don’t Necesita A do anything else. If Necesita A preserve state like El current URL path, Usar El {@Enlace PluginsConfig.CapacitorUpdater.keepUrlPathAfterReload} config Opción. For other state preservation needs, Guardar your data before calling this Método (e.g., A localStorage). **Do not** try A execute additional logic after calling `set()` - it won’t work as expected. **Parámetros** | Name | Type | Description | | --------- | ---------- | --------------------------------------------------------------------------------- | | `options` | `BundleId` | A {@Enlace BundleId} object containing El new Paquete id A Establecer as current. | **Returns** `Promise` — A promise that Va a never resolve because El JavaScript context is destroyed. **Throws:** {Error} When there is no index.HTML file inside El Paquete folder. *** ### Eliminar [Section titled “Eliminar”](#eliminar) ```typescript delete(options: BundleId) => Promise ``` Eliminar a Paquete De local storage A free up disk space. You cannot Eliminar: * El currently active Paquete * El `builtin` Paquete (El Versión shipped with your Aplicación) * El Paquete Establecer as `next` (call {@Enlace next} with a different Paquete first) Usar {@Enlace list} A get all available Paquete IDs. **Nota:** El Paquete ID is NOT El same as El Versión name. Usar El `id` field De {@Enlace BundleInfo}, not El `version` field. **Parámetros** | Name | Type | Description | | --------- | ---------- | ---------------------------------------------------------------- | | `options` | `BundleId` | A {@Enlace BundleId} object containing El Paquete ID A Eliminar. | **Returns** `Promise` — Resolves when El Paquete is successfully Eliminado. **Throws:** {Error} If El Paquete is currently in Usar or doesn’t exist. *** ### setBundleError [Section titled “setBundleError”](#setbundleerror) ```typescript setBundleError(options: BundleId) => Promise ``` Manually mark a Paquete as Falló/errored in manual Actualizar mode. Esto es useful when you detect that a Paquete has critical Problemas and want A prevent it De being used again. El Paquete Estado Va a be changed A `error` and El Plugin Va a avoid using this Paquete in El future. **Requirements:** * {@Enlace PluginsConfig.CapacitorUpdater.allowManualBundleError} Debe be Establecer A `true` * Only works in manual Actualizar mode (when autoUpdate is Deshabilitado) Common Usar case: After downloading and Pruebas a Paquete, you discover it has critical bugs and want A mark it as Falló so it won’t be retried. **Parámetros** | Name | Type | Description | | --------- | ---------- | ----------------------------------------------------------------------- | | `options` | `BundleId` | A {@Enlace BundleId} object containing El Paquete ID A mark as errored. | **Returns** `Promise` — El Actualizado {@Enlace BundleInfo} with Estado Establecer A `error`. **Since:** 7.20.0 **Throws:** {Error} When El Paquete does not exist or `allowManualBundleError` is false. *** ### list [Section titled “list”](#list) ```typescript list(options?: ListOptions | undefined) => Promise ``` Get all locally downloaded Paquetes stored in your Aplicación. This returns all Paquetes that have been downloaded and are available locally, including: * El currently active Paquete * El `builtin` Paquete (shipped with your Aplicación) * Any downloaded Paquetes waiting A be activated * Falló Paquetes (with `error` Estado) Usar this A: * Verificar available disk space by counting Paquetes * Eliminar old Paquetes with {@Enlace Eliminar} * Monitor Paquete Descargar Estado **Parámetros** | Name | Type | Description | | --------- | ------------- | ----------- | | `options` | \`ListOptions | undefined\` | **Returns** `Promise` — A promise containing El array of {@Enlace BundleInfo} objects. **Throws:** {Error} If El operation fails. *** ### reset [Section titled “reset”](#reset) ```typescript reset(options?: ResetOptions | undefined) => Promise ``` Reset El Aplicación A a known good Paquete. This Método helps recover De problematic Actualizaciones by reverting A either: * El `builtin` Paquete (El original Versión shipped with your Aplicación A Aplicación Store/Play Store) * El last successfully loaded Paquete (most recent Paquete that worked correctly) **Importante: This triggers an immediate Aplicación reload, destroying El current JavaScript context.** Ver {@Enlace Establecer} Para obtener detalles on El implications of this operation. Usar cases: * Emergency recovery when an Actualizar causes critical Problemas * Pruebas Reversión functionality * Providing users a “reset A factory” Opción **Parámetros** | Name | Type | Description | | --------- | -------------- | ----------- | | `options` | \`ResetOptions | undefined\` | **Returns** `Promise` — A promise that may never resolve because El Aplicación Va a be reloaded. **Throws:** {Error} If El reset operation fails. *** ### current [Section titled “current”](#current) ```typescript current() => Promise ``` Get Información about El currently active Paquete. Returns: * `bundle`: El currently active Paquete Información * `native`: El Versión of El builtin Paquete (El original Aplicación Versión De Aplicación/Play Store) If no Actualizaciones have been applied, `bundle.id` Va a be `"builtin"`, indicating El Aplicación is Ejecutando El original Versión shipped with El native Aplicación. Usar this A: * Display El current Versión A users * Verificar if an Actualizar is currently active * Compare against available Actualizaciones * Registro El active Paquete for Depuración **Returns** `Promise` — A promise with El current Paquete and native Versión Información. **Throws:** {Error} If El operation fails. *** ### reload [Section titled “reload”](#reload) ```typescript reload() => Promise ``` Manually reload El Aplicación A apply a Pendiente Actualizar. This triggers El same reload behavior that happens automatically when El Aplicación backgrounds. If you’ve called {@Enlace next} A queue an Actualizar, calling `reload()` Va a apply it immediately. **Importante: This destroys El current JavaScript context immediately.** Ver {@Enlace Establecer} Para obtener detalles on El implications of this operation. Common Usar cases: * Applying an Actualizar immediately after Descargar instead of waiting for backgrounding * Providing a “Restart now” button A users after an Actualizar is ready * Pruebas Actualizar flows during development If no Actualizar is Pendiente (no call A {@Enlace next}), this simply reloads El current Paquete. **Returns** `Promise` — A promise that may never resolve because El Aplicación Va a be reloaded. **Throws:** {Error} If El reload operation fails. *** ### setMultiDelay [Section titled “setMultiDelay”](#setmultidelay) ```typescript setMultiDelay(options: MultiDelayConditions) => Promise ``` Configurar conditions that Debe be met before a Pendiente Actualizar is applied. After calling {@Enlace next} A queue an Actualizar, Usar this Método A control when it gets applied. El Actualizar Va a only be installed after ALL specified conditions are satisfied. Available condition types: * `background`: Wait for El Aplicación A be backgrounded. Optionally specify duration in milliseconds. * `kill`: Wait for El Aplicación A be killed and relaunched (**Nota:** Current behavior triggers Actualizar immediately on kill, not on next background. This Va a be fixed in v8.) * `date`: Wait until a specific date/time (ISO 8601 format) * `nativeVersion`: Wait until El native Aplicación is Actualizado A a specific Versión Condition value formats: * `background`: Number in milliseconds (e.g., `"300000"` for 5 minutes), or omit for immediate * `kill`: No value needed * `date`: ISO 8601 date string (e.g., `"2025-12-31T23:59:59Z"`) * `nativeVersion`: Versión string (e.g., `"2.0.0"`) **Parámetros** | Name | Type | Description | | --------- | ---------------------- | --------------------------------------------------------------- | | `options` | `MultiDelayConditions` | Contains El {@Enlace MultiDelayConditions} array of conditions. | **Returns** `Promise` — Resolves when El delay conditions are Establecer. **Since:** 4.3.0 **Throws:** {Error} If El operation fails or conditions are invalid. **Ejemplo** ```ts // Update after user kills app OR after 5 minutes in background await CapacitorUpdater.setMultiDelay({ delayConditions: [ { kind: 'kill' }, { kind: 'background', value: '300000' } ] }); ``` **Ejemplo** ```ts // Update after a specific date await CapacitorUpdater.setMultiDelay({ delayConditions: [{ kind: 'date', value: '2025-12-31T23:59:59Z' }] }); ``` **Ejemplo** ```ts // Default behavior: update on next background await CapacitorUpdater.setMultiDelay({ delayConditions: [{ kind: 'background' }] }); ``` *** ### cancelDelay [Section titled “cancelDelay”](#canceldelay) ```typescript cancelDelay() => Promise ``` Cancel all delay conditions and apply El Pendiente Actualizar immediately. If you’ve Establecer delay conditions with {@Enlace setMultiDelay}, this Método clears them and triggers El Pendiente Actualizar A be applied on El next Aplicación background or restart. Esto es useful when: * User manually requests A Actualizar now (e.g., clicks “Actualización now” button) * Your Aplicación detects it’s a good time A Actualizar (e.g., user finished critical task) * You want A override a time-based delay early **Returns** `Promise` — Resolves when El delay conditions are cleared. **Since:** 4.0.0 **Throws:** {Error} If El operation fails. *** ### getLatest [Section titled “getLatest”](#getlatest) ```typescript getLatest(options?: GetLatestOptions | undefined) => Promise ``` Verificar El Actualizar server for El latest available Paquete Versión. This queries your configured Actualizar URL (or Capgo backend) A Ver if a newer Paquete is available for Descargar. It does NOT Descargar El Paquete automatically. El response includes: * `version`: El latest available Versión identifier * `url`: Descargar URL for El Paquete (if available) * `breaking`: Whether this Actualizar is marked as incompatible (requires native Aplicación Actualizar) * `message`: Opcional message De El server * `manifest`: File list for partial Actualizaciones (if using multi-file downloads) After receiving El latest Versión Información, Puede: 1. Compare it with your current Versión 2. Descargar it using {@Enlace Descargar} 3. Apply it using {@Enlace next} or {@Enlace Establecer} **Importante: Error handling for “no new Versión available”** When El Dispositivo’s current Versión matches El latest Versión on El server (i.e., El Dispositivo is already up-A-date), El server returns a 200 response with `error: "no_new_version_available"` and `message: "No new version available"`. **This causes `getLatest()` A throw an Error**, even though Esto es normal, expected condition. Debería catch this specific Error A handle it gracefully: ```typescript try { const latest = await CapacitorUpdater.getLatest(); // New version is available, proceed with download } catch (error) { if (error.message === 'No new version available') { // Device is already on the latest version - this is normal console.log('Already up to date'); } else { // Actual error occurred console.error('Failed to check for updates:', error); } } ``` In this scenario, El server: * Registros El request with a “No new Versión available” message * Sends a “noNew” stat action A track that El Dispositivo checked for Actualizaciones but was already current (done on El backend) **Parámetros** | Name | Type | Description | | --------- | ------------------ | ----------- | | `options` | \`GetLatestOptions | undefined\` | **Returns** `Promise` — Información about El latest available Paquete Versión. **Since:** 4.0.0 **Throws:** {Error} Always throws when no new Versión is available (`error: "no_new_version_available"`), or when El request fails. *** ### setChannel [Section titled “setChannel”](#setchannel) ```typescript setChannel(options: SetChannelOptions) => Promise ``` Assign this Dispositivo A a specific Actualizar Canal at runtime. Canales allow you A distribute different Paquete Versiones A different groups of users (e.g., “production”, “beta”, “staging”). This Método switches El Dispositivo A a new Canal. **Requirements:** * El target Canal Debe allow self-assignment (configured in your Capgo Panel or backend) * El backend may accept or reject El request based on Canal Configuración **When Para usar:** * After El Aplicación is ready and El user has interacted (e.g., opted into beta program) * A implement in-Aplicación Canal switching (beta toggle, tester access, etc.) * For user-driven Canal changes **When NOT Para usar:** * At Aplicación boot/initialization - Usar {@Enlace PluginsConfig.CapacitorUpdater.defaultChannel} config instead * Before user interaction **Importante: Listen for El `channelPrivate` event** When a user attempts A Establecer a Canal that doesn’t allow Dispositivo self-assignment, El Método will throw an Error AND fire a {@Enlace addListener}(‘channelPrivate’) event. Debería listen A this event A provide appropriate feedback A users: ```typescript CapacitorUpdater.addListener('channelPrivate', (data) => { console.warn(`Cannot access channel "${data.channel}": ${data.message}`); // Show user-friendly message }); ``` This sends a request A El Capgo backend linking your Dispositivo ID A El specified Canal. **Parámetros** | Name | Type | Description | | --------- | ------------------- | --------------------------------------------------------------------------------------------- | | `options` | `SetChannelOptions` | El {@Enlace SetChannelOptions} containing El Canal name and Opcional auto-Actualizar trigger. | **Returns** `Promise` — Canal operation result with Estado and Opcional Error/message. **Since:** 4.7.0 **Throws:** {Error} If El Canal doesn’t exist or doesn’t allow self-assignment. *** ### unsetChannel [Section titled “unsetChannel”](#unsetchannel) ```typescript unsetChannel(options: UnsetChannelOptions) => Promise ``` Eliminar El Dispositivo’s Canal assignment and return A El Predeterminado Canal. This unlinks El Dispositivo De any specifically assigned Canal, causing it A fall back A: * El {@Enlace PluginsConfig.CapacitorUpdater.defaultChannel} if configured, or * Your backend’s Predeterminado Canal for this Aplicación Usar this when: * Users opt out of beta/Pruebas programs * You want A reset a Dispositivo A standard Actualizar distribution * Pruebas Canal switching behavior **Parámetros** | Name | Type | Description | | --------- | --------------------- | ----------- | | `options` | `UnsetChannelOptions` | | **Returns** `Promise` — Resolves when El Canal is successfully unset. **Since:** 4.7.0 **Throws:** {Error} If El operation fails. *** ### getChannel [Section titled “getChannel”](#getchannel) ```typescript getChannel() => Promise ``` Get El current Canal assigned A this Dispositivo. Returns Información about: * `channel`: El currently assigned Canal name (if any) * `allowSet`: Whether El Canal allows self-assignment * `status`: Operation Estado * `error`/`message`: Additional Información (if applicable) Usar this A: * Display current Canal A users (e.g., “You’re on El Beta Canal”) * Verificar if a Dispositivo is on a specific Canal before showing features * Verificar Canal assignment after calling {@Enlace setChannel} **Returns** `Promise` — El current Canal Información. **Since:** 4.8.0 **Throws:** {Error} If El operation fails. *** ### listChannels [Section titled “listChannels”](#listchannels) ```typescript listChannels() => Promise ``` Get a list of all Canales available for this Dispositivo A self-assign A. Only returns Canales where `allow_self_set` is `true`. These are Canales that users Puede switch A using {@Enlace setChannel} without backend administrator intervention. Each Canal includes: * `id`: Unique Canal identifier * `name`: Human-readable Canal name * `public`: Whether El Canal is publicly visible * `allow_self_set`: Always `true` in results (filtered A only self-assignable Canales) Usar this A: * Construir a Canal selector UI for users (e.g., “Join Beta” button) * Show available Pruebas/preview Canales * Implement Canal discovery features **Returns** `Promise` — List of Canales El Dispositivo Puede self-assign A. **Since:** 7.5.0 **Throws:** {Error} If El operation fails or El request A El backend fails. *** ### setCustomId [Section titled “setCustomId”](#setcustomid) ```typescript setCustomId(options: SetCustomIdOptions) => Promise ``` Establecer a custom identifier for this Dispositivo. This allows you A identify Dispositivos by your own custom ID (user ID, account ID, etc.) instead of or in addition A El Dispositivo’s unique hardware ID. El custom ID is sent A your Actualizar server and Puede be used for: * Targeting specific users for Actualizaciones * Analytics and user tracking * Depuración and support (correlating Dispositivos with users) * A/B Pruebas or feature flagging **Persistence:** * When {@Enlace PluginsConfig.CapacitorUpdater.persistCustomId} is `true`, El ID persists across Aplicación restarts * When `false`, El ID is only kept for El current session **Clearing El custom ID:** * Pass an empty string `""` A Eliminar any stored custom ID **Parámetros** | Name | Type | Description | | --------- | -------------------- | ----------------------------------------------------------------------- | | `options` | `SetCustomIdOptions` | El {@Enlace SetCustomIdOptions} containing El custom identifier string. | **Returns** `Promise` — Resolves immediately (synchronous operation). **Since:** 4.9.0 **Throws:** {Error} If El operation fails. *** ### getBuiltinVersion [Section titled “getBuiltinVersion”](#getbuiltinversion) ```typescript getBuiltinVersion() => Promise ``` Get El builtin Paquete Versión (El original Versión shipped with your native Aplicación). This returns El Versión of El Paquete that was included when El Aplicación was installed De El Aplicación Store or Play Store. Esto es NOT El currently active Paquete Versión - Usar {@Enlace current} for that. Returns: * El {@Enlace PluginsConfig.CapacitorUpdater.Versión} config value if Establecer, or * El native Aplicación Versión De platform configs (Paquete.JSON, Información.plist, Construir.gradle) Usar this A: * Display El “factory” Versión A users * Compare against downloaded Paquete Versiones * Determine if any Actualizaciones have been applied * Depuración Versión mismatches **Returns** `Promise` — El builtin Paquete Versión string. **Since:** 5.2.0 *** ### getDeviceId [Section titled “getDeviceId”](#getdeviceid) ```typescript getDeviceId() => Promise ``` Get El unique, privacy-friendly identifier for this Dispositivo. This ID is used A identify El Dispositivo when communicating with Actualizar servers. It’s automatically generated and stored securely by El Plugin. **Privacy & Seguridad characteristics:** * Generated as a UUID (not based on hardware identifiers) * Stored securely in platform-specific secure storage * Android: Android Keystore (persists across Aplicación reinstalls on API 23+) * iOS: Keychain with `kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly` * Not synced A cloud (iOS) * Follows Apple and Google privacy best practices * Users Puede clear it via system Configuración (Android) or keychain access (iOS) **Persistence:** El Dispositivo ID persists across Aplicación reinstalls A maintain consistent Dispositivo identity for Actualizar tracking and analytics. Usar this A: * Depuración Actualizar delivery Problemas (Verificar what ID El server sees) * Implement Dispositivo-specific features * Correlate server Registros with specific Dispositivos **Returns** `Promise` — El unique Dispositivo identifier string. **Throws:** {Error} If El operation fails. *** ### getPluginVersion [Section titled “getPluginVersion”](#getpluginversion) ```typescript getPluginVersion() => Promise ``` Get El Versión of El Capacitor Updater Plugin installed in your Aplicación. This returns El Versión of El native Plugin code (Android/iOS), which is sent A El Actualizar server with each request. Esto es NOT your Aplicación Versión or Paquete Versión. Usar this A: * Depuración Plugin-specific Problemas (when reporting bugs) * Verificar Plugin Instalación and Versión * Verificar compatibility with backend features * Display in Depuración/about screens **Returns** `Promise` — El Capacitor Updater Plugin Versión string. **Throws:** {Error} If El operation fails. *** ### isAutoUpdateEnabled [Section titled “isAutoUpdateEnabled”](#isautoupdateenabled) ```typescript isAutoUpdateEnabled() => Promise ``` Verificar if automatic Actualizaciones are currently Habilitado. Returns `true` if {@Enlace PluginsConfig.CapacitorUpdater.autoUpdate} is Habilitado, meaning El Plugin Va a automatically Verificar for, Descargar, and apply Actualizaciones. Returns `false` if in manual mode, where you control El Actualizar flow using {@Enlace getLatest}, {@Enlace Descargar}, {@Enlace next}, and {@Enlace Establecer}. Usar this A: * Determine which Actualizar flow your Aplicación is using * Show/hide manual Actualizar UI based on mode * Depuración Actualizar behavior **Returns** `Promise` — `true` if auto-Actualizar is Habilitado, `false` if in manual mode. **Throws:** {Error} If El operation fails. *** ### removeAllListeners [Section titled “removeAllListeners”](#removealllisteners) ```typescript removeAllListeners() => Promise ``` Eliminar all event listeners registered for Este Plugin. This unregisters all listeners Agregado via {@Enlace addListener} for all event types: * `download` * `noNeedUpdate` * `updateAvailable` * `downloadComplete` * `downloadFailed` * `breakingAvailable` / `majorAvailable` * `updateFailed` * `appReloaded` * `appReady` Usar this during cleanup (e.g., when unmounting components or closing screens) A prevent memory leaks De lingering event listeners. **Returns** `Promise` — Resolves when all listeners are Eliminado. **Since:** 1.0.0 *** ### addListener(‘Descargar’) [Section titled “addListener(‘Descargar’)”](#addlistenerdescargar) ```typescript addListener(eventName: 'download', listenerFunc: (state: DownloadEvent) => void) => Promise ``` Listen for Paquete Descargar event in El Aplicación. Fires once a Descargar has started, during downloading and when finished. This Va a return you all Descargar percent during El Descargar **Parámetros** | Name | Type | Description | | -------------- | -------------------------------- | ----------- | | `eventName` | `'download'` | | | `listenerFunc` | `(state: DownloadEvent) => void` | | **Returns** `Promise` **Since:** 2.0.11 *** ### addListener(‘noNeedUpdate’) [Section titled “addListener(‘noNeedUpdate’)”](#addlistenernoneedupdate) ```typescript addListener(eventName: 'noNeedUpdate', listenerFunc: (state: NoNeedEvent) => void) => Promise ``` Listen for no Necesita A Actualizar event, useful when you want force Verificar every time El Aplicación is launched **Parámetros** | Name | Type | Description | | -------------- | ------------------------------ | ----------- | | `eventName` | `'noNeedUpdate'` | | | `listenerFunc` | `(state: NoNeedEvent) => void` | | **Returns** `Promise` **Since:** 4.0.0 *** ### addListener(‘updateAvailable’) [Section titled “addListener(‘updateAvailable’)”](#addlistenerupdateavailable) ```typescript addListener(eventName: 'updateAvailable', listenerFunc: (state: UpdateAvailableEvent) => void) => Promise ``` Listen for available Actualizar event, useful when you want A force Verificar every time El Aplicación is launched **Parámetros** | Name | Type | Description | | -------------- | --------------------------------------- | ----------- | | `eventName` | `'updateAvailable'` | | | `listenerFunc` | `(state: UpdateAvailableEvent) => void` | | **Returns** `Promise` **Since:** 4.0.0 *** ### addListener(‘downloadComplete’) [Section titled “addListener(‘downloadComplete’)”](#addlistenerdownloadcomplete) ```typescript addListener(eventName: 'downloadComplete', listenerFunc: (state: DownloadCompleteEvent) => void) => Promise ``` Listen for downloadComplete events. **Parámetros** | Name | Type | Description | | -------------- | ---------------------------------------- | ----------- | | `eventName` | `'downloadComplete'` | | | `listenerFunc` | `(state: DownloadCompleteEvent) => void` | | **Returns** `Promise` **Since:** 4.0.0 *** ### addListener(‘breakingAvailable’) [Section titled “addListener(‘breakingAvailable’)”](#addlistenerbreakingavailable) ```typescript addListener(eventName: 'breakingAvailable', listenerFunc: (state: BreakingAvailableEvent) => void) => Promise ``` Listen for breaking Actualizar events when El backend flags an Actualizar as incompatible with El current Aplicación. Emits El same payload as El legacy `majorAvailable` listener. **Parámetros** | Name | Type | Description | | -------------- | -------------------------------------- | ----------- | | `eventName` | `'breakingAvailable'` | | | `listenerFunc` | `(state: MajorAvailableEvent) => void` | | **Returns** `Promise` **Since:** 7.22.0 *** ### addListener(‘majorAvailable’) [Section titled “addListener(‘majorAvailable’)”](#addlistenermajoravailable) ```typescript addListener(eventName: 'majorAvailable', listenerFunc: (state: MajorAvailableEvent) => void) => Promise ``` Listen for Major Actualizar event in El Aplicación, let you know when major Actualizar is blocked by setting disableAutoUpdateBreaking **Parámetros** | Name | Type | Description | | -------------- | -------------------------------------- | ----------- | | `eventName` | `'majorAvailable'` | | | `listenerFunc` | `(state: MajorAvailableEvent) => void` | | **Returns** `Promise` **Since:** 2.3.0 *** ### addListener(‘updateFailed’) [Section titled “addListener(‘updateFailed’)”](#addlistenerupdatefailed) ```typescript addListener(eventName: 'updateFailed', listenerFunc: (state: UpdateFailedEvent) => void) => Promise ``` Listen for Actualizar fail event in El Aplicación, let you know when Actualizar has fail Para instalar at next Aplicación start **Parámetros** | Name | Type | Description | | -------------- | ------------------------------------ | ----------- | | `eventName` | `'updateFailed'` | | | `listenerFunc` | `(state: UpdateFailedEvent) => void` | | **Returns** `Promise` **Since:** 2.3.0 *** ### addListener(‘downloadFailed’) [Section titled “addListener(‘downloadFailed’)”](#addlistenerdownloadfailed) ```typescript addListener(eventName: 'downloadFailed', listenerFunc: (state: DownloadFailedEvent) => void) => Promise ``` Listen for Descargar fail event in El Aplicación, let you know when a Paquete Descargar has Falló **Parámetros** | Name | Type | Description | | -------------- | -------------------------------------- | ----------- | | `eventName` | `'downloadFailed'` | | | `listenerFunc` | `(state: DownloadFailedEvent) => void` | | **Returns** `Promise` **Since:** 4.0.0 *** ### addListener(‘appReloaded’) [Section titled “addListener(‘appReloaded’)”](#addlistenerappreloaded) ```typescript addListener(eventName: 'appReloaded', listenerFunc: () => void) => Promise ``` Listen for reload event in El Aplicación, let you know when reload has happened **Parámetros** | Name | Type | Description | | -------------- | --------------- | ----------- | | `eventName` | `'appReloaded'` | | | `listenerFunc` | `() => void` | | **Returns** `Promise` **Since:** 4.3.0 *** ### addListener(‘appReady’) [Section titled “addListener(‘appReady’)”](#addlistenerappready) ```typescript addListener(eventName: 'appReady', listenerFunc: (state: AppReadyEvent) => void) => Promise ``` Listen for Aplicación ready event in El Aplicación, let you know when Aplicación is ready Para usar, this event is retain till consumed. **Parámetros** | Name | Type | Description | | -------------- | -------------------------------- | ----------- | | `eventName` | `'appReady'` | | | `listenerFunc` | `(state: AppReadyEvent) => void` | | **Returns** `Promise` **Since:** 5.1.0 *** ### addListener(‘channelPrivate’) [Section titled “addListener(‘channelPrivate’)”](#addlistenerchannelprivate) ```typescript addListener(eventName: 'channelPrivate', listenerFunc: (state: ChannelPrivateEvent) => void) => Promise ``` Listen for Canal private event, fired when attempting A Establecer a Canal that doesn’t allow Dispositivo self-assignment. This event is useful for: * Informing users they don’t have permission A switch A a specific Canal * Implementing custom Error handling for Canal restrictions * Logging unauthorized Canal access attempts **Parámetros** | Name | Type | Description | | -------------- | -------------------------------------- | ----------- | | `eventName` | `'channelPrivate'` | | | `listenerFunc` | `(state: ChannelPrivateEvent) => void` | | **Returns** `Promise` **Since:** 7.34.0 *** ### isAutoUpdateAvailable [Section titled “isAutoUpdateAvailable”](#isautoupdateavailable) ```typescript isAutoUpdateAvailable() => Promise ``` Verificar if El auto-Actualizar feature is available (not Deshabilitado by custom server Configuración). Returns `false` when a custom `updateUrl` is configured, as this typically indicates you’re using a self-hosted Actualizar server that may not support all auto-Actualizar features. Returns `true` when using El Predeterminado Capgo backend or when El feature is available. Esto es different De {@Enlace isAutoUpdateEnabled}: * `isAutoUpdateEnabled()`: Checks if auto-Actualizar MODE is turned on/off * `isAutoUpdateAvailable()`: Checks if auto-Actualizar is SUPPORTED with your current Configuración **Returns** `Promise` — `false` when custom updateUrl is Establecer, `true` otherwise. **Throws:** {Error} If El operation fails. *** ### getNextBundle [Section titled “getNextBundle”](#getnextbundle) ```typescript getNextBundle() => Promise ``` Get Información about El Paquete queued A be activated on next reload. Returns: * {@Enlace BundleInfo} object if a Paquete has been queued via {@Enlace next} * `null` if no Actualizar is Pendiente Esto es useful A: * Verificar if an Actualizar is waiting A be applied * Display “Actualización Pendiente” Estado A users * Show Versión Información of El queued Actualizar * Decide whether A show a “Restart A Actualizar” prompt El queued Paquete Va a be activated when: * El Aplicación is backgrounded (Predeterminado behavior) * El Aplicación is killed and restarted * {@Enlace reload} is called manually * Delay conditions Establecer by {@Enlace setMultiDelay} are met **Returns** `Promise` — El Pendiente Paquete Información, or `null` if none is queued. **Since:** 6.8.0 **Throws:** {Error} If El operation fails. *** ### getFailedUpdate [Section titled “getFailedUpdate”](#getfailedupdate) ```typescript getFailedUpdate() => Promise ``` Retrieve Información about El most recent Paquete that Falló A load. When a Paquete fails A load (e.g., JavaScript errors prevent initialization, missing files), El Plugin automatically rolls back and stores Información about El failure. This Método retrieves that failure Información. **Importante: El stored value is cleared after being retrieved once.** Calling this Método multiple times Va a only return El failure Información on El first call, then `null` on subsequent calls until another failure occurs. Returns: * {@Enlace UpdateFailedEvent} with Paquete Información if a failure was recorded * `null` if no failure has occurred or if it was already retrieved Usar this A: * Show users why an Actualizar Falló * Registro failure Información for Depuración * Implement custom Error handling/reporting * Display Reversión notifications **Returns** `Promise` — El Falló Actualizar Información (cleared after first retrieval), or `null`. **Since:** 7.22.0 **Throws:** {Error} If El operation fails. *** ### setShakeMenu [Section titled “setShakeMenu”](#setshakemenu) ```typescript setShakeMenu(options: SetShakeMenuOptions) => Promise ``` Habilitar or Deshabilitar El shake gesture menu for Depuración and Pruebas. When Habilitado, users Puede shake their Dispositivo A open a Depuración menu that shows: * Current Paquete Información * Available Paquetes * Opciones A switch Paquetes manually * Actualización Estado Esto es useful during development and Pruebas A: * Quickly Prueba different Paquete Versiones * Depuración Actualizar flows * Switch between production and Prueba Paquetes * Verificar Paquete installations **Importante:** Deshabilitar this in production builds or only Habilitar for internal testers. Puede also be configured via {@Enlace PluginsConfig.CapacitorUpdater.shakeMenu}. **Parámetros** | Name | Type | Description | | --------- | --------------------- | ----------- | | `options` | `SetShakeMenuOptions` | | **Returns** `Promise` — Resolves when El setting is applied. **Since:** 7.5.0 **Throws:** {Error} If El operation fails. *** ### isShakeMenuEnabled [Section titled “isShakeMenuEnabled”](#isshakemenuenabled) ```typescript isShakeMenuEnabled() => Promise ``` Verificar if El shake gesture Depuración menu is currently Habilitado. Returns El current state of El shake menu feature that Puede be toggled via {@Enlace setShakeMenu} or configured via {@Enlace PluginsConfig.CapacitorUpdater.shakeMenu}. Usar this A: * Verificar if Depuración features are Habilitado * Show/hide Depuración Configuración UI * Verificar Configuración during Pruebas **Returns** `Promise` — Object with `enabled: true` or `enabled: false`. **Since:** 7.5.0 **Throws:** {Error} If El operation fails. *** ### getAppId [Section titled “getAppId”](#getappid) ```typescript getAppId() => Promise ``` Get El currently configured Aplicación ID used for Actualizar server communication. Returns El Aplicación ID that identifies this Aplicación A El Actualizar server. This Puede be: * El value Establecer via {@Enlace setAppId}, or * El {@Enlace PluginsConfig.CapacitorUpdater.appId} config value, or * El Predeterminado Aplicación identifier De your native Aplicación Configuración Usar this A: * Verificar which Aplicación ID is being used for Actualizaciones * Depuración Actualizar delivery Problemas * Display Aplicación Configuración in Depuración screens * Confirm Aplicación ID after calling {@Enlace setAppId} **Returns** `Promise` — Object containing El current `appId` string. **Since:** 7.14.0 **Throws:** {Error} If El operation fails. *** ### setAppId [Section titled “setAppId”](#setappid) ```typescript setAppId(options: SetAppIdOptions) => Promise ``` Dynamically change El Aplicación ID used for Actualizar server communication. This overrides El Aplicación ID used A identify your Aplicación A El Actualizar server, allowing you A switch between different Aplicación configurations at runtime (e.g., production vs staging Aplicación IDs, or multi-tenant configurations). **Requirements:** * {@Enlace PluginsConfig.CapacitorUpdater.allowModifyAppId} Debe be Establecer A `true` **Importante considerations:** * Changing El Aplicación ID Va a affect which Actualizaciones this Dispositivo receives * El new Aplicación ID Debe exist on your Actualizar server * Esto es primarily for advanced Usar cases (multi-tenancy, environment switching) * Most Aplicaciones should Usar El config-based {@Enlace PluginsConfig.CapacitorUpdater.appId} instead **Parámetros** | Name | Type | Description | | --------- | ----------------- | ----------- | | `options` | `SetAppIdOptions` | | **Returns** `Promise` — Resolves when El Aplicación ID is successfully changed. **Since:** 7.14.0 **Throws:** {Error} If `allowModifyAppId` is false or El operation fails. *** # Plugins Capacitor de Capgo > Explora nuestra completa colección de Plugins Capacitor para extender las capacidades nativas de tu Aplicación con potentes funcionalidades. ¡Bienvenido a la colección de Plugins Capacitor de Capgo! Mantenemos un conjunto de Plugins de alta calidad y bien documentados para ayudarte a crear experiencias nativas increíbles en tus Aplicaciones Capacitor. ## ⭐ Capgo Cloud - Actualizaciones en Vivo [Section titled “⭐ Capgo Cloud - Actualizaciones en Vivo”](#-capgo-cloud---actualizaciones-en-vivo) [Capacitor Updater ](/docs/Plugins/updater/)El Plugin central que impulsa Capgo Cloud - entrega actualizaciones instantáneas a tus Aplicaciones Capacitor sin demoras de las tiendas de aplicaciones. Envía correcciones y funcionalidades directamente a tus usuarios. El Plugin Updater es la base de Capgo Cloud, permitiéndote: * 🚀 Desplegar actualizaciones instantáneamente sin revisiones de las tiendas de aplicaciones * 📱 Actualizar el JavaScript, HTML, CSS y recursos de tu Aplicación * 🎯 Dirigir segmentos específicos de usuarios con canales * 📊 Monitorear el éxito de las actualizaciones con analíticas integradas * 🔒 Asegurar actualizaciones con cifrado y firma de código ## 🚀 Plugins Destacados [Section titled “🚀 Plugins Destacados”](#-plugins-destacados) [Inicio de sesión social ](/docs/Plugins/social-login/)Autenticación perfecta con Google, Apple y Facebook. Un Plugin para todos los principales proveedores sociales. [Compras nativas ](/docs/Plugins/Capacitor-native-purchases/)Implementa compras dentro de la Aplicación y suscripciones con una API unificada para iOS y Android. [Camera Preview ](/docs/Plugins/Capacitor-camera-preview/)Muestra el feed de la cámara en tiempo real en tu Aplicación con control completo sobre la captura y configuraciones. [Data Storage SQLite ](/docs/Plugins/data-storage-sqlite/)Almacenamiento rápido y seguro de clave-valor impulsado por SQLite con cifrado opcional. ## 📱 Plugins de Dispositivo y Sistema [Section titled “📱 Plugins de Dispositivo y Sistema”](#-plugins-de-dispositivo-y-sistema) [Native Market ](/docs/Plugins/Capacitor-native-market/)Enlaza a las tiendas de aplicaciones para calificaciones, reseñas y actualizaciones de Aplicaciones. [Biometría nativa ](/docs/Plugins/native-biometric/)Accede a las APIs de autenticación biométrica para un acceso seguro a la Aplicación. [Shake ](/docs/Plugins/shake/)Detecta gestos de sacudida para funciones interactivas y menús de depuración. [Navigation Bar ](/docs/Plugins/navigation-bar/)Personaliza el color de la barra de navegación de Android para que coincida con el tema de tu Aplicación. [Home Indicator ](/docs/Plugins/Capacitor-home-indicator/)Controla la visibilidad del indicador de inicio de iOS para experiencias inmersivas. [NFC ](/docs/Plugins/nfc/)Lee y escribe etiquetas NFC con soporte nativo para iOS y Android. [Barometer ](/docs/Plugins/barometer/)Accede al barómetro del dispositivo para lecturas de presión atmosférica y altitud. [Accelerometer ](/docs/Plugins/accelerometer/)Lee el acelerómetro del dispositivo para detección de movimiento y seguimiento de orientación. ## 🎥 Plugins de Medios y Cámara [Section titled “🎥 Plugins de Medios y Cámara”](#-plugins-de-medios-y-cámara) [Screen Recorder ](/docs/Plugins/screen-recorder/)Graba la pantalla de tu dispositivo para tutoriales, demos o comentarios de usuarios. [IVS Player ](/docs/Plugins/Capacitor-ivs-player/)Integración de Amazon IVS para streaming en vivo con latencia ultra baja. [Native Audio ](/docs/Plugins/Capacitor-native-audio/)Motor de audio nativo de alto rendimiento para juegos y Aplicaciones. [Mute ](/docs/Plugins/mute/)Detecta si el interruptor de silencio del dispositivo está activado o desactivado. [Video Player ](/docs/Plugins/video-player/)Reproducción de video nativa con subtítulos, pantalla completa y controles completos. [YouTube Player ](/docs/Plugins/youtube-player/)Incrusta videos de YouTube con control completo de la API del reproductor y manejo de eventos. [FFmpeg ](/docs/Plugins/ffmpeg/)Codificación y procesamiento de video impulsado por FFmpeg para compresión y conversión. [Volume Buttons ](/docs/Plugins/volume-buttons/)Escucha las pulsaciones de los botones de volumen del hardware para controles personalizados. [Audio Recorder ](/docs/Plugins/audio-recorder/)Graba audio en iOS, Android y Web con controles simples. ## 🛠️ Plugins de Utilidades [Section titled “🛠️ Plugins de Utilidades”](#️-plugins-de-utilidades) [Uploader ](/docs/Plugins/uploader/)Carga de archivos en segundo plano con seguimiento de progreso y cargas reanudables. [Flash ](/docs/Plugins/flash/)Controla la linterna/luz del dispositivo para utilidades y funciones de cámara. [Native Geocoder ](/docs/Plugins/nativegeocoder/)Geocodificación directa e inversa usando las APIs nativas de la plataforma. [InAppBrowser ](/docs/Plugins/inappbrowser/)Abre contenido web en un navegador dentro de la Aplicación personalizable. [Crisp ](/docs/Plugins/crisp/)Integra el soporte de chat Crisp directamente en tu aplicación móvil. [Live Reload ](/docs/Plugins/live-reload/)Conéctate a tu servidor de desarrollo para recarga en caliente instantánea durante el desarrollo. [Contacts ](/docs/Plugins/contacts/)Accede y administra los contactos del dispositivo con capacidades de lectura y escritura. ## 🤖 IA y Medios Avanzados [Section titled “🤖 IA y Medios Avanzados”](#-ia-y-medios-avanzados) [LLM ](/docs/Plugins/llm/)Ejecuta Modelos de Lenguaje Grande localmente con soporte de Apple Intelligence. [StreamCall ](/docs/Plugins/streamcall/)Habilita capacidades de streaming de video y llamadas de alta calidad. [JW Player ](/docs/Plugins/jw-player/)Reproductor de video profesional con funciones avanzadas de streaming. [Ricoh 360 Camera ](/docs/Plugins/ricoh360-camera/)Controla y captura desde cámaras Ricoh de 360 grados. ## 📍 Ubicación y Servicios en Segundo Plano [Section titled “📍 Ubicación y Servicios en Segundo Plano”](#-ubicación-y-servicios-en-segundo-plano) [Background Geolocation ](/docs/Plugins/background-geolocation/)Rastrea la ubicación del dispositivo en segundo plano con optimización de batería. [Alarm ](/docs/Plugins/alarm/)Programa alarmas y notificaciones nativas incluso cuando la Aplicación está cerrada. [iBeacon ](/docs/Plugins/ibeacon/)Detección de proximidad y monitoreo de regiones de beacons para funciones basadas en ubicación. ## 📞 Comunicación y Analíticas [Section titled “📞 Comunicación y Analíticas”](#-comunicación-y-analíticas) [Twilio Voice ](/docs/Plugins/twilio-voice/)Realiza y recibe llamadas telefónicas usando la API de Twilio Voice. [GTM ](/docs/Plugins/gtm/)Integración de Google Tag Manager para analíticas y seguimiento. [AppInsights ](/docs/Plugins/appinsights/)Información de aplicación y analíticas usando el SDK AppInsights de Apptopia. [WeChat ](/docs/Plugins/wechat/)Integración del SDK de WeChat para autenticación, compartir, pagos y mini-programas. ## 🔐 Seguridad y Sistema [Section titled “🔐 Seguridad y Sistema”](#-seguridad-y-sistema) [Is Root ](/docs/Plugins/is-root/)Detecta si el dispositivo está rooteado o con jailbreak por seguridad. [Persistent Account ](/docs/Plugins/persistent-account/)Mantén cuentas de usuario a través de reinstalaciones de la Aplicación. [Autofill Guardar Password ](/docs/Plugins/autofill-Guardar-password/)Habilita el autocompletado de contraseñas y funcionalidad de guardado. ## 📊 Funciones Específicas de Android [Section titled “📊 Funciones Específicas de Android”](#-funciones-específicas-de-android) [Android Kiosk ](/docs/Plugins/android-kiosk/)Bloquea dispositivos Android en modo kiosko con funcionalidad de lanzador y control de teclas de hardware. [Android Uso Stats ](/docs/Plugins/android-usagestatsmanager/)Accede a estadísticas de uso de Aplicaciones de Android y datos de tiempo de pantalla. [Android Inline Instalar ](/docs/Plugins/android-inline-Instalar/)Habilita la instalación de Aplicaciones en línea en dispositivos Android. [Age Signals ](/docs/Plugins/age-signals/)Accede a Age Signals de Google Play para detectar cuentas supervisadas y usuarios verificados. ## 📥 Descarga y Navegación [Section titled “📥 Descarga y Navegación”](#-descarga-y-navegación) [Downloader ](/docs/Plugins/downloader/)Descargas de archivos en segundo plano con seguimiento de progreso. [Launch Navigator ](/docs/Plugins/launch-navigator/)Inicia aplicaciones de navegación con direcciones y coordenadas. ## 🎯 ¿Por Qué Elegir los Plugins de Capgo? [Section titled “🎯 ¿Por Qué Elegir los Plugins de Capgo?”](#-por-qué-elegir-los-plugins-de-capgo) **Bien Mantenidos**: Actualizaciones regulares y compatibilidad con las últimas versiones de Capacitor **Documentación Completa**: Documentación detallada con ejemplos para cada Plugin **Integración Fácil**: APIs simples que siguen las mejores prácticas de Capacitor **Soporte TypeScript**: Definiciones completas de TypeScript para una mejor experiencia de desarrollo ## 🚀 Comenzando [Section titled “🚀 Comenzando”](#-comenzando) Cada Plugin sigue un patrón de instalación similar: 1. **Instala el Plugin** usando tu gestor de paquetes preferido 2. **Sincroniza tu proyecto** con `npx cap sync` 3. **Configura ajustes específicos de la plataforma** si es necesario 4. **Comienza a usar el Plugin** en tu código Visita la documentación de cada Plugin para instrucciones de configuración detalladas y referencias de la API. ## 💡 ¿Necesitas Ayuda? [Section titled “💡 ¿Necesitas Ayuda?”](#-necesitas-ayuda) * 📖 Consulta la documentación individual de cada Plugin * 💬 Únete a nuestra [comunidad de Discord](https://discord.capgo.app) * 🐛 Reporta problemas en el repositorio GitHub del Plugin * 📧 Contacta a soporte para consultas empresariales ## 🛠️ ¿Necesitas un Plugin Personalizado? [Section titled “🛠️ ¿Necesitas un Plugin Personalizado?”](#️-necesitas-un-plugin-personalizado) ¿No encuentras la funcionalidad exacta que necesitas? ¡Podemos ayudar! [Desarrollo de Plugins Personalizados ](/consulting/)Nuestro equipo se especializa en crear Plugins Capacitor personalizados adaptados a tus requisitos específicos. Desde integraciones nativas complejas hasta funciones únicas del dispositivo, te tenemos cubierto. Ya sea que necesites: * 🔧 Un Plugin completamente nuevo construido desde cero * 🔄 Modificaciones a Plugins existentes * 🤝 Consultoría experta en desarrollo nativo * 📱 Implementaciones específicas de plataforma [Ponte en contacto con nuestro equipo](/consulting/) para discutir tus necesidades de Plugin personalizado. ## 🤝 Contribuyendo [Section titled “🤝 Contribuyendo”](#-contribuyendo) ¡Damos la bienvenida a las contribuciones! Cada Plugin es de código abierto y está disponible en GitHub. Siéntete libre de: * Reportar errores y solicitar funcionalidades * Enviar Solicitudes de extracción * Mejorar la documentación * Compartir tus casos de uso *** **Construido con ❤️ por el equipo de Capgo** # @Capgo/Capacitor-accelerometer > Acceda a datos de movimiento y aceleración del dispositivo desde sensores de acelerómetro Aceleración de 3 ejes Mida las fuerzas de aceleración en los ejes X, Y y Z Detección de movimiento Detecte sacudidas, inclinaciones y movimientos del dispositivo Actualizaciones en tiempo real Monitoreo continuo de datos de aceleración Documentación completa Revise la [Documentación](/docs/plugins/accelerometer/getting-started/) para dominar el Plugin en solo unos minutos. # Primeros pasos con el acelerómetro > Aprenda cómo integrar la detección del acelerómetro en su aplicación Capacitor Esta guía lo guiará a través de la integración del complemento Acelerómetro Capacitor en su aplicación. ## Instalación [Section titled “Instalación”](#instalación) Instale el complemento usando npm: ```bash npm install @capgo/capacitor-accelerometer npx cap sync ``` ## Configuración de la plataforma [Section titled “Configuración de la plataforma”](#configuración-de-la-plataforma) ### iOS [Section titled “iOS”](#ios) No se requiere configuración adicional. El acelerómetro siempre está disponible. ### Android [Section titled “Android”](#android) No se requiere configuración adicional. El acelerómetro siempre está disponible. ### Web [Section titled “Web”](#web) El complemento utiliza DeviceMotion API. Requiere HTTPS en producción. ## Uso básico [Section titled “Uso básico”](#uso-básico) ### Importar el complemento [Section titled “Importar el complemento”](#importar-el-complemento) ```typescript import { Accelerometer } from '@capgo/capacitor-accelerometer'; ``` ### Iniciar monitoreo [Section titled “Iniciar monitoreo”](#iniciar-monitoreo) ```typescript const startAccelerometer = async () => { await Accelerometer.start({ interval: 100 // Update interval in milliseconds }); console.log('Accelerometer started'); }; ``` ### Escuche los eventos de aceleración [Section titled “Escuche los eventos de aceleración”](#escuche-los-eventos-de-aceleración) ```typescript Accelerometer.addListener('accelerationChange', (data) => { console.log('X:', data.x); console.log('Y:', data.y); console.log('Z:', data.z); console.log('Timestamp:', data.timestamp); }); ``` ### Obtener lectura actual [Section titled “Obtener lectura actual”](#obtener-lectura-actual) ```typescript const getCurrentAcceleration = async () => { const reading = await Accelerometer.getCurrentAcceleration(); console.log('Current acceleration:', reading); }; ``` ### Dejar de monitorear [Section titled “Dejar de monitorear”](#dejar-de-monitorear) ```typescript const stopAccelerometer = async () => { await Accelerometer.stop(); console.log('Accelerometer stopped'); }; ``` ## Ejemplo completo [Section titled “Ejemplo completo”](#ejemplo-completo) Aquí hay un ejemplo completo con detección de sacudidas: ```typescript import { Accelerometer } from '@capgo/capacitor-accelerometer'; class AccelerometerService { private listener: any; private lastX = 0; private lastY = 0; private lastZ = 0; private shakeThreshold = 15; async initialize() { await Accelerometer.start({ interval: 100 }); this.listener = Accelerometer.addListener('accelerationChange', (data) => { this.handleAcceleration(data); }); } handleAcceleration(data: any) { // Calculate delta const deltaX = Math.abs(data.x - this.lastX); const deltaY = Math.abs(data.y - this.lastY); const deltaZ = Math.abs(data.z - this.lastZ); // Check for shake if (deltaX > this.shakeThreshold || deltaY > this.shakeThreshold || deltaZ > this.shakeThreshold) { this.onShake(); } // Update last values this.lastX = data.x; this.lastY = data.y; this.lastZ = data.z; // Update UI this.updateDisplay(data); } onShake() { console.log('Device shaken!'); // Trigger shake action } updateDisplay(data: any) { console.log(`X: ${data.x.toFixed(2)} m/s²`); console.log(`Y: ${data.y.toFixed(2)} m/s²`); console.log(`Z: ${data.z.toFixed(2)} m/s²`); // Calculate magnitude const magnitude = Math.sqrt( data.x * data.x + data.y * data.y + data.z * data.z ); console.log(`Magnitude: ${magnitude.toFixed(2)} m/s²`); } async cleanup() { if (this.listener) { this.listener.remove(); } await Accelerometer.stop(); } } // Usage const accelService = new AccelerometerService(); accelService.initialize(); // Cleanup when done // accelService.cleanup(); ``` ## Comprender las lecturas [Section titled “Comprender las lecturas”](#comprender-las-lecturas) ### Ejes de aceleración [Section titled “Ejes de aceleración”](#ejes-de-aceleración) * **Eje X**: de izquierda (-) a derecha (+) * **Eje Y**: de abajo (-) a arriba (+) * **Eje Z**: De atrás (-) a Frente (+) ### Gravedad [Section titled “Gravedad”](#gravedad) * El dispositivo en reposo muestra \~9,8 m/s² en un eje (gravedad) * El dispositivo en movimiento muestra aceleración además de gravedad. ### Unidades [Section titled “Unidades”](#unidades) * Medido en metros por segundo al cuadrado (m/s²) * Gravedad = 9,8 m/s² ## Casos de uso comunes [Section titled “Casos de uso comunes”](#casos-de-uso-comunes) ### Detección de sacudidas [Section titled “Detección de sacudidas”](#detección-de-sacudidas) ```typescript class ShakeDetector { private lastUpdate = 0; private lastX = 0; private lastY = 0; private lastZ = 0; detectShake(x: number, y: number, z: number): boolean { const currentTime = Date.now(); if (currentTime - this.lastUpdate > 100) { const deltaX = Math.abs(x - this.lastX); const deltaY = Math.abs(y - this.lastY); const deltaZ = Math.abs(z - this.lastZ); this.lastUpdate = currentTime; this.lastX = x; this.lastY = y; this.lastZ = z; return deltaX + deltaY + deltaZ > 15; } return false; } } ``` ### Detección de inclinación [Section titled “Detección de inclinación”](#detección-de-inclinación) ```typescript class TiltDetector { getTiltAngles(x: number, y: number, z: number) { const roll = Math.atan2(y, z) * (180 / Math.PI); const pitch = Math.atan2(-x, Math.sqrt(y * y + z * z)) * (180 / Math.PI); return { roll, pitch }; } isDeviceFlat(z: number): boolean { return Math.abs(z - 9.8) < 1.0; } isDeviceUpright(y: number): boolean { return Math.abs(y - 9.8) < 2.0; } } ``` ### Contador de pasos [Section titled “Contador de pasos”](#contador-de-pasos) ```typescript class StepCounter { private steps = 0; private lastMagnitude = 0; private threshold = 11; processAcceleration(x: number, y: number, z: number) { const magnitude = Math.sqrt(x * x + y * y + z * z); if (magnitude > this.threshold && this.lastMagnitude < this.threshold) { this.steps++; console.log('Steps:', this.steps); } this.lastMagnitude = magnitude; } } ``` ## Mejores prácticas [Section titled “Mejores prácticas”](#mejores-prácticas) 1. **Elija intervalos adecuados**: equilibre la capacidad de respuesta y la duración de la batería * Juegos: 16-50 ms * Condición física: 100-200 ms * Generales: 200-500 ms 2. **Eliminar oyentes**: limpie siempre cuando haya terminado 3. **Filtrar ruido**: utilice promedios móviles para obtener datos más fluidos 4. **Considere la batería**: el sondeo de alta frecuencia agota la batería 5. **Prueba en dispositivos reales**: los simuladores no proporcionan datos precisos ## Consejos de rendimiento [Section titled “Consejos de rendimiento”](#consejos-de-rendimiento) ### Antirrebote [Section titled “Antirrebote”](#antirrebote) ```typescript class AccelerometerDebouncer { private timeout: any; debounce(callback: Function, delay: number) { return (...args: any[]) => { clearTimeout(this.timeout); this.timeout = setTimeout(() => callback(...args), delay); }; } } ``` ### Suavizado de datos [Section titled “Suavizado de datos”](#suavizado-de-datos) ```typescript class AccelerometerFilter { private alpha = 0.8; private filteredX = 0; private filteredY = 0; private filteredZ = 0; filter(x: number, y: number, z: number) { this.filteredX = this.alpha * x + (1 - this.alpha) * this.filteredX; this.filteredY = this.alpha * y + (1 - this.alpha) * this.filteredY; this.filteredZ = this.alpha * z + (1 - this.alpha) * this.filteredZ; return { x: this.filteredX, y: this.filteredY, z: this.filteredZ }; } } ``` ## Próximos pasos [Section titled “Próximos pasos”](#próximos-pasos) * Explore la [Referencia API](https://github.com/Cap-go/capacitor-accelerometer#api) para obtener documentación completa del método * Consulte la [aplicación de ejemplo](https://github.com/Cap-go/capacitor-accelerometer/tree/main/example) para un uso avanzado * Consulte el [tutorial](/plugins/capacitor-accelerometer) para ver ejemplos completos de implementación. # @Capgo/Capacitor-admob > Monetice su aplicación Capacitor con banners, intersticiales y anuncios de recompensa de Google AdMob gestionados completamente desde JavaScript. El Plugin AdMob de Capgo ofrece un puente de primera clase al SDK de Google Mobile Ads para que pueda monetizar experiencias nativas sin sacrificar la simplicidad de Capacitor. Formatos de anuncio flexibles Sirva anuncios de banner, intersticiales, de recompensa y de apertura de aplicación con una API unificada. Configuración de solicitudes Ajuste finamente las solicitudes de anuncios con banderas dirigidas a niños, clasificaciones de contenido y parámetros de red adicionales. Gestión del ciclo de vida Cree, cargue, muestre, oculte y destruya anuncios bajo demanda mientras rastrea el estado de carga. Listo para privacidad Maneje solicitudes ATT y monitoree el estado de autorización de seguimiento para mantenerse conforme. Usar la guía de inicio para conectar sus IDs de aplicación de AdMob, configurar el consentimiento y gestionar el inventario de anuncios directamente desde su proyecto Capacitor. # Comenzando > Instale y Configurar el Plugin AdMob de Capgo para servir anuncios dentro de su aplicación Capacitor. 1. **Instalar el Plugin** * npm ```sh npm i @Capgo/Capacitor-admob ``` * pnpm ```sh pnpm add @Capgo/Capacitor-admob ``` * yarn ```sh yarn add @Capgo/Capacitor-admob ``` * bun ```sh bun add @Capgo/Capacitor-admob ``` 2. **Sincronizar proyectos nativos** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Inicializar el SDK [Section titled “Inicializar el SDK”](#inicializar-el-sdk) ```typescript import { AdMob, MaxAdContentRating } from '@capgo/capacitor-admob'; // Iniciar el SDK de Mobile Ads una vez en el arranque de su aplicación await AdMob.start(); // Opcional: configurar ajustes de solicitud global await AdMob.configure({ appMuted: false, appVolume: 1, }); await AdMob.configRequest({ maxAdContentRating: MaxAdContentRating.T, tagForChildDirectedTreatment: false, tagForUnderAgeOfConsent: false, }); ``` ## Mostrar un anuncio de banner [Section titled “Mostrar un anuncio de banner”](#mostrar-un-anuncio-de-banner) ```typescript import { BannerAd } from '@capgo/capacitor-admob'; const banner = new BannerAd({ adUnitId: 'ca-app-pub-xxxxxxxxxxxxxxxx/banner', position: 'bottom', }); await banner.show(); ``` ## Anuncios intersticiales o de recompensa [Section titled “Anuncios intersticiales o de recompensa”](#anuncios-intersticiales-o-de-recompensa) ```typescript import { InterstitialAd, RewardedAd } from '@capgo/capacitor-admob'; const interstitial = new InterstitialAd({ adUnitId: 'ca-app-pub-xxxxxxxxxxxxxxxx/interstitial', }); await interstitial.load(); await interstitial.show(); const rewarded = new RewardedAd({ adUnitId: 'ca-app-pub-xxxxxxxxxxxxxxxx/rewarded', }); await rewarded.load(); await rewarded.show(); ``` ## Escuchar eventos de anuncios [Section titled “Escuchar eventos de anuncios”](#escuchar-eventos-de-anuncios) ```typescript import { AdMob } from '@capgo/capacitor-admob'; const handle = await AdMob.addListener('adImpression', (event) => { console.log('Impresión de anuncio', event); }); // Más tarde al limpiar await handle.remove(); ``` ## Notas de plataforma [Section titled “Notas de plataforma”](#notas-de-plataforma) * **iOS**: Agregue su ID de aplicación de AdMob a `Info.plist` bajo la clave `GADApplicationIdentifier` e incluya cualquier ID de SKAdNetwork en el que confíe. * **Android**: Declare su ID de aplicación de AdMob en `AndroidManifest.xml` agregando `com.google.android.gms.ads.APPLICATION_ID` dentro de la etiqueta ``. * **Consentimiento y privacidad**: Usar `requestTrackingAuthorization()` en iOS 14+ y banderas de `configRequest()` dirigidas a niños para cumplir con las reglas de privacidad regionales. # @Capgo/Capacitor-android-age-signals > Accede a Google Play Age Signals para detectar cuentas supervisadas, verificar edades de usuarios y cumplir con las regulaciones de seguridad infantil en tu aplicación Android. Cuentas supervisadas Detecta cuando los usuarios tienen cuentas de Google supervisadas administradas por tutores 👨‍👧‍👦 Verificación de edad Identifica usuarios verificados que son mayores de 18 años con la verificación de edad de Google 🔐 Cumplimiento Cumple con las regulaciones de seguridad infantil como COPPA y GDPR 📋 Aprobaciones de tutores Rastrea aprobaciones pendientes y estado de consentimiento del tutor ✅ Solo Android Diseñado específicamente para Android con Google Play Services 📱 API simple Una sola llamada de método para recuperar todas las señales de edad 🚀 # Empezando > Aprenda a instalar y utilizar el complemento Age Signals para detectar cuentas supervisadas y usuarios verificados en su aplicación Android. ### GDPR (Reglamento General de Protección de Datos) [Section titled “GDPR (Reglamento General de Protección de Datos)”](#gdpr-reglamento-general-de-protección-de-datos) Para usuarios supervisados: * Tratar los datos de forma lícita * Proporcionar mecanismos de consentimiento del tutor. * Permitir el acceso y eliminación de datos. * Implementar la privacidad por diseño ## Notas de plataforma [Section titled “Notas de plataforma”](#notas-de-plataforma) ### Android [Section titled “Android”](#android) * Requiere servicios Google Play * Mínimo API nivel 21 (Android 5.0+) * Sólo funciona en dispositivos con Play Store * Es posible que las señales no estén disponibles en todas las regiones. * Los resultados pueden cambiar si el guardián modifica la configuración. ### iOS / Web [Section titled “iOS / Web”](#ios--web) * **No compatible** - Este es un complemento exclusivo de Android * Lanzará un error si se llama en plataformas no compatibles ## Solución de problemas [Section titled “Solución de problemas”](#solución-de-problemas) ### No se devuelve señal (estado vacío) [Section titled “No se devuelve señal (estado vacío)”](#no-se-devuelve-señal-estado-vacío) Esto es normal para: * Usuarios fuera de las regiones admitidas * Dispositivos sin servicios Google Play * Usuarios que no han configurado Family Link * Cuentas nuevas sin verificación ### Estado desconocido [Section titled “Estado desconocido”](#estado-desconocido) El usuario debe: 1. Abra la tienda Google Play 2. Vaya a Configuración → Familia 3. Completa el proceso de verificación de edad. ### Problemas de permisos [Section titled “Problemas de permisos”](#problemas-de-permisos) Asegurar: * Google Play Servicios actualizados * La aplicación tiene el nombre de paquete correcto en Play Console * Pruebas en un dispositivo real (no en un emulador sin Play Services) ## Recursos [Section titled “Recursos”](#recursos) * [Google Play Documentación de señales de edad](https://developers.google.com/android/reference/com/google/android/gms/agesignals) * [Guía para desarrolladores de Family Link](https://developers.google.com/families) * [Guía de cumplimiento de COPPA](https://www.ftc.gov/business-guidance/resources/complying-coppa-frequently-asked-questions) # @Capgo/Capacitor-alarm > Gestiona alarmas nativas en iOS y Android directamente desde tu aplicación Capacitor. Integración nativa Usa AlarmKit nativo en iOS e intents de AlarmClock en Android 📱 API simple Métodos fáciles de usar para crear y gestionar alarmas ⚡ Manejo de permisos Soporte integrado para solicitar los permisos necesarios 🔒 Documentación completa Consulta la [Documentación](/docs/plugins/alarm/getting-started/) para dominar el Plugin en solo unos minutos. # Comenzando > Aprende cómo instalar y usar el Plugin Capacitor Alarm para gestionar alarmas nativas en iOS y Android. ## Instalación [Section titled “Instalación”](#instalación) * npm ```bash npm install @capgo/capacitor-alarm npx cap sync ``` * yarn ```bash yarn add @capgo/capacitor-alarm npx cap sync ``` * pnpm ```bash pnpm add @capgo/capacitor-alarm npx cap sync ``` * bun ```bash bun add @capgo/capacitor-alarm npx cap sync ``` ## Requisitos [Section titled “Requisitos”](#requisitos) * **iOS**: Solo iOS 26+. Este Plugin depende de las APIs de `AlarmKit` y reportará no compatible en versiones anteriores o cuando el framework no esté disponible. * **Android**: Usa intents de `AlarmClock`; el comportamiento depende de la aplicación de reloj predeterminada y las políticas del OEM. Nota: Este Plugin solo expone acciones de alarma nativas (crear/abrir). No implementa ninguna programación/CRUD de alarmas personalizada en la aplicación. ## API [Section titled “API”](#api) ### createAlarm(…) [Section titled “createAlarm(…)”](#createalarm) ```typescript createAlarm(options: NativeAlarmCreateOptions) => Promise ``` Crea una alarma nativa del sistema operativo usando la aplicación de reloj de la plataforma. En Android esto usa el intent de Reloj de Alarma; en iOS esto usa AlarmKit si está disponible (iOS 26+). | Parámetro | Tipo | | ------------- | -------------------------- | | **`options`** | `NativeAlarmCreateOptions` | **Retorna:** `Promise` ### openAlarms() [Section titled “openAlarms()”](#openalarms) ```typescript openAlarms() => Promise ``` Abre la interfaz de usuario de la lista de alarmas nativas de la plataforma, si está disponible. **Retorna:** `Promise` ### getOSInfo() [Section titled “getOSInfo()”](#getosinfo) ```typescript getOSInfo() => Promise ``` Obtiene información sobre el sistema operativo y capacidades. **Retorna:** `Promise` ### requestPermissions(…) [Section titled “requestPermissions(…)”](#requestpermissions) ```typescript requestPermissions(options?: { exactAlarm?: boolean | undefined; } | undefined) => Promise ``` Solicita permisos relevantes para el uso de alarmas en la plataforma. En Android, puede dirigir a la configuración para alarmas exactas. | Parámetro | Tipo | | ------------- | --------------------------- | | **`options`** | `{ exactAlarm?: boolean; }` | **Retorna:** `Promise` ## Interfaces [Section titled “Interfaces”](#interfaces) ### NativeActionResult [Section titled “NativeActionResult”](#nativeactionresult) | Prop | Tipo | | ------------- | --------- | | **`success`** | `boolean` | | **`message`** | `string` | ### NativeAlarmCreateOptions [Section titled “NativeAlarmCreateOptions”](#nativealarmcreateoptions) Opciones para crear una alarma nativa del sistema operativo a través de la aplicación de reloj de la plataforma. | Prop | Tipo | Descripción | | ------------- | --------- | ------------------------------------------------ | | **`hour`** | `number` | Hora del día en formato de 24h (0-23) | | **`minute`** | `number` | Minuto de la hora (0-59) | | **`label`** | `string` | Etiqueta opcional para la alarma | | **`skipUi`** | `boolean` | Solo Android: intenta omitir la UI si es posible | | **`vibrate`** | `boolean` | Solo Android: configura la alarma para vibrar | ### OSInfo [Section titled “OSInfo”](#osinfo) Información devuelta sobre el sistema operativo actual y capacidades. | Prop | Tipo | Descripción | | ------------------------------------ | --------- | ---------------------------------------------------------------------- | | **`platform`** | `string` | ’ios’ \| ‘android’ \| ‘web’ | | **`version`** | `string` | Cadena de versión del sistema operativo | | **`supportsNativeAlarms`** | `boolean` | Si la plataforma expone una integración de aplicación de alarma nativa | | **`supportsScheduledNotifications`** | `boolean` | Si se admite la programación de notificaciones locales | | **`canScheduleExactAlarms`** | `boolean` | Solo Android: si se permiten alarmas exactas | ### PermissionResult [Section titled “PermissionResult”](#permissionresult) Resultado de una solicitud de permisos. | Prop | Tipo | Descripción | | ------------- | ------------------------- | -------------------------------------------- | | **`granted`** | `boolean` | Concesión general para el alcance solicitado | | **`details`** | `Record` | Detalles opcionales por clave de permiso | # @Capgo/Capacitor-android-inline-Instalar > Habilita instalación y actualizaciones en la aplicación sin problemas para aplicaciones Android con capacidades de instalación en línea y flujos amigables para el usuario. ## Descripción general [Section titled “Descripción general”](#descripción-general) El Plugin Capacitor Android Inline Instalar habilita la activación de la superposición de instalación en línea de Google Play para aplicaciones Android. Este Plugin proporciona una experiencia de instalación en la aplicación sin problemas utilizando las herramientas de crecimiento premium de Google, permitiendo a los usuarios instalar aplicaciones sin salir de tu aplicación. Superposición de Google Play Activa la superposición de instalación nativa de Google Play 📱 Experiencia sin problemas Instala aplicaciones sin salir de tu aplicación 🔄 Soporte de respaldo Respaldo automático a página completa de Play Store ❤️ Seguimiento de campañas Soporte para seguimiento de referrer y analíticas 📊 ## Documentación [Section titled “Documentación”](#documentación) Consulta la [documentación completa](/docs/plugins/android-inline-install/getting-started/) para guías detalladas de implementación y patrones avanzados de integración. # Comenzando > Guía de instalación y uso para @Capgo/Capacitor-android-inline-Instalar ## Instalación [Section titled “Instalación”](#instalación) * npm ```bash npm install @capgo/capacitor-android-inline-install npx cap sync ``` * yarn ```bash yarn add @capgo/capacitor-android-inline-install npx cap sync ``` * pnpm ```bash pnpm add @capgo/capacitor-android-inline-install npx cap sync ``` * bun ```bash bun add @capgo/capacitor-android-inline-install npx cap sync ``` ## Ejemplo de uso [Section titled “Ejemplo de uso”](#ejemplo-de-uso) ```typescript import { AndroidInlineInstall } from '@capgo/capacitor-android-inline-install'; // Basic inline install await AndroidInlineInstall.startInlineInstall({ id: 'com.example.targetapp' }); // Advanced install with tracking await AndroidInlineInstall.startInlineInstall({ id: 'com.example.targetapp', referrer: 'campaign=my-campaign&source=app', overlay: true, fallback: true }); // Handle the installation flow try { await AndroidInlineInstall.startInlineInstall({ id: 'com.spotify.music', referrer: 'utm_source=myapp&utm_campaign=music_promotion' }); console.log('Install overlay triggered successfully'); } catch (error) { console.error('Install failed:', error); } ``` ## Métodos principales de API [Section titled “Métodos principales de API”](#métodos-principales-de-api) ### Gestión de instalación [Section titled “Gestión de instalación”](#gestión-de-instalación) * `startInlineInstall(options)` - Activa la superposición de instalación en línea de Google Play para la aplicación especificada ## Opciones de configuración [Section titled “Opciones de configuración”](#opciones-de-configuración) ```typescript interface InlineInstallOptions { id: string; // Target app package name (required) referrer?: string; // Tracking campaign string (optional) callerId?: string; // Caller app package name (defaults to current app) overlay?: boolean; // Enable/disable Play overlay (default: true) fallback?: boolean; // Use full store page if overlay fails (default: true) } ``` ## Requisitos de Google Play [Section titled “Requisitos de Google Play”](#requisitos-de-google-play) ### Elegibilidad para herramientas de crecimiento premium [Section titled “Elegibilidad para herramientas de crecimiento premium”](#elegibilidad-para-herramientas-de-crecimiento-premium) Tu aplicación debe calificar para las herramientas de crecimiento premium de Google Play para usar la instalación en línea: * Aplicaciones con un compromiso significativo del usuario * Buenas calificaciones y reseñas en Play Store * Cumplimiento con las políticas de Google Play ### Requisitos de la aplicación de destino [Section titled “Requisitos de la aplicación de destino”](#requisitos-de-la-aplicación-de-destino) * La aplicación de destino debe estar disponible en Google Play Store * La aplicación de destino debe admitir instalación en línea * El usuario debe haber iniciado sesión en Google Play Store ## Comportamiento y respaldos [Section titled “Comportamiento y respaldos”](#comportamiento-y-respaldos) ### Modo de superposición (predeterminado) [Section titled “Modo de superposición (predeterminado)”](#modo-de-superposición-predeterminado) 1. Intenta abrir la superposición de Google Play dentro de tu aplicación 2. Si la superposición no está disponible, vuelve a la página completa de Play Store 3. Si Play Store no está disponible, muestra un Error ### Modo de tienda completo [Section titled “Modo de tienda completo”](#modo-de-tienda-completo) * Abre directamente la página completa de Google Play Store * Omite por completo el intento de superposición ## Soporte de plataforma [Section titled “Soporte de plataforma”](#soporte-de-plataforma) * **Android**: Soporte completo con Google Play Services * **iOS**: No compatible (característica específica de Android) * **Web**: No compatible (característica nativa de Android) ## Detalles de implementación [Section titled “Detalles de implementación”](#detalles-de-implementación) El Plugin usa intents de Android para comunicarse con Google Play Store: * Acción de intent para superposición de instalación en línea * Respaldo a intent estándar de Play Store * Manejo automático de disponibilidad de Play Services ## Casos de uso [Section titled “Casos de uso”](#casos-de-uso) * **Descubrimiento de aplicaciones**: Promociona aplicaciones relacionadas dentro de tu ecosistema * **Promoción cruzada**: Impulsa instalaciones para aplicaciones de socios * **Desbloqueo de funciones**: Instala módulos o extensiones adicionales * **Aplicaciones complementarias**: Instala aplicaciones de soporte sin problemas ## Mejores prácticas [Section titled “Mejores prácticas”](#mejores-prácticas) * Siempre proporciona opciones de respaldo para usuarios sin Play Services * Prueba con diferentes configuraciones de dispositivos y versiones de Play Store * Usa seguimiento de referrer para medir tasas de conversión * Maneja errores de instalación con elegancia * Respeta la elección del usuario si rechaza la instalación ## Limitaciones [Section titled “Limitaciones”](#limitaciones) * Funcionalidad solo para Android * Requiere Google Play Services * Solo funciona con aplicaciones que califican para herramientas de crecimiento premium * Sujeto a las políticas de Google Play Store y disponibilidad # @Capgo/Capacitor-android-kiosk > Bloquea tu dispositivo Android en modo quiosco con control total sobre los botones físicos y funcionalidad de lanzador para tus aplicaciones Capacitor. Modo quiosco Oculta la interfaz de usuario del sistema y entra en modo de pantalla completa inmersivo Integración de lanzador Establece tu aplicación como el lanzador/aplicación de inicio del dispositivo Control de teclas físicas Bloquea o permite botones físicos específicos (retroceso, inicio, recientes, volumen, encendido) Detección de estado Verifica si el modo quiosco está activo o si la aplicación está configurada como lanzador Android 6.0+ Compatible con Android API 23 hasta Android 15 (API 35) Documentación completa Consulta la [Documentación](/docs/plugins/android-kiosk/getting-started/) para integrar el modo quiosco en minutos. ## Soporte de plataforma [Section titled “Soporte de plataforma”](#soporte-de-plataforma) Este Plugin es **solo para Android**. Para la funcionalidad de modo quiosco en iOS, por favor usa la función integrada del dispositivo [Acceso Guiado](https://support.apple.com/en-us/HT202612). # Primeros pasos > Learn how Para instalar and Usar El Android Kiosk Plugin A lock your Android Dispositivo into kiosk mode in your Capacitor Aplicación. 1. **Instalar El Paquete** * npm ```sh npm i @Capgo/Capacitor-android-kiosk ``` * pnpm ```sh pnpm add @Capgo/Capacitor-android-kiosk ``` * yarn ```sh yarn add @Capgo/Capacitor-android-kiosk ``` * bun ```sh bun add @Capgo/Capacitor-android-kiosk ``` 2. **Sync with native projects** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` Este Plugin is **Android-only**. For iOS kiosk mode functionality, please Usar El Dispositivo’s built-in [Guided Access](https://support.apple.com/en-us/HT202612) feature. * **Kiosk Mode**: Hide system UI and enter immersive fullscreen mode * **Launcher Integración**: Establecer your Aplicación as El Dispositivo launcher/home Aplicación * **Hardware Key Control**: Block or allow specific hardware buttons * **Estado Detection**: Verificar if kiosk mode is active or if Aplicación is Establecer as launcher * **Android 6.0+**: Supports Android API 23 through Android 15 (API 35) ```typescript import { CapacitorAndroidKiosk } from '@capgo/capacitor-android-kiosk'; // Enter kiosk mode await CapacitorAndroidKiosk.enterKioskMode(); // Exit kiosk mode await CapacitorAndroidKiosk.exitKioskMode(); // Check if in kiosk mode const { isInKioskMode } = await CapacitorAndroidKiosk.isInKioskMode(); console.log('Kiosk mode active:', isInKioskMode); ``` For full kiosk mode functionality, Necesita A Establecer your Aplicación as El Dispositivo launcher: ```typescript // Open home screen settings for user to select your app as launcher await CapacitorAndroidKiosk.setAsLauncher(); // Check if app is set as launcher const { isLauncher } = await CapacitorAndroidKiosk.isSetAsLauncher(); console.log('App is launcher:', isLauncher); ``` Control which hardware buttons are allowed A Función in kiosk mode: ```typescript // Allow only volume keys await CapacitorAndroidKiosk.setAllowedKeys({ volumeUp: true, volumeDown: true, back: false, home: false, recent: false }); // Block all keys (default) await CapacitorAndroidKiosk.setAllowedKeys({}); ``` ```typescript async function setupKioskMode() { try { // Check if already set as launcher const { isLauncher } = await CapacitorAndroidKiosk.isSetAsLauncher(); if (!isLauncher) { // Prompt user to set as launcher await CapacitorAndroidKiosk.setAsLauncher(); alert('Please select this app as your Home app'); return; } // Configure allowed keys await CapacitorAndroidKiosk.setAllowedKeys({ volumeUp: true, volumeDown: true, back: false, home: false, recent: false, power: false }); // Enter kiosk mode await CapacitorAndroidKiosk.enterKioskMode(); console.log('Kiosk mode activated'); } catch (error) { console.error('Failed to setup kiosk mode:', error); } } ``` Checks if El Aplicación is currently Ejecutando in kiosk mode. ```typescript const { isInKioskMode } = await CapacitorAndroidKiosk.isInKioskMode(); ``` **Returns:** * `isInKioskMode` (boolean): Whether kiosk mode is currently active *** ### isSetAsLauncher() [Section titled “isSetAsLauncher()”](#issetaslauncher) Checks if El Aplicación is Establecer as El Dispositivo launcher (home Aplicación). ```typescript const { isLauncher } = await CapacitorAndroidKiosk.isSetAsLauncher(); ``` **Returns:** * `isLauncher` (boolean): Whether El Aplicación is Establecer as El Dispositivo launcher *** ### enterKioskMode() [Section titled “enterKioskMode()”](#enterkioskmode) Enters kiosk mode, hiding system UI and blocking hardware buttons. El Aplicación Debe be Establecer as El Dispositivo launcher for this A work effectively. ```typescript await CapacitorAndroidKiosk.enterKioskMode(); ``` *** ### exitKioskMode() [Section titled “exitKioskMode()”](#exitkioskmode) Exits kiosk mode, restoring normal system UI and hardware button functionality. ```typescript await CapacitorAndroidKiosk.exitKioskMode(); ``` *** ### setAsLauncher() [Section titled “setAsLauncher()”](#setaslauncher) Opens El Dispositivo’s home screen Configuración A allow user A Establecer this Aplicación as El launcher. Esto es Requerido for full kiosk mode functionality. ```typescript await CapacitorAndroidKiosk.setAsLauncher(); ``` *** ### setAllowedKeys(Opciones) [Section titled “setAllowedKeys(Opciones)”](#setallowedkeysopciones) Sets which hardware keys are allowed A Función in kiosk mode. By Predeterminado, all hardware keys are blocked in kiosk mode. ```typescript await CapacitorAndroidKiosk.setAllowedKeys({ volumeUp: true, volumeDown: true, back: false, home: false, recent: false, power: false, camera: false, menu: false }); ``` **Parámetros:** * `volumeUp` (boolean, Opcional): Allow volume up button (Predeterminado: false) * `volumeDown` (boolean, Opcional): Allow volume down button (Predeterminado: false) * `back` (boolean, Opcional): Allow back button (Predeterminado: false) * `home` (boolean, Opcional): Allow home button (Predeterminado: false) * `recent` (boolean, Opcional): Allow recent Aplicaciones button (Predeterminado: false) * `power` (boolean, Opcional): Allow power button (Predeterminado: false) * `camera` (boolean, Opcional): Allow camera button if present (Predeterminado: false) * `menu` (boolean, Opcional): Allow menu button if present (Predeterminado: false) *** ### getPluginVersion() [Section titled “getPluginVersion()”](#getpluginversion) Get El native Capacitor Plugin Versión. ```typescript const { version } = await CapacitorAndroidKiosk.getPluginVersion(); console.log('Plugin version:', version); ``` **Returns:** * `version` (string): El Plugin Versión number ## Android Configuración [Section titled “Android Configuración”](#android-configuración) ### 1. MainActivity Configuración [Section titled “1. MainActivity Configuración”](#1-mainactivity-configuración) Para habilitar full hardware key blocking, Necesita A override `dispatchKeyEvent` in your `MainActivity.java`: ```java import android.view.KeyEvent; import ee.forgr.plugin.android_kiosk.CapacitorAndroidKioskPlugin; public class MainActivity extends BridgeActivity { @Override public boolean dispatchKeyEvent(KeyEvent event) { // Get the kiosk plugin CapacitorAndroidKioskPlugin kioskPlugin = (CapacitorAndroidKioskPlugin) this.getBridge().getPlugin("CapacitorAndroidKiosk").getInstance(); if (kioskPlugin != null && kioskPlugin.shouldBlockKey(event.getKeyCode())) { return true; // Block the key } return super.dispatchKeyEvent(event); } @Override public void onBackPressed() { // Don't call super.onBackPressed() to disable back button // Or call the plugin's handleOnBackPressed } } ``` ### 2. AndroidManifest.XML [Section titled “2. AndroidManifest.XML”](#2-androidmanifestxml) Agregar launcher intent filter A make your Aplicación selectable as a launcher: ```xml ``` ## Importante Notas [Section titled “Importante Notas”](#importante-notas) 1. **Launcher Requirement**: For full kiosk mode functionality (blocking home button, preventing task switching), your Aplicación Debe be Establecer as El Dispositivo launcher. 2. **Pruebas**: When Pruebas, Puede exit kiosk mode programmatically or by setting another Aplicación as El launcher. 3. **Android Versiones**: El Plugin uses modern Android APIs for Android 11+ and falls back A older Métodos for compatibility with Android 6.0+. 4. **Seguridad**: Este Plugin is designed for legitimate kiosk applications. Ensure you provide users with a way A exit kiosk mode. 5. **Battery**: Kiosk mode keeps El screen on. Consider implementing your own screen timeout or brightness management. ## iOS Alternative [Section titled “iOS Alternative”](#ios-alternative) For iOS Dispositivos, Usar El built-in [Guided Access](https://support.apple.com/en-us/HT202612) feature: 1. Go A Configuración > Accessibility > Guided Access 2. Turn on Guided Access 3. Establecer a passcode 4. Open your Aplicación 5. Triple-Haga clic El side button 6. Adjust Configuración and start Guided Access ## Buenas prácticas [Section titled “Buenas prácticas”](#buenas-prácticas) 1. **Verificar launcher Estado first** ```typescript const { isLauncher } = await CapacitorAndroidKiosk.isSetAsLauncher(); if (!isLauncher) { // Prompt user to set as launcher first await CapacitorAndroidKiosk.setAsLauncher(); } ``` 2. **Provide exit mechanism** ```typescript // Allow specific key combination to exit // Or implement a secret gesture/pattern async function exitKioskWithConfirmation() { const confirmed = confirm('Exit kiosk mode?'); if (confirmed) { await CapacitorAndroidKiosk.exitKioskMode(); } } ``` 3. **Handle Aplicación lifecycle** ```typescript // Re-enter kiosk mode when app resumes window.addEventListener('resume', async () => { const { isInKioskMode } = await CapacitorAndroidKiosk.isInKioskMode(); if (!isInKioskMode) { await CapacitorAndroidKiosk.enterKioskMode(); } }); ``` 4. **Error handling** ```typescript try { await CapacitorAndroidKiosk.enterKioskMode(); } catch (error) { console.error('Failed to enter kiosk mode:', error); // Notify user and provide alternative } ``` # @Capgo/Capacitor-android-usagestatsmanager > Accede a las estadísticas de uso de Android para monitorear el uso de aplicaciones, el tiempo de pantalla y analíticas de comportamiento del usuario con datos a nivel del sistema. ## Descripción general [Section titled “Descripción general”](#descripción-general) El Plugin Capacitor Android Uso Stats Manager expone el SDK de UsageStatsManager de Android a aplicaciones Capacitor, permitiendo el acceso a estadísticas de uso de aplicaciones detalladas y datos de uso del dispositivo. Este Plugin permite a los desarrolladores rastrear patrones de uso de aplicaciones, tiempo de pantalla y analíticas de comportamiento del usuario en dispositivos Android. Estadísticas de uso Accede a datos del SDK de UsageStatsManager de Android 📱 Monitoreo de aplicaciones Rastrea el tiempo de uso y frecuencia de aplicaciones individuales 🕐 Gestión de permisos Maneja permisos de estadísticas de uso sin problemas 🛡️ Información de paquetes Consulta detalles y metadatos de paquetes instalados 📦 ## Instalación [Section titled “Instalación”](#instalación) ```bash npm install @capgo/capacitor-android-usagestatsmanager npx cap sync ``` ## Permisos requeridos [Section titled “Permisos requeridos”](#permisos-requeridos) Agrega estos permisos a tu `android/app/src/main/AndroidManifest.xml`: ```xml ``` ## Métodos principales de API [Section titled “Métodos principales de API”](#métodos-principales-de-api) ### Estadísticas de uso [Section titled “Estadísticas de uso”](#estadísticas-de-uso) * `queryAndAggregateUsageStats(options)` - Recupera estadísticas de uso detalladas para aplicaciones instaladas * `isUsageStatsPermissionGranted()` - Verifica si el permiso de estadísticas de uso está concedido * `openUsageStatsSettings()` - Abre la configuración del sistema para el permiso de estadísticas de uso ### Información de paquetes [Section titled “Información de paquetes”](#información-de-paquetes) * `queryAllPackages()` - Obtiene información sobre todos los paquetes instalados ## Ejemplo de uso [Section titled “Ejemplo de uso”](#ejemplo-de-uso) ```typescript import { AndroidUsageStatsManager } from '@capgo/capacitor-android-usagestatsmanager'; // Check if permission is granted const permissionResult = await AndroidUsageStatsManager.isUsageStatsPermissionGranted(); if (!permissionResult.granted) { // Open settings to grant permission await AndroidUsageStatsManager.openUsageStatsSettings(); return; } // Query usage statistics const statsOptions = { intervalType: 0, // INTERVAL_DAILY startTime: Date.now() - (7 * 24 * 60 * 60 * 1000), // 7 days ago endTime: Date.now() }; const usageStats = await AndroidUsageStatsManager.queryAndAggregateUsageStats(statsOptions); console.log('Usage statistics:', usageStats); // Get all installed packages const packages = await AndroidUsageStatsManager.queryAllPackages(); console.log('Installed packages:', packages); ``` ## Manejo de permisos [Section titled “Manejo de permisos”](#manejo-de-permisos) El Plugin requiere permisos especiales que no pueden ser concedidos a través de solicitudes normales de permisos en tiempo de ejecución: 1. **PACKAGE\_USAGE\_STATS**: Permite el acceso a estadísticas de uso 2. **QUERY\_ALL\_PACKAGES**: Requerido para información de paquetes (Android 11+) Los usuarios deben conceder manualmente estos permisos a través de la configuración del sistema. Usa `openUsageStatsSettings()` para dirigir a los usuarios a la página de configuración apropiada. ## Tipos de datos [Section titled “Tipos de datos”](#tipos-de-datos) ### Estadísticas de uso [Section titled “Estadísticas de uso”](#estadísticas-de-uso-1) * Tiempo de uso de la aplicación y frecuencia * Primera y última vez usado * Tiempo total en primer plano * Conteo de lanzamientos ### Información de paquetes [Section titled “Información de paquetes”](#información-de-paquetes-1) * Nombre y versión del paquete * Tiempo de instalación * Etiquetas e iconos de aplicaciones * Aplicaciones del sistema vs aplicaciones de usuario ## Casos de uso [Section titled “Casos de uso”](#casos-de-uso) * **Aplicaciones de bienestar digital**: Monitorea el tiempo de pantalla y el uso de aplicaciones * **Controles parentales**: Rastrea el uso del dispositivo de los niños * **Aplicaciones de productividad**: Analiza patrones de trabajo y tiempo de concentración * **Analíticas**: Comprende el comportamiento del usuario y el compromiso con la aplicación ## Compatibilidad con Android [Section titled “Compatibilidad con Android”](#compatibilidad-con-android) * **Versión mínima de Android**: Nivel de API 21 (Android 5.0) * **Características avanzadas**: Nivel de API 29+ (Android 10+) para estadísticas mejoradas * **Consultas de paquetes**: Nivel de API 30+ (Android 11+) requiere QUERY\_ALL\_PACKAGES ## Consideraciones de seguridad [Section titled “Consideraciones de seguridad”](#consideraciones-de-seguridad) * El permiso de estadísticas de uso es sensible y requiere consentimiento del usuario * Considera la privacidad del usuario al recopilar datos de uso * Implementa prácticas adecuadas de manejo y almacenamiento de datos * Sigue las políticas de Google Play para la recopilación de datos de uso ## Documentación [Section titled “Documentación”](#documentación) Consulta la [documentación completa](/docs/plugins/android-usagestatsmanager/getting-started/) para guías detalladas de implementación y patrones de uso avanzados. # Comenzando > Aprende cómo instalar y usar el Plugin Android Uso Stats Manager para acceder a estadísticas de uso de aplicaciones y datos de uso del dispositivo. ## Instalación [Section titled “Instalación”](#instalación) * npm ```bash npm install @capgo/capacitor-android-usagestatsmanager npx cap sync ``` * yarn ```bash yarn add @capgo/capacitor-android-usagestatsmanager npx cap sync ``` * pnpm ```bash pnpm add @capgo/capacitor-android-usagestatsmanager npx cap sync ``` * bun ```bash bun add @capgo/capacitor-android-usagestatsmanager npx cap sync ``` ## Configuración de plataforma [Section titled “Configuración de plataforma”](#configuración-de-plataforma) ### Android [Section titled “Android”](#android) Agrega los permisos requeridos a tu `android/app/src/main/AndroidManifest.xml`: ```xml ``` ## Ejemplo de uso [Section titled “Ejemplo de uso”](#ejemplo-de-uso) ```typescript import { AndroidUsageStatsManager } from '@capgo/capacitor-android-usagestatsmanager'; // Check if permission is granted const permissionResult = await AndroidUsageStatsManager.isUsageStatsPermissionGranted(); if (!permissionResult.granted) { // Open settings to grant permission await AndroidUsageStatsManager.openUsageStatsSettings(); return; } // Query usage statistics for the last 7 days const statsOptions = { intervalType: 0, // INTERVAL_DAILY startTime: Date.now() - (7 * 24 * 60 * 60 * 1000), // 7 days ago endTime: Date.now() }; const usageStats = await AndroidUsageStatsManager.queryAndAggregateUsageStats(statsOptions); console.log('Usage statistics:', usageStats); // Get all installed packages const packages = await AndroidUsageStatsManager.queryAllPackages(); console.log('Installed packages:', packages); ``` ## Referencia de API [Section titled “Referencia de API”](#referencia-de-api) ### isUsageStatsPermissionGranted() [Section titled “isUsageStatsPermissionGranted()”](#isusagestatspermissiongranted) ```typescript isUsageStatsPermissionGranted() => Promise<{ granted: boolean }> ``` Verifica si el permiso PACKAGE\_USAGE\_STATS está concedido. **Retorna:** `Promise<{ granted: boolean }>` ### openUsageStatsSettings() [Section titled “openUsageStatsSettings()”](#openusagestatssettings) ```typescript openUsageStatsSettings() => Promise ``` Abre la página de configuración del sistema para conceder el permiso de estadísticas de uso. ### queryAndAggregateUsageStats(Opciones) [Section titled “queryAndAggregateUsageStats(Opciones)”](#queryandaggregateusagestatsopciones) ```typescript queryAndAggregateUsageStats(options: UsageStatsOptions) => Promise ``` Consulta estadísticas de uso detalladas para aplicaciones instaladas. | Parámetro | Tipo | | ------------- | ------------------- | | **`options`** | `UsageStatsOptions` | **Retorna:** `Promise` ### queryAllPackages() [Section titled “queryAllPackages()”](#queryallpackages) ```typescript queryAllPackages() => Promise ``` Obtiene información sobre todos los paquetes instalados en el dispositivo. **Retorna:** `Promise` ## Interfaces [Section titled “Interfaces”](#interfaces) ### UsageStatsOptions [Section titled “UsageStatsOptions”](#usagestatsoptions) | Prop | Tipo | Descripción | | ------------------ | -------- | -------------------------------------------------------- | | **`intervalType`** | `number` | Tipo de intervalo (0=DIARIO, 1=SEMANAL, 2=MENSUAL, etc.) | | **`startTime`** | `number` | Tiempo de inicio en milisegundos | | **`endTime`** | `number` | Tiempo de fin en milisegundos | ### UsageStatsResult [Section titled “UsageStatsResult”](#usagestatsresult) Contiene datos de estadísticas de uso para cada aplicación: * Tiempo de uso de la aplicación y frecuencia * Primera y última vez usado * Tiempo total en primer plano * Conteo de lanzamientos ### PackagesResult [Section titled “PackagesResult”](#packagesresult) Contiene información sobre paquetes instalados: * Nombre y versión del paquete * Tiempo de instalación * Etiquetas e iconos de aplicaciones * Aplicaciones del sistema vs aplicaciones de usuario ## Manejo de permisos [Section titled “Manejo de permisos”](#manejo-de-permisos) El Plugin requiere permisos especiales que no pueden ser concedidos a través de solicitudes normales de permisos en tiempo de ejecución: 1. **PACKAGE\_USAGE\_STATS**: Permite el acceso a estadísticas de uso 2. **QUERY\_ALL\_PACKAGES**: Requerido para información de paquetes (Android 11+) Los usuarios deben conceder manualmente estos permisos a través de la configuración del sistema. Usa `openUsageStatsSettings()` para dirigir a los usuarios a la página de configuración apropiada. ## Mejores prácticas [Section titled “Mejores prácticas”](#mejores-prácticas) * Siempre verifica el estado del permiso antes de consultar estadísticas de uso * Maneja la denegación de permisos con elegancia con mensajes amigables para el usuario * Considera la privacidad del usuario al recopilar datos de uso * Implementa prácticas adecuadas de manejo y almacenamiento de datos * Sigue las políticas de Google Play para la recopilación de datos de uso ## Casos de uso [Section titled “Casos de uso”](#casos-de-uso) * **Aplicaciones de bienestar digital**: Monitorea el tiempo de pantalla y el uso de aplicaciones * **Controles parentales**: Rastrea el uso del dispositivo de los niños * **Aplicaciones de productividad**: Analiza patrones de trabajo y tiempo de concentración * **Analíticas**: Comprende el comportamiento del usuario y el compromiso con la aplicación # @Capgo/Capacitor-appinsights > Rastrea el uso de la aplicación, el comportamiento del usuario y las métricas de rendimiento con la integración de AppInsights para tus aplicaciones Capacitor. Seguimiento de analíticas Rastrea el comportamiento del usuario y el uso de la aplicación con AppInsights Identificación de usuario Establece y gestiona IDs de usuario para seguimiento personalizado Gestión de estado del SDK Monitorea la inicialización del SDK y el estado de permisos Documentación completa Consulta la [Documentación](/docs/plugins/appinsights/getting-started/) para integrar analíticas en minutos. # Comenzando > Aprende cómo instalar y usar el Plugin AppInsights para analíticas de aplicación en tu aplicación Capacitor. 1. **Instalar el paquete** * npm ```sh npm i @Capgo/Capacitor-appinsights ``` * pnpm ```sh pnpm add @Capgo/Capacitor-appinsights ``` * yarn ```sh yarn add @Capgo/Capacitor-appinsights ``` * bun ```sh bun add @Capgo/Capacitor-appinsights ``` 2. **Sincronizar con proyectos nativos** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Uso [Section titled “Uso”](#uso) Importa el Plugin e inicialízalo con tus credenciales de AppInsights: ```typescript import { CapacitorAppinsights } from '@capgo/capacitor-appinsights'; // Initialize the SDK const initializeAnalytics = async () => { await CapacitorAppinsights.init({ partnerId: 'your-partner-id', partnerKey: 'your-partner-key' }); }; // Set user ID for tracking const setUser = async (userId: string) => { await CapacitorAppinsights.setUserId({ userId }); }; // Get SDK state const checkSDKState = async () => { const state = await CapacitorAppinsights.getState(); console.log('SDK State:', state); }; // Get plugin version const checkVersion = async () => { const { version } = await CapacitorAppinsights.getPluginVersion(); console.log('Plugin version:', version); }; ``` ## Referencia de API [Section titled “Referencia de API”](#referencia-de-api) ### init(Opciones) [Section titled “init(Opciones)”](#initopciones) Inicializa el SDK de AppInsights con tus credenciales. ```typescript await CapacitorAppinsights.init({ partnerId: 'your-partner-id', partnerKey: 'your-partner-key' }); ``` **Parámetros:** * `partnerId` (string): Tu ID de socio de AppInsights * `partnerKey` (string): Tu clave de socio de AppInsights ### setUserId(Opciones) [Section titled “setUserId(Opciones)”](#setuseridopciones) Establece o actualiza el ID de usuario después de la inicialización. ```typescript await CapacitorAppinsights.setUserId({ userId: 'user-123' }); ``` **Parámetros:** * `userId` (string): El ID de usuario a establecer para el seguimiento ### getState() [Section titled “getState()”](#getstate) Obtiene el estado actual del SDK. ```typescript const state = await CapacitorAppinsights.getState(); console.log(state); // { // initCompleted: true, // jobScheduled: true, // permissionAcquired: true // } ``` **Retorna:** * `initCompleted` (boolean): Si la inicialización del SDK está completa * `jobScheduled` (boolean): Si el trabajo de recopilación de datos está programado * `permissionAcquired` (boolean): Si se han adquirido los permisos necesarios ### getPluginVersion() [Section titled “getPluginVersion()”](#getpluginversion) Obtiene la versión nativa del Plugin Capacitor. ```typescript const { version } = await CapacitorAppinsights.getPluginVersion(); console.log('Version:', version); ``` ## Ejemplo completo [Section titled “Ejemplo completo”](#ejemplo-completo) ```typescript import { CapacitorAppinsights } from '@capgo/capacitor-appinsights'; export class AnalyticsService { private initialized = false; async initialize(partnerId: string, partnerKey: string) { try { await CapacitorAppinsights.init({ partnerId, partnerKey }); const state = await CapacitorAppinsights.getState(); this.initialized = state.initCompleted; console.log('AppInsights initialized:', state); } catch (error) { console.error('Failed to initialize AppInsights:', error); } } async identifyUser(userId: string) { if (!this.initialized) { throw new Error('AppInsights not initialized'); } await CapacitorAppinsights.setUserId({ userId }); console.log('User identified:', userId); } async getSDKStatus() { const state = await CapacitorAppinsights.getState(); return { ready: state.initCompleted && state.permissionAcquired, tracking: state.jobScheduled, state }; } } ``` ## Mejores prácticas [Section titled “Mejores prácticas”](#mejores-prácticas) 1. **Inicializa temprano** Inicializa el SDK tan pronto como tu aplicación se inicie para asegurar un seguimiento preciso: ```typescript // In your app initialization await CapacitorAppinsights.init({ partnerId: process.env.APPINSIGHTS_PARTNER_ID, partnerKey: process.env.APPINSIGHTS_PARTNER_KEY }); ``` 2. **Establece el ID de usuario al iniciar sesión** Identifica a los usuarios después de la autenticación: ```typescript async function onUserLogin(user) { await CapacitorAppinsights.setUserId({ userId: user.id }); } ``` 3. **Verifica el estado del SDK** Verifica que el SDK esté listo antes de operaciones críticas: ```typescript const state = await CapacitorAppinsights.getState(); if (!state.initCompleted) { console.warn('AppInsights not ready'); } ``` 4. **Maneja los errores con elegancia** ```typescript try { await CapacitorAppinsights.init(config); } catch (error) { console.error('Analytics initialization failed:', error); // Continue app operation even if analytics fails } ``` ## Notas de plataforma [Section titled “Notas de plataforma”](#notas-de-plataforma) ### iOS [Section titled “iOS”](#ios) * Requiere iOS 10.0+ * Asegúrate de tener los permisos necesarios en Información.plist para el seguimiento ### Android [Section titled “Android”](#android) * Requiere Android 5.0 (API 21)+ * Puede requerir permiso de estadísticas de uso dependiendo de las características de seguimiento ### Web [Section titled “Web”](#web) * No compatible con la plataforma web # @Capgo/Capacitor-audio-recorder > Grabación de audio de alta calidad para tus aplicaciones Capacitor Multiplataforma Graba audio en iOS, Android y Web con una API unificada 🌐 Múltiples formatos Soporte para codificación AAC, MP3 y WAV 🎵 Calidad configurable Controla la tasa de muestreo, tasa de bits y canales para calidad óptima 🎚️ Documentación completa Consulta la [Documentación](/docs/plugins/audio-recorder/getting-started/) para dominar el Plugin en solo unos minutos. # Primeros pasos con la grabadora de audio > Aprenda cómo integrar la grabación de audio en su aplicación Capacitor Esta guía lo guiará a través de la integración del complemento Grabador de audio Capacitor en su aplicación. ## Instalación [Section titled “Instalación”](#instalación) Instale el complemento usando npm: ```bash npm install @capgo/capacitor-audio-recorder npx cap sync ``` ## iOS Configuration [Section titled “iOS Configuration”](#ios-configuration) Agregue lo siguiente a su `Info.plist`: ```xml NSMicrophoneUsageDescription This app needs access to the microphone to record audio ``` ## Android Configuración [Section titled “Android Configuración”](#android-configuración) Agregue los siguientes permisos a su `AndroidManifest.xml`: ```xml ``` ## Configuración web [Section titled “Configuración web”](#configuración-web) El complemento utiliza MediaRecorder API. Requires HTTPS in production. ## Uso básico [Section titled “Uso básico”](#uso-básico) ### Importar el complemento [Section titled “Importar el complemento”](#importar-el-complemento) ```typescript import { AudioRecorder } from '@capgo/capacitor-audio-recorder'; ``` ### Solicitar permisos [Section titled “Solicitar permisos”](#solicitar-permisos) ```typescript const requestPermission = async () => { const permission = await AudioRecorder.requestPermissions(); console.log('Permission status:', permission.recordAudio); }; ``` ### Iniciar grabación [Section titled “Iniciar grabación”](#iniciar-grabación) ```typescript const startRecording = async () => { await AudioRecorder.startRecording(); console.log('Recording started'); }; ``` ### Detener grabación [Section titled “Detener grabación”](#detener-grabación) ```typescript const stopRecording = async () => { const result = await AudioRecorder.stopRecording(); console.log('Recording path:', result.filePath); console.log('Duration:', result.duration); }; ``` ### Pause/Resume Recording [Section titled “Pause/Resume Recording”](#pauseresume-recording) ```typescript const pauseRecording = async () => { await AudioRecorder.pauseRecording(); console.log('Recording paused'); }; const resumeRecording = async () => { await AudioRecorder.resumeRecording(); console.log('Recording resumed'); }; ``` ### Obtener estado [Section titled “Obtener estado”](#obtener-estado) ```typescript const getStatus = async () => { const status = await AudioRecorder.getStatus(); console.log('Is recording:', status.isRecording); console.log('Duration:', status.currentTime); }; ``` ## Ejemplo completo [Section titled “Ejemplo completo”](#ejemplo-completo) Here’s a complete voice recorder implementation: ```typescript import { AudioRecorder } from '@capgo/capacitor-audio-recorder'; class VoiceRecorder { private isRecording = false; private isPaused = false; private recordingPath: string | null = null; async initialize() { const permission = await AudioRecorder.checkPermissions(); if (permission.recordAudio !== 'granted') { const requested = await AudioRecorder.requestPermissions(); if (requested.recordAudio !== 'granted') { throw new Error('Microphone permission denied'); } } } async startRecording() { try { await AudioRecorder.startRecording(); this.isRecording = true; this.isPaused = false; console.log('Recording started'); } catch (error) { console.error('Failed to start recording:', error); throw error; } } async pauseRecording() { if (!this.isRecording || this.isPaused) { return; } try { await AudioRecorder.pauseRecording(); this.isPaused = true; console.log('Recording paused'); } catch (error) { console.error('Failed to pause recording:', error); throw error; } } async resumeRecording() { if (!this.isRecording || !this.isPaused) { return; } try { await AudioRecorder.resumeRecording(); this.isPaused = false; console.log('Recording resumed'); } catch (error) { console.error('Failed to resume recording:', error); throw error; } } async stopRecording() { if (!this.isRecording) { return null; } try { const result = await AudioRecorder.stopRecording(); this.isRecording = false; this.isPaused = false; this.recordingPath = result.filePath; console.log('Recording stopped'); console.log('File path:', result.filePath); console.log('Duration:', result.duration, 'seconds'); return result; } catch (error) { console.error('Failed to stop recording:', error); throw error; } } async getCurrentStatus() { const status = await AudioRecorder.getStatus(); return { isRecording: status.isRecording, duration: status.currentTime, isPaused: this.isPaused }; } formatDuration(seconds: number): string { const mins = Math.floor(seconds / 60); const secs = Math.floor(seconds % 60); return `${mins}:${secs.toString().padStart(2, '0')}`; } getRecordingPath(): string | null { return this.recordingPath; } } // Usage const recorder = new VoiceRecorder(); // Initialize await recorder.initialize(); // Start recording await recorder.startRecording(); // Pause await recorder.pauseRecording(); // Resume await recorder.resumeRecording(); // Stop and get result const result = await recorder.stopRecording(); console.log('Recording saved:', result?.filePath); ``` ## Advanced Configuration [Section titled “Advanced Configuration”](#advanced-configuration) ### Configure Audio Quality [Section titled “Configure Audio Quality”](#configure-audio-quality) ```typescript const startHighQualityRecording = async () => { await AudioRecorder.startRecording({ format: 'aac', // aac, mp3, or wav sampleRate: 44100, // 44100 Hz (CD quality) channels: 2, // Stereo bitRate: 320000 // 320 kbps }); }; ``` ### Configurar para voz [Section titled “Configurar para voz”](#configurar-para-voz) ```typescript const startVoiceRecording = async () => { await AudioRecorder.startRecording({ format: 'aac', sampleRate: 16000, // 16 kHz (voice optimized) channels: 1, // Mono bitRate: 64000 // 64 kbps }); }; ``` ## UI Integration Example [Section titled “UI Integration Example”](#ui-integration-example) ```typescript class AudioRecorderUI { private recorder: VoiceRecorder; private updateInterval: any; constructor() { this.recorder = new VoiceRecorder(); } async init() { await this.recorder.initialize(); this.setupEventListeners(); } setupEventListeners() { const recordButton = document.getElementById('record-btn'); const pauseButton = document.getElementById('pause-btn'); const stopButton = document.getElementById('stop-btn'); recordButton?.addEventListener('click', () => this.handleRecord()); pauseButton?.addEventListener('click', () => this.handlePause()); stopButton?.addEventListener('click', () => this.handleStop()); } async handleRecord() { await this.recorder.startRecording(); this.startDurationUpdate(); this.updateUI('recording'); } async handlePause() { const status = await this.recorder.getCurrentStatus(); if (status.isPaused) { await this.recorder.resumeRecording(); this.updateUI('recording'); } else { await this.recorder.pauseRecording(); this.updateUI('paused'); } } async handleStop() { const result = await this.recorder.stopRecording(); this.stopDurationUpdate(); this.updateUI('stopped'); if (result) { this.showRecordingResult(result); } } startDurationUpdate() { this.updateInterval = setInterval(async () => { const status = await this.recorder.getCurrentStatus(); this.updateDurationDisplay(status.duration); }, 100); } stopDurationUpdate() { if (this.updateInterval) { clearInterval(this.updateInterval); } } updateDurationDisplay(duration: number) { const display = document.getElementById('duration'); if (display) { display.textContent = this.recorder.formatDuration(duration); } } updateUI(state: 'recording' | 'paused' | 'stopped') { // Update button states, colors, etc. console.log('UI state:', state); } showRecordingResult(result: any) { console.log('Recording complete:', result); // Show playback UI, save options, etc. } } // Initialize UI const ui = new AudioRecorderUI(); ui.init(); ``` ## Mejores prácticas [Section titled “Mejores prácticas”](#mejores-prácticas) 1. **Solicitar permisos con anticipación**: verifique los permisos antes de mostrar la interfaz de usuario de grabación 2. **Manejar interrupciones**: llamadas telefónicas, las alarmas pueden interrumpir la grabación 3. **Administrar almacenamiento**: limpia grabaciones antiguas para ahorrar espacio 4. **Proporcionar comentarios**: muestra el estado de la grabación, la duración y la forma de onda 5. **Prueba en dispositivos**: los simuladores/emuladores tienen soporte de audio limitado ## Problemas comunes [Section titled “Problemas comunes”](#problemas-comunes) ### Permiso denegado [Section titled “Permiso denegado”](#permiso-denegado) ```typescript const handlePermissionDenied = async () => { const permission = await AudioRecorder.checkPermissions(); if (permission.recordAudio === 'denied') { alert('Microphone permission is required to record audio. Please enable it in Settings.'); } }; ``` ### Recording Interrupted [Section titled “Recording Interrupted”](#recording-interrupted) ```typescript // Handle app going to background document.addEventListener('pause', async () => { const status = await recorder.getCurrentStatus(); if (status.isRecording) { await recorder.pauseRecording(); // Notify user } }); ``` ### Gestión de almacenamiento [Section titled “Gestión de almacenamiento”](#gestión-de-almacenamiento) ```typescript const cleanupOldRecordings = async () => { // Implement cleanup logic // Delete recordings older than X days // Or keep only last N recordings }; ``` ## Próximos pasos [Section titled “Próximos pasos”](#próximos-pasos) * Explore la [Referencia API](https://github.com/Cap-go/capacitor-audio-recorder#api) para obtener documentación completa del método * Consulte la [aplicación de ejemplo](https://github.com/Cap-go/capacitor-audio-recorder/tree/main/example) para un uso avanzado * Consulte el [tutorial](/plugins/capacitor-audio-recorder) para ver ejemplos completos de implementación. # @Capgo/Capacitor-Plugin-audiosession > Escucha los cambios de ruta de audio, reacciona a interrupciones y redirige la reproducción al altavoz sin salir de Capacitor. El Plugin AudioSession de Capgo te brinda control detallado sobre la capa AVAudioSession de iOS para que puedas manejar con elegancia auriculares, dispositivos Bluetooth e interrupciones inesperadas. Detección de ruta Detecta cuando los usuarios conectan o desconectan auriculares, AirPods, altavoces Bluetooth y más. Anulación de altavoz Fuerza la reproducción a través del altavoz integrado cuando necesites audio manos libres. Manejo de interrupciones Reacciona a eventos de interrupción como llamadas telefónicas o Siri para pausar y reanudar el audio de forma segura. Seguridad de tipos Definiciones sólidas de TypeScript facilitan la creación de experiencias de audio resilientes. Este Plugin se dirige a iOS donde se requiere el control de AVAudioSession. En otras plataformas, las llamadas se resuelven sin efecto, lo que te permite mantener una ruta de código compartida. # Empezando > Configure el complemento AudioSession para reaccionar a iOS cambios e interrupciones en la ruta de audio. > ℹ️ **Soporte de plataforma:** La sesión de audio API está disponible en iOS. Calls on Android and the web resolve without effect so you can keep shared logic. 1. **Instalar el paquete** * npm ```sh npm i @capgo/capacitor-plugin-audiosession ``` * pnpm ```sh pnpm add @capgo/capacitor-plugin-audiosession ``` * yarn ```sh yarn add @capgo/capacitor-plugin-audiosession ``` * bun ```sh bun add @capgo/capacitor-plugin-audiosession ``` 2. **Sincronizar plataforma iOS** * npm ```sh npx cap sync ios ``` * pnpm ```sh pnpm cap sync ios ``` * yarn ```sh yarn cap sync ios ``` * bun ```sh bunx cap sync ios ``` ## Inspeccionar las salidas disponibles [Section titled “Inspeccionar las salidas disponibles”](#inspeccionar-las-salidas-disponibles) ```typescript import { AudioSession, AudioSessionPorts } from '@capgo/capacitor-plugin-audiosession'; const outputs = await AudioSession.currentOutputs(); if (outputs.includes(AudioSessionPorts.BLUETOOTH_A2DP)) { console.log('Bluetooth speaker connected'); } ``` ## Anular al modo de altavoz [Section titled “Anular al modo de altavoz”](#anular-al-modo-de-altavoz) ```typescript import { AudioSession, OutputOverrideType } from '@capgo/capacitor-plugin-audiosession'; await AudioSession.overrideOutput(OutputOverrideType.speaker); // Restore system routing when you are done await AudioSession.overrideOutput(OutputOverrideType.default); ``` ## Escuche eventos de ruta e interrupción [Section titled “Escuche eventos de ruta e interrupción”](#escuche-eventos-de-ruta-e-interrupción) ```typescript import { AudioSession, RouteChangeReasons, InterruptionTypes } from '@capgo/capacitor-plugin-audiosession'; const routeListener = await AudioSession.addListener('routeChanged', (reason) => { if (reason === RouteChangeReasons.NEW_DEVICE_AVAILABLE) { console.log('User connected a new audio route'); } }); const interruptionListener = await AudioSession.addListener('interruption', (type) => { if (type === InterruptionTypes.BEGAN) { // Pause playback while we are interrupted } else { // Resume playback } }); // Later during cleanup await routeListener.remove(); await interruptionListener.remove(); ``` ## Derechos [Section titled “Derechos”](#derechos) * **iOS**: agregue los modos de audio de fondo necesarios (por ejemplo, `audio`, `voice`) dentro de `ios/App/App/Info.plist` si su aplicación reproduce audio en segundo plano. * **Other platforms**: No additional setup is necessary; Los métodos se resolverán con resultados vacíos. # @Capgo/Capacitor-autofill-Guardar-password > Habilita el autocompletar de contraseñas y la gestión de credenciales con integración del sistema para experiencias de autenticación fluidas. ## Descripción General [Section titled “Descripción General”](#descripción-general) El Plugin Capacitor Autofill Guardar Password proporciona funcionalidad de guardado y autocompletar de contraseñas para aplicaciones Capacitor. Este Plugin se integra con la gestión de credenciales a nivel del sistema para ofrecer experiencias de autenticación fluidas con almacenamiento y recuperación segura de contraseñas. Guardado de contraseñas Guarda credenciales en el llavero del sistema de forma segura 🔐 Integración de autocompletar Soporte de autocompletar de contraseñas a nivel del sistema 🗝️ Multiplataforma Soporte iOS con desarrollo de Android en progreso 📱 Asociación de dominios Dominios asociados para autenticación fluida ❤️ ## Instalación [Section titled “Instalación”](#instalación) ```bash npm install @capgo/capacitor-autofill-save-password npx cap sync ``` ## Soporte de Plataforma [Section titled “Soporte de Plataforma”](#soporte-de-plataforma) * **iOS**: Soporte completo (funciona con iOS 18.3 y versiones anteriores) * **Android**: Trabajo en progreso ## Configuración de iOS [Section titled “Configuración de iOS”](#configuración-de-ios) ### 1. Configuración de Dominios Asociados [Section titled “1. Configuración de Dominios Asociados”](#1-configuración-de-dominios-asociados) Configura dominios asociados en tu cuenta de Apple Developer y agrégalos a tu archivo `App.entitlements`: ```xml com.apple.developer.associated-domains webcredentials:yourdomain.com webcredentials:www.yourdomain.com ``` ### 2. Configuración de Credenciales Web [Section titled “2. Configuración de Credenciales Web”](#2-configuración-de-credenciales-web) Agrega un archivo `.well-known/apple-app-site-association` a tu sitio web: ```json { "webcredentials": { "apps": [ "TEAMID.com.yourcompany.yourapp" ] } } ``` ## Métodos API Principales [Section titled “Métodos API Principales”](#métodos-api-principales) ### Gestión de Contraseñas [Section titled “Gestión de Contraseñas”](#gestión-de-contraseñas) * `promptDialog(options)` - Guardar contraseña en el llavero del sistema * `readPassword()` - Leer contraseña guardada del llavero ## Ejemplo de Uso [Section titled “Ejemplo de Uso”](#ejemplo-de-uso) ```typescript import { SavePassword } from '@capgo/capacitor-autofill-save-password'; // Guardar contraseña después de un inicio de sesión exitoso async function login(username: string, password: string) { try { // Realiza tu lógica de inicio de sesión aquí const loginSuccess = await performLogin(username, password); if (loginSuccess) { // Solicitar al usuario guardar la contraseña await SavePassword.promptDialog({ username: username, password: password, url: 'https://yourdomain.com' // Solo iOS }); console.log('Password saved successfully'); } } catch (error) { console.error('Failed to save password:', error); } } // Leer contraseña guardada para autocompletar async function loadSavedCredentials() { try { const credentials = await SavePassword.readPassword(); if (credentials.username && credentials.password) { console.log('Found saved credentials'); // Pre-llenar formulario de inicio de sesión return { username: credentials.username, password: credentials.password }; } } catch (error) { console.error('Failed to read saved password:', error); } return null; } ``` ## Configuración de Android (Trabajo en Progreso) [Section titled “Configuración de Android (Trabajo en Progreso)”](#configuración-de-android-trabajo-en-progreso) Para soporte futuro de Android, se requerirá la siguiente configuración: ### 1. Plugin de Google Services [Section titled “1. Plugin de Google Services”](#1-plugin-de-google-services) Agrega a `android/app/build.gradle`: ```kotlin apply plugin: 'com.google.gms.google-services' ``` ### 2. Configuración de Dominio [Section titled “2. Configuración de Dominio”](#2-configuración-de-dominio) Agrega a `android/app/src/main/res/values/strings.xml`: ```xml [{ "relation": ["delegate_permission/common.handle_all_urls"], "target": { "namespace": "web", "site": "https://yourdomain.com" } }] ``` ### 3. JSON de Google Services [Section titled “3. JSON de Google Services”](#3-json-de-google-services) Agrega `google-services.json` a tu proyecto Android. ## Referencia de API [Section titled “Referencia de API”](#referencia-de-api) ### promptDialog(Opciones) [Section titled “promptDialog(Opciones)”](#promptdialogopciones) Guarda credenciales de usuario en el llavero del sistema. ```typescript interface SavePasswordOptions { username: string; password: string; url?: string; // Solo iOS - URL del dominio asociado } ``` ### readPassword() [Section titled “readPassword()”](#readpassword) Recupera credenciales guardadas del llavero del sistema. ```typescript interface SavedCredentials { username: string; password: string; } ``` ## Integración con Flujo de Inicio de Sesión [Section titled “Integración con Flujo de Inicio de Sesión”](#integración-con-flujo-de-inicio-de-sesión) ```typescript import { SavePassword } from '@capgo/capacitor-autofill-save-password'; class AuthService { async login(username: string, password: string) { try { // Autenticarse con tu backend const response = await fetch('/api/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username, password }) }); if (response.ok) { // Ofrecer guardar credenciales await this.offerToSavePassword(username, password); return true; } } catch (error) { console.error('Login failed:', error); } return false; } private async offerToSavePassword(username: string, password: string) { try { await SavePassword.promptDialog({ username, password, url: 'https://yourdomain.com' }); } catch (error) { // Usuario rechazó guardar o ocurrió un error console.log('Password not saved:', error); } } async loadSavedCredentials() { try { return await SavePassword.readPassword(); } catch (error) { console.log('No saved credentials found'); return null; } } } ``` ## Mejores Prácticas [Section titled “Mejores Prácticas”](#mejores-prácticas) * Solo solicita guardar contraseñas después de una autenticación exitosa * Maneja casos donde los usuarios rechazan guardar contraseñas con elegancia * Implementa manejo de errores adecuado para fallos de acceso al llavero * Usa dominios asociados para compartir credenciales sin problemas entre web y Aplicación * Prueba la funcionalidad de autocompletar en diferentes versiones de iOS ## Consideraciones de Seguridad [Section titled “Consideraciones de Seguridad”](#consideraciones-de-seguridad) * Las credenciales se almacenan en el llavero del sistema con indicadores de seguridad apropiados * Los dominios asociados aseguran que las credenciales solo sean accesibles para aplicaciones autorizadas * Sigue las pautas de seguridad de la plataforma para la gestión de credenciales * Considera implementar medidas de seguridad adicionales para aplicaciones sensibles ## Limitaciones [Section titled “Limitaciones”](#limitaciones) * El soporte de Android está actualmente en desarrollo * iOS requiere configuración adecuada de dominios asociados * El comportamiento de autocompletar puede variar entre diferentes versiones de iOS * Requiere consentimiento del usuario para guardar y acceder a credenciales ## Documentación [Section titled “Documentación”](#documentación) Consulta la [documentación completa](/docs/plugins/autofill-save-password/getting-started/) para guías de implementación detalladas y patrones de integración avanzados. # Comenzar > Aprende cómo instalar y configurar el Plugin Autofill Guardar Password para una gestión de contraseñas fluida en tu aplicación Capacitor. ## Instalación [Section titled “Instalación”](#instalación) * npm ```bash npm install @capgo/capacitor-autofill-save-password npx cap sync ``` * yarn ```bash yarn add @capgo/capacitor-autofill-save-password npx cap sync ``` * pnpm ```bash pnpm add @capgo/capacitor-autofill-save-password npx cap sync ``` * bun ```bash bun add @capgo/capacitor-autofill-save-password npx cap sync ``` ## Soporte de Plataforma [Section titled “Soporte de Plataforma”](#soporte-de-plataforma) * **iOS**: Soporte completo (iOS 18.3 y versiones anteriores) * **Android**: Trabajo en progreso ## Configuración de Plataforma [Section titled “Configuración de Plataforma”](#configuración-de-plataforma) ### iOS [Section titled “iOS”](#ios) #### 1. Configuración de Dominios Asociados [Section titled “1. Configuración de Dominios Asociados”](#1-configuración-de-dominios-asociados) Configura dominios asociados en tu cuenta de Apple Developer y agrégalos a tu archivo `App.entitlements`: ```xml com.apple.developer.associated-domains webcredentials:yourdomain.com webcredentials:www.yourdomain.com ``` #### 2. Configuración de Credenciales Web [Section titled “2. Configuración de Credenciales Web”](#2-configuración-de-credenciales-web) Agrega un archivo `.well-known/apple-app-site-association` a tu sitio web: ```json { "webcredentials": { "apps": [ "TEAMID.com.yourcompany.yourapp" ] } } ``` ### Android (Soporte Futuro) [Section titled “Android (Soporte Futuro)”](#android-soporte-futuro) #### 1. Plugin de Google Services [Section titled “1. Plugin de Google Services”](#1-plugin-de-google-services) Agrega a `android/app/build.gradle`: ```kotlin apply plugin: 'com.google.gms.google-services' ``` #### 2. Configuración de Dominio [Section titled “2. Configuración de Dominio”](#2-configuración-de-dominio) Agrega a `android/app/src/main/res/values/strings.xml`: ```xml [{ "relation": ["delegate_permission/common.handle_all_urls"], "target": { "namespace": "web", "site": "https://yourdomain.com" } }] ``` ## Ejemplo de Uso [Section titled “Ejemplo de Uso”](#ejemplo-de-uso) ```typescript import { SavePassword } from '@capgo/capacitor-autofill-save-password'; // Guardar contraseña después de un inicio de sesión exitoso async function login(username: string, password: string) { try { // Realiza tu lógica de inicio de sesión aquí const loginSuccess = await performLogin(username, password); if (loginSuccess) { // Solicitar al usuario guardar la contraseña await SavePassword.promptDialog({ username: username, password: password, url: 'https://yourdomain.com' // Solo iOS }); console.log('Password saved successfully'); } } catch (error) { console.error('Failed to save password:', error); } } // Leer contraseña guardada para autocompletar async function loadSavedCredentials() { try { const credentials = await SavePassword.readPassword(); if (credentials.username && credentials.password) { console.log('Found saved credentials'); // Pre-llenar formulario de inicio de sesión return { username: credentials.username, password: credentials.password }; } } catch (error) { console.error('Failed to read saved password:', error); } return null; } ``` ## Referencia de API [Section titled “Referencia de API”](#referencia-de-api) ### promptDialog(Opciones) [Section titled “promptDialog(Opciones)”](#promptdialogopciones) ```typescript promptDialog(options: SavePasswordOptions) => Promise ``` Guarda credenciales de usuario en el llavero del sistema. | Parámetro | Tipo | | ------------- | --------------------- | | **`options`** | `SavePasswordOptions` | ### readPassword() [Section titled “readPassword()”](#readpassword) ```typescript readPassword() => Promise ``` Recupera credenciales guardadas del llavero del sistema. **Retorna:** `Promise` ## Interfaces [Section titled “Interfaces”](#interfaces) ### SavePasswordOptions [Section titled “SavePasswordOptions”](#savepasswordoptions) | Propiedad | Tipo | Descripción | | -------------- | -------- | ---------------------------------------------- | | **`username`** | `string` | El nombre de usuario a guardar | | **`password`** | `string` | La contraseña a guardar | | **`url`** | `string` | Solo iOS - URL del dominio asociado (opcional) | ### SavedCredentials [Section titled “SavedCredentials”](#savedcredentials) | Propiedad | Tipo | Descripción | | -------------- | -------- | ----------------------------- | | **`username`** | `string` | El nombre de usuario guardado | | **`password`** | `string` | La contraseña guardada | ## Integración con Flujo de Inicio de Sesión [Section titled “Integración con Flujo de Inicio de Sesión”](#integración-con-flujo-de-inicio-de-sesión) ```typescript import { SavePassword } from '@capgo/capacitor-autofill-save-password'; class AuthService { async login(username: string, password: string) { try { // Autenticarse con tu backend const response = await fetch('/api/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username, password }) }); if (response.ok) { // Ofrecer guardar credenciales await this.offerToSavePassword(username, password); return true; } } catch (error) { console.error('Login failed:', error); } return false; } private async offerToSavePassword(username: string, password: string) { try { await SavePassword.promptDialog({ username, password, url: 'https://yourdomain.com' }); } catch (error) { // Usuario rechazó guardar o ocurrió un error console.log('Password not saved:', error); } } async loadSavedCredentials() { try { return await SavePassword.readPassword(); } catch (error) { console.log('No saved credentials found'); return null; } } } ``` ## Mejores Prácticas [Section titled “Mejores Prácticas”](#mejores-prácticas) * Solo solicita guardar contraseñas después de una autenticación exitosa * Maneja casos donde los usuarios rechazan guardar contraseñas con elegancia * Implementa manejo de errores adecuado para fallos de acceso al llavero * Usa dominios asociados para compartir credenciales sin problemas entre web y Aplicación * Prueba la funcionalidad de autocompletar en diferentes versiones de iOS ## Consideraciones de Seguridad [Section titled “Consideraciones de Seguridad”](#consideraciones-de-seguridad) * Las credenciales se almacenan en el llavero del sistema con indicadores de seguridad apropiados * Los dominios asociados aseguran que las credenciales solo sean accesibles para aplicaciones autorizadas * Sigue las pautas de seguridad de la plataforma para la gestión de credenciales * Considera implementar medidas de seguridad adicionales para aplicaciones sensibles # Background Geolocation > Recibe actualizaciones precisas de geolocalización incluso mientras la Aplicación está en segundo plano. Rastreo Preciso Prioriza la precisión para casos de uso como senderismo y navegación 🎯 Actualizaciones en Segundo Plano Continúa recibiendo actualizaciones de ubicación incluso cuando la Aplicación está en segundo plano 🏃 Eficiente en Batería Optimizado para equilibrar precisión y consumo de batería 🔋 Comenzar Consulta la [Guía de Inicio](/docs/plugins/background-geolocation/getting-started/) para instalar y configurar el Plugin. ## Comparación de Plugins [Section titled “Comparación de Plugins”](#comparación-de-plugins) Una breve comparación entre los tres principales Plugins de geolocalización en segundo plano comúnmente usados en aplicaciones Capacitor. | Plugin | Precisión | Segundo Plano | Carga HTTP | Precio | | --------------------------------------------------------- | ---------- | ------------- | ------------------------------------- | ------ | | `@capacitor-community/background-geolocation` (Community) | No preciso | Sí | No | Gratis | | `@capgo/background-geolocation` (este Plugin) | Preciso | Sí | No | Gratis | | Transistorsoft (original) | Preciso | Sí | Sí — cargador HTTP integrado a tu API | Pago | **Notas:** * El Plugin Community es ligero y continúa funcionando en segundo plano, pero se sabe que es menos preciso que las opciones siguientes. * Este Plugin de Capgo está optimizado para proporcionar posiciones de ubicación precisas y operación confiable en segundo plano sin requerir una licencia de pago. * Transistorsoft ofrece una solución comercial completa que también incluye capacidades integradas de carga HTTP para enviar datos de ubicación a tu backend. # Comenzar > Aprende cómo instalar y usar el Plugin Background Geolocation para rastrear la ubicación del dispositivo en segundo plano. ## Instalación [Section titled “Instalación”](#instalación) Este Plugin soporta Capacitor v7: | Capacitor | Plugin | | --------- | ------ | | v7 | v7 | * npm ```bash npm install @capgo/background-geolocation npx cap update ``` * yarn ```bash yarn add @capgo/background-geolocation npx cap update ``` * pnpm ```bash pnpm add @capgo/background-geolocation npx cap update ``` * bun ```bash bun add @capgo/background-geolocation npx cap update ``` ## Configuración de Plataforma [Section titled “Configuración de Plataforma”](#configuración-de-plataforma) ### iOS [Section titled “iOS”](#ios) Agrega las siguientes claves a `Info.plist.`: ```xml ... NSLocationWhenInUseUsageDescription We need to track your location NSLocationAlwaysAndWhenInUseUsageDescription We need to track your location while your device is locked. UIBackgroundModes location ... ``` ### Android [Section titled “Android”](#android) Establece la opción `android.useLegacyBridge` en `true` en tu configuración de Capacitor. Esto evita que las actualizaciones de ubicación se detengan después de 5 minutos en segundo plano. Ver y . En Android 13+, la aplicación necesita el permiso de tiempo de ejecución `POST_NOTIFICATIONS` para mostrar la notificación persistente que informa al usuario que su ubicación está siendo utilizada en segundo plano. Este permiso de tiempo de ejecución se solicita después de que se otorga el permiso de ubicación. Si tu aplicación reenvía actualizaciones de ubicación a un servidor en tiempo real, ten en cuenta que después de 5 minutos en segundo plano Android limitará las solicitudes HTTP iniciadas desde el WebView. La solución es usar un Plugin HTTP nativo como [CapacitorHttp](https://capacitorjs.com/docs/apis/http). Ver . La configuración específica de Android se puede hacer en `strings.xml`: ```xml Background Tracking drawable/ic_tracking yellow ``` ## Uso [Section titled “Uso”](#uso) ```javascript import { BackgroundGeolocation } from "@capgo/background-geolocation"; BackgroundGeolocation.start( { backgroundMessage: "Cancel to prevent battery drain.", backgroundTitle: "Tracking You.", requestPermissions: true, stale: false, distanceFilter: 50 }, (location, error) => { if (error) { if (error.code === "NOT_AUTHORIZED") { if (window.confirm( "This app needs your location, " + "but does not have permission.\n\n" + "Open settings now?" )) { // Puede ser útil dirigir al usuario a la configuración de su // dispositivo cuando se han denegado los permisos de ubicación. // El plugin proporciona el método 'openSettings' para hacer // exactamente esto. BackgroundGeolocation.openSettings(); } } return console.error(error); } return console.log(location); } ).then(() => { // Cuando ya no se necesiten actualizaciones de ubicación, el plugin debe detenerse llamando a BackgroundGeolocation.stop(); }); // Establece una ruta planificada para obtener un sonido de notificación cuando llega una nueva ubicación y no está en la ruta: BackgroundGeolocation.setPlannedRoute({soundFile: "assets/myFile.mp3", route: [[1,2], [3,4]], distance: 30 }); // Si solo quieres la ubicación actual, prueba algo como esto. Cuanto mayor sea el // tiempo de espera, más precisa será la estimación. Yo no bajaría de unos 100ms. function guessLocation(callback, timeout) { let last_location; BackgroundGeolocation.start( { requestPermissions: false, stale: true }, (location) => { last_location = location || undefined; } ).then(() => { setTimeout(() => { callback(last_location); BackgroundGeolocation.stop(); }, timeout); }); } ``` ## API [Section titled “API”](#api) ### start(…) [Section titled “start(…)”](#start) ```typescript start(options: StartOptions, callback: (position?: Location | undefined, error?: CallbackError | undefined) => void) => Promise ``` Para comenzar a escuchar cambios en la ubicación del dispositivo, llama a este método. Se devuelve una Promise para indicar que finalizó la llamada. El callback se llamará cada vez que una nueva ubicación esté disponible, o si hubo un Error al llamar a este método. No confíes en el rechazo de la promesa para esto. | Parámetro | Tipo | Descripción | | -------------- | ------------------------------------------------------ | -------------------------------------------------------------------------------------------- | | **`options`** | `StartOptions` | Las opciones de configuración | | **`callback`** | `(position?: Location, error?: CallbackError) => void` | La función de callback invocada cuando una nueva ubicación está disponible o ocurre un Error | ### stop() [Section titled “stop()”](#stop) ```typescript stop() => Promise ``` Detiene las actualizaciones de ubicación. ### openSettings() [Section titled “openSettings()”](#opensettings) ```typescript openSettings() => Promise ``` Abre la página de configuración de ubicación del dispositivo. Útil para dirigir a los usuarios a habilitar servicios de ubicación o ajustar permisos. ### setPlannedRoute(…) [Section titled “setPlannedRoute(…)”](#setplannedroute) ```typescript setPlannedRoute(options: SetPlannedRouteOptions) => Promise ``` Reproduce un archivo de sonido cuando el usuario se desvía de la ruta planificada. Esto debe usarse para reproducir un sonido (también en segundo plano, solo para nativo). | Parámetro | Tipo | Descripción | | ------------- | ------------------------ | ----------------------------------------------------------------------- | | **`options`** | `SetPlannedRouteOptions` | Las opciones para establecer la ruta planificada y el archivo de sonido | ## Interfaces [Section titled “Interfaces”](#interfaces) ### StartOptions [Section titled “StartOptions”](#startoptions) Las opciones para configurar las actualizaciones de ubicación. | Propiedad | Tipo | Descripción | Predeterminado | | ------------------------ | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------- | | **`backgroundMessage`** | `string` | Si la opción “backgroundMessage” está definida, el Plugin proporcionará actualizaciones de ubicación tanto si la Aplicación está en segundo plano como en primer plano. Si no está definida, las actualizaciones de ubicación solo están garantizadas en primer plano. Esto es cierto en ambas plataformas. En Android, debe mostrarse una notificación para continuar recibiendo actualizaciones de ubicación en segundo plano. Esta opción especifica el texto de esa notificación. | | | **`backgroundTitle`** | `string` | El título de la notificación mencionada anteriormente. | `"Using your location"` | | **`requestPermissions`** | `boolean` | Si los permisos deben solicitarse automáticamente al usuario, si no están ya otorgados. | `true` | | **`stale`** | `boolean` | Si es “true”, pueden entregarse ubicaciones obsoletas mientras el dispositivo obtiene una posición GPS. Eres responsable de verificar la propiedad “time”. Si es “false”, las ubicaciones están garantizadas de estar actualizadas. | `false` | | **`distanceFilter`** | `number` | La distancia en metros que el dispositivo debe moverse antes de que se active una nueva actualización de ubicación. Esto se usa para filtrar movimientos pequeños y reducir el número de actualizaciones. | `0` | ### Location [Section titled “Location”](#location) Representa una ubicación geográfica con varios atributos. Contiene todas las propiedades estándar de ubicación devueltas por proveedores GPS/red. | Propiedad | Tipo | Descripción | | ---------------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | | **`latitude`** | `number` | Latitud en grados. Rango: -90.0 a +90.0 | | **`longitude`** | `number` | Longitud en grados. Rango: -180.0 a +180.0 | | **`accuracy`** | `number` | Radio de incertidumbre horizontal en metros, con 68% de confianza. Valores más bajos indican ubicación más precisa. | | **`altitude`** | `number \| null` | Metros sobre el nivel del mar (o null si no está disponible). | | **`altitudeAccuracy`** | `number \| null` | Incertidumbre vertical en metros, con 68% de confianza (o null si no está disponible). | | **`simulated`** | `boolean` | `true` si la ubicación fue simulada por software, en lugar de GPS. Útil para detectar ubicaciones simuladas en desarrollo o pruebas. | | **`bearing`** | `number \| null` | Desviación del norte verdadero en grados (o null si no está disponible). Rango: 0.0 a 360.0 | | **`speed`** | `number \| null` | Velocidad en metros por segundo (o null si no está disponible). | | **`time`** | `number \| null` | Tiempo en que se produjo la ubicación, en milisegundos desde la época unix. Usa esto para verificar si una ubicación está obsoleta al usar stale: true. | ### CallbackError [Section titled “CallbackError”](#callbackerror) Objeto de Error que puede pasarse al callback de inicio de ubicación. Extiende el Error estándar con códigos de Error opcionales. | Propiedad | Tipo | Descripción | | ---------- | -------- | --------------------------------------------------------------- | | **`code`** | `string` | Código de Error opcional para manejo de errores más específico. | ### SetPlannedRouteOptions [Section titled “SetPlannedRouteOptions”](#setplannedrouteoptions) | Propiedad | Tipo | Descripción | Predeterminado | | --------------- | -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------- | | **`soundFile`** | `string` | El nombre del archivo de sonido a reproducir. Debe ser una ruta de sonido relativa válida en la carpeta pública de la Aplicación para funcionar tanto en plataformas web como nativas. No es necesario incluir la carpeta pública en la ruta. | | | **`route`** | `[number, number][]` | La ruta planificada como un array de pares de longitud y latitud. Cada par representa un punto en la ruta. Esto se usa para definir una ruta que el usuario puede seguir. La ruta se usa para reproducir un sonido cuando el usuario se desvía de ella. | | | **`distance`** | `number` | La distancia en metros que el usuario debe desviarse de la ruta planificada para activar el sonido. Esto se usa para determinar qué tan lejos de la ruta puede estar el usuario antes de que se reproduzca el sonido. Si no se especifica, se usa un valor predeterminado de 50 metros. | `50` | # @Capgo/Capacitor-barometer > Accede a datos de presión barométrica y altitud de sensores del dispositivo para aplicaciones de clima y fitness. Monitoreo de Presión Lecturas de presión atmosférica en tiempo real 🌡️ Cálculo de Altitud Deriva altitud relativa de cambios de presión 📈 Listeners de Eventos Suscríbete a eventos de cambio de presión 🔔 Multiplataforma Funciona en dispositivos iOS y Android con sensores de barómetro 📱 Comenzar Consulta la [Guía de Inicio](/docs/plugins/barometer/getting-started/) para instalar y configurar el Plugin. # Comenzar con Barometer > Aprende cómo integrar la detección de presión barométrica en tu aplicación Capacitor Esta guía te llevará a través de la integración del Plugin Capacitor Barometer en tu aplicación. ## Instalación [Section titled “Instalación”](#instalación) Instala el Plugin usando npm: ```bash npm install @capgo/capacitor-barometer npx cap sync ``` ## Configuración de iOS [Section titled “Configuración de iOS”](#configuración-de-ios) Agrega lo siguiente a tu `Info.plist`: ```xml NSMotionUsageDescription This app uses the barometer to measure atmospheric pressure ``` ## Configuración de Android [Section titled “Configuración de Android”](#configuración-de-android) No se requiere configuración adicional. El Plugin solicita automáticamente los permisos necesarios. ## Uso Básico [Section titled “Uso Básico”](#uso-básico) ### Importar el Plugin [Section titled “Importar el Plugin”](#importar-el-plugin) ```typescript import { Barometer } from '@capgo/capacitor-barometer'; ``` ### Verificar Disponibilidad del Sensor [Section titled “Verificar Disponibilidad del Sensor”](#verificar-disponibilidad-del-sensor) ```typescript const checkBarometer = async () => { const { available } = await Barometer.isAvailable(); console.log('Barometer available:', available); }; ``` ### Iniciar Monitoreo de Presión [Section titled “Iniciar Monitoreo de Presión”](#iniciar-monitoreo-de-presión) ```typescript const startMonitoring = async () => { await Barometer.start({ interval: 1000 // Intervalo de actualización en milisegundos }); console.log('Barometer monitoring started'); }; ``` ### Escuchar Eventos de Presión [Section titled “Escuchar Eventos de Presión”](#escuchar-eventos-de-presión) ```typescript Barometer.addListener('pressureChange', (data) => { console.log('Pressure (hPa):', data.pressure); console.log('Relative altitude (m):', data.relativeAltitude); console.log('Timestamp:', data.timestamp); }); ``` ### Obtener Lectura Actual [Section titled “Obtener Lectura Actual”](#obtener-lectura-actual) ```typescript const getCurrentPressure = async () => { const reading = await Barometer.getCurrentReading(); console.log('Current pressure:', reading.pressure, 'hPa'); console.log('Relative altitude:', reading.relativeAltitude, 'm'); }; ``` ### Detener Monitoreo [Section titled “Detener Monitoreo”](#detener-monitoreo) ```typescript const stopMonitoring = async () => { await Barometer.stop(); console.log('Barometer monitoring stopped'); }; ``` ## Ejemplo Completo [Section titled “Ejemplo Completo”](#ejemplo-completo) Aquí hay un ejemplo completo mostrando la integración del barómetro: ```typescript import { Barometer } from '@capgo/capacitor-barometer'; class BarometerService { private listener: any; async initialize() { // Verificar disponibilidad const { available } = await Barometer.isAvailable(); if (!available) { console.error('Barometer not available on this device'); return; } // Iniciar monitoreo await Barometer.start({ interval: 1000 }); // Agregar listener this.listener = Barometer.addListener('pressureChange', (data) => { this.handlePressureChange(data); }); } handlePressureChange(data: any) { console.log('Pressure:', data.pressure, 'hPa'); console.log('Altitude:', data.relativeAltitude, 'm'); // Actualizar UI o procesar datos this.updateDisplay(data); } updateDisplay(data: any) { // Convertir hPa a otras unidades const inHg = (data.pressure * 0.02953).toFixed(2); const mmHg = (data.pressure * 0.750062).toFixed(1); console.log(`Pressure: ${data.pressure.toFixed(1)} hPa`); console.log(` ${inHg} inHg`); console.log(` ${mmHg} mmHg`); console.log(`Altitude: ${data.relativeAltitude.toFixed(1)} m`); } async cleanup() { // Eliminar listener if (this.listener) { this.listener.remove(); } // Detener monitoreo await Barometer.stop(); } } // Uso const barometerService = new BarometerService(); barometerService.initialize(); // Limpieza cuando termine // barometerService.cleanup(); ``` ## Entendiendo las Lecturas [Section titled “Entendiendo las Lecturas”](#entendiendo-las-lecturas) ### Presión Atmosférica [Section titled “Presión Atmosférica”](#presión-atmosférica) * Medida en hectopascales (hPa) o milibares (mb) * Presión estándar al nivel del mar: 1013.25 hPa * Mayor altitud = menor presión * Los sistemas meteorológicos afectan las lecturas de presión ### Altitud Relativa [Section titled “Altitud Relativa”](#altitud-relativa) * Calculada a partir de cambios de presión * Relativa al punto de referencia inicial * Precisión: ±10-30 metros * Funciona mejor para seguimiento a corto plazo ## Mejores Prácticas [Section titled “Mejores Prácticas”](#mejores-prácticas) 1. **Verificar Disponibilidad**: Siempre verifica la disponibilidad del sensor antes de usarlo 2. **Intervalos Razonables**: No consultes con demasiada frecuencia (1000ms es bueno) 3. **Eliminar Listeners**: Limpia los listeners cuando no sean necesarios 4. **Calibración**: Usa altitud conocida para lecturas precisas 5. **Impacto en Batería**: Monitorea el uso de batería con detección continua ## Problemas Comunes [Section titled “Problemas Comunes”](#problemas-comunes) ### Sensor No Disponible [Section titled “Sensor No Disponible”](#sensor-no-disponible) ```typescript try { const { available } = await Barometer.isAvailable(); if (!available) { // Alternativa a altitud GPS o entrada manual showAlternativeMethod(); } } catch (error) { console.error('Barometer check failed:', error); } ``` ### Datos Ruidosos [Section titled “Datos Ruidosos”](#datos-ruidosos) ```typescript class PressureFilter { private readings: number[] = []; private maxReadings = 5; addReading(pressure: number): number { this.readings.push(pressure); if (this.readings.length > this.maxReadings) { this.readings.shift(); } // Devolver promedio móvil const sum = this.readings.reduce((a, b) => a + b, 0); return sum / this.readings.length; } } ``` ## Próximos Pasos [Section titled “Próximos Pasos”](#próximos-pasos) * Explora la [Referencia de API](https://github.com/Cap-go/capacitor-barometer#api) para documentación completa de métodos * Consulta la [aplicación de ejemplo](https://github.com/Cap-go/capacitor-barometer/tree/main/example) para uso avanzado * Ve el [tutorial](/plugins/capacitor-barometer) para ejemplos completos de implementación # @Capgo/camera-preview > Muestra la vista previa de la cámara en tu aplicación con control total sobre la configuración de la cámara, captura fotos y videos directamente desde la vista previa. Vista previa en tiempo real Muestra la transmisión de cámara en vivo directamente en la UI de tu aplicación 🚀 Control total Controla el flash, zoom, enfoque y cambia entre cámaras 📸 Capacidades de captura Toma fotos y graba videos desde la vista previa 🎥 Documentación Completa Consulta la [Documentación](/docs/plugins/camera-preview/getting-started/) para dominar el Plugin en solo unos minutos. # Comenzar > Aprende cómo instalar y usar el Plugin Camera Preview para mostrar la transmisión de cámara en tiempo real y capturar fotos/videos en tu aplicación Capacitor. 1. **Instalar el paquete** * npm ```sh npm i @Capgo/camera-preview ``` * pnpm ```sh pnpm add @Capgo/camera-preview ``` * yarn ```sh yarn add @Capgo/camera-preview ``` * bun ```sh bun add @Capgo/camera-preview ``` 2. **Sincronizar con proyectos nativos** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` 3. **Configurar permisos** ### iOS [Section titled “iOS”](#ios) Agrega la descripción de uso de la cámara a tu `Info.plist`: ```xml NSCameraUsageDescription To take photos and videos NSMicrophoneUsageDescription To record audio with videos NSPhotoLibraryUsageDescription To save photos and videos ``` ### Android [Section titled “Android”](#android) Agrega permisos de cámara a tu `AndroidManifest.xml`: ```xml ``` ## Uso [Section titled “Uso”](#uso) Importa el Plugin y usa sus métodos para controlar la cámara: ```typescript import { CameraPreview } from '@capgo/camera-preview'; import { CameraPreviewOptions, CameraPreviewPictureOptions } from '@capgo/camera-preview'; // Start camera preview const startCamera = async () => { const cameraPreviewOptions: CameraPreviewOptions = { position: 'rear', height: 1920, width: 1080, quality: 50, toBack: false, paddingBottom: 0, rotateWhenOrientationChanged: true, x: 0, y: 0 }; await CameraPreview.start(cameraPreviewOptions); }; // Stop camera preview const stopCamera = async () => { await CameraPreview.stop(); }; // Take a picture const takePicture = async () => { const pictureOptions: CameraPreviewPictureOptions = { quality: 90, width: 1920, height: 1080 }; const result = await CameraPreview.capture(pictureOptions); const base64PictureData = result.value; // Use the base64 image data console.log(base64PictureData); }; // Switch camera const flipCamera = async () => { await CameraPreview.flip(); }; // Control flash const setFlashMode = async (mode: 'on' | 'off' | 'auto' | 'torch') => { await CameraPreview.setFlashMode({ flashMode: mode }); }; // Start recording video const startRecording = async () => { await CameraPreview.startRecordVideo({ storeToFile: true, fileName: 'myvideo' }); }; // Stop recording const stopRecording = async () => { const result = await CameraPreview.stopRecordVideo(); console.log('Video path:', result.videoFilePath); }; ``` ## Referencia de API [Section titled “Referencia de API”](#referencia-de-api) ### start(Opciones) [Section titled “start(Opciones)”](#startopciones) Inicia la vista previa de la cámara. ```typescript interface CameraPreviewOptions { position?: 'rear' | 'front'; height?: number; width?: number; x?: number; y?: number; toBack?: boolean; paddingBottom?: number; rotateWhenOrientationChanged?: boolean; storeToFile?: boolean; disableExifHeaderStripping?: boolean; enableHighResolution?: boolean; disableAudio?: boolean; lockAndroidOrientation?: boolean; enableOpacity?: boolean; enableZoom?: boolean; } ``` ### stop() [Section titled “stop()”](#stop) Detiene la vista previa de la cámara. ### capture(Opciones) [Section titled “capture(Opciones)”](#captureopciones) Toma una foto y la devuelve como cadena codificada en base64. ```typescript interface CameraPreviewPictureOptions { height?: number; width?: number; quality?: number; } ``` ### flip() [Section titled “flip()”](#flip) Cambia entre la cámara frontal y trasera. ### setFlashMode(Opciones) [Section titled “setFlashMode(Opciones)”](#setflashmodeopciones) Establece el modo de flash. ```typescript interface CameraPreviewFlashMode { flashMode: 'off' | 'on' | 'auto' | 'torch'; } ``` ### startRecordVideo(Opciones) [Section titled “startRecordVideo(Opciones)”](#startrecordvideoopciones) Inicia la grabación de video. ```typescript interface CameraPreviewVideoOptions { storeToFile?: boolean; fileName?: string; width?: number; height?: number; quality?: number; withFlash?: boolean; } ``` ### stopRecordVideo() [Section titled “stopRecordVideo()”](#stoprecordvideo) Detiene la grabación de video y devuelve la ruta del archivo de video. ## Funciones Avanzadas [Section titled “Funciones Avanzadas”](#funciones-avanzadas) ### Control de Enfoque [Section titled “Control de Enfoque”](#control-de-enfoque) ```typescript // Set focus await CameraPreview.setFocusCoordinate({ x: 100, y: 100 }); // Get supported focus modes const focusModes = await CameraPreview.getFocusModes(); // Set focus mode await CameraPreview.setFocusMode({ focusMode: 'continuous-picture' }); ``` ### Control de Zoom [Section titled “Control de Zoom”](#control-de-zoom) ```typescript // Get max zoom const maxZoom = await CameraPreview.getMaxZoom(); // Set zoom await CameraPreview.setZoom({ zoom: 2.0 }); // Get current zoom const currentZoom = await CameraPreview.getZoom(); ``` ### Control de Exposición [Section titled “Control de Exposición”](#control-de-exposición) ```typescript // Get exposure compensation range const range = await CameraPreview.getExposureCompensationRange(); // Set exposure compensation await CameraPreview.setExposureCompensation({ exposureCompensation: 0.5 }); ``` ## Mejores Prácticas [Section titled “Mejores Prácticas”](#mejores-prácticas) 1. **Solicitar permisos primero** ```typescript import { Camera } from '@capacitor/camera'; const requestPermissions = async () => { const permissions = await Camera.requestPermissions(); if (permissions.camera === 'granted') { // Start camera preview } }; ``` 2. **Manejar cambios de orientación** Establece `rotateWhenOrientationChanged: true` para ajustar automáticamente la vista previa. 3. **Optimizar el rendimiento** * Usa configuraciones de resolución apropiadas * Detén la vista previa cuando no esté en uso * Desactiva las funciones que no necesites 4. **Manejo de errores** ```typescript try { await CameraPreview.start(options); } catch (error) { console.error('Failed to start camera:', error); } ``` ## Notas de Plataforma [Section titled “Notas de Plataforma”](#notas-de-plataforma) ### iOS [Section titled “iOS”](#ios-1) * Requiere iOS 10.0+ * Usa el framework AVFoundation * Soporta aceleración por hardware ### Android [Section titled “Android”](#android-1) * Requiere Android 5.0 (API 21)+ * Usa Camera2 API * Soporta varias funciones de cámara según las capacidades del dispositivo # Capacitor+ > Capacitor+ es un Bifurcación automatizado de Capacitor que fusiona PRs de la comunidad más rápido, con lanzamientos revisados por seguridad. Tip **¿Por qué Capacitor+?** Grandes PRs se quedan sin fusionar en el repositorio oficial de Capacitor durante meses. Capacitor+ fusiona activamente estas contribuciones de la comunidad para que no tengas que esperar. PRs de la Comunidad Fusionados Más Rápido ¿Correcciones de errores y funciones atascadas en upstream? Las fusionamos para que puedas beneficiarte inmediatamente. Auto-Sincronizado Diariamente Cada cambio de Capacitor oficial es automáticamente extraído, probado y verificado. Revisión de Seguridad Cada lanzamiento es analizado por IA para vulnerabilidades de seguridad, cambios importantes y riesgos de estabilidad. Reemplazo Directo Misma API que Capacitor oficial - solo cambia el ámbito del paquete y listo. ## Filosofía [Section titled “Filosofía”](#filosofía) El equipo de Ionic mantiene Capacitor con sus propias prioridades y calendario de lanzamientos. Las contribuciones de la comunidad pueden esperar meses o incluso años para ser fusionadas. Capacitor+ toma un enfoque diferente: 1. **Fusionar PRs de Forks** - PRs valiosos atascados en la cola upstream son fusionados activamente 2. **Sincronización Continua** - Cada cambio upstream es automáticamente extraído y probado 3. **Lanzamientos Rápidos** - Los cambios se publican en npm tan pronto como pasan CI 4. **Comunidad Primero** - Tus contribuciones importan y son priorizadas 5. **Transparencia** - Toda la automatización es de código abierto y visible ## Paquetes [Section titled “Paquetes”](#paquetes) | Paquete | npm | | ------------------------- | --------------------------------------------------------------------------------------------------------------------- | | `@capacitor-plus/core` | [![npm](https://img.shields.io/npm/v/@capacitor-plus/core)](https://www.npmjs.com/Paquete/@Capacitor-plus/core) | | `@capacitor-plus/cli` | [![npm](https://img.shields.io/npm/v/@capacitor-plus/cli)](https://www.npmjs.com/Paquete/@Capacitor-plus/CLI) | | `@capacitor-plus/android` | [![npm](https://img.shields.io/npm/v/@capacitor-plus/android)](https://www.npmjs.com/Paquete/@Capacitor-plus/android) | | `@capacitor-plus/ios` | [![npm](https://img.shields.io/npm/v/@capacitor-plus/ios)](https://www.npmjs.com/Paquete/@Capacitor-plus/ios) | # Comenzar > Aprende cómo instalar Capacitor+ como reemplazo directo de los paquetes oficiales de Capacitor. ## Instalación de Nuevo Proyecto [Section titled “Instalación de Nuevo Proyecto”](#instalación-de-nuevo-proyecto) 1. **Instalar paquetes principales** ```bash npm install @capacitor-plus/core @capacitor-plus/cli ``` 2. **Agregar paquetes de plataforma** ```bash npm install @capacitor-plus/android # for Android npm install @capacitor-plus/ios # for iOS ``` 3. **Inicializar Capacitor** * npm ```sh npx cap init ``` * pnpm ```sh pnpm cap init ``` * yarn ```sh yarn cap init ``` * bun ```sh bunx cap init ``` 4. **Agregar plataformas** * npm ```sh npx cap Agregar android ``` * pnpm ```sh pnpm cap Agregar android ``` * yarn ```sh yarn cap Agregar android ``` * bun ```sh bunx cap Agregar android ``` - npm ```sh npx cap Agregar ios ``` - pnpm ```sh pnpm cap Agregar ios ``` - yarn ```sh yarn cap Agregar ios ``` - bun ```sh bunx cap Agregar ios ``` ## Migración desde Capacitor Oficial [Section titled “Migración desde Capacitor Oficial”](#migración-desde-capacitor-oficial) Si tienes un proyecto de Capacitor existente, migrar a Capacitor+ es simple: 1. **Eliminar paquetes oficiales** ```bash npm uninstall @capacitor/core @capacitor/cli @capacitor/android @capacitor/ios ``` 2. **Instalar paquetes de Capacitor+** ```bash npm install @capacitor-plus/core @capacitor-plus/cli npm install @capacitor-plus/android # if using Android npm install @capacitor-plus/ios # if using iOS ``` 3. **Sincronizar tu proyecto** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` Note ¡No se requieren cambios en el código! Capacitor+ tiene la misma API que Capacitor oficial. Tus importaciones y código permanecen exactamente iguales. ## Uso [Section titled “Uso”](#uso) Dado que Capacitor+ es compatible con la API, tu código existente funciona sin cambios: ```typescript import { Capacitor } from '@capacitor/core'; import { registerPlugin } from '@capacitor/core'; // Check platform const platform = Capacitor.getPlatform(); console.log('Running on:', platform); // Check if native if (Capacitor.isNativePlatform()) { console.log('Running on native platform'); } // Register a custom plugin const MyPlugin = registerPlugin('MyPlugin'); ``` ### Con Plugins Oficiales de Capacitor [Section titled “Con Plugins Oficiales de Capacitor”](#con-plugins-oficiales-de-capacitor) Todos los Plugins oficiales de Capacitor funcionan sin problemas: ```typescript import { Camera, CameraResultType } from '@capacitor/camera'; import { Geolocation } from '@capacitor/geolocation'; import { Storage } from '@capacitor/preferences'; // Camera const photo = await Camera.getPhoto({ quality: 90, resultType: CameraResultType.Uri }); // Geolocation const position = await Geolocation.getCurrentPosition(); // Storage await Storage.set({ key: 'name', value: 'John' }); ``` ### Con Plugins de Capgo [Section titled “Con Plugins de Capgo”](#con-plugins-de-capgo) Los Plugins de Capgo funcionan perfectamente con Capacitor+: ```typescript import { CapacitorUpdater } from '@capgo/capacitor-updater'; import { ScreenOrientation } from '@capgo/capacitor-screen-orientation'; import { CapacitorFlash } from '@capgo/capacitor-flash'; // Live updates await CapacitorUpdater.notifyAppReady(); // Screen orientation await ScreenOrientation.lock({ orientation: 'portrait' }); // Flashlight await CapacitorFlash.toggle(); ``` ## Cómo Funciona la Sincronización [Section titled “Cómo Funciona la Sincronización”](#cómo-funciona-la-sincronización) ```plaintext ┌─────────────────────┐ ┌──────────────────┐ ┌──────────────────┐ ┌─────────────────┐ │ ionic-team/ │ │ CI/CD │ │ Claude Code │ │ npm publish │ │ capacitor │────▶│ Pipeline │────▶│ Security Review │────▶│ @capacitor-plus│ │ (upstream) │ │ (daily sync) │ │ (AI analysis) │ │ packages │ └─────────────────────┘ └──────────────────┘ └──────────────────┘ └─────────────────┘ ``` 1. **Sincronización Diaria**: GitHub Actions obtiene los últimos cambios de `ionic-team/capacitor` 2. **Creación de PR**: Los cambios se proponen como Solicitudes de extracción a la rama `plus` 3. **Validación CI**: Se ejecuta la suite de pruebas completa (lint, pruebas unitarias, compilación iOS, compilación Android) 4. **Revisión de Seguridad**: El análisis impulsado por IA verifica vulnerabilidades y cambios importantes 5. **Auto-Merge**: Solo si CI pasa Y la revisión de seguridad aprueba 6. **Auto-Publish**: Nueva versión publicada en npm bajo `@capacitor-plus/*` ## Detalles de Revisión de Seguridad [Section titled “Detalles de Revisión de Seguridad”](#detalles-de-revisión-de-seguridad) Cada sincronización upstream se analiza para: | Verificación | Qué Detecta | | ------------------------- | --------------------------------------------------------------------------------------- | | **Seguridad** | Inyección de comandos, XSS, path traversal, secretos hardcodeados | | **Cambios Importantes** | APIs eliminadas/renombradas, firmas modificadas, cambios de configuración | | **Estabilidad** | Dereferencias nulas, excepciones no manejadas, condiciones de carrera, fugas de memoria | | **Seguridad de Datos** | Escenarios de pérdida de datos, violaciones de privacidad, almacenamiento inseguro | | **Integridad del Código** | Código ofuscado, llamadas de red sospechosas, puertas traseras | Caution Si se detectan problemas, el PR se marca para revisión manual y NO se fusionará automáticamente. Esto garantiza que siempre obtengas versiones estables y seguras. ## Enviar tu PR [Section titled “Enviar tu PR”](#enviar-tu-pr) ¿Tienes un PR atascado en el repositorio oficial de Capacitor? Hazlo fusionar en Capacitor+: 1. **Abre un Problema** en el [repositorio de Capacitor+](https://github.com/Cap-go/capacitor-plus/issues) enlazando a tu PR upstream 2. **O envíalo directamente** como PR a la rama `plus` 3. El equipo revisará, ejecutará CI y fusionará si pasa De esta manera tú y otros pueden beneficiarse de tu trabajo inmediatamente sin esperar el ciclo de lanzamiento upstream. ## Preguntas Frecuentes [Section titled “Preguntas Frecuentes”](#preguntas-frecuentes) ### ¿Está listo para producción? [Section titled “¿Está listo para producción?”](#está-listo-para-producción) Sí. Capacitor+ se usa en aplicaciones de producción. Cada lanzamiento pasa la misma suite de pruebas que Capacitor oficial, además de análisis de seguridad adicional. ### ¿Mis Plugins oficiales seguirán funcionando? [Section titled “¿Mis Plugins oficiales seguirán funcionando?”](#mis-plugins-oficiales-seguirán-funcionando) Sí. Todos los Plugins `@capacitor/*` funcionan con Capacitor+ sin problemas. ### ¿Qué pasa si upstream lanza un cambio importante? [Section titled “¿Qué pasa si upstream lanza un cambio importante?”](#qué-pasa-si-upstream-lanza-un-cambio-importante) La revisión de seguridad de IA marca cambios importantes para revisión manual. Verás los cambios documentados antes de que se fusionen. ### ¿Cómo reporto problemas? [Section titled “¿Cómo reporto problemas?”](#cómo-reporto-problemas) Reporta Problemas en el [repositorio de GitHub de Capacitor+](https://github.com/Cap-go/capacitor-plus/issues). Para problemas que también afectan a Capacitor oficial, ayudaremos a coordinar upstream. ### ¿Puedo contribuir? [Section titled “¿Puedo contribuir?”](#puedo-contribuir) ¡Absolutamente! Los PRs son bienvenidos. Puedes enviar correcciones directamente o solicitar que se fusionen PRs upstream específicos. # @Capgo/Capacitor-compass > Access Dispositivo compass sensor for heading data with real-time Actualizaciones on iOS and Android. Real-time Heading Get continuous compass heading Actualizaciones in degrees (0-360) Cross-platform Works on both iOS and Android Dispositivos with native sensor access Permission Handling Built-in permission management for location services (iOS) Event-driven Subscribe A heading changes with efficient event listeners # Comenzar > Aprende cómo instalar y usar el Plugin Compass para leer el rumbo de la brújula del dispositivo en tu aplicación Capacitor. 1. **Instalar el paquete** * npm ```sh npm i @Capgo/Capacitor-compass ``` * pnpm ```sh pnpm add @Capgo/Capacitor-compass ``` * yarn ```sh yarn add @Capgo/Capacitor-compass ``` * bun ```sh bun add @Capgo/Capacitor-compass ``` 2. **Sincronizar con proyectos nativos** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Configuración de iOS [Section titled “Configuración de iOS”](#configuración-de-ios) En iOS, el acceso a la brújula requiere permiso de ubicación. Agrega lo siguiente a tu `Info.plist`: ```xml NSLocationWhenInUseUsageDescription We need location permission to access the compass ``` ## Configuración de Android [Section titled “Configuración de Android”](#configuración-de-android) No se requiere configuración adicional. El Plugin usa los sensores magnetómetro y acelerómetro del dispositivo. ## Uso [Section titled “Uso”](#uso) Importa el Plugin y usa sus métodos para leer el rumbo de la brújula: ```typescript import { CapgoCompass } from '@capgo/capacitor-compass'; // Obtener rumbo actual una vez const getCurrentHeading = async () => { const { value } = await CapgoCompass.getCurrentHeading(); console.log('Current heading:', value, 'degrees'); }; // Iniciar actualizaciones continuas de rumbo const startCompass = async () => { // Comenzar a escuchar actualizaciones await CapgoCompass.startListening(); // Agregar listener para cambios de rumbo const handle = await CapgoCompass.addListener('headingChange', (event) => { console.log('Heading:', event.value, 'degrees'); }); // Más tarde, para dejar de escuchar: // await CapgoCompass.stopListening(); // await handle.remove(); }; // Verificar permisos const checkPermission = async () => { const status = await CapgoCompass.checkPermissions(); console.log('Permission status:', status.compass); }; // Solicitar permisos const requestPermission = async () => { const status = await CapgoCompass.requestPermissions(); if (status.compass === 'granted') { console.log('Compass access granted'); } }; ``` ## Referencia de API [Section titled “Referencia de API”](#referencia-de-api) ### getCurrentHeading() [Section titled “getCurrentHeading()”](#getcurrentheading) Obtiene el rumbo actual de la brújula en grados. ```typescript const result = await CapgoCompass.getCurrentHeading(); // Retorna: { value: number } - rumbo en grados (0-360) ``` ### startListening() [Section titled “startListening()”](#startlistening) Comienza a escuchar cambios de rumbo de la brújula. Debe llamarse antes de que se emitan eventos de rumbo. ```typescript await CapgoCompass.startListening(); ``` ### stopListening() [Section titled “stopListening()”](#stoplistening) Detiene la escucha de cambios de rumbo de la brújula. ```typescript await CapgoCompass.stopListening(); ``` ### addListener(‘headingChange’, callback) [Section titled “addListener(‘headingChange’, callback)”](#addlistenerheadingchange-callback) Agrega un listener para eventos de cambio de rumbo. ```typescript const handle = await CapgoCompass.addListener('headingChange', (event) => { console.log('Heading:', event.value); }); // Eliminar listener cuando termine await handle.remove(); ``` ### removeAllListeners() [Section titled “removeAllListeners()”](#removealllisteners) Elimina todos los listeners registrados. ```typescript await CapgoCompass.removeAllListeners(); ``` ### checkPermissions() [Section titled “checkPermissions()”](#checkpermissions) Verifica el estado actual de permisos. ```typescript const status = await CapgoCompass.checkPermissions(); // Retorna: { compass: 'prompt' | 'granted' | 'denied' } ``` ### requestPermissions() [Section titled “requestPermissions()”](#requestpermissions) Solicita permiso para acceder a datos de la brújula. ```typescript const status = await CapgoCompass.requestPermissions(); ``` ### getPluginVersion() [Section titled “getPluginVersion()”](#getpluginversion) Obtiene la versión del Plugin nativo. ```typescript const { version } = await CapgoCompass.getPluginVersion(); ``` ## Ejemplo Completo [Section titled “Ejemplo Completo”](#ejemplo-completo) ```typescript import { CapgoCompass } from '@capgo/capacitor-compass'; export class CompassService { private listenerHandle: any = null; async init() { // Verificar y solicitar permisos const status = await CapgoCompass.checkPermissions(); if (status.compass !== 'granted') { const result = await CapgoCompass.requestPermissions(); if (result.compass !== 'granted') { throw new Error('Compass permission denied'); } } } async startTracking(onHeadingChange: (heading: number) => void) { // Comenzar a escuchar actualizaciones await CapgoCompass.startListening(); // Agregar listener de eventos this.listenerHandle = await CapgoCompass.addListener( 'headingChange', (event) => { onHeadingChange(event.value); } ); } async stopTracking() { if (this.listenerHandle) { await this.listenerHandle.remove(); this.listenerHandle = null; } await CapgoCompass.stopListening(); } async getHeading(): Promise { const { value } = await CapgoCompass.getCurrentHeading(); return value; } getCardinalDirection(heading: number): string { const directions = ['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW']; const index = Math.round(heading / 45) % 8; return directions[index]; } } ``` ## Notas de Plataforma [Section titled “Notas de Plataforma”](#notas-de-plataforma) ### iOS [Section titled “iOS”](#ios) * Requiere iOS 10.0+ * Usa Core Location para datos de rumbo * Requiere permiso de ubicación (NSLocationWhenInUseUsageDescription) * Las actualizaciones de rumbo son continuas cuando la escucha está activa ### Android [Section titled “Android”](#android) * Requiere Android 6.0 (API 23)+ * Usa sensores de acelerómetro y magnetómetro * No se requieren permisos especiales para sensores de brújula * El rumbo se calcula a partir de fusión de sensores ### Web [Section titled “Web”](#web) * No soportado en plataforma web * Los métodos lanzarán errores cuando se llamen # @Capgo/Capacitor-contacts > Accede, busca y gestiona los contactos del dispositivo en tu aplicación Capacitor con operaciones CRUD completas y manejo de permisos. Leer Contactos Accede a la lista de contactos del dispositivo con todos los detalles 📇 Funcionalidad de Búsqueda Encuentra contactos por nombre, correo electrónico o teléfono 🔍 Crear y Actualizar Agrega nuevos contactos o modifica los existentes ✏️ Eliminar Contactos Elimina contactos del dispositivo de forma segura 🗑️ Manejo de Permisos Gestiona los permisos de contactos correctamente con cumplimiento del manifiesto de privacidad 🔒 Primeros Pasos Consulta la [Guía de Inicio](/docs/plugins/contacts/getting-started/) para instalar y configurar el Plugin. # Primeros Pasos con Contactos > Aprende cómo integrar la gestión de contactos en tu aplicación Capacitor Esta guía te guiará a través de la integración del Plugin de Contactos de Capacitor en tu aplicación. ## Instalación [Section titled “Instalación”](#instalación) Instala el Plugin usando npm: ```bash npm install @capgo/capacitor-contacts npx cap sync ``` ## Configuración de iOS [Section titled “Configuración de iOS”](#configuración-de-ios) Agrega lo siguiente a tu `Info.plist`: ```xml NSContactsUsageDescription Esta aplicación necesita acceso a los contactos para permitirte seleccionar destinatarios ``` ## Configuración de Android [Section titled “Configuración de Android”](#configuración-de-android) Agrega los siguientes permisos a tu `AndroidManifest.xml`: ```xml ``` ## Uso Básico [Section titled “Uso Básico”](#uso-básico) ### Importar el Plugin [Section titled “Importar el Plugin”](#importar-el-plugin) ```typescript import { Contacts } from '@capgo/capacitor-contacts'; ``` ### Solicitar Permisos [Section titled “Solicitar Permisos”](#solicitar-permisos) ```typescript const requestPermissions = async () => { const permission = await Contacts.requestPermissions(); console.log('Estado del permiso:', permission.contacts); }; ``` ### Obtener Todos los Contactos [Section titled “Obtener Todos los Contactos”](#obtener-todos-los-contactos) ```typescript const getAllContacts = async () => { const result = await Contacts.getContacts({ projection: { name: true, phones: true, emails: true, image: true } }); console.log('Contactos:', result.contacts); }; ``` ### Buscar Contactos [Section titled “Buscar Contactos”](#buscar-contactos) ```typescript const searchContacts = async (query: string) => { const result = await Contacts.getContacts({ projection: { name: true, phones: true, emails: true }, query: query }); console.log('Resultados de búsqueda:', result.contacts); }; ``` ### Crear Contacto [Section titled “Crear Contacto”](#crear-contacto) ```typescript const createContact = async () => { const newContact = { name: { given: 'John', family: 'Doe' }, phones: [{ type: 'mobile', number: '+1234567890' }], emails: [{ type: 'work', address: 'john.doe@example.com' }] }; const result = await Contacts.createContact(newContact); console.log('Contacto creado:', result); }; ``` ### Actualizar Contacto [Section titled “Actualizar Contacto”](#actualizar-contacto) ```typescript const updateContact = async (contactId: string) => { const updates = { contactId: contactId, name: { given: 'Jane', family: 'Doe' } }; await Contacts.updateContact(updates); console.log('Contacto actualizado'); }; ``` ### Eliminar Contacto [Section titled “Eliminar Contacto”](#eliminar-contacto) ```typescript const deleteContact = async (contactId: string) => { await Contacts.deleteContact({ contactId }); console.log('Contacto eliminado'); }; ``` ## Ejemplo Completo [Section titled “Ejemplo Completo”](#ejemplo-completo) Aquí hay un ejemplo completo con un servicio de contactos: ```typescript import { Contacts } from '@capgo/capacitor-contacts'; interface Contact { contactId: string; name: { display?: string; given?: string; family?: string; }; phones?: Array<{ type: string; number: string }>; emails?: Array<{ type: string; address: string }>; image?: { base64String: string }; } class ContactsService { async checkPermissions(): Promise { const permission = await Contacts.checkPermissions(); if (permission.contacts === 'granted') { return true; } const requested = await Contacts.requestPermissions(); return requested.contacts === 'granted'; } async getAllContacts(): Promise { const hasPermission = await this.checkPermissions(); if (!hasPermission) { throw new Error('Permiso de contactos denegado'); } const result = await Contacts.getContacts({ projection: { name: true, phones: true, emails: true, image: true } }); return result.contacts; } async searchContacts(query: string): Promise { if (!query || query.length < 2) { return []; } const result = await Contacts.getContacts({ projection: { name: true, phones: true, emails: true }, query: query }); return result.contacts; } async getContactById(contactId: string): Promise { const result = await Contacts.getContacts({ projection: { name: true, phones: true, emails: true, image: true, organization: true, birthday: true, note: true, urls: true, postalAddresses: true }, contactId: contactId }); return result.contacts.length > 0 ? result.contacts[0] : null; } async createContact(contact: Partial): Promise { const hasPermission = await this.checkPermissions(); if (!hasPermission) { throw new Error('Permiso de contactos denegado'); } const result = await Contacts.createContact(contact); return result.contactId; } async updateContact(contactId: string, updates: Partial): Promise { await Contacts.updateContact({ contactId, ...updates }); } async deleteContact(contactId: string): Promise { await Contacts.deleteContact({ contactId }); } formatPhoneNumber(phone: string): string { // Eliminar caracteres no numéricos const cleaned = phone.replace(/\D/g, ''); // Formatear como (XXX) XXX-XXXX if (cleaned.length === 10) { return `(${cleaned.slice(0, 3)}) ${cleaned.slice(3, 6)}-${cleaned.slice(6)}`; } return phone; } getContactInitials(contact: Contact): string { const given = contact.name?.given || ''; const family = contact.name?.family || ''; if (given && family) { return `${given[0]}${family[0]}`.toUpperCase(); } const display = contact.name?.display || ''; const parts = display.split(' '); if (parts.length >= 2) { return `${parts[0][0]}${parts[1][0]}`.toUpperCase(); } return display.slice(0, 2).toUpperCase(); } } // Uso const contactsService = new ContactsService(); // Obtener todos los contactos const contacts = await contactsService.getAllContacts(); console.log('Contactos:', contacts); // Buscar contactos const results = await contactsService.searchContacts('john'); console.log('Resultados de búsqueda:', results); // Crear contacto const newContactId = await contactsService.createContact({ name: { given: 'Jane', family: 'Smith' }, phones: [{ type: 'mobile', number: '+1234567890' }] }); // Actualizar contacto await contactsService.updateContact(newContactId, { emails: [{ type: 'work', address: 'jane@example.com' }] }); ``` ## Entendiendo la Proyección [Section titled “Entendiendo la Proyección”](#entendiendo-la-proyección) El parámetro `projection` controla qué campos recuperar: ```typescript const result = await Contacts.getContacts({ projection: { name: true, // Nombre del contacto phones: true, // Números de teléfono emails: true, // Direcciones de correo electrónico image: true, // Foto del contacto organization: true, // Empresa/organización birthday: true, // Fecha de nacimiento note: true, // Notas urls: true, // Sitios web postalAddresses: true // Direcciones físicas } }); ``` **Consejo**: Solo solicita los campos que necesitas para mejorar el rendimiento. ## Selector de Contactos UI [Section titled “Selector de Contactos UI”](#selector-de-contactos-ui) Muchas aplicaciones prefieren usar el selector de contactos nativo: ```typescript const pickContact = async () => { try { const result = await Contacts.pickContact({ projection: { name: true, phones: true, emails: true } }); if (result.contacts.length > 0) { const contact = result.contacts[0]; console.log('Contacto seleccionado:', contact); } } catch (error) { console.error('Selector de contactos cancelado o falló:', error); } }; ``` ## Mejores Prácticas [Section titled “Mejores Prácticas”](#mejores-prácticas) 1. **Solicita Permisos Mínimos**: Solo solicita permiso de contactos cuando sea necesario 2. **Usa Proyección**: Solo recupera los campos que realmente usas 3. **Maneja Denegaciones**: Proporciona alternativas cuando se denieguen los permisos 4. **Cachea Sabiamente**: Los contactos pueden cambiar, no guardes en caché por mucho tiempo 5. **Respeta la Privacidad**: Sé transparente sobre el uso de contactos 6. **Operaciones Asíncronas**: Todas las operaciones de contactos son asíncronas ## Problemas Comunes [Section titled “Problemas Comunes”](#problemas-comunes) ### Permiso Denegado [Section titled “Permiso Denegado”](#permiso-denegado) ```typescript const handlePermissionDenied = async () => { const permission = await Contacts.checkPermissions(); if (permission.contacts === 'denied') { // Mostrar diálogo explicando por qué se necesita el permiso showPermissionDialog(); // Dirigir al usuario a la configuración // En iOS: Configuración > [App] > Contactos // En Android: Configuración > Aplicaciones > [App] > Permisos } }; ``` ### Listas de Contactos Grandes [Section titled “Listas de Contactos Grandes”](#listas-de-contactos-grandes) ```typescript const loadContactsInBatches = async () => { // Obtener primero el conteo (ligero) const projection = { name: true }; // Proyección mínima // Implementar paginación si es necesario const allContacts = await Contacts.getContacts({ projection }); // Procesar en fragmentos const chunkSize = 100; for (let i = 0; i < allContacts.contacts.length; i += chunkSize) { const chunk = allContacts.contacts.slice(i, i + chunkSize); await processContactChunk(chunk); } }; ``` ## Próximos Pasos [Section titled “Próximos Pasos”](#próximos-pasos) * Explora la [Referencia de API](https://github.com/Cap-go/capacitor-contacts#api) para documentación completa de métodos * Revisa la [aplicación de ejemplo](https://github.com/Cap-go/capacitor-contacts/tree/main/example) para uso avanzado * Consulta el [tutorial](/plugins/capacitor-contacts) para ejemplos completos de implementación # @Capgo/Capacitor-crisp > Integra el SDK de chat nativo de Crisp en tu aplicación Capacitor para soporte al cliente en la aplicación y mensajería en tiempo real. Rendimiento nativo Integración SDK nativa para una experiencia de chat fluida 🚀 Mensajería en tiempo real Chat en vivo con clientes directamente en tu aplicación 💬 Funciones completas Soporte para datos de usuario, eventos y estilo personalizado ✨ Documentación completa Consulta la [Documentación](/docs/plugins/crisp/getting-started/) para dominar el Plugin en solo unos minutos. # Comenzando > Aprende cómo instalar y usar el Plugin SDK de chat Crisp para soporte al cliente en tu aplicación Capacitor. 1. **Instalar el paquete** * npm ```sh npm i @Capgo/Capacitor-crisp ``` * pnpm ```sh pnpm add @Capgo/Capacitor-crisp ``` * yarn ```sh yarn add @Capgo/Capacitor-crisp ``` * bun ```sh bun add @Capgo/Capacitor-crisp ``` 2. **Sincronizar con proyectos nativos** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` 3. **Obtener tu ID de sitio web de Crisp** * Regístrate en [crisp.chat](https://crisp.chat) * Crea un sitio web/proyecto * Encuentra tu ID de sitio web en Configuración > Instrucciones de configuración ## Uso básico [Section titled “Uso básico”](#uso-básico) Importa el Plugin y configúralo con tu ID de sitio web: ```typescript import { CapacitorCrisp } from '@capgo/capacitor-crisp'; // Configurar Crisp con tu ID de sitio web const configureCrisp = async () => { await CapacitorCrisp.configure({ websiteID: 'YOUR_WEBSITE_ID' }); }; // Abrir el chat const openChat = async () => { await CapacitorCrisp.openMessenger(); }; // Establecer información del usuario const setUserInfo = async () => { await CapacitorCrisp.setUser({ email: 'user@example.com', nickname: 'John Doe', phone: '+1234567890', avatar: 'https://example.com/avatar.jpg' }); }; // Restablecer sesión de usuario (cerrar sesión) const logout = async () => { await CapacitorCrisp.resetChatSession(); }; ``` ## Referencia de API [Section titled “Referencia de API”](#referencia-de-api) ### Configurar(Opciones) [Section titled “Configurar(Opciones)”](#configuraropciones) Configura Crisp con tu ID de sitio web y ajustes opcionales. ```typescript interface ConfigureOptions { websiteID: string; locale?: string; // ej., 'en', 'fr', 'es' tokenID?: string; // Para sesiones autenticadas } await CapacitorCrisp.configure({ websiteID: 'YOUR_WEBSITE_ID', locale: 'en' }); ``` ### openMessenger() [Section titled “openMessenger()”](#openmessenger) Abre la interfaz de chat de Crisp. ```typescript await CapacitorCrisp.openMessenger(); ``` ### setUser(Opciones) [Section titled “setUser(Opciones)”](#setuseropciones) Establece la información del usuario para la sesión de chat. ```typescript interface UserOptions { email?: string; nickname?: string; phone?: string; avatar?: string; } await CapacitorCrisp.setUser({ email: 'user@example.com', nickname: 'John Doe' }); ``` ### setUserCompany(Opciones) [Section titled “setUserCompany(Opciones)”](#setusercompanyopciones) Establece información de la empresa para usuarios empresariales. ```typescript interface CompanyOptions { name: string; url?: string; description?: string; employment?: { title?: string; role?: string; }; geolocation?: { city?: string; country?: string; }; } await CapacitorCrisp.setUserCompany({ name: 'Acme Corp', url: 'https://acme.com', employment: { title: 'CEO', role: 'Leadership' } }); ``` ### pushEvent(Opciones) [Section titled “pushEvent(Opciones)”](#pusheventopciones) Envía eventos personalizados para rastrear acciones del usuario. ```typescript interface EventOptions { name: string; color?: 'red' | 'orange' | 'yellow' | 'green' | 'blue' | 'purple' | 'pink' | 'brown' | 'grey' | 'black'; data?: { [key: string]: any }; } await CapacitorCrisp.pushEvent({ name: 'checkout_completed', color: 'green', data: { price: 99.99, currency: 'USD' } }); ``` ### setSessionSegment(segment) [Section titled “setSessionSegment(segment)”](#setsessionsegmentsegment) Establece un segmento para categorizar la sesión de chat. ```typescript await CapacitorCrisp.setSessionSegment('premium_customer'); ``` ### resetChatSession() [Section titled “resetChatSession()”](#resetchatsession) Restablece la sesión de chat actual (útil para cerrar sesión). ```typescript await CapacitorCrisp.resetChatSession(); ``` ## Funciones avanzadas [Section titled “Funciones avanzadas”](#funciones-avanzadas) ### Datos personalizados del usuario [Section titled “Datos personalizados del usuario”](#datos-personalizados-del-usuario) ```typescript // Establecer múltiples campos de datos personalizados await CapacitorCrisp.setSessionData({ key: 'plan', value: 'premium' }); await CapacitorCrisp.setSessionData({ key: 'signup_date', value: '2024-01-15' }); ``` ### Mensaje predeterminado [Section titled “Mensaje predeterminado”](#mensaje-predeterminado) Establece un mensaje prellenado en la entrada del chat: ```typescript await CapacitorCrisp.setMessageText( "Hola, necesito ayuda con mi pedido #12345" ); ``` ### Disponibilidad del chat [Section titled “Disponibilidad del chat”](#disponibilidad-del-chat) Controla cuándo el widget de chat está disponible: ```typescript // Ocultar chat temporalmente await CapacitorCrisp.setTokenID('user_token_12345'); ``` ## Ejemplo completo [Section titled “Ejemplo completo”](#ejemplo-completo) ```typescript import { CapacitorCrisp } from '@capgo/capacitor-crisp'; export class ChatService { async initialize() { // Configurar Crisp await CapacitorCrisp.configure({ websiteID: 'YOUR_WEBSITE_ID', locale: 'en' }); } async loginUser(user: any) { // Establecer información del usuario await CapacitorCrisp.setUser({ email: user.email, nickname: user.name, phone: user.phone, avatar: user.avatarUrl }); // Establecer datos personalizados await CapacitorCrisp.setSessionData({ key: 'user_id', value: user.id }); await CapacitorCrisp.setSessionData({ key: 'account_type', value: user.accountType }); // Establecer segmento if (user.isPremium) { await CapacitorCrisp.setSessionSegment('premium'); } // Rastrear evento de inicio de sesión await CapacitorCrisp.pushEvent({ name: 'user_login', color: 'blue', data: { method: 'email' } }); } async openSupport(context?: string) { if (context) { await CapacitorCrisp.setMessageText( `Necesito ayuda con: ${context}` ); } await CapacitorCrisp.openMessenger(); } async logout() { await CapacitorCrisp.resetChatSession(); } } ``` ## Estilo y personalización [Section titled “Estilo y personalización”](#estilo-y-personalización) Crisp se adapta automáticamente al tema de tu aplicación, pero puedes personalizarlo aún más a través del panel de Crisp: 1. Ve a tu panel de Crisp 2. Navega a Configuración > Configuración del sitio web > Configuración de Chatbox y Email 3. Personaliza colores, posición y comportamiento ## Mejores prácticas [Section titled “Mejores prácticas”](#mejores-prácticas) 1. **Inicializar temprano** Configura Crisp durante la inicialización de la aplicación para disponibilidad inmediata: ```typescript import { App } from '@capacitor/app'; App.addListener('appStateChange', async ({ isActive }) => { if (isActive) { await CapacitorCrisp.configure({ websiteID: 'YOUR_WEBSITE_ID' }); } }); ``` 2. **Establecer contexto del usuario** Siempre proporciona información del usuario cuando esté disponible para un mejor soporte: ```typescript if (user.isAuthenticated) { await CapacitorCrisp.setUser({ email: user.email, nickname: user.name }); } ``` 3. **Rastrear eventos importantes** Usa eventos para proporcionar contexto a los agentes de soporte: ```typescript await CapacitorCrisp.pushEvent({ name: 'error_occurred', color: 'red', data: { error: error.message, screen: 'checkout' } }); ``` 4. **Manejar el cierre de sesión correctamente** Siempre restablece la sesión cuando los usuarios cierran sesión: ```typescript async logout() { await CapacitorCrisp.resetChatSession(); // Tu otra lógica de cierre de sesión } ``` ## Notas de plataforma [Section titled “Notas de plataforma”](#notas-de-plataforma) ### iOS [Section titled “iOS”](#ios) * Requiere iOS 10.0+ * Usa SDK nativo de Crisp para iOS * Soporta notificaciones push (configurar en el panel de Crisp) ### Android [Section titled “Android”](#android) * Requiere Android 5.0 (API 21)+ * Usa SDK nativo de Crisp para Android * Cumple con Material Design ### Web [Section titled “Web”](#web) * Recurre al SDK JavaScript de Crisp * Paridad completa de funciones con plataformas nativas # @Capgo/Capacitor-data-storage-sqlite > Almacenamiento clave-valor rápido y confiable basado en SQLite para tus aplicaciones Capacitor con soporte de encriptación. Impulsado por SQLite Almacenamiento de datos rápido y confiable usando SQLite 💾 Soporte de encriptación Encriptación opcional para datos sensibles 🔐 Simplicidad clave-valor API simple de clave-valor con funciones potentes 🗂️ Documentación completa Consulta la [Documentación](/docs/plugins/data-storage-sqlite/getting-started/) para dominar el Plugin en solo unos minutos. # Comenzando > Aprende cómo instalar y configurar el Plugin Capacitor Data Storage SQLite para almacenamiento rápido de clave-valor con encriptación opcional. 1. **Instalar el paquete** * npm ```sh npm i @Capgo/Capacitor-data-storage-sqlite ``` * pnpm ```sh pnpm add @Capgo/Capacitor-data-storage-sqlite ``` * yarn ```sh yarn add @Capgo/Capacitor-data-storage-sqlite ``` * bun ```sh bun add @Capgo/Capacitor-data-storage-sqlite ``` 2. **Sincronizar con proyectos nativos** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` 3. **Configurar el Plugin** **Ejemplo de almacenamiento básico:** ```typescript import { CapacitorDataStorageSqlite } from '@capgo/capacitor-data-storage-sqlite'; // Abrir una base de datos de almacenamiento await CapacitorDataStorageSqlite.openStore({ database: 'myapp_storage' }); // Almacenar datos await CapacitorDataStorageSqlite.set({ key: 'user_preferences', value: JSON.stringify({ theme: 'dark' }) }); ``` **Ejemplo de almacenamiento encriptado:** ```typescript // Abrir almacenamiento encriptado await CapacitorDataStorageSqlite.openStore({ database: 'secure_storage', encrypted: true, mode: 'encryption' }); // Almacenar datos sensibles await CapacitorDataStorageSqlite.set({ key: 'api_token', value: 'secret_token_value' }); ``` * iOS No se requiere configuración adicional para iOS. * Android No se requiere configuración adicional para Android. 4. **Operaciones básicas** ```typescript import { CapacitorDataStorageSqlite } from '@capgo/capacitor-data-storage-sqlite'; // Establecer un valor await CapacitorDataStorageSqlite.set({ key: 'username', value: 'john_doe' }); // Obtener un valor const { value } = await CapacitorDataStorageSqlite.get({ key: 'username' }); console.log('Nombre de usuario:', value); // "john_doe" // Eliminar un valor await CapacitorDataStorageSqlite.remove({ key: 'username' }); // Limpiar todos los datos await CapacitorDataStorageSqlite.clear(); // Verificar si existe la clave const { result } = await CapacitorDataStorageSqlite.iskey({ key: 'username' }); console.log('La clave existe:', result); // true o false // Obtener todas las claves const { keys } = await CapacitorDataStorageSqlite.keys(); console.log('Todas las claves:', keys); // Obtener todos los valores const { values } = await CapacitorDataStorageSqlite.values(); console.log('Todos los valores:', values); ``` 5. **Uso avanzado** ```typescript import { CapacitorDataStorageSqlite } from '@capgo/capacitor-data-storage-sqlite'; export class StorageService { private dbName = 'app_storage'; private isEncrypted = false; async initialize(encrypted = false) { this.isEncrypted = encrypted; // Abrir almacenamiento con opciones await CapacitorDataStorageSqlite.openStore({ database: this.dbName, encrypted: encrypted, mode: encrypted ? 'encryption' : 'no-encryption', version: 1 }); } // Métodos de almacenamiento genéricos async setObject(key: string, data: T): Promise { const value = JSON.stringify(data); await CapacitorDataStorageSqlite.set({ key, value }); } async getObject(key: string): Promise { try { const { value } = await CapacitorDataStorageSqlite.get({ key }); return value ? JSON.parse(value) : null; } catch (error) { console.error('Error al obtener objeto:', error); return null; } } // Operaciones por lotes async setMultiple(items: Record): Promise { for (const [key, value] of Object.entries(items)) { await CapacitorDataStorageSqlite.set({ key, value: typeof value === 'string' ? value : JSON.stringify(value) }); } } async getMultiple(keys: string[]): Promise> { const results: Record = {}; for (const key of keys) { try { const { value } = await CapacitorDataStorageSqlite.get({ key }); results[key] = value; } catch (error) { results[key] = null; } } return results; } // Gestión de tablas async getTables(): Promise { const { tables } = await CapacitorDataStorageSqlite.tables(); return tables; } async deleteTable(table: string): Promise { await CapacitorDataStorageSqlite.deleteTable({ table }); } // Funcionalidad de importar/exportar async exportToJson(): Promise { const { keys } = await CapacitorDataStorageSqlite.keys(); const { values } = await CapacitorDataStorageSqlite.values(); return keys.map((key, index) => ({ key, value: values[index] })); } async importFromJson(data: Array<{ key: string; value: string }>): Promise { // Limpiar datos existentes await CapacitorDataStorageSqlite.clear(); // Importar nuevos datos for (const item of data) { await CapacitorDataStorageSqlite.set({ key: item.key, value: item.value }); } } // Filtrado y búsqueda async keysStartingWith(prefix: string): Promise { const { keys } = await CapacitorDataStorageSqlite.keys(); return keys.filter(key => key.startsWith(prefix)); } async filterByPrefix(prefix: string): Promise> { const { keys } = await CapacitorDataStorageSqlite.keys(); const { values } = await CapacitorDataStorageSqlite.values(); const filtered: Array<{ key: string; value: string }> = []; keys.forEach((key, index) => { if (key.startsWith(prefix)) { filtered.push({ key, value: values[index] }); } }); return filtered; } // Cerrar base de datos cuando hayas terminado async close(): Promise { await CapacitorDataStorageSqlite.closeStore({ database: this.dbName }); } } // Ejemplo de uso const storage = new StorageService(); await storage.initialize(true); // Usar encriptación // Almacenar datos de usuario await storage.setObject('user_profile', { id: 123, name: 'John Doe', email: 'john@example.com' }); // Recuperar datos de usuario const profile = await storage.getObject('user_profile'); console.log('Perfil de usuario:', profile); ``` ## Referencia de API [Section titled “Referencia de API”](#referencia-de-api) ### Métodos [Section titled “Métodos”](#métodos) #### `openStore(options: OpenStoreOptions)` [Section titled “openStore(options: OpenStoreOptions)”](#openstoreoptions-openstoreoptions) Abre una base de datos de almacenamiento. **Parámetros:** * `options.database`: string - Nombre de la base de datos * `options.encrypted`: boolean - Habilitar encriptación * `options.mode`: string - ‘Cifrado’ o ‘no-Cifrado’ * `options.version`: number - Versión de la base de datos #### `closeStore(options: CloseStoreOptions)` [Section titled “closeStore(options: CloseStoreOptions)”](#closestoreoptions-closestoreoptions) Cierra la base de datos de almacenamiento. #### `set(options: SetOptions)` [Section titled “set(options: SetOptions)”](#setoptions-setoptions) Almacena un par clave-valor. **Parámetros:** * `options.key`: string - Clave de almacenamiento * `options.value`: string - Valor a almacenar #### `get(options: GetOptions)` [Section titled “get(options: GetOptions)”](#getoptions-getoptions) Recupera un valor por clave. **Retorna:** `Promise<{ value: string }>` #### `remove(options: RemoveOptions)` [Section titled “remove(options: RemoveOptions)”](#removeoptions-removeoptions) Elimina un par clave-valor. #### `clear()` [Section titled “clear()”](#clear) Limpia todos los datos del almacenamiento. #### `iskey(options: IskeyOptions)` [Section titled “iskey(options: IskeyOptions)”](#iskeyoptions-iskeyoptions) Verifica si existe una clave. **Retorna:** `Promise<{ result: boolean }>` #### `keys()` [Section titled “keys()”](#keys) Obtiene todas las claves de almacenamiento. **Retorna:** `Promise<{ keys: string[] }>` #### `values()` [Section titled “values()”](#values) Obtiene todos los valores de almacenamiento. **Retorna:** `Promise<{ values: string[] }>` #### `tables()` [Section titled “tables()”](#tables) Obtiene todos los nombres de tablas. **Retorna:** `Promise<{ tables: string[] }>` #### `deleteTable(options: DeleteTableOptions)` [Section titled “deleteTable(options: DeleteTableOptions)”](#deletetableoptions-deletetableoptions) Elimina una tabla específica. ### Interfaces [Section titled “Interfaces”](#interfaces) ```typescript interface OpenStoreOptions { database: string; encrypted?: boolean; mode?: string; version?: number; } interface SetOptions { key: string; value: string; } interface GetOptions { key: string; } interface RemoveOptions { key: string; } ``` ## Notas de plataforma [Section titled “Notas de plataforma”](#notas-de-plataforma) ### iOS [Section titled “iOS”](#ios) * Usa SQLite3 con SQLCipher opcional para encriptación * Los datos persisten entre actualizaciones de la aplicación * Soporta iOS 11.0+ ### Android [Section titled “Android”](#android) * Usa SQLite con SQLCipher opcional * Los datos persisten entre actualizaciones de la aplicación * Soporta Android 5.0 (API 21)+ ## Casos de uso comunes [Section titled “Casos de uso comunes”](#casos-de-uso-comunes) 1. **Preferencias de usuario**: Almacenar configuraciones y preferencias de la aplicación 2. **Gestión de caché**: Cachear respuestas API y datos 3. **Almacenamiento sin conexión**: Almacenar datos para acceso sin conexión 4. **Gestión de sesiones**: Gestionar sesiones de usuario de forma segura 5. **Almacenamiento de tokens**: Almacenar tokens de autenticación de forma segura ## Mejores prácticas [Section titled “Mejores prácticas”](#mejores-prácticas) 1. **Usar encriptación para datos sensibles** ```typescript // Para datos sensibles como tokens await openStore({ database: 'secure_db', encrypted: true, mode: 'encryption' }); ``` 2. **Organizar claves con prefijos** ```typescript // Usar prefijos para organización await set({ key: 'user:123:profile', value: userData }); await set({ key: 'cache:api:users', value: apiData }); ``` 3. **Manejar datos grandes con cuidado** ```typescript // Para objetos grandes, considerar compresión const compressed = compress(largeData); await set({ key: 'large_data', value: compressed }); ``` 4. **Limpieza regular** ```typescript // Eliminar entradas de caché expiradas const keys = await keys(); for (const key of keys.keys) { if (key.startsWith('cache:') && isExpired(key)) { await remove({ key }); } } ``` ## Solución de problemas [Section titled “Solución de problemas”](#solución-de-problemas) **La base de datos no se abre:** * Verifica que el nombre de la base de datos sea válido (alfanumérico, guiones bajos) * Asegúrate de que no haya caracteres especiales en el nombre de la base de datos * Verifica que el modo de encriptación coincida con la base de datos existente **Los datos no persisten:** * Asegúrate de que `openStore` se llame antes de las operaciones * Verifica cualquier Error en la consola * Verifica que los nombres de claves sean cadenas **Problemas de rendimiento:** * Evita almacenar valores muy grandes * Usa operaciones por lotes cuando sea posible * Considera usar múltiples bases de datos para diferentes tipos de datos # @Capgo/Capacitor-document-scanner > Lanza una experiencia de escaneo de documentos pulida con recorte automático, captura multipágina y formatos de salida configurables. El Plugin Document Scanner brinda a tu aplicación Capacitor un flujo de escaneo con calidad nativa que funciona en dispositivos iOS y Android. Detección automática Detecta bordes de página, recorta automáticamente y permite a los usuarios ajustar antes de guardar. Captura multipágina Captura una o muchas páginas en una sola sesión de escaneo con límites ajustables. Salida personalizada Devuelve rutas de archivo o cadenas base64 con la calidad de imagen que elijas. API unificada Una interfaz TypeScript abarca las implementaciones de iOS y Android. Usa la guía de inicio para habilitar permisos de cámara, configurar ajustes de calidad y exportar archivos escaneados a tu flujo de trabajo de almacenamiento. # Comenzando > Agrega escaneo de documentos con calidad nativa con detección de bordes y formatos de salida ajustables. 1. **Instalar el Plugin** * npm ```sh npm i @Capgo/Capacitor-document-scanner ``` * pnpm ```sh pnpm add @Capgo/Capacitor-document-scanner ``` * yarn ```sh yarn add @Capgo/Capacitor-document-scanner ``` * bun ```sh bun add @Capgo/Capacitor-document-scanner ``` 2. **Sincronizar plataformas nativas** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Iniciar un escaneo [Section titled “Iniciar un escaneo”](#iniciar-un-escaneo) ```typescript import { DocumentScanner, ResponseType, ScanDocumentResponseStatus, } from '@capgo/capacitor-document-scanner'; const result = await DocumentScanner.scanDocument({ croppedImageQuality: 90, letUserAdjustCrop: true, maxNumDocuments: 10, responseType: ResponseType.ImageFilePath, }); if (result.status === ScanDocumentResponseStatus.Success) { console.log('Archivos escaneados:', result.scannedImages); } else { console.log('Escaneo cancelado por el usuario'); } ``` ## Devolver datos base64 [Section titled “Devolver datos base64”](#devolver-datos-base64) ```typescript const result = await DocumentScanner.scanDocument({ responseType: ResponseType.Base64, }); const [firstPage] = result.scannedImages ?? []; if (firstPage) { const dataUrl = `data:image/jpeg;base64,${firstPage}`; // Mostrar vista previa o cargar al servidor } ``` ## Requisitos de plataforma [Section titled “Requisitos de plataforma”](#requisitos-de-plataforma) * **iOS**: Agrega `NSCameraUsageDescription` a `ios/App/App/Info.plist` explicando cómo usas la cámara. * **Android**: Confirma que el permiso `CAMERA` esté declarado (Capacitor lo agrega automáticamente) y ajusta `croppedImageQuality`, `letUserAdjustCrop` y `maxNumDocuments` para que coincidan con tu interfaz de usuario. * **Almacenamiento**: Cuando uses `ResponseType.ImageFilePath`, mueve o copia los archivos escaneados a tu ubicación de almacenamiento deseada antes de que finalice la sesión de la aplicación. # @Capgo/Capacitor-downloader > Descarga archivos con soporte en segundo plano, seguimiento de progreso, capacidades de pausar/reanudar y gestión eficiente de descargas. ## Resumen [Section titled “Resumen”](#resumen) El Plugin Capacitor Downloader proporciona potentes capacidades de descarga de archivos con soporte para descargas en segundo plano, seguimiento de progreso y gestión integral de descargas. Este Plugin permite descargas de archivos robustas con funcionalidad de pausar/reanudar y monitoreo detallado del progreso. > ⚠️ **Trabajo en progreso**: Este Plugin está actualmente en desarrollo y aún no está listo para uso en producción. Descargas en segundo plano Continúa las descargas cuando la aplicación está en segundo plano 📥 Seguimiento de progreso Monitoreo en tiempo real del progreso y estado de descarga 📊 Control de descarga Pausa, reanuda y detiene descargas dinámicamente ⏯️ Opciones de red Configura preferencias de red y prioridades 📶 ## Estado de desarrollo [Section titled “Estado de desarrollo”](#estado-de-desarrollo) Este Plugin está inspirado en React-native-background-downloader y actualmente está en desarrollo. Las funciones pueden cambiar a medida que avanza el desarrollo. # Comenzando > Guía de instalación y uso para @Capgo/Capacitor-downloader ## Instalación [Section titled “Instalación”](#instalación) * npm ```bash npm install @capgo/capacitor-downloader npx cap sync ``` * yarn ```bash yarn add @capgo/capacitor-downloader npx cap sync ``` * pnpm ```bash pnpm add @capgo/capacitor-downloader npx cap sync ``` * bun ```bash bun add @capgo/capacitor-downloader npx cap sync ``` ## Ejemplo de uso [Section titled “Ejemplo de uso”](#ejemplo-de-uso) ```typescript import { CapacitorDownloader } from '@capgo/capacitor-downloader'; // Iniciar una descarga const downloadId = 'my-download-001'; await CapacitorDownloader.download({ id: downloadId, url: 'https://example.com/large-file.zip', destination: '/downloads/large-file.zip', headers: { 'Authorization': 'Bearer token123' }, network: 'wifi-only', priority: 'high' }); // Escuchar actualizaciones de progreso CapacitorDownloader.addListener('downloadProgress', (data) => { console.log(`Descarga ${data.id}: ${data.progress}% completada`); console.log(`Descargado: ${data.bytesDownloaded}/${data.totalBytes} bytes`); }); // Manejar la finalización CapacitorDownloader.addListener('downloadCompleted', (data) => { console.log(`Descarga completada: ${data.id}`); console.log(`Archivo guardado en: ${data.path}`); }); // Manejar errores CapacitorDownloader.addListener('downloadFailed', (error) => { console.error(`Descarga fallida: ${error.id}`, error.message); }); // Pausar una descarga await CapacitorDownloader.pause(downloadId); // Reanudar la descarga await CapacitorDownloader.resume(downloadId); // Verificar estado de descarga const status = await CapacitorDownloader.checkStatus(downloadId); console.log('Estado de descarga:', status); // Detener la descarga await CapacitorDownloader.stop(downloadId); ``` ## Métodos principales de API [Section titled “Métodos principales de API”](#métodos-principales-de-api) ### Gestión de descargas [Section titled “Gestión de descargas”](#gestión-de-descargas) * `download(options)` - Iniciar una nueva descarga de archivo * `pause(id)` - Pausar una descarga en curso * `resume(id)` - Reanudar una descarga pausada * `stop(id)` - Detener y cancelar una descarga * `checkStatus(id)` - Obtener el estado actual de descarga ### Operaciones de archivos [Section titled “Operaciones de archivos”](#operaciones-de-archivos) * `getFileInfo(path)` - Recuperar información y metadatos del archivo ## Configuración de descarga [Section titled “Configuración de descarga”](#configuración-de-descarga) ```typescript interface DownloadOptions { id: string; // Identificador único de descarga url: string; // URL de origen de descarga destination: string; // Ruta de guardado local headers?: Record; // Encabezados HTTP personalizados network?: 'cellular' | 'wifi-only'; // Restricciones de red priority?: 'high' | 'normal' | 'low'; // Prioridad de descarga } ``` ## Escuchadores de eventos [Section titled “Escuchadores de eventos”](#escuchadores-de-eventos) El Plugin proporciona manejo integral de eventos: * `downloadProgress` - Rastrear el progreso de descarga en tiempo real * `downloadCompleted` - Manejar la finalización exitosa de descarga * `downloadFailed` - Manejar errores y fallos de descarga ## Configuración de red [Section titled “Configuración de red”](#configuración-de-red) ### Descargas solo por WiFi [Section titled “Descargas solo por WiFi”](#descargas-solo-por-wifi) ```typescript await CapacitorDownloader.download({ id: 'large-file', url: 'https://example.com/video.mp4', destination: '/downloads/video.mp4', network: 'wifi-only' // Restringe solo a redes WiFi }); ``` ### Gestión de prioridades [Section titled “Gestión de prioridades”](#gestión-de-prioridades) ```typescript // Descarga de alta prioridad await CapacitorDownloader.download({ id: 'urgent-update', url: 'https://example.com/update.zip', destination: '/downloads/update.zip', priority: 'high' }); ``` ## Estados de descarga [Section titled “Estados de descarga”](#estados-de-descarga) Las descargas pueden estar en varios estados: * **Pendiente**: En cola para descarga * **Ejecutando**: Descargando actualmente * **Paused**: Detenida temporalmente * **Completado**: Finalizada exitosamente * **Falló**: Encontró un Error * **Stopped**: Cancelada manualmente ## Información del archivo [Section titled “Información del archivo”](#información-del-archivo) ```typescript // Obtener detalles del archivo const fileInfo = await CapacitorDownloader.getFileInfo('/downloads/my-file.pdf'); console.log('Tamaño del archivo:', fileInfo.size); console.log('Última modificación:', fileInfo.lastModified); console.log('Tipo MIME:', fileInfo.mimeType); ``` ## Mejores prácticas [Section titled “Mejores prácticas”](#mejores-prácticas) * Usa IDs de descarga únicos para evitar conflictos * Implementa manejo de errores apropiado para fallos de red * Considera el espacio de almacenamiento antes de iniciar descargas grandes * Usa el modo solo WiFi para archivos grandes para preservar datos móviles * Limpia las descargas completadas para gestionar el almacenamiento # @Capgo/Capacitor-env > Entrega configuraciones específicas por inquilino sin nuevas compilaciones leyendo valores de configuración seguros directamente desde Capacitor. Capgo Env te permite almacenar secretos y configuración por compilación dentro de `capacitor.config.*` y resolverlos en tiempo de ejecución a través de una API simple. Configuración en tiempo de ejecución Lee valores de archivos de configuración nativos sin enviarlos en tu paquete JavaScript. Anulaciones por compilación Crea múltiples variantes de aplicación o inquilinos proporcionando diferentes archivos de configuración. Acceso con seguridad de tipos Obtén claves con soporte TypeScript y centraliza la lógica de valores predeterminados. Seguro por defecto Mantén las claves API y secretos fuera de Git mientras los envías con binarios nativos. Sigue la guía de inicio para declarar claves en tu configuración de Capacitor y obtenerlas de forma segura en todas las plataformas. # Comenzando > Configura valores de entorno por compilación y obtenlos en tiempo de ejecución con el Plugin Env. 1. **Instalar el paquete** * npm ```sh npm i @Capgo/Capacitor-env ``` * pnpm ```sh pnpm add @Capgo/Capacitor-env ``` * yarn ```sh yarn add @Capgo/Capacitor-env ``` * bun ```sh bun add @Capgo/Capacitor-env ``` 2. **Sincronizar plataformas nativas** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Declarar valores de configuración [Section titled “Declarar valores de configuración”](#declarar-valores-de-configuración) Agrega claves a la configuración de Capacitor para que se incorporen en tus compilaciones nativas. Puedes crear múltiples variantes de configuración (`capacitor.config.prod.ts`, `capacitor.config.dev.ts`, etc.) para intercambiar valores por entorno. capacitor.config.ts ```ts import { CapacitorConfig } from '@capacitor/cli'; const config: CapacitorConfig = { appId: 'com.example.app', appName: 'Example', webDir: 'dist', plugins: { Env: { API_URL: 'https://api.example.com', PUBLIC_KEY: 'pk_live_123', }, }, }; export default config; ``` En plataformas nativas, los valores se almacenan dentro de los archivos de configuración generados (`ios/App/App/capacitor.config.json` y `android/app/src/main/assets/capacitor.config.json`). Actualiza esos archivos por variante si necesitas valores específicos por inquilino. ## Leer valores en el código [Section titled “Leer valores en el código”](#leer-valores-en-el-código) ```typescript import { Env } from '@capgo/capacitor-env'; const apiUrl = await Env.getKey({ key: 'API_URL' }).then((result) => result.value); if (!apiUrl) { throw new Error('Falta configuración de API_URL'); } ``` ## Proporcionar valores predeterminados [Section titled “Proporcionar valores predeterminados”](#proporcionar-valores-predeterminados) ```typescript const loadConfig = async () => { const { value: endpoint } = await Env.getKey({ key: 'API_URL' }); return endpoint || 'https://staging.example.com'; }; ``` ## Consejos [Section titled “Consejos”](#consejos) * Usa diferentes archivos `capacitor.config` por entorno y apunta el CLI al correcto con `npx cap run ios --configuration=prod`. * Combina con canales del actualizador Capgo para enviar valores específicos por inquilino sin publicar nuevos binarios. * Mantén los secretos fuera del control de código fuente sustituyéndolos durante tu compilación CI antes de `npx cap sync`. # @Capgo/Capacitor-fast-SQL > SQLite nativo ultrarrápido con protocolo personalizado para sistemas de sincronización y grandes conjuntos de datos, ofreciendo hasta 25 veces más rendimiento. Protocolo HTTP personalizado Evita el puente de Capacitor para hasta 25 veces más rendimiento ⚡ Soporte completo de SQLite Soporte SQL completo con transacciones y operaciones por lotes 📊 Compatible con sincronización Diseñado para sistemas de sincronización local como CRDTs y transformaciones operacionales 🔄 Multiplataforma iOS, Android y Web usando SQL.js + IndexedDB 🌐 Soporte de transacciones Transacciones ACID con niveles de aislamiento configurables 🔒 Datos binarios Soporte nativo para almacenamiento Uint8Array/BLOB 📁 Comenzando Consulta la [Guía de inicio](/docs/plugins/fast-sql/getting-started/) para instalar y configurar el Plugin. # Comenzando con SQL rápido > Instale y configure el complemento **@capgo/capacitor-fast-sql** para acceder a la base de datos SQLite nativa de alto rendimiento 1. **Instalar el paquete** * npm ```sh npm i @capgo/capacitor-fast-sql ``` * pnpm ```sh pnpm add @capgo/capacitor-fast-sql ``` * yarn ```sh yarn add @capgo/capacitor-fast-sql ``` * bun ```sh bun add @capgo/capacitor-fast-sql ``` 2. **Sincronización con proyectos nativos** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` 3. **Configurar plataformas** ### iOS [Section titled “iOS”](#ios) Permitir el acceso a la red local en `Info.plist`: ios/App/App/Info.plist ```xml NSAppTransportSecurity NSAllowsLocalNetworking ``` ### Android [Section titled “Android”](#android) Agregue una excepción de texto sin cifrar para el tráfico de host local: android/app/src/main/AndroidManifest.xml ```xml ... ``` ### Web [Section titled “Web”](#web) Instale sql.js como respaldo de almacenamiento web: ```bash npm install sql.js ``` ## ¿Por qué SQL rápido? [Section titled “¿Por qué SQL rápido?”](#por-qué-sql-rápido) Fast SQL avoids heavy bridge serialization by using a local HTTP transport to native SQLite, which is much faster for large result sets and sync-style writes. ## Uso básico [Section titled “Uso básico”](#uso-básico) ```typescript import { FastSQL, IsolationLevel, KeyValueStore } from '@capgo/capacitor-fast-sql'; const db = await FastSQL.connect({ database: 'myapp', encrypted: false, readOnly: false, }); await db.execute(` CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, email TEXT UNIQUE, created_at INTEGER DEFAULT (strftime('%s', 'now')) ) `); await db.run('INSERT INTO users (name, email) VALUES (?, ?)', ['John', 'john@example.com']); const users = await db.query('SELECT * FROM users'); ``` ## Métodos de conexión principales [Section titled “Métodos de conexión principales”](#métodos-de-conexión-principales) * `execute(statement, params?)` -> devuelve el resultado SQL completo * `query(statement, params?)` -> devuelve `rows` * `run(statement, params?)` -> devuelve `{ rowsAffected, insertId }` * `executeBatch(operations)` -> devuelve una serie de resultados ```typescript import { type SQLBatchOperation } from '@capgo/capacitor-fast-sql'; const rows = await db.query('SELECT * FROM users WHERE email LIKE ?', ['john%']); const batch: SQLBatchOperation[] = [ { statement: 'INSERT INTO users (name, email) VALUES (?, ?)', params: ['Alice', 'alice@acme.dev'] }, { statement: 'INSERT INTO users (name, email) VALUES (?, ?)', params: ['Bob', 'bob@acme.dev'] }, ]; await db.executeBatch(batch); ``` ## Transacciones [Section titled “Transacciones”](#transacciones) ### ayudante de devolución de llamada [Section titled “ayudante de devolución de llamada”](#ayudante-de-devolución-de-llamada) ```typescript await db.transaction(async (tx) => { await tx.run('UPDATE accounts SET balance = balance - 100 WHERE id = ?', [1]); await tx.run('UPDATE accounts SET balance = balance + 100 WHERE id = ?', [2]); }); ``` ### control explícito [Section titled “control explícito”](#control-explícito) ```typescript import { IsolationLevel } from '@capgo/capacitor-fast-sql'; await db.beginTransaction(IsolationLevel.Serializable); try { await db.run('UPDATE wallets SET points = points - 1 WHERE user_id = ?', [42]); await db.commit(); } catch (error) { await db.rollback(); throw error; } ``` Niveles de aislamiento admitidos: * `ReadUncommitted` * `ReadCommitted` * `RepeatableRead` * `Serializable` ## Datos binarios (BLOB) [Section titled “Datos binarios (BLOB)”](#datos-binarios-blob) ```typescript const imageData = new Uint8Array([0xFF, 0xD8, 0xFF, 0xE0]); await db.run('INSERT INTO assets (name, data) VALUES (?, ?)', ['avatar', imageData]); const rows = await db.query('SELECT data FROM assets WHERE name = ?', ['avatar']); const retrieved = rows[0].data; // Uint8Array ``` ## Cifrado y modos de solo lectura [Section titled “Cifrado y modos de solo lectura”](#cifrado-y-modos-de-solo-lectura) ```typescript const secureDb = await FastSQL.connect({ database: 'secure_db', encrypted: true, encryptionKey: 'replace-with-a-strong-key', }); const readOnlyDb = await FastSQL.connect({ database: 'public_db', readOnly: true, }); ``` En Android, el modo cifrado utiliza SQLCipher; incluir dependencia en la aplicación `build.gradle`. ## Almacén de valores clave [Section titled “Almacén de valores clave”](#almacén-de-valores-clave) `KeyValueStore` es un contenedor conveniente para datos de clave/valor móviles. ```typescript import { KeyValueStore } from '@capgo/capacitor-fast-sql'; const kv = await KeyValueStore.open({ database: 'kv', store: 'session', encrypted: true, encryptionKey: 'super-secret-key', }); await kv.set('session', { token: 'abc', expiresAt: Date.now() + 3600_000 }); const session = await kv.get('session'); await kv.has('session'); await kv.keys(); await kv.remove('session'); await kv.clear(); await kv.close(); ``` ## Ciclo de vida de la conexión [Section titled “Ciclo de vida de la conexión”](#ciclo-de-vida-de-la-conexión) ```typescript await FastSQL.disconnect('myapp'); await FastSQL.disconnectAll(); const openDatabases = FastSQL.getOpenDatabases(); const same = FastSQL.getConnection('myapp'); ``` ## Manejo de errores [Section titled “Manejo de errores”](#manejo-de-errores) ```typescript try { await FastSQL.connect({ database: 'myapp' }); await db.query('SELECT * FROM unknown_table'); } catch (error) { console.error('Fast SQL error:', error); } ``` ## Patrones SQL comunes [Section titled “Patrones SQL comunes”](#patrones-sql-comunes) ### Comprobar si existe la tabla [Section titled “Comprobar si existe la tabla”](#comprobar-si-existe-la-tabla) ```typescript const result = await db.query( "SELECT name FROM sqlite_master WHERE type='table' AND name=?", ['users'] ); const tableExists = result.length > 0; ``` ### Obtener esquema de tabla [Section titled “Obtener esquema de tabla”](#obtener-esquema-de-tabla) ```typescript const schema = await db.query('PRAGMA table_info(users)'); console.log('Columns:', schema); ``` ### Contar filas [Section titled “Contar filas”](#contar-filas) ```typescript const result = await db.query('SELECT COUNT(*) as count FROM users'); const count = result[0].count; ``` ### Paginación [Section titled “Paginación”](#paginación) ```typescript const pageSize = 20; const page = 1; const offset = (page - 1) * pageSize; const users = await db.query( 'SELECT * FROM users ORDER BY created_at DESC LIMIT ? OFFSET ?', [pageSize, offset] ); ``` ## Consejos de rendimiento [Section titled “Consejos de rendimiento”](#consejos-de-rendimiento) 1. **Utilice Transacciones** para operaciones múltiples: significativamente más rápido que las confirmaciones individuales 2. **Utilice operaciones por lotes** para inserciones masivas: más eficientes que los bucles 3. **Crear índices** en columnas consultadas con frecuencia 4. **Utilice declaraciones preparadas** con parámetros (?): evita la inyección de SQL y mejora el rendimiento 5. **Utilice el protocolo HTTP directamente** para conjuntos de resultados muy grandes 6. **Cerrar conexiones** cuando no esté en uso para liberar recursos ## Próximos pasos [Section titled “Próximos pasos”](#próximos-pasos) Consulte el [tutorial completo](/plugins/capacitor-fast-sql) para conocer patrones avanzados que incluyen: * Arquitectura del servicio de base de datos. * Sistemas de migración * Sincronizar motores * Consultas complejas y uniones. * Optimización del rendimiento ## Documentación relacionada [Section titled “Documentación relacionada”](#documentación-relacionada) * [Migrar desde Ionic Secure Storage](/docs/upgrade/from-ionic-secure-storage/) * [Solución de migración de complementos empresariales de Ionic](/solutions/ionic-enterprise-plugins/) # @Capgo/Capacitor-ffmpeg > Re-codifica videos con resolución y bitrate personalizados utilizando la poderosa biblioteca FFmpeg en tus aplicaciones Capacitor. Re-codificación de Video Convierte y comprime videos con FFmpeg Resolución Personalizada Redimensiona videos a cualquier ancho y alto Control de Bitrate Controla la calidad del video y el tamaño del archivo Documentación Completa Consulta la [Documentación](/docs/plugins/ffmpeg/getting-started/) para comenzar a procesar videos. # Empezando > Aprenda a instalar y utilizar el complemento FFmpeg para procesar videos en su aplicación Capacitor. 1. **Instalar el paquete** * npm ```sh npm i @capgo/capacitor-ffmpeg ``` * pnpm ```sh pnpm add @capgo/capacitor-ffmpeg ``` * yarn ```sh yarn add @capgo/capacitor-ffmpeg ``` * bun ```sh bun add @capgo/capacitor-ffmpeg ``` 2. **Sincronización con proyectos nativos** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Uso [Section titled “Uso”](#uso) Importe el complemento y utilícelo para volver a codificar videos: ```typescript import { CapacitorFFmpeg } from '@capgo/capacitor-ffmpeg'; // Re-encode a video with custom settings const processVideo = async () => { await CapacitorFFmpeg.reencodeVideo({ inputPath: '/path/to/input/video.mp4', outputPath: '/path/to/output/video.mp4', width: 1280, height: 720, bitrate: 2000000 // Optional: 2 Mbps }); }; // Get plugin version const checkVersion = async () => { const { version } = await CapacitorFFmpeg.getPluginVersion(); console.log('FFmpeg plugin version:', version); }; ``` ## API Referencia [Section titled “API Referencia”](#api-referencia) ### recodificarVideo(opciones) [Section titled “recodificarVideo(opciones)”](#recodificarvideoopciones) Vuelva a codificar un archivo de vídeo con dimensiones y tasa de bits específicas. ```typescript await CapacitorFFmpeg.reencodeVideo({ inputPath: '/path/to/input.mp4', outputPath: '/path/to/output.mp4', width: 1920, height: 1080, bitrate: 5000000 // Optional: 5 Mbps }); ``` **Parámetros:** * `inputPath` (cadena): ruta completa al archivo de vídeo de entrada * `outputPath` (cadena): Ruta completa donde se guardará el vídeo de salida * `width` (número): Ancho objetivo en píxeles * `height` (número): altura del objetivo en píxeles * `bitrate` (número, opcional): tasa de bits objetivo en bits por segundo ### obtenerPluginVersion() [Section titled “obtenerPluginVersion()”](#obtenerpluginversion) Obtenga la versión nativa del complemento Capacitor. ```typescript const { version } = await CapacitorFFmpeg.getPluginVersion(); ``` ## Ejemplo completo [Section titled “Ejemplo completo”](#ejemplo-completo) ```typescript import { CapacitorFFmpeg } from '@capgo/capacitor-ffmpeg'; import { Filesystem, Directory } from '@capacitor/filesystem'; export class VideoProcessor { /** * Compress a video to reduce file size */ async compressVideo(inputPath: string, quality: 'low' | 'medium' | 'high') { const qualitySettings = { low: { width: 640, height: 360, bitrate: 500000 }, medium: { width: 1280, height: 720, bitrate: 2000000 }, high: { width: 1920, height: 1080, bitrate: 5000000 } }; const settings = qualitySettings[quality]; const outputPath = inputPath.replace('.mp4', `_${quality}.mp4`); try { await CapacitorFFmpeg.reencodeVideo({ inputPath, outputPath, width: settings.width, height: settings.height, bitrate: settings.bitrate }); console.log(`Video compressed to ${quality} quality:`, outputPath); return outputPath; } catch (error) { console.error('Video compression failed:', error); throw error; } } /** * Resize video to specific dimensions */ async resizeVideo( inputPath: string, width: number, height: number ): Promise { const outputPath = inputPath.replace('.mp4', '_resized.mp4'); await CapacitorFFmpeg.reencodeVideo({ inputPath, outputPath, width, height }); return outputPath; } /** * Create a thumbnail-quality version of a video */ async createThumbnailVideo(inputPath: string): Promise { return this.compressVideo(inputPath, 'low'); } /** * Batch process multiple videos */ async processMultipleVideos( videoPaths: string[], width: number, height: number, bitrate?: number ): Promise { const outputPaths: string[] = []; for (const inputPath of videoPaths) { const outputPath = inputPath.replace('.mp4', '_processed.mp4'); try { await CapacitorFFmpeg.reencodeVideo({ inputPath, outputPath, width, height, bitrate }); outputPaths.push(outputPath); } catch (error) { console.error(`Failed to process ${inputPath}:`, error); } } return outputPaths; } } ``` ## Mejores prácticas [Section titled “Mejores prácticas”](#mejores-prácticas) 1. **Utilice velocidades de bits apropiadas** Elija la tasa de bits según la resolución y el caso de uso: ```typescript // Mobile sharing (low bandwidth) const lowQuality = { width: 640, height: 360, bitrate: 500000 }; // Standard quality const standardQuality = { width: 1280, height: 720, bitrate: 2000000 }; // High quality const highQuality = { width: 1920, height: 1080, bitrate: 5000000 }; ``` 2. **Mantener la relación de aspecto** Calcule las dimensiones para preservar la relación de aspecto: ```typescript function calculateDimensions(originalWidth: number, originalHeight: number, targetWidth: number) { const aspectRatio = originalWidth / originalHeight; return { width: targetWidth, height: Math.round(targetWidth / aspectRatio) }; } ``` 3. **Maneje las rutas de los archivos correctamente** Utilice el sistema de archivos Capacitor para el manejo de rutas multiplataforma: ```typescript import { Filesystem, Directory } from '@capacitor/filesystem'; const inputPath = await Filesystem.getUri({ directory: Directory.Documents, path: 'input.mp4' }); ``` 4. **Mostrar el progreso a los usuarios** El procesamiento de vídeo puede ser lento; informe a los usuarios: ```typescript async function processWithProgress(inputPath: string) { // Show loading indicator showLoading('Processing video...'); try { await CapacitorFFmpeg.reencodeVideo({ inputPath, outputPath: '/path/to/output.mp4', width: 1280, height: 720 }); showSuccess('Video processed successfully!'); } catch (error) { showError('Failed to process video'); } finally { hideLoading(); } } ``` 5. **Limpiar archivos temporales** Elimine archivos intermedios para ahorrar almacenamiento: ```typescript async function processAndCleanup(inputPath: string) { const outputPath = inputPath.replace('.mp4', '_output.mp4'); await CapacitorFFmpeg.reencodeVideo({ inputPath, outputPath, width: 1280, height: 720 }); // Remove original if no longer needed await Filesystem.deleteFile({ path: inputPath }); return outputPath; } ``` ## Notas de plataforma [Section titled “Notas de plataforma”](#notas-de-plataforma) ### iOS [Section titled “iOS”](#ios) * Requiere iOS 11.0+ * El procesamiento de videos de gran tamaño puede requerir permisos para tareas en segundo plano * Utiliza iOS VideoToolbox nativo para aceleración de hardware ### Android [Section titled “Android”](#android) * Requiere Android 5.0 (API 21)+ * La aceleración del hardware varía según el dispositivo. * Puede requerir permiso WRITE\_EXTERNAL\_STORAGE para acceder a archivos ### Web [Section titled “Web”](#web) * No compatible con la plataforma web ## Consejos de rendimiento [Section titled “Consejos de rendimiento”](#consejos-de-rendimiento) 1. **Resolución más baja para un procesamiento más rápido**: Dimensiones más pequeñas = codificación más rápida 2. **Utilice aceleración de hardware**: permita que la plataforma nativa optimice la codificación 3. **Proceso en segundo plano**: no bloquee la interfaz de usuario durante el procesamiento del video 4. **Supervisar el uso de la memoria**: los vídeos de gran tamaño pueden consumir una cantidad significativa de memoria 5. **Prueba en dispositivos reales**: Es posible que los emuladores no reflejen el rendimiento real # @Capgo/Capacitor-file > Operaciones completas del sistema de archivos con lectura, escritura, copia, movimiento y gestión de directorios para iOS y Android. Operaciones Completas de Archivos Lee, escribe, añade, copia, mueve y elimina archivos con facilidad Gestión de Directorios Crea, lista y elimina directorios con soporte recursivo Múltiples Codificaciones Soporte para datos UTF-8, ASCII, UTF-16 y binarios (base64) Multiplataforma Funciona en iOS y Android con API consistente entre plataformas # @Capgo/Capacitor-file-compressor > Comprime imágenes de manera eficiente con soporte para formatos PNG, JPEG y WebP en iOS, Android y Web. Múltiples Formatos Soporte para compresión JPEG y WebP en todas las plataformas 🖼️ Control de Calidad Calidad de compresión ajustable de 0.0 a 1.0 para un equilibrio perfecto ⚙️ Redimensionamiento Inteligente Preservación automática de la relación de aspecto durante el redimensionamiento 📐 Sin Backend Toda la compresión ocurre en el dispositivo - no se necesita servidor 🚀 Multiplataforma API consistente en plataformas iOS, Android y Web 📱 Documentación Completa Consulta la [Documentación](/docs/plugins/file-compressor/getting-started/) para dominar el Plugin en solo unos minutos. # Comenzando > Aprende cómo instalar y usar el Plugin File Compressor para comprimir imágenes en tu aplicación Capacitor. 1. **Instala el paquete** * npm ```sh npm i @Capgo/Capacitor-file-compressor ``` * pnpm ```sh pnpm add @Capgo/Capacitor-file-compressor ``` * yarn ```sh yarn add @Capgo/Capacitor-file-compressor ``` * bun ```sh bun add @Capgo/Capacitor-file-compressor ``` 2. **Sincroniza con proyectos nativos** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Soporte de Plataformas [Section titled “Soporte de Plataformas”](#soporte-de-plataformas) | Plataforma | Formatos Soportados | Notas | | ---------- | ------------------- | --------------------------------------- | | iOS | JPEG | Solo se soporta compresión JPEG | | Android | JPEG, WebP | Ambos formatos completamente soportados | | Web | JPEG, WebP | Compresión basada en Canvas API | Nota: Los metadatos EXIF se eliminan durante la compresión en todas las plataformas. ## Uso [Section titled “Uso”](#uso) Importa el Plugin y comprime imágenes: ```typescript import { FileCompressor } from '@capgo/capacitor-file-compressor'; // Comprimir una imagen const compressImage = async () => { const result = await FileCompressor.compressImage({ source: 'file:///path/to/image.jpg', quality: 0.8, width: 1920, height: 1080 }); console.log('Ruta de imagen comprimida:', result.path); console.log('Tamaño original:', result.originalSize); console.log('Tamaño comprimido:', result.size); }; ``` ## Referencia de API [Section titled “Referencia de API”](#referencia-de-api) ### compressImage(Opciones) [Section titled “compressImage(Opciones)”](#compressimageopciones) Comprime un archivo de imagen con dimensiones y configuraciones de calidad especificadas. ```typescript interface CompressImageOptions { source: string; // Ruta al archivo de imagen quality?: number; // 0.0 a 1.0 (predeterminado: 0.8) width?: number; // Ancho objetivo en píxeles height?: number; // Alto objetivo en píxeles format?: 'jpeg' | '.webp'; // Formato de salida } interface CompressImageResult { path: string; // Ruta a la imagen comprimida size: number; // Tamaño del archivo comprimido en bytes originalSize: number; // Tamaño del archivo original en bytes } const result = await FileCompressor.compressImage(options); ``` **Notas Importantes:** * Los metadatos EXIF se eliminan durante la compresión en todas las plataformas * La relación de aspecto se mantiene automáticamente si solo se proporciona una dimensión * Los archivos comprimidos se guardan en directorios temporales en plataformas nativas ### getPluginVersion() [Section titled “getPluginVersion()”](#getpluginversion) Obtiene la versión del Plugin nativo. ```typescript const { version } = await FileCompressor.getPluginVersion(); console.log('Versión del plugin:', version); ``` ## Ejemplo Completo [Section titled “Ejemplo Completo”](#ejemplo-completo) ```typescript import { FileCompressor } from '@capgo/capacitor-file-compressor'; import { Camera } from '@capacitor/camera'; export class ImageCompressionService { async captureAndCompress() { try { // Tomar una foto const photo = await Camera.getPhoto({ quality: 100, allowEditing: false, resultType: 'uri' }); if (!photo.path) { throw new Error('No image path'); } // Comprimir la foto const compressed = await FileCompressor.compressImage({ source: photo.path, quality: 0.7, width: 1920, height: 1080, format: 'jpeg' }); console.log(`Ratio de compresión: ${ ((1 - compressed.size / compressed.originalSize) * 100).toFixed(1) }%`); return compressed.path; } catch (error) { console.error('Compresión fallida:', error); throw error; } } async batchCompress(imagePaths: string[]) { const results = []; for (const path of imagePaths) { try { const result = await FileCompressor.compressImage({ source: path, quality: 0.8, width: 1280 }); results.push(result); } catch (error) { console.error(`Fallo al comprimir ${path}:`, error); } } return results; } } ``` ## Mejores Prácticas [Section titled “Mejores Prácticas”](#mejores-prácticas) 1. **Configuración de Calidad**: Comienza con calidad 0.8 para un buen equilibrio entre tamaño de archivo y calidad de imagen 2. **Dimensiones de Redimensionamiento**: Solo especifica dimensiones cuando sea necesario - la relación de aspecto se preserva automáticamente 3. **Selección de Formato**: Usa JPEG para fotos y WebP para mejor compresión (solo Android/Web) 4. **Manejo de Errores**: Siempre envuelve las llamadas de compresión en bloques try-catch 5. **Limpieza**: Recuerda limpiar archivos temporales después de usar ## Solución de Problemas [Section titled “Solución de Problemas”](#solución-de-problemas) ### Problemas Comunes [Section titled “Problemas Comunes”](#problemas-comunes) **La imagen no se comprime**: Asegúrate de que la ruta de origen sea válida y accesible **Sin memoria**: Reduce las dimensiones objetivo o comprime imágenes de una en una **Formato no soportado**: Consulta la tabla de soporte de plataformas arriba # Comenzando > Aprende cómo instalar y usar el Plugin File para operaciones del sistema de archivos en tu aplicación Capacitor. 1. **Instala el paquete** * npm ```sh npm i @Capgo/Capacitor-file ``` * pnpm ```sh pnpm add @Capgo/Capacitor-file ``` * yarn ```sh yarn add @Capgo/Capacitor-file ``` * bun ```sh bun add @Capgo/Capacitor-file ``` 2. **Sincroniza con proyectos nativos** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Uso [Section titled “Uso”](#uso) Importa el Plugin y usa sus métodos para operaciones de archivos: ```typescript import { CapacitorFile, Directory, Encoding } from '@capgo/capacitor-file'; // Escribir un archivo de texto const writeFile = async () => { const result = await CapacitorFile.writeFile({ path: 'my-file.txt', directory: Directory.Documents, data: 'Hello, World!', encoding: Encoding.UTF8, recursive: true, // Crear directorios padres si es necesario }); console.log('Archivo escrito en:', result.uri); }; // Leer un archivo de texto const readFile = async () => { const result = await CapacitorFile.readFile({ path: 'my-file.txt', directory: Directory.Documents, encoding: Encoding.UTF8, }); console.log('Contenido del archivo:', result.data); }; // Verificar si el archivo existe const checkExists = async () => { const result = await CapacitorFile.exists({ path: 'my-file.txt', directory: Directory.Documents, }); console.log('El archivo existe:', result.exists); }; // Eliminar un archivo const deleteFile = async () => { await CapacitorFile.deleteFile({ path: 'my-file.txt', directory: Directory.Documents, }); }; ``` ## Ubicaciones de Directorios [Section titled “Ubicaciones de Directorios”](#ubicaciones-de-directorios) El Plugin proporciona varias constantes de directorio: ```typescript import { Directory } from '@capgo/capacitor-file'; // Directorios disponibles Directory.Documents // Documentos visibles para el usuario (con copia de seguridad) Directory.Data // Almacenamiento de datos privados de la app Directory.Library // Archivos de soporte de la app (iOS) / archivos (Android) Directory.Cache // Caché temporal (puede ser borrado por el SO) Directory.External // Almacenamiento externo (solo Android) Directory.Application // Bundle de la app de solo lectura ``` ## Opciones de Codificación [Section titled “Opciones de Codificación”](#opciones-de-codificación) ```typescript import { Encoding } from '@capgo/capacitor-file'; Encoding.UTF8 // Codificación de texto UTF-8 Encoding.ASCII // Codificación de texto ASCII Encoding.UTF16 // Codificación de texto UTF-16 // Omitir codificación para datos binarios (devuelve base64) ``` ## Referencia de API [Section titled “Referencia de API”](#referencia-de-api) ### writeFile(Opciones) [Section titled “writeFile(Opciones)”](#writefileopciones) Escribe datos en un archivo. ```typescript const result = await CapacitorFile.writeFile({ path: 'folder/file.txt', directory: Directory.Documents, data: 'Contenido del archivo', encoding: Encoding.UTF8, recursive: true, // Crear directorios si es necesario append: false, // Sobrescribir archivo existente position: 0, // Posición en bytes para escrituras de acceso aleatorio }); // Devuelve: { uri: string } ``` ### readFile(Opciones) [Section titled “readFile(Opciones)”](#readfileopciones) Lee el contenido de un archivo. ```typescript const result = await CapacitorFile.readFile({ path: 'file.txt', directory: Directory.Documents, encoding: Encoding.UTF8, offset: 0, // Comenzar lectura desde desplazamiento de bytes length: 100, // Leer solo esta cantidad de bytes }); // Devuelve: { data: string } ``` ### appendFile(Opciones) [Section titled “appendFile(Opciones)”](#appendfileopciones) Añade datos a un archivo. ```typescript await CapacitorFile.appendFile({ path: 'file.txt', directory: Directory.Documents, data: '\nNueva línea', encoding: Encoding.UTF8, }); ``` ### deleteFile(Opciones) [Section titled “deleteFile(Opciones)”](#deletefileopciones) Elimina un archivo. ```typescript await CapacitorFile.deleteFile({ path: 'file.txt', directory: Directory.Documents, }); ``` ### mkdir(Opciones) [Section titled “mkdir(Opciones)”](#mkdiropciones) Crea un directorio. ```typescript await CapacitorFile.mkdir({ path: 'my-folder/sub-folder', directory: Directory.Documents, recursive: true, // Crear directorios padres }); ``` ### rmdir(Opciones) [Section titled “rmdir(Opciones)”](#rmdiropciones) Elimina un directorio. ```typescript await CapacitorFile.rmdir({ path: 'my-folder', directory: Directory.Documents, recursive: true, // Eliminar contenidos recursivamente }); ``` ### readdir(Opciones) [Section titled “readdir(Opciones)”](#readdiropciones) Lista el contenido del directorio. ```typescript const result = await CapacitorFile.readdir({ path: '', // Vacío para la raíz del directorio directory: Directory.Documents, }); // Devuelve: { entries: Entry[] } // Cada entrada tiene: { name, isFile, isDirectory, fullPath, nativeURL } ``` ### stat(Opciones) [Section titled “stat(Opciones)”](#statopciones) Obtiene metadatos de archivo o directorio. ```typescript const result = await CapacitorFile.stat({ path: 'file.txt', directory: Directory.Documents, }); // Devuelve: { type, size, mtime, ctime, uri } ``` ### exists(Opciones) [Section titled “exists(Opciones)”](#existsopciones) Verifica si existe un archivo o directorio. ```typescript const result = await CapacitorFile.exists({ path: 'file.txt', directory: Directory.Documents, }); // Devuelve: { exists: boolean, type?: 'file' | 'directory' } ``` ### Copiar(Opciones) [Section titled “Copiar(Opciones)”](#copiaropciones) Copia un archivo o directorio. ```typescript const result = await CapacitorFile.copy({ from: 'source.txt', to: 'destination.txt', directory: Directory.Documents, toDirectory: Directory.Documents, }); // Devuelve: { uri: string } ``` ### rename(Opciones) / move(Opciones) [Section titled “rename(Opciones) / move(Opciones)”](#renameopciones--moveopciones) Renombra o mueve un archivo o directorio. ```typescript await CapacitorFile.rename({ from: 'old-name.txt', to: 'new-name.txt', directory: Directory.Documents, toDirectory: Directory.Documents, }); ``` ### truncate(Opciones) [Section titled “truncate(Opciones)”](#truncateopciones) Trunca un archivo a un tamaño específico. ```typescript await CapacitorFile.truncate({ path: 'file.txt', directory: Directory.Documents, size: 100, // Truncar a 100 bytes }); ``` ### getUri(Opciones) [Section titled “getUri(Opciones)”](#geturiopciones) Obtiene el URI nativo para un archivo. ```typescript const result = await CapacitorFile.getUri({ path: 'file.txt', directory: Directory.Documents, }); // Devuelve: { uri: string } ``` ### getDirectories() [Section titled “getDirectories()”](#getdirectories) Obtiene todas las rutas de directorios disponibles. ```typescript const dirs = await CapacitorFile.getDirectories(); // Devuelve rutas para: documents, data, cache, external, etc. ``` ### getFreeDiskSpace() [Section titled “getFreeDiskSpace()”](#getfreediskspace) Obtiene el espacio disponible en disco. ```typescript const result = await CapacitorFile.getFreeDiskSpace(); console.log('Espacio libre:', result.free, 'bytes'); ``` ### checkPermissions() / requestPermissions() [Section titled “checkPermissions() / requestPermissions()”](#checkpermissions--requestpermissions) Maneja permisos de almacenamiento (Android). ```typescript const status = await CapacitorFile.checkPermissions(); if (status.publicStorage !== 'granted') { await CapacitorFile.requestPermissions(); } ``` ## Ejemplo Completo [Section titled “Ejemplo Completo”](#ejemplo-completo) ```typescript import { CapacitorFile, Directory, Encoding } from '@capgo/capacitor-file'; export class FileService { async saveJson(filename: string, data: object): Promise { await CapacitorFile.writeFile({ path: filename, directory: Directory.Documents, data: JSON.stringify(data, null, 2), encoding: Encoding.UTF8, recursive: true, }); } async loadJson(filename: string): Promise { try { const { exists } = await CapacitorFile.exists({ path: filename, directory: Directory.Documents, }); if (!exists) return null; const result = await CapacitorFile.readFile({ path: filename, directory: Directory.Documents, encoding: Encoding.UTF8, }); return JSON.parse(result.data) as T; } catch (error) { console.error('Error al cargar JSON:', error); return null; } } async listFiles(folder: string = ''): Promise { const result = await CapacitorFile.readdir({ path: folder, directory: Directory.Documents, }); return result.entries .filter(entry => entry.isFile) .map(entry => entry.name); } async deleteAll(folder: string): Promise { const { exists } = await CapacitorFile.exists({ path: folder, directory: Directory.Documents, }); if (exists) { await CapacitorFile.rmdir({ path: folder, directory: Directory.Documents, recursive: true, }); } } async copyToBackup(filename: string): Promise { const timestamp = Date.now(); const backupName = `backup/${timestamp}-${filename}`; const result = await CapacitorFile.copy({ from: filename, to: backupName, directory: Directory.Documents, toDirectory: Directory.Documents, }); return result.uri; } } ``` ## Notas de Plataforma [Section titled “Notas de Plataforma”](#notas-de-plataforma) ### iOS [Section titled “iOS”](#ios) * Requiere iOS 13.0+ * El directorio Documents es visible en la Aplicación Files * El directorio Library es para archivos de soporte de la Aplicación * Cache puede ser borrado cuando el almacenamiento del dispositivo es bajo ### Android [Section titled “Android”](#android) * Requiere Android 6.0 (API 23)+ * El almacenamiento externo requiere permisos de tiempo de ejecución en versiones antiguas de Android * El directorio Documents se mapea al directorio de archivos de la Aplicación * Usa el directorio External para acceso a almacenamiento compartido ### Web [Section titled “Web”](#web) * No soportado en plataforma web # @Capgo/Capacitor-flash > Enciende y apaga la linterna/antorcha de tu dispositivo programáticamente con este ligero Plugin de Capacitor. API Simple Enciende/apaga la linterna con solo una llamada de método 🚀 Multiplataforma Funciona en plataformas iOS, Android y Web 📱 Ligero Huella mínima sin dependencias ⚡ Documentación Completa Consulta la [Documentación](/docs/plugins/flash/getting-started/) para dominar el Plugin en solo unos minutos. # Empezando > Aprenda cómo instalar y usar el complemento Flash para controlar la linterna de su dispositivo en su aplicación Capacitor. 1. **Instalar el paquete** * npm ```sh npm i @capgo/capacitor-flash ``` * pnpm ```sh pnpm add @capgo/capacitor-flash ``` * yarn ```sh yarn add @capgo/capacitor-flash ``` * bun ```sh bun add @capgo/capacitor-flash ``` 2. **Sincronización con proyectos nativos** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Uso [Section titled “Uso”](#uso) Importe el complemento y use sus métodos para controlar la linterna: ```typescript import { CapacitorFlash } from '@capgo/capacitor-flash'; // Check if flashlight is available const checkFlashlight = async () => { const { value } = await CapacitorFlash.isAvailable(); console.log('Flashlight available:', value); }; // Turn on flashlight const turnOn = async () => { await CapacitorFlash.switchOn(); }; // Turn off flashlight const turnOff = async () => { await CapacitorFlash.switchOff(); }; // Check if flashlight is on const checkStatus = async () => { const { value } = await CapacitorFlash.isSwitchedOn(); console.log('Flashlight is on:', value); }; // Toggle flashlight const toggle = async () => { await CapacitorFlash.toggle(); }; ``` ## API Referencia [Section titled “API Referencia”](#api-referencia) ### está disponible() [Section titled “está disponible()”](#está-disponible) Comprueba si la linterna está disponible en el dispositivo. ```typescript const result = await CapacitorFlash.isAvailable(); // Returns: { value: boolean } ``` ### encender (¿opciones?) [Section titled “encender (¿opciones?)”](#encender-opciones) Enciende la linterna. ```typescript interface SwitchOnOptions { intensity?: number; // 0.0 to 1.0 (iOS only) } await CapacitorFlash.switchOn({ intensity: 0.5 }); ``` ### apagar() [Section titled “apagar()”](#apagar) Apaga la linterna. ```typescript await CapacitorFlash.switchOff(); ``` ### está encendido() [Section titled “está encendido()”](#está-encendido) Comprueba si la linterna está encendida actualmente. ```typescript const result = await CapacitorFlash.isSwitchedOn(); // Returns: { value: boolean } ``` ### alternar() [Section titled “alternar()”](#alternar) Activa y desactiva la linterna. ```typescript await CapacitorFlash.toggle(); ``` ## Ejemplo completo [Section titled “Ejemplo completo”](#ejemplo-completo) ```typescript import { CapacitorFlash } from '@capgo/capacitor-flash'; export class FlashlightService { private isOn = false; async init() { const { value } = await CapacitorFlash.isAvailable(); if (!value) { throw new Error('Flashlight not available on this device'); } } async toggle() { if (this.isOn) { await CapacitorFlash.switchOff(); } else { await CapacitorFlash.switchOn(); } this.isOn = !this.isOn; } async turnOn(intensity?: number) { await CapacitorFlash.switchOn({ intensity }); this.isOn = true; } async turnOff() { await CapacitorFlash.switchOff(); this.isOn = false; } async strobe(intervalMs: number = 100, duration: number = 3000) { const endTime = Date.now() + duration; while (Date.now() < endTime) { await CapacitorFlash.toggle(); await new Promise(resolve => setTimeout(resolve, intervalMs)); } // Ensure flashlight is off after strobe await CapacitorFlash.switchOff(); this.isOn = false; } } ``` ## Mejores prácticas [Section titled “Mejores prácticas”](#mejores-prácticas) 1. **Consulta primero la disponibilidad** Siempre verifique la disponibilidad de la linterna antes de usarla: ```typescript const { value } = await CapacitorFlash.isAvailable(); if (!value) { // Show alternative UI or disable flashlight features } ``` 2. **Maneja los errores con elegancia** ```typescript try { await CapacitorFlash.switchOn(); } catch (error) { console.error('Failed to turn on flashlight:', error); } ``` 3. **Apagar cuando no sea necesario** Apague siempre la linterna cuando su aplicación pase a segundo plano o cuando la función ya no sea necesaria para ahorrar batería. 4. **Evita el cambio rápido** Implemente la función antirrebote para evitar un encendido/apagado rápido que podría dañar el LED o agotar la batería. ## Notas de plataforma [Section titled “Notas de plataforma”](#notas-de-plataforma) ### iOS [Section titled “iOS”](#ios) * Requiere iOS 10.0+ * Utiliza el modo antorcha `AVCaptureDevice` * Admite control de intensidad (0,0 a 1,0) ### Android [Section titled “Android”](#android) * Requiere Android 6.0 (API 23)+ * Utiliza Cámara2 API * No se admite el control de intensidad (utiliza el brillo predeterminado) ### Web [Section titled “Web”](#web) * Compatible con navegadores con MediaDevices API y capacidad de antorcha * Utiliza la transmisión de la cámara para acceder a la linterna del dispositivo * Requiere permiso del usuario para acceder a la cámara * Funciona mejor en navegadores móviles (Chrome en Android) * Los navegadores de escritorio normalmente devuelven `isAvailable: false` (sin hardware de antorcha) * El control de intensidad no es compatible con la web. # @Capgo/Capacitor-gtm > Integra Google Tag Manager en tus aplicaciones móviles con el SDK oficial para un seguimiento completo de analíticas. SDK Oficial Usa el SDK oficial de Google Tag Manager para iOS y Android 🏆 Seguimiento de Eventos Envía eventos al dataLayer con parámetros personalizados 📊 Propiedades de Usuario Establece y gestiona propiedades de usuario para segmentación 👤 Gestión de Contenedores Inicializa y gestiona contenedores GTM con facilidad 📦 Acceso al DataLayer Obtén valores de la configuración del contenedor 🔍 Comenzar Consulta la [Guía de Inicio](/docs/plugins/gtm/getting-started/) para instalar y configurar el Plugin. # Comenzar > Aprende a instalar e integrar Google Tag Manager en tu aplicación Capacitor usando el SDK oficial. ## Instalación [Section titled “Instalación”](#instalación) * npm ```bash npm install @capgo/capacitor-gtm npx cap sync ``` * yarn ```bash yarn add @capgo/capacitor-gtm npx cap sync ``` * pnpm ```bash pnpm add @capgo/capacitor-gtm npx cap sync ``` * bun ```bash bun add @capgo/capacitor-gtm npx cap sync ``` ## Configuración de Plataforma [Section titled “Configuración de Plataforma”](#configuración-de-plataforma) ### iOS [Section titled “iOS”](#ios) 1. Descarga tu archivo de configuración del contenedor GTM desde la consola de Google Tag Manager 2. Agrega el archivo JSON del contenedor a tu proyecto iOS en Xcode 3. El archivo debe llamarse algo como `GTM-XXXXXX.json` ### Android [Section titled “Android”](#android) 1. Descarga tu archivo de configuración del contenedor GTM desde la consola de Google Tag Manager 2. Agrega el archivo JSON del contenedor a `android/app/src/main/assets/` 3. El archivo debe llamarse algo como `GTM-XXXXXX.json` ## Ejemplo de Uso [Section titled “Ejemplo de Uso”](#ejemplo-de-uso) ```typescript import { CapacitorGTM } from '@capgo/capacitor-gtm'; // Initialize GTM with your container ID await CapacitorGTM.init({ containerId: 'GTM-XXXXXX' }); // Push events to dataLayer await CapacitorGTM.pushEvent({ event: 'screen_view', parameters: { screen_name: 'Home', screen_class: 'HomeScreen' } }); // Push custom events with parameters await CapacitorGTM.pushEvent({ event: 'purchase', parameters: { transaction_id: 'T12345', value: 25.00, currency: 'USD', items: [ { item_id: 'SKU_12345', item_name: 'Product Name', quantity: 1, price: 25.00 } ] } }); // Set user properties await CapacitorGTM.setUserProperty({ key: 'user_type', value: 'premium' }); // Get values from dataLayer const result = await CapacitorGTM.getVariable({ key: 'config_value' }); console.log('Config value:', result.value); ``` ## Referencia de API [Section titled “Referencia de API”](#referencia-de-api) ### init(Opciones) [Section titled “init(Opciones)”](#initopciones) ```typescript init(options: { containerId: string }) => Promise ``` Inicializa Google Tag Manager con tu ID de contenedor. | Param | Type | | ------------- | ------------------------- | | **`options`** | `{ containerId: string }` | ### pushEvent(Opciones) [Section titled “pushEvent(Opciones)”](#pusheventopciones) ```typescript pushEvent(options: GTMEvent) => Promise ``` Envía un evento al dataLayer con parámetros opcionales. | Param | Type | | ------------- | ---------- | | **`options`** | `GTMEvent` | ### setUserProperty(Opciones) [Section titled “setUserProperty(Opciones)”](#setuserpropertyopciones) ```typescript setUserProperty(options: { key: string; value: string }) => Promise ``` Establece una propiedad de usuario para segmentación y targeting. | Param | Type | | ------------- | -------------------------------- | | **`options`** | `{ key: string; value: string }` | ### getVariable(Opciones) [Section titled “getVariable(Opciones)”](#getvariableopciones) ```typescript getVariable(options: { key: string }) => Promise<{ value: any }> ``` Obtiene un valor de la configuración del contenedor GTM. | Param | Type | | ------------- | ----------------- | | **`options`** | `{ key: string }` | **Retorna:** `Promise<{ value: any }>` ## Interfaces [Section titled “Interfaces”](#interfaces) ### GTMEvent [Section titled “GTMEvent”](#gtmevent) | Prop | Type | Description | | ---------------- | --------------------- | ------------------------------------------- | | **`event`** | `string` | El nombre del evento | | **`parameters`** | `Record` | Parámetros opcionales del evento (opcional) | ## Ejemplos de Eventos Comunes [Section titled “Ejemplos de Eventos Comunes”](#ejemplos-de-eventos-comunes) ### Vistas de Pantalla [Section titled “Vistas de Pantalla”](#vistas-de-pantalla) ```typescript await CapacitorGTM.pushEvent({ event: 'screen_view', parameters: { screen_name: 'Product Details', screen_class: 'ProductScreen' } }); ``` ### Acciones de Usuario [Section titled “Acciones de Usuario”](#acciones-de-usuario) ```typescript await CapacitorGTM.pushEvent({ event: 'button_click', parameters: { button_name: 'add_to_cart', product_id: 'SKU_12345' } }); ``` ### Eventos de E-commerce [Section titled “Eventos de E-commerce”](#eventos-de-e-commerce) ```typescript await CapacitorGTM.pushEvent({ event: 'begin_checkout', parameters: { currency: 'USD', value: 75.00, items: [ { item_id: 'SKU_12345', item_name: 'Product 1', quantity: 2, price: 25.00 }, { item_id: 'SKU_67890', item_name: 'Product 2', quantity: 1, price: 25.00 } ] } }); ``` ## Mejores Prácticas [Section titled “Mejores Prácticas”](#mejores-prácticas) * Inicializa GTM temprano en el ciclo de vida de tu aplicación * Usa convenciones de nombres de eventos consistentes * Incluye parámetros relevantes con cada evento * Prueba tu configuración de GTM en modo de vista previa * Monitorea los datos en la consola de Google Tag Manager * Usa propiedades de usuario para segmentación significativa ## Depuración [Section titled “Depuración”](#depuración) Habilita el modo de depuración en la consola de Google Tag Manager para probar tu implementación: 1. Abre la consola de GTM 2. Ve al modo de Vista Previa 3. Conéctate a tu aplicación usando el ID del contenedor 4. Verifica que los eventos se estén rastreando correctamente ## Casos de Uso [Section titled “Casos de Uso”](#casos-de-uso) * Rastrear interacciones y comportamiento del usuario * Monitorear métricas de rendimiento de la aplicación * Recopilar datos de e-commerce * Gestionar etiquetas de terceros sin cambios de código * Implementar pruebas A/B y personalización * Integrar con Google Analytics y otros servicios # @Capgo/Capacitor-health > Lee y escribe métricas de fitness en iOS y Android mientras mantienes un contrato TypeScript consistente. El Plugin Capgo Health unifica el acceso a HealthKit y Health Connect para que puedas rastrear actividad, métricas biométricas y mediciones corporales con una sola integración de Capacitor. Datos de salud multiplataforma Soporta Apple HealthKit y Android Health Connect desde el inicio. Permisos granulares Solicita permisos separados de lectura/escritura y reacciona a cambios de autorización. Unidades consistentes Trabaja con tipos de datos normalizados como `steps`, `heartRate` y `weight`. Escritura en tiempo real Guarda muestras de vuelta en el almacén de salud del usuario cuando registre actividad. Sigue la guía de inicio para habilitar capacidades de plataforma, recopilar consentimiento y comenzar a sincronizar métricas. # Comenzar > Habilita el acceso a HealthKit y Health Connect con el Plugin Capgo Health. 1. **Instalar el Plugin** * npm ```sh npm i @Capgo/Capacitor-health ``` * pnpm ```sh pnpm add @Capgo/Capacitor-health ``` * yarn ```sh yarn add @Capgo/Capacitor-health ``` * bun ```sh bun add @Capgo/Capacitor-health ``` 2. **Sincronizar proyectos nativos** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Configuración de iOS [Section titled “Configuración de iOS”](#configuración-de-ios) * Abre `ios/App/App.xcworkspace` en Xcode y habilita la capacidad de **HealthKit**. * Proporciona descripciones de uso en `Info.plist`: ```xml NSHealthShareUsageDescription This app reads your health metrics to personalize your experience. NSHealthUpdateUsageDescription This app writes health data that you explicitly record. ``` ## Configuración de Android [Section titled “Configuración de Android”](#configuración-de-android) Los datos de salud están respaldados por [Health Connect](https://developer.android.com/health-and-fitness/guides/health-connect). Asegúrate de que: * `minSdkVersion` sea **26 o superior** (Capacitor 5 ya satisface esto). * Los usuarios tengan Health Connect instalado (Android 14+ lo incluye; versiones anteriores requieren descarga desde Play Store). * Guíes a los usuarios a través de la interfaz de permisos de tiempo de ejecución al solicitar acceso. ## Verificar disponibilidad y solicitar acceso [Section titled “Verificar disponibilidad y solicitar acceso”](#verificar-disponibilidad-y-solicitar-acceso) ```typescript import { Health } from '@capgo/capacitor-health'; const { available, reason } = await Health.isAvailable(); if (!available) { console.warn('Health APIs unavailable:', reason); return; } await Health.requestAuthorization({ read: ['steps', 'heartRate', 'weight'], write: ['weight'], }); ``` ## Leer muestras [Section titled “Leer muestras”](#leer-muestras) ```typescript const { samples } = await Health.readSamples({ dataType: 'steps', startDate: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString(), endDate: new Date().toISOString(), limit: 200, }); samples.forEach((sample) => { console.log(sample.value, sample.startDate, sample.endDate); }); ``` ## Escribir muestras [Section titled “Escribir muestras”](#escribir-muestras) ```typescript await Health.saveSample({ dataType: 'weight', value: 74.6, startDate: new Date().toISOString(), }); ``` ## Gestión de autorizaciones [Section titled “Gestión de autorizaciones”](#gestión-de-autorizaciones) * Usa `checkAuthorization()` para verificar los permisos actuales de lectura/escritura y ajustar tu interfaz. * Maneja las denegaciones con gracia explicando por qué los datos mejoran la experiencia y ofreciendo un reintento. * Siempre respeta la privacidad del usuario y solicita solo los permisos mínimos que requiere tu función. # @Capgo/home-indicator > Oculta o muestra el indicador de inicio en dispositivos iOS para crear experiencias verdaderamente inmersivas a pantalla completa en tu aplicación. Control del indicador de inicio de iOS Control total sobre la visibilidad del indicador de inicio 📱 Experiencias inmersivas Crea experiencias a pantalla completa sin distracciones 🎮 Soporte de auto-ocultación Configura el comportamiento de auto-ocultación para UX sin interrupciones ✨ Documentación Completa Consulta la [Documentación](/docs/plugins/home-indicator/getting-started/) para dominar el Plugin en solo unos minutos. # Empezando > Aprenda a instalar y configurar el complemento Capacitor Home Indicator para controlar el indicador de inicio en dispositivos iOS. 1. **Instalar el paquete** * npm ```sh npm i @capgo/capacitor-home-indicator ``` * pnpm ```sh pnpm add @capgo/capacitor-home-indicator ``` * yarn ```sh yarn add @capgo/capacitor-home-indicator ``` * bun ```sh bun add @capgo/capacitor-home-indicator ``` 2. **Sincronización con proyectos nativos** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` 3. **Configurar el complemento** **Ocultar indicador de inicio:** ```typescript import { HomeIndicator } from '@capgo/capacitor-home-indicator'; // Hide the home indicator await HomeIndicator.hide(); ``` **Mostrar indicador de inicio:** ```typescript // Show the home indicator await HomeIndicator.show(); // Check visibility status const { hidden } = await HomeIndicator.isHidden(); console.log('Is hidden:', hidden); ``` * iOS No se requiere configuración adicional. El complemento solo funciona en dispositivos iOS con indicador de inicio (iPhone X y posteriores). * Android Este complemento no tiene ningún efecto en los dispositivos Android ya que no tienen un indicador de inicio estilo iOS. 4. **Uso avanzado** ```typescript import { HomeIndicator } from '@capgo/capacitor-home-indicator'; import { App } from '@capacitor/app'; export class ImmersiveMode { private isImmersive = false; async enterImmersiveMode() { this.isImmersive = true; // Hide home indicator await HomeIndicator.hide(); // Hide status bar for full immersion // StatusBar.hide(); // If using @capacitor/status-bar } async exitImmersiveMode() { this.isImmersive = false; // Show home indicator await HomeIndicator.show(); // Show status bar // StatusBar.show(); // If using @capacitor/status-bar } async toggleImmersiveMode() { const { hidden } = await HomeIndicator.isHidden(); if (hidden) { await this.exitImmersiveMode(); } else { await this.enterImmersiveMode(); } } setupAppLifecycle() { // Handle app state changes App.addListener('appStateChange', async ({ isActive }) => { if (!isActive && this.isImmersive) { // App went to background, might want to show indicator await HomeIndicator.show(); } else if (isActive && this.isImmersive) { // App came to foreground, restore immersive mode await HomeIndicator.hide(); } }); } } ``` ## API Referencia [Section titled “API Referencia”](#api-referencia) ### Métodos [Section titled “Métodos”](#métodos) #### `hide()` [Section titled “hide()”](#hide) Oculta el indicador de inicio. **Devoluciones:** `Promise` #### `show()` [Section titled “show()”](#show) Muestra el indicador de inicio. **Devoluciones:** `Promise` #### `isHidden()` [Section titled “isHidden()”](#ishidden) Compruebe si el indicador de inicio está actualmente oculto. **Devoluciones:** `Promise<{ hidden: boolean }>` ### Interfaces [Section titled “Interfaces”](#interfaces) ```typescript interface HiddenResult { hidden: boolean; } ``` ## Notas de plataforma [Section titled “Notas de plataforma”](#notas-de-plataforma) ### iOS [Section titled “iOS”](#ios) * Sólo funciona en dispositivos con indicador de inicio (iPhone X y posteriores) * Requiere iOS 11.0 o posterior * El indicador reaparece cuando los usuarios deslizan el dedo desde abajo. ### Android [Section titled “Android”](#android) * Este complemento no tiene ningún efecto en Android * Android usa diferentes gestos/botones de navegación ## Casos de uso comunes [Section titled “Casos de uso comunes”](#casos-de-uso-comunes) 1. **Juegos**: juegos en pantalla completa sin distracciones 2. **Reproductores de vídeo**: reproducción de vídeo inmersiva 3. **Visualizadores de fotos**: galerías de fotos en pantalla completa 4. **Presentaciones**: Presentaciones sin distracciones 5. **Aplicaciones de quiosco**: aplicaciones de visualización pública ## Mejores prácticas [Section titled “Mejores prácticas”](#mejores-prácticas) 1. **Control de usuario**: proporcione siempre una forma de salir del modo inmersivo ```typescript // Add a gesture or button to toggle immersive mode const toggleButton = document.getElementById('toggle-immersive'); toggleButton.addEventListener('click', () => { immersiveMode.toggleImmersiveMode(); }); ``` 2. **Respeta los gestos del sistema**: no interfieras con la navegación del sistema 3. **Guardar estado**: recuerda la preferencia del usuario por el modo inmersivo ## Solución de problemas [Section titled “Solución de problemas”](#solución-de-problemas) **El indicador de inicio no se oculta:** * Verifique que el dispositivo tenga un indicador de inicio (iPhone X+) * Verifique que la versión iOS sea 11.0 o superior * Asegúrese de que la aplicación tenga foco **El indicador reaparece inesperadamente:** * Este es el comportamiento normal de iOS para los gestos del sistema. # @Capgo/Capacitor-ibeacon > Detecta y monitorea beacons Bluetooth para funciones basadas en proximidad, navegación interior y experiencias conscientes de ubicación. Monitoreo de Beacon Monitorea regiones de beacon y recibe notificaciones al entrar o salir 🎯 Detección de Beacon Detecta beacons cercanos y mide su distancia en tiempo real 📡 Detección en Segundo Plano Monitorea beacons incluso cuando la aplicación está en segundo plano 🔔 Anuncio de Beacon Convierte tu dispositivo en un transmisor iBeacon (solo iOS) 📱 Multiplataforma Funciona en iOS y Android con bibliotecas nativas de beacon 📱 Documentación Completa Consulta la [Documentación](/docs/plugins/ibeacon/getting-started/) para dominar el Plugin en solo unos minutos. # Empezando > Aprenda a instalar y utilizar el complemento iBeacon para detectar y monitorear balizas Bluetooth en su aplicación Capacitor. 1. **Instalar el paquete** * npm ```sh npm i @capgo/capacitor-ibeacon ``` * pnpm ```sh pnpm add @capgo/capacitor-ibeacon ``` * yarn ```sh yarn add @capgo/capacitor-ibeacon ``` * bun ```sh bun add @capgo/capacitor-ibeacon ``` 2. **Sincronización con proyectos nativos** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Configuración [Section titled “Configuración”](#configuración) ### iOS Configuración [Section titled “iOS Configuración”](#ios-configuración) Agregue lo siguiente a su `Info.plist`: ```xml NSLocationWhenInUseUsageDescription This app needs location access to detect nearby beacons NSLocationAlwaysAndWhenInUseUsageDescription This app needs location access to monitor beacons in the background NSBluetoothAlwaysUsageDescription This app uses Bluetooth to detect nearby beacons UIBackgroundModes location ``` ### Android Configuración [Section titled “Android Configuración”](#android-configuración) Agregue lo siguiente a su `AndroidManifest.xml`: ```xml ``` **Importante**: Para Android, debe integrar la biblioteca [AltBeacon](https://altbeacon.github.io/android-beacon-library/) en su proyecto para que funcione la detección de balizas. Agregue al `build.gradle` de su aplicación: ```kotlin dependencies { implementation 'org.altbeacon:android-beacon-library:2.20+' } ``` ## Uso [Section titled “Uso”](#uso) ### Configuración básica [Section titled “Configuración básica”](#configuración-básica) ```typescript import { CapacitorIbeacon } from '@capgo/capacitor-ibeacon'; // Define your beacon region const beaconRegion = { identifier: 'MyStore', uuid: 'B9407F30-F5F8-466E-AFF9-25556B57FE6D', major: 1, // Optional minor: 2 // Optional }; ``` ### Solicitar permisos [Section titled “Solicitar permisos”](#solicitar-permisos) ```typescript // Request "When In Use" permission const requestPermission = async () => { const { status } = await CapacitorIbeacon.requestWhenInUseAuthorization(); console.log('Permission status:', status); // status: 'not_determined' | 'restricted' | 'denied' | 'authorized_always' | 'authorized_when_in_use' }; // Request "Always" permission (for background monitoring) const requestAlwaysPermission = async () => { const { status } = await CapacitorIbeacon.requestAlwaysAuthorization(); console.log('Always permission status:', status); }; // Check current authorization status const checkPermission = async () => { const { status } = await CapacitorIbeacon.getAuthorizationStatus(); console.log('Current status:', status); }; ``` ### Verifique las capacidades del dispositivo [Section titled “Verifique las capacidades del dispositivo”](#verifique-las-capacidades-del-dispositivo) ```typescript // Check if Bluetooth is enabled const checkBluetooth = async () => { const { enabled } = await CapacitorIbeacon.isBluetoothEnabled(); if (!enabled) { console.log('Please enable Bluetooth'); } }; // Check if ranging is available const checkRanging = async () => { const { available } = await CapacitorIbeacon.isRangingAvailable(); console.log('Ranging available:', available); }; ``` ### Monitorear regiones de baliza [Section titled “Monitorear regiones de baliza”](#monitorear-regiones-de-baliza) El monitoreo detecta cuando ingresa o sale de una región de baliza. Esto funciona en segundo plano. ```typescript // Start monitoring const startMonitoring = async () => { await CapacitorIbeacon.startMonitoringForRegion({ identifier: 'MyStore', uuid: 'B9407F30-F5F8-466E-AFF9-25556B57FE6D', major: 1, minor: 2 }); console.log('Started monitoring for beacons'); }; // Stop monitoring const stopMonitoring = async () => { await CapacitorIbeacon.stopMonitoringForRegion({ identifier: 'MyStore', uuid: 'B9407F30-F5F8-466E-AFF9-25556B57FE6D' }); }; ``` ### Balizas de alcance [Section titled “Balizas de alcance”](#balizas-de-alcance) La medición de distancia proporciona actualizaciones continuas sobre las balizas cercanas y sus distancias. Esto requiere que la aplicación esté en primer plano. ```typescript // Start ranging const startRanging = async () => { await CapacitorIbeacon.startRangingBeaconsInRegion({ identifier: 'MyStore', uuid: 'B9407F30-F5F8-466E-AFF9-25556B57FE6D' }); console.log('Started ranging beacons'); }; // Stop ranging const stopRanging = async () => { await CapacitorIbeacon.stopRangingBeaconsInRegion({ identifier: 'MyStore', uuid: 'B9407F30-F5F8-466E-AFF9-25556B57FE6D' }); }; ``` ### Escuchar eventos [Section titled “Escuchar eventos”](#escuchar-eventos) ```typescript import { PluginListenerHandle } from '@capacitor/core'; // Listen for ranging events (continuous distance updates) const rangingListener: PluginListenerHandle = await CapacitorIbeacon.addListener( 'didRangeBeacons', (data) => { console.log('Beacons detected:', data.beacons); data.beacons.forEach(beacon => { console.log(`UUID: ${beacon.uuid}`); console.log(`Major: ${beacon.major}, Minor: ${beacon.minor}`); console.log(`Distance: ${beacon.accuracy}m`); console.log(`Proximity: ${beacon.proximity}`); // immediate, near, far, unknown console.log(`RSSI: ${beacon.rssi}`); }); } ); // Listen for region enter events const enterListener: PluginListenerHandle = await CapacitorIbeacon.addListener( 'didEnterRegion', (data) => { console.log('Entered region:', data.region.identifier); // Show welcome notification or trigger action } ); // Listen for region exit events const exitListener: PluginListenerHandle = await CapacitorIbeacon.addListener( 'didExitRegion', (data) => { console.log('Exited region:', data.region.identifier); // Trigger goodbye action } ); // Listen for region state changes const stateListener: PluginListenerHandle = await CapacitorIbeacon.addListener( 'didDetermineStateForRegion', (data) => { console.log(`Region ${data.region.identifier}: ${data.state}`); // state: 'inside' | 'outside' | 'unknown' } ); // Clean up listeners when done const cleanup = () => { rangingListener.remove(); enterListener.remove(); exitListener.remove(); stateListener.remove(); }; ``` ### Anuncie como iBeacon (solo iOS) [Section titled “Anuncie como iBeacon (solo iOS)”](#anuncie-como-ibeacon-solo-ios) Convierta su dispositivo en un transmisor iBeacon. ```typescript // Start advertising const startAdvertising = async () => { await CapacitorIbeacon.startAdvertising({ uuid: 'B9407F30-F5F8-466E-AFF9-25556B57FE6D', major: 1, minor: 2, identifier: 'MyBeacon', measuredPower: -59 // Optional: calibrated power at 1 meter }); console.log('Started advertising as iBeacon'); }; // Stop advertising const stopAdvertising = async () => { await CapacitorIbeacon.stopAdvertising(); }; ``` ## Ejemplo completo [Section titled “Ejemplo completo”](#ejemplo-completo) ```typescript import { CapacitorIbeacon } from '@capgo/capacitor-ibeacon'; import { PluginListenerHandle } from '@capacitor/core'; export class BeaconService { private listeners: PluginListenerHandle[] = []; async init() { // Request permissions const { status } = await CapacitorIbeacon.requestWhenInUseAuthorization(); if (status !== 'authorized_when_in_use' && status !== 'authorized_always') { throw new Error('Location permission denied'); } // Check Bluetooth const { enabled } = await CapacitorIbeacon.isBluetoothEnabled(); if (!enabled) { throw new Error('Bluetooth is not enabled'); } // Set up event listeners this.setupListeners(); } private setupListeners() { this.listeners.push( await CapacitorIbeacon.addListener('didEnterRegion', (data) => { console.log('Welcome! Entered:', data.region.identifier); this.onEnterRegion(data.region); }) ); this.listeners.push( await CapacitorIbeacon.addListener('didExitRegion', (data) => { console.log('Goodbye! Left:', data.region.identifier); this.onExitRegion(data.region); }) ); this.listeners.push( await CapacitorIbeacon.addListener('didRangeBeacons', (data) => { this.onRangeBeacons(data.beacons); }) ); } async startMonitoring(uuid: string, identifier: string, major?: number, minor?: number) { await CapacitorIbeacon.startMonitoringForRegion({ identifier, uuid, major, minor }); } async startRanging(uuid: string, identifier: string) { await CapacitorIbeacon.startRangingBeaconsInRegion({ identifier, uuid }); } private onEnterRegion(region: any) { // Handle region entry (e.g., show notification, trigger content) console.log('Entered beacon region:', region); } private onExitRegion(region: any) { // Handle region exit console.log('Left beacon region:', region); } private onRangeBeacons(beacons: any[]) { // Process beacon distances const nearestBeacon = beacons.reduce((nearest, beacon) => { return beacon.accuracy < nearest.accuracy ? beacon : nearest; }, beacons[0]); if (nearestBeacon) { console.log('Nearest beacon:', nearestBeacon); this.handleProximity(nearestBeacon); } } private handleProximity(beacon: any) { switch (beacon.proximity) { case 'immediate': // < 0.5m console.log('Very close to beacon'); break; case 'near': // 0.5m - 3m console.log('Near beacon'); break; case 'far': // > 3m console.log('Far from beacon'); break; case 'unknown': console.log('Distance unknown'); break; } } cleanup() { this.listeners.forEach(listener => listener.remove()); this.listeners = []; } } ``` ## API Referencia [Section titled “API Referencia”](#api-referencia) ### iniciarMonitoreoParaRegión(opciones) [Section titled “iniciarMonitoreoParaRegión(opciones)”](#iniciarmonitoreopararegiónopciones) Inicie el monitoreo de una región de baliza. Activa eventos al entrar/salir. ```typescript interface BeaconRegion { identifier: string; uuid: string; major?: number; minor?: number; notifyEntryStateOnDisplay?: boolean; } await CapacitorIbeacon.startMonitoringForRegion(options); ``` ### detenerMonitoringForRegion(opciones) [Section titled “detenerMonitoringForRegion(opciones)”](#detenermonitoringforregionopciones) Detener el monitoreo de una región de baliza. ```typescript await CapacitorIbeacon.stopMonitoringForRegion(options); ``` ### iniciarRangingBeaconsInRegion(opciones) [Section titled “iniciarRangingBeaconsInRegion(opciones)”](#iniciarrangingbeaconsinregionopciones) Comience a localizar balizas en una región para obtener actualizaciones continuas de distancia. ```typescript await CapacitorIbeacon.startRangingBeaconsInRegion(options); ``` ### detenerRangingBeaconsInRegion(opciones) [Section titled “detenerRangingBeaconsInRegion(opciones)”](#detenerrangingbeaconsinregionopciones) Detener el alcance de las balizas en una región. ```typescript await CapacitorIbeacon.stopRangingBeaconsInRegion(options); ``` ### iniciarPublicidad(opciones) [Section titled “iniciarPublicidad(opciones)”](#iniciarpublicidadopciones) Comience a anunciar el dispositivo como un iBeacon (solo iOS). ```typescript interface BeaconAdvertisingOptions { uuid: string; major: number; minor: number; identifier: string; measuredPower?: number; // Calibrated power at 1 meter } await CapacitorIbeacon.startAdvertising(options); ``` ### detener la publicidad() [Section titled “detener la publicidad()”](#detener-la-publicidad) Deje de anunciar el dispositivo como un iBeacon. ```typescript await CapacitorIbeacon.stopAdvertising(); ``` ### solicitudCuandoInUseAuthorization() [Section titled “solicitudCuandoInUseAuthorization()”](#solicitudcuandoinuseauthorization) Solicite autorización de ubicación “Cuando esté en uso”. ```typescript const result = await CapacitorIbeacon.requestWhenInUseAuthorization(); // Returns: { status: string } ``` ### solicitarAlwaysAuthorization() [Section titled “solicitarAlwaysAuthorization()”](#solicitaralwaysauthorization) Solicite autorización de ubicación “Siempre” (requerida para el monitoreo en segundo plano). ```typescript const result = await CapacitorIbeacon.requestAlwaysAuthorization(); // Returns: { status: string } ``` ### obtener estado de autorización() [Section titled “obtener estado de autorización()”](#obtener-estado-de-autorización) Obtenga el estado de autorización de ubicación actual. ```typescript const result = await CapacitorIbeacon.getAuthorizationStatus(); // Returns: { status: 'not_determined' | 'restricted' | 'denied' | 'authorized_always' | 'authorized_when_in_use' } ``` ### está habilitado para Bluetooth () [Section titled “está habilitado para Bluetooth ()”](#está-habilitado-para-bluetooth) Compruebe si Bluetooth está habilitado. ```typescript const result = await CapacitorIbeacon.isBluetoothEnabled(); // Returns: { enabled: boolean } ``` ### estáRangingAvailable() [Section titled “estáRangingAvailable()”](#estárangingavailable) Compruebe si el alcance está disponible en el dispositivo. ```typescript const result = await CapacitorIbeacon.isRangingAvailable(); // Returns: { available: boolean } ``` ### habilitarARMAFilter(opciones) [Section titled “habilitarARMAFilter(opciones)”](#habilitararmafilteropciones) Habilite el filtrado ARMA para cálculos de distancia (solo Android). ```typescript await CapacitorIbeacon.enableARMAFilter({ enabled: true }); ``` ## Eventos [Section titled “Eventos”](#eventos) ### didRangeBeacons [Section titled “didRangeBeacons”](#didrangebeacons) Se dispara cuando se detectan balizas durante el alcance. ```typescript interface RangingEvent { region: BeaconRegion; beacons: Beacon[]; } interface Beacon { uuid: string; major: number; minor: number; rssi: number; // Signal strength proximity: 'immediate' | 'near' | 'far' | 'unknown'; accuracy: number; // Distance in meters } ``` ### entró en la región [Section titled “entró en la región”](#entró-en-la-región) Se dispara al ingresar a una región de baliza monitoreada. ```typescript interface RegionEvent { region: BeaconRegion; } ``` ### didExitRegión [Section titled “didExitRegión”](#didexitregión) Se dispara al salir de una región de baliza monitoreada. ```typescript interface RegionEvent { region: BeaconRegion; } ``` ### didDetermineStateForRegión [Section titled “didDetermineStateForRegión”](#diddeterminestateforregión) Se activa cuando se determina el estado de la región. ```typescript interface StateEvent { region: BeaconRegion; state: 'inside' | 'outside' | 'unknown'; } ``` ## Valores de proximidad [Section titled “Valores de proximidad”](#valores-de-proximidad) * **inmediato**: Muy cerca (< 0.5 meters) * **near**: Relatively close (0.5 - 3 meters) * **far**: Further away (> 3 metros) * **desconocido**: No se puede determinar la distancia ## Mejores prácticas [Section titled “Mejores prácticas”](#mejores-prácticas) 1. **Solicite los permisos adecuados** * Utilice “Cuando esté en uso” para funciones de primer plano * Solicite “Siempre” solo si necesita monitoreo en segundo plano * Explique claramente por qué necesita acceso a la ubicación.2. **Manejar el estado de Bluetooth** ```typescript const { enabled } = await CapacitorIbeacon.isBluetoothEnabled(); if (!enabled) { // Prompt user to enable Bluetooth } ``` 2. **Optimización de la batería** * Use monitoreo en lugar de rango cuando sea posible (más eficiencia de batería) * Dejar de realizar rangos cuando no sea necesario activamente * Considere el uso de rangos mayores/menores más grandes para reducir el procesamiento 3. **Manejo de errores** ```typescript try { await CapacitorIbeacon.startMonitoringForRegion(region); } catch (error) { console.error('Failed to start monitoring:', error); } ``` 4. **Limpiar a los oyentes** Elimine siempre los detectores de eventos cuando el componente se desmonte para evitar pérdidas de memoria. ## Notas de plataforma [Section titled “Notas de plataforma”](#notas-de-plataforma) ### iOS [Section titled “iOS”](#ios) * Requiere iOS 10.0+ * Utiliza el marco nativo CoreLocation * Admite monitoreo en segundo plano con permiso “Siempre” * Puede anunciarse como iBeacon usando CoreBluetooth * El rango requiere que la aplicación esté en primer plano ### Android [Section titled “Android”](#android) * Requiere Android 6.0 (API 23)+ * Utiliza la biblioteca AltBeacon * Requiere permisos de ubicación aunque las balizas usen Bluetooth * La supervisión en segundo plano requiere ACCESS\_BACKGROUND\_LOCATION (Android 10+) * No se puede anunciar como iBeacon (limitación de hardware en la mayoría de los dispositivos) ### Web [Section titled “Web”](#web) * No compatible con la plataforma web ## Casos de uso comunes [Section titled “Casos de uso comunes”](#casos-de-uso-comunes) 1. **Marketing de proximidad**: active notificaciones o contenido cuando los usuarios se acerquen a su tienda. 2. **Navegación interior**: guía a los usuarios a través de edificios utilizando puntos de referencia de balizas 3. **Seguimiento de asistencia**: regístrese automáticamente cuando los usuarios ingresan a una ubicación 4. **Seguimiento de activos**: monitorear el movimiento de equipos o inventario 5. **Visitas a museos**: proporcione información contextual a medida que los visitantes se acerquen a las exhibiciones. 6. **Hogar inteligente**: activa automatizaciones según la presencia de la habitación ## Solución de problemas [Section titled “Solución de problemas”](#solución-de-problemas) ### Balizas no detectadas [Section titled “Balizas no detectadas”](#balizas-no-detectadas) * Asegúrese de que Bluetooth esté habilitado * Verificar que se concedan los permisos de ubicación * Verifique que el UUID de la baliza coincida exactamente (distingue entre mayúsculas y minúsculas) * Confirmar que la baliza esté encendida y transmitiendo. * Pruebe primero sin filtros mayores/menores ### La supervisión en segundo plano no funciona [Section titled “La supervisión en segundo plano no funciona”](#la-supervisión-en-segundo-plano-no-funciona) * Asegúrese de que se conceda el permiso de ubicación “Siempre” * Agregue `location` a UIBackgroundModes (iOS) * Solicitar ACCESS\_BACKGROUND\_LOCATION (Android 10+) * Nota: iOS puede retrasar las devoluciones de llamadas en segundo plano para ahorrar batería ### Mediciones de distancia inexactas [Section titled “Mediciones de distancia inexactas”](#mediciones-de-distancia-inexactas) * La baliza RSSI varía según el entorno (paredes, interferencias) * Utilice múltiples balizas para la triangulación. * Calibrar la potencia medida a 1 metro de la baliza. * Habilite el filtrado ARMA en Android para valores más suaves # @Capgo/inappbrowser > Abre contenido web en tu aplicación con un navegador integrado completo que soporta eventos de cambio de URL y controles de navegación. Eventos de cambio de URL Rastrea la navegación con listeners de eventos de cambio de URL 🔗 Control completo de navegación Controles de atrás, adelante, recargar y cerrar 🎮 UI personalizable Color de barra de herramientas, botones y opciones de visualización 🎨 Documentación Completa Consulta la [Documentación](/docs/plugins/inappbrowser/getting-started/) para dominar el Plugin en solo unos minutos. # Empezando 1. **Instalar el paquete** * npm ```sh npm i @capgo/inappbrowser ``` * pnpm ```sh pnpm add @capgo/inappbrowser ``` * yarn ```sh yarn add @capgo/inappbrowser ``` * bun ```sh bun add @capgo/inappbrowser ``` 2. **Sincronización con proyectos nativos** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Uso [Section titled “Uso”](#uso) Importe el complemento y utilice sus dos puntos de entrada principales: ```typescript import { InAppBrowser, ToolBarType, BackgroundColor } from '@capgo/inappbrowser'; // 1) Simple custom tab / SFSafariViewController const openExternal = async () => { await InAppBrowser.open({ url: 'https://capgo.app', isPresentAfterPageLoad: true, preventDeeplink: false, }); }; // 2) Full WebView with navigation, headers, share, messaging, etc. const openWebView = async () => { await InAppBrowser.openWebView({ url: 'https://capgo.app', title: 'Capgo', toolbarType: ToolBarType.NAVIGATION, backgroundColor: BackgroundColor.BLACK, activeNativeNavigationForWebview: true, showReloadButton: true, shareSubject: 'Check this page', shareDisclaimer: { title: 'Disclaimer', message: 'You are about to share content', confirmBtn: 'Continue', cancelBtn: 'Cancel', }, }); }; // Messaging between app and the opened WebView const setupListeners = async () => { await InAppBrowser.addListener('urlChangeEvent', (event) => { console.log('URL changed to:', event.url); }); await InAppBrowser.addListener('messageFromWebview', (event) => { console.log('Message from web:', event.detail); }); await InAppBrowser.addListener('closeEvent', () => { console.log('WebView closed'); }); }; // Send data to the WebView const sendData = async () => { await InAppBrowser.postMessage({ detail: { action: 'refresh-profile' } }); }; // Close and reload helpers const closeBrowser = () => InAppBrowser.close(); const reloadPage = () => InAppBrowser.reload(); ``` ## API Referencia [Section titled “API Referencia”](#api-referencia) ### abierto(opciones: OpenOptions) [Section titled “abierto(opciones: OpenOptions)”](#abiertoopciones-openoptions) Abre una URL en una pestaña personalizada/SFSafariViewController. ```typescript interface OpenOptions { /** Target URL to load */ url: string; /** Present after the page finishes loading */ isPresentAfterPageLoad?: boolean; /** Prevent deep links from opening external apps */ preventDeeplink?: boolean; } await InAppBrowser.open({ url: 'https://example.com', preventDeeplink: true }); ``` ### openWebView (opciones: OpenWebViewOptions) [Section titled “openWebView (opciones: OpenWebViewOptions)”](#openwebview-opciones-openwebviewoptions) Carga un WebView con todas las funciones con interfaz de usuario de navegación, encabezados, credenciales, secuencias de comandos y mensajes. ```typescript interface OpenWebViewOptions { url: string; headers?: Record; credentials?: { username: string; password: string }; materialPicker?: boolean; shareDisclaimer?: { title: string; message: string; confirmBtn: string; cancelBtn: string; }; toolbarType?: ToolBarType; shareSubject?: string; title?: string; backgroundColor?: BackgroundColor; activeNativeNavigationForWebview?: boolean; disableGoBackOnNativeApplication?: boolean; isPresentAfterPageLoad?: boolean; isInspectable?: boolean; isAnimated?: boolean; showReloadButton?: boolean; closeModal?: boolean; closeModalTitle?: string; closeModalDescription?: string; closeModalOk?: string; closeModalCancel?: string; visibleTitle?: boolean; toolbarColor?: string; toolbarTextColor?: string; showArrow?: boolean; ignoreUntrustedSSLError?: boolean; preShowScript?: string; preShowScriptInjectionTime?: 'documentStart' | 'pageLoad'; proxyRequests?: string; buttonNearDone?: { ios: { iconType: 'sf-symbol' | 'asset'; icon: string }; android: { iconType: 'asset' | 'vector'; icon: string; width?: number; height?: number }; }; textZoom?: number; preventDeeplink?: boolean; authorizedAppLinks?: string[]; enabledSafeBottomMargin?: boolean; useTopInset?: boolean; enableGooglePaySupport?: boolean; blockedHosts?: string[]; width?: number; height?: number; x?: number; y?: number; } await InAppBrowser.openWebView({ url: 'https://new-page.com', toolbarType: ToolBarType.NAVIGATION, showReloadButton: true, }); ``` Valores de `ToolBarType`: `activity` (cerrar + compartir), `compact` (solo cerrar), `navigation` (atrás/adelante + cerrar), `blank` (sin barra de herramientas). Valores `BackgroundColor`: `white` o `black`. ### cerrar(¿opciones?) [Section titled “cerrar(¿opciones?)”](#cerraropciones) Cierra la pestaña WebView/personalizado. **Opciones:** * `isAnimated?: boolean` - Si se anima la acción de cierre ### recargar() [Section titled “recargar()”](#recargar) Vuelve a cargar la página WebView actual. ### volver() [Section titled “volver()”](#volver) Vuelve al historial de WebView y devuelve `{ canGoBack: boolean }`. ### `setUrl({ url: string })` [Section titled “setUrl({ url: string })”](#seturl-url-string) Reemplaza la URL de WebView actual. ### `executeScript({ code: string })` [Section titled “executeScript({ code: string })”](#executescript-code-string) Inyecta JavaScript en WebView. ### `postMessage({ detail: Record })` [Section titled “postMessage({ detail: Record\ })”](#postmessage-detail-recordstring-any) Envía datos desde la aplicación nativa a WebView (recibirlos en JS a través de `window.addEventListener('messageFromNative', ...)`). ### `getCookies({ url, includeHttpOnly? })` [Section titled “getCookies({ url, includeHttpOnly? })”](#getcookies-url-includehttponly) Devuelve cookies para la URL. ### `clearCookies({ url })` / clearAllCookies() / clearCache() [Section titled “clearCookies({ url }) / clearAllCookies() / clearCache()”](#clearcookies-url---clearallcookies--clearcache) Ayudantes de gestión de cookies y caché. ### actualizarDimensiones(opciones: DimensionOptions) [Section titled “actualizarDimensiones(opciones: DimensionOptions)”](#actualizardimensionesopciones-dimensionoptions) Cambie el tamaño/posición de WebView en tiempo de ejecución (`width`, `height`, `x`, `y`). ### eliminarTodos los oyentes() [Section titled “eliminarTodos los oyentes()”](#eliminartodos-los-oyentes) Anule el registro de todos los oyentes del complemento. ## Eventos [Section titled “Eventos”](#eventos) ### Evento de cambio de URL [Section titled “Evento de cambio de URL”](#evento-de-cambio-de-url) Se activa cuando la URL cambia en el navegador. ```typescript interface UrlChangeEvent { url: string; } InAppBrowser.addListener('urlChangeEvent', (event) => { console.log('New URL:', event.url); }); ``` ### messageFromWebview [Section titled “messageFromWebview”](#messagefromwebview) Se activa cuando se llama a `window.mobileApp.postMessage(...)` dentro de WebView. ```typescript InAppBrowser.addListener('messageFromWebview', (event) => { console.log('Payload from web:', event.detail); }); ``` ### cerrarEvento [Section titled “cerrarEvento”](#cerrarevento) Se activa cuando se cierra el navegador. ```typescript InAppBrowser.addListener('closeEvent', () => { console.log('Browser closed'); }); ``` ### botónCerca de hechoClic [Section titled “botónCerca de hechoClic”](#botóncerca-de-hechoclic) Se activa cuando se presiona el botón personalizado agregado con `buttonNearDone`. ```typescript InAppBrowser.addListener('buttonNearDoneClick', (event) => { console.log('Button near done tapped', event); }); ``` ### confirmarBtnClicked [Section titled “confirmarBtnClicked”](#confirmarbtnclicked) Se activa cuando se acepta un cuadro de diálogo de confirmación (exención de responsabilidad o modo de cierre). ```typescript InAppBrowser.addListener('confirmBtnClicked', (event) => { console.log('Confirm accepted, current URL:', event.url); }); ``` ### navegadorPageLoaded / pageLoadError [Section titled “navegadorPageLoaded / pageLoadError”](#navegadorpageloaded--pageloaderror) Eventos del ciclo de vida para el éxito o el fracaso de la carga de WebView. ```typescript InAppBrowser.addListener('browserPageLoaded', () => console.log('Page loaded')); InAppBrowser.addListener('pageLoadError', () => console.log('Page failed to load')); ``` ## Uso avanzado [Section titled “Uso avanzado”](#uso-avanzado) ### OAuth Implementación de flujo [Section titled “OAuth Implementación de flujo”](#oauth-implementación-de-flujo) ```typescript import { InAppBrowser } from '@capgo/inappbrowser'; export class OAuthService { private listeners: any[] = []; async authenticate(authUrl: string, redirectUri: string) { return new Promise((resolve, reject) => { // Listen for URL changes const urlListener = InAppBrowser.addListener('urlChangeEvent', (event) => { if (event.url.startsWith(redirectUri)) { // Extract OAuth code/token from URL const url = new URL(event.url); const code = url.searchParams.get('code'); if (code) { InAppBrowser.close(); resolve(code); } else { const error = url.searchParams.get('error'); reject(new Error(error || 'OAuth failed')); } } }); this.listeners.push(urlListener); // Open OAuth provider InAppBrowser.open({ url: authUrl, preventDeeplink: true, }); }); } cleanup() { this.listeners.forEach(listener => listener.remove()); this.listeners = []; } } ``` ### Interfaz de usuario personalizada del navegador [Section titled “Interfaz de usuario personalizada del navegador”](#interfaz-de-usuario-personalizada-del-navegador) ```typescript const openCustomBrowser = async () => { await InAppBrowser.open({ url: 'https://example.com', isPresentAfterPageLoad: true, preventDeeplink: false, }); }; ``` ### Manejo de enlaces externos [Section titled “Manejo de enlaces externos”](#manejo-de-enlaces-externos) ```typescript import { InAppBrowser } from '@capgo/inappbrowser'; export class LinkHandler { async openExternalLink(url: string) { // Check if URL should open in browser if (this.shouldOpenInBrowser(url)) { await InAppBrowser.open({ url, preventDeeplink: true, }); } else { // Handle internally window.location.href = url; } } private shouldOpenInBrowser(url: string): boolean { // External domains const externalDomains = ['youtube.com', 'twitter.com', 'facebook.com']; const urlDomain = new URL(url).hostname; return externalDomains.some(domain => urlDomain.includes(domain)); } } ``` ## Mejores prácticas [Section titled “Mejores prácticas”](#mejores-prácticas) 1. **Eliminar siempre a los oyentes** ```typescript const listener = await InAppBrowser.addListener('urlChangeEvent', handler); // When done listener.remove(); ``` 2. **Manejar estados del navegador** ```typescript let browserOpen = false; const launch = async () => { browserOpen = true; await InAppBrowser.openWebView({ url: 'https://example.com' }); }; InAppBrowser.addListener('closeEvent', () => { browserOpen = false; }); ``` 3. **Valide las URL antes de abrirlas** ```typescript const isValidUrl = (url: string): boolean => { try { new URL(url); return true; } catch { return false; } }; if (isValidUrl(url)) { await InAppBrowser.open({ url }); } ``` 4. **Personalizar según la plataforma** ```typescript import { Capacitor } from '@capacitor/core'; const options = { url: 'https://example.com', preventDeeplink: Capacitor.getPlatform() === 'ios', }; ``` ## Notas de plataforma [Section titled “Notas de plataforma”](#notas-de-plataforma) ### iOS [Section titled “iOS”](#ios) * Utiliza `SFSafariViewController` * Soporta iOS 11.0+ * Respeta las inserciones del Área Segura * Admite estilos de presentación personalizados ### Android [Section titled “Android”](#android) * Utiliza pestañas personalizadas de Chrome * Vuelve a WebView si Chrome no está disponible * Soporta Android 5.0 (API 21)+ * Personalización de la barra de herramientas compatible a través de `toolbarType`, `toolbarColor`, `buttonNearDone`, etc. ### Web [Section titled “Web”](#web) * Se abre en una nueva pestaña/ventana del navegador * Opciones de personalización limitadas * No hay eventos de cambio de URL # @Capgo/Capacitor-is-root > Detecta dispositivos Android rooteados y dispositivos iOS con jailbreak para mejorar la seguridad de la Aplicación y proteger datos sensibles. ## Descripción General [Section titled “Descripción General”](#descripción-general) El Plugin Capacitor Is Root proporciona detección completa de root y jailbreak para dispositivos Android y detección de emuladores. Este Plugin ayuda a mejorar la seguridad de la Aplicación identificando dispositivos comprometidos y entornos emulados que pueden representar riesgos de seguridad. Detección de root Detección avanzada de root en Android con múltiples métodos 🔒 Detección de emuladores Identifica entornos emulados y frameworks de prueba 🛡️ Validación de seguridad Múltiples técnicas de detección para mayor precisión ✅ Enfocado en Android Detección especializada para evaluación de seguridad en Android 🤖 ## Instalación [Section titled “Instalación”](#instalación) ```bash npm install @capgo/capacitor-is-root npx cap sync ``` ## Métodos Principales de la API [Section titled “Métodos Principales de la API”](#métodos-principales-de-la-api) ### Detección de Root [Section titled “Detección de Root”](#detección-de-root) * `isRooted()` - Realiza detección completa de root usando métodos predeterminados * `isRootedWithBusyBox()` - Detección extendida incluyendo verificaciones de BusyBox * `detectRootManagementApps()` - Identifica aplicaciones de gestión de root instaladas * `checkForSuBinary()` - Verifica la presencia del binario `su` en rutas del sistema ### Detección de Emuladores [Section titled “Detección de Emuladores”](#detección-de-emuladores) * `isRunningOnEmulator()` - Detecta huellas comunes de emuladores Android ## Técnicas de Detección [Section titled “Técnicas de Detección”](#técnicas-de-detección) El Plugin emplea múltiples métodos de detección: ### Detección de Root [Section titled “Detección de Root”](#detección-de-root-1) * Verifica aplicaciones de gestión de root (SuperSU, Magisk, etc.) * Escanea propiedades sospechosas del sistema * Identifica etiquetas de compilación de prueba y banderas de depuración * Valida ubicaciones de binarios peligrosos * Examina permisos de rutas del sistema ### Detección de Emuladores [Section titled “Detección de Emuladores”](#detección-de-emuladores-1) * Análisis de huella de hardware * Inspección de propiedades de compilación * Características específicas de emuladores * Indicadores de entorno virtual ## Ejemplo de Uso [Section titled “Ejemplo de Uso”](#ejemplo-de-uso) ```typescript import { IsRoot } from '@capgo/capacitor-is-root'; // Detección básica de root const rootResult = await IsRoot.isRooted(); if (rootResult.isRooted) { console.log('El dispositivo está rooteado'); // Maneja el dispositivo rooteado apropiadamente } // Detección extendida de root con BusyBox const extendedResult = await IsRoot.isRootedWithBusyBox(); if (extendedResult.isRooted) { console.log('El dispositivo está rooteado (verificación extendida)'); } // Verificar emulador const emulatorResult = await IsRoot.isRunningOnEmulator(); if (emulatorResult.isEmulator) { console.log('Ejecutándose en un emulador'); } // Detectar aplicaciones de gestión de root const rootAppsResult = await IsRoot.detectRootManagementApps(); if (rootAppsResult.hasRootApps) { console.log('Se detectaron aplicaciones de gestión de root'); } ``` ## Consideraciones de Seguridad [Section titled “Consideraciones de Seguridad”](#consideraciones-de-seguridad) * Usar múltiples métodos de detección para mayor precisión * Implementar degradación gradual para entornos detectados * Considerar la privacidad del usuario al implementar medidas de seguridad * Se recomiendan actualizaciones regulares a medida que evolucionan los métodos de detección ## Documentación [Section titled “Documentación”](#documentación) Consulta la [documentación completa](/docs/plugins/is-root/getting-started/) para guías de implementación detalladas y patrones de seguridad avanzados. # Comenzando > Aprende cómo instalar y usar el Plugin Is Root para detectar dispositivos Android rooteados y emuladores para mayor seguridad de la Aplicación. ## Instalación [Section titled “Instalación”](#instalación) * npm ```bash npm install @capgo/capacitor-is-root npx cap sync ``` * yarn ```bash yarn add @capgo/capacitor-is-root npx cap sync ``` * pnpm ```bash pnpm add @capgo/capacitor-is-root npx cap sync ``` * bun ```bash bun add @capgo/capacitor-is-root npx cap sync ``` ## Soporte de Plataformas [Section titled “Soporte de Plataformas”](#soporte-de-plataformas) * **Android**: Soporte completo para detección de root y emuladores * **iOS**: No se requiere configuración (el Plugin está enfocado en Android) ## Ejemplo de Uso [Section titled “Ejemplo de Uso”](#ejemplo-de-uso) ```typescript import { IsRoot } from '@capgo/capacitor-is-root'; // Detección básica de root const rootResult = await IsRoot.isRooted(); if (rootResult.isRooted) { console.log('El dispositivo está rooteado'); // Maneja el dispositivo rooteado apropiadamente // Ejemplo: Mostrar advertencia, limitar funcionalidad o bloquear acceso } // Detección extendida de root con BusyBox const extendedResult = await IsRoot.isRootedWithBusyBox(); if (extendedResult.isRooted) { console.log('El dispositivo está rooteado (verificación extendida)'); } // Verificar emulador const emulatorResult = await IsRoot.isRunningOnEmulator(); if (emulatorResult.isEmulator) { console.log('Ejecutándose en un emulador'); // Manejar entorno de emulador } // Detectar aplicaciones de gestión de root const rootAppsResult = await IsRoot.detectRootManagementApps(); if (rootAppsResult.hasRootApps) { console.log('Se detectaron aplicaciones de gestión de root'); } // Verificar binario su const suResult = await IsRoot.checkForSuBinary(); if (suResult.hasSu) { console.log('Se encontró binario SU en el dispositivo'); } ``` ## Referencia de la API [Section titled “Referencia de la API”](#referencia-de-la-api) ### isRooted() [Section titled “isRooted()”](#isrooted) ```typescript isRooted() => Promise<{ isRooted: boolean }> ``` Realiza una detección completa de root usando métodos predeterminados. **Retorna:** `Promise<{ isRooted: boolean }>` ### isRootedWithBusyBox() [Section titled “isRootedWithBusyBox()”](#isrootedwithbusybox) ```typescript isRootedWithBusyBox() => Promise<{ isRooted: boolean }> ``` Detección extendida de root incluyendo verificaciones de BusyBox. **Retorna:** `Promise<{ isRooted: boolean }>` ### detectRootManagementApps() [Section titled “detectRootManagementApps()”](#detectrootmanagementapps) ```typescript detectRootManagementApps() => Promise<{ hasRootApps: boolean }> ``` Identifica aplicaciones de gestión de root instaladas (SuperSU, Magisk, etc.). **Retorna:** `Promise<{ hasRootApps: boolean }>` ### checkForSuBinary() [Section titled “checkForSuBinary()”](#checkforsubinary) ```typescript checkForSuBinary() => Promise<{ hasSu: boolean }> ``` Verifica la presencia del binario `su` en rutas del sistema. **Retorna:** `Promise<{ hasSu: boolean }>` ### isRunningOnEmulator() [Section titled “isRunningOnEmulator()”](#isrunningonemulator) ```typescript isRunningOnEmulator() => Promise<{ isEmulator: boolean }> ``` Detecta huellas comunes de emuladores Android. **Retorna:** `Promise<{ isEmulator: boolean }>` ## Verificación de Seguridad Completa [Section titled “Verificación de Seguridad Completa”](#verificación-de-seguridad-completa) ```typescript import { IsRoot } from '@capgo/capacitor-is-root'; async function performSecurityCheck() { const checks = { rooted: false, emulator: false, rootApps: false, suBinary: false }; try { // Ejecutar todos los métodos de detección const [rootResult, emulatorResult, rootAppsResult, suResult] = await Promise.all([ IsRoot.isRootedWithBusyBox(), IsRoot.isRunningOnEmulator(), IsRoot.detectRootManagementApps(), IsRoot.checkForSuBinary() ]); checks.rooted = rootResult.isRooted; checks.emulator = emulatorResult.isEmulator; checks.rootApps = rootAppsResult.hasRootApps; checks.suBinary = suResult.hasSu; // Determinar nivel de seguridad const securityIssues = Object.values(checks).filter(v => v).length; if (securityIssues > 0) { console.warn(`El dispositivo tiene ${securityIssues} problema(s) de seguridad`, checks); return { secure: false, issues: checks }; } return { secure: true, issues: checks }; } catch (error) { console.error('Falló la verificación de seguridad:', error); throw error; } } // Usar en tu app const securityStatus = await performSecurityCheck(); if (!securityStatus.secure) { // Manejar dispositivo inseguro showSecurityWarning(securityStatus.issues); } ``` ## Técnicas de Detección [Section titled “Técnicas de Detección”](#técnicas-de-detección) ### Detección de Root [Section titled “Detección de Root”](#detección-de-root) El Plugin emplea múltiples métodos de detección: * Verifica aplicaciones de gestión de root (SuperSU, Magisk, KingRoot, etc.) * Escanea propiedades sospechosas del sistema * Identifica etiquetas de compilación de prueba y banderas de depuración * Valida ubicaciones de binarios peligrosos * Examina permisos de rutas del sistema * Detecta aplicaciones conocidas de ocultación de root ### Detección de Emuladores [Section titled “Detección de Emuladores”](#detección-de-emuladores) * Análisis de huella de hardware * Inspección de propiedades de compilación * Características específicas de emuladores * Indicadores de entorno virtual ## Manejo de Problemas de Seguridad [Section titled “Manejo de Problemas de Seguridad”](#manejo-de-problemas-de-seguridad) ```typescript import { IsRoot } from '@capgo/capacitor-is-root'; async function handleDeviceSecurity() { const rootResult = await IsRoot.isRooted(); if (rootResult.isRooted) { // Opción 1: Mostrar advertencia y continuar showWarning('Tu dispositivo parece estar rooteado. Algunas funciones pueden estar limitadas.'); // Opción 2: Limitar funcionalidad disableSensitiveFeatures(); // Opción 3: Bloquear acceso a la app showBlockedScreen('Esta app no puede ejecutarse en dispositivos rooteados por razones de seguridad.'); return false; } return true; } function showWarning(message: string) { // Mostrar diálogo de advertencia amigable alert(message); } function disableSensitiveFeatures() { // Desactivar procesamiento de pagos, acceso a datos sensibles, etc. console.log('Funciones sensibles deshabilitadas debido a dispositivo rooteado'); } function showBlockedScreen(message: string) { // Mostrar pantalla de bloqueo y salir de la app alert(message); } ``` ## Mejores Prácticas [Section titled “Mejores Prácticas”](#mejores-prácticas) * Usar múltiples métodos de detección para mayor precisión * Implementar degradación gradual en lugar de bloquear el acceso completamente * Proporcionar comunicación clara al usuario sobre preocupaciones de seguridad * Considerar la experiencia del usuario al implementar medidas de seguridad * Mantener el Plugin actualizado a medida que evolucionan los métodos de detección * Probar en dispositivos tanto rooteados como no rooteados * Manejar fallos de detección con gracia ## Consideraciones de Seguridad [Section titled “Consideraciones de Seguridad”](#consideraciones-de-seguridad) * Ningún método de detección es 100% infalible * Los usuarios avanzados pueden eludir los mecanismos de detección * Usar en combinación con medidas de seguridad del lado del servidor * Considerar la privacidad del usuario al implementar verificaciones de seguridad * Seguir las directrices de la plataforma para implementaciones de seguridad * Se recomiendan actualizaciones regulares a medida que evolucionan las técnicas de ocultación de root ## Casos de Uso [Section titled “Casos de Uso”](#casos-de-uso) * **Aplicaciones bancarias y financieras**: Prevenir acceso en dispositivos comprometidos * **Contenido protegido con DRM**: Proteger material con derechos de autor * **Aplicaciones empresariales**: Aplicar políticas de seguridad BYOD * **Procesamiento de pagos**: Asegurar entorno de transacciones seguras * **Aplicaciones de datos sensibles**: Proteger información confidencial # @Capgo/ivs-player > Integra el reproductor Amazon IVS para streaming en vivo de latencia ultra baja con funciones interactivas en tus Aplicaciones Capacitor. Streaming de baja latencia Transmite con latencia subsegundo usando Amazon IVS 📡 Funciones interactivas Soporte para metadatos temporales e interacciones de espectadores 💬 Adaptación de calidad Cambio automático de calidad basado en condiciones de red 📶 Documentación Completa Consulta la [Documentación](/docs/plugins/ivs-player/getting-started/) para dominar el Plugin en solo unos minutos. # Empezando > Aprenda a instalar y configurar el complemento Capacitor IVS Player para integrar la transmisión de baja latencia de Amazon IVS en su aplicación. 1. **Instalar el paquete** * npm ```sh npm i @capgo/ivs-player ``` * pnpm ```sh pnpm add @capgo/ivs-player ``` * yarn ```sh yarn add @capgo/ivs-player ``` * bun ```sh bun add @capgo/ivs-player ``` 2. **Sincronización con proyectos nativos** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` 3. **Configurar el complemento** **Configuración básica del reproductor:** ```typescript import { IvsPlayer } from '@capgo/ivs-player'; // Create a player const { playerId } = await IvsPlayer.create({ url: 'https://your-ivs-playback-url.m3u8', autoplay: true }); // Start playback await IvsPlayer.play({ playerId }); ``` **Controles del jugador:** ```typescript // Pause playback await IvsPlayer.pause({ playerId }); // Set volume await IvsPlayer.setVolume({ playerId, volume: 0.5 // 0.0 to 1.0 }); // Seek to position await IvsPlayer.seekTo({ playerId, position: 30 // seconds }); ``` * iOS Agregue a su `Info.plist`: ```xml NSAppTransportSecurity NSAllowsArbitraryLoads ``` * Android Agregue a su `AndroidManifest.xml`: ```xml ``` 4. **Manejo de eventos** ```typescript import { IvsPlayer } from '@capgo/ivs-player'; // Listen for player events IvsPlayer.addListener('onState', (event) => { console.log('Player state:', event.state); // States: idle, buffering, ready, playing, ended }); IvsPlayer.addListener('onError', (event) => { console.error('Player error:', event.error); }); IvsPlayer.addListener('onDuration', (event) => { console.log('Video duration:', event.duration); }); IvsPlayer.addListener('onProgress', (event) => { console.log('Current position:', event.position); }); // Listen for timed metadata IvsPlayer.addListener('onMetadata', (event) => { console.log('Timed metadata:', event.metadata); }); ``` 5. **Uso avanzado** ```typescript import { IvsPlayer } from '@capgo/ivs-player'; export class StreamPlayer { private playerId: string | null = null; private listeners: any[] = []; async initialize(streamUrl: string) { // Create player with custom configuration const result = await IvsPlayer.create({ url: streamUrl, autoplay: false, muted: true, // Start muted for autoplay policies controls: true }); this.playerId = result.playerId; this.setupEventListeners(); } private setupEventListeners() { // State changes const stateListener = IvsPlayer.addListener('onState', (event) => { this.handleStateChange(event.state); }); this.listeners.push(stateListener); // Quality changes const qualityListener = IvsPlayer.addListener('onQuality', (event) => { console.log(`Quality changed to: ${event.quality.name}`); }); this.listeners.push(qualityListener); // Buffer updates const bufferListener = IvsPlayer.addListener('onBuffer', (event) => { console.log(`Buffer: ${event.percentage}%`); }); this.listeners.push(bufferListener); } private handleStateChange(state: string) { switch (state) { case 'playing': console.log('Stream is playing'); break; case 'buffering': console.log('Stream is buffering'); break; case 'ended': console.log('Stream ended'); break; } } async play() { if (this.playerId) { await IvsPlayer.play({ playerId: this.playerId }); } } async pause() { if (this.playerId) { await IvsPlayer.pause({ playerId: this.playerId }); } } async setQuality(qualityName: string) { if (this.playerId) { await IvsPlayer.setQuality({ playerId: this.playerId, quality: qualityName }); } } async destroy() { // Remove listeners this.listeners.forEach(listener => listener.remove()); // Destroy player if (this.playerId) { await IvsPlayer.destroy({ playerId: this.playerId }); } } } ``` ## API Referencia [Section titled “API Referencia”](#api-referencia) ### Métodos [Section titled “Métodos”](#métodos) #### `create(options: CreateOptions)` [Section titled “create(options: CreateOptions)”](#createoptions-createoptions) Cree una nueva instancia de reproductor IVS. **Parámetros:** * `options`: Configuración del reproductor * `url`: cadena - URL de reproducción de IVS * `autoplay`: booleano - Iniciar reproducción automáticamente * `muted`: booleano - Inicio silenciado * `controls`: booleano - Mostrar controles nativos **Devoluciones:** `Promise<{ playerId: string }>` #### `play(options: PlayerOptions)` [Section titled “play(options: PlayerOptions)”](#playoptions-playeroptions) Inicie la reproducción. #### `pause(options: PlayerOptions)` [Section titled “pause(options: PlayerOptions)”](#pauseoptions-playeroptions) Pausar la reproducción. #### `stop(options: PlayerOptions)` [Section titled “stop(options: PlayerOptions)”](#stopoptions-playeroptions) Detenga la reproducción y restablezca la posición. #### `seekTo(options: SeekOptions)` [Section titled “seekTo(options: SeekOptions)”](#seektooptions-seekoptions) Buscar un puesto específico. #### `setVolume(options: VolumeOptions)` [Section titled “setVolume(options: VolumeOptions)”](#setvolumeoptions-volumeoptions) Establece el volumen del reproductor (0,0 a 1,0). #### `setQuality(options: QualityOptions)` [Section titled “setQuality(options: QualityOptions)”](#setqualityoptions-qualityoptions) Establece la calidad de reproducción. #### `destroy(options: PlayerOptions)` [Section titled “destroy(options: PlayerOptions)”](#destroyoptions-playeroptions) Destruye la instancia del jugador. ### Eventos [Section titled “Eventos”](#eventos) * `onState`: Cambios de estado del jugador * `onError`: Errores de reproducción * `onDuration`: Duración del vídeo disponible * `onProgress`: actualizaciones del progreso de la reproducción * `onQuality`: Cambios de calidad * `onMetadata`: Eventos de metadatos cronometrados * `onBuffer`: Actualizaciones de estado del búfer ### Interfaces [Section titled “Interfaces”](#interfaces) ```typescript interface CreateOptions { url: string; autoplay?: boolean; muted?: boolean; controls?: boolean; } interface PlayerOptions { playerId: string; } interface SeekOptions extends PlayerOptions { position: number; // seconds } interface VolumeOptions extends PlayerOptions { volume: number; // 0.0 to 1.0 } interface QualityOptions extends PlayerOptions { quality: string; // quality name } ``` ## Notas de plataforma [Section titled “Notas de plataforma”](#notas-de-plataforma) ### iOS [Section titled “iOS”](#ios) * Requiere iOS 12.0 o posterior * Utiliza AVPlayer nativo con extensiones IVS * Admite imagen en imagen en iPads ### Android [Section titled “Android”](#android) * Requiere Android 5.0 (API nivel 21) o posterior * Utiliza el reproductor Amazon IVS SDK * Se recomienda aceleración de hardware ## Casos de uso comunes [Section titled “Casos de uso comunes”](#casos-de-uso-comunes) 1. **Transmisión en vivo**: eventos, deportes y juegos en tiempo real 2. **Transmisión interactiva**: encuestas de espectadores, sesiones de preguntas y respuestas 3. **Comercio electrónico**: compras en vivo con productos destacados 4. **Educación**: Clases en vivo con contenido cronometrado 5. **Entretenimiento**: Conciertos, espectáculos con interacción del público. ## Mejores prácticas [Section titled “Mejores prácticas”](#mejores-prácticas) 1. **Manejo de red**: monitorear la calidad de la conexión ```typescript IvsPlayer.addListener('onError', (event) => { if (event.error.includes('network')) { // Handle network errors showRetryButton(); } }); ``` 2. **Gestión de recursos**: Destruye siempre a los jugadores cuando hayas terminado 3. **Políticas de reproducción automática**: inicio silenciado para una reproducción automática confiable 4. **Selección de calidad**: deje que IVS se encargue del cambio automático de calidad ## Solución de problemas [Section titled “Solución de problemas”](#solución-de-problemas) **La transmisión no se reproduce:** * Verifique que la URL de reproducción de IVS sea válida * Verificar permisos de red * Asegúrate de que la transmisión esté en vivo. **Alta latencia:** * IVS está optimizado para baja latencia, verifique la configuración de su codificador * Verifica que estás usando la URL de reproducción específica de IVS **Problemas de calidad:** * Permitir el cambio automático de calidad. * Verifique los requisitos de ancho de banda de la red # @Capgo/Capacitor-jw-player > Reproduce videos de JW Player con interfaz de pantalla completa, listas de reproducción y control completo de reproducción en tu Aplicación Capacitor. Reproductor de Pantalla Completa Interfaz de reproductor de video siempre en pantalla completa 📺 Soporte de Listas de Reproducción Soporta tanto videos individuales como listas de reproducción completas 📋 Controles de Reproducción Control completo sobre reproducir, pausar, buscar, velocidad 🎮 Audio y Subtítulos Selección de pistas de audio y soporte de subtítulos 🔊 Sistema de Eventos Listeners de eventos para cambios de estado del reproductor 📡 Comenzando Consulta la [Guía de Inicio](/docs/plugins/jw-player/getting-started/) para instalar y configurar el Plugin. # Comenzando > Aprende cómo instalar e integrar JW Player en tu Aplicación Capacitor para capacidades de streaming de video profesionales. ## Instalación [Section titled “Instalación”](#instalación) * npm ```bash npm install @capgo/capacitor-jw-player npx cap sync ``` * yarn ```bash yarn add @capgo/capacitor-jw-player npx cap sync ``` * pnpm ```bash pnpm add @capgo/capacitor-jw-player npx cap sync ``` * bun ```bash bun add @capgo/capacitor-jw-player npx cap sync ``` ## Requisitos Previos [Section titled “Requisitos Previos”](#requisitos-previos) Necesitarás una cuenta y clave de licencia de JW Player para usar este Plugin. Regístrate en [JW Player](https://www.jwplayer.com/) si no tienes una. ## Configuración de Plataforma [Section titled “Configuración de Plataforma”](#configuración-de-plataforma) ### iOS [Section titled “iOS”](#ios) Agrega tu clave de licencia de JW Player a tu `Info.plist`: ```xml JWPlayerLicenseKey YOUR_LICENSE_KEY ``` ### Android [Section titled “Android”](#android) Agrega tu clave de licencia de JW Player a `android/app/src/main/res/values/strings.xml`: ```xml YOUR_LICENSE_KEY ``` ## Ejemplo de Uso [Section titled “Ejemplo de Uso”](#ejemplo-de-uso) ```typescript import { JWPlayer } from '@capgo/capacitor-jw-player'; // Reproducir un solo video await JWPlayer.playVideo({ mediaUrl: 'https://example.com/video.mp4', title: 'Mi Video', description: 'Descripción del video', poster: 'https://example.com/poster.jpg' }); // Reproducir una lista de reproducción await JWPlayer.playPlaylist({ playlistUrl: 'https://example.com/playlist.json' }); // Escuchar eventos del reproductor JWPlayer.addListener('playerReady', () => { console.log('El reproductor está listo'); }); JWPlayer.addListener('play', (data) => { console.log('El video comenzó a reproducirse'); }); JWPlayer.addListener('pause', (data) => { console.log('Video pausado'); }); JWPlayer.addListener('complete', (data) => { console.log('Reproducción de video completada'); }); JWPlayer.addListener('error', (error) => { console.error('Error del reproductor:', error); }); ``` ## Referencia de la API [Section titled “Referencia de la API”](#referencia-de-la-api) ### playVideo(Opciones) [Section titled “playVideo(Opciones)”](#playvideoopciones) ```typescript playVideo(options: VideoOptions) => Promise ``` Reproduce un solo video en modo pantalla completa. | Param | Type | | ------------- | -------------- | | **`options`** | `VideoOptions` | ### playPlaylist(Opciones) [Section titled “playPlaylist(Opciones)”](#playplaylistopciones) ```typescript playPlaylist(options: PlaylistOptions) => Promise ``` Reproduce una lista de reproducción en modo pantalla completa. | Param | Type | | ------------- | ----------------- | | **`options`** | `PlaylistOptions` | ### pause() [Section titled “pause()”](#pause) ```typescript pause() => Promise ``` Pausar el video actual. ### play() [Section titled “play()”](#play) ```typescript play() => Promise ``` Reanudar la reproducción del video. ### seek(Opciones) [Section titled “seek(Opciones)”](#seekopciones) ```typescript seek(options: { position: number }) => Promise ``` Buscar una posición específica en el video. | Param | Type | | ------------- | ---------------------- | | **`options`** | `{ position: number }` | ### setPlaybackSpeed(Opciones) [Section titled “setPlaybackSpeed(Opciones)”](#setplaybackspeedopciones) ```typescript setPlaybackSpeed(options: { speed: number }) => Promise ``` Establecer la velocidad de reproducción. | Param | Type | | ------------- | ------------------- | | **`options`** | `{ speed: number }` | ### setVolume(Opciones) [Section titled “setVolume(Opciones)”](#setvolumeopciones) ```typescript setVolume(options: { volume: number }) => Promise ``` Establecer el volumen de audio (0.0 a 1.0). | Param | Type | | ------------- | -------------------- | | **`options`** | `{ volume: number }` | ### selectAudioTrack(Opciones) [Section titled “selectAudioTrack(Opciones)”](#selectaudiotrackopciones) ```typescript selectAudioTrack(options: { trackIndex: number }) => Promise ``` Seleccionar una pista de audio por índice. | Param | Type | | ------------- | ------------------------ | | **`options`** | `{ trackIndex: number }` | ### selectCaptionTrack(Opciones) [Section titled “selectCaptionTrack(Opciones)”](#selectcaptiontrackopciones) ```typescript selectCaptionTrack(options: { trackIndex: number }) => Promise ``` Seleccionar una pista de subtítulos por índice. | Param | Type | | ------------- | ------------------------ | | **`options`** | `{ trackIndex: number }` | ### stop() [Section titled “stop()”](#stop) ```typescript stop() => Promise ``` Detener la reproducción y cerrar el reproductor. ## Interfaces [Section titled “Interfaces”](#interfaces) ### VideoOptions [Section titled “VideoOptions”](#videooptions) | Prop | Type | Description | | ----------------- | --------- | --------------------------------------------------------------------- | | **`mediaUrl`** | `string` | URL del archivo de video | | **`title`** | `string` | Título del video (opcional) | | **`description`** | `string` | Descripción del video (opcional) | | **`poster`** | `string` | URL del póster/miniatura (opcional) | | **`autoStart`** | `boolean` | Iniciar reproducción automáticamente (opcional, predeterminado: true) | ### PlaylistOptions [Section titled “PlaylistOptions”](#playlistoptions) | Prop | Type | Description | | ----------------- | --------- | ----------------------------------------------- | | **`playlistUrl`** | `string` | URL del JSON de la lista de reproducción JW | | **`autoStart`** | `boolean` | Iniciar reproducción automáticamente (opcional) | ## Listeners de Eventos [Section titled “Listeners de Eventos”](#listeners-de-eventos) ### Eventos Disponibles [Section titled “Eventos Disponibles”](#eventos-disponibles) * `playerReady` - El reproductor está inicializado y listo * `play` - Reproducción de video iniciada * `pause` - Reproducción de video pausada * `complete` - Reproducción de video completada * `error` - El reproductor encontró un Error * `seek` - El usuario buscó una posición diferente * `time` - Tiempo de reproducción actualizado * `bufferChange` - Estado del buffer cambió ### Ejemplo de Evento [Section titled “Ejemplo de Evento”](#ejemplo-de-evento) ```typescript // Escuchar actualizaciones de tiempo JWPlayer.addListener('time', (data) => { console.log('Tiempo actual:', data.position); console.log('Duración:', data.duration); }); // Escuchar cambios de buffer JWPlayer.addListener('bufferChange', (data) => { console.log('Buffering:', data.buffering); }); // Eliminar listener cuando termines const listener = await JWPlayer.addListener('play', (data) => { console.log('Reproduciendo'); }); // Más tarde... listener.remove(); ``` ## Uso Avanzado [Section titled “Uso Avanzado”](#uso-avanzado) ### Reproducir con Subtítulos [Section titled “Reproducir con Subtítulos”](#reproducir-con-subtítulos) ```typescript await JWPlayer.playVideo({ mediaUrl: 'https://example.com/video.mp4', title: 'Video con Subtítulos', poster: 'https://example.com/poster.jpg', tracks: [ { file: 'https://example.com/captions-en.vtt', label: 'English', kind: 'captions' }, { file: 'https://example.com/captions-es.vtt', label: 'Spanish', kind: 'captions' } ] }); ``` ### Controles de Reproducción Personalizados [Section titled “Controles de Reproducción Personalizados”](#controles-de-reproducción-personalizados) ```typescript import { JWPlayer } from '@capgo/capacitor-jw-player'; // Comenzar a reproducir await JWPlayer.play(); // Pausar después de 10 segundos setTimeout(() => { JWPlayer.pause(); }, 10000); // Buscar a los 30 segundos await JWPlayer.seek({ position: 30 }); // Establecer velocidad de reproducción a 1.5x await JWPlayer.setPlaybackSpeed({ speed: 1.5 }); // Establecer volumen al 50% await JWPlayer.setVolume({ volume: 0.5 }); ``` ## Mejores Prácticas [Section titled “Mejores Prácticas”](#mejores-prácticas) * Siempre proporciona una imagen de póster para mejor experiencia del usuario * Maneja los errores del reproductor con mensajes de Error apropiados * Limpia los listeners de eventos cuando el componente se desmonte * Prueba la reproducción de video en dispositivos iOS y Android * Usa formatos de video apropiados (MP4 recomendado) * Implementa estados de carga mientras el video se inicializa * Considera las condiciones de red al transmitir ## Solución de Problemas [Section titled “Solución de Problemas”](#solución-de-problemas) ### El Video No Se Reproduce [Section titled “El Video No Se Reproduce”](#el-video-no-se-reproduce) * Verifica que tu clave de licencia de JW Player sea correcta * Verifica que la URL del video sea accesible y válida * Asegúrate de tener cabeceras CORS apropiadas si usas servidor personalizado * Prueba con diferentes formatos de video ### Problemas de Pantalla Completa [Section titled “Problemas de Pantalla Completa”](#problemas-de-pantalla-completa) * El Plugin siempre reproduce en modo pantalla completa * Asegúrate de tener permisos apropiados para pantalla completa en tu Aplicación ### Rendimiento [Section titled “Rendimiento”](#rendimiento) * Usa calidad de video apropiada para dispositivos objetivo * Considera streaming adaptativo para mejor rendimiento * Implementa indicadores de buffering apropiados # @Capgo/Capacitor-launch-navigator > Inicia aplicaciones de navegación con direcciones paso a paso, soportando múltiples proveedores de mapas y opciones de enrutamiento personalizadas. ## Descripción General [Section titled “Descripción General”](#descripción-general) El Plugin Capacitor Launch Navigator permite iniciar aplicaciones de navegación nativas en dispositivos iOS y Android con navegación basada en coordenadas. Este Plugin proporciona integración perfecta con aplicaciones de mapas populares y soporta múltiples modos de transporte para soluciones de navegación completas. Soporte multi-Aplicación Google Maps, Apple Maps, Waze, Citymapper y más 🗺️ Modos de transporte Direcciones en coche, a pie, en transporte público y en bicicleta 🧭 Detección de Aplicaciones Verifica disponibilidad y lista aplicaciones de navegación instaladas 🔍 Basado en coordenadas Navega usando coordenadas de latitud/longitud 📍 ## Instalación [Section titled “Instalación”](#instalación) ```bash npm install @capgo/capacitor-launch-navigator npx cap sync ``` ## Configuración de iOS [Section titled “Configuración de iOS”](#configuración-de-ios) Agrega esquemas de URL a tu `Info.plist` para detectar aplicaciones de navegación instaladas: ```xml LSApplicationQueriesSchemes googlemaps waze citymapper ``` ## Métodos Principales de la API [Section titled “Métodos Principales de la API”](#métodos-principales-de-la-api) ### Navegación [Section titled “Navegación”](#navegación) * `navigate(options)` - Iniciar navegación a coordenadas especificadas * `isAppAvailable(options)` - Verificar si una aplicación de navegación específica está instalada * `getAvailableApps()` - Listar todas las aplicaciones de navegación disponibles en el dispositivo * `getSupportedApps()` - Obtener todas las aplicaciones soportadas para la plataforma actual ## Ejemplo de Uso [Section titled “Ejemplo de Uso”](#ejemplo-de-uso) ```typescript import { LaunchNavigator, TransportMode } from '@capgo/capacitor-launch-navigator'; // Navegación básica a coordenadas await LaunchNavigator.navigate({ destination: [37.7749, -122.4194], // Coordenadas de San Francisco }); // Navegación avanzada con opciones await LaunchNavigator.navigate({ destination: [37.7749, -122.4194], options: { start: [37.7849, -122.4094], // Punto de inicio opcional transportMode: TransportMode.DRIVING, app: 'google_maps', // Aplicación de navegación preferida appDisplayName: 'Google Maps' } }); // Verificar si una aplicación específica está disponible const result = await LaunchNavigator.isAppAvailable({ app: 'google_maps' }); if (result.available) { console.log('Google Maps está instalado'); } else { console.log('Google Maps no está disponible'); } // Obtener todas las aplicaciones de navegación disponibles const availableApps = await LaunchNavigator.getAvailableApps(); console.log('Aplicaciones de navegación disponibles:', availableApps); // Obtener todas las aplicaciones soportadas para la plataforma const supportedApps = await LaunchNavigator.getSupportedApps(); console.log('Aplicaciones soportadas:', supportedApps); ``` ## Opciones de Navegación [Section titled “Opciones de Navegación”](#opciones-de-navegación) ```typescript interface NavigationOptions { start?: [number, number]; // Coordenadas de inicio [lat, lng] transportMode?: TransportMode; // Método de transporte app?: string; // Aplicación de navegación preferida appDisplayName?: string; // Nombre para mostrar de la app launchMode?: LaunchMode; // Cómo iniciar la app } ``` ## Modos de Transporte [Section titled “Modos de Transporte”](#modos-de-transporte) ```typescript enum TransportMode { DRIVING = 'driving', WALKING = 'walking', TRANSIT = 'transit', CYCLING = 'cycling' } ``` ## Aplicaciones de Navegación Soportadas [Section titled “Aplicaciones de Navegación Soportadas”](#aplicaciones-de-navegación-soportadas) ### iOS [Section titled “iOS”](#ios) * Apple Maps (integrado) * Google Maps * Waze * Citymapper * Transit * Moovit * Uber * Lyft * Y muchas más… ### Android [Section titled “Android”](#android) * Google Maps (integrado) * Waze * Citymapper * HERE WeGo * Sygic * MapQuest * Moovit * Y muchas más… ## Requisitos de Coordenadas [Section titled “Requisitos de Coordenadas”](#requisitos-de-coordenadas) > ⚠️ **Importante**: Este Plugin solo acepta coordenadas de latitud/longitud para navegación. Usa [@capgo/capacitor-nativegeocoder](https://github.com/Cap-go/capacitor-nativegeocoder) para convertir direcciones a coordenadas. ```typescript // Ejemplo con geocodificación import { NativeGeocoder } from '@capgo/capacitor-nativegeocoder'; // Convertir dirección a coordenadas const geocodeResult = await NativeGeocoder.forwardGeocode({ address: '1600 Amphitheatre Parkway, Mountain View, CA' }); if (geocodeResult.results.length > 0) { const coords = geocodeResult.results[0]; // Iniciar navegación con coordenadas geocodificadas await LaunchNavigator.navigate({ destination: [coords.latitude, coords.longitude] }); } ``` ## Detección y Selección de Aplicaciones [Section titled “Detección y Selección de Aplicaciones”](#detección-y-selección-de-aplicaciones) ```typescript // Verificar múltiples apps y usar la primera disponible const appsToCheck = ['google_maps', 'waze', 'apple_maps']; let selectedApp = null; for (const app of appsToCheck) { const result = await LaunchNavigator.isAppAvailable({ app }); if (result.available) { selectedApp = app; break; } } if (selectedApp) { await LaunchNavigator.navigate({ destination: [37.7749, -122.4194], options: { app: selectedApp } }); } else { console.log('No se encontraron aplicaciones de navegación soportadas'); } ``` ## Manejo de Errores [Section titled “Manejo de Errores”](#manejo-de-errores) ```typescript try { await LaunchNavigator.navigate({ destination: [37.7749, -122.4194], options: { app: 'google_maps', transportMode: TransportMode.DRIVING } }); } catch (error) { console.error('La navegación falló:', error); // Manejar error - app no disponible, coordenadas inválidas, etc. } ``` ## Casos de Uso [Section titled “Casos de Uso”](#casos-de-uso) * **Servicios basados en ubicación**: Navegar usuarios a puntos de interés * **Aplicaciones de entrega**: Guiar conductores a ubicaciones de entrega * **Aplicaciones de eventos**: Dirigir asistentes a ubicaciones de lugares * **Aplicaciones inmobiliarias**: Navegar a ubicaciones de propiedades * **Aplicaciones de viaje**: Guiar turistas a atracciones ## Mejores Prácticas [Section titled “Mejores Prácticas”](#mejores-prácticas) * Siempre verifica la disponibilidad de la Aplicación antes de intentar navegar * Proporciona opciones de respaldo cuando las Aplicaciones preferidas no estén disponibles * Usa nombres de visualización significativos para selección del usuario * Maneja errores con gracia con mensajes amigables para el usuario * Considera las preferencias del usuario para aplicaciones de navegación predeterminadas ## Documentación [Section titled “Documentación”](#documentación) Consulta la [documentación completa](/docs/plugins/launch-navigator/getting-started/) para guías de implementación detalladas y patrones de navegación avanzados. # Comenzando > Aprende cómo instalar y usar el Plugin Launch Navigator para integrar con aplicaciones de navegación nativas en iOS y Android. ## Instalación [Section titled “Instalación”](#instalación) * npm ```bash npm install @capgo/capacitor-launch-navigator npx cap sync ``` * yarn ```bash yarn add @capgo/capacitor-launch-navigator npx cap sync ``` * pnpm ```bash pnpm add @capgo/capacitor-launch-navigator npx cap sync ``` * bun ```bash bun add @capgo/capacitor-launch-navigator npx cap sync ``` ## Configuración de Plataforma [Section titled “Configuración de Plataforma”](#configuración-de-plataforma) ### iOS [Section titled “iOS”](#ios) Agrega esquemas de URL a tu `Info.plist` para detectar aplicaciones de navegación instaladas: ```xml LSApplicationQueriesSchemes googlemaps waze citymapper transit moovit uber lyft ``` ### Android [Section titled “Android”](#android) No se requiere configuración adicional. El Plugin detectará automáticamente las aplicaciones de navegación instaladas. ## Ejemplo de Uso [Section titled “Ejemplo de Uso”](#ejemplo-de-uso) ```typescript import { LaunchNavigator, TransportMode } from '@capgo/capacitor-launch-navigator'; // Navegación básica a coordenadas await LaunchNavigator.navigate({ destination: [37.7749, -122.4194], // Coordenadas de San Francisco }); // Navegación avanzada con opciones await LaunchNavigator.navigate({ destination: [37.7749, -122.4194], options: { start: [37.7849, -122.4094], // Punto de inicio opcional transportMode: TransportMode.DRIVING, app: 'google_maps', // Aplicación de navegación preferida appDisplayName: 'Google Maps' } }); // Verificar si una aplicación específica está disponible const result = await LaunchNavigator.isAppAvailable({ app: 'google_maps' }); if (result.available) { console.log('Google Maps está instalado'); } else { console.log('Google Maps no está disponible'); } // Obtener todas las aplicaciones de navegación disponibles const availableApps = await LaunchNavigator.getAvailableApps(); console.log('Aplicaciones de navegación disponibles:', availableApps); // Obtener todas las aplicaciones soportadas para la plataforma const supportedApps = await LaunchNavigator.getSupportedApps(); console.log('Aplicaciones soportadas:', supportedApps); ``` ## Referencia de la API [Section titled “Referencia de la API”](#referencia-de-la-api) ### navigate(Opciones) [Section titled “navigate(Opciones)”](#navigateopciones) ```typescript navigate(options: NavigateOptions) => Promise ``` Inicia la navegación a coordenadas especificadas. | Param | Type | | ------------- | ----------------- | | **`options`** | `NavigateOptions` | ### isAppAvailable(Opciones) [Section titled “isAppAvailable(Opciones)”](#isappavailableopciones) ```typescript isAppAvailable(options: { app: string }) => Promise<{ available: boolean }> ``` Verifica si una aplicación de navegación específica está instalada. | Param | Type | | ------------- | ----------------- | | **`options`** | `{ app: string }` | **Retorna:** `Promise<{ available: boolean }>` ### getAvailableApps() [Section titled “getAvailableApps()”](#getavailableapps) ```typescript getAvailableApps() => Promise<{ apps: string[] }> ``` Obtiene la lista de todas las aplicaciones de navegación disponibles en el dispositivo. **Retorna:** `Promise<{ apps: string[] }>` ### getSupportedApps() [Section titled “getSupportedApps()”](#getsupportedapps) ```typescript getSupportedApps() => Promise<{ apps: string[] }> ``` Obtiene la lista de todas las aplicaciones soportadas para la plataforma actual. **Retorna:** `Promise<{ apps: string[] }>` ## Interfaces [Section titled “Interfaces”](#interfaces) ### NavigateOptions [Section titled “NavigateOptions”](#navigateoptions) | Prop | Type | Description | | ----------------- | ------------------- | --------------------------------------------- | | **`destination`** | `[number, number]` | Coordenadas de destino \[lat, lng] | | **`options`** | `NavigationOptions` | Opciones de navegación adicionales (opcional) | ### NavigationOptions [Section titled “NavigationOptions”](#navigationoptions) | Prop | Type | Description | | -------------------- | ------------------ | ----------------------------------------------- | | **`start`** | `[number, number]` | Coordenadas de inicio \[lat, lng] (opcional) | | **`transportMode`** | `TransportMode` | Método de transporte (opcional) | | **`app`** | `string` | Aplicación de navegación preferida (opcional) | | **`appDisplayName`** | `string` | Nombre para mostrar de la Aplicación (opcional) | ### TransportMode [Section titled “TransportMode”](#transportmode) ```typescript enum TransportMode { DRIVING = 'driving', WALKING = 'walking', TRANSIT = 'transit', CYCLING = 'cycling' } ``` ## Requisito de Coordenadas [Section titled “Requisito de Coordenadas”](#requisito-de-coordenadas) > **Importante**: Este Plugin solo acepta coordenadas de latitud/longitud para navegación. Usa [@capgo/capacitor-nativegeocoder](https://github.com/Cap-go/capacitor-nativegeocoder) para convertir direcciones a coordenadas. ```typescript import { NativeGeocoder } from '@capgo/capacitor-nativegeocoder'; import { LaunchNavigator } from '@capgo/capacitor-launch-navigator'; // Convertir dirección a coordenadas const geocodeResult = await NativeGeocoder.forwardGeocode({ address: '1600 Amphitheatre Parkway, Mountain View, CA' }); if (geocodeResult.results.length > 0) { const coords = geocodeResult.results[0]; // Iniciar navegación con coordenadas geocodificadas await LaunchNavigator.navigate({ destination: [coords.latitude, coords.longitude] }); } ``` ## Aplicaciones de Navegación Soportadas [Section titled “Aplicaciones de Navegación Soportadas”](#aplicaciones-de-navegación-soportadas) ### iOS [Section titled “iOS”](#ios-1) * Apple Maps (integrado) * Google Maps * Waze * Citymapper * Transit * Moovit * Uber * Lyft * Y muchas más… ### Android [Section titled “Android”](#android-1) * Google Maps (integrado) * Waze * Citymapper * HERE WeGo * Sygic * MapQuest * Moovit * Y muchas más… ## Ejemplos Avanzados [Section titled “Ejemplos Avanzados”](#ejemplos-avanzados) ### Verificar Múltiples Aplicaciones y Usar la Primera Disponible [Section titled “Verificar Múltiples Aplicaciones y Usar la Primera Disponible”](#verificar-múltiples-aplicaciones-y-usar-la-primera-disponible) ```typescript const appsToCheck = ['google_maps', 'waze', 'apple_maps']; let selectedApp = null; for (const app of appsToCheck) { const result = await LaunchNavigator.isAppAvailable({ app }); if (result.available) { selectedApp = app; break; } } if (selectedApp) { await LaunchNavigator.navigate({ destination: [37.7749, -122.4194], options: { app: selectedApp } }); } else { console.log('No se encontraron aplicaciones de navegación soportadas'); } ``` ### Selección de Usuario de Aplicación de Navegación [Section titled “Selección de Usuario de Aplicación de Navegación”](#selección-de-usuario-de-aplicación-de-navegación) ```typescript // Obtener aplicaciones disponibles const { apps } = await LaunchNavigator.getAvailableApps(); if (apps.length === 0) { alert('No hay aplicaciones de navegación disponibles'); return; } // Mostrar selección de aplicación al usuario (pseudo-código) const selectedApp = await showAppSelectionDialog(apps); // Navegar con aplicación seleccionada await LaunchNavigator.navigate({ destination: [37.7749, -122.4194], options: { app: selectedApp } }); ``` ### Navegación con Diferentes Modos de Transporte [Section titled “Navegación con Diferentes Modos de Transporte”](#navegación-con-diferentes-modos-de-transporte) ```typescript // Direcciones en coche await LaunchNavigator.navigate({ destination: [37.7749, -122.4194], options: { transportMode: TransportMode.DRIVING } }); // Direcciones a pie await LaunchNavigator.navigate({ destination: [37.7749, -122.4194], options: { transportMode: TransportMode.WALKING } }); // Direcciones en transporte público await LaunchNavigator.navigate({ destination: [37.7749, -122.4194], options: { transportMode: TransportMode.TRANSIT } }); ``` ## Mejores Prácticas [Section titled “Mejores Prácticas”](#mejores-prácticas) * Siempre verifica la disponibilidad de la Aplicación antes de intentar navegar * Proporciona opciones de respaldo cuando las Aplicaciones preferidas no estén disponibles * Usa nombres de visualización significativos para selección del usuario * Maneja errores con gracia con mensajes amigables para el usuario * Considera las preferencias del usuario para aplicaciones de navegación predeterminadas * Prueba en dispositivos iOS y Android * Implementa manejo de errores apropiado para coordenadas inválidas ## Manejo de Errores [Section titled “Manejo de Errores”](#manejo-de-errores) ```typescript try { await LaunchNavigator.navigate({ destination: [37.7749, -122.4194], options: { app: 'google_maps', transportMode: TransportMode.DRIVING } }); } catch (error) { console.error('La navegación falló:', error); // Manejar error - app no disponible, coordenadas inválidas, etc. alert('No se pudo iniciar la navegación. Por favor verifica tus coordenadas e intenta nuevamente.'); } ``` ## Casos de Uso [Section titled “Casos de Uso”](#casos-de-uso) * **Servicios basados en ubicación**: Navegar usuarios a puntos de interés * **Aplicaciones de entrega**: Guiar conductores a ubicaciones de entrega * **Aplicaciones de eventos**: Dirigir asistentes a ubicaciones de lugares * **Aplicaciones inmobiliarias**: Navegar a ubicaciones de propiedades * **Aplicaciones de viaje**: Guiar turistas a atracciones * **Aplicaciones de servicio**: Dirigir trabajadores de campo a sitios de trabajo # @Capgo/Capacitor-live-reload > Conecta tu Aplicación Capacitor a tu servidor de desarrollo local para recarga en caliente instantánea e iteración más rápida durante el desarrollo. Reemplazo de Módulo en Caliente Actualizaciones instantáneas sin reconstruir tu Aplicación Servidor de Desarrollo Remoto Conecta a Vite o cualquier servidor de desarrollo compatible Conexión WebSocket Comunicación en tiempo real para actualizaciones en vivo Documentación Completa Consulta la [Documentación](/docs/plugins/live-reload/getting-started/) para configurar la recarga en vivo. # Comenzando > Aprende cómo configurar la recarga en vivo para un desarrollo más rápido con tu Aplicación Capacitor. 1. **Instalar el paquete** * npm ```sh npm i @Capgo/Capacitor-live-reload ``` * pnpm ```sh pnpm add @Capgo/Capacitor-live-reload ``` * yarn ```sh yarn add @Capgo/Capacitor-live-reload ``` * bun ```sh bun add @Capgo/Capacitor-live-reload ``` 2. **Sincronizar con proyectos nativos** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Configuración [Section titled “Configuración”](#configuración) Configura tu servidor de desarrollo Vite para trabajar con recarga en vivo. Necesitas: 1. Deshabilitar el cliente HMR integrado 2. Reenviar eventos de recarga sobre un punto final WebSocket dedicado ## Uso [Section titled “Uso”](#uso) ```typescript import { LiveReload } from '@capgo/capacitor-live-reload'; // Configurar el servidor de desarrollo await LiveReload.configureServer({ url: 'http://localhost:5173', websocketPath: '/capgo-livereload', autoReconnect: true, reconnectInterval: 2000 }); // Conectar al servidor de desarrollo await LiveReload.connect(); // Escuchar eventos de recarga LiveReload.addListener('reloadEvent', (event) => { console.log('Evento de recarga:', event); if (event.type === 'full-reload') { console.log('Recarga completa de página activada'); } else if (event.type === 'file-update') { console.log('Archivo actualizado:', event.file); } }); // Escuchar cambios de estado de conexión LiveReload.addListener('statusChange', (status) => { console.log('Estado de conexión:', status.connected); console.log('URL del servidor:', status.url); }); ``` ## Referencia de la API [Section titled “Referencia de la API”](#referencia-de-la-api) ### configureServer(Opciones) [Section titled “configureServer(Opciones)”](#configureserveropciones) Almacenar configuraciones del servidor de desarrollo remoto para conexiones posteriores. ```typescript const status = await LiveReload.configureServer({ url: 'http://192.168.1.100:5173', websocketPath: '/capgo-livereload', headers: { 'Authorization': 'Bearer token' }, autoReconnect: true, reconnectInterval: 2000 }); ``` **Parámetros:** * `url` (string): URL base para el servidor de desarrollo (ej., `http://dev.local:5173`) * `websocketPath` (string, opcional): Anulación de ruta WebSocket (predeterminado: `/ws`) * `headers` (Record\, opcional): Cabeceras extra para conexión WebSocket * `autoReconnect` (boolean, opcional): Reconectar automáticamente al desconectar (predeterminado: `true`) * `reconnectInterval` (number, opcional): Retraso entre intentos de reconexión en ms (predeterminado: `2000`) **Retorna:** `LiveReloadStatus` con información de conexión ### connect() [Section titled “connect()”](#connect) Establecer una conexión WebSocket si no hay una activa. ```typescript const status = await LiveReload.connect(); console.log('Conectado:', status.connected); ``` **Retorna:** Estado de conexión actual ### disconnect() [Section titled “disconnect()”](#disconnect) Cerrar la conexión WebSocket actual y deshabilitar reconexión automática. ```typescript await LiveReload.disconnect(); ``` ### getStatus() [Section titled “getStatus()”](#getstatus) Obtener el estado de conexión actual. ```typescript const status = await LiveReload.getStatus(); console.log('Conectado:', status.connected); console.log('URL:', status.url); ``` ### reload() [Section titled “reload()”](#reload) Activar manualmente una recarga completa del WebView de Capacitor. ```typescript await LiveReload.reload(); ``` ### reloadFile(Opciones) [Section titled “reloadFile(Opciones)”](#reloadfileopciones) Recargar un solo archivo/módulo (retrocede a recarga completa si no es compatible). ```typescript await LiveReload.reloadFile({ path: '/src/components/MyComponent.tsx', hash: 'abc123' }); ``` ### addListener(‘reloadEvent’, callback) [Section titled “addListener(‘reloadEvent’, callback)”](#addlistenerreloadevent-callback) Escuchar eventos de recarga entrantes del servidor. ```typescript const handle = await LiveReload.addListener('reloadEvent', (event) => { switch (event.type) { case 'full-reload': console.log('Recarga completa solicitada'); break; case 'file-update': console.log('Archivo actualizado:', event.file?.path); break; case 'error': console.error('Error:', event.message); break; } }); // Eliminar listener cuando termines await handle.remove(); ``` ### addListener(‘statusChange’, callback) [Section titled “addListener(‘statusChange’, callback)”](#addlistenerstatuschange-callback) Escuchar cambios de estado del socket. ```typescript await LiveReload.addListener('statusChange', (status) => { console.log(status.connected ? 'Conectado' : 'Desconectado'); }); ``` ### removeAllListeners() [Section titled “removeAllListeners()”](#removealllisteners) Eliminar todos los listeners registrados. ```typescript await LiveReload.removeAllListeners(); ``` ## Ejemplo Completo [Section titled “Ejemplo Completo”](#ejemplo-completo) ```typescript import { LiveReload } from '@capgo/capacitor-live-reload'; export class DevServer { private connected = false; async initialize() { // Solo habilitar en desarrollo if (process.env.NODE_ENV !== 'development') { return; } try { // Configurar servidor await LiveReload.configureServer({ url: 'http://192.168.1.100:5173', websocketPath: '/capgo-livereload', autoReconnect: true, reconnectInterval: 3000 }); // Configurar listeners antes de conectar await LiveReload.addListener('reloadEvent', this.handleReloadEvent.bind(this)); await LiveReload.addListener('statusChange', this.handleStatusChange.bind(this)); // Conectar await LiveReload.connect(); } catch (error) { console.error('Falló la inicialización de recarga en vivo:', error); } } private handleReloadEvent(event: any) { console.log('Evento de recarga recibido:', event.type); switch (event.type) { case 'full-reload': this.performFullReload(); break; case 'file-update': this.handleFileUpdate(event.file); break; case 'error': console.error('Error del servidor:', event.message); break; case 'connected': console.log('Servidor conectado'); break; case 'disconnected': console.log('Servidor desconectado'); break; } } private handleStatusChange(status: any) { this.connected = status.connected; console.log(`Recarga en vivo ${status.connected ? 'conectada' : 'desconectada'}`); } private performFullReload() { console.log('Realizando recarga completa de página...'); window.location.reload(); } private handleFileUpdate(file: any) { console.log('Archivo actualizado:', file?.path); // HMR manejará esto automáticamente en la mayoría de los casos } async disconnect() { await LiveReload.disconnect(); await LiveReload.removeAllListeners(); this.connected = false; } isConnected(): boolean { return this.connected; } } ``` ## Ejemplo de Configuración de Vite [Section titled “Ejemplo de Configuración de Vite”](#ejemplo-de-configuración-de-vite) Configura tu servidor Vite para trabajar con el Plugin de recarga en vivo: vite.config.ts ```typescript import { defineConfig } from 'vite'; export default defineConfig({ server: { host: '0.0.0.0', // Permitir conexiones desde la red port: 5173, hmr: { // Ruta WebSocket personalizada para recarga en vivo path: '/capgo-livereload', } } }); ``` ## Mejores Prácticas [Section titled “Mejores Prácticas”](#mejores-prácticas) 1. **Solo usar en desarrollo** ```typescript if (import.meta.env.DEV) { await LiveReload.configureServer({ url: 'http://localhost:5173', websocketPath: '/capgo-livereload' }); await LiveReload.connect(); } ``` 2. **Usar tu IP local para pruebas móviles** ```typescript const devServerUrl = process.env.VITE_DEV_SERVER_URL || 'http://192.168.1.100:5173'; await LiveReload.configureServer({ url: devServerUrl }); ``` 3. **Manejar errores de conexión con gracia** ```typescript try { await LiveReload.connect(); } catch (error) { console.warn('No se pudo conectar al servidor de desarrollo, usando compilación de producción'); } ``` 4. **Limpiar al salir de la Aplicación** ```typescript window.addEventListener('beforeunload', async () => { await LiveReload.disconnect(); }); ``` 5. **Mostrar estado de conexión en la UI** ```typescript LiveReload.addListener('statusChange', (status) => { // Mostrar indicador en compilaciones de desarrollo updateDevIndicator(status.connected); }); ``` ## Notas de Plataforma [Section titled “Notas de Plataforma”](#notas-de-plataforma) ### iOS [Section titled “iOS”](#ios) * Funciona con iOS 11.0+ * Asegúrate de que el servidor de desarrollo sea accesible desde la red de tu dispositivo * Puede necesitar configurar el firewall para permitir conexiones ### Android [Section titled “Android”](#android) * Funciona con Android 5.0 (API 21)+ * Usa `adb reverse` para conexiones localhost: ```bash adb reverse tcp:5173 tcp:5173 ``` ### Web [Section titled “Web”](#web) * Soporte completo para plataforma web * Conexión WebSocket directa al servidor de desarrollo Vite ## Solución de Problemas [Section titled “Solución de Problemas”](#solución-de-problemas) **Falla la conexión:** * Verifica que el servidor de desarrollo esté ejecutándose * Verifica que el dispositivo y la computadora estén en la misma red * Asegúrate de que el firewall permita conexiones en el puerto * Usa dirección IP en lugar de localhost para dispositivos móviles **Recarga lenta:** * Verifica velocidad de red * Reduce intervalo de reconexión * Optimiza configuración de compilación de Vite **Errores de WebSocket:** * Verifica que websocketPath coincida con la configuración de Vite * Verifica conflictos de puertos * Asegúrate de que las cabeceras sean correctas si usas autenticación # @Capgo/Capacitor-llm > Ejecuta modelos LLM directamente en el dispositivo con integración nativa de Apple Intelligence y aceleración de hardware. Privacidad en el Dispositivo Ejecuta modelos LLM directamente en el dispositivo para privacidad y capacidades offline 🤖 Apple Intelligence Integración nativa de Apple Intelligence en iOS 26.0+ 🍎 Múltiples Formatos Soporte para formatos de modelo .task y .litertlm 📦 Aceleración de Hardware Inferencia rápida con aceleración de hardware nativo ⚡ Respuestas en Streaming Respuestas en streaming en tiempo real con detectores de eventos 🔄 Comenzar Consulta la [Guía de Inicio](/docs/plugins/llm/getting-started/) para instalar y configurar el Plugin. # Empezando ## Instalación [Section titled “Instalación”](#instalación) * npm ```bash npm install @capgo/capacitor-llm npx cap sync ``` * yarn ```bash yarn add @capgo/capacitor-llm npx cap sync ``` * pnpm ```bash pnpm add @capgo/capacitor-llm npx cap sync ``` * bun ```bash bun add @capgo/capacitor-llm npx cap sync ``` ## Configuración de la plataforma [Section titled “Configuración de la plataforma”](#configuración-de-la-plataforma) ### iOS Configuración [Section titled “iOS Configuración”](#ios-configuración) * **iOS 26.0+**: utiliza Apple Inteligencia de forma predeterminada (no se necesita modelo) - **Recomendado** * **iOS < 26.0**: Requiere modelos personalizados de MediaPipe (experimental, puede tener problemas de compatibilidad) Para modelos personalizados en versiones anteriores de iOS, coloque archivos de modelo en su paquete de aplicaciones iOS a través de “Copiar recursos del paquete” de Xcode. ### Android Configuración [Section titled “Android Configuración”](#android-configuración) Coloque los archivos del modelo en su carpeta de activos Android: ```plaintext android/app/src/main/assets/ ``` Necesita **ambos** archivos para Android: * Archivo `.task` (modelo principal) * Archivo `.litertlm` (archivo complementario) Descargar desde [modelos Kaggle Gemma](https://www.kaggle.com/models/google/gemma) → pestaña “LiteRT (anteriormente TFLite)“ ## Modelos recomendados [Section titled “Modelos recomendados”](#modelos-recomendados) ### Para Android (modelos Gemma-3) [Section titled “Para Android (modelos Gemma-3)”](#para-android-modelos-gemma-3) * **Gemma 3 270M** - El más pequeño y eficiente para dispositivos móviles (\~240-400 MB) - **Recomendado** * **Gemma 3 1B** - Modelo de generación de texto más grande (\~892 MB-1,5 GB) Descargue desde \[modelos Kaggle Gemma] () → Haga clic en la pestaña “LiteRT (anteriormente TFLite)“ ### Para iOS [Section titled “Para iOS”](#para-ios) * **Apple Inteligencia** (iOS 26.0+) - Integrado, no necesita descarga - **Recomendado** * **Gemma-2 2B** (experimental) - Puede tener problemas de compatibilidad con el formato `.task` Para modelos iOS personalizados, descárguelos desde [Modelos Hugging Face MediaPipe](https://huggingface.co/collections/google/mediapipe-668392ead2d6768e82fb3b87) ## Uso [Section titled “Uso”](#uso) Importe el complemento e inicialícelo: ```typescript import { CapgoLLM } from '@capgo/capacitor-llm'; import { Capacitor } from '@capacitor/core'; // Check if LLM is ready const { readiness } = await CapgoLLM.getReadiness(); console.log('LLM readiness:', readiness); // Set the model based on platform const platform = Capacitor.getPlatform(); if (platform === 'ios') { // iOS: Use Apple Intelligence (default) await CapgoLLM.setModel({ path: 'Apple Intelligence' }); } else { // Android: Use MediaPipe model await CapgoLLM.setModel({ path: '/android_asset/gemma-3-270m-it-int8.task' }); } // Create a chat session const { id: chatId } = await CapgoLLM.createChat(); // Listen for AI responses CapgoLLM.addListener('textFromAi', (event) => { console.log('AI response:', event.text); }); // Listen for completion CapgoLLM.addListener('aiFinished', (event) => { console.log('AI completed response'); }); // Send a message await CapgoLLM.sendMessage({ chatId, message: 'Hello! How are you today?' }); ``` ## Funciones avanzadas [Section titled “Funciones avanzadas”](#funciones-avanzadas) ### Descargar modelos [Section titled “Descargar modelos”](#descargar-modelos) ```typescript // Download a model from URL await CapgoLLM.downloadModel({ url: 'https://example.com/model.task', filename: 'model.task' }); // For Android, download both .task and .litertlm files await CapgoLLM.downloadModel({ url: 'https://example.com/gemma-3-270m-it-int8.task', companionUrl: 'https://example.com/gemma-3-270m-it-int8.litertlm', filename: 'gemma-3-270m-it-int8.task' }); // Listen for download progress CapgoLLM.addListener('downloadProgress', (event) => { console.log(`Download progress: ${event.progress}%`); console.log(`Downloaded: ${event.downloadedBytes} / ${event.totalBytes}`); }); ``` ### Gestión de modelos [Section titled “Gestión de modelos”](#gestión-de-modelos) ```typescript // Set a specific model with configuration await CapgoLLM.setModel({ path: '/android_asset/gemma-3-270m-it-int8.task', maxTokens: 2048, topk: 40, temperature: 0.8 }); // Check readiness const { readiness } = await CapgoLLM.getReadiness(); if (readiness === 'ready') { // Model is loaded and ready } // Listen for readiness changes CapgoLLM.addListener('readinessChange', (event) => { console.log('Readiness changed:', event.readiness); }); ``` ## API Métodos [Section titled “API Métodos”](#api-métodos) ### crearChat() [Section titled “crearChat()”](#crearchat) Crea una nueva sesión de chat. ```typescript const { id: chatId } = await CapgoLLM.createChat(); ``` **Devoluciones:** `Promise<{ id: string; instructions?: string }>` ### enviar mensaje(…) [Section titled “enviar mensaje(…)”](#enviar-mensaje) Envíe un mensaje al LLM. ```typescript await CapgoLLM.sendMessage({ chatId: 'chat-id', message: 'What is the weather like?' }); ``` | Parámetro | Tipo | Descripción | | ------------- | -------- | -------------------- | | **`chatId`** | `string` | ID de sesión de chat | | **`message`** | `string` | Mensaje para enviar | ### obtener preparación() [Section titled “obtener preparación()”](#obtener-preparación) Verifique si el LLM está listo para usar. ```typescript const { readiness } = await CapgoLLM.getReadiness(); ``` **Devoluciones:** `Promise<{ readiness: string }>` Valores posibles: * `ready` - El modelo está cargado y listo * `loading` - Se está cargando el modelo * `not_ready` - Modelo aún no cargado * `error` - Error al cargar el modelo ### establecerModelo(…) [Section titled “establecerModelo(…)”](#establecermodelo) Establezca la configuración del modelo. ````typescript // iOS: Use Apple Intelligence (recommended) await CapgoLLM.setModel({ path: 'Apple Intelligence' }); // iOS: Use custom MediaPipe model (experimental) await CapgoLLM.setModel({ path: 'Gemma2-2B-IT_multi-prefill-seq_q8_ekv1280', modelType: 'task', maxTokens: 1280 }); // Android: Use MediaPipe model await CapgoLLM.setModel({ path: '/android_asset/gemma-3-270m-it-int8.task', maxTokens: 2048, topk: 40, temperature: 0.8 }); ```| Parámetro | Tipo | Descripción | | --------------- | -------- | ------------------------------------------------- | | **`path`** | `string` | Ruta del modelo o "Apple Inteligencia" para el sistema iOS | | **`modelType`** | `string` | Opcional: tipo de archivo de modelo (por ejemplo, "tarea", "bin") | | **`maxTokens`** | `number` | Opcional: tokens máximos que maneja el modelo | | **`topk`** | `number` | Opcional: Número de tokens considerados en cada paso | | **`temperature`** | `number` | Opcional: Aleatoriedad en la generación (0,0-1,0) | | **`randomSeed`** | `number` | Opcional: Semilla aleatoria para generación | ### descargarModelo(...) Descargue un modelo desde la URL y guárdelo en el almacenamiento del dispositivo. ```typescript await CapgoLLM.downloadModel({ url: 'https://example.com/gemma-3-270m-it-int8.task', companionUrl: 'https://example.com/gemma-3-270m-it-int8.litertlm', filename: 'gemma-3-270m-it-int8.task' }); ```` | Parámetro | Tipo | Descripción | | ------------------ | -------- | ---------------------------------------------------- | | **`url`** | `string` | URL para descargar desde | | **`companionUrl`** | `string` | Opcional: URL del archivo complementario (.litertlm) | | **`filename`** | `string` | Opcional: Nombre de archivo para guardar como | **Devoluciones:** `Promise<{ path: string; companionPath?: string }>` ## Eventos [Section titled “Eventos”](#eventos) ### textoDeAi [Section titled “textoDeAi”](#textodeai) Se activa cuando la IA genera texto (respuesta de transmisión). ```typescript CapgoLLM.addListener('textFromAi', (event) => { console.log('AI text:', event.text); console.log('Chat ID:', event.chatId); console.log('Is chunk:', event.isChunk); }); ``` **Datos del evento:** * `text` (cadena) - Fragmento de texto incremental de AI * `chatId` (cadena) - ID de sesión de chat * `isChunk` (booleano): si se trata de un fragmento completo o de datos de transmisión parciales ### aiTerminado [Section titled “aiTerminado”](#aiterminado) Se dispara cuando la IA completa la respuesta. ```typescript CapgoLLM.addListener('aiFinished', (event) => { console.log('Completed for chat:', event.chatId); }); ``` **Datos del evento:** * `chatId` (cadena) - ID de sesión de chat ### descargarProgreso [Section titled “descargarProgreso”](#descargarprogreso) Activado durante la descarga del modelo para informar el progreso. ```typescript CapgoLLM.addListener('downloadProgress', (event) => { console.log('Progress:', event.progress, '%'); console.log('Downloaded:', event.downloadedBytes, '/', event.totalBytes); }); ``` **Datos del evento:** * `progress` (número) - Porcentaje de descarga completada (0-100) * `downloadedBytes` (número) - Bytes descargados hasta el momento * `totalBytes` (número) - Total de bytes para descargar ### preparaciónCambiar [Section titled “preparaciónCambiar”](#preparacióncambiar) Se activa cuando cambia el estado de preparación del LLM. ```typescript CapgoLLM.addListener('readinessChange', (event) => { console.log('Readiness changed to:', event.readiness); }); ``` **Datos del evento:** * `readiness` (cadena) - El nuevo estado de preparación ## Ejemplo completo [Section titled “Ejemplo completo”](#ejemplo-completo) ```typescript import { CapgoLLM } from '@capgo/capacitor-llm'; import { Capacitor } from '@capacitor/core'; class AIService { private chatId: string | null = null; private messageBuffer: string = ''; async initialize() { // Set up model based on platform const platform = Capacitor.getPlatform(); if (platform === 'ios') { // iOS: Use Apple Intelligence (recommended) await CapgoLLM.setModel({ path: 'Apple Intelligence' }); } else { // Android: Use MediaPipe model await CapgoLLM.setModel({ path: '/android_asset/gemma-3-270m-it-int8.task', maxTokens: 2048, topk: 40, temperature: 0.8 }); } // Wait for model to be ready let isReady = false; while (!isReady) { const { readiness } = await CapgoLLM.getReadiness(); if (readiness === 'ready') { isReady = true; } else if (readiness === 'error') { throw new Error('Failed to load model'); } await new Promise(resolve => setTimeout(resolve, 500)); } // Create chat session const { id } = await CapgoLLM.createChat(); this.chatId = id; // Set up event listeners this.setupListeners(); } private setupListeners() { CapgoLLM.addListener('textFromAi', (event) => { if (event.chatId === this.chatId) { this.messageBuffer += event.text; this.onTextReceived(event.text); } }); CapgoLLM.addListener('aiFinished', (event) => { if (event.chatId === this.chatId) { this.onMessageComplete(this.messageBuffer); this.messageBuffer = ''; } }); } async sendMessage(message: string) { if (!this.chatId) { throw new Error('Chat not initialized'); } await CapgoLLM.sendMessage({ chatId: this.chatId, message }); } onTextReceived(text: string) { // Update UI with streaming text console.log('Received:', text); } onMessageComplete(fullMessage: string) { // Handle complete message console.log('Complete message:', fullMessage); } } // Usage const ai = new AIService(); await ai.initialize(); await ai.sendMessage('Tell me about AI'); ``` ## Soporte de plataforma [Section titled “Soporte de plataforma”](#soporte-de-plataforma) | Plataforma | Apoyado | Requisitos | | ---------- | ------- | ----------------------------------------- | | iOS | ✅ | iOS 13.0+ (26.0+ para Apple Inteligencia) | | Android | ✅ | API 24+ | | Web | ❌ | No compatible | ## Mejores prácticas [Section titled “Mejores prácticas”](#mejores-prácticas) 1. **Selección de modelo**: elija modelos según las capacidades del dispositivo * Utilice 270M para la mayoría de los dispositivos móviles * Utilice 1B para dispositivos de gama alta con más RAM * Pruebe el rendimiento en los dispositivos de destino. 2. **Administración de memoria**: borre las sesiones de chat cuando haya terminado ```typescript // Create new chat for new conversations const { id } = await CapacitorLLM.createChat(); ``` 3. **Manejo de errores**: siempre verifique que esté listo antes de usar ```typescript const { readiness } = await CapacitorLLM.getReadiness(); if (readiness !== 'ready') { // Handle not ready state } ``` 4. **Interfaz de usuario de transmisión**: actualice la interfaz de usuario de forma incremental con transmisión de texto * Mostrar texto tal como llega a través de `onAiText` * Marcar completo con `onAiCompletion` 5. **Descarga de modelos**: descarga modelos durante la configuración de la aplicación, no en el primer uso. ```typescript // During app initialization await CapacitorLLM.downloadModel({ url: 'https://your-cdn.com/model.task', filename: 'model.task' }); ``` ## Solución de problemas### El modelo no se carga [Section titled “Solución de problemas### El modelo no se carga”](#solución-de-problemas-el-modelo-no-se-carga) * Verifique que el archivo del modelo esté en la ubicación correcta * Verifique que el formato del modelo coincida con la plataforma (.gguf para iOS, .task para Android) * Garantizar suficiente almacenamiento en el dispositivo ### Mal desempeño [Section titled “Mal desempeño”](#mal-desempeño) * Pruebe un modelo más pequeño (270M en lugar de 1B) * Cierra otras aplicaciones para liberar memoria. * Prueba en un dispositivo real, no en un simulador ### Sin respuestas [Section titled “Sin respuestas”](#sin-respuestas) * Verificar que el estado de preparación sea “listo” * Verificar que los detectores de eventos estén configurados antes de enviar mensajes. * Verifique la consola en busca de errores. ## Recursos [Section titled “Recursos”](#recursos) * [GitHub Repositorio](https://github.com/Cap-go/capacitor-llm) * [Modelos Gemma](https://ai.google.dev/gemma) * [Apple Inteligencia](https://developer.apple.com/machine-learning/) * [MediaPipe](https://developers.google.com/mediapipe) # @Capgo/Capacitor-media-session > Publica metadatos de pistas, reacciona a eventos de reproducción/pausa y sincroniza el estado de reproducción con los controles multimedia nativos. El Plugin Media Session conecta Capacitor con la API de Media Session para que tus reproductores de audio y video se sientan nativos en todas partes. Metadatos enriquecidos Proporciona arte de álbum, artista y título para los controles de pantalla de bloqueo y notificaciones. Sincronización de estado de reproducción Mantén los botones de reproducción/pausa y los indicadores de progreso alineados con tu reproductor. Callbacks de acciones Maneja botones de auriculares y acciones multimedia del sistema operativo desde tu código Capacitor. Reporte de posición Envía duración, posición y velocidad para que el desplazamiento funcione con fluidez. Salta a la guía de inicio para conectar las actualizaciones de metadatos al ciclo de vida de tu reproductor. # Comenzar > Sincroniza la interfaz de usuario de tu reproductor con los controles multimedia nativos usando el Plugin Media Session. 1. **Instalar el Plugin** * npm ```sh npm i @Capgo/Capacitor-media-session ``` * pnpm ```sh pnpm add @Capgo/Capacitor-media-session ``` * yarn ```sh yarn add @Capgo/Capacitor-media-session ``` * bun ```sh bun add @Capgo/Capacitor-media-session ``` 2. **Sincronizar plataformas** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` > ℹ️ La API de Media Session está disponible en Android (navegadores basados en Chrome, WebView) y muchos navegadores de escritorio. En plataformas no compatibles, las llamadas se resuelven sin tener efecto, por lo que protege tu interfaz de usuario en consecuencia. ## Publicar metadatos [Section titled “Publicar metadatos”](#publicar-metadatos) ```typescript import { MediaSession } from '@capgo/capacitor-media-session'; await MediaSession.setMetadata({ title: 'Weekly Tech Podcast', artist: 'Capgo Studio', album: 'Season 2', artwork: [ { src: 'https://cdn.example.com/covers/s02e05-512.png', sizes: '512x512', type: 'image/.png' }, ], }); ``` ## Actualizar el estado de reproducción [Section titled “Actualizar el estado de reproducción”](#actualizar-el-estado-de-reproducción) ```typescript await MediaSession.setPlaybackState({ playbackState: 'playing' }); // Pausar más tarde si es necesario await MediaSession.setPlaybackState({ playbackState: 'paused' }); ``` ## Manejar acciones multimedia [Section titled “Manejar acciones multimedia”](#manejar-acciones-multimedia) ```typescript const pauseHandler = async () => { player.pause(); await MediaSession.setPlaybackState({ playbackState: 'paused' }); }; await MediaSession.setActionHandler({ action: 'pause' }, pauseHandler); await MediaSession.setActionHandler({ action: 'play' }, async () => { await player.play(); await MediaSession.setPlaybackState({ playbackState: 'playing' }); }); // Eliminar un manejador cuando descartes el reproductor await MediaSession.setActionHandler({ action: 'pause' }, null); ``` ## Mantener la posición sincronizada [Section titled “Mantener la posición sincronizada”](#mantener-la-posición-sincronizada) ```typescript const updatePosition = async () => { await MediaSession.setPositionState({ duration: player.duration, position: player.currentTime, playbackRate: player.playbackRate, }); }; player.ontimeupdate = updatePosition; player.onratechange = updatePosition; ``` ## Recomendaciones [Section titled “Recomendaciones”](#recomendaciones) * Actualiza los metadatos cuando cambie la pista para que las notificaciones y los altavoces inteligentes se mantengan actualizados. * Limita las actualizaciones de posición (por ejemplo, cada 250 ms) para evitar saturar la capa nativa. * Siempre elimina los manejadores de acciones al desmontar una instancia del reproductor para evitar fugas accidentales. # @Capgo/Capacitor-mute > Verifica si un dispositivo está silenciado o en modo silencioso. Detecta el interruptor de silencio en iOS y el estado de volumen en Android. Detección de modo silencioso Detecta si el dispositivo está en modo silencioso/silenciado 🔇 Multiplataforma Funciona tanto en iOS (interruptor de silencio) como en Android (volumen) 📱 API Simple Fácil de usar con métodos sencillos 🎯 Documentación Completa Consulta la [Documentación](/docs/plugins/mute/getting-started/) para dominar el Plugin en solo unos minutos. # Comenzar > Aprende cómo instalar y usar el Plugin Mute para detectar el estado de modo silencioso en tu aplicación Capacitor. 1. **Instalar el paquete** * npm ```sh npm i @Capgo/Capacitor-mute ``` * pnpm ```sh pnpm add @Capgo/Capacitor-mute ``` * yarn ```sh yarn add @Capgo/Capacitor-mute ``` * bun ```sh bun add @Capgo/Capacitor-mute ``` 2. **Sincronizar con proyectos nativos** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Uso [Section titled “Uso”](#uso) Importa el Plugin y usa sus métodos para verificar el estado de silencio: ```typescript import { CapacitorMute } from '@capgo/capacitor-mute'; // Verificar si el dispositivo está silenciado const checkMuteState = async () => { const { value } = await CapacitorMute.isMuted(); if (value) { console.log('El dispositivo está silenciado/en modo silencioso'); // Ajustar el comportamiento de la aplicación para modo silencioso } else { console.log('El sonido del dispositivo está activado'); } }; ``` ## Referencia de API [Section titled “Referencia de API”](#referencia-de-api) ### isMuted() [Section titled “isMuted()”](#ismuted) Verifica si el dispositivo está actualmente silenciado/en modo silencioso. ```typescript const result = await CapacitorMute.isMuted(); // Devuelve: { value: boolean } ``` ## Ejemplo Completo [Section titled “Ejemplo Completo”](#ejemplo-completo) ```typescript import { CapacitorMute } from '@capgo/capacitor-mute'; export class SoundManager { private isMuted = false; async initialize() { // Obtener el estado inicial de silencio await this.checkMuteState(); } async checkMuteState() { try { const { value } = await CapacitorMute.isMuted(); this.isMuted = value; this.updateAppBehavior(); } catch (error) { console.error('Error al verificar el estado de silencio:', error); } } private updateAppBehavior() { if (this.isMuted) { // El dispositivo está silenciado - ajustar el comportamiento de la aplicación this.disableSoundEffects(); this.showVisualNotifications(); } else { // El sonido del dispositivo está activado this.enableSoundEffects(); this.useAudioNotifications(); } } private disableSoundEffects() { // Desactivar sonidos dentro de la aplicación console.log('Desactivando efectos de sonido'); } private enableSoundEffects() { // Activar sonidos dentro de la aplicación console.log('Activando efectos de sonido'); } private showVisualNotifications() { // Usar retroalimentación visual en lugar de audio console.log('Usando notificaciones visuales'); } private useAudioNotifications() { // Usar notificaciones de audio console.log('Usando notificaciones de audio'); } } // Uso const soundManager = new SoundManager(); await soundManager.initialize(); // Sondear periódicamente para verificar cambios setInterval(() => { soundManager.checkMuteState(); }, 5000); // Verificar cada 5 segundos ``` ## Comportamiento por Plataforma [Section titled “Comportamiento por Plataforma”](#comportamiento-por-plataforma) ### iOS [Section titled “iOS”](#ios) * Detecta el estado del interruptor físico de silencio * Sin eventos de cambio en tiempo real (se requiere sondeo) * Funciona en iPhone y iPad con interruptor de silencio ### Android [Section titled “Android”](#android) * Verifica si el modo de timbre está configurado en silencio o vibración * Sin eventos de cambio (se requiere sondeo) * Basado en el modo de timbre de AudioManager ## Mejores Prácticas [Section titled “Mejores Prácticas”](#mejores-prácticas) 1. **Sondear para detectar cambios** Dado que el Plugin no proporciona eventos en tiempo real, sondea periódicamente si necesitas rastrear cambios: ```typescript // Verificar el estado de silencio periódicamente setInterval(async () => { const { value } = await CapacitorMute.isMuted(); if (value !== previousMuteState) { handleMuteStateChange(value); } }, 5000); ``` 2. **Respetar las preferencias del usuario** Siempre honra el estado de silencio y ajusta el comportamiento de audio de tu aplicación en consecuencia. 3. **Proporcionar alternativas visuales** ```typescript const { value: isMuted } = await CapacitorMute.isMuted(); if (isMuted) { // Mostrar notificaciones visuales showToast('Nuevo mensaje recibido'); } else { // Reproducir notificación de sonido playNotificationSound(); } ``` ## Casos de Uso [Section titled “Casos de Uso”](#casos-de-uso) 1. **Gestión de Sonido en Juegos** ```typescript const shouldPlaySound = async () => { const { value: isMuted } = await CapacitorMute.isMuted(); return !isMuted && userPreferences.soundEnabled; }; ``` 2. **Manejo de Notificaciones** ```typescript const sendNotification = async (message: string) => { const { value: isMuted } = await CapacitorMute.isMuted(); if (isMuted) { // Usar vibración o notificación visual await Haptics.vibrate(); showBanner(message); } else { // Reproducir sonido de notificación await playSound('notification.mp3'); } }; ``` 3. **Reproductor de Video** ```typescript const initVideoPlayer = async () => { const { value: isMuted } = await CapacitorMute.isMuted(); videoPlayer.setMuted(isMuted); if (isMuted) { showSubtitles(true); showMuteIndicator(); } }; ``` ## Solución de Problemas [Section titled “Solución de Problemas”](#solución-de-problemas) 1. **Siempre devuelve false en Android** * Verifica si el modo de timbre del dispositivo está realmente configurado en silencio * Algunos dispositivos Android pueden tener un comportamiento diferente 2. **No hay eventos de cambio disponibles** * El Plugin no proporciona eventos de cambio en tiempo real * Implementa sondeo si necesitas detectar cambios 3. **No funciona en el simulador** * El Simulador de iOS no tiene un interruptor de silencio * Prueba en dispositivos reales para obtener resultados precisos # @Capgo/Capacitor-mux-player > Transmite video Mux de alta calidad en pantalla completa con DRM, análisis y controles consistentes en iOS, Android y web. Ofrece reproducción premium envolviendo los SDK nativos oficiales de Mux en una interfaz Capacitor simple. Experiencia de SDK nativo Usa el mismo stack de reproducción que los SDK móviles de Mux con tasa de bits adaptativa y DRM. Reproducción firmada Proporciona tokens de reproducción y DRM para entrega segura de transmisiones protegidas. API Uniforme Activa la reproducción, descarta el reproductor y escucha eventos con una API TypeScript única. Listo para análisis Pasa claves de entorno y nombres de reproductores para alimentar la telemetría de Mux Data automáticamente. La guía de inicio te guía a través de la conexión de los SDK nativos, el manejo de tokens y la respuesta a eventos del reproductor. # Comenzar > Integra el SDK nativo de Mux Player dentro de tu aplicación Capacitor. 1. **Instalar el Plugin** * npm ```sh npm i @Capgo/Capacitor-mux-player ``` * pnpm ```sh pnpm add @Capgo/Capacitor-mux-player ``` * yarn ```sh yarn add @Capgo/Capacitor-mux-player ``` * bun ```sh bun add @Capgo/Capacitor-mux-player ``` 2. **Sincronizar proyectos nativos** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Preparación para iOS [Section titled “Preparación para iOS”](#preparación-para-ios) 1. Abre el espacio de trabajo de Xcode en `ios/App/`. 2. Agrega el paquete Swift `https://github.com/muxinc/mux-player-swift` a tu objetivo de aplicación para que el módulo `MuxPlayerSwift` esté disponible. 3. Asegúrate de que el objetivo de implementación sea **iOS 15+** y reconstruye. ## Preparación para Android [Section titled “Preparación para Android”](#preparación-para-android) El módulo Gradle viene con los repositorios y dependencias correctos. Si usas un proxy corporativo personalizado, permite solicitudes a `https://muxinc.jfrog.io/artifactory/default-maven-release-local`. ## Lanzar el reproductor [Section titled “Lanzar el reproductor”](#lanzar-el-reproductor) ```typescript import { MuxPlayer } from '@capgo/capacitor-mux-player'; await MuxPlayer.play({ playbackId: 'your-playback-id', environmentKey: 'your-mux-data-key', title: 'Launch Announcement', subtitle: 'Filmed live at Capgo HQ', poster: 'https://stream.example.com/poster.jpg', }); ``` ## Escuchar eventos del reproductor [Section titled “Escuchar eventos del reproductor”](#escuchar-eventos-del-reproductor) ```typescript const readyHandle = await MuxPlayer.addListener('ready', ({ playerName }) => { console.log('Mux player ready', playerName); }); const errorHandle = await MuxPlayer.addListener('error', ({ message }) => { console.error('Mux player error:', message); }); // Limpiar después de descartar el reproductor const dismissPlayer = async () => { await MuxPlayer.dismiss(); await readyHandle.remove(); await errorHandle.remove(); }; ``` ## Reproducción basada en tokens [Section titled “Reproducción basada en tokens”](#reproducción-basada-en-tokens) ```typescript await MuxPlayer.play({ playbackId: 'signed-playback-id', playbackToken: signedPlaybackToken, drmToken: signedDrmToken, // Opcional si habilitaste políticas DRM autoPlay: true, startTime: 120, // comenzar en 2 minutos }); ``` ## Reaccionar al ciclo de vida del reproductor [Section titled “Reaccionar al ciclo de vida del reproductor”](#reaccionar-al-ciclo-de-vida-del-reproductor) ```typescript const { active } = await MuxPlayer.isActive(); if (active) { console.log('Reproductor actualmente visible'); } // Eliminar todos los registros de detectores al desmontar await MuxPlayer.removeAllListeners(); ``` ## Consejos [Section titled “Consejos”](#consejos) * Genera tokens de reproducción firmados y DRM en tu backend usando las claves de firma de Mux. * Pasa un `playerName` para separar los análisis al ejecutar múltiples reproductores en tu aplicación. * Combina con `enableSmartCache` para mejorar la resistencia offline en plataformas compatibles. # @Capgo/native-audio > Reproduce audio con rendimiento nativo, baja latencia y reproducción concurrente. Perfecto para juegos, efectos de sonido y música de fondo. Baja latencia APIs de audio nativo para reproducción instantánea 🚀 Reproducción concurrente Reproduce múltiples sonidos simultáneamente 🎵 Audio en segundo plano Continúa la reproducción cuando la aplicación está en segundo plano 🎧 Documentación Completa Consulta la [Documentación](/docs/plugins/native-audio/getting-started/) para dominar el Plugin en solo unos minutos. # Empezando > Aprenda a instalar y utilizar el complemento Native Audio para la reproducción de audio de alto rendimiento en su aplicación Capacitor. 1. **Instalar el paquete** * npm ```sh npm i @capgo/native-audio ``` * pnpm ```sh pnpm add @capgo/native-audio ``` * yarn ```sh yarn add @capgo/native-audio ``` * bun ```sh bun add @capgo/native-audio ``` 2. **Sincronización con proyectos nativos** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` 3. **Agregar archivos de audio** Coloque sus archivos de audio en las carpetas de la plataforma apropiadas: * **iOS**: `ios/App/App/sounds/` * **Android**: `android/app/src/main/res/raw/` ## Uso [Section titled “Uso”](#uso) Importe el complemento y precargue los archivos de audio antes de reproducirlos: ```typescript import { NativeAudio } from '@capgo/native-audio'; // Preload audio files const preloadAudio = async () => { // Simple preload for short sounds await NativeAudio.preload({ assetId: 'click', assetPath: 'sounds/click.mp3', audioChannelNum: 1, isUrl: false }); // Complex preload for music/longer audio await NativeAudio.preloadComplex({ assetId: 'background-music', assetPath: 'sounds/background.mp3', audioChannelNum: 1, volume: 0.5, delay: 0, isUrl: false }); }; // Play audio const playSound = async () => { await NativeAudio.play({ assetId: 'click' }); }; // Play with options const playMusic = async () => { await NativeAudio.play({ assetId: 'background-music', time: 0 // Start from beginning }); }; // Loop audio const loopMusic = async () => { await NativeAudio.loop({ assetId: 'background-music' }); }; // Stop audio const stopMusic = async () => { await NativeAudio.stop({ assetId: 'background-music' }); }; // Unload when done const cleanup = async () => { await NativeAudio.unload({ assetId: 'click' }); await NativeAudio.unload({ assetId: 'background-music' }); }; ``` ## API Referencia [Section titled “API Referencia”](#api-referencia) ### precarga (opciones) [Section titled “precarga (opciones)”](#precarga-opciones) Precarga un archivo de audio para una reproducción sencilla (mejor para sonidos cortos). ```typescript interface PreloadOptions { assetId: string; assetPath: string; audioChannelNum?: number; isUrl?: boolean; } await NativeAudio.preload({ assetId: 'sound-effect', assetPath: 'sounds/effect.mp3', audioChannelNum: 1, isUrl: false }); ``` ### precargaComplejo(opciones) [Section titled “precargaComplejo(opciones)”](#precargacomplejoopciones) Precarga audio con opciones avanzadas (mejor para música/audio de fondo). ```typescript interface PreloadComplexOptions { assetId: string; assetPath: string; volume?: number; // 0.0 to 1.0 audioChannelNum?: number; delay?: number; isUrl?: boolean; fadeDuration?: number; } await NativeAudio.preloadComplex({ assetId: 'theme-song', assetPath: 'sounds/theme.mp3', volume: 0.7, audioChannelNum: 2, isUrl: false }); ``` ### jugar(opciones) [Section titled “jugar(opciones)”](#jugaropciones) Reproduce un archivo de audio precargado. ```typescript interface PlayOptions { assetId: string; time?: number; // Start time in seconds } await NativeAudio.play({ assetId: 'sound-effect', time: 0 }); ``` ### bucle (opciones) [Section titled “bucle (opciones)”](#bucle-opciones) Realiza un bucle continuo de un archivo de audio precargado. ```typescript await NativeAudio.loop({ assetId: 'background-music' }); ``` ### detener(opciones) [Section titled “detener(opciones)”](#deteneropciones) Deja de reproducir un archivo de audio. ```typescript await NativeAudio.stop({ assetId: 'background-music' }); ``` ### pausa (opciones) [Section titled “pausa (opciones)”](#pausa-opciones) Pausa la reproducción de audio. ```typescript await NativeAudio.pause({ assetId: 'background-music' }); ``` ### currículum (opciones) [Section titled “currículum (opciones)”](#currículum-opciones) Reanuda el audio en pausa. ```typescript await NativeAudio.resume({ assetId: 'background-music' }); ``` ### establecerVolumen(opciones) [Section titled “establecerVolumen(opciones)”](#establecervolumenopciones) Establece el volumen de un recurso de audio. ```typescript interface SetVolumeOptions { assetId: string; volume: number; // 0.0 to 1.0 } await NativeAudio.setVolume({ assetId: 'background-music', volume: 0.3 }); ``` ### obtenerDuración(opciones) [Section titled “obtenerDuración(opciones)”](#obtenerduraciónopciones) Obtiene la duración de un archivo de audio en segundos. ```typescript const { duration } = await NativeAudio.getDuration({ assetId: 'background-music' }); console.log(`Duration: ${duration} seconds`); ``` ### getCurrentTime(opciones) [Section titled “getCurrentTime(opciones)”](#getcurrenttimeopciones) Obtiene el tiempo de reproducción actual en segundos. ```typescript const { currentTime } = await NativeAudio.getCurrentTime({ assetId: 'background-music' }); console.log(`Current time: ${currentTime} seconds`); ``` ### está reproduciendo (opciones) [Section titled “está reproduciendo (opciones)”](#está-reproduciendo-opciones) Comprueba si el audio se está reproduciendo actualmente. ```typescript const { isPlaying } = await NativeAudio.isPlaying({ assetId: 'background-music' }); console.log(`Is playing: ${isPlaying}`); ``` ### descargar (opciones) [Section titled “descargar (opciones)”](#descargar-opciones) Descarga un archivo de audio de la memoria. ```typescript await NativeAudio.unload({ assetId: 'sound-effect' }); ``` ## Uso avanzado [Section titled “Uso avanzado”](#uso-avanzado) ### Clase de administrador de sonido [Section titled “Clase de administrador de sonido”](#clase-de-administrador-de-sonido) ```typescript import { NativeAudio } from '@capgo/native-audio'; export class SoundManager { private sounds: Map = new Map(); private volume = 1.0; async init() { // Preload all sounds await this.preloadSound('click', 'sounds/click.mp3'); await this.preloadSound('success', 'sounds/success.mp3'); await this.preloadSound('error', 'sounds/error.mp3'); // Preload music await this.preloadMusic('background', 'sounds/background.mp3', 0.5); } private async preloadSound(id: string, path: string) { try { await NativeAudio.preload({ assetId: id, assetPath: path, audioChannelNum: 1, isUrl: false }); this.sounds.set(id, true); } catch (error) { console.error(`Failed to preload ${id}:`, error); } } private async preloadMusic(id: string, path: string, volume: number) { try { await NativeAudio.preloadComplex({ assetId: id, assetPath: path, volume, audioChannelNum: 2, isUrl: false }); this.sounds.set(id, true); } catch (error) { console.error(`Failed to preload ${id}:`, error); } } async playSound(id: string) { if (!this.sounds.has(id)) { console.warn(`Sound ${id} not preloaded`); return; } try { await NativeAudio.play({ assetId: id }); } catch (error) { console.error(`Failed to play ${id}:`, error); } } async playMusic(id: string) { if (!this.sounds.has(id)) return; try { await NativeAudio.loop({ assetId: id }); } catch (error) { console.error(`Failed to play music ${id}:`, error); } } async stopMusic(id: string) { try { await NativeAudio.stop({ assetId: id }); } catch (error) { console.error(`Failed to stop ${id}:`, error); } } async setMasterVolume(volume: number) { this.volume = Math.max(0, Math.min(1, volume)); // Update all loaded sounds for (const [id] of this.sounds) { await NativeAudio.setVolume({ assetId: id, volume: this.volume }); } } async cleanup() { for (const [id] of this.sounds) { await NativeAudio.unload({ assetId: id }); } this.sounds.clear(); } } ``` ### Cargando desde URL [Section titled “Cargando desde URL”](#cargando-desde-url) ```typescript // Load audio from URL await NativeAudio.preloadComplex({ assetId: 'remote-audio', assetPath: 'https://example.com/audio.mp3', isUrl: true, volume: 0.8 }); ``` ### Efectos de aparición y desaparición gradual [Section titled “Efectos de aparición y desaparición gradual”](#efectos-de-aparición-y-desaparición-gradual) ```typescript const fadeIn = async (assetId: string, duration: number) => { const steps = 20; const stepDuration = duration / steps; await NativeAudio.setVolume({ assetId, volume: 0 }); await NativeAudio.play({ assetId }); for (let i = 1; i <= steps; i++) { await new Promise(resolve => setTimeout(resolve, stepDuration)); await NativeAudio.setVolume({ assetId, volume: i / steps }); } }; const fadeOut = async (assetId: string, duration: number) => { const steps = 20; const stepDuration = duration / steps; for (let i = steps; i >= 0; i--) { await NativeAudio.setVolume({ assetId, volume: i / steps }); await new Promise(resolve => setTimeout(resolve, stepDuration)); } await NativeAudio.stop({ assetId }); }; ``` ## Mejores prácticas [Section titled “Mejores prácticas”](#mejores-prácticas) 1. **Precarga durante la inicialización de la aplicación** ```typescript import { App } from '@capacitor/app'; App.addListener('appStateChange', async ({ isActive }) => { if (isActive) { await soundManager.init(); } }); ``` 2. **Maneja los errores con elegancia** ```typescript try { await NativeAudio.play({ assetId: 'sound' }); } catch (error) { console.log('Audio playback failed, continuing silently'); } ``` 3. **Descargar audio no utilizado** ```typescript // Unload sounds when leaving a screen ionViewWillLeave() { this.unloadScreenSounds(); } ``` 4. **Utilice métodos de precarga adecuados** * `preload()` para efectos de sonido cortos (< 5 segundos) * `preloadComplex()` para música y audio más largo ## Notas de plataforma [Section titled “Notas de plataforma”](#notas-de-plataforma) ### iOS [Section titled “iOS”](#ios) * Admite AAC, MP3, WAV y otros formatos Core Audio * Utiliza AVAudioPlayer para audio complejo * Utiliza System Sound Services para audio simple * Admite audio de fondo con la configuración adecuada ### Android [Section titled “Android”](#android) * Soporta formatos MP3, OGG, WAV * Utiliza SoundPool para audio simple * Utiliza MediaPlayer para audio complejo * Puede requerir permiso WAKE\_LOCK para reproducción en segundo plano ### Colocación de archivos [Section titled “Colocación de archivos”](#colocación-de-archivos) #### iOS [Section titled “iOS”](#ios-1) Coloque archivos en `ios/App/App/sounds/` o cree una referencia de carpeta en Xcode. #### Android [Section titled “Android”](#android-1) Coloque archivos en `android/app/src/main/res/raw/`. Nota: Los nombres de los archivos deben estar en minúsculas y sin caracteres especiales. ## Problemas comunes [Section titled “Problemas comunes”](#problemas-comunes) 1. **El audio no se reproduce** * Asegúrese de que los archivos estén en los directorios correctos. * Verificar la compatibilidad del formato de archivo * Verificar que el identificador de activo coincida exactamente 2. **Retraso en la reproducción** * Utilice `preload()` para efectos de sonido * Precarga antes de que necesites jugar 3. **Problemas de memoria** * Descargar archivos de audio cuando no sea necesario * No precargues demasiados archivos grandes 4. **Reproducción en segundo plano** * Configurar la capacidad de audio de fondo en iOS * Manejar el enfoque de audio en Android # @Capgo/Capacitor-native-biometric > Accede a las APIs de autenticación biométrica nativa para Android e iOS, proporcionando autenticación de usuario segura y conveniente. Múltiples biométricos Soporta huella dactilar, Face ID, Touch ID y otros sensores biométricos 🚀 Autenticación segura Proporciona autenticación biométrica segura para operaciones sensibles 🔒 Opciones alternativas Incluye alternativa de código de acceso del dispositivo cuando la biometría no está disponible 😊 Documentación Completa Consulta la [Documentación](/docs/plugins/native-biometric/getting-started/) para dominar el Plugin en solo unos minutos. # Comenzar > Aprende cómo instalar y usar el Plugin Biometría nativa para autenticación biométrica segura en tu aplicación Capacitor. 1. **Instalar el paquete** * npm ```sh npm i @Capgo/Capacitor-native-biometric ``` * pnpm ```sh pnpm add @Capgo/Capacitor-native-biometric ``` * yarn ```sh yarn add @Capgo/Capacitor-native-biometric ``` * bun ```sh bun add @Capgo/Capacitor-native-biometric ``` 2. **Sincronizar con proyectos nativos** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` 3. **Configurar permisos** ### iOS [Section titled “iOS”](#ios) Agrega la descripción de uso de Face ID a tu `Info.plist`: ```xml NSFaceIDUsageDescription For secure authentication ``` ### Android [Section titled “Android”](#android) Agrega el permiso biométrico a tu `AndroidManifest.xml`: ```xml ``` ## Uso [Section titled “Uso”](#uso) Importa el Plugin y usa sus métodos para autenticación biométrica: ```typescript import { NativeBiometric } from '@capgo/capacitor-native-biometric'; // Verificar si la autenticación biométrica está disponible const checkBiometric = async () => { const result = await NativeBiometric.isAvailable(); if (result.isAvailable) { console.log(`Tipo de biometría: ${result.biometryType}`); } }; // Realizar verificación biométrica const verify = async () => { const verified = await NativeBiometric.verifyIdentity({ reason: "Para acceso seguro a tu cuenta", title: "Autenticación", subtitle: "Verifica tu identidad", description: "Coloca tu dedo en el sensor" }) .then(() => true) .catch(() => false); if (verified) { console.log("Autenticación exitosa"); } }; // Almacenar credenciales de forma segura const saveCredentials = async () => { await NativeBiometric.setCredentials({ username: "user@example.com", password: "securepassword", server: "https://api.example.com" }); }; // Recuperar credenciales almacenadas const getCredentials = async () => { const credentials = await NativeBiometric.getCredentials({ server: "https://api.example.com" }); console.log(credentials.username); console.log(credentials.password); }; // Eliminar credenciales almacenadas const deleteCredentials = async () => { await NativeBiometric.deleteCredentials({ server: "https://api.example.com" }); }; ``` ## Referencia de API [Section titled “Referencia de API”](#referencia-de-api) ### isAvailable() [Section titled “isAvailable()”](#isavailable) Verifica si la autenticación biométrica está disponible en el dispositivo. ```typescript interface AvailableResult { isAvailable: boolean; biometryType?: BiometryType; errorCode?: number; } enum BiometryType { NONE = 0, TOUCH_ID = 1, FACE_ID = 2, FINGERPRINT = 3, FACE_AUTHENTICATION = 4, IRIS_AUTHENTICATION = 5 } ``` ### verifyIdentity(Opciones) [Section titled “verifyIdentity(Opciones)”](#verifyidentityopciones) Realiza la autenticación biométrica. ```typescript interface BiometricOptions { reason?: string; title?: string; subtitle?: string; description?: string; fallbackTitle?: string; useFallback?: boolean; maxAttempts?: number; } ``` ### setCredentials(Opciones) [Section titled “setCredentials(Opciones)”](#setcredentialsopciones) Almacena credenciales de forma segura con protección biométrica. ```typescript interface Credentials { username: string; password: string; server: string; } ``` ### getCredentials(Opciones) [Section titled “getCredentials(Opciones)”](#getcredentialsopciones) Recupera credenciales almacenadas después de la autenticación biométrica. ```typescript interface GetCredentialOptions { server: string; } ``` ### deleteCredentials(Opciones) [Section titled “deleteCredentials(Opciones)”](#deletecredentialsopciones) Elimina credenciales almacenadas. ```typescript interface DeleteCredentialOptions { server: string; } ``` ## Mejores Prácticas [Section titled “Mejores Prácticas”](#mejores-prácticas) 1. **Siempre verificar disponibilidad primero** ```typescript const result = await NativeBiometric.isAvailable(); if (!result.isAvailable) { // Volver a autenticación con contraseña } ``` 2. **Proporcionar razones claras** Siempre explica por qué necesitas autenticación biométrica para generar confianza en el usuario. 3. **Manejar errores con gracia** ```typescript try { await NativeBiometric.verifyIdentity({ reason: "Acceder a tus datos seguros" }); } catch (error) { // Manejar cancelación o fallo console.log("Autenticación fallida o cancelada"); } ``` 4. **Usar alternativas apropiadas** Habilita la alternativa de código de acceso para dispositivos donde la biometría puede fallar. ## Diferencias por Plataforma [Section titled “Diferencias por Plataforma”](#diferencias-por-plataforma) ### iOS [Section titled “iOS”](#ios-1) * Soporta Touch ID y Face ID * Requiere `NSFaceIDUsageDescription` para Face ID * Alternativa de código de acceso del dispositivo disponible ### Android [Section titled “Android”](#android-1) * Soporta reconocimiento de huella dactilar, facial e iris * Requiere permiso `USE_BIOMETRIC` * API de Biometric Prompt para Android 9+ # @Capgo/native-market > Un Plugin Capacitor para redirigir usuarios a las tiendas de aplicaciones (Google Play o Apple Aplicación Store) desde tu aplicación móvil. Multiplataforma Funciona sin problemas en plataformas Android e iOS 🚀 Integración fácil API simple para redirigir usuarios a calificar o ver tu Aplicación en sus respectivas tiendas de aplicaciones 💨 Soporte TypeScript Soporte completo de TypeScript para una mejor experiencia de desarrollo 😊 Documentación Completa Consulta la [Documentación](/docs/plugins/native-market/getting-started/) para dominar el Plugin en solo unos minutos. # Comenzar > Aprende cómo instalar y usar el Plugin Native Market para redirigir usuarios a Google Play Store o Apple Aplicación Store desde tu aplicación Capacitor. 1. **Instalar el paquete** * npm ```sh npm i @Capgo/native-market ``` * pnpm ```sh pnpm add @Capgo/native-market ``` * yarn ```sh yarn add @Capgo/native-market ``` * bun ```sh bun add @Capgo/native-market ``` 2. **Sincronizar con proyectos nativos** * npm ```sh npx cap sync ``` * pnpm ```sh pnpm cap sync ``` * yarn ```sh yarn cap sync ``` * bun ```sh bunx cap sync ``` ## Uso [Section titled “Uso”](#uso) Importa el Plugin y usa sus métodos para redirigir usuarios a las tiendas de aplicaciones: ```typescript import { NativeMarket } from '@capgo/native-market'; // Abrir listado en la tienda de aplicaciones const openAppStore = async () => { await NativeMarket.openStoreListing({ appId: 'com.example.app' // El ID de paquete de tu aplicación }); }; // Solicitar reseña de la aplicación const requestReview = async () => { await NativeMarket.requestReview(); }; // Abrir búsqueda en la tienda de aplicaciones const searchInStore = async () => { await NativeMarket.search({ terms: 'fitness app' // Términos de búsqueda }); }; ``` ## Referencia de API [Section titled “Referencia de API”](#referencia-de-api) ### openStoreListing(Opciones) [Section titled “openStoreListing(Opciones)”](#openstorelistingopciones) Abre el listado de la tienda de aplicaciones para la aplicación especificada. ```typescript interface OpenStoreListingOptions { appId: string; // Bundle ID en iOS, Nombre de paquete en Android } ``` ### requestReview() [Section titled “requestReview()”](#requestreview) Solicita una reseña dentro de la aplicación del usuario. En iOS 10.3+, esto muestra el diálogo de calificación sin salir de la aplicación. ### Buscar(Opciones) [Section titled “Buscar(Opciones)”](#buscaropciones) Abre la tienda de aplicaciones con resultados de búsqueda. ```typescript interface SearchOptions { terms: string; // Términos de búsqueda a usar } ``` ## Notas de Plataforma [Section titled “Notas de Plataforma”](#notas-de-plataforma) ### iOS [Section titled “iOS”](#ios) * Usa `SKStoreReviewController` para reseñas dentro de la aplicación en iOS 10.3+ * Vuelve a abrir Aplicación Store para versiones anteriores ### Android [Section titled “Android”](#android) * Abre Google Play Store * Usa la API de reseñas dentro de la aplicación cuando está disponible ## Ejemplo [Section titled “Ejemplo”](#ejemplo) ```typescript import { NativeMarket } from '@Capgo/native-market'; import { Capacitor } from '@Capacitor/core'; Exportar Clase AppService { async rateApp() { try { // Intentar reseña dentro de la aplicación primero await NativeMarket.requestReview(); } catch (Error) { // Alternativa a abrir el listado de la tienda const platform = Capacitor.getPlatform(); const appId = platform === 'ios' ? 'id123456789' // Tu ID de aplicación iOS : 'com.Ejemplo.Aplicación'; // Tu nombre de paquete Android await NativeMarket.openStoreListing({ appId }); } } } ``` # @Capgo/native-purchases > Simplifica las compras y suscripciones en la aplicación con una API unificada que funciona perfectamente en las plataformas iOS y Android. API unificada API única para compras en iOS y Android 💳 Gestión de suscripciones Gestiona suscripciones con renovación automática 🔄 Validación de recibos Validación de recibos integrada para seguridad 🔒 Primeros pasos Consulta la [Guía de inicio](/docs/plugins/native-purchases/getting-started/) para instalar y configurar el Plugin. ## Guías de configuración de plataforma [Section titled “Guías de configuración de plataforma”](#guías-de-configuración-de-plataforma) Configuración de Android Guías completas para configurar compras en la aplicación en Android: * [Configurar pruebas de Sandbox](/docs/plugins/native-purchases/android-sandbox-testing/) * [Crear suscripciones](/docs/plugins/native-purchases/android-create-subscription/) * [Ofertas introductorias](/docs/plugins/native-purchases/android-introductory-offer/) Configuración de iOS Guías completas para configurar compras en la aplicación en iOS: * [Configurar pruebas de Sandbox](/docs/plugins/native-purchases/ios-sandbox-testing/) * [Grupos de suscripciones](/docs/plugins/native-purchases/ios-subscription-group/) * [Crear suscripciones](/docs/plugins/native-purchases/ios-create-subscription/) * [Ofertas introductorias](/docs/plugins/native-purchases/ios-introductory-offer/) # Crear Suscripción Renovable Automática en Android > Guía paso a paso para crear suscripciones renovables automáticas en Google Play Console para el Plugin native-purchases. Las suscripciones renovables automáticas proporcionan acceso a contenido, servicios o funciones premium en tu aplicación de manera continua. Esta guía te ayudará a crear y configurar suscripciones en Google Play Console. ## Descripción General [Section titled “Descripción General”](#descripción-general) Las suscripciones se renuevan automáticamente al final de cada período de facturación hasta que el usuario cancela. Son ideales para: * Acceso a contenido premium * Experiencias sin anuncios * Almacenamiento en la nube * Servicios continuos ## Crear una Suscripción [Section titled “Crear una Suscripción”](#crear-una-suscripción) 1. **Navegar a Suscripciones** En Google Play Console, selecciona tu Aplicación y elige **Monetizar > Suscripciones** del menú izquierdo. Haz clic en el botón **Crear suscripción** para comenzar. ![Navegar a suscripciones](/native-purchases/android/create-subscription/navigate-to-subscriptions.webp) 2. **Ingresar Información Básica** Proporciona un nombre de suscripción e ID de producto. El ID de producto es necesario para la configuración en tu Aplicación y no se puede cambiar después. ![Ingresar detalles de suscripción](/native-purchases/android/create-subscription/enter-subscription-details.webp) 3. **Crear Plan Base** Google Play requiere exactamente un plan base por suscripción. El Plugin native-purchases soporta solo un plan base para mantener compatibilidad con iOS. Haz clic en **Agregar plan base** para continuar. ![Crear plan base](/native-purchases/android/create-subscription/create-base-plan.webp) 4. **Configurar Detalles del Plan Base** Ingresa: * **ID del plan base**: Identificador único para este plan * **Período de facturación**: Con qué frecuencia se cobra a los usuarios (semanal, mensual, anual, etc.) * **Período de gracia**: Ventana de tiempo durante la cual Google mantiene la suscripción mientras reintenta el pago antes de la cancelación ![Configurar plan base](/native-purchases/android/create-subscription/configure-base-plan-details.webp) 5. **Configurar Precios** Accede a la sección de precios y selecciona todos los países/regiones donde quieras ofrecer la suscripción. ![Seleccionar regiones](/native-purchases/android/create-subscription/select-pricing-regions.webp) 6. **Configurar Precio** Establece tu precio base en tu moneda principal. Google Play convierte automáticamente esto a monedas locales. ![Establecer precio](/native-purchases/android/create-subscription/set-base-price.webp) 7. **Revisar Precios Regionales** Revisa los precios convertidos automáticamente para cada país. Puedes ajustar precios individuales si es necesario. ![Revisar precios](/native-purchases/android/create-subscription/review-regional-pricing.webp) 8. **Guardar Configuración** Guarda tu configuración de precios. ![Guardar precios](/native-purchases/android/create-subscription/save-pricing-configuration.webp) 9. **Activar Suscripción** Haz clic en el botón **Activar** para hacer que tu producto de suscripción esté disponible para compra. ![Activar suscripción](/native-purchases/android/create-subscription/activate-subscription.webp) ## Consideraciones Importantes [Section titled “Consideraciones Importantes”](#consideraciones-importantes) ### Limitación del Plan Base [Section titled “Limitación del Plan Base”](#limitación-del-plan-base) El Plugin native-purchases requiere exactamente un plan base por suscripción para asegurar consistencia con el manejo de suscripciones de iOS. No se soportan múltiples planes base. ### Período de Gracia [Section titled “Período de Gracia”](#período-de-gracia) El período de gracia permite a Google Play reintentar pagos fallidos mientras mantiene el acceso a la suscripción del usuario. Los períodos de gracia comunes son: * 3 días para suscripciones mensuales * 7 días para suscripciones más largas ### Estado de Suscripción [Section titled “Estado de Suscripción”](#estado-de-suscripción) Después de la creación, tu suscripción estará en estado “Borrador” hasta que sea activada. Puedes probar suscripciones en borrador en modo sandbox. ## Usar en tu Aplicación [Section titled “Usar en tu Aplicación”](#usar-en-tu-aplicación) Una vez creada, referencia la suscripción en tu Aplicación usando el ID de producto: ```typescript import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases'; // Cargar información de suscripción const { products } = await NativePurchases.getProducts({ productIdentifiers: ['com.example.premium.monthly'], productType: PURCHASE_TYPE.SUBS, }); const product = products[0]; console.log(`${product.title} — ${product.priceString}`); // Comprar (planIdentifier = ID del Plan Base de Google Play Console) const transaction = await NativePurchases.purchaseProduct({ productIdentifier: 'com.example.premium.monthly', planIdentifier: 'monthly-plan', // REQUERIDO en Android, ignorado en iOS productType: PURCHASE_TYPE.SUBS, }); console.log('ID de Transacción', transaction.transactionId); // Más tarde, verificar estado de compra const { purchases } = await NativePurchases.getPurchases({ productType: PURCHASE_TYPE.SUBS, }); const subscription = purchases.find( (purchase) => purchase.productIdentifier === 'com.example.premium.monthly', ); if (subscription && subscription.purchaseState === 'PURCHASED' && subscription.isAcknowledged) { console.log('Suscripción activa localmente'); // Para expiración/cancelación, valida purchaseToken a través de tu backend } ``` ## Próximos Pasos [Section titled “Próximos Pasos”](#próximos-pasos) * [Crear una oferta introductoria](/docs/plugins/native-purchases/android-introductory-offer/) para atraer nuevos suscriptores * [Configurar pruebas en sandbox](/docs/plugins/native-purchases/android-sandbox-testing/) para probar tus suscripciones * Configurar validación de recibos en el backend para seguridad ## Solución de Problemas [Section titled “Solución de Problemas”](#solución-de-problemas) **La suscripción no aparece en la Aplicación:** * Verifica que el ID de producto coincida exactamente * Asegúrate de que la suscripción esté activada * Verifica que tu Aplicación tenga el nombre de paquete correcto * Espera 2-3 horas después de la activación para que los cambios se propaguen **Errores del plan base:** * Asegúrate de tener exactamente un plan base * Verifica que todos los campos requeridos estén completados * Verifica que el período de facturación sea válido **Problemas de precios:** * Confirma que al menos un país esté seleccionado * Verifica que el precio base sea mayor que el mínimo permitido * Verifica que las tasas de conversión de moneda sean aceptables # Crear Oferta Introductoria de Suscripción en Android > Aprende cómo crear ofertas introductorias para suscripciones renovables automáticas en Android para atraer nuevos suscriptores. Las ofertas introductorias te permiten proporcionar a usuarios elegibles un período de prueba gratuito o un precio introductorio con descuento. Después de que concluya el período introductorio, las suscripciones se renuevan automáticamente a precio estándar a menos que se cancelen. ## Descripción General [Section titled “Descripción General”](#descripción-general) Las ofertas introductorias son una herramienta poderosa para: * Reducir barreras de entrada para nuevos suscriptores * Aumentar tasas de conversión * Permitir a los usuarios probar tus funciones premium sin riesgo * Construir relaciones de suscriptores a largo plazo ## Elegibilidad [Section titled “Elegibilidad”](#elegibilidad) Los usuarios pueden recibir una oferta introductoria si no han comprado previamente o recibido una oferta introductoria para la suscripción. Google Play maneja la elegibilidad automáticamente. ## Requisitos Previos [Section titled “Requisitos Previos”](#requisitos-previos) Primero debes [crear una suscripción renovable automática](/docs/plugins/native-purchases/android-create-subscription/) antes de agregar una oferta introductoria. ## Crear una Oferta Introductoria [Section titled “Crear una Oferta Introductoria”](#crear-una-oferta-introductoria) 1. **Acceder a Configuración de Oferta** Navega a tu suscripción en Google Play Console y selecciona el botón **Agregar oferta**. ![Botón agregar oferta](/native-purchases/android/introductory-offer/add-offer-button.webp) 2. **Seleccionar Plan Base** Aparecerá un modal que requiere que elijas tu plan base. Típicamente, solo tendrás un plan base. Haz clic en **Agregar oferta** para continuar. ![Seleccionar plan base](/native-purchases/android/introductory-offer/select-base-plan.webp) 3. **Configurar Detalles de la Oferta** Ingresa la siguiente información: **ID de Oferta**: Un identificador único para esta oferta **Elegibilidad**: Elige quién puede recibir esta oferta * **Nuevos clientes**: Solo usuarios que nunca se han suscrito * **Clientes existentes**: Usuarios que previamente se suscribieron * **Determinado por desarrollador**: Lógica de elegibilidad personalizada (no soportada por native-purchases) :::Precaución El Plugin native-purchases no soporta la opción de elegibilidad “Determinado por desarrollador”. Usa “Nuevos clientes” o “Clientes existentes” en su lugar. ::: ![Configurar oferta](/native-purchases/android/introductory-offer/configure-offer-details.webp) 4. **Agregar Fases** Haz clic en **Agregar fase** en la parte inferior de la página para definir la estructura de tu oferta. Puedes agregar hasta dos fases, permitiendo combinaciones como: * Solo prueba gratuita * Solo precio con descuento * Prueba gratuita seguida de pago recurrente con descuento 5. **Seleccionar Tipo de Fase** Elige entre tres tipos de fase: **Prueba Gratuita** * Acceso complementario por una duración establecida * Ejemplo: 7 días gratis, luego $9.99/mes **Pago Único** * Precio con descuento de una sola vez por un período específico * Ejemplo: $1.99 por 2 meses, luego $9.99/mes **Pago Recurrente con Descuento** * Tasa reducida por ciclo de facturación para múltiples ciclos * Ejemplo: $4.99/mes por 3 meses, luego $9.99/mes 6. **Configurar Duración de la Fase** Establece cuánto durará la fase introductoria: * Días, semanas o meses * Número de ciclos de facturación 7. **Finalizar y Activar** Haz clic en **Aplicar**, luego **Guardar** para activar la oferta. El botón **Activar** estará disponible una vez guardado. ## Ejemplos de Fases de Oferta [Section titled “Ejemplos de Fases de Oferta”](#ejemplos-de-fases-de-oferta) ### Ejemplo 1: Prueba Gratuita Simple [Section titled “Ejemplo 1: Prueba Gratuita Simple”](#ejemplo-1-prueba-gratuita-simple) * Fase 1: 7 días gratis * Luego: $9.99/mes precio estándar ### Ejemplo 2: Introducción con Descuento [Section titled “Ejemplo 2: Introducción con Descuento”](#ejemplo-2-introducción-con-descuento) * Fase 1: $1.99 por el primer mes * Luego: $9.99/mes precio estándar ### Ejemplo 3: Prueba Extendida + Descuento [Section titled “Ejemplo 3: Prueba Extendida + Descuento”](#ejemplo-3-prueba-extendida--descuento) * Fase 1: 14 días gratis * Fase 2: $4.99/mes por 2 meses * Luego: $9.99/mes precio estándar ## Usar en tu Aplicación [Section titled “Usar en tu Aplicación”](#usar-en-tu-aplicación) El Plugin native-purchases maneja automáticamente la elegibilidad y presentación de ofertas introductorias: ```typescript import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases'; // Obtener productos (incluye metadatos de oferta introductoria) const { products } = await NativePurchases.getProducts({ productIdentifiers: ['com.example.premium.monthly'], productType: PURCHASE_TYPE.SUBS, }); const product = products[0]; if (product.introductoryPrice) { console.log(`Precio intro: ${product.introductoryPriceString}`); console.log(`Precio regular: ${product.priceString}`); console.log( `Duración de oferta: ${product.introductoryPrice.subscriptionPeriod?.numberOfUnits} ${product.introductoryPrice.subscriptionPeriod?.unit}`, ); } else { console.log('No hay oferta introductoria configurada para este producto'); } // Comprar (Google Play aplica precios introductorios automáticamente si el usuario es elegible) const transaction = await NativePurchases.purchaseProduct({ productIdentifier: 'com.example.premium.monthly', planIdentifier: 'monthly-plan', // ID del Plan Base de Google Play Console productType: PURCHASE_TYPE.SUBS, }); console.log('Transacción de compra introductoria', transaction.transactionId); ``` ## Mejores Prácticas [Section titled “Mejores Prácticas”](#mejores-prácticas) ### Duración de la Oferta [Section titled “Duración de la Oferta”](#duración-de-la-oferta) * **Pruebas gratuitas**: 3-14 días es óptimo para la mayoría de las Aplicaciones * **Períodos con descuento**: 1-3 meses funciona bien para construir hábito * **Descuento de precio**: 50-70% del precio regular impulsa conversiones ### Marketing [Section titled “Marketing”](#marketing) * Muestra claramente la oferta introductoria y el precio regular * Muestra qué sucede después del período introductorio * Haz que la cancelación sea fácil y transparente * Recuerda a los usuarios antes de que termine el período introductorio ### Pruebas A/B [Section titled “Pruebas A/B”](#pruebas-ab) Prueba diferentes estructuras de oferta: * Duración de prueba gratuita * Porcentaje de descuento * Duración del descuento * Fase única vs. multi-fase ## Notas Importantes [Section titled “Notas Importantes”](#notas-importantes) * Solo una oferta introductoria puede estar activa por suscripción a la vez * Los usuarios solo pueden reclamar una oferta introductoria una vez por suscripción * Las ofertas introductorias no se aplican a actualizaciones/cambios de suscripción * Los cambios a ofertas introductorias no afectan a suscriptores existentes ## Solución de Problemas [Section titled “Solución de Problemas”](#solución-de-problemas) **La oferta introductoria no se muestra:** * Verifica que la oferta esté activada en Play Console * Verifica elegibilidad del usuario (puede haber usado la oferta antes) * Asegúrate de que la Aplicación esté usando la información más reciente del producto **Usuarios incorrectos recibiendo oferta:** * Revisa configuración de elegibilidad (nuevos vs. clientes existentes) * Verifica si el usuario se suscribió previamente en otro dispositivo * Verifica historial de cuenta de Play Store **La oferta no se aplica en la compra:** * Confirma que el ID de producto coincida exactamente * Verifica que la oferta aún esté activa y no haya expirado * Verifica configuración de rango de fechas para la oferta ## Próximos Pasos [Section titled “Próximos Pasos”](#próximos-pasos) * [Configurar pruebas en sandbox](/docs/plugins/native-purchases/android-sandbox-testing/) para probar tus ofertas * Monitorear tasas de conversión en analíticas de Play Console * Considera crear múltiples niveles de suscripción con diferentes ofertas # Guías de Revisión de Play Store de Android para IAP > Guía completa para pasar la revisión de Google Play con compras dentro de la aplicación y suscripciones, incluyendo requisitos de cumplimiento y mejores prácticas. Obtener la aprobación de tu Aplicación de Android en Google Play requiere cumplimiento con las políticas de Google, especialmente para Aplicaciones con compras dentro de la aplicación y suscripciones. Esta guía cubre todo lo que necesitas para pasar la revisión exitosamente. ## Requisitos de Facturación de Google Play [Section titled “Requisitos de Facturación de Google Play”](#requisitos-de-facturación-de-google-play) ### Sistema de Facturación Obligatorio