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 |
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 | 
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 | 
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 | 
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 | 
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 | 
34 |
35 | Next, expand the "Build" drop down and select "Post-actions" to run the script after your build has completed:
36 |
37 | 
38 |
39 | Then, click the "+" button and select "New Run Script Action":
40 |
41 | 
42 |
43 | In the Run Script Action editor, select the target whose build settings you need to inherit:
44 |
45 | 
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 | 
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 | 
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 | 
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 | 
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 |
Platform
76 |
Latest Portals Version
77 |
Minimum Supported Platform Version
78 |
79 |
80 |
iOS
81 |
{getPortalsVersionIos()}
82 |
iOS {getiOSMinVersion()}
83 |
84 |
85 |
Android
86 |
{getPortalsVersionAndroid()}
87 |
Android SDK {getAndroidMinSdk()}
88 |
89 |
90 |
React Native
91 |
{getPortalsVersionRN()}
92 |
React Native {getRnMinVersion()}
93 |
94 |
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 | 
21 |
22 | Next, expand the "Build" drop down and select "Post-actions" to run the script after your build has completed:
23 |
24 | 
25 |
26 | Then, click the "+" button and select "New Run Script Action":
27 |
28 | 
29 |
30 | In the Run Script Action editor, select the target whose build settings you need to inherit:
31 |
32 | 
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 | 
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 | 
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 | 
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 |
Platform
43 |
Latest Portals Version
44 |
Minimum Supported Platform Version
45 |
46 |
47 |
iOS
48 |
{getPortalsVersionIos()}
49 |
iOS {getiOSMinVersion()}
50 |
51 |
52 |
Android
53 |
{getPortalsVersionAndroid()}
54 |
Android SDK {getAndroidMinSdk()}
55 |
56 |
57 |
React Native
58 |
{getPortalsVersionRN()}
59 |
React Native {getRnMinVersion()}
60 |
61 |
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 |