├── .github └── workflows │ ├── ci.yml │ ├── docs-preview.yml │ ├── docs-production.yml │ ├── pre-release.yml │ └── release.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── lerna.json ├── plugin ├── .eslintignore ├── .gitignore ├── .npmrc ├── .prettierignore ├── CHANGELOG.md ├── README.md ├── package.json ├── rollup.config.js ├── src │ └── index.ts └── tsconfig.json ├── scripts ├── .gitignore ├── changelog-release.mjs ├── definitions.ts ├── get-latest-capacitor-version.mjs ├── package-lock.json ├── package.json └── send-to-slack.mjs └── website ├── .gitignore ├── README.md ├── babel.config.js ├── docs ├── appflow │ ├── native │ │ └── getting-started.md │ └── web │ │ ├── getting-started.md │ │ ├── self-hosted.md │ │ └── using-appflow.md ├── choosing-a-communication.md ├── cli │ ├── commands │ │ ├── poc.md │ │ ├── serve-android.md │ │ ├── serve-ios.md │ │ └── sync.md │ ├── configuration.md │ └── overview.md ├── for-android │ ├── changelog.json │ ├── changelog.md │ ├── examples │ │ ├── ecommerce-app-live-updates.md │ │ └── ecommerce-app.md │ ├── getting-started.md │ ├── guide.md │ ├── how-to │ │ ├── advanced-configuration.md │ │ ├── define-api-in-typescript.md │ │ ├── multiple-portals-multiple-web-apps.md │ │ ├── multiple-portals-single-web-app.md │ │ ├── pull-in-web-bundle.md │ │ ├── reloading-with-live-updates.md │ │ ├── sharing-assets.md │ │ ├── sync-with-live-updates.md │ │ ├── use-portals-in-an-android-library.md │ │ ├── using-a-capacitor-plugin.md │ │ └── using-the-portals-plugin.md │ ├── live-updates.md │ ├── quick-start.md │ ├── tutorials │ │ ├── auth-token-example.md │ │ └── monorepo-example.md │ ├── upgrade-guides.md │ └── version-matrix.md ├── for-capacitor │ ├── example.md │ ├── live-updates.md │ ├── module-federation.md │ ├── overview.md │ ├── reference.md │ └── upgrade-guides.md ├── for-ios │ ├── changelog.json │ ├── changelog.md │ ├── examples │ │ ├── ecommerce-app-live-updates.md │ │ └── ecommerce-app.md │ ├── getting-started.md │ ├── how-to │ │ ├── advanced-configuration.md │ │ ├── define-api-in-typescript.md │ │ ├── multiple-portals-multiple-web-apps.md │ │ ├── multiple-portals-single-web-app.md │ │ ├── pull-in-web-bundle.md │ │ ├── reloading-with-live-updates.md │ │ ├── sharing-assets.md │ │ ├── sync-with-live-updates.md │ │ ├── use-portals-in-an-ios-library.md │ │ ├── using-a-capacitor-plugin.md │ │ └── using-the-portals-plugin.md │ ├── known-issues.md │ ├── live-updates.md │ ├── quick-start.md │ ├── tutorials │ │ ├── auth-token-example.md │ │ └── monorepo-example.md │ └── upgrade-guides.md ├── for-react-native │ ├── changelog.json │ ├── changelog.md │ ├── examples │ │ └── ecommerce.md │ ├── getting-started.md │ ├── guide.md │ ├── how-to │ │ ├── define-api-in-typescript.md │ │ ├── pull-in-web-bundle.md │ │ ├── sharing-assets.md │ │ ├── statically-define-portals.md │ │ ├── sync-with-live-updates.md │ │ ├── using-a-capacitor-plugin.md │ │ └── using-the-portals-plugin.md │ ├── known-issues.md │ ├── live-updates.md │ ├── tutorials │ │ ├── auth-token-example.md │ │ └── monorepo-example.md │ └── upgrade-guides.md ├── for-web │ ├── android-profiling.md │ ├── changelog.json │ ├── changelog.md │ ├── ios-profiling.md │ ├── overview.md │ ├── portals-plugin.md │ ├── sharing-assets.md │ ├── upgrade-guides.md │ └── web-vitals.md ├── getting-started.md ├── overview.md ├── what-are-live-updates.md └── what-is-a-portal.md ├── docusaurus.config.js ├── package-lock.json ├── package.json ├── sidebars.js ├── src ├── components │ ├── WistiaVideo │ │ └── index.tsx │ └── page │ │ └── changelog │ │ ├── index.tsx │ │ └── styles.module.css ├── styles │ └── custom.css └── util │ └── index.ts ├── static ├── .nojekyll └── img │ ├── android-run-script-thumbnail.webp │ ├── android-run-script.webp │ ├── builds-screen-thumbnail.webp │ ├── builds-screen.webp │ ├── components │ └── product-dropdown │ │ └── logo.png │ ├── create-a-new-build-selected-thumbnail.webp │ ├── create-a-new-build-selected.webp │ ├── create-a-new-build-thumbnail.webp │ ├── create-a-new-build.webp │ ├── deployments-screen-thumbnail.webp │ ├── deployments-screen.webp │ ├── ecommerce-storyboard.svg │ ├── favicon.ico │ ├── federated-capacitor-diagram.webp │ ├── how-to │ ├── ios-create-folder-references.png │ ├── ios-web-asset-folder.png │ ├── obtain-registration-key-1.png │ └── obtain-registration-key-2.png │ ├── import-existing-app-thumbnail.webp │ ├── import-existing-app.webp │ ├── known-issues │ └── spm-workarounds │ │ ├── 01-scheme-edit.png │ │ ├── 02-post-actions-select.png │ │ ├── 03-run-script-select.png │ │ ├── 04-build-settings-select.png │ │ ├── 05-script-entry.png │ │ └── 06-script-entry.png │ ├── logo-dark.png │ ├── logo-light.png │ ├── logo.png │ ├── logo.svg │ ├── module-federation-diagram.webp │ ├── personal-access-token-thumbnail.webp │ ├── personal-access-token.webp │ ├── portals-diagram-reactnative.png │ ├── portals-diagram-swift.png │ ├── portals-key-screenshot-thumbnail.webp │ ├── portals-key-screenshot.webp │ ├── portals-key-signup-screenshot-thumbnail.webp │ ├── portals-key-signup-screenshot.webp │ ├── profiling │ ├── android │ │ ├── full │ │ │ ├── profiling-console-01.webP │ │ │ ├── profiling-console-01.webp │ │ │ ├── profiling-console-02.webP │ │ │ ├── profiling-console-02.webp │ │ │ ├── profiling-elements.webP │ │ │ ├── profiling-elements.webp │ │ │ ├── profiling-inspect-devices.webP │ │ │ ├── profiling-inspect-devices.webp │ │ │ ├── profiling-lighthouse-00.webP │ │ │ ├── profiling-lighthouse-00.webp │ │ │ ├── profiling-lighthouse-01.webP │ │ │ ├── profiling-lighthouse-01.webp │ │ │ ├── profiling-lighthouse-02.webP │ │ │ ├── profiling-lighthouse-02.webp │ │ │ ├── profiling-lighthouse-03.webP │ │ │ ├── profiling-lighthouse-03.webp │ │ │ ├── profiling-network-01.webP │ │ │ ├── profiling-network-01.webp │ │ │ ├── profiling-open-01.webP │ │ │ ├── profiling-open-01.webp │ │ │ ├── profiling-open-02.webP │ │ │ └── profiling-open-02.webp │ │ └── thumb │ │ │ ├── profiling-console-01.webP │ │ │ ├── profiling-console-01.webp │ │ │ ├── profiling-console-02.webP │ │ │ ├── profiling-console-02.webp │ │ │ ├── profiling-elements.webP │ │ │ ├── profiling-elements.webp │ │ │ ├── profiling-inspect-devices.webP │ │ │ ├── profiling-inspect-devices.webp │ │ │ ├── profiling-lighthouse-00.webP │ │ │ ├── profiling-lighthouse-00.webp │ │ │ ├── profiling-lighthouse-01.webP │ │ │ ├── profiling-lighthouse-01.webp │ │ │ ├── profiling-lighthouse-02.webP │ │ │ ├── profiling-lighthouse-02.webp │ │ │ ├── profiling-lighthouse-03.webP │ │ │ ├── profiling-lighthouse-03.webp │ │ │ ├── profiling-network-01.webP │ │ │ ├── profiling-network-01.webp │ │ │ ├── profiling-open-01.webP │ │ │ ├── profiling-open-01.webp │ │ │ └── profiling-open-02.webP │ └── ios │ │ ├── full │ │ ├── profiling-console-01.webP │ │ ├── profiling-console-02.webP │ │ ├── profiling-export-01.webP │ │ ├── profiling-export-02.webP │ │ ├── profiling-import-01.webP │ │ ├── profiling-import-02.webP │ │ ├── profiling-network-01.webP │ │ ├── profiling-network-02.webP │ │ ├── profiling-network-03.webP │ │ ├── profiling-open-01.webP │ │ ├── profiling-open-02.webP │ │ ├── profiling-open-03.webP │ │ ├── profiling-timeline-record-01.webP │ │ ├── profiling-timeline-record-02.webP │ │ ├── profiling-timeline-record-03.webP │ │ └── profiling-timeline-record-04.webP │ │ └── thumb │ │ ├── profiling-console-01.webP │ │ ├── profiling-console-02.webP │ │ ├── profiling-export-01.webP │ │ ├── profiling-export-02.webP │ │ ├── profiling-import-01.webP │ │ ├── profiling-import-02.webP │ │ ├── profiling-network-01.webP │ │ ├── profiling-network-02.webP │ │ ├── profiling-network-03.webP │ │ ├── profiling-open-01.webP │ │ ├── profiling-open-02.webP │ │ ├── profiling-open-03.webP │ │ ├── profiling-timeline-record-01.webP │ │ ├── profiling-timeline-record-02.webP │ │ ├── profiling-timeline-record-03.webP │ │ └── profiling-timeline-record-04.webP │ ├── start-by-adding-an-app-thumbnail.webp │ ├── start-by-adding-an-app.webp │ ├── tutorial │ ├── docsVersionDropdown.png │ └── localeDropdown.png │ ├── xcode-run-script-thumbnail.webp │ └── xcode-run-script.webp └── vercel.json /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | paths: 6 | - 'plugin/**' 7 | - '.github/workflows/ci.yml' 8 | pull_request: 9 | paths: 10 | - 'plugin/**' 11 | - '.github/workflows/ci.yml' 12 | jobs: 13 | setup: 14 | runs-on: ubuntu-latest 15 | timeout-minutes: 30 16 | steps: 17 | - name: Cancel Previous Runs 18 | uses: styfle/cancel-workflow-action@ce177499ccf9fd2aded3b0426c97e5434c2e8a73 19 | with: 20 | access_token: ${{ secrets.GITHUB_TOKEN }} 21 | - name: Get Latest 22 | uses: actions/setup-node@v1 23 | with: 24 | node-version: 14.x 25 | - uses: actions/checkout@v2 26 | - name: Restore Dependency Cache 27 | uses: actions/cache@v1 28 | with: 29 | path: ~/.npm 30 | key: ${{ runner.OS }}-dependency-cache-${{ hashFiles('**/package.json') }} 31 | - name: Get Package Version 32 | id: package-version 33 | uses: martinbeentjes/npm-get-version-action@master 34 | with: 35 | path: core/ 36 | verify-plugin: 37 | runs-on: ubuntu-latest 38 | timeout-minutes: 30 39 | needs: 40 | - setup 41 | steps: 42 | - uses: actions/setup-node@v1 43 | with: 44 | node-version: 16.x 45 | - uses: actions/checkout@v2 46 | - name: Restore Dependency Cache 47 | uses: actions/cache@v1 48 | with: 49 | path: ~/.npm 50 | key: ${{ runner.OS }}-dependency-cache-${{ hashFiles('**/package.json') }} 51 | - run: npm install 52 | working-directory: plugin 53 | - run: npm run build 54 | working-directory: plugin 55 | -------------------------------------------------------------------------------- /.github/workflows/docs-preview.yml: -------------------------------------------------------------------------------- 1 | name: Deploy docs PR preview 2 | 3 | on: 4 | pull_request: 5 | paths: 6 | - .github/workflows/docs-preview.yml 7 | - website/** 8 | 9 | permissions: 10 | contents: read 11 | deployments: write 12 | id-token: write 13 | 14 | env: 15 | SLUG: portals-${{ github.event.number }}-${{ github.run_id }} 16 | 17 | defaults: 18 | run: 19 | working-directory: website 20 | 21 | jobs: 22 | deploy: 23 | runs-on: ubuntu-latest 24 | timeout-minutes: 15 25 | environment: 26 | name: preview-${{ github.event.number }} 27 | url: https://${{ env.SLUG }}.ionicpreview.com 28 | steps: 29 | - uses: actions/checkout@v3 30 | - uses: actions/setup-node@v3 31 | with: 32 | node-version: 18 33 | cache: npm 34 | cache-dependency-path: website/package-lock.json 35 | - name: Build 36 | run: | 37 | npm ci 38 | npm run build 39 | - uses: aws-actions/configure-aws-credentials@v1 40 | with: 41 | role-to-assume: arn:aws:iam::319312831725:role/github-docs 42 | aws-region: us-east-1 43 | - name: Deploy 44 | run: | 45 | aws s3 sync build/ s3://ionic-docs/preview/${{ env.SLUG }}/ --exclude '*.html' --cache-control max-age=31536000 --only-show-errors 46 | aws s3 sync build/ s3://ionic-docs/preview/${{ env.SLUG }}/ --exclude '*' --include '*.html' --cache-control max-age=60 --only-show-errors 47 | -------------------------------------------------------------------------------- /.github/workflows/docs-production.yml: -------------------------------------------------------------------------------- 1 | name: Deploy docs to production 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths: 8 | - .github/workflows/docs-production.yml 9 | - website/** 10 | 11 | permissions: 12 | contents: read 13 | deployments: write 14 | id-token: write 15 | 16 | env: 17 | SLUG: portals 18 | 19 | defaults: 20 | run: 21 | working-directory: website 22 | 23 | jobs: 24 | deploy: 25 | runs-on: ubuntu-latest 26 | timeout-minutes: 15 27 | environment: 28 | name: production 29 | url: https://ionic.io/docs/${{ env.SLUG }} 30 | steps: 31 | - uses: actions/checkout@v3 32 | - uses: actions/setup-node@v3 33 | with: 34 | node-version: 18 35 | cache: npm 36 | cache-dependency-path: website/package-lock.json 37 | - name: Build 38 | run: | 39 | npm ci 40 | npm run build 41 | - uses: aws-actions/configure-aws-credentials@v1 42 | with: 43 | role-to-assume: arn:aws:iam::319312831725:role/github-docs 44 | aws-region: us-east-1 45 | - name: Deploy 46 | run: | 47 | aws s3 sync build/ s3://ionic-docs/production/${{ env.SLUG }}/ --exclude '*.html' --cache-control max-age=31536000 --only-show-errors 48 | aws s3 sync build/ s3://ionic-docs/production/${{ env.SLUG }}/ --exclude '*' --include '*.html' --cache-control max-age=60 --only-show-errors 49 | -------------------------------------------------------------------------------- /.github/workflows/pre-release.yml: -------------------------------------------------------------------------------- 1 | name: Pre-Release 2 | 3 | on: 4 | push: 5 | branches: 6 | - 'release/**' 7 | 8 | jobs: 9 | increment-version: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | - uses: actions/setup-node@v1 14 | with: 15 | node-version: 16.x 16 | - name: Assign version to RELEASE_VERSION environment variable 17 | run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/*/}" >> $GITHUB_ENV 18 | - name: Bump npm package version 19 | run: npm version $RELEASE_VERSION --no-git-tag-version --allow-same-version 20 | env: 21 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 22 | working-directory: plugin 23 | # Disabling this for now. It works locally? 🤷 24 | # - name: Generate changelog 25 | # run: npx -y conventional-changelog-cli -p conventionalcommits -i CHANGELOG.md -s -k plugin/package.json 26 | - name: Push version bump and changelog commit 27 | uses: EndBug/add-and-commit@v9 28 | - name: Generate .npmrc 29 | run: echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > .npmrc 30 | working-directory: plugin 31 | - name: Validate Publish Flow 32 | run: | 33 | npm install 34 | npm publish --dry-run 35 | env: 36 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 37 | working-directory: plugin 38 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Publish NPM Package 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*' 7 | 8 | jobs: 9 | publish-to-npm: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | - uses: actions/setup-node@v1 14 | with: 15 | node-version: 16.x 16 | - name: Publish package 17 | run: | 18 | echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > .npmrc 19 | npm install 20 | npm publish 21 | env: 22 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 23 | working-directory: plugin 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | # ========== 4 | # Plugin ignore 5 | # ========== 6 | node_modules 7 | plugin/dist 8 | .vercel 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 |
6 | ⚡️ A supercharged native Web View for iOS and Android ⚡️ 7 |
8 |
9 |

10 | 11 | 12 | 13 |

14 |

15 | 16 | 17 | 18 |

19 |

20 | 21 | Follow @ionicframework 22 |

23 | 24 | --- 25 | 26 | Ionic Portals is a supercharged native Web View component for iOS and Android that lets you add web-based experiences to native mobile apps. It enables native and web teams to better collaborate and bring new and existing web experiences to mobile in a safe, controlled way. 27 | 28 | ## Getting Started 29 | 30 | See our docs to [get started with Portals](https://ionic.io/docs/portals/getting-started/guide). 31 | 32 | ## Registration 33 | 34 | The Ionic Portals library for [Android](https://github.com/ionic-team/ionic-portals-android) and [iOS](https://github.com/ionic-team/ionic-portals-ios) requires a license key to use. Once you have integrated Portals into your project, login to your ionic account to get a key. See our doc on [how to register for your Portals license key](https://ionic.io/docs/portals/getting-started#signup-for-access) and refer to the [Android](https://ionic.io/docs/portals/getting-started/android) or [iOS](https://ionic.io/docs/portals/getting-started/iOS) getting started guides to see where to add your key. 35 | 36 | ## FAQ 37 | 38 | ### Where is the iOS and Android Code? 39 | 40 | This repo contains the documentation and web plugin code for Portals. The [Android](https://github.com/ionic-team/ionic-portals-android) and [iOS](https://github.com/ionic-team/ionic-portals-ios) native libraries are split into their own repos to more easily version and release for their respective platforms. 41 | 42 | ### What is the pricing for Portals use? 43 | 44 | [Contact our sales team](https://ionic.io/portals#sales) for more information about pricing. 45 | 46 | ### Is Portals Open Source? 47 | 48 | See the [license](https://github.com/ionic-team/ionic-portals/blob/main/LICENSE.md). 49 | 50 | ### How is Portals Related to Capacitor and Ionic? 51 | 52 | Ionic Portals is a solution that lets you add web-based experiences to your native mobile apps. Portals uses [Capacitor](https://capacitorjs.com) as a bridge between the native code and the web code to allow for cross-communication between the two layers. Because Portals uses Capacitor under the hood, you are able to use any existing [Capacitor Plugins](https://capacitorjs.com/docs/plugins) and even most [Cordova Plugins](https://capacitorjs.com/docs/plugins/cordova) while continuing to use your existing native workflow. 53 | 54 | [Ionic Framework](https://ionicframework.com/) is the open source mobile app development framework that makes it easy to build top quality native and progressive web apps with web technologies. You can develop your web experiences with Ionic, but it's not necessary to use Portals. 55 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "capacitorVersion": "7.0.0", 3 | "iosMinVersion": "14.0", 4 | "androidMinSdk": "23", 5 | "rnMinVersion": "0.75.4", 6 | "androidLiveUpdatesVersion": "0.5.5", 7 | "rnVersion": "0.8.0", 8 | "iosVersion": "0.12.0", 9 | "androidVersion": "0.12.0", 10 | "cliVersion": "0.3.1", 11 | "version": "0.12.0" 12 | } -------------------------------------------------------------------------------- /plugin/.eslintignore: -------------------------------------------------------------------------------- 1 | build 2 | dist 3 | types 4 | -------------------------------------------------------------------------------- /plugin/.gitignore: -------------------------------------------------------------------------------- 1 | # node files 2 | dist 3 | node_modules 4 | types 5 | build 6 | 7 | # iOS files 8 | Pods 9 | Podfile.lock 10 | Build 11 | xcuserdata 12 | 13 | # macOS files 14 | .DS_Store 15 | 16 | 17 | 18 | # Based on Android gitignore template: https://github.com/github/gitignore/blob/HEAD/Android.gitignore 19 | 20 | # Built application files 21 | *.apk 22 | *.ap_ 23 | 24 | # Files for the ART/Dalvik VM 25 | *.dex 26 | 27 | # Java class files 28 | *.class 29 | 30 | # Generated files 31 | bin 32 | gen 33 | out 34 | 35 | # Gradle files 36 | .gradle 37 | build 38 | 39 | # Local configuration file (sdk path, etc) 40 | local.properties 41 | 42 | # Proguard folder generated by Eclipse 43 | proguard 44 | 45 | # Log Files 46 | *.log 47 | 48 | # Android Studio Navigation editor temp files 49 | .navigation 50 | 51 | # Android Studio captures folder 52 | captures 53 | 54 | # IntelliJ 55 | *.iml 56 | .idea 57 | 58 | # Keystore files 59 | # Uncomment the following line if you do not want to check your keystore files in. 60 | #*.jks 61 | 62 | # External native build folder generated in Android Studio 2.2 and later 63 | .externalNativeBuild 64 | 65 | package-lock.json 66 | -------------------------------------------------------------------------------- /plugin/.npmrc: -------------------------------------------------------------------------------- 1 | @native-portal:registry=https://npm.pkg.github.com -------------------------------------------------------------------------------- /plugin/.prettierignore: -------------------------------------------------------------------------------- 1 | build 2 | dist 3 | -------------------------------------------------------------------------------- /plugin/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | # [0.5.0](https://github.com/ionic-team/ionic-portals/compare/0.4.2...0.5.0) (2022-02-16) 7 | 8 | **Note:** Version bump only for package @ionic/portals 9 | 10 | 11 | 12 | 13 | 14 | ## [0.4.1](https://github.com/ionic-team/ionic-portals/compare/0.4.0...0.4.1) (2022-01-27) 15 | 16 | **Note:** Version bump only for package @ionic/portals 17 | 18 | 19 | 20 | 21 | 22 | ## [0.3.1](https://github.com/ionic-team/ionic-portals/compare/0.3.0...0.3.1) (2021-12-17) 23 | 24 | **Note:** Version bump only for package @ionic/portals 25 | 26 | 27 | 28 | 29 | 30 | # [0.3.0](https://github.com/ionic-team/ionic-portals/compare/0.3.0-pre...0.3.0) (2021-11-18) 31 | 32 | **Note:** Version bump only for package @ionic/portals 33 | 34 | 35 | 36 | 37 | 38 | ## [0.2.2](https://github.com/ionic-team/ionic-portals/compare/0.2.1...0.2.2) (2021-10-15) 39 | 40 | **Note:** Version bump only for package @ionic/portals 41 | 42 | 43 | 44 | 45 | 46 | ## [0.2.1](https://github.com/ionic-team/ionic-portals/compare/0.2.0...0.2.1) (2021-09-27) 47 | 48 | **Note:** Version bump only for package @ionic/portals 49 | 50 | 51 | 52 | 53 | 54 | # 0.2.0 (2021-09-14) 55 | 56 | 57 | ### Features 58 | 59 | * **ios:** registration error logic ([5a00853](https://github.com/ionic-team/ionic-portals/commit/5a0085344d8ac0f43b64c7ce6a69ed09ba1a20c8)) 60 | * ios pub/sub changes and api updates ([#14](https://github.com/ionic-team/ionic-portals/issues/14)) ([e923499](https://github.com/ionic-team/ionic-portals/commit/e923499302005e312cb9412b498ba9b34504a6f6)) 61 | * **ios:** adding ios libs ([26bb9ce](https://github.com/ionic-team/ionic-portals/commit/26bb9ce981668157f07441502713eda8ce419eab)) 62 | 63 | 64 | 65 | 66 | 67 | ## [0.1.4](https://github.com/ionic-team/ionic-portals/compare/0.1.3...0.1.4) (2021-09-14) 68 | 69 | 70 | ### Bug Fixes 71 | 72 | * **plugin:** silly change4 ([64cadad](https://github.com/ionic-team/ionic-portals/commit/64cadadc90c08ee3b51941f4a7438b24ff59b5c9)) 73 | 74 | 75 | 76 | 77 | 78 | ## [0.1.3](https://github.com/ionic-team/ionic-portals/compare/0.1.2...0.1.3) (2021-09-14) 79 | 80 | 81 | ### Bug Fixes 82 | 83 | * **plugin:** silly change3 ([1f42546](https://github.com/ionic-team/ionic-portals/commit/1f4254692800fbae2423df1ece7d13139f651c2e)) 84 | 85 | 86 | 87 | 88 | 89 | ## [0.1.2](https://github.com/ionic-team/ionic-portals/compare/0.1.1...0.1.2) (2021-09-14) 90 | 91 | 92 | ### Bug Fixes 93 | 94 | * **plugin:** silly change2 ([d84e0e7](https://github.com/ionic-team/ionic-portals/commit/d84e0e7db7caa61cee4d0c9d910d287365e35e12)) 95 | 96 | 97 | 98 | 99 | 100 | ## [0.1.1](https://github.com/ionic-team/ionic-portals/compare/0.1.0...0.1.1) (2021-09-14) 101 | 102 | 103 | ### Bug Fixes 104 | 105 | * **plugin:** silly change ([27d32bd](https://github.com/ionic-team/ionic-portals/commit/27d32bd5c3de4dd1e59a43ff362e2eb450ebdfba)) 106 | 107 | 108 | 109 | 110 | 111 | # 0.1.0 (2021-09-14) 112 | 113 | 114 | ### Features 115 | 116 | * **ios:** registration error logic ([5a00853](https://github.com/ionic-team/ionic-portals/commit/5a0085344d8ac0f43b64c7ce6a69ed09ba1a20c8)) 117 | * ios pub/sub changes and api updates ([#14](https://github.com/ionic-team/ionic-portals/issues/14)) ([e923499](https://github.com/ionic-team/ionic-portals/commit/e923499302005e312cb9412b498ba9b34504a6f6)) 118 | * **ios:** adding ios libs ([26bb9ce](https://github.com/ionic-team/ionic-portals/commit/26bb9ce981668157f07441502713eda8ce419eab)) 119 | -------------------------------------------------------------------------------- /plugin/README.md: -------------------------------------------------------------------------------- 1 | # Ionic Portals 2 | 3 | Plugin SDK for Ionic Portals 4 | 5 | ## Install 6 | 7 | ```bash 8 | npm install @ionic/portals 9 | ``` 10 | 11 | ## API 12 | 13 | 14 | 15 | * [`getInitialContext()`](#getinitialcontext) 16 | * [`publish(...)`](#publish) 17 | * [`subscribe(...)`](#subscribe) 18 | * [`unsubscribe(...)`](#unsubscribe) 19 | * [Interfaces](#interfaces) 20 | 21 | 22 | 23 | 24 | 25 | 26 | ### getInitialContext() 27 | 28 | ```typescript 29 | getInitialContext() => any 30 | ``` 31 | 32 | **Returns:** any 33 | 34 | -------------------- 35 | 36 | 37 | ### publish(...) 38 | 39 | ```typescript 40 | publish(message: PortalMessage) => any 41 | ``` 42 | 43 | | Param | Type | 44 | | ------------- | -------------------------------------------------------------------- | 45 | | **`message`** | PortalMessage<TData> | 46 | 47 | **Returns:** any 48 | 49 | -------------------- 50 | 51 | 52 | ### subscribe(...) 53 | 54 | ```typescript 55 | subscribe(options: SubscribeOptions, callback: SubscriptionCallback) => any 56 | ``` 57 | 58 | | Param | Type | 59 | | -------------- | ------------------------------------------------------------- | 60 | | **`options`** | SubscribeOptions | 61 | | **`callback`** | (result: { topic: string; data: T; }) => void | 62 | 63 | **Returns:** any 64 | 65 | -------------------- 66 | 67 | 68 | ### unsubscribe(...) 69 | 70 | ```typescript 71 | unsubscribe(options: PortalSubscription) => any 72 | ``` 73 | 74 | | Param | Type | 75 | | ------------- | ----------------------------------------------------------------- | 76 | | **`options`** | PortalSubscription | 77 | 78 | **Returns:** any 79 | 80 | -------------------- 81 | 82 | 83 | ### Interfaces 84 | 85 | 86 | #### InitialContext 87 | 88 | | Prop | Type | 89 | | ------------ | --------------------------------------- | 90 | | **`name`** | string | 91 | | **`value`** | T | 92 | | **`assets`** | { [key: string]: string; } | 93 | 94 | 95 | #### PortalMessage 96 | 97 | | Prop | Type | 98 | | ----------- | ------------------- | 99 | | **`topic`** | string | 100 | | **`data`** | TData | 101 | 102 | 103 | #### SubscribeOptions 104 | 105 | | Prop | Type | 106 | | ----------- | ------------------- | 107 | | **`topic`** | string | 108 | 109 | 110 | #### PortalSubscription 111 | 112 | | Prop | Type | 113 | | --------------------- | ------------------- | 114 | | **`subscriptionRef`** | number | 115 | | **`topic`** | string | 116 | 117 | 118 | -------------------------------------------------------------------------------- /plugin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ionic/portals", 3 | "version": "0.12.0", 4 | "description": "SDK Plugin for Ionic Portals", 5 | "homepage": "https://ionic.io/portals", 6 | "main": "dist/index.cjs.js", 7 | "module": "dist/index.js", 8 | "types": "types/index.d.ts", 9 | "unpkg": "dist/plugin.js", 10 | "files": [ 11 | "dist/", 12 | "types" 13 | ], 14 | "author": "Ionic Team (https://ionic.io)", 15 | "license": "Commercial", 16 | "repository": { 17 | "type": "git", 18 | "url": "git+https://github.com/ionic-team/ionic-portals.git" 19 | }, 20 | "bugs": { 21 | "url": "https://github.com/ionic-team/ionic-portals/issues" 22 | }, 23 | "keywords": [ 24 | "capacitor", 25 | "plugin", 26 | "native" 27 | ], 28 | "scripts": { 29 | "lint": "npm run eslint && npm run prettier -- --check", 30 | "fmt": "npm run eslint -- --fix && npm run prettier -- --write", 31 | "eslint": "eslint . --ext ts", 32 | "prettier": "prettier \"**/*.{css,html,ts,js,java}\"", 33 | "docgen": "docgen --api PortalsPlugin --output-readme README.md --output-json dist/docs.json", 34 | "build": "npm run clean && tsc && rollup -c rollup.config.js", 35 | "clean": "rimraf ./dist && rimraf ./build && rimraf ./types", 36 | "watch": "tsc --watch", 37 | "prepublishOnly": "npm run build" 38 | }, 39 | "devDependencies": { 40 | "@capacitor/core": "^7.0.0", 41 | "@capacitor/docgen": "^0.0.10", 42 | "@ionic/eslint-config": "^0.4.0", 43 | "@ionic/prettier-config": "^1.0.1", 44 | "@ionic/swiftlint-config": "^2.0.0", 45 | "@rollup/plugin-node-resolve": "^13.0.4", 46 | "eslint": "^8.57.0", 47 | "prettier": "~2.2.0", 48 | "prettier-plugin-java": "~1.0.0", 49 | "rimraf": "^6.0.1", 50 | "rollup": "^2.32.0", 51 | "swiftlint": "^2.0.0", 52 | "typescript": "~5.0.2" 53 | }, 54 | "peerDependencies": { 55 | "@capacitor/core": ">=7.0.0" 56 | }, 57 | "prettier": "@ionic/prettier-config", 58 | "swiftlint": "@ionic/swiftlint-config", 59 | "eslintConfig": { 60 | "extends": "@ionic/eslint-config/recommended" 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /plugin/rollup.config.js: -------------------------------------------------------------------------------- 1 | import nodeResolve from '@rollup/plugin-node-resolve'; 2 | const banner = 3 | '/*! Ionic Portals: https://ionic.io/portals - Commercial License */'; 4 | export default { 5 | input: 'build/index.js', 6 | output: [ 7 | { 8 | file: 'dist/plugin.js', 9 | format: 'iife', 10 | name: 'ionicPortals', 11 | banner, 12 | globals: { 13 | '@capacitor/core': 'ionicExports', 14 | }, 15 | sourcemap: true, 16 | inlineDynamicImports: true, 17 | }, 18 | { 19 | file: 'dist/index.js', 20 | format: 'esm', 21 | banner, 22 | preferConst: true, 23 | sourcemap: true, 24 | inlineDynamicImports: true, 25 | }, 26 | { 27 | file: 'dist/index.cjs.js', 28 | format: 'cjs', 29 | banner, 30 | sourcemap: true, 31 | inlineDynamicImports: true, 32 | }, 33 | ], 34 | external: ['@capacitor/core'], 35 | plugins: [nodeResolve()], 36 | }; 37 | -------------------------------------------------------------------------------- /plugin/src/index.ts: -------------------------------------------------------------------------------- 1 | import { Capacitor, WebPlugin, registerPlugin } from '@capacitor/core'; 2 | import type { PluginListenerHandle } from '@capacitor/core'; 3 | 4 | /** 5 | * 6 | * A type definining the `InitialContext` from the native application that you can pass into your web application. 7 | */ 8 | export interface InitialContext { 9 | name: string; 10 | value?: T; 11 | assets?: { 12 | [key: string]: string; 13 | }; 14 | } 15 | 16 | /** 17 | * A message that you can publish to a topic using Portals.publish() 18 | */ 19 | export interface PortalMessage { 20 | topic: string; 21 | data?: TData; 22 | } 23 | 24 | interface PortalsPlugin { 25 | publishNative( 26 | message: TMessage, 27 | ): Promise; 28 | addListener( 29 | eventName: string, 30 | listenerFunc: (result: PortalMessage) => void, 31 | ): Promise 32 | } 33 | 34 | class PortalsWeb extends WebPlugin implements PortalsPlugin { 35 | async publishNative(_message: PortalMessage): Promise { 36 | return Promise.resolve(); 37 | } 38 | addListener(_eventName: string, _listenerFunc: (result: PortalMessage) => void): Promise { 39 | return Promise.reject('Method not implemented on web.') 40 | } 41 | } 42 | 43 | const Portals = registerPlugin('Portals', { 44 | web: () => new PortalsWeb(), 45 | }); 46 | 47 | /** 48 | * Provides access to any initial state provided by the native application. 49 | * If the web application is running in a Portal, this will always be defined 50 | * with the name property. 51 | * */ 52 | export function getInitialContext(): 53 | | InitialContext 54 | | undefined { 55 | if (Capacitor.getPlatform() === 'android') { 56 | // eslint-disable-next-line 57 | //@ts-ignore 58 | return JSON.parse(AndroidInitialContext.initialContext()); 59 | } else { 60 | return (window as any).portalInitialContext; 61 | } 62 | } 63 | 64 | export function subscribe( 65 | topic: string, 66 | callback: (result: PortalMessage) => void, 67 | ): Promise { 68 | return Portals.addListener(topic, callback); 69 | } 70 | 71 | export function publish( 72 | message: TMessage, 73 | ): Promise { 74 | return Portals.publishNative(message); 75 | } 76 | -------------------------------------------------------------------------------- /plugin/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowUnreachableCode": false, 4 | "declaration": true, 5 | "esModuleInterop": true, 6 | "lib": ["dom", "es2017"], 7 | "module": "esnext", 8 | "moduleResolution": "node", 9 | "noFallthroughCasesInSwitch": true, 10 | "noUnusedLocals": true, 11 | "noUnusedParameters": true, 12 | "outDir": "build", 13 | "pretty": true, 14 | "sourceMap": true, 15 | "strict": true, 16 | "target": "es2017", 17 | "declarationDir": "types" 18 | }, 19 | "files": ["src/index.ts"] 20 | } 21 | -------------------------------------------------------------------------------- /scripts/.gitignore: -------------------------------------------------------------------------------- 1 | tmp 2 | -------------------------------------------------------------------------------- /scripts/definitions.ts: -------------------------------------------------------------------------------- 1 | export type Release = { 2 | pageUrl: string; 3 | productTitle: string; 4 | mdBody: string; 5 | body: string; 6 | name: string; 7 | raw_published_at: string; 8 | published_at: string; 9 | tag_name: string; 10 | type: string; 11 | version: string; 12 | }; 13 | 14 | export type GithubRelease = { 15 | url: string; 16 | assets_url: string; 17 | upload_url: string; 18 | html_url: string; 19 | id: number; 20 | author: any; 21 | node_id: string; 22 | tag_name: string; 23 | target_commitish: string; 24 | name: string; 25 | draft: boolean; 26 | prerelease: boolean; 27 | created_at: string; 28 | published_at: string; 29 | assets: any[]; 30 | tarball_url: string; 31 | zipball_url: string; 32 | body: string; 33 | mentions_count: number; 34 | }; 35 | -------------------------------------------------------------------------------- /scripts/get-latest-capacitor-version.mjs: -------------------------------------------------------------------------------- 1 | import https from 'https' 2 | import fs from 'fs' 3 | 4 | const options = { 5 | hostname: 'registry.npmjs.org', 6 | path: '/@capacitor/core/', 7 | method: 'GET' 8 | } 9 | 10 | const req = https.request(options, res => { 11 | let json = '' 12 | res.on('data', d => { 13 | json += d 14 | }) 15 | 16 | res.on('close', () => { 17 | const latestVersion = JSON.parse(json)['dist-tags'].latest; 18 | const lernaConfig = JSON.parse(fs.readFileSync('./lerna.json', 'utf-8')) 19 | lernaConfig.capacitorVersion = latestVersion 20 | fs.writeFileSync('./lerna.json', JSON.stringify(lernaConfig, null, 2) + '\n') 21 | }) 22 | }) 23 | 24 | req.on('error', error => { 25 | console.error(error) 26 | }) 27 | 28 | req.end() 29 | -------------------------------------------------------------------------------- /scripts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "scripts", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "changelog": "node ./changelog-release.mjs", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@slack/web-api": "^6.9.0", 14 | "fetch": "^1.1.0", 15 | "node-fetch": "^3.3.2", 16 | "remark-html": "^16.0.1", 17 | "remark-parse": "^11.0.0", 18 | "semver": "^7.5.4", 19 | "unified": "^11.0.3" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /scripts/send-to-slack.mjs: -------------------------------------------------------------------------------- 1 | /** 2 | * @typedef {import("./definitions").Release} Release 3 | * @typedef {import("@slack/web-api").ChatPostMessageArguments} ChatPostMessageArguments 4 | */ 5 | import { WebClient } from "@slack/web-api"; 6 | 7 | // This is a specific channel to share updates to. 8 | const SLACK_CONVERSATION_ID = "C061QQ33UEB"; 9 | 10 | /** 11 | * 12 | * @param {Release[]} slackUpdateList 13 | * @returns {Promise} 14 | */ 15 | export async function postUpdatesToSlack(slackUpdateList) { 16 | const token = process.env.SLACK_TOKEN; 17 | const web = new WebClient(token); 18 | const testResult = await web.auth.test({ token }); 19 | 20 | if (testResult.ok) { 21 | console.log("sending to slack"); 22 | } else { 23 | console.error(JSON.stringify(testResult, null, 2)); 24 | } 25 | 26 | const slackBlocks = slackUpdateList 27 | .sort((a, b) => { 28 | return new Date(a.raw_published_at) - new Date(b.raw_published_at); 29 | }) 30 | .map(({ version, published_at, type, productTitle, mdBody, pageUrl }) => ({ 31 | type: "section", 32 | text: { 33 | type: "mrkdwn", 34 | text: 35 | `-------------------------------------\n` + 36 | `*${productTitle} Release ${version}* (${type}) ${published_at}\n` + 37 | `\n${markdownToSlackMarkdown(mdBody)}` + 38 | `<${pageUrl}#release-${version}|shareable link>`, 39 | }, 40 | })); 41 | 42 | await web.chat.postMessage({ 43 | text: "This is text", 44 | blocks: slackBlocks, 45 | channel: SLACK_CONVERSATION_ID, 46 | }); 47 | } 48 | 49 | function markdownToSlackMarkdown(markdown) { 50 | return markdown 51 | .replace(/\*\*(\S+)\*\*/g, (all, one) => `*${one}*`) 52 | .replace(/\[(\S+)\]\((\S+?)\)/g, (all, one, two) => `<${two}|${one}>`) 53 | .replace(/\n\* /g, "\n- ") 54 | .replace(/#+ (.*)\n/, (all, word) => `*${word}*\n`); 55 | } 56 | -------------------------------------------------------------------------------- /website/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /node_modules 3 | 4 | # Production 5 | /build 6 | 7 | # Generated files 8 | .docusaurus 9 | .cache-loader 10 | 11 | # Misc 12 | .DS_Store 13 | .env.local 14 | .env.development.local 15 | .env.test.local 16 | .env.production.local 17 | 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* 21 | .vercel 22 | -------------------------------------------------------------------------------- /website/README.md: -------------------------------------------------------------------------------- 1 | # Website 2 | 3 | This website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator. 4 | 5 | ### Installation 6 | 7 | ``` 8 | $ yarn 9 | ``` 10 | 11 | ### Local Development 12 | 13 | ``` 14 | $ yarn start 15 | ``` 16 | 17 | This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server. 18 | 19 | ### Build 20 | 21 | ``` 22 | $ yarn build 23 | ``` 24 | 25 | This command generates static content into the `build` directory and can be served using any static contents hosting service. 26 | 27 | ### Deployment 28 | 29 | ``` 30 | $ GIT_USER= USE_SSH=true yarn deploy 31 | ``` 32 | 33 | If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch. 34 | -------------------------------------------------------------------------------- /website/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')], 3 | }; 4 | -------------------------------------------------------------------------------- /website/docs/choosing-a-communication.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Communication Mechanisms 3 | sidebar_label: Communication Mechanisms 4 | --- 5 | 6 | Communicating through a Portal from web to native, or vice versa, creates immersive experiences that blurs the boundary between where native ends and web starts. Portals provides two different ways to communicate: 7 | 8 | 1. The Portals Plugin 9 | 2. Capacitor Plugins 10 | 11 | ## The Portals Plugin 12 | 13 | The Portals Plugin provides a helpful, lightweight way to publish and subscribe to messages through a Portal without needing to use create a Capacitor plugin in your native application. 14 | 15 | We recommend this approach when performing small UI tasks like dismissing a native modal from inside the web application, or passing short messages. This mechanism may also be more appealing to small teams or solo developers who want to communicate through a Portal without the desire to build a custom Capacitor plugin. 16 | 17 | ### Initial Context 18 | 19 | The Ionic Portals library also provides a way to set initial context data for the web application within a Portal. This is helpful when you need to pass some data in so that it is available before the web application renders. Examples where this is useful include: 20 | 21 | - Passing session information or data to the application so that the page is pre-filled as it loads. This avoids any delay between the page loading and data being populated after by other means. 22 | 23 | - You may wish to use a single-page web application in your project and navigate to different sections depending on which Portal is displayed in the native application. Navigating after the Portal is loaded reveals the page reload event to the user, whereas using the initial context mechanism to navigate before the page is loaded in the Portal provides a more immersive experience. 24 | 25 | For more information about using the initial context mechanism, see one of the platform guides: 26 | 27 | - [iOS to Web communication through a Portal.](./for-ios/how-to/using-the-portals-plugin.md) 28 | - [Android to Web communication through a Portal.](./for-android/how-to/using-the-portals-plugin.md) 29 | - [React Native to Web communication through a Portal.](./for-react-native/how-to/using-the-portals-plugin.md) 30 | 31 | ## Capacitor Plugins 32 | 33 | Developing a custom Capacitor plugin is a great way to have more structured communication through a Portal. The Capacitor bridge is used under the hood in Portals and this allows any Capacitor plugin to be used, even the Core Plugins. 34 | 35 | For custom communication between your web and native application, you can write a Capacitor plugin inside your native code and provide the web code with a Typescript API that will use the plugin. 36 | 37 | We recommend this approach for larger teams or developers who prefer to separate logic in their applications, share functionality between multiple portals, or even develop their custom plugin separately outside the native application code. 38 | 39 | See one of our guides on how to define a portal API: 40 | 41 | - [Creating a Plugin in iOS.](./for-ios/how-to/define-api-in-typescript.md) 42 | - [Creating a Plugin in Android.](./for-android/how-to/define-api-in-typescript.md) 43 | 44 | ### Capacitor Core Plugins 45 | 46 | The library of [Core Capacitor Plugins](https://capacitorjs.com/docs/apis) is available out of the box and ready to use with Portals. We have made all core plugins available as native dependencies through Maven Central and CocoaPods so that when added to a native project, they will allow web applications in Portals to use them with no custom code required. By using the Capacitor Core Plugins you can save time by not having to write your own native code to take a photo or store files, for example. 47 | 48 | See one of our guides on How To Use a Capacitor Plugin: 49 | 50 | - [Using a Core plugin in iOS](/for-ios/how-to/using-a-capacitor-plugin.md). 51 | - [Using a Core plugin in Android](/for-android/how-to/using-a-capacitor-plugin.md). 52 | - [Using a Core plugin in React Native](/for-react-native/how-to/using-a-capacitor-plugin.md). 53 | -------------------------------------------------------------------------------- /website/docs/cli/commands/poc.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: CLI Command - poc 3 | sidebar_label: poc 4 | --- 5 | 6 | One common scenario during the Portals development lifecycle is that a Portal has been defined in the Native 7 | application but there is not yet a web application available to test against it. This command 8 | is meant resolve this scenario and allow native developers to have confidence in how the Portal 9 | was configured. 10 | 11 | The `poc` command will download a prebuilt example application to a predefined directory. The portal 12 | can then be configured to point to this application for testing purposes. The web application reflects 13 | the `initialContext` and plugins that have been exposed. You can also even use the web application to 14 | test some pub/sub interactions. 15 | 16 | ### Usage: 17 | 18 | ```bash 19 | portals poc [flags] 20 | ``` 21 | 22 | ### Aliases: 23 | 24 | ```bash 25 | poc, sample-app 26 | ``` 27 | 28 | ### Examples: 29 | 30 | ```bash 31 | portals poc [--destination path/to/copy/web/apps] 32 | ``` 33 | 34 | ### Flags: 35 | 36 | - `--destination` **(string)** The location to download the web application to. 37 | - `-h, --help` help for sync 38 | 39 | ### Global Flags: 40 | 41 | - `--config` **(string)** config file (default is $PWD/.portals.yaml) 42 | -------------------------------------------------------------------------------- /website/docs/cli/commands/serve-ios.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: CLI Command - serve ios 3 | sidebar_label: serve ios 4 | --- 5 | 6 | The `portals serve ios` command is a tool for web developers to debug and test their 7 | web code in different Portal configurations on a simulator or a device without having 8 | to go through the build process of a native application or even have access to its 9 | source code. The command allows web developers to run their web code from their local 10 | dev server and see the changes on the fly. 11 | 12 | :::note 13 | 14 | The serve command requires IonicPortals 0.9.0 or higher and requires that the portal 15 | has [`devModeEnabled`](https://ionic-portals-ios.vercel.app/documentation/ionicportals/portal/devmodeenabled) set to `true`. 16 | 17 | ::: 18 | 19 | ### Usage 20 | ```bash 21 | portals serve ios [simulator | device] \ 22 | --application /path/to/your/ios.app \ 23 | --dev-server http://localhost:8100 24 | ``` 25 | This command will present an interactive list of eligible destinations since no specific device details were provided. When selected, the command will launch the iOS app to the selected device and will override all portals with the content served from the development server URL by default. If a capacitor.config.{json, ts, js} file is located in the current working directory, it will use that, otherwise it will fallback to any configuration potentially shipped in the application. 26 | 27 | ### Examples 28 | 29 | #### Specify capacitor configuration file 30 | 31 | ```bash 32 | portals serve ios [simulator | device] \ 33 | --application /path/to/your/ios.app \ 34 | --dev-server http://localhost:8100 \ 35 | --capacitor-config /path/to/your/capacitor.config.ts 36 | ``` 37 | 38 | #### Specify device details 39 | 40 | If you know the specific device ID of the device you want to target, you can use the `--device-id` flag: 41 | 42 | ```bash 43 | portals serve ios [simulator | device] \ 44 | --application /path/to/your/ios.app \ 45 | --dev-server http://localhost:8100 \ 46 | --device-id "deadbeef-dead-beef-dead-beefdeadbeef" 47 | ``` 48 | 49 | Alternatively, you can use a combination of device name and OS version to target an eligible simulator: 50 | 51 | ```bash 52 | portals serve ios simulator \ 53 | --application /path/to/your/ios.app \ 54 | --dev-server http://localhost:8100 \ 55 | --device-name "iPhone 12" \ 56 | --device-os-version "14.0" 57 | ``` 58 | 59 | Or you can specify the device name to target an eligible physical device: 60 | 61 | ```bash 62 | portals serve ios device \ 63 | --application /path/to/your/ios.app \ 64 | --dev-server http://localhost:8100 \ 65 | --device-name "Carl's iPhone 15" 66 | ``` 67 | 68 | #### Specify portal name 69 | 70 | If you want to override only portals with a specific name, use the `--portal-name` flag: 71 | 72 | ```bash 73 | portals serve ios [simulator | device] \ 74 | --application /path/to/your/ios.app \ 75 | --dev-server http://localhost:8100 \ 76 | --portal-name "profile" 77 | ``` 78 | ### Flags: 79 | - `--device-id` **(string)** The ID of the target device. 80 | - `--device-name` **(string)** The name of the device. ('iPhone 13 Pro Max') 81 | - `-h, --help` help for ios 82 | 83 | ### Global Flags: 84 | - `--application` **(string)** Path to the native application. (required) 85 | - `--dev-server` **(string)** URL of the development server. (required) 86 | - `--capacitor-config` **(string)** Path to the capacitor configuration file. 87 | - `--portal-name` **(string)** The name of the target Portal. (default "PORTAL") 88 | - `--config` **(string)** config file (default $PWD/.portals.yaml) 89 | 90 | -------------------------------------------------------------------------------- /website/docs/cli/commands/sync.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: CLI Command - sync 3 | sidebar_label: sync 4 | --- 5 | 6 | During the build process of Native Applications it is good practice to pull the latest Live Updates available 7 | from each of the web applications that are used in the Portals. This ensures that the native release is shipping 8 | with the latest web applications instead of relying on a live update to immediately update. 9 | 10 | The `sync` CLI command will query the Live Update service, download, and extract the latest builds for a set of configured Appflow Applications. 11 | 12 | When running inside of an Xcode Run Script build step the destination will default to 13 | the root of the built target: `$BUILT_PRODUCTS_DIR/$TARGET_NAME.app` 14 | 15 | When running under any other context the destination will default to the current working 16 | directory. 17 | 18 | ### Usage: 19 | 20 | ```bash 21 | portals sync [flags] 22 | ``` 23 | 24 | ### Examples: 25 | 26 | ```bash 27 | portals sync [--destination path/to/copy/web/apps] 28 | ``` 29 | 30 | ### Flags: 31 | 32 | - `--destination` **(string)** The location to download the web applications to. 33 | - `-h, --help` help for sync 34 | 35 | ### Global Flags: 36 | 37 | - `--config` **(string)** config file (default is $PWD/.portals.yaml) 38 | -------------------------------------------------------------------------------- /website/docs/cli/overview.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: CLI Overview 3 | sidebar_label: Overview 4 | --- 5 | 6 | # Portals CLI 7 | 8 | The Portals CLI is a command-line tool designed to simplify and streamline the management and deployment of your Portals projects. It empowers developers to interact with Portals programmatically, automating common tasks and enhancing productivity. The CLI provides functionality for both Native Developers and Web Developers. 9 | 10 | The CLI is a companion tool but is not required to use Portals. 11 | 12 | ## Installation 13 | 14 | Installing the Ionic Portals CLI is a straightforward process, with options available for macOS, Linux, Windows, and various distribution methods to suit your preferences. Here's how to get started on your platform of choice: 15 | 16 | ### Homebrew 17 | 18 | If you're using macOS or Linux, you can streamline the installation process with Homebrew. First, tap into the Portals releases repository and then install Portals with these commands: 19 | 20 | ```bash 21 | # tap the Portals releases repository 22 | brew tap ionic-team/portals https://github.com/ionic-team/portals-cli-releases.git 23 | 24 | # install 25 | brew install portals 26 | ``` 27 | 28 | ### Shell script 29 | 30 | Another option for macOS and Linux users is the shell script-based installation. Execute this single command to install Portals: 31 | 32 | ```bash 33 | curl -sL https://raw.githubusercontent.com/ionic-team/portals-cli-releases/main/install.sh | bash 34 | ``` 35 | 36 | ### Windows Binaries 37 | 38 | For Windows users, manual installation is currently required. You can download the appropriate zip file for your architecture (arm64, i386, or x86_64) from the list provided and extract it to a directory on your system's `PATH` for convenient access. 39 | 40 | - [arm64](https://github.com/ionic-team/portals-cli-releases/releases/latest/download/portals_Windows_arm64.zip) 41 | - [i386](https://github.com/ionic-team/portals-cli-releases/releases/latest/download/portals_Windows_i386.zip) 42 | - [x86_64](https://github.com/ionic-team/portals-cli-releases/releases/latest/download/portals_Windows_x86_64.zip) 43 | 44 | ### Linux Binaries 45 | 46 | Debs and RPMs are made available for each [release](https://github.com/ionic-team/portals-cli-releases/releases). 47 | Download the appropriate package for your system and install with your package manager. 48 | 49 | ## Command List​ 50 | 51 | View all available CLI commands and options. 52 | 53 | - [poc](./commands/poc.md) 54 | - [sync](./commands/sync.md) 55 | -------------------------------------------------------------------------------- /website/docs/for-android/changelog.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Changelog 3 | hide_table_of_contents: true 4 | --- 5 | 6 | import ReleaseNotes from '@site/src/components/page/changelog'; 7 | import releases from "./changelog.json"; 8 | 9 | 10 | -------------------------------------------------------------------------------- /website/docs/for-android/examples/ecommerce-app-live-updates.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: E-commerce App with Live Updates 3 | sidebar_label: Live Updates E-commerce App 4 | --- 5 | 6 | ## Overview 7 | 8 | The Ionic Team has enhanced the E-commerce demo application to demonstrate how to use Live Updates in an app using Portals. The Android application uses Fragments while the iOS application uses Storyboard/ViewController based views. For more information about the demo app, see our [Portals E-commerce demo page](./ecommerce-app.md). 9 | 10 | Below is a list of which portions of the app are native and which portions of the app are portals. 11 | 12 | Native Screens 13 | 14 | - List of Products Page 15 | - Individual Product Page 16 | - Cart Page 17 | - Settings Page 18 | 19 | Web Screens 20 | 21 | - Checkout Page 22 | - Help Page 23 | - User Details Page 24 | 25 | The source is [available on GitHub](https://github.com/ionic-team/live-updates-ecommerce-demo) and includes the iOS, Android, and Web projects. 26 | 27 | ## Highlights 28 | 29 | ### Portals Apps 30 | 31 | The demo app for Live Updates uses two separate web apps to provide content for the three Portals: 32 | 33 | - Help Page 34 | - User Details and Checkout Page 35 | 36 | ### Settings Page 37 | 38 | A settings page was added to demonstrate features of the Live Updates SDK. This page allows you to view the status of any occurring updates, delete the content of any previous updates, and trigger a sync manually. The settings page also allows you to change the current channel used by Live Updates for both web apps. The app must be fully closed for the channel change to take effect for the next sync. 39 | 40 | ### Update Strategy 41 | 42 | In both Android and iOS, the Portals are configured on initial app load time and a Live Updates sync occurs immediately. Subsequent checks are made when the native app is resumed from the background and a sync will occur if more than six hours have passed since the last sync. 43 | -------------------------------------------------------------------------------- /website/docs/for-android/guide.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Getting Started Guide 3 | sidebar_label: Getting Started Guide 4 | --- 5 | 6 | import Tabs from '@theme/Tabs'; 7 | import TabItem from '@theme/TabItem'; 8 | import CodeBlock from '@theme/CodeBlock'; 9 | import { getCapacitorVersion, getPortalsVersion, getPortalsVersionIos, getPortalsVersionAndroid, getPortalsVersionRN, getiOSMinVersion, getAndroidMinSdk, getRnMinVersion } from '@site/src/util'; 10 | 11 | ## Signup 12 | 13 | Ionic Portals requires a product key to use. Getting a key is easy. 14 | Just head to the [Ionic Dashboard](https://ionic.io/register-portals) and click "Get Access". 15 | 16 | This will present you with a form asking for some additional information. 17 | After submitting the page will refresh and you will immediately see the key that can be used to unlock the use of Portals in your app. 18 | 19 | :::info 20 | You can always use this shareable link to signup for a Product Key: [ionic.io/register-portals](https://ionic.io/register-portals) 21 | ::: 22 | 23 | ## Install 24 | 25 | Ionic Portals is publicly available via Maven Central, Cocoapods, SPM, and NPM. 26 | 27 | To add Portals to your Android project, add the dependency to your `build.gradle` files 28 | 29 | 30 | { 31 | ` 32 | // ---------------------------------------------- 33 | // Module-level build.gradle 34 | // ---------------------------------------------- 35 | dependencies { 36 | implementation 'io.ionic:portals:${getPortalsVersionAndroid()}' 37 | }`.trim() 38 | } 39 | 40 | 41 | And in the top level `build.gradle` file, be sure that you include `jcenter` and `maven` in your repositories section 42 | 43 | ```groovy title=build.gradle 44 | // ---------------------------------------------- 45 | // Top-level build.gradle 46 | // ---------------------------------------------- 47 | allprojects { 48 | repositories { 49 | google() 50 | 51 | // Make sure JCenter and Maven Central are 52 | // in your project repositories 53 | jcenter() 54 | mavenCentral() 55 | } 56 | } 57 | ``` 58 | 59 | To add Portals to your web project, install it via NPM: 60 | 61 | 62 | {`npm install @ionic/portals@${getPortalsVersion()}`} 63 | 64 | 65 | If you need help configuring specific versions of Portals with Capacitor or Capacitor Plugins, check out our [SDK Version Compatibility](./version-matrix.md) page. 66 | 67 | ## Configure 68 | 69 | After installing the dependency you need to register your copy of Ionic Portals at runtime. This works both offline and in production. You'll need to call [PortalManager.register(myApiKey)](https://ionic.io/docs/portals-android-api-ref/-ionic-portals/io.ionic.portals/-portal-manager/index.html#-1847662668%2FFunctions%2F-149544105) before creating any Portals in your app. To get an API Key, refer to the [Sign Up](#signup) section. 70 | 71 | Below is a simple example of how to bootstrap Ionic Portals before loading any Portal instances in your app. We recommend placing this register call inside the `onCreate()` function of a custom `Application` class so that it is handled immediately when your app is launched, but you can place it anywhere as long as it is called before your app tries to load any Portals. 72 | 73 | ```kotlin title=MyApplication.kt 74 | import android.app.Application 75 | import io.ionic.portals.PortalManager 76 | 77 | class MyApplication : Application() { 78 | override fun onCreate(): Unit { 79 | super.onCreate() 80 | PortalManager.register("YOUR_PORTALS_KEY") 81 | // setup portals... 82 | }} 83 | } 84 | ``` 85 | 86 | :::warning 87 | Avoid committing your Portals key to source code repositories where it may be publicly visible! 88 | On Android, you can use the [Secrets Gradle Plugin](https://github.com/google/secrets-gradle-plugin) to keep it out of a public repository. 89 | ::: 90 | -------------------------------------------------------------------------------- /website/docs/for-android/how-to/advanced-configuration.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Advanced Configuration 3 | sidebar_label: Advanced Configuration 4 | --- 5 | 6 | import Tabs from '@theme/Tabs'; 7 | import TabItem from '@theme/TabItem'; 8 | import CodeBlock from '@theme/CodeBlock'; 9 | import { getCapacitorVersion, getPortalsVersion, getPortalsVersionIos, getPortalsVersionAndroid } from '@site/src/util'; 10 | 11 | Portals instances can be further configured by providing a Capacitor Configuration to the Portal. 12 | 13 | ## Capacitor Config File 14 | 15 | Providing a [Capacitor Configuration](https://capacitorjs.com/docs/config) json file with your web assets will configure Capacitor with the provided settings. 16 | 17 | Place your `capacitor.config.json` file in your Portal web assets directory within the `assets` directory of your Android project. For example, if your Portal assets are in `assets/myportal` then place the config file in the root of that directory. 18 | 19 | ### Live Updates 20 | 21 | Config files can be updated by placing them in your Live Update build assets. Any `capacitor.config.json` file found in the root of a Live Update payload will be used as a priority over one provided in the bundled `assets` of the app. 22 | 23 | ## Programmatic Configuration 24 | 25 | Portals for Android allows you to provide a programmatically defined Capacitor Configuration to customize the behavior of Capacitor in each instance of a Portal. 26 | 27 | :::note 28 | A config defined in code and applied to a Portal will be used as a priority over any config file provided in the `assets` directory bundled with the app AND any provided in a Live Update. 29 | ::: 30 | 31 | ### Android 32 | 33 | 39 | 40 | 41 | ```kotlin 42 | val fragment = PortalFragment(portal) 43 | 44 | /* 45 | * Create your configuration and apply it to 46 | * the Portal fragment before using it 47 | */ 48 | val myConfig = CapConfig.Builder(this) 49 | .setInitialFocus(true) 50 | .setLoggingEnabled(false) 51 | .create() 52 | 53 | fragment.setConfig(myConfig) 54 | 55 | supportFragmentManager 56 | .beginTransaction() 57 | .replace(R.id.container, fragment) 58 | .commit() 59 | ``` 60 | 61 | 62 | 63 | 64 | 65 | ```java 66 | PortalFragment fragment = new PortalFragment(myPortal); 67 | 68 | /* 69 | * Create your configuration and apply it to 70 | * the Portal fragment before using it 71 | */ 72 | CapConfig myConfig = new CapConfig.Builder(getContext()) 73 | .setInitialFocus(true) 74 | .setLoggingEnabled(false) 75 | .create(); 76 | 77 | fragment.setConfig(myConfig); 78 | fragmentManager.beginTransaction() 79 | .replace(R.id.portal_space, fragment) 80 | .commit(); 81 | ``` 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /website/docs/for-android/how-to/multiple-portals-multiple-web-apps.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Multiple Portals and Multiple Web Applications 3 | sidebar_label: Multi Portal & Multiple Web Apps 4 | --- 5 | 6 | import Tabs from '@theme/Tabs'; 7 | import TabItem from '@theme/TabItem'; 8 | 9 | In some cases, it makes sense to have different teams handle different portals in bigger applications. For example, one team may maintain a _"Maps & Geolocation"_ Portal while another might handle a _"Shopping & Checkout"_ Portal. 10 | 11 | ## Declaring Multiple Portals 12 | 13 | Setting up multiple Portals is as easy as declaring another Portal in the PortalManager. No further code is neccessary and each Portal will function independently. 14 | 15 | 21 | 22 | 23 | ```kotlin 24 | PortalManager.newPortal("maps").create() 25 | PortalManager.newPortal("shopping").create() 26 | ``` 27 | 28 | 29 | 30 | 31 | 32 | ```java 33 | PortalManager.newPortal("maps").create(); 34 | PortalManager.newPortal("shopping").create(); 35 | ``` 36 | 37 | 38 | 39 | 40 | 41 | Now, the _"Maps & Geolocation"_ Portal will read from the `maps` directory in your assets folder and the _"Shopping & Checkout"_ Portal will read from the `shopping` directory in your assets folder. 42 | 43 | ## Project Structure 44 | 45 | In your project, you'll need to setup multiple folders in your Assets directory if your web applications are discrete apps. For more information on how to setup web bundles in your native project, see [our how-to guide](./pull-in-web-bundle.md). 46 | -------------------------------------------------------------------------------- /website/docs/for-android/how-to/multiple-portals-single-web-app.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Multiple Portals and Single Page Applications 3 | sidebar_label: Multi Portal & Single Page Apps 4 | --- 5 | 6 | import Tabs from '@theme/Tabs'; 7 | import TabItem from '@theme/TabItem'; 8 | 9 | In some cases, it break down a large Single Page Application (SPA) into multiple Portals to create a better User Experience. The below example is similar to the [Multi Portals with Multiple Web Applications](./multiple-portals-multiple-web-apps.md) example, but it is a SPA rather than multiple applications. 10 | 11 | ## Declaring Multiple Portals 12 | 13 | Setting up multiple Portals is as easy as declaring another Portal in the PortalManager. Each Portal will function independently of one another and will be a separate instance of the SPA. 14 | 15 | 21 | 22 | 23 | ```kotlin 24 | PortalManager.newPortal("maps") 25 | .setStartDir("web") 26 | .setInitialContext(mapOf("route" to "/maps")) 27 | .create() 28 | 29 | PortalManager.newPortal("shopping") 30 | .setStartDir("web") 31 | .setInitialContext(mapOf("route" to "/shopping")) 32 | .create() 33 | ``` 34 | 35 | 36 | 37 | 38 | 39 | ```java 40 | PortalManager.newPortal("maps") 41 | .setStartDir("web") 42 | .setInitialContext(Map.of("route", "/maps")) 43 | .create(); 44 | 45 | PortalManager.newPortal("shopping") 46 | .setStartDir("web") 47 | .setInitialContext(Map.of("route", "/shopping")) 48 | .create(); 49 | ``` 50 | 51 | 52 | 53 | 54 | 55 | The _"Maps & Geolocation"_ Portal will be an instance of the SPA in the `web` folder in the Assets directory with an _"initialContext"_ of the following. 56 | 57 | ```json 58 | { 59 | "route": "/map" 60 | } 61 | ``` 62 | 63 | Similarly, the "Shopping & Checkout" Portal will be a separate instance of the SPA in the `web` folder in the Assets directory with an _"initialContext"_ of the following. 64 | 65 | ```json 66 | { 67 | "route": "/shopping" 68 | } 69 | ``` 70 | 71 | ## Injecting the Initial Context Into the Web Application 72 | 73 | With this _initialContext_ value set, you can use this to properly navigate to the correct route within your Portal. The [Portals E-commerce example](../examples/ecommerce-app.md) uses this method. You can see how we [inject the context here on Github](https://github.com/ionic-team/portals-ecommerce-demo/blob/main/web/src/index.tsx) using the [Portal.getInitialContext() function](../../for-web/portals-plugin.md#getinitialcontext). 74 | 75 | ## Project Structure 76 | 77 | In your project, you'll need to a single folder in your Assets directory that contains the built output for your SPA. For more information on how to setup web bundles in your native project, see [our how-to guide](./pull-in-web-bundle.md). 78 | -------------------------------------------------------------------------------- /website/docs/for-android/how-to/pull-in-web-bundle.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: How to Use a Web App in a Portal 3 | sidebar_label: Use a Web App in a Portal 4 | --- 5 | 6 | import Tabs from '@theme/Tabs'; 7 | import TabItem from '@theme/TabItem'; 8 | 9 | In order to use web applications in your native applications, you'll need to properly setup your project to be able to include a web bundle. 10 | 11 | ## Setup the Web Asset Directory 12 | 13 | In Android, your web application needs to be in the assets folder; which by default is `src/main/assests`. For example, if your web application is a help page, you can put your web application in the `src/main/assets/help` folder. From there, you can either set the `portalId` for the Portal to `help` or you can manually specify `help` as the directory using the [.setStartDir()](https://ionic.io/docs/portals-android-api-ref/-ionic-portals/io.ionic.portals/-portal-builder/index.html#-1989968072%2FFunctions%2F-149544105) function. 14 | 15 | 21 | 22 | 23 | ```kotlin 24 | PortalManager.newPortal("help").create() 25 | 26 | // or... 27 | 28 | PortalManager.newPortal("MY_PORTAL_ID") 29 | .setStartDir("help") 30 | .create() 31 | ``` 32 | 33 | 34 | 35 | 36 | ```java 37 | PortalManager.newPortal("help").create(); 38 | 39 | // or... 40 | 41 | PortalManager.newPortal("MY_PORTAL_ID") 42 | .setStartDir("help") 43 | .create(); 44 | ``` 45 | 46 | 47 | 48 | 49 | ## Automating the Process 50 | 51 | Once you have your web code and native code linked up, you will need a process to continually copy in new versions of the web application into your mobile projects. 52 | 53 | We recommend having some type of automation set up so the mobile developer doesn't have to manually copy over the web code every time there is a new change. We have a few guides for ideas to do so in a [monorepo](../tutorials/monorepo-example.md) or [git submodules](https://git-scm.com/book/en/v2/Git-Tools-Submodules). 54 | -------------------------------------------------------------------------------- /website/docs/for-android/how-to/sharing-assets.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Sharing Assets Between Portals 3 | sidebar_label: Sharing Assets 4 | --- 5 | 6 | import Tabs from '@theme/Tabs'; 7 | import TabItem from '@theme/TabItem'; 8 | 9 | If you are devloping an application that contains multiple Portals, the Portals library supports the ability to share asset files between them. This is helpful to reduce the size of your overall app if those Portals use assets that are the same such as large media files or font files. 10 | 11 | ## Register Shared Assets 12 | 13 | Create a directory in the `src/main/assets` directory to hold shared assets for your Portals. In this example we will name this directory "shared". Therefore, the full path of the shared asset directory where we will place the shared resources is `src/main/assets/shared`. 14 | 15 | Next, when creating the Portal register an `AssetMap` for the shared asset directory. The first property is a name for your shared assets registration, followed by a virtual path the web content will use. The last property is the name of the directory we created for the shared assets, "shared". 16 | 17 | 23 | 24 | 25 | ```kotlin 26 | PortalManager.newPortal("myportal") 27 | .addAssetMap(AssetMap("myshared","/shared/assets","shared")) 28 | .create() 29 | ``` 30 | 31 | 32 | 33 | 34 | ```java 35 | PortalManager.newPortal("myportal") 36 | .addAssetMap(new AssetMap("myshared","/shared/assets","shared")) 37 | .create(); 38 | ``` 39 | 40 | 41 | 42 | 43 | When this Portal loads, the shared asset information will be passed to the web content as part of the Portal's [InitialContext](../../for-web/portals-plugin.md#initialcontext) object. 44 | 45 | ## Using Registered Assets 46 | 47 | Refer to the page in the [Web docs](../../for-web/sharing-assets.md) to learn how to use your registered shared assets in your web content. -------------------------------------------------------------------------------- /website/docs/for-android/live-updates.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Getting Started with Live Updates 3 | sidebar_label: Live Updates 4 | --- 5 | 6 | import Tabs from '@theme/Tabs'; 7 | import TabItem from '@theme/TabItem'; 8 | import CodeBlock from '@theme/CodeBlock'; 9 | import { getCapacitorVersion, getPortalsVersion, getPortalsVersionIos, getPortalsVersionAndroid, getPortalsVersionRN, getLiveUpdatesAndroidVersion } from '@site/src/util'; 10 | 11 | Getting started with Live Updates in your Portals app. 12 | 13 | :::info 14 | To use the Live Updates SDK with Ionic Portals, check out the [Getting Started Guide](./guide.md) for Ionic Portals first. 15 | ::: 16 | 17 | ## Appflow 18 | 19 | Create an app for your Portal in the [Ionic Dashboard](https://dashboard.ionicframework.com). For more information, see our documentation on [using Appflow](https://ionic.io/docs/appflow/quickstart/connect). 20 | 21 | :::info 22 | Take note of the **appId** of your app in Appflow, this will be used by the Live Updates sdk. 23 | ::: 24 | 25 | To test Live Updates, create a new build of your app in Appflow and create a deployment to Live Updates from that build. Take note of the **channel** name as this is also used in the Live Updates sdk. 26 | 27 | ![Deployments](https://i.imgur.com/73detdm.png) 28 | 29 | Deployments in Appflow will be downloaded as new Live Updates. 30 | 31 | ## Install 32 | 33 | The Live Updates SDK is publicly available via Maven Central, Cocoapods, and SPM. 34 | 35 | To add Live Updates to your Portals Android project, add the dependency to your `build.gradle` file 36 | 37 | 38 | { 39 | ` 40 | // ---------------------------------------------- 41 | // Module-level build.gradle 42 | // ---------------------------------------------- 43 | dependencies { 44 | implementation 'io.ionic:portals:${getPortalsVersionAndroid()}' 45 | implementation 'io.ionic:liveupdates:${getLiveUpdatesAndroidVersion()}' 46 | }`.trim() 47 | } 48 | 49 | 50 | And in the top level `build.gradle` file, be sure that you include `jcenter` and `maven` in your repositories section 51 | 52 | ```groovy title=build.gradle 53 | // ---------------------------------------------- 54 | // Top-level build.gradle 55 | // ---------------------------------------------- 56 | allprojects { 57 | repositories { 58 | google() 59 | 60 | // Make sure JCenter and Maven Central are 61 | // in your project repositories 62 | jcenter() 63 | mavenCentral() 64 | } 65 | } 66 | ``` 67 | 68 | ## Configure 69 | 70 | After installing the dependency you need to configure Live Updates as part of the Portal creation process. Add a LiveUpdate config where your Portal is created. Provide the **appId** that corresponds with the app in Appflow, and the **channel** name to subscribe to for updates. 71 | 72 | ```kotlin title=MyApplication.kt 73 | import android.app.Application 74 | import io.ionic.portals.PortalManager 75 | 76 | class MyApplication : Application() { 77 | override fun onCreate(): Unit { 78 | super.onCreate() 79 | PortalManager.register("YOUR_PORTALS_KEY") 80 | 81 | // setup portals (example) 82 | PortalManager.newPortal("portal1") 83 | .setLiveUpdateConfig(applicationContext, LiveUpdate("ebd6138b", "production")) 84 | .create() 85 | }} 86 | } 87 | ``` 88 | 89 | By default, when the app loads for the first time and the portal is created, a sync will occur. A sync operation checks the Appflow servers for a new version of the app. If a new version is available, the app files are downloaded to the device and setup with the Portal. The next time the Portal is loaded, the new version will load automatically. 90 | -------------------------------------------------------------------------------- /website/docs/for-android/tutorials/monorepo-example.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Copying Web Assets to Native Projects in a Monorepo 3 | sidebar_label: Copying Web Assets to Native Projects in a Monorepo 4 | --- 5 | 6 | With Ionic Portals, you need to get the built web assets for each of the portals into your native applications. There could be many ways to go about doing so, which will highly depend on your project. This quick tutorial will show how you can accomplish this when working on a monorepo project in which your web apps are in the same repository as your native apps. 7 | 8 | ## Project Structure 9 | 10 | We will set up some NPM scripts in a moment to do the copy tasks. First, though, you need to know where to copy the assets to each native project. For Android, the web assets will go into a folder under `app/src/main/assets`, and for iOS, the folder will go directly into the folder named after your app. Below is a sample monorepo setup with three projects in the root directory (android, ios, and web), and in each native project, we will copy the web assets into a folder named `web app`. The scripts below match this structure. You will need to modify the scripts accordingly based on your project. 11 | 12 | 13 | ``` 14 | project root/ 15 | ├─ android/ 16 | ├─ .gradle 17 | ├─ app/ 18 | ├─ src/ 19 | ├─ main/ 20 | ├─ assets/ 21 | ├─ webapp/ 22 | ├─ build.gradle 23 | ├─ [etc...] 24 | ├─ ios/ 25 | ├─ Pods/ 26 | ├─ your native app/ 27 | ├─ webapp/ 28 | ├─ Podfile 29 | ├─ [etc...] 30 | ├─ web/ 31 | ├─ build/ 32 | ├─ src/ 33 | ├─ package.json 34 | ├─ [etc...] 35 | ``` 36 | 37 | :::note 38 | For iOS, the `webapp` folder will also need to be added to the XCode project to package the web assets with the app. To do so, drag the `webapp` folder from Finder and drop it to the same folder in the Project Navigator while in XCode. 39 | ::: 40 | 41 | ## Using NPM Scripts to Copy Assets 42 | 43 | Now that we know where to copy the built web assets, we will use NPM scripts to run after the build step. 44 | 45 | Here, we will set up a `postbuild` script that will run after the `npm run build` task finishes, which will in turn call `copyto:android` and `copyto:ios`: 46 | 47 | ```json title="package.json scripts" 48 | "postbuild": "npm run copyto:android && npm run copyto:ios", 49 | "copyto:android": "rm -rf ../android/app/src/main/assets/webapp && cp -R build/ ../android/app/src/main/assets/webapp", 50 | "copyto:ios": "rm -rf '../ios/portal test app/webapp' && cp -R build/ .'./ios/portal test app/webapp'" 51 | ``` 52 | 53 | The `copyto:xxx` scripts will first remove the current folder and then copy the web assets from the build folder in the web project to the directories in which they go in the native apps. 54 | 55 | :::note 56 | We use the Unix command `rm` in the scripts to remove the directories, which might not work on other platforms. If you need a cross-platform solution, look into the [rimraf](https://www.npmjs.com/package/rimraf) NPM package. 57 | ::: 58 | 59 | The dev workflow for this process is: 60 | 61 | 1. Make changes to the web app 62 | 2. Run the `npm run build` task in the web folder 63 | 3. Build the native apps 64 | -------------------------------------------------------------------------------- /website/docs/for-capacitor/example.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: E-commerce Example App 3 | sidebar_label: E-commerce Example App 4 | --- 5 | 6 | ## Overview​ 7 | 8 | The Ionic Team has put together an E-commerce demo application for Capacitor that uses multiple web-based Portals. This is meant to help gain an understanding of how Federated Capacitor can be used in conjunction with Module Federation. The example is built in React. Although this example is a monorepo it is not a requirement on Federated Capacitor applications. 9 | 10 | The app contains one primary application that is named `shell`. This application is responsible for displaying when the app first starts. It is also the application that contains all of the required Capacitor plugins. 11 | 12 | ### Micro Frontend structure 13 | 14 | The application is broken down into micro frontends in this way. 15 | 16 | **shell** 17 | 18 | - List of Products Page 19 | - Individual Product Page 20 | - Cart Page 21 | 22 | **account** 23 | 24 | - User Profile Page 25 | 26 | **checkout** 27 | 28 | - Checkout Page 29 | 30 | **helpinfo** 31 | 32 | - Help Page Content 33 | 34 | The source is [available on GitHub](https://github.com/ionic-team/capacitor-portals-ecommerce) and including the primary shell application and the micro frontends. 35 | -------------------------------------------------------------------------------- /website/docs/for-capacitor/live-updates.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Live Updates 3 | sidebar_label: Live Updates 4 | --- 5 | 6 | The real power of Federated Capacitor comes in with [Appflow Live Updates](https://ionic.io/docs/appflow/deploy/intro). So let's configure a few of the applications we are using. If you are using a monorepo you will need to add some configuration to your base directory. You can read more about that in [Appflows docs](https://ionic.io/docs/appflow/cookbook/appflow-config) 7 | 8 | First let's add Live Updates to the main shell application. Modify the `capacitor.config.ts` file to add a `liveUpdateConfig` section to the FederatedCapacitor application. 9 | 10 | ```typescript title=capacitor.config.ts 11 | FederatedCapacitor: { 12 | shell: { 13 | name: 'shell', 14 | liveUpdateConfig: { 15 | appId: "YOUR_APP_ID_IN_APPFLOW", 16 | channel: "production", 17 | autoUpdateMethod: "none", 18 | strategy: "differential" 19 | } 20 | }, 21 | ``` 22 | 23 | - **appId**: Replace `YOUR_APP_ID_IN_APPFLOW` with your own id. (example: `e9597b11`) 24 | - **channel**: By default all releases happen in Appflow from the `production` channel but you can setup whatever channel you need here. 25 | - **autoUpdateMethod**: Options would be `none` or `background`. You would choose `none` if you want to call the update method on your own, but the most common choice here would be `background` where the updates happen automatically. 26 | - **strategy**: Options would be `zip` or `differential`. `zip` is the default and will download the entire update each time. `differential` will only download the changes between the current version and the new version. 27 | 28 | After you have made these changes and done a build the application will begin pulling updates from Appflow using the IDs that you have provided. See the [reference](reference) documentation for methods on performing live updates manually from your application code. 29 | 30 | ## Self-hosted Live Updates 31 | 32 | Federated Capacitor supports [Self-hosted Live Updates](https://ionic.io/docs/appflow/deploy/setup/self-hosted)! For our customers with strict security requirements, Self-hosted Live Updates includes enhanced security features built on top of Appflow's already secure delivery mechanisms. Follow the instructions in the [Appflow Docs](https://ionic.io/docs/appflow/deploy/setup/self-hosted#code-signing-generate-live-update-signing-keys) to get started. However, there is no need to install any additional plugins in your app. 33 | 34 | Modify the `capacitor.config.ts` file to add a `liveUpdatesKey` field to the FederatedCapacitor application. This informs the Federated Capacitor plugin where to find the public key used for Self-hosted Live Updates. 35 | 36 | ```typescript title=capacitor.config.ts 37 | FederatedCapacitor: { 38 | liveUpdatesKey: "public.pem" 39 | shell: { 40 | name: 'shell', 41 | liveUpdateConfig: { 42 | appId: "YOUR_APP_ID_IN_APPFLOW", 43 | channel: "production", 44 | autoUpdateMethod: "none", 45 | strategy: "differential" 46 | } 47 | }, 48 | ``` 49 | 50 | The location specified is relative to the **shell** directory specified in the config. For example, in the [example](example) application we placed the `public.pem` key file in the `packages/shell` directory. 51 | 52 | :::note 53 | While the `liveUpdatesKey` is present in the config, Federated Capacitor will assume that all Live Updates need to be validated against the provided public key. If this feature is not required, do not provide a key in the config. 54 | ::: 55 | -------------------------------------------------------------------------------- /website/docs/for-capacitor/overview.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Overview 3 | sidebar_label: Overview 4 | --- 5 | 6 | Federated Capacitor is a flavor of Portals that works within [Capacitor](https://capacitorjs.com/). 7 | 8 | ## Why use micro frontends 9 | 10 | Traditional development of a monolithic frontend applications have required all developers to collaborate within a single codebase and also ship updates together using a release process. As an application grows over time and more teams are added this can slow the development of the overall system. 11 | 12 | One way of alleviating this is to divide the larger monolith into smaller applications. This way each team can develop in isolation and then ship updates on their own timeline. There are many existing tools for frontend developers that do just that. The most common is [Module Federation](https://ionic.io/resources/articles/micro-frontends-with-module-federation). This tool allows for teams to develop web applications independently and then bring the builds dependencies together at run-time. It works great for hosted web applications. For most teams a tool like this is probably not needed, but when you have multiple teams trying to collaborate and ship updates independently it can offer a lot of autonomy and value. 13 | 14 | ## Purpose of Federated Capacitor 15 | 16 | Micro frontends work great for the web because developers can deploy in real time. Each application lives on a different server or directory location so this separation is inherent on the web, but the same can not be said for mobile applications. 17 | 18 | Mobile applications (Native or Hybrid) are monoliths by their very nature. They are built, packaged, and released through a single release pipeline straight to an app store or to a Mobile Device Management solution. 19 | 20 | Federated Capacitor complements existing micro frontend tools like Module Federation to break down mobile monoliths into a micro frontend architecture. These applications can be developed in isolation and then released independently using Appflow with over the air live updates. 21 | 22 | **Steps for implementing Federated Capacitor** 23 | 24 | 1. Setup Module Federation between your mobile applications. Ensure that it works in a browser. 25 | 2. Configure the shell application to work in Capacitor. Ensure that it works in a mobile emulator. 26 | 3. Setup Appflow live updates so that you can publish updates to each application independently. 27 | 28 | ## Getting Started 29 | 30 | If you have not already setup Ionic Enterprise in your app, [follow the one-time setup steps](https://ionic.io/docs/supported-plugins/setup). 31 | 32 | Next, install the plugin from command line: 33 | 34 | ```bash 35 | npm install @ionic-enterprise/federated-capacitor 36 | npx cap sync 37 | ``` 38 | 39 | ### iOS 40 | 41 | In the generated Main.storyboard in your Capacitor project, update the subclass of the view controller from `CAPBridgeViewController` to `FederatedCapacitorViewController`. 42 | 43 | ### Android 44 | 45 | In the generated MainActivity in your Capacitor project, extend FederatedCapacitorBridgeActivity instead of the usual BridgeActivity. 46 | 47 | ```java 48 | public class MainActivity extends FederatedCapacitorBridgeActivity {} 49 | ``` 50 | -------------------------------------------------------------------------------- /website/docs/for-capacitor/upgrade-guides.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Upgrade Guides 3 | sidebar_label: Upgrade Guides 4 | --- 5 | 6 | ## @ionic-enterprise/federated-capacitor 0.3.0 7 | 8 | This update supports Capacitor version 7. 9 | 10 | ## @ionic-enterprise/federated-capacitor 0.2.4 11 | 12 | Adds a new function `refreshMicroApps()` to allow for the update of web apps through Live Updates without refreshing the currently loaded apps. 13 | 14 | ## @ionic-enterprise/federated-capacitor 0.2.3 15 | 16 | Changes the use of Appflow CLI to the new Portals CLI internally. 17 | 18 | ## @ionic-enterprise/federated-capacitor 0.2.2 19 | 20 | Provides ability for iOS users to override `capacitorDidLoad()` on iOS. 21 | 22 | ## @ionic-enterprise/federated-capacitor 0.2.1 23 | 24 | Addressed a bug where the config was not correctly loaded on Android. 25 | 26 | ## @ionic-enterprise/federated-capacitor 0.2.0 27 | 28 | This update supports Capacitor version 6. 29 | 30 | ## @ionic-enterprise/federated-capacitor 0.1.7 31 | 32 | ### Differential Support 33 | Differential Live Update support has been added with this release. See the [Live Updates](live-updates) documentation for more information on how to configure this feature. 34 | 35 | ## @ionic-enterprise/federated-capacitor 0.1.0 36 | 37 | 'Portals for Capacitor' has been rebranded as 'Federated Capacitor'. This has come with 38 | a number of breaking changes.`@ionic-enterprise/capacitor-portals` has been renamed to `@ionic-enterprise/federated-capacitor`. 39 | You will need to update your dependency to reflect the package name. 40 | 41 | ### Configuration 42 | 43 | The plugin itself has been renamed from `Portals` to `FederatedCapacitor` and the requirement for `webDir` for the shell app to be redefined has been removed. 44 | 45 | Using `@ionic-enterprise/capacitor-portals`: 46 | ```typescript 47 | const config: CapacitorConfig = { 48 | appId: 'com.foo', 49 | appName: 'Foo', 50 | webDir: './build', 51 | plugins: { 52 | Portals: { 53 | shell: { 54 | name: 'shell', 55 | webDir: './build' 56 | } 57 | } 58 | } 59 | } 60 | ``` 61 | 62 | Using `@ionic-enterprise/federated-capacitor`: 63 | ```typescript 64 | const config: CapacitorConfig = { 65 | appId: 'com.foo', 66 | appName: 'Foo', 67 | webDir: './build', 68 | plugins: { 69 | FederatedCapacitor: { 70 | shell: { 71 | name: 'shell' 72 | } 73 | } 74 | } 75 | } 76 | ``` 77 | 78 | ### Plugin Methods 79 | 80 | `syncOne`, `syncSome`, and `syncAll` now provide more metadata when a sync has completed. 81 | The most useful piece of information is whether or not the active application path for a 82 | microfrontend has changed, which can be used to determine whether or not a reload needs to 83 | occur. 84 | 85 | ### MainActivity and UIViewController subclass name changes 86 | 87 | In your Android project, update `MainActivity` to extend `FederatedCapacitorBridgeActivity` instead of `PortalsBridgeActivity` 88 | In your iOS project, update the ViewController in Main.storyboard to subclass `FederatedCapacitorViewController` instead of `PortalsViewController`. 89 | 90 | -------------------------------------------------------------------------------- /website/docs/for-ios/changelog.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Changelog 3 | hide_table_of_contents: true 4 | --- 5 | 6 | import ReleaseNotes from '@site/src/components/page/changelog'; 7 | import releases from "./changelog.json"; 8 | 9 | 10 | -------------------------------------------------------------------------------- /website/docs/for-ios/examples/ecommerce-app-live-updates.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: E-commerce App with Live Updates 3 | sidebar_label: Live Updates E-commerce App 4 | --- 5 | 6 | ## Overview 7 | 8 | The Ionic Team has enhanced the E-commerce demo application to demonstrate how to use Live Updates in an app using Portals. The Android application uses Fragments while the iOS application uses Storyboard/ViewController based views. For more information about the demo app, see our [Portals E-commerce demo page](./ecommerce-app.md). 9 | 10 | Below is a list of which portions of the app are native and which portions of the app are portals. 11 | 12 | Native Screens 13 | 14 | - List of Products Page 15 | - Individual Product Page 16 | - Cart Page 17 | - Settings Page 18 | 19 | Web Screens 20 | 21 | - Checkout Page 22 | - Help Page 23 | - User Details Page 24 | 25 | The source is [available on GitHub](https://github.com/ionic-team/live-updates-ecommerce-demo) and includes the iOS, Android, and Web projects. 26 | 27 | ## Highlights 28 | 29 | ### Portals Apps 30 | 31 | The demo app for Live Updates uses two separate web apps to provide content for the three Portals: 32 | 33 | - Help Page 34 | - User Details and Checkout Page 35 | 36 | ### Settings Page 37 | 38 | A settings page was added to demonstrate features of the Live Updates SDK. This page allows you to view the status of any occurring updates, delete the content of any previous updates, and trigger a sync manually. The settings page also allows you to change the current channel used by Live Updates for both web apps. The app must be fully closed for the channel change to take effect for the next sync. 39 | 40 | ### Update Strategy 41 | 42 | In both Android and iOS, the Portals are configured on initial app load time and a Live Updates sync occurs immediately. Subsequent checks are made when the native app is resumed from the background and a sync will occur if more than six hours have passed since the last sync. 43 | -------------------------------------------------------------------------------- /website/docs/for-ios/examples/ecommerce-app.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: E-commerce App 3 | sidebar_label: E-commerce App 4 | --- 5 | 6 | ## Overview 7 | 8 | The Ionic Team has put together an E-commerce demo application for Android and iOS that uses both native layouts and web-based Portals. The Android application uses Fragments while the iOS application uses Storyboard/ViewController based views. 9 | 10 | Below is a list of which portions of the app are native and which portions of the app are portals. 11 | 12 | Native Screens 13 | 14 | - List of Products Page 15 | - Individual Product Page 16 | - Cart Page 17 | 18 | Web Screens 19 | 20 | - Checkout Page 21 | - Help Page 22 | - User Details Page 23 | 24 | ![Example banner](/img/portals-diagram-swift.png) 25 | 26 | The source is [available on GitHub](https://github.com/ionic-team/portals-ecommerce-demo/) and includes the iOS, Android, and Web projects. 27 | 28 | ## Highlights 29 | 30 | ### Native 31 | 32 | The iOS application uses ViewControllers for each page in the app. The main navigation is built using Storyboard with a [UITabBarController](https://developer.apple.com/documentation/uikit/uitabbarcontroller) to navigate between the three main different sections of the application: the Products page, Cart page, and the User Details page. 33 | 34 | ### Cart Portal 35 | 36 | The Portal used to present the shopping cart web application is displayed as an overlay with the [PageSheet](https://developer.apple.com/documentation/uikit/uimodalpresentationstyle/pagesheet) style, demonstrating Portals use in a modal view. 37 | 38 | ### Help and User Details Portal 39 | 40 | The Help and User Details pages display the portal in a ViewController. These are implemented the same way, however the User Details page is displayed as the third tab in the main application navigation Storyboard. 41 | -------------------------------------------------------------------------------- /website/docs/for-ios/how-to/advanced-configuration.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Advanced Configuration 3 | sidebar_label: Advanced Configuration 4 | --- 5 | 6 | import Tabs from '@theme/Tabs'; 7 | import TabItem from '@theme/TabItem'; 8 | import CodeBlock from '@theme/CodeBlock'; 9 | import { getCapacitorVersion, getPortalsVersion, getPortalsVersionIos, getPortalsVersionAndroid } from '@site/src/util'; 10 | 11 | Portals instances can be further configured by providing a Capacitor Configuration to the Portal. 12 | 13 | ## Capacitor Config File 14 | 15 | The easiest approach for configuring a Portal is to let the web application include a [Capacitor Configuration](https://capacitorjs.com/docs/config) 16 | in the form of a `capacitor.config.json` in the root of its build folder. Portals will provide the Capacitor runtime with the configuration and 17 | no intervention is needed on the part of the native team. 18 | 19 | :::note 20 | Each Portal may have its own config file. 21 | ::: 22 | 23 | ## Programmatic Capacitor Configuration 24 | 25 | To programmatically configure Capacitor, use the `configuring` method on `Portal` to override any default Capacitor configuration: 26 | 27 | ```swift 28 | let portal = Portal(name: "foo") 29 | .configuring(\.loggingBehavior, .none) 30 | .configuring(\.allowLinkPreviews, true) 31 | .configuring(\.isWebDebuggable, false) 32 | ``` 33 | 34 | In the event the same value is configured both programmatically and via `capacitor.config.json`, the programmatic configuration takes precedence. 35 | 36 | -------------------------------------------------------------------------------- /website/docs/for-ios/how-to/multiple-portals-multiple-web-apps.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Multiple Portals and Multiple Web Applications 3 | sidebar_label: Multi Portal & Multiple Web Apps 4 | --- 5 | 6 | import Tabs from '@theme/Tabs'; 7 | import TabItem from '@theme/TabItem'; 8 | 9 | In some cases, it makes sense to have different teams handle different portals in bigger applications. For example, one team may maintain a _"Maps & Geolocation"_ Portal while another might handle a _"Shopping & Checkout"_ Portal. 10 | 11 | ## Declaring Multiple Portals 12 | 13 | Setting up multiple Portals is as easy as initializing another Portal. 14 | 15 | 21 | 22 | 23 | ```swift 24 | let maps = Portal(name: "maps") 25 | let shopping = Portal(name: "shopping") 26 | ``` 27 | 28 | This can be made even simpler using the `ExpressibleByStringLiteral` conformance of Portal: 29 | 30 | ```swift 31 | let maps: Portal = "maps" 32 | let shopping: Portal = "shopping" 33 | ``` 34 | 35 | 36 | 37 | 38 | 39 | ```objectivec 40 | IONPortal *mapsPortal = [[IONPortal alloc] initWithName:@"maps" startDir:nil initialContext:nil]; 41 | IONPortal *shoppingPortal = [[IONPortal alloc] initWithName:@"shopping" startDir:nil initialContext:nil]; 42 | ``` 43 | 44 | 45 | 46 | 47 | 48 | Now, the _"Maps & Geolocation"_ Portal will read from the `maps` directory in your assets folder and the _"Shopping & Checkout"_ Portal will read from the `shopping` directory in your assets folder. 49 | 50 | ## Project Structure 51 | 52 | In your project, you'll need to setup multiple folders in your Assets directory if your web applications are discrete apps. For more information on how to setup web bundles in your native project, see [our how-to guide](./pull-in-web-bundle.md). 53 | -------------------------------------------------------------------------------- /website/docs/for-ios/how-to/multiple-portals-single-web-app.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Multiple Portals and Single Page Applications 3 | sidebar_label: Multi Portal & Single Page Apps 4 | --- 5 | 6 | import Tabs from '@theme/Tabs'; 7 | import TabItem from '@theme/TabItem'; 8 | 9 | In some cases, it break down a large Single Page Application (SPA) into multiple Portals to create a better User Experience. The below example is similar to the [Multi Portals with Multiple Web Applications](./multiple-portals-multiple-web-apps.md) example, but it is a SPA rather than multiple applications. 10 | 11 | ## Declaring Multiple Portals 12 | 13 | Setting up multiple Portals is as simple as initializing them. Each Portal will function independently of one another and will be a separate instance of the SPA. 14 | 15 | 21 | 22 | 23 | ```swift 24 | let mapsPortal = Portal( 25 | name: "maps", 26 | startDir: "web", 27 | initialContext: ["route": "/maps"] 28 | ) 29 | 30 | let shoppingPortal = Portal( 31 | name: "shopping", 32 | startDir: "web", 33 | initialContext: ["route": "/shopping"] 34 | ) 35 | ``` 36 | 37 | If you find yourself needing these Portals in multiple locations in your application, you may find it convenient to extend `Portal`: 38 | 39 | ```swift title=Portal+SPAPortals.swift 40 | extension Portal { 41 | static let maps = Portal( 42 | name: "maps", 43 | startDir: "web", 44 | initialContext: ["route": "/maps"] 45 | ) 46 | 47 | static let shopping = Portal( 48 | name: "shopping", 49 | startDir: "web", 50 | initialContext: ["route": "/shopping"] 51 | ) 52 | } 53 | ``` 54 | 55 | Then you can use them throughout your application: 56 | 57 | ```swift 58 | PortalUIView(portal: .maps) 59 | PortalUIView(portal: .shopping) 60 | ``` 61 | 62 | 63 | 64 | 65 | 66 | ```objectivec 67 | IONPortal *mapsPortal = [[IONPortal alloc] initWithName:@"maps" startDir:@"web" initialContext:@{ @"route": @"/map" }]; 68 | IONPortal *shoppingPortal = [[IONPortal alloc] initWithName:@"maps" startDir:@"web" initialContext:@{ @"route": @"map" }]; 69 | ``` 70 | 71 | 72 | 73 | 74 | 75 | The _"Maps & Geolocation"_ Portal will be an instance of the SPA in the `web` folder in the Assets directory with an _"initialContext"_ of the following. 76 | 77 | ```json 78 | { 79 | "route": "/map" 80 | } 81 | ``` 82 | 83 | Similarly, the "Shopping & Checkout" Portal will be a separate instance of the SPA in the `web` folder in the Assets directory with an _"initialContext"_ of the following. 84 | 85 | ```json 86 | { 87 | "route": "/shopping" 88 | } 89 | ``` 90 | 91 | ## Injecting the Initial Context Into the Web Application 92 | 93 | With this _initialContext_ value set, you can use this to properly navigate to the correct route within your Portal. The [Portals E-commerce example](../examples/ecommerce-app.md) uses this method. You can see how we [inject the context here on Github](https://github.com/ionic-team/portals-ecommerce-demo/blob/main/web/src/index.tsx) using the [Portal.getInitialContext() function](../../for-web/portals-plugin.md#getinitialcontext). 94 | 95 | ## Project Structure 96 | 97 | In your project, you'll need to a single folder in your Assets directory that contains the built output for your SPA. For more information on how to setup web bundles in your native project, see [our how-to guide](./pull-in-web-bundle.md). 98 | -------------------------------------------------------------------------------- /website/docs/for-ios/how-to/pull-in-web-bundle.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: How to Use a Web App in a Portal 3 | sidebar_label: Use a Web App in a Portal 4 | --- 5 | 6 | import Tabs from '@theme/Tabs'; 7 | import TabItem from '@theme/TabItem'; 8 | 9 | In order to use web applications in your native applications, you'll need to properly setup your project to be able to include a web bundle. 10 | 11 | ## Setup the Web Asset Directory 12 | 13 | Your web application needs to be copied into your native application. On iOS, put your web assets in a directory at the same level that contains your main source and the `info.plist` file: 14 | 15 | ![iOS Web Asset Directory](/img/how-to/ios-web-asset-folder.png) 16 | 17 | :::info 18 | You must ensure that you add your web application root folder as a folder reference and not as a group in Xcode. Otherwise, your directory structure will be ignored and the Portal will not render. If you have completed this step successfully, the folder icon in Xcode will be **blue**. 19 | 20 | ![iOS Add folder as reference](/img/how-to/ios-create-folder-references.png) 21 | ::: 22 | 23 | ```swift 24 | let portal = Portal(name: "myPortalWebApp") 25 | // or...using a different portalId and starting directory 26 | let helpPortal = Portal(name: "help", startDir: "myPortalWebApp") 27 | ``` 28 | 29 | ## Automating the Process 30 | 31 | Once you have your web code and native code linked up, you will need a process to continually copy in new versions of the web application into your mobile projects. 32 | 33 | We recommend having some type of automation set up so the mobile developer doesn't have to manually copy over the web code every time there is a new change. We have a few guides for ideas to do so in a [monorepo](../tutorials/monorepo-example.md) or [git submodules](https://git-scm.com/book/en/v2/Git-Tools-Submodules). 34 | -------------------------------------------------------------------------------- /website/docs/for-ios/how-to/reloading-with-live-updates.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Reload Portals with Live Updates 3 | sidebar_label: Reload Portals with Live Updates 4 | --- 5 | 6 | import Tabs from '@theme/Tabs'; 7 | import TabItem from '@theme/TabItem'; 8 | 9 | When new Portal web content is downloaded from Appflow using Live Updates, the Portal will need to be reloaded before the app user sees the updated content. The default behavior of the Portals library is to continue displaying the existing web content to the user until the view is reloaded by the user. A Portal can be reloaded with code if you want the user to view the new content sooner. 10 | 11 | :::tip 12 | Consider that a user may be in the middle of doing work inside the Portal as new web content is downlaoded through Live Updates and reloading the Portal may interrupt them. 13 | ::: 14 | 15 | The following examples show how an active Portal could be reloaded after a Live Update has finished downloading. 16 | 17 | ```swift title="ViewController.swift" 18 | override func viewDidLoad() { 19 | LiveUpdateManager.shared.sync(appId: self.appId) { result in 20 | switch result { 21 | case .error(let error): 22 | // handle error 23 | print("Sync failed with error: \(error)") 24 | case .success: 25 | self.portalView.reload() 26 | } 27 | } 28 | 29 | super.viewDidLoad() 30 | } 31 | ``` 32 | -------------------------------------------------------------------------------- /website/docs/for-ios/how-to/sharing-assets.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Sharing Assets Between Portals 3 | sidebar_label: Sharing Assets 4 | --- 5 | 6 | If you are devloping an application that contains multiple Portals, the Portals library supports the ability to share asset files between them. This is helpful to reduce the size of your overall app if those Portals use assets that are the same such as large media files or font files. 7 | 8 | ## Register Shared Assets 9 | 10 | Create a directory within your app bundle to hold shared assets for your Portals. In this example we will name this directory "shared". 11 | 12 | Next, when creating the Portal register an [AssetMap](https://ionic-portals-ios.vercel.app/documentation/ionicportals/assetmap) for the shared asset directory. The first property is a name for your shared assets registration, followed by a virtual path the web content will use. The last property is the name of the directory we created for the shared assets, "shared". 13 | 14 | ```swift 15 | extension Portal { 16 | static let webapp = Self( 17 | name: "myportal", 18 | startDir: "portals/myportal", 19 | assetMaps: [.webapp] 20 | ) 21 | } 22 | 23 | extension AssetMap { 24 | static let webapp = Self( 25 | name: "myshared", 26 | virtualPath: "/shared/assets", 27 | startDir: "shared" 28 | ) 29 | } 30 | ``` 31 | 32 | When this Portal loads, the shared asset information will be passed to the web content as part of the Portal's [InitialContext](../../for-web/portals-plugin.md#initialcontext) object. 33 | 34 | ## Using Registered Assets 35 | 36 | Refer to the page in the [Web docs](../../for-web/sharing-assets.md) to learn how to use your registered shared assets in your web content. -------------------------------------------------------------------------------- /website/docs/for-ios/how-to/sync-with-live-updates.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Syncing a Portal with Live Updates 3 | sidebar_label: Syncing with Live Updates 4 | --- 5 | 6 | import Tabs from '@theme/Tabs'; 7 | import TabItem from '@theme/TabItem'; 8 | 9 | The sync operation checks Appflow for a new version of a web app used in a Portal. If an update is available, the files are downloaded and the Portal is updated to use those new files the next time it loads. The Live Updates SDK will perform a sync when the Live Update Config is added to a Portal by default. This is typically done when an app is initially launched, and requires a full restart of an app to trigger subsequent syncs. We recommend performing a sync in other situations to provide more chances for Portals to update. 10 | 11 | ## Triggering a Sync 12 | 13 | A sync can be triggered by calling the `sync` function in the Live Update Manager. 14 | 15 | ```swift 16 | // Sync all apps 17 | LiveUpdateManager.shared.sync() 18 | 19 | // Sync a specific app 20 | LiveUpdateManager.shared.sync(appId: "appId") 21 | 22 | // Sync all apps not in parallel and with a callback 23 | LiveUpdateManager.shared.sync( 24 | isParallel: false, 25 | syncComplete: { print("Sync completed!") }, 26 | appComplete: { _ in print("App update complete") } 27 | ) 28 | ``` 29 | 30 | ## When to Sync 31 | 32 | Deciding when to sync is at your discretion. 33 | 34 | :::tip 35 | Depending on the size of your web app assets, a sync operation could be expensive. Keep in mind that mobile users may be on a cellular network data connection or may be opening the app from a mininized or background state to use it. 36 | ::: 37 | 38 | The following example performs a sync when an app resumes as long as six hours has elapsed since the previous sync. This ensures a check is performed every time a user opens the app whether it is opened for the first time or opened from a minimized state. 39 | 40 | ```swift title="ViewController.swift" 41 | override func viewDidLoad() { 42 | // If it has been more than 6 hours since last update check, sync now. 43 | if let lastUpdate = LiveUpdateManager.shared.lastSync(for: "appId"), 44 | let hoursSinceLastUpdate = Calendar.current 45 | .dateComponents([.hour], from: lastUpdate, to: Date()).hour, 46 | hours > 6 { 47 | 48 | LiveUpdateManager.shared.sync(appId: "appId") 49 | } 50 | } 51 | ``` 52 | -------------------------------------------------------------------------------- /website/docs/for-ios/how-to/use-portals-in-an-ios-library.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: How to Use Portals in an iOS Library 3 | sidebar_label: Use Portals in an iOS Library 4 | --- 5 | 6 | import Tabs from '@theme/Tabs'; 7 | import TabItem from '@theme/TabItem'; 8 | import CodeBlock from '@theme/CodeBlock'; 9 | 10 | In larger teams and organizations, teams may be split to work on one or more feature frameworks. In those situations, it may be arduous or impossible to have web builds integrated as part of the applications main bundle. 11 | 12 | ## Web Asset Location 13 | 14 | In order to get Portals to locate the appropriate resources, you need to provide a Bundle for it to access through it's initializer. 15 | 16 | 22 | 23 | 24 | In a framework, you need to explicitly initialize a bundle: 25 | 26 | ```swift title=Bundle+Framework.swift 27 | extension Bundle { 28 | private class Finder {} 29 | static let framework = Bundle(from: Finder.self) 30 | } 31 | ``` 32 | 33 | Then, in the Portal initializer, pass your frameworks bundle to the Portal initializer: 34 | 35 | ```swift 36 | let portal = Portal( 37 | name: "webapp", 38 | bundle: .framework 39 | ) 40 | ``` 41 | 42 | 43 | 44 | 45 | 46 | In the Portal initializer, pass `Bundle.module` to the Portal initializer: 47 | 48 | ```swift 49 | import IonicPortals 50 | 51 | let portal = Portal( 52 | name: "webapp", 53 | bundle: .module 54 | ) 55 | ``` 56 | 57 | 58 | 59 | 60 | 61 | ## Live Update Configuration 62 | 63 | If many teams are using Portals in an application using Live Updates, you may want to control the update lifecycle of your framework's Portals. Using `LiveUpdateManager.shared` in a large team across many frameworks would force every framework to share the same update policies and caches. 64 | 65 | First, create a LiveUpdateManager that can be easily referenced: 66 | 67 | ```swift file=LiveUpdateManager+Framework.swift 68 | import IonicLiveUpdates 69 | 70 | extension LiveUpdateManager { 71 | static let framework = LiveUpdateManager(named: "MyFrameworkName") 72 | } 73 | ``` 74 | 75 | Then, provide the custom `LiveUpdateManager` to your Portal in its initializer: 76 | 77 | ```swift 78 | import IonicPortals 79 | import IonicLiveUpdates 80 | 81 | let portal = Portal( 82 | name: "webapp", 83 | liveUpdateConfig: LiveUpdate(appId: "abc123", channel: "production"), 84 | liveUpdateManager: .framework 85 | ) 86 | ``` 87 | -------------------------------------------------------------------------------- /website/docs/for-ios/known-issues.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Known Issues 3 | sidebar_label: Known Issues 4 | --- 5 | 6 | ## Objective-C App Delegate 7 | 8 | Capacitor (due to a dependency on Cordova for legacy compatibility resons) contains an AppDelegate that will clash with any other Objective-C AppDelegate. To workaround this issue, you will need to rename your applications `AppDelegate` to something else: 9 | 10 | ```@objc title=AppDelegate.h 11 | @interface MYAppDelegate : UIReeponder 12 | ``` 13 | 14 | ```@objc title=AppDelegate.m 15 | @implementation MYAppDelegate 16 | @end 17 | ``` 18 | 19 | ## Notifications 20 | 21 | All versions of IonicPortals on iOS prior to 0.6.5 contain a bug where Capacitor takes control as the `UNNotificationCenterDelegate`. Upgrading to 0.6.5 will resolve the issue. 22 | 23 | ## Swift Package Manager Integration 24 | 25 | There are currently two separate, but similar issues when integrating IonicPortals as an SPM dependency. Both manifest themselves as Invalid Bundle errors from App Store Connect 26 | 27 | ### IonicPortals as a single target dependency 28 | 29 | Capacitor v3.5.1 has an issue where it has an embedded `Cordova.framework`. This causes uploading to App Store Connect to fail with error codes `ITMS-90205` and `ITMS-90206`. The workaround is to delete the embedded framework as part of your build process: 30 | 31 | First, select the affected scheme in Xcode and click "Edit Scheme...": 32 | 33 | ![XCode scheme selector](/img/known-issues/spm-workarounds/01-scheme-edit.png) 34 | 35 | Next, expand the "Build" drop down and select "Post-actions" to run the script after your build has completed: 36 | 37 | ![Scheme editor with build drop down expanded and post-actions selected](/img/known-issues/spm-workarounds/02-post-actions-select.png) 38 | 39 | Then, click the "+" button and select "New Run Script Action": 40 | 41 | ![Post-actions scheme editor with plus button selected and New Run Script Action highlighted](/img/known-issues/spm-workarounds/03-run-script-select.png) 42 | 43 | In the Run Script Action editor, select the target whose build settings you need to inherit: 44 | 45 | ![Run script action editor with build settings drop-down selected](/img/known-issues/spm-workarounds/04-build-settings-select.png) 46 | 47 | Finally, add the following script in the script editor: 48 | 49 | ```bash 50 | rm -rf "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/Frameworks/Capacitor.framework/Frameworks" 51 | ``` 52 | 53 | ![script entered into script editor](/img/known-issues/spm-workarounds/05-script-entry.png) 54 | 55 | ### IonicPortals as a multi-target dependency 56 | 57 | When using IonicPortals as a multi-target dependency across Application and Framework targets, Xcode embeds `Capacitor.framework`, `Cordova.framework`, `IonicLiveUpdates.framework`, and `IonicPortals.framework` into your Framework targets as well. 58 | 59 | To avoid this altogether, you can migrate your Frameworks to be Swift Packages. However, if migrating framework targets to be Swift Packages isn't an option, then in addition to the steps outlined in [IonicPortals as a single target dependency](#ionicportals-as-a-single-target-dependency) add the following to the "Run Script Action" configured in that section: 60 | 61 | ```bash 62 | rm -rf "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/Frameworks/YourFrameworkUsingPortals.framework/Frameworks" 63 | ``` 64 | 65 | ![additional script entered into script editor](/img/known-issues/spm-workarounds/06-script-entry.png) 66 | -------------------------------------------------------------------------------- /website/docs/for-ios/live-updates.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Adding Live Updates 3 | sidebar_label: Adding Live Updates 4 | --- 5 | 6 | import Tabs from '@theme/Tabs'; 7 | import TabItem from '@theme/TabItem'; 8 | import CodeBlock from '@theme/CodeBlock'; 9 | import { getCapacitorVersion, getPortalsVersion, getPortalsVersionIos, getPortalsVersionAndroid, getPortalsVersionRN } from '@site/src/util'; 10 | 11 | Getting started with Live Updates in your Portals app. 12 | 13 | ## Appflow 14 | 15 | Create an app for your Portal in the [Ionic Dashboard](https://dashboard.ionicframework.com). For more information, see our documentation on [using Appflow](https://ionic.io/docs/appflow/quickstart/connect). 16 | 17 | :::info 18 | Take note of the **appId** of your app in Appflow, this will be used by the Live Updates sdk. 19 | ::: 20 | 21 | To test Live Updates, create a new build of your app in Appflow and create a deployment to Live Updates from that build. Take note of the **channel** name as this is also used in the Live Updates sdk. 22 | 23 | ![Deployments](https://i.imgur.com/73detdm.png) 24 | 25 | Deployments in Appflow will be downloaded as new Live Updates. 26 | 27 | ## Configure 28 | 29 | After installing the dependency you need to configure Live Updates as part of the Portal creation process. Add a LiveUpdate config where your Portal is created. Provide the **appId** that corresponds with the app in Appflow, and the **channel** name to subscribe to for updates. 30 | 31 | ```swift {17,21-29} title=AppDelegate.swift 32 | import UIKit 33 | import IonicPortals 34 | 35 | @main 36 | class AppDelegate: UIResponder, UIApplicationDelegate { 37 | func application(_ application: UIApplication, didFinishLaunchingWithOptions 38 | launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 39 | // Override point for customization after application launch. 40 | PortalsRegistrationManager.shared.register(key: "YOUR_PORTALS_KEY") 41 | return true 42 | } 43 | } 44 | 45 | extension Portal { 46 | static let featuredProducts = Portal( 47 | name: "featured_products", 48 | liveUpdateConfig: .webapp 49 | ) 50 | } 51 | 52 | extension LiveUpdate { 53 | private static let activeChannel = "production" 54 | 55 | static let webapp = Self( 56 | appId: "11a0971f", 57 | channel: activeChannel, 58 | syncOnAdd: true 59 | ) 60 | } 61 | ``` 62 | 63 | :::note 64 | Note the `syncOnAdd` property above. When this property is set to true (or omitted), a sync operation will occur the first time a Portal is created to check if an update is available, If so, the assets are downloaded to the device and setup with the Portal and will be applied the next time the Portal is loaded. 65 | 66 | If set to false then it is up to the application developer to apply the Live Updates manually. More info available in the [Syncing with Live Updates](./how-to/sync-with-live-updates.md) how to. 67 | ::: 68 | 69 | By default, when the app loads for the first time and the portal is created, a sync will occur. A sync operation checks the Appflow servers for a new version of the app. If a new version is available, the app files are downloaded to the device and setup with the Portal. The next time the Portal is loaded, the new version will load automatically. 70 | -------------------------------------------------------------------------------- /website/docs/for-ios/tutorials/auth-token-example.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Passing Auth Token from Native to Portal 3 | sidebar_label: Passing Auth Token from Native to Portal 4 | --- 5 | 6 | A common scenario that a developer might run into is having a web experience tailored for the current logged in user. Another scenario might be having a log-in screen be a Portal so it can easily be designed and updated across Android and iOS. In both of these scenarios, a developer would need to account for handling auth tokens between native and web code. Below are a few examples of how to solve these problems. 7 | 8 | ## Passing From Native to Web 9 | 10 | When showing a Portal after a user has logged in, there are a few different ways to pass user auth tokens to a Portal. 11 | 12 | - Providing a value to the `initialContext` argument of the Portal initializer. 13 | - Using the `@ionic/portals` to publish a message to the web app with the current auth tokens. 14 | - Using a custom plugin to send data back and forth from native and web. 15 | 16 | ### Setting the Initial Context 17 | 18 | The easiest way to set the Portal's auth tokens is to set the initial context of the portal. The initial context will allow you to pass data that can be read almost immediately in the Portal. 19 | 20 | ```swift {5} 21 | let userPage = Portal( 22 | name: "user_page", 23 | initialContext: [ 24 | "route": "/user", 25 | "auth": /* Auth Data */ 26 | ] 27 | ) 28 | ``` 29 | 30 | Then, in the entry point to your web application, you can use `getInitialContext()` to read the data passed in and act on it. 31 | 32 | ```typescript title=main.ts 33 | import { getInitialContext } from "@ionic/portals"; 34 | 35 | type MyPortalContext = { route: string; auth: any }; 36 | const auth = getInitialContext()?.value?.auth; 37 | // rest of the web app... 38 | ``` 39 | 40 | ### Using a Custom Plugin 41 | 42 | Another solution you can do is to create a custom Plugin to handle passing data to and from native and web. This is the solution we used in [the example E-commerce Application](../examples/ecommerce-app.md) since it scales nicely and allows more than just authentication data be passed to and from the layers. 43 | 44 | For information on how to build your own Portal APIs, [see our how-to guide](../how-to/define-api-in-typescript.md). 45 | 46 | ## How to Pass Data From Web to Native 47 | 48 | In some cases, login information changes in the web layer and you want to save the new auth credentials in the native layer. There are several ways of doing that similar to the previous methods. 49 | 50 | ### Using the Pub/Sub Functions in `@ionic/portals` 51 | 52 | One of the functions of the built-in portals module is to publish/subscribe to events. In this example, you could create a `login` topic and call `publish` as shown below. 53 | 54 | ```typescript {9} 55 | import { publish } from "@ionic/portals"; 56 | 57 | const login = () => { 58 | // Login code... 59 | 60 | const topic = "login"; 61 | const newTokens = 62 | /* Values from Login */ 63 | 64 | publish(topic, newTokens); 65 | }; 66 | 67 | login(); 68 | ``` 69 | 70 | To subscribe to the topic, call `subcribe` after loading the Portal. 71 | 72 | ```swift 73 | let cancellable = PortalsPubSub.shared.subscribe(to: "login") { result in 74 | let auth = result.data 75 | // Rest of the native app... 76 | } 77 | ``` 78 | 79 | For more information on how to communicate with a Portal web application, [see our how to guide](../how-to/using-the-portals-plugin.md). 80 | 81 | ### Using a Custom Plugin 82 | 83 | Similar to the previous section, by creating a custom plugin, you have complete control over how to communicate between web and native layers. It is possible to create a function such as `MyPlugin.syncAuthAcrossWebAndNative()` to handle managing auth tokens across web and native. 84 | 85 | For information on how to build your own Portal APIs, [see our how-to guide](../how-to/define-api-in-typescript.md). 86 | -------------------------------------------------------------------------------- /website/docs/for-ios/tutorials/monorepo-example.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Copying Web Assets to Native Projects in a Monorepo 3 | sidebar_label: Copying Web Assets to Native Projects in a Monorepo 4 | --- 5 | 6 | With Ionic Portals, you need to get the built web assets for each of the portals into your native applications. There could be many ways to go about doing so, which will highly depend on your project. This quick tutorial will show how you can accomplish this when working on a monorepo project in which your web apps are in the same repository as your native apps. 7 | 8 | ## Project Structure 9 | 10 | We will set up some NPM scripts in a moment to do the copy tasks. First, though, you need to know where to copy the assets to each native project. For Android, the web assets will go into a folder under `app/src/main/assets`, and for iOS, the folder will go directly into the folder named after your app. Below is a sample monorepo setup with three projects in the root directory (android, ios, and web), and in each native project, we will copy the web assets into a folder named `web app`. The scripts below match this structure. You will need to modify the scripts accordingly based on your project. 11 | 12 | 13 | ``` 14 | project root/ 15 | ├─ android/ 16 | ├─ .gradle 17 | ├─ app/ 18 | ├─ src/ 19 | ├─ main/ 20 | ├─ assets/ 21 | ├─ webapp/ 22 | ├─ build.gradle 23 | ├─ [etc...] 24 | ├─ ios/ 25 | ├─ Pods/ 26 | ├─ your native app/ 27 | ├─ webapp/ 28 | ├─ Podfile 29 | ├─ [etc...] 30 | ├─ web/ 31 | ├─ build/ 32 | ├─ src/ 33 | ├─ package.json 34 | ├─ [etc...] 35 | ``` 36 | 37 | :::note 38 | For iOS, the `webapp` folder will also need to be added to the XCode project to package the web assets with the app. To do so, drag the `webapp` folder from Finder and drop it to the same folder in the Project Navigator while in XCode. 39 | ::: 40 | 41 | ## Using NPM Scripts to Copy Assets 42 | 43 | Now that we know where to copy the built web assets, we will use NPM scripts to run after the build step. 44 | 45 | Here, we will set up a `postbuild` script that will run after the `npm run build` task finishes, which will in turn call `copyto:android` and `copyto:ios`: 46 | 47 | ```json title="package.json scripts" 48 | "postbuild": "npm run copyto:android && npm run copyto:ios", 49 | "copyto:android": "rm -rf ../android/app/src/main/assets/webapp && cp -R build/ ../android/app/src/main/assets/webapp", 50 | "copyto:ios": "rm -rf '../ios/portal test app/webapp' && cp -R build/ .'./ios/portal test app/webapp'" 51 | ``` 52 | 53 | The `copyto:xxx` scripts will first remove the current folder and then copy the web assets from the build folder in the web project to the directories in which they go in the native apps. 54 | 55 | :::note 56 | We use the Unix command `rm` in the scripts to remove the directories, which might not work on other platforms. If you need a cross-platform solution, look into the [rimraf](https://www.npmjs.com/package/rimraf) NPM package. 57 | ::: 58 | 59 | The dev workflow for this process is: 60 | 61 | 1. Make changes to the web app 62 | 2. Run the `npm run build` task in the web folder 63 | 3. Build the native apps 64 | -------------------------------------------------------------------------------- /website/docs/for-react-native/changelog.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Changelog 3 | hide_table_of_contents: true 4 | --- 5 | 6 | import ReleaseNotes from '@site/src/components/page/changelog'; 7 | import releases from "./changelog.json"; 8 | 9 | 10 | -------------------------------------------------------------------------------- /website/docs/for-react-native/examples/ecommerce.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: React Native E-commerce App 3 | sidebar_label: E-commerce App 4 | --- 5 | 6 | ## Overview 7 | 8 | The Ionic Team has put together an E-commerce demo application for React Native that uses both React Native views and web-based Portals. 9 | 10 | Below is a list of which views of the app use React Native and which portions of the app use portals. 11 | 12 | Native Screens 13 | 14 | - List of Products 15 | - Individual Product Detail 16 | - Shopping Cart 17 | 18 | Web Screens 19 | 20 | - Checkout 21 | - Help 22 | - User Profile 23 | 24 | ![Example banner](/img/portals-diagram-reactnative.png) 25 | 26 | The source is [available on GitHub](https://github.com/ionic-team/cs-portals-ecommerce-react-native) and includes the React Native and Web projects. 27 | -------------------------------------------------------------------------------- /website/docs/for-react-native/guide.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Getting Started Guide 3 | sidebar_label: Getting Started Guide 4 | --- 5 | 6 | import Tabs from '@theme/Tabs'; 7 | import TabItem from '@theme/TabItem'; 8 | import CodeBlock from '@theme/CodeBlock'; 9 | import { getCapacitorVersion, getPortalsVersion, getPortalsVersionIos, getPortalsVersionAndroid, getPortalsVersionRN, getiOSMinVersion, getAndroidMinSdk, getRnMinVersion } from '@site/src/util'; 10 | 11 | ## Signup 12 | 13 | Ionic Portals requires a product key to use. Getting a key is easy. 14 | Just head to the [Ionic Dashboard](https://ionic.io/register-portals) and click "Get Access". 15 | 16 | This will present you with a form asking for some additional information. 17 | After submitting the page will refresh and you will immediately see the key that can be used to unlock the use of Portals in your app. 18 | 19 | :::info 20 | You can always use this shareable link to signup for a Product Key: [ionic.io/register-portals](https://ionic.io/register-portals) 21 | ::: 22 | 23 | ## Install 24 | 25 | Ionic Portals is publicly available via Maven Central, Cocoapods, SPM, and NPM. 26 | 27 | 33 | 34 | 35 | To add Portals to your web project, install it via NPM: 36 | 37 | 38 | {`npm install @ionic/portals@${getPortalsVersion()}`} 39 | 40 | 41 | 42 | 43 | 44 | 45 | To add Portals to your React Native project, install it via NPM: 46 | 47 | 48 | {`npm install @ionic/portals-react-native@${getPortalsVersionRN()}`} 49 | 50 | 51 | 52 | 53 | 54 | 55 | ## Configure 56 | 57 | After installing the dependency you need to register your copy of Ionic Portals at runtime. This works both offline and in production. You'll need to call [register(myApiKey)](https://react-native-ionic-portals.vercel.app/functions/register.html) before rendering any Portals in your app. Below is a simple example of how to bootstrap Ionic Portals before loading any Portal instances in your app. To get an API Key, refer to the [Sign Up](#signup) section. 58 | 59 | ```javascript title=App.tsx 60 | import { register } from "@ionic/portals-react-native"; 61 | 62 | await register("YOUR_PORTALS_KEY"); 63 | ``` 64 | 65 | :::warning 66 | Avoid committing your Portals key to source code repositories where it may be publicly visible! 67 | On Android, you can use the [Secrets Gradle Plugin](https://github.com/google/secrets-gradle-plugin) to keep it out of a public repository. 68 | On iOS, you can use an [`.xcconfig` file](https://nshipster.com/xcconfig/) to keep it out of a public repository. 69 | ::: 70 | 71 | ## Supported Platform Versions 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 |
PlatformLatest Portals VersionMinimum Supported Platform Version
iOS{getPortalsVersionIos()}iOS {getiOSMinVersion()}
Android{getPortalsVersionAndroid()}Android SDK {getAndroidMinSdk()}
React Native{getPortalsVersionRN()}React Native {getRnMinVersion()}
95 | -------------------------------------------------------------------------------- /website/docs/for-react-native/how-to/pull-in-web-bundle.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: How to Use a Web App in a Portal 3 | sidebar_label: Use a Web App in a Portal 4 | --- 5 | 6 | import Tabs from '@theme/Tabs'; 7 | import TabItem from '@theme/TabItem'; 8 | 9 | In order to use web applications in your native applications, you'll need to properly setup your project to be able to include a web bundle. 10 | 11 | ## Setup the Web Asset Directory 12 | 13 | Follow the [Android instructions](../../for-android/how-to/pull-in-web-bundle.md) and [iOS instructions](../../for-ios/how-to/pull-in-web-bundle.md) in your native applications. You should make the start directories the same between both platforms to avoid issues rendering a Portal. 14 | 15 | ```javascript 16 | const help = { 17 | name: "myPortalWebApp", 18 | }; 19 | // or...using a different portalId and starting directory 20 | const help = { 21 | name: "help", 22 | startDir: "myPortalWebApp", 23 | }; 24 | ``` 25 | 26 | ## Automating the Process 27 | 28 | Once you have your web code and native code linked up, you will need a process to continually copy in new versions of the web application into your mobile projects. 29 | 30 | We recommend having some type of automation set up so the mobile developer doesn't have to manually copy over the web code every time there is a new change. We have a few guides for ideas to do so in a [monorepo](../tutorials/monorepo-example.md) or [git submodules](https://git-scm.com/book/en/v2/Git-Tools-Submodules). 31 | -------------------------------------------------------------------------------- /website/docs/for-react-native/how-to/sharing-assets.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Sharing Assets Between Portals 3 | sidebar_label: Sharing Assets 4 | --- 5 | 6 | If you are developing an application that contains multiple Portals, the Portals library supports the ability to share asset files between them. This is helpful to reduce the size of your overall app if those Portals use assets that are the same such as large media files or font files. 7 | 8 | ## Register Shared Assets 9 | 10 | Create a directory in `src/main/assets` in your Android project and a folder reference in your iOS project named `shared`. 11 | 12 | :::caution 13 | 14 | It is _extremely_ important that the relative path from `src/main/assets` and `Bundle.main` be the same on both platforms. 15 | 16 | ::: 17 | 18 | Next, when creating the Portal, add an `AssetMap` for the shared asset directory. 19 | 20 | ```typescript 21 | const portal = { 22 | name: 'foo', 23 | assetMaps: [ 24 | { 25 | // This name will be made available to portal code by accessing 26 | // `getInitialContext`: 27 | // const mySharedAssetsVirtualPath = getInitialContext()?.assets?.mySharedAssets 28 | // This can allow for falling back to local development assets if 29 | // a developer working on a portal is not running in the application 30 | // context: 31 | // const assetPathPrefix = mySharedAssetsVirtualPath ?? '/public' 32 | // ` 33 | name: 'mySharedAssets', 34 | // The virtual path is how hrefs from the root of the shared 35 | // directory are determined. For example, to use a shared image 36 | // the portal code may look something like this: 37 | // 38 | virtualPath: '/virtual', 39 | // The root directory of the assets relative to Bundle.main on 40 | // iOS and src/main/assets on Android. If omitted, the root 41 | // of Bundle.main and src/main/assets will be used. 42 | startDir: 'shared' 43 | } 44 | ] 45 | } 46 | ``` 47 | 48 | When this Portal loads, the shared asset information will be passed to the web content as part of the Portal's [InitialContext](../../for-web/portals-plugin.md#initialcontext) object. 49 | 50 | ## Using Registered Assets 51 | 52 | Refer to the page in the [Web docs](../../for-web/sharing-assets.md) to learn how to use your registered shared assets in your web content. 53 | -------------------------------------------------------------------------------- /website/docs/for-react-native/how-to/statically-define-portals.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Static Portals Configuration File 3 | sidebar_label: Statically Register Portals 4 | --- 5 | 6 | An additional configuration method is now included to support a JSON configuration file to configure Portals on application start. The JSON file must be named `portals.config.json` and be placed in the application root on iOS and `assets` root on Android. 7 | 8 | ## JSON Schema 9 | 10 | This Typescript schema is for illustration purposes only. 11 | 12 | ```typescript 13 | type PortalConfig = { 14 | // The name of the public key file for secure live updates. If secure 15 | // live updates are not being used, this field is not needed. 16 | // Must be located in the Bundle.main root on iOS and assets root on Android. 17 | liveUpdatesKey?: string; 18 | // The portals registration key normally passed to `register`. 19 | // This can be omitted if the `register` function is called before 20 | // attempting to render any portals in code. 21 | registrationKey?: string; 22 | } 23 | ``` 24 | 25 | ### Example Configuration 26 | 27 | ```json title=portals.config.json 28 | { 29 | "registrationKey": "YOUR_PORTALS_KEY", 30 | } 31 | ``` 32 | 33 | -------------------------------------------------------------------------------- /website/docs/for-react-native/how-to/sync-with-live-updates.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Syncing a Portal with Live Updates 3 | sidebar_label: Syncing with Live Updates 4 | --- 5 | 6 | The sync operation checks AppFlow for a new version of a web app used in a Portal. If an update is available, the files are downloaded and the Portal is updated to use those new files the next time it loads. The Live Updates SDK will perform a sync with the Live Update Config is added to the portal if it has been configured with `syncOnAdd: true`. This is typically done when an app is initially launched and requires a restart of an app to trigger subsequent syncs. We recommend performing a sync in other situations to provide more chances for Portals to update. 7 | 8 | ## Triggering a Sync 9 | 10 | A sync can be triggered by calling the `syncOne`, `syncSome`, or `syncAll` functions exposed in `@ionic/portals-react-native`: 11 | 12 | ```typescript 13 | import { syncOne, syncSome, syncAll } from `ionic/portals-react-native`; 14 | 15 | // Sync a single live update 16 | const singleResult = await syncOne('appId1'); 17 | 18 | // Sync many live updates 19 | const manyResult = await syncSome(['appId1', 'appId2']); 20 | 21 | // Sync all live updates 22 | const allResult = await syncAll(); 23 | ``` 24 | 25 | The appId provided to `syncOne` and `syncSome` should correspond to the live update configuration associated with the Portal desired to be updated. 26 | 27 | See the Portals for React Native [reference documentation](https://react-native-ionic-portals.vercel.app) for the data that is returned from the sync calls. 28 | 29 | -------------------------------------------------------------------------------- /website/docs/for-react-native/known-issues.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Known Issues 3 | sidebar_label: Known Issues 4 | --- 5 | 6 | ## iOS Notifications 7 | 8 | All versions of IonicPortals on iOS prior to 0.6.5 contain a bug where Capacitor takes control as the `UNNotificationCenterDelegate`. Upgrading to 0.6.5 will resolve the issue. 9 | 10 | ## iOS Swift Package Manager Integration 11 | 12 | There are currently two separate, but similar issues when integrating IonicPortals as an SPM dependency. Both manifest themselves as Invalid Bundle errors from App Store Connect 13 | 14 | ### IonicPortals as a single target dependency 15 | 16 | Capacitor v3.5.1 has an issue where it has an embedded `Cordova.framework`. This causes uploading to App Store Connect to fail with error codes `ITMS-90205` and `ITMS-90206`. The workaround is to delete the embedded framework as part of your build process: 17 | 18 | First, select the affected scheme in Xcode and click "Edit Scheme...": 19 | 20 | ![XCode scheme selector](/img/known-issues/spm-workarounds/01-scheme-edit.png) 21 | 22 | Next, expand the "Build" drop down and select "Post-actions" to run the script after your build has completed: 23 | 24 | ![Scheme editor with build drop down expanded and post-actions selected](/img/known-issues/spm-workarounds/02-post-actions-select.png) 25 | 26 | Then, click the "+" button and select "New Run Script Action": 27 | 28 | ![Post-actions scheme editor with plus button selected and New Run Script Action highlighted](/img/known-issues/spm-workarounds/03-run-script-select.png) 29 | 30 | In the Run Script Action editor, select the target whose build settings you need to inherit: 31 | 32 | ![Run script action editor with build settings drop-down selected](/img/known-issues/spm-workarounds/04-build-settings-select.png) 33 | 34 | Finally, add the following script in the script editor: 35 | 36 | ```bash 37 | rm -rf "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/Frameworks/Capacitor.framework/Frameworks" 38 | ``` 39 | 40 | ![script entered into script editor](/img/known-issues/spm-workarounds/05-script-entry.png) 41 | 42 | ### IonicPortals as a multi-target dependency 43 | 44 | When using IonicPortals as a multi-target dependency across Application and Framework targets, Xcode embeds `Capacitor.framework`, `Cordova.framework`, `IonicLiveUpdates.framework`, and `IonicPortals.framework` into your Framework targets as well. 45 | 46 | To avoid this altogether, you can migrate your Frameworks to be Swift Packages. However, if migrating framework targets to be Swift Packages isn't an option, then in addition to the steps outlined in [IonicPortals as a single target dependency](#ionicportals-as-a-single-target-dependency) add the following to the "Run Script Action" configured in that section: 47 | 48 | ```bash 49 | rm -rf "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/Frameworks/YourFrameworkUsingPortals.framework/Frameworks" 50 | ``` 51 | 52 | ![additional script entered into script editor](/img/known-issues/spm-workarounds/06-script-entry.png) 53 | -------------------------------------------------------------------------------- /website/docs/for-react-native/live-updates.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Getting Started with Live Updates 3 | sidebar_label: Live Updates 4 | --- 5 | 6 | import Tabs from '@theme/Tabs'; 7 | import TabItem from '@theme/TabItem'; 8 | import CodeBlock from '@theme/CodeBlock'; 9 | import { getCapacitorVersion, getPortalsVersion, getPortalsVersionIos, getPortalsVersionAndroid, getPortalsVersionRN } from '@site/src/util'; 10 | 11 | Getting started with Live Updates in your Portals app. 12 | 13 | :::info 14 | To use the Live Updates SDK with Ionic Portals, check out the [Getting Started Guide](./guide.md) for Ionic Portals first. 15 | ::: 16 | 17 | ## Appflow 18 | 19 | Create an app for your Portal in the [Ionic Dashboard](https://dashboard.ionicframework.com). For more information, see our documentation on [using Appflow](https://ionic.io/docs/appflow/quickstart/connect). 20 | 21 | :::info 22 | Take note of the **appId** of your app in Appflow, this will be used by the Live Updates sdk. 23 | ::: 24 | 25 | To test Live Updates, create a new build of your app in Appflow and create a deployment to Live Updates from that build. Take note of the **channel** name as this is also used in the Live Updates sdk. 26 | 27 | ![Deployments](https://i.imgur.com/73detdm.png) 28 | 29 | Deployments in Appflow will be downloaded as new Live Updates. 30 | 31 | ## Install 32 | 33 | The Live Updates SDK is publicly available via Maven Central, Cocoapods, and SPM. 34 | 35 | Live Updates is already added to your React Native project if you have the dependency for Portals in your `package.json`: 36 | 37 | 38 | {`npm install @ionic/portals-react-native@${getPortalsVersionRN()}`} 39 | 40 | 41 | ## Configure 42 | 43 | After installing the dependency you need to configure Live Updates as part of the Portal creation process. Add a LiveUpdate config where your Portal is created. Provide the **appId** that corresponds with the app in Appflow, and the **channel** name to subscribe to for updates. 44 | 45 | ```javascript title=App.tsx 46 | const portal = { 47 | name: "checkout", 48 | liveUpdate: { 49 | appId: "ebd6138b", 50 | channel: "production", 51 | syncOnAdd: true, // pass false if you do not want a sync to immediately occur 52 | }, 53 | }; 54 | ``` 55 | 56 | By default, when the app loads for the first time and the portal is created, a sync will occur. A sync operation checks the Appflow servers for a new version of the app. If a new version is available, the app files are downloaded to the device and setup with the Portal. The next time the Portal is loaded, the new version will load automatically. 57 | -------------------------------------------------------------------------------- /website/docs/for-react-native/tutorials/auth-token-example.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Passing Auth Token from Native to Portal 3 | sidebar_label: Passing Auth Token from Native to Portal 4 | --- 5 | 6 | import Tabs from '@theme/Tabs'; 7 | import TabItem from '@theme/TabItem'; 8 | 9 | A common scenario that a developer might run into is having a web experience tailored for the current logged in user. Another scenario might be having a log-in screen be a Portal so it can easily be designed and updated across Android and iOS. In both of these scenarios, a developer would need to account for handling auth tokens between native and web code. Below are a few examples of how to solve these problems. 10 | 11 | ## Passing From Native to Web 12 | 13 | When showing a Portal after a user has logged in, there are a few different ways to pass user auth tokens to a Portal. 14 | 15 | - Using the `PortalBuilder.setInitialContext()` function to set the initial state of the Portal. 16 | - Using the `PortalsPlugin` to publish a message to the web app with the current auth tokens. 17 | - Using a custom plugin to send data back and forth from native and web. 18 | 19 | ### Setting the Initial Context 20 | 21 | The easiest way to set the Portal's auth tokens is to set the initial context of the portal. The initial context will allow you to pass data that can be read almost immediately in the Portal. 22 | 23 | ```javascript 24 | const userPage = { 25 | name: 'user_page', 26 | startDir: 'web', 27 | initialContext: { 28 | route: '/user', 29 | auth: /* Auth Data */ 30 | } 31 | }; 32 | ``` 33 | 34 | Then, in the entry point to your web application, you can use `getInitialContext()` to read the data passed in and act on it. 35 | 36 | ```typescript title=main.ts 37 | import { getInitialContext } from "@ionic/portals"; 38 | 39 | type MyPortalContext = { route: string; auth: any }; 40 | const auth = getInitialContext()?.value?.auth; 41 | // rest of the web app... 42 | ``` 43 | 44 | ### Using a Custom Plugin 45 | 46 | Another solution you can do is to create a custom Plugin to handle passing data to and from native and web. This is the solution we used in [the example E-commerce Application](../examples/ecommerce.md) since it scales nicely and allows more than just authentication data be passed to and from the layers. 47 | 48 | For information on how to build your own Portal APIs, [see our how-to guide](../how-to/define-api-in-typescript.md). 49 | 50 | ## How to Pass Data From Web to Native 51 | 52 | In some cases, login information changes in the web layer and you want to save the new auth credentials in the native layer. There are several ways of doing that similar to the previous methods. 53 | 54 | ### Using the Built-in Portals Plugin Pub/Sub Functions 55 | 56 | One of the functions of the built-in portals module is to publish/subscribe to events. In this example, you could create a `login` topic and call `publish` as shown below. 57 | 58 | ```typescript {9} 59 | import { publish } from "@ionic/portals"; 60 | 61 | const login = () => { 62 | // Login code... 63 | 64 | const topic = "login"; 65 | const newTokens = 66 | /* Values from Login */ 67 | 68 | publish(topic, newTokens); 69 | }; 70 | 71 | login(); 72 | ``` 73 | 74 | To subscribe to the topic, call `PortalsPlugin.subcribe()` after loading the Portal. 75 | 76 | ```javascript 77 | import { subscribe } from "@ionic/portals-react-native"; 78 | 79 | const subscriptionRef = subscribe("login", (message) => { 80 | const auth = message.data; 81 | // Rest of the React Native app... 82 | }); 83 | ``` 84 | 85 | For more information on how to communicate with a Portal web application, [see our how to guide](../how-to/using-the-portals-plugin.md). 86 | 87 | ### Using a Custom Plugin 88 | 89 | Similar to the previous section, by creating a custom plugin, you have complete control over how to communicate between web and native layers. It is possible to create a function such as `MyPlugin.syncAuthAcrossWebAndNative()` to handle managing auth tokens across web and native. 90 | 91 | For information on how to build your own Portal APIs, [see our how-to guide](../how-to/define-api-in-typescript.md). 92 | -------------------------------------------------------------------------------- /website/docs/for-react-native/tutorials/monorepo-example.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Copying Web Assets to Native Projects in a Monorepo 3 | sidebar_label: Copying Web Assets to Native Projects in a Monorepo 4 | --- 5 | 6 | With Ionic Portals, you need to get the built web assets for each of the portals into your native applications. There could be many ways to go about doing so, which will highly depend on your project. This quick tutorial will show how you can accomplish this when working on a monorepo project in which your web apps are in the same repository as your native apps. 7 | 8 | ## Project Structure 9 | 10 | We will set up some NPM scripts in a moment to do the copy tasks. First, though, you need to know where to copy the assets to each native project. For Android, the web assets will go into a folder under `app/src/main/assets`, and for iOS, the folder will go directly into the folder named after your app. Below is a sample monorepo setup with three projects in the root directory (android, ios, and web), and in each native project, we will copy the web assets into a folder named `web app`. The scripts below match this structure. You will need to modify the scripts accordingly based on your project. 11 | 12 | 13 | ``` 14 | project root/ 15 | ├─ android/ 16 | ├─ .gradle 17 | ├─ app/ 18 | ├─ src/ 19 | ├─ main/ 20 | ├─ assets/ 21 | ├─ webapp/ 22 | ├─ build.gradle 23 | ├─ [etc...] 24 | ├─ ios/ 25 | ├─ Pods/ 26 | ├─ your native app/ 27 | ├─ webapp/ 28 | ├─ Podfile 29 | ├─ [etc...] 30 | ├─ web/ 31 | ├─ build/ 32 | ├─ src/ 33 | ├─ package.json 34 | ├─ [etc...] 35 | ``` 36 | 37 | :::note 38 | For iOS, the `webapp` folder will also need to be added to the XCode project to package the web assets with the app. To do so, drag the `webapp` folder from Finder and drop it to the same folder in the Project Navigator while in XCode. 39 | ::: 40 | 41 | ## Using NPM Scripts to Copy Assets 42 | 43 | Now that we know where to copy the built web assets, we will use NPM scripts to run after the build step. 44 | 45 | Here, we will set up a `postbuild` script that will run after the `npm run build` task finishes, which will in turn call `copyto:android` and `copyto:ios`: 46 | 47 | ```json title="package.json scripts" 48 | "postbuild": "npm run copyto:android && npm run copyto:ios", 49 | "copyto:android": "rm -rf ../android/app/src/main/assets/webapp && cp -R build/ ../android/app/src/main/assets/webapp", 50 | "copyto:ios": "rm -rf '../ios/portal test app/webapp' && cp -R build/ .'./ios/portal test app/webapp'" 51 | ``` 52 | 53 | The `copyto:xxx` scripts will first remove the current folder and then copy the web assets from the build folder in the web project to the directories in which they go in the native apps. 54 | 55 | :::note 56 | We use the Unix command `rm` in the scripts to remove the directories, which might not work on other platforms. If you need a cross-platform solution, look into the [rimraf](https://www.npmjs.com/package/rimraf) NPM package. 57 | ::: 58 | 59 | The dev workflow for this process is: 60 | 61 | 1. Make changes to the web app 62 | 2. Run the `npm run build` task in the web folder 63 | 3. Build the native apps 64 | -------------------------------------------------------------------------------- /website/docs/for-web/changelog.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Changelog 3 | hide_table_of_contents: true 4 | --- 5 | 6 | import ReleaseNotes from '@site/src/components/page/changelog'; 7 | import releases from "./changelog.json"; 8 | 9 | 10 | -------------------------------------------------------------------------------- /website/docs/for-web/overview.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Overview 3 | sidebar_label: Overview 4 | --- 5 | 6 | # Overview 7 | 8 | Although most of the API surface area of portals is Native, it is important to ensure that web developers understand what APIs are available to them and what considerations need to be taken when building web experiences for use in Portals. 9 | 10 | ## Portals Plugin 11 | 12 | The most commonly used Portals API for web applications will be the Portals Plugin that is provided out of the box. This plugin is used for simple communication between the web application and the native application through the Portal. This includes InitialContext and PubSub. 13 | 14 | - [Portals Web Plugin](./portals-plugin.md) 15 | 16 | ## Performance 17 | 18 | One of the most important considerations is understanding how a web application should be built to allow for it to be used within a Portal. Although most web applications are hosted by servers it is different for Portals. The web application files are local to device. This empowers Portals to remove the network latency from the rendering experience and reduce the overall time to load. 19 | 20 | This also means that the measurement values that you might use for web page rendering don't apply in the same way for Portals applications. A first contentful paint (FCP) of 1.8 seconds or less on a web page might be considered good, but when transitioning between a native page and a Portal 1.8 seconds could feel like a very long time. 21 | 22 | With this in mind we have provided a few tools to help developers identify the time it takes to load content and with how to profile web experiences within a Portal. 23 | 24 | - [Web Vitals](./web-vitals.md) 25 | - [Profiling for iOS](./ios-profiling.md) 26 | - [Profiling for Android](./android-profiling.md) 27 | -------------------------------------------------------------------------------- /website/docs/for-web/sharing-assets.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Sharing Assets Between Portals 3 | sidebar_label: Sharing Assets 4 | --- 5 | 6 | # Overview 7 | 8 | If you are devloping an application that contains multiple Portals, the Portals library supports the ability to share asset files between them. This is helpful to reduce the size of your overall app if those Portals use assets that are the same such as large media files or font files. 9 | 10 | ## Register Shared Assets 11 | 12 | Shared Assets should be placed inside both native platforms in an accessible location and registered in the Portals Library. This provides the Web content with a mechanism to get a path to the shared assets within the native platform bundle. 13 | 14 | Refer to the applicable documentation for [iOS](../for-ios/how-to/sharing-assets.md), [Android](../for-android/how-to/sharing-assets.md), or [React Native](../for-react-native/how-to/sharing-assets.md) to learn how to register shared assets on each native platform. 15 | 16 | ## Using Registered Assets 17 | 18 | When shared assets are registered in the native platforms the path will be available from the [InitialContext](./portals-plugin.md#initialcontext) object from the [Portal Plugin](./portals-plugin.md). The names of asset maps registered in the native platforms will be available in this object as keys, and the values will be the path to use. 19 | 20 | :::tip 21 | When developing web content that is designed to use shared assets, check for the presence of the initialcontext object and default to a working path to view a working copy of the image during development that won't be included in the individual app assets. 22 | ::: 23 | 24 | ```typescript 25 | const path_assets = getInitialContext()?.assets?.images || 'src/assets'; 26 | const img_logo = path_assets + '/logo.jpg'; 27 | ``` 28 | -------------------------------------------------------------------------------- /website/docs/for-web/web-vitals.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Web Vitals 3 | sidebar_label: Web Vitals 4 | --- 5 | 6 | ## Android 7 | 8 | Portals for Android supports three [Web Vitals](https://web.dev/vitals/) metrics you can use to measure the performance of your web app. These are the First Contentful Paint (FCP), Time to First Byte (TTFB), and First Input Delay (FID). 9 | 10 | To measure these metrics of a Portal, create an instance of `WebVitals` and add it to the portal: 11 | 12 | ```java 13 | 14 | WebVitals webVitalsPlugin = new WebVitals((portalName, metric, duration) -> { 15 | Log.d("VITALS", portalName + " Portal = " + metric.name() + ": " + speed + "ms"); 16 | return null; 17 | }) 18 | 19 | Portal portal = new PortalBuilder("profile") 20 | .addPluginInstance(webVitalsPlugin) 21 | ``` 22 | 23 | ## iOS 24 | 25 | On iOS, the only [Web Vitals](https://web.dev/vitals/) metric that is available to measure is First Contentful Paint (FCP). To measure the FCP of 26 | a Portal, add the WebVitals plugin to the Portal. 27 | 28 | ```swift 29 | let webVitals = WebVitalsPlugin { portalName, duration in 30 | print("\(portalName) portal = FCP: \(duration) ms") 31 | } 32 | 33 | let portal = Portal( 34 | name: "profile", 35 | plugins: [.instance(webVitals)] 36 | ) 37 | ``` 38 | 39 | ## React Native 40 | 41 | Portals for React Native provides functions to register callbacks for First Contentful Paint (FCP), Time to First Byte (TTFB), and First Input Delay (FID). When running on iOS, TTFB and FID will never be called due to limitations of the platform. 42 | 43 | ```typescript 44 | import { onFirstContentfulPaint, onFirstInputDelay, onTimeToFirstByte, registerWebVitals } from '@ionic/portals-react-native' 45 | 46 | // You can register each individually by providing the Portal name and a callback. 47 | onFirstContentfulPaint('profile', duration => console.log('profile FCP:', duration)); 48 | onFirstInputDelay('profile', duration => console.log('profile FID:', duration)); 49 | onTimeToFirstByte('profile', duration => console.log('profile TTFB:', duration)); 50 | 51 | // Or register all in a single call by providing the portal name and callbacks for each of the metrics 52 | registerWebVitals( 53 | 'profile', 54 | duration => console.log('profile FCP:', duration), 55 | duration => console.log('profile FID:', duration), 56 | duration => console.log('profile TTFB:', duration) 57 | ); 58 | ``` 59 | -------------------------------------------------------------------------------- /website/docs/getting-started.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Getting Started Guide 3 | sidebar_label: Getting Started Guide 4 | --- 5 | 6 | import useBaseUrl from '@docusaurus/useBaseUrl'; 7 | import { getCapacitorVersion, getPortalsVersion, getPortalsVersionIos, getPortalsVersionAndroid, getPortalsVersionRN, getiOSMinVersion, getAndroidMinSdk, getRnMinVersion } from '@site/src/util'; 8 | 9 | ## Using your Product Key 10 | 11 | To use Ionic Portals, you need to have a product key. If you don't have a product key please [contact sales](https://ionic.io/demo?source=portals) to run a pilot. 12 | 13 | Once you have been provided access to a key head over to the [Ionic Dashboard](https://dashboard.ionicframework.com/portals). 14 | 15 | Clicking the copy button will copy the entire key to your clipboard: 16 | 17 | 21 | 22 | 23 | :::tip 24 | Later in these docs this key will be referred to as `YOUR_PORTALS_KEY`. 25 | ::: 26 | 27 | From here, continue on to configuring the key in your Portals application. 28 | 29 | - [Getting Started Guide iOS](./for-ios/quick-start.md) 30 | - [Getting Started Guide Android](./for-android/guide.md) 31 | - [Getting Started Guide React Native](./for-react-native/guide.md) 32 | - [Federated Capacitor](./for-capacitor/overview.md) 33 | 34 | :::note 35 | You only need to register for a product key once for each organization you belong to. You can return to the Portals Key section of the Ionic Dashboard to retrieve your key at any time. 36 | ::: 37 | 38 | ## Supported Platform Versions 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 |
PlatformLatest Portals VersionMinimum Supported Platform Version
iOS{getPortalsVersionIos()}iOS {getiOSMinVersion()}
Android{getPortalsVersionAndroid()}Android SDK {getAndroidMinSdk()}
React Native{getPortalsVersionRN()}React Native {getRnMinVersion()}
62 | -------------------------------------------------------------------------------- /website/docs/overview.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Ionic Portals 3 | sidebar_label: Overview 4 | slug: / 5 | --- 6 | 7 | 8 | 9 | 10 | 11 | Ionic Portals is a supercharged native WebView component for iOS and Android that lets you add web-based experiences to native mobile apps. It enables native and web teams to better collaborate and bring new and existing web experiences to mobile in a safe, controlled way. 12 | 13 |
19 | 35 |
36 | 37 | ## Why Ionic Portals? 38 | 39 | The vast majority of apps in the app stores need to integrate web assets for specific screens and experiences. It could be for authentication forms, features such as mortgage applications, or to bring web experiences to mobile without needing to port them to native. This is the case even for teams that are fully invested in a traditional native app development stack. 40 | 41 | Despite how common this need is, bringing these web experiences to native mobile apps and code bases is anything but straightforward. Teams struggle to integrate web apps with native functionality and do it in a safe, controlled way that doesn’t disrupt the roadmap of the traditional native development teams, so they are stuck reinventing the wheel by extending stock Web View controls such as WKWebView. 42 | 43 | Enter Ionic Portals, a supercharged native Web View component for iOS and Android. With Portals, you can: 44 | 45 | - **Extend native apps with web content.** Portals enables users to add web-based features and experiences to an existing native mobile app. 46 | - **Access native features via the Capacitor bridge.** Safely and securely access features like camera, geolocation, haptics, and more — all from the WebView, to deliver truly native mobile experiences using the web. 47 | - **Safely control access to native features and data.** Native development teams get granular control over which parts of the app - including specific features and data - web teams can access when collaborating on a mobile project. 48 | - **Keep pace with platform changes and version updates.** Ionic’s solution is fully managed and updated over time. Focus on what matters most - your app. 49 | - **Get access to enterprise-grade support and services.** Keep your project on track by working with our mobile experts to resolve any issues occurring during development or while the app is in production. 50 | 51 | ## How It Works 52 | 53 | Ionic Portals is built on the industry leading [Capacitor](https://capacitorjs.com) Web Native app runtime. There's just a handful of steps required to embed web experiences into your native apps: 54 | 55 | - Drop the Portals iOS and Android libraries into your app. 56 | 57 | - Choose the web experiences you want to integrate with. Open a Portal within an [iOS](./for-ios/getting-started.md),[Android](./for-android/getting-started.md), or [React Native](./for-react-native/getting-started.md) native app to embed/expose web app. 58 | 59 | - Set granular permissions that designate which parts of the native app your web teams can touch. 60 | 61 | - Choose from pre-built native plugins to unlock the full power of the native mobile device in your web-based experiences. 62 | 63 | - Build and ship! 🚀 64 | 65 | ## Reference Apps 66 | 67 | While implementing Ionic Portals, refer to various reference apps including an [E-commerce demo application](./for-ios/examples/ecommerce-app.md) for Android and iOS that uses both native layouts and web-based portals. The Android application uses Fragments while the iOS application uses Storyboard/ViewController based views. 68 | -------------------------------------------------------------------------------- /website/docs/what-are-live-updates.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: What are Live Updates? 3 | sidebar_label: What are Live Updates? 4 | --- 5 | 6 | Ionic Appflow's Live Update feature makes it easy to deploy app updates in real time without going through a traditional app store submission process for the vast majority of business logic, UI, and style changes. 7 | 8 | ## Portal Live Updates 9 | 10 | With Ionic Portals you are able to use Appflow Live Update for each of your Portals. This allows for Portals to update independent of each other. 11 | 12 | Portal Live Updates allow teams to work independently on their own release cycles. Typical collaboration in native apps requires all teams involved to deploy updates on the same native release schedule. 13 | 14 | With Live Updates each Portal can be updated independent of the rest of the code in the application. This allows teams to iterate quickly and fix critical issues by pushing updates directly to your users, when you're ready, for immediate impact. 15 | 16 | Because Portals are web applications built from JavaScript, HTML, CSS, images, and other web files changes to this code can be sent directly to users and testers without submitting a new native update. This process is fully compliant with Apple and Android requirements. 17 | -------------------------------------------------------------------------------- /website/docs/what-is-a-portal.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: What Is A Portal? 3 | sidebar_label: What Is A Portal? 4 | --- 5 | 6 | A "Portal" is a mobile view for iOS and Android projects capable of displaying and running a web based application; similar to the system WebView. It uses [Capacitor](https://capacitorjs.com) as a bridge between the native code and the web code to allow for cross-communication between the two layers. Because Ionic Portals uses Capacitor under the hood you are able to use any existing [Capacitor Plugins](https://capacitorjs.com/docs/plugins). 7 | 8 | ## How Does It Work? 9 | 10 | Ionic Portals uses [Capacitor](https://capacitorjs.com) as a bridge between the native code and the web code. This means that there is a production ready, battle tested solution for connecting these two codebases. The Ionic Portals library then provides tooling and functionality around Capacitor to easily embed Capacitor instances, known as "Portals," into your application. Each web application is sandboxed and has a separate connection to the native app. The native code will remain as the "source of truth" for your data as well as allowing your project to continue to use native layouts/views first and foremost. 11 | 12 | ## When Should I Use a Portal? 13 | 14 | If your company has a complex web experience that they'd like to embed into an existing native application, then Ionic Portals is probably an excellent fit. Portals are, at their best, web experiences that should remain consistent on both Android and iOS applications. Ionic Portals allows you to quickly and safely embed those experiences in an Android or iOS application without massively impacting native developers workflows while also allowing web developers to test their code without needing a mobile device. This asynchronous workflow allows neither team to block each other's work until the time comes to glue it all together with Portals. 15 | 16 | ## What Is Ionic Portals Not? 17 | 18 | While Ionic Portals may seem like a silver bullet to completely replace the existing system WebView, that isn't the case. 19 | 20 | #### Ionic Portals Is Not Drop-in Replacement for the System WebView 21 | 22 | While we do use the system WebView in Ionic Portals, it is not a drop-in replacement. Think of it as a WebView with a more robust API for communication between native and web layers. Ionic Portals provides access to the WebView itself, but handles all of the heavy lifting for you. 23 | 24 | #### Ionic Portals Is Not a Different Browser Engine 25 | 26 | Ionic Portals leverages the existing browser engine on iOS and Android devices. On Android, Ionic Portals uses Chromium and on iOS, it uses Mobile Safari. This means developers don't need to break their existing workflow in order to test/run Portals before deploying them to a mobile device. 27 | 28 | #### Ionic Portals Is Not Framework/Toolkit for Building Entire Applications Using Web Code Only 29 | 30 | Ionic Portals is used to embed web experiences in native codebases on Android and iOS. If you want to use HTML, CSS, and Javascript for an entire mobile application, try out [Capacitor](https://capacitorjs.com/docs/getting-started). 31 | -------------------------------------------------------------------------------- /website/docusaurus.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('@docusaurus/types').DocusaurusConfig} */ 2 | 3 | const lernaConfig = require('../lerna.json'); 4 | 5 | const { themes } = require('prism-react-renderer') 6 | 7 | module.exports = { 8 | title: 'Ionic Portals', 9 | tagline: 'Portals tagline', 10 | url: 'https://ionic.io', 11 | trailingSlash: false, 12 | baseUrl: '/docs/portals/', 13 | baseUrlIssueBanner: false, 14 | onBrokenLinks: 'throw', 15 | onBrokenMarkdownLinks: 'throw', 16 | onBrokenAnchors: 'throw', 17 | favicon: 'img/logo.svg', 18 | organizationName: 'ionic-team', 19 | projectName: 'portals-docs', 20 | titleDelimiter: '-', 21 | themeConfig: { 22 | logo: { 23 | alt: 'Portals Logo', 24 | src: 'img/logo-light.png', 25 | srcDark: 'img/logo-dark.png', 26 | href: '/docs/portals', 27 | height: 24, 28 | width: 84, 29 | }, 30 | sidebar: { 31 | productDropdown: { 32 | title: 'Portals Docs', 33 | logo: { 34 | width: 20, 35 | height: 20, 36 | alt: 'Portals Logo', 37 | src: 'img/components/product-dropdown/logo.png', 38 | }, 39 | textLinks: [ 40 | { 41 | url: { 42 | href: 'https://forum.ionicframework.com/c/portals/31', 43 | target: '_blank', 44 | rel: 'noopener nofollow', 45 | }, 46 | label: 'Forum', 47 | }, 48 | ], 49 | iconLinks: [ 50 | { 51 | key: 'github', 52 | url: { 53 | href: 'https://github.com/ionic-team/ionic-portals', 54 | target: '_blank', 55 | rel: 'noopener nofollow', 56 | }, 57 | }, 58 | ], 59 | }, 60 | backButton: { 61 | url: { 62 | href: '/docs', 63 | }, 64 | }, 65 | }, 66 | colorMode: { 67 | respectPrefersColorScheme: true, 68 | }, 69 | prism: { 70 | theme: themes.github, 71 | darkTheme: themes.dracula, 72 | additionalLanguages: ['shell-session', 'kotlin', 'groovy', 'java', 'swift', 'ruby', 'json', 'bash'], 73 | }, 74 | zoom: { 75 | selector: '.markdown em > img', 76 | background: { 77 | light: 'var(--token-background-color)', 78 | dark: 'var(--token-background-color)', 79 | }, 80 | config: { 81 | margin: 75, 82 | scrollOffset: 20, 83 | }, 84 | }, 85 | }, 86 | plugins: ['docusaurus-plugin-image-zoom'], 87 | presets: [ 88 | [ 89 | '@ionic-docs/preset-classic', 90 | { 91 | docs: { 92 | routeBasePath: '/', 93 | sidebarPath: require.resolve('./sidebars.js'), 94 | breadcrumbs: false, 95 | }, 96 | pages: false, 97 | theme: { 98 | customCss: [require.resolve('./src/styles/custom.css')], 99 | }, 100 | googleTagManager: { 101 | containerId: 'GTM-TKMGCBC', 102 | }, 103 | }, 104 | ], 105 | ], 106 | customFields: { 107 | portalsVersion: lernaConfig.version, 108 | portalsVersionIos: lernaConfig.iosVersion, 109 | portalsVersionAndroid: lernaConfig.androidVersion, 110 | portalsVersionRN: lernaConfig.rnVersion, 111 | capacitorVersion: lernaConfig.capacitorVersion, 112 | iosMinVersion: lernaConfig.iosMinVersion, 113 | androidMinSdk: lernaConfig.androidMinSdk, 114 | rnMinVersion: lernaConfig.rnMinVersion, 115 | androidLiveUpdatesVersion: lernaConfig.androidLiveUpdatesVersion, 116 | }, 117 | }; 118 | -------------------------------------------------------------------------------- /website/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "portals-docs-website", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "docusaurus": "docusaurus", 7 | "start": "docusaurus start", 8 | "build": "docusaurus build", 9 | "swizzle": "docusaurus swizzle", 10 | "deploy": "docusaurus deploy", 11 | "clear": "docusaurus clear", 12 | "serve": "docusaurus serve", 13 | "write-translations": "docusaurus write-translations", 14 | "write-heading-ids": "docusaurus write-heading-ids" 15 | }, 16 | "dependencies": { 17 | "@docusaurus/core": "^3.4.0", 18 | "@ionic-docs/preset-classic": "^1.2.7", 19 | "clsx": "^2.0.0", 20 | "docusaurus-plugin-image-zoom": "^0.1.1", 21 | "prism-react-renderer": "^2.3.0", 22 | "react": "^18.2.0", 23 | "react-dom": "^18.2.0" 24 | }, 25 | "devDependencies": { 26 | "@ionic/prettier-config": "^4.0.0", 27 | "prettier": "^3.1.1" 28 | }, 29 | "prettier": "@ionic/prettier-config", 30 | "browserslist": { 31 | "production": [ 32 | ">0.5%", 33 | "not dead", 34 | "not op_mini all" 35 | ], 36 | "development": [ 37 | "last 1 chrome version", 38 | "last 1 firefox version", 39 | "last 1 safari version" 40 | ] 41 | }, 42 | "engines": { 43 | "node": ">=18.0" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /website/src/components/WistiaVideo/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react'; 2 | 3 | interface Props { 4 | videoId: string; 5 | } 6 | 7 | function WistiaVideo(props: Props): JSX.Element { 8 | useEffect(() => { 9 | const wistiaScript = document.createElement('script'); 10 | wistiaScript.id = 'wistia_script'; 11 | wistiaScript.type = 'text/javascript'; 12 | wistiaScript.src = 'https://fast.wistia.com/assets/external/E-v1.js'; 13 | wistiaScript.async = true; 14 | document.body.appendChild(wistiaScript); 15 | 16 | return () => { 17 | document.body.removeChild(wistiaScript); 18 | }; 19 | }, []); 20 | 21 | return ( 22 |
23 |   24 |
25 | ); 26 | } 27 | 28 | export default WistiaVideo; 29 | -------------------------------------------------------------------------------- /website/src/components/page/changelog/index.tsx: -------------------------------------------------------------------------------- 1 | import clsx from "clsx"; 2 | import React from "react"; 3 | 4 | import styles from "./styles.module.css"; 5 | 6 | interface Release { 7 | body: string; 8 | name: string; 9 | published_at: string; 10 | tag_name: string; 11 | type: string; 12 | version: string; 13 | } 14 | 15 | export default function ReleaseNotes(props: { 16 | repo: string; 17 | name: string; 18 | releases: any[]; 19 | }) { 20 | if (props.releases.length === 0) { 21 | console.warn(`Could not load release notes data. Make sure that you have a valid GITHUB_TOKEN. 22 | 23 | Create a personal access token by following the below guide: 24 | https://docs.github.com/en/enterprise-cloud@latest/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token 25 | 26 | and then authorize it to work with SSO: 27 | https://docs.github.com/en/enterprise-cloud@latest/authentication/authenticating-with-saml-single-sign-on/authorizing-a-personal-access-token-for-use-with-saml-single-sign-on`); 28 | 29 | return [ 30 |

31 | Unable to load Releases. Please see all releases{" "} 32 | 33 | on GitHub 34 | 35 | . 36 |

, 37 | ]; 38 | } 39 | 40 | return ( 41 |
42 |

43 | A complete release history for {props.name} is available{" "} 44 | 45 | on GitHub 46 | 47 | . Documentation for recent releases can also be found below. 48 |

49 | 50 |
51 | {props.releases.map((release: Release, index) => ( 52 |
58 |
59 |
60 | 64 |

65 | 66 | {release.version} 67 | 68 |

69 |
70 | {release.type} 71 | {index === 0 ? ( 72 | 78 | Latest Production Version 79 | 80 | ) : null} 81 |
82 |
83 |

{release.published_at}

84 |
85 |
90 |
91 |
92 | ))} 93 |
94 |
95 | To see more releases, visit{" "} 96 | 97 | GitHub 98 | 99 | . 100 |
101 |
102 | ); 103 | } 104 | -------------------------------------------------------------------------------- /website/src/components/page/changelog/styles.module.css: -------------------------------------------------------------------------------- 1 | .release-notes { 2 | margin-block-start: 4rem; 3 | 4 | --accent-color: #3880ff; 5 | --accent-color-rgb: 56, 128, 255; 6 | --accent-color-100: #4d8dff; 7 | --accent-color-200: #639cff; 8 | --accent-color-300: #4d8dff; 9 | --accent-color-400: #7cabff; 10 | --accent-color-500: #97bdff; 11 | --accent-color-600: #b2ceff; 12 | --accent-color-700: #cddfff; 13 | --accent-color-800: #e3edff; 14 | --accent-color-900: #f0f6ff; 15 | --background: var(--token-primary-bg-c); 16 | --color: var(--ifm-font-color-base); 17 | --code-font-family: "SFMono-Regular", "Roboto Mono", Consolas, 18 | "Liberation Mono", Menlo, Courier, monospace; 19 | } 20 | 21 | .intro { 22 | margin-bottom: 2.2em; 23 | } 24 | 25 | .release-note { 26 | position: relative; 27 | 28 | display: flex; 29 | 30 | padding-bottom: 80px; 31 | } 32 | 33 | /* Turn off the # on anchor hover */ 34 | .release-notes h2 a[href^="#"]:hover:before, 35 | .release-notes h3 a[href^="#"]:hover:before, 36 | .release-notes h4 a[href^="#"]:hover:before, 37 | .release-notes h5 a[href^="#"]:hover:before, 38 | .release-notes h6 a[href^="#"]:hover:before { 39 | opacity: 0; 40 | } 41 | 42 | .release-notes h2 { 43 | margin: 0; 44 | /* These lines exist for correct positioning of anchor links */ 45 | padding-top: 55px; 46 | margin-top: -55px; 47 | 48 | font-size: 32px; 49 | } 50 | 51 | .release-notes h3 { 52 | margin-top: 32px; 53 | margin-bottom: 0; 54 | 55 | font-size: 20px; 56 | } 57 | 58 | .release-notes h3 { 59 | padding-top: 0; 60 | } 61 | 62 | .release-notes ul { 63 | margin-top: 0.8em; 64 | } 65 | 66 | .release-notes ul li { 67 | line-height: 1.5; 68 | margin-bottom: 8px; 69 | } 70 | 71 | /* 72 | * Release Note Header 73 | * -------------------------------------------------------- 74 | */ 75 | 76 | .release-header { 77 | display: flex; 78 | align-items: center; 79 | flex-wrap: wrap; 80 | } 81 | 82 | .release-header a.header-link { 83 | color: var(--ifm-font-color-base); 84 | } 85 | 86 | .release-header h2 { 87 | font-family: var(--font-family); 88 | 89 | margin-right: 8px; 90 | } 91 | 92 | .release-header .release-version { 93 | font-weight: 700; 94 | } 95 | 96 | .release-published h3 { 97 | font-family: var(--font-family); 98 | 99 | font-weight: 300; 100 | 101 | margin-top: 4px; 102 | margin-bottom: 32px; 103 | color: var(--ifm-secondary-heading-color, #4e5b6a); 104 | } 105 | 106 | .release-badge { 107 | margin-right: 8px; 108 | margin-top: 5px; 109 | margin-bottom: 4px; 110 | 111 | text-transform: uppercase; 112 | 113 | background: transparent; 114 | color: #b2becd; 115 | border: 1px solid #b2becd; 116 | border-radius: 14px; 117 | height: 24px; 118 | padding: 2px 8px; 119 | 120 | font-weight: 600; 121 | font-size: 10px; 122 | line-height: 14px; 123 | display: flex; 124 | align-items: center; 125 | letter-spacing: 0.08em; 126 | } 127 | 128 | .release-note-major .release-badge { 129 | background: var(--color, #04060b); 130 | border-color: var(--color, #04060b); 131 | color: var(--background, #fff); 132 | } 133 | 134 | .release-note-minor .release-badge { 135 | background: #b2becd; 136 | border-color: #b2becd; 137 | color: var(--background, #fff); 138 | } 139 | 140 | .release-badge.release-badge-latest { 141 | background: transparent; 142 | color: #43c465; 143 | border-color: #43c465; 144 | } 145 | 146 | /* 147 | * Release Note Blockquote 148 | * -------------------------------------------------------- 149 | */ 150 | 151 | .release-notes blockquote { 152 | margin-left: 0; 153 | 154 | background: rgba(var(--accent-color-rgb), 0.05); 155 | border-color: var(--accent-color); 156 | } 157 | 158 | .release-notes a { 159 | color: var(--accent-color); 160 | } 161 | -------------------------------------------------------------------------------- /website/src/styles/custom.css: -------------------------------------------------------------------------------- 1 | .bottom-item { 2 | border-bottom: 1px solid #e9edf3; 3 | padding-bottom: 1rem; 4 | margin-bottom: 1rem; 5 | } 6 | -------------------------------------------------------------------------------- /website/src/util/index.ts: -------------------------------------------------------------------------------- 1 | //@ts-ignore 2 | import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; 3 | 4 | export const getCapacitorVersion = () => { 5 | const { siteConfig } = useDocusaurusContext(); 6 | return siteConfig.customFields.capacitorVersion; 7 | } 8 | 9 | export const getPortalsVersion = () => { 10 | const { siteConfig } = useDocusaurusContext(); 11 | return siteConfig.customFields.portalsVersion; 12 | } 13 | 14 | export const getPortalsVersionIos = () => { 15 | const { siteConfig } = useDocusaurusContext(); 16 | return siteConfig.customFields.portalsVersionIos; 17 | } 18 | 19 | export const getPortalsVersionAndroid = () => { 20 | const { siteConfig } = useDocusaurusContext(); 21 | return siteConfig.customFields.portalsVersionAndroid; 22 | } 23 | 24 | export const getPortalsVersionRN = () => { 25 | const { siteConfig } = useDocusaurusContext(); 26 | return siteConfig.customFields.portalsVersionRN; 27 | } 28 | 29 | export const getiOSMinVersion = () => { 30 | const { siteConfig } = useDocusaurusContext(); 31 | return siteConfig.customFields.iosMinVersion; 32 | } 33 | 34 | export const getAndroidMinSdk = () => { 35 | const { siteConfig } = useDocusaurusContext(); 36 | return siteConfig.customFields.androidMinSdk; 37 | } 38 | 39 | export const getRnMinVersion = () => { 40 | const { siteConfig } = useDocusaurusContext(); 41 | return siteConfig.customFields.rnMinVersion; 42 | } 43 | 44 | export const getLiveUpdatesAndroidVersion = () => { 45 | const { siteConfig } = useDocusaurusContext(); 46 | return siteConfig.customFields.androidLiveUpdatesVersion; 47 | } -------------------------------------------------------------------------------- /website/static/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/.nojekyll -------------------------------------------------------------------------------- /website/static/img/android-run-script-thumbnail.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/android-run-script-thumbnail.webp -------------------------------------------------------------------------------- /website/static/img/android-run-script.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/android-run-script.webp -------------------------------------------------------------------------------- /website/static/img/builds-screen-thumbnail.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/builds-screen-thumbnail.webp -------------------------------------------------------------------------------- /website/static/img/builds-screen.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/builds-screen.webp -------------------------------------------------------------------------------- /website/static/img/components/product-dropdown/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/components/product-dropdown/logo.png -------------------------------------------------------------------------------- /website/static/img/create-a-new-build-selected-thumbnail.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/create-a-new-build-selected-thumbnail.webp -------------------------------------------------------------------------------- /website/static/img/create-a-new-build-selected.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/create-a-new-build-selected.webp -------------------------------------------------------------------------------- /website/static/img/create-a-new-build-thumbnail.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/create-a-new-build-thumbnail.webp -------------------------------------------------------------------------------- /website/static/img/create-a-new-build.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/create-a-new-build.webp -------------------------------------------------------------------------------- /website/static/img/deployments-screen-thumbnail.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/deployments-screen-thumbnail.webp -------------------------------------------------------------------------------- /website/static/img/deployments-screen.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/deployments-screen.webp -------------------------------------------------------------------------------- /website/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/favicon.ico -------------------------------------------------------------------------------- /website/static/img/federated-capacitor-diagram.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/federated-capacitor-diagram.webp -------------------------------------------------------------------------------- /website/static/img/how-to/ios-create-folder-references.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/how-to/ios-create-folder-references.png -------------------------------------------------------------------------------- /website/static/img/how-to/ios-web-asset-folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/how-to/ios-web-asset-folder.png -------------------------------------------------------------------------------- /website/static/img/how-to/obtain-registration-key-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/how-to/obtain-registration-key-1.png -------------------------------------------------------------------------------- /website/static/img/how-to/obtain-registration-key-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/how-to/obtain-registration-key-2.png -------------------------------------------------------------------------------- /website/static/img/import-existing-app-thumbnail.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/import-existing-app-thumbnail.webp -------------------------------------------------------------------------------- /website/static/img/import-existing-app.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/import-existing-app.webp -------------------------------------------------------------------------------- /website/static/img/known-issues/spm-workarounds/01-scheme-edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/known-issues/spm-workarounds/01-scheme-edit.png -------------------------------------------------------------------------------- /website/static/img/known-issues/spm-workarounds/02-post-actions-select.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/known-issues/spm-workarounds/02-post-actions-select.png -------------------------------------------------------------------------------- /website/static/img/known-issues/spm-workarounds/03-run-script-select.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/known-issues/spm-workarounds/03-run-script-select.png -------------------------------------------------------------------------------- /website/static/img/known-issues/spm-workarounds/04-build-settings-select.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/known-issues/spm-workarounds/04-build-settings-select.png -------------------------------------------------------------------------------- /website/static/img/known-issues/spm-workarounds/05-script-entry.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/known-issues/spm-workarounds/05-script-entry.png -------------------------------------------------------------------------------- /website/static/img/known-issues/spm-workarounds/06-script-entry.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/known-issues/spm-workarounds/06-script-entry.png -------------------------------------------------------------------------------- /website/static/img/logo-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/logo-dark.png -------------------------------------------------------------------------------- /website/static/img/logo-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/logo-light.png -------------------------------------------------------------------------------- /website/static/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/logo.png -------------------------------------------------------------------------------- /website/static/img/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /website/static/img/module-federation-diagram.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/module-federation-diagram.webp -------------------------------------------------------------------------------- /website/static/img/personal-access-token-thumbnail.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/personal-access-token-thumbnail.webp -------------------------------------------------------------------------------- /website/static/img/personal-access-token.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/personal-access-token.webp -------------------------------------------------------------------------------- /website/static/img/portals-diagram-reactnative.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/portals-diagram-reactnative.png -------------------------------------------------------------------------------- /website/static/img/portals-diagram-swift.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/portals-diagram-swift.png -------------------------------------------------------------------------------- /website/static/img/portals-key-screenshot-thumbnail.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/portals-key-screenshot-thumbnail.webp -------------------------------------------------------------------------------- /website/static/img/portals-key-screenshot.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/portals-key-screenshot.webp -------------------------------------------------------------------------------- /website/static/img/portals-key-signup-screenshot-thumbnail.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/portals-key-signup-screenshot-thumbnail.webp -------------------------------------------------------------------------------- /website/static/img/portals-key-signup-screenshot.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/portals-key-signup-screenshot.webp -------------------------------------------------------------------------------- /website/static/img/profiling/android/full/profiling-console-01.webP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/android/full/profiling-console-01.webP -------------------------------------------------------------------------------- /website/static/img/profiling/android/full/profiling-console-01.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/android/full/profiling-console-01.webp -------------------------------------------------------------------------------- /website/static/img/profiling/android/full/profiling-console-02.webP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/android/full/profiling-console-02.webP -------------------------------------------------------------------------------- /website/static/img/profiling/android/full/profiling-console-02.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/android/full/profiling-console-02.webp -------------------------------------------------------------------------------- /website/static/img/profiling/android/full/profiling-elements.webP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/android/full/profiling-elements.webP -------------------------------------------------------------------------------- /website/static/img/profiling/android/full/profiling-elements.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/android/full/profiling-elements.webp -------------------------------------------------------------------------------- /website/static/img/profiling/android/full/profiling-inspect-devices.webP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/android/full/profiling-inspect-devices.webP -------------------------------------------------------------------------------- /website/static/img/profiling/android/full/profiling-inspect-devices.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/android/full/profiling-inspect-devices.webp -------------------------------------------------------------------------------- /website/static/img/profiling/android/full/profiling-lighthouse-00.webP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/android/full/profiling-lighthouse-00.webP -------------------------------------------------------------------------------- /website/static/img/profiling/android/full/profiling-lighthouse-00.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/android/full/profiling-lighthouse-00.webp -------------------------------------------------------------------------------- /website/static/img/profiling/android/full/profiling-lighthouse-01.webP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/android/full/profiling-lighthouse-01.webP -------------------------------------------------------------------------------- /website/static/img/profiling/android/full/profiling-lighthouse-01.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/android/full/profiling-lighthouse-01.webp -------------------------------------------------------------------------------- /website/static/img/profiling/android/full/profiling-lighthouse-02.webP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/android/full/profiling-lighthouse-02.webP -------------------------------------------------------------------------------- /website/static/img/profiling/android/full/profiling-lighthouse-02.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/android/full/profiling-lighthouse-02.webp -------------------------------------------------------------------------------- /website/static/img/profiling/android/full/profiling-lighthouse-03.webP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/android/full/profiling-lighthouse-03.webP -------------------------------------------------------------------------------- /website/static/img/profiling/android/full/profiling-lighthouse-03.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/android/full/profiling-lighthouse-03.webp -------------------------------------------------------------------------------- /website/static/img/profiling/android/full/profiling-network-01.webP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/android/full/profiling-network-01.webP -------------------------------------------------------------------------------- /website/static/img/profiling/android/full/profiling-network-01.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/android/full/profiling-network-01.webp -------------------------------------------------------------------------------- /website/static/img/profiling/android/full/profiling-open-01.webP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/android/full/profiling-open-01.webP -------------------------------------------------------------------------------- /website/static/img/profiling/android/full/profiling-open-01.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/android/full/profiling-open-01.webp -------------------------------------------------------------------------------- /website/static/img/profiling/android/full/profiling-open-02.webP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/android/full/profiling-open-02.webP -------------------------------------------------------------------------------- /website/static/img/profiling/android/full/profiling-open-02.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/android/full/profiling-open-02.webp -------------------------------------------------------------------------------- /website/static/img/profiling/android/thumb/profiling-console-01.webP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/android/thumb/profiling-console-01.webP -------------------------------------------------------------------------------- /website/static/img/profiling/android/thumb/profiling-console-01.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/android/thumb/profiling-console-01.webp -------------------------------------------------------------------------------- /website/static/img/profiling/android/thumb/profiling-console-02.webP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/android/thumb/profiling-console-02.webP -------------------------------------------------------------------------------- /website/static/img/profiling/android/thumb/profiling-console-02.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/android/thumb/profiling-console-02.webp -------------------------------------------------------------------------------- /website/static/img/profiling/android/thumb/profiling-elements.webP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/android/thumb/profiling-elements.webP -------------------------------------------------------------------------------- /website/static/img/profiling/android/thumb/profiling-elements.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/android/thumb/profiling-elements.webp -------------------------------------------------------------------------------- /website/static/img/profiling/android/thumb/profiling-inspect-devices.webP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/android/thumb/profiling-inspect-devices.webP -------------------------------------------------------------------------------- /website/static/img/profiling/android/thumb/profiling-inspect-devices.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/android/thumb/profiling-inspect-devices.webp -------------------------------------------------------------------------------- /website/static/img/profiling/android/thumb/profiling-lighthouse-00.webP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/android/thumb/profiling-lighthouse-00.webP -------------------------------------------------------------------------------- /website/static/img/profiling/android/thumb/profiling-lighthouse-00.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/android/thumb/profiling-lighthouse-00.webp -------------------------------------------------------------------------------- /website/static/img/profiling/android/thumb/profiling-lighthouse-01.webP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/android/thumb/profiling-lighthouse-01.webP -------------------------------------------------------------------------------- /website/static/img/profiling/android/thumb/profiling-lighthouse-01.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/android/thumb/profiling-lighthouse-01.webp -------------------------------------------------------------------------------- /website/static/img/profiling/android/thumb/profiling-lighthouse-02.webP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/android/thumb/profiling-lighthouse-02.webP -------------------------------------------------------------------------------- /website/static/img/profiling/android/thumb/profiling-lighthouse-02.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/android/thumb/profiling-lighthouse-02.webp -------------------------------------------------------------------------------- /website/static/img/profiling/android/thumb/profiling-lighthouse-03.webP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/android/thumb/profiling-lighthouse-03.webP -------------------------------------------------------------------------------- /website/static/img/profiling/android/thumb/profiling-lighthouse-03.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/android/thumb/profiling-lighthouse-03.webp -------------------------------------------------------------------------------- /website/static/img/profiling/android/thumb/profiling-network-01.webP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/android/thumb/profiling-network-01.webP -------------------------------------------------------------------------------- /website/static/img/profiling/android/thumb/profiling-network-01.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/android/thumb/profiling-network-01.webp -------------------------------------------------------------------------------- /website/static/img/profiling/android/thumb/profiling-open-01.webP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/android/thumb/profiling-open-01.webP -------------------------------------------------------------------------------- /website/static/img/profiling/android/thumb/profiling-open-01.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/android/thumb/profiling-open-01.webp -------------------------------------------------------------------------------- /website/static/img/profiling/android/thumb/profiling-open-02.webP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/android/thumb/profiling-open-02.webP -------------------------------------------------------------------------------- /website/static/img/profiling/ios/full/profiling-console-01.webP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/ios/full/profiling-console-01.webP -------------------------------------------------------------------------------- /website/static/img/profiling/ios/full/profiling-console-02.webP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/ios/full/profiling-console-02.webP -------------------------------------------------------------------------------- /website/static/img/profiling/ios/full/profiling-export-01.webP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/ios/full/profiling-export-01.webP -------------------------------------------------------------------------------- /website/static/img/profiling/ios/full/profiling-export-02.webP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/ios/full/profiling-export-02.webP -------------------------------------------------------------------------------- /website/static/img/profiling/ios/full/profiling-import-01.webP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/ios/full/profiling-import-01.webP -------------------------------------------------------------------------------- /website/static/img/profiling/ios/full/profiling-import-02.webP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/ios/full/profiling-import-02.webP -------------------------------------------------------------------------------- /website/static/img/profiling/ios/full/profiling-network-01.webP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/ios/full/profiling-network-01.webP -------------------------------------------------------------------------------- /website/static/img/profiling/ios/full/profiling-network-02.webP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/ios/full/profiling-network-02.webP -------------------------------------------------------------------------------- /website/static/img/profiling/ios/full/profiling-network-03.webP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/ios/full/profiling-network-03.webP -------------------------------------------------------------------------------- /website/static/img/profiling/ios/full/profiling-open-01.webP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/ios/full/profiling-open-01.webP -------------------------------------------------------------------------------- /website/static/img/profiling/ios/full/profiling-open-02.webP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/ios/full/profiling-open-02.webP -------------------------------------------------------------------------------- /website/static/img/profiling/ios/full/profiling-open-03.webP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/ios/full/profiling-open-03.webP -------------------------------------------------------------------------------- /website/static/img/profiling/ios/full/profiling-timeline-record-01.webP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/ios/full/profiling-timeline-record-01.webP -------------------------------------------------------------------------------- /website/static/img/profiling/ios/full/profiling-timeline-record-02.webP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/ios/full/profiling-timeline-record-02.webP -------------------------------------------------------------------------------- /website/static/img/profiling/ios/full/profiling-timeline-record-03.webP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/ios/full/profiling-timeline-record-03.webP -------------------------------------------------------------------------------- /website/static/img/profiling/ios/full/profiling-timeline-record-04.webP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/ios/full/profiling-timeline-record-04.webP -------------------------------------------------------------------------------- /website/static/img/profiling/ios/thumb/profiling-console-01.webP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/ios/thumb/profiling-console-01.webP -------------------------------------------------------------------------------- /website/static/img/profiling/ios/thumb/profiling-console-02.webP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/ios/thumb/profiling-console-02.webP -------------------------------------------------------------------------------- /website/static/img/profiling/ios/thumb/profiling-export-01.webP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/ios/thumb/profiling-export-01.webP -------------------------------------------------------------------------------- /website/static/img/profiling/ios/thumb/profiling-export-02.webP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/ios/thumb/profiling-export-02.webP -------------------------------------------------------------------------------- /website/static/img/profiling/ios/thumb/profiling-import-01.webP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/ios/thumb/profiling-import-01.webP -------------------------------------------------------------------------------- /website/static/img/profiling/ios/thumb/profiling-import-02.webP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/ios/thumb/profiling-import-02.webP -------------------------------------------------------------------------------- /website/static/img/profiling/ios/thumb/profiling-network-01.webP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/ios/thumb/profiling-network-01.webP -------------------------------------------------------------------------------- /website/static/img/profiling/ios/thumb/profiling-network-02.webP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/ios/thumb/profiling-network-02.webP -------------------------------------------------------------------------------- /website/static/img/profiling/ios/thumb/profiling-network-03.webP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/ios/thumb/profiling-network-03.webP -------------------------------------------------------------------------------- /website/static/img/profiling/ios/thumb/profiling-open-01.webP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/ios/thumb/profiling-open-01.webP -------------------------------------------------------------------------------- /website/static/img/profiling/ios/thumb/profiling-open-02.webP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/ios/thumb/profiling-open-02.webP -------------------------------------------------------------------------------- /website/static/img/profiling/ios/thumb/profiling-open-03.webP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/ios/thumb/profiling-open-03.webP -------------------------------------------------------------------------------- /website/static/img/profiling/ios/thumb/profiling-timeline-record-01.webP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/ios/thumb/profiling-timeline-record-01.webP -------------------------------------------------------------------------------- /website/static/img/profiling/ios/thumb/profiling-timeline-record-02.webP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/ios/thumb/profiling-timeline-record-02.webP -------------------------------------------------------------------------------- /website/static/img/profiling/ios/thumb/profiling-timeline-record-03.webP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/ios/thumb/profiling-timeline-record-03.webP -------------------------------------------------------------------------------- /website/static/img/profiling/ios/thumb/profiling-timeline-record-04.webP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/profiling/ios/thumb/profiling-timeline-record-04.webP -------------------------------------------------------------------------------- /website/static/img/start-by-adding-an-app-thumbnail.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/start-by-adding-an-app-thumbnail.webp -------------------------------------------------------------------------------- /website/static/img/start-by-adding-an-app.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/start-by-adding-an-app.webp -------------------------------------------------------------------------------- /website/static/img/tutorial/docsVersionDropdown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/tutorial/docsVersionDropdown.png -------------------------------------------------------------------------------- /website/static/img/tutorial/localeDropdown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/tutorial/localeDropdown.png -------------------------------------------------------------------------------- /website/static/img/xcode-run-script-thumbnail.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/xcode-run-script-thumbnail.webp -------------------------------------------------------------------------------- /website/static/img/xcode-run-script.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionic-team/ionic-portals/6a7ea7b1d096bf590135cd7a39944b83894ca19b/website/static/img/xcode-run-script.webp -------------------------------------------------------------------------------- /website/vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "cleanUrls": true, 3 | "redirects": [ 4 | { 5 | "source": "/live-updates", 6 | "destination": "/what-are-live-updates" 7 | }, 8 | { 9 | "source": "/upgrade-guides", 10 | "destination": "/for-ios/upgrade-guides" 11 | }, 12 | { 13 | "source": "/known-issues", 14 | "destination": "/for-ios/known-issues" 15 | }, 16 | { 17 | "source": "/getting-started/guide", 18 | "destination": "/getting-started" 19 | }, 20 | { 21 | "source": "/getting-started/iOS-all-in-one", 22 | "destination": "/for-ios/quick-start" 23 | }, 24 | { 25 | "source": "/getting-started/iOS", 26 | "destination": "/for-ios/quick-start" 27 | }, 28 | { 29 | "source": "/getting-started/android", 30 | "destination": "/for-android/getting-started" 31 | }, 32 | { 33 | "source": "/getting-started/react-native", 34 | "destination": "/for-react-native/getting-started" 35 | }, 36 | { 37 | "source": "/getting-started/live-updates", 38 | "destination": "/portals/getting-started" 39 | }, 40 | { 41 | "source": "/tutorials/*", 42 | "destination": "/for-ios/tutorials/*" 43 | }, 44 | { 45 | "source": "/how-to/*", 46 | "destination": "/for-ios/how-to/*" 47 | }, 48 | { 49 | "source": "/reference/android/*", 50 | "destination": "/for-android/reference/api/*" 51 | }, 52 | { 53 | "source": "/reference-live-updates/android/*", 54 | "destination": "/for-android/reference/live-updates/*" 55 | }, 56 | { 57 | "source": "/examples/ecommerce-app", 58 | "destination": "/for-ios/examples/ecommerce-app" 59 | }, 60 | { 61 | "source": "/reference/web/portals-plugin", 62 | "destination": "/portals/portals-plugin" 63 | }, 64 | { 65 | "source": "/examples/ecommerce-react-native", 66 | "destination": "/for-react-native/examples/ecommerce" 67 | }, 68 | { 69 | "source": "/plugin-overview", 70 | "destination": "/for-ios/how-to/using-a-capacitor-plugin#published-plugins" 71 | } 72 | ], 73 | "rewrites": [ 74 | { 75 | "source": "/docs/portals/:match*", 76 | "destination": "/:match*" 77 | } 78 | ] 79 | } 80 | --------------------------------------------------------------------------------