├── .github
├── actions
│ └── setup-tools
│ │ └── action.yml
└── workflows
│ ├── basic-tests.yml
│ ├── docs-preview.yml
│ ├── docs-production.yml
│ ├── release-cocoapods.yml
│ ├── release-dev.yml
│ ├── release-rc.yml
│ ├── release.yml
│ ├── reusable_build-packages.yml
│ ├── reusable_lint-packages.yml
│ ├── reusable_release-npm.yml
│ └── reusable_setup.yml
├── .gitignore
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── img
├── add-capability.png
├── capabilities-final.png
├── example-watchui.png
├── spm-add-local.png
├── spm-finished.png
├── spm-nav-to-package.png
├── spm-pick-target.png
├── spm-project-dependancies.png
├── target-dropdown.png
├── target-watch.png
├── watch-main-code.png
├── watch-remote-not.png
├── watch-sources-added.png
└── watch-target-options.png
├── lerna.json
├── nx.json
├── package.json
├── packages
├── capacitor-plugin
│ ├── .eslintignore
│ ├── .eslintrc.js
│ ├── .gitignore
│ ├── .npmignore
│ ├── .prettierignore
│ ├── .prettierrc.js
│ ├── CHANGELOG.md
│ ├── CONTRIBUTING.md
│ ├── CapWatch-Watch-SPM
│ │ ├── .gitignore
│ │ ├── Package.swift
│ │ ├── README.md
│ │ ├── Sources
│ │ │ └── iOS-capWatch-watch
│ │ │ │ ├── CapWatchComponentView.swift
│ │ │ │ ├── CapWatchContentView.swift
│ │ │ │ ├── CapWatchText.swift
│ │ │ │ ├── CapWatchView.swift
│ │ │ │ ├── StringExtensions.swift
│ │ │ │ ├── WatchViewModel.swift
│ │ │ │ └── iOS_capWatch_watch.swift
│ │ └── Tests
│ │ │ └── iOS-capWatch-watchTests
│ │ │ └── iOS_capWatch_watchTests.swift
│ ├── CapacitorWatch.podspec
│ ├── LICENSE
│ ├── README.md
│ ├── android
│ │ ├── .gitignore
│ │ ├── build.gradle
│ │ ├── gradle.properties
│ │ ├── gradle
│ │ │ └── wrapper
│ │ │ │ ├── gradle-wrapper.jar
│ │ │ │ └── gradle-wrapper.properties
│ │ ├── gradlew
│ │ ├── gradlew.bat
│ │ ├── proguard-rules.pro
│ │ ├── settings.gradle
│ │ └── src
│ │ │ ├── androidTest
│ │ │ └── java
│ │ │ │ └── com
│ │ │ │ └── getcapacitor
│ │ │ │ └── android
│ │ │ │ └── ExampleInstrumentedTest.java
│ │ │ ├── main
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── java
│ │ │ │ └── io
│ │ │ │ │ └── ionic
│ │ │ │ │ └── sslpinning
│ │ │ │ │ ├── Watch.java
│ │ │ │ │ └── WatchPlugin.java
│ │ │ └── res
│ │ │ │ └── .gitkeep
│ │ │ └── test
│ │ │ └── java
│ │ │ └── com
│ │ │ └── getcapacitor
│ │ │ └── ExampleUnitTest.java
│ ├── ios
│ │ ├── Plugin.xcodeproj
│ │ │ ├── project.pbxproj
│ │ │ ├── project.xcworkspace
│ │ │ │ ├── contents.xcworkspacedata
│ │ │ │ └── xcshareddata
│ │ │ │ │ └── IDEWorkspaceChecks.plist
│ │ │ └── xcshareddata
│ │ │ │ └── xcschemes
│ │ │ │ ├── Plugin.xcscheme
│ │ │ │ └── PluginTests.xcscheme
│ │ ├── Plugin.xcworkspace
│ │ │ ├── contents.xcworkspacedata
│ │ │ └── xcshareddata
│ │ │ │ └── IDEWorkspaceChecks.plist
│ │ ├── Plugin
│ │ │ ├── CapWatch
│ │ │ │ ├── CapWatchDelegate.swift
│ │ │ │ └── Consts.swift
│ │ │ ├── Info.plist
│ │ │ ├── WatchPlugin.h
│ │ │ ├── WatchPlugin.m
│ │ │ └── WatchPlugin.swift
│ │ ├── PluginTests
│ │ │ ├── CapWatchTests.swift
│ │ │ └── Info.plist
│ │ └── Podfile
│ ├── package-lock.json
│ ├── package.json
│ ├── rollup.config.js
│ ├── src
│ │ ├── definitions.ts
│ │ ├── index.ts
│ │ └── web.ts
│ ├── tsconfig.json
│ └── typedoc.json
├── example-app
│ ├── .browserslistrc
│ ├── .eslintrc.js
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── babel.config.js
│ ├── capacitor.config.ts
│ ├── cypress.json
│ ├── ionic.config.json
│ ├── ios
│ │ ├── .gitignore
│ │ └── App
│ │ │ ├── App.xcodeproj
│ │ │ ├── project.pbxproj
│ │ │ ├── project.xcworkspace
│ │ │ │ └── contents.xcworkspacedata
│ │ │ └── xcshareddata
│ │ │ │ └── xcschemes
│ │ │ │ ├── App.xcscheme
│ │ │ │ └── example-watch Watch App.xcscheme
│ │ │ ├── App.xcworkspace
│ │ │ ├── contents.xcworkspacedata
│ │ │ └── xcshareddata
│ │ │ │ └── IDEWorkspaceChecks.plist
│ │ │ ├── App
│ │ │ ├── AppDelegate.swift
│ │ │ ├── Assets.xcassets
│ │ │ │ ├── AppIcon.appiconset
│ │ │ │ │ ├── AppIcon-20x20@1x.png
│ │ │ │ │ ├── AppIcon-20x20@2x-1.png
│ │ │ │ │ ├── AppIcon-20x20@2x.png
│ │ │ │ │ ├── AppIcon-20x20@3x.png
│ │ │ │ │ ├── AppIcon-29x29@1x.png
│ │ │ │ │ ├── AppIcon-29x29@2x-1.png
│ │ │ │ │ ├── AppIcon-29x29@2x.png
│ │ │ │ │ ├── AppIcon-29x29@3x.png
│ │ │ │ │ ├── AppIcon-40x40@1x.png
│ │ │ │ │ ├── AppIcon-40x40@2x-1.png
│ │ │ │ │ ├── AppIcon-40x40@2x.png
│ │ │ │ │ ├── AppIcon-40x40@3x.png
│ │ │ │ │ ├── AppIcon-512@2x.png
│ │ │ │ │ ├── AppIcon-60x60@2x.png
│ │ │ │ │ ├── AppIcon-60x60@3x.png
│ │ │ │ │ ├── AppIcon-76x76@1x.png
│ │ │ │ │ ├── AppIcon-76x76@2x.png
│ │ │ │ │ ├── AppIcon-83.5x83.5@2x.png
│ │ │ │ │ └── Contents.json
│ │ │ │ ├── Contents.json
│ │ │ │ └── Splash.imageset
│ │ │ │ │ ├── Contents.json
│ │ │ │ │ ├── splash-2732x2732-1.png
│ │ │ │ │ ├── splash-2732x2732-2.png
│ │ │ │ │ └── splash-2732x2732.png
│ │ │ ├── Base.lproj
│ │ │ │ ├── LaunchScreen.storyboard
│ │ │ │ └── Main.storyboard
│ │ │ └── Info.plist
│ │ │ ├── Podfile
│ │ │ └── example-watch Watch App
│ │ │ ├── Assets.xcassets
│ │ │ ├── AccentColor.colorset
│ │ │ │ └── Contents.json
│ │ │ ├── AppIcon.appiconset
│ │ │ │ └── Contents.json
│ │ │ └── Contents.json
│ │ │ ├── Preview Content
│ │ │ └── Preview Assets.xcassets
│ │ │ │ └── Contents.json
│ │ │ ├── example_watchApp.swift
│ │ │ └── faux-spm
│ │ │ ├── CapWatchComponentView.swift
│ │ │ ├── CapWatchContentView.swift
│ │ │ ├── CapWatchText.swift
│ │ │ ├── CapWatchView.swift
│ │ │ ├── StringExtensions.swift
│ │ │ ├── WatchViewModel.swift
│ │ │ └── iOS_capWatch_watch.swift
│ ├── jest.config.js
│ ├── package.json
│ ├── public
│ │ ├── assets
│ │ │ ├── icon
│ │ │ │ ├── favicon.png
│ │ │ │ └── icon.png
│ │ │ └── shapes.svg
│ │ └── index.html
│ ├── src
│ │ ├── App.vue
│ │ ├── main.ts
│ │ ├── router
│ │ │ └── index.ts
│ │ ├── shims-vue.d.ts
│ │ ├── theme
│ │ │ └── variables.css
│ │ └── views
│ │ │ └── HomePage.vue
│ ├── tests
│ │ ├── e2e
│ │ │ ├── .eslintrc.js
│ │ │ ├── plugins
│ │ │ │ └── index.js
│ │ │ ├── specs
│ │ │ │ └── test.js
│ │ │ └── support
│ │ │ │ ├── commands.js
│ │ │ │ └── index.js
│ │ └── unit
│ │ │ └── example.spec.ts
│ └── tsconfig.json
├── iOS-capWatch-sdk
│ ├── .gitignore
│ ├── Package.swift
│ ├── README.md
│ ├── Sources
│ │ └── CapWatch
│ │ │ ├── WatchSessionDelegate.swift
│ │ │ └── iOS_capWatch_sdk.swift
│ └── Tests
│ │ └── iOS-capWatch-sdkTests
│ │ └── iOS_capWatch_sdkTests.swift
└── iOS-capWatch-watch
│ ├── .gitignore
│ ├── Package.swift
│ ├── README.md
│ ├── Sources
│ └── iOS-capWatch-watch
│ │ ├── CapWatchComponentView.swift
│ │ ├── CapWatchContentView.swift
│ │ ├── CapWatchText.swift
│ │ ├── CapWatchView.swift
│ │ ├── StringExtensions.swift
│ │ ├── WatchViewModel.swift
│ │ └── iOS_capWatch_watch.swift
│ └── Tests
│ └── iOS-capWatch-watchTests
│ └── iOS_capWatch_watchTests.swift
├── pnpm-lock.yaml
└── pnpm-workspace.yaml
/.github/actions/setup-tools/action.yml:
--------------------------------------------------------------------------------
1 | name: 'Setup Tools'
2 | description: 'Setup tools needed in repo'
3 |
4 | inputs:
5 | skip-install-on-cache-hit:
6 | description: 'If a Cache Hit happens, skip pnpm install'
7 | required: true
8 | default: 'false'
9 |
10 | runs:
11 | using: 'composite'
12 | steps:
13 | - name: Install Node.js
14 | uses: actions/setup-node@v3
15 | with:
16 | node-version: 20
17 |
18 | - name: Install PNPM
19 | uses: pnpm/action-setup@v2
20 | id: pnpm-install
21 | with:
22 | version: 7
23 | run_install: false
24 |
25 | - name: Get pnpm store directory
26 | id: pnpm-cache
27 | shell: bash
28 | run: |
29 | echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_ENV
30 |
31 | - name: Setup PNPM cache
32 | id: cache-pnpm-store
33 | uses: actions/cache@v3
34 | env:
35 | STORE_PATH: ${{ env.STORE_PATH }}
36 | with:
37 | path: ${{ env.STORE_PATH }}
38 | key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
39 | restore-keys: |
40 | ${{ runner.os }}-pnpm-store-
41 |
42 | - name: Install dependencies
43 | if: inputs.skip-install-on-cache-hit == 'false' || (inputs.skip-install-on-cache-hit == 'true' && steps.cache-pnpm-store.cache-hit == 'false')
44 | shell: bash
45 | run: pnpm install --no-frozen-lockfile
46 |
--------------------------------------------------------------------------------
/.github/workflows/basic-tests.yml:
--------------------------------------------------------------------------------
1 | name: "Basic Tests"
2 |
3 | on:
4 | pull_request:
5 | types:
6 | - 'synchronize'
7 | - 'opened'
8 |
9 | jobs:
10 | setup:
11 | uses: ./.github/workflows/reusable_setup.yml
12 | secrets:
13 | CAP_GH_RELEASE_TOKEN: ${{ secrets.CAP_GH_RELEASE_TOKEN }}
14 |
15 | lint-test:
16 | needs: 'setup'
17 | uses: ./.github/workflows/reusable_lint-packages.yml
18 | secrets:
19 | CAP_GH_RELEASE_TOKEN: ${{ secrets.CAP_GH_RELEASE_TOKEN }}
20 |
21 | build-packages:
22 | needs: 'setup'
23 | uses: ./.github/workflows/reusable_build-packages.yml
24 | secrets:
25 | CAP_GH_RELEASE_TOKEN: ${{ secrets.CAP_GH_RELEASE_TOKEN }}
26 |
--------------------------------------------------------------------------------
/.github/workflows/docs-preview.yml:
--------------------------------------------------------------------------------
1 | name: Deploy docs PR preview
2 |
3 | on:
4 | pull_request:
5 | paths:
6 | - .github/workflows/docs-preview.yml
7 | - website/**
8 |
9 | permissions:
10 | contents: read
11 | deployments: write
12 | id-token: write
13 |
14 | env:
15 | SLUG: watch-${{ github.event.number }}-${{ github.run_id }}
16 |
17 | defaults:
18 | run:
19 | working-directory: website
20 |
21 | jobs:
22 | deploy:
23 | if: false
24 | runs-on: ubuntu-latest
25 | timeout-minutes: 15
26 | environment:
27 | name: preview-${{ github.event.number }}
28 | url: https://${{ env.SLUG }}.ionicpreview.com
29 | steps:
30 | - uses: actions/checkout@v3
31 |
32 | - name: 'Setup Tools'
33 | uses: ./.github/actions/setup-tools
34 |
35 | - name: Build
36 | run: pnpm run build-docs
37 |
38 | - uses: aws-actions/configure-aws-credentials@v1
39 | with:
40 | role-to-assume: arn:aws:iam::319312831725:role/github-docs
41 | aws-region: us-east-1
42 |
43 | - name: Deploy
44 | run: |
45 | aws s3 sync build/ s3://ionic-docs/preview/${{ env.SLUG }}/ --exclude '*.html' --cache-control max-age=31536000 --only-show-errors
46 | aws s3 sync build/ s3://ionic-docs/preview/${{ env.SLUG }}/ --exclude '*' --include '*.html' --cache-control max-age=60 --only-show-errors
--------------------------------------------------------------------------------
/.github/workflows/docs-production.yml:
--------------------------------------------------------------------------------
1 | name: Deploy docs to production
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | paths:
8 | - .github/workflows/docs-production.yml
9 | - website/**
10 |
11 | permissions:
12 | contents: read
13 | deployments: write
14 | id-token: write
15 |
16 | env:
17 | SLUG: watch
18 |
19 | defaults:
20 | run:
21 | working-directory: website
22 |
23 | jobs:
24 | deploy:
25 | runs-on: ubuntu-latest
26 | timeout-minutes: 15
27 | environment:
28 | name: production
29 | url: https://ionic.io/docs/${{ env.SLUG }}
30 | steps:
31 | - uses: actions/checkout@v3
32 |
33 | - name: 'Setup Tools'
34 | uses: ./.github/actions/setup-tools
35 |
36 | - name: Build
37 | run: pnpm run build-docs
38 |
39 | - uses: aws-actions/configure-aws-credentials@v1
40 | with:
41 | role-to-assume: arn:aws:iam::319312831725:role/github-docs
42 | aws-region: us-east-1
43 |
44 | - name: Deploy
45 | run: |
46 | aws s3 sync build/ s3://ionic-docs/production/${{ env.SLUG }}/ --exclude '*.html' --cache-control max-age=31536000 --only-show-errors
47 | aws s3 sync build/ s3://ionic-docs/production/${{ env.SLUG }}/ --exclude '*' --include '*.html' --cache-control max-age=60 --only-show-errors
--------------------------------------------------------------------------------
/.github/workflows/release-cocoapods.yml:
--------------------------------------------------------------------------------
1 | name: Publish to Cocoapods
2 |
3 | on:
4 | push:
5 | tags:
6 | - '*'
7 |
8 | defaults:
9 | run:
10 | working-directory: packages/capacitor-plugin
11 |
12 | jobs:
13 | publish-to-cocoapods:
14 | runs-on: macos-12
15 | timeout-minutes: 30
16 | steps:
17 | - uses: actions/checkout@v3
18 | - run: sudo xcode-select --switch /Applications/Xcode_14.1.app
19 | - name: Install build dependencies
20 | run: gem install cocoapods
21 | - name: Validate podspec
22 | run: pod spec lint
23 | - name: Publish podspec
24 | run: pod trunk push
25 | env:
26 | COCOAPODS_TRUNK_TOKEN: ${{ secrets.COCOAPODS_TRUNK_TOKEN }}
27 |
--------------------------------------------------------------------------------
/.github/workflows/release-dev.yml:
--------------------------------------------------------------------------------
1 | name: "Release Dev"
2 |
3 | on:
4 | push:
5 | branches:
6 | - 'main'
7 | workflow_dispatch:
8 |
9 | jobs:
10 | setup:
11 | uses: ./.github/workflows/reusable_setup.yml
12 | secrets:
13 | CAP_GH_RELEASE_TOKEN: ${{ secrets.CAP_GH_RELEASE_TOKEN }}
14 |
15 | lint-test:
16 | needs: 'setup'
17 | uses: ./.github/workflows/reusable_lint-packages.yml
18 | secrets:
19 | CAP_GH_RELEASE_TOKEN: ${{ secrets.CAP_GH_RELEASE_TOKEN }}
20 |
21 | build-packages:
22 | needs: 'setup'
23 | uses: ./.github/workflows/reusable_build-packages.yml
24 | secrets:
25 | CAP_GH_RELEASE_TOKEN: ${{ secrets.CAP_GH_RELEASE_TOKEN }}
26 |
27 | release-npm-dev:
28 | needs: ['build-packages', 'lint-test']
29 | uses: ./.github/workflows/reusable_release-npm.yml
30 | with:
31 | create-dev-release: true
32 | create-rc-release: false
33 | secrets:
34 | CAP_GH_RELEASE_TOKEN: ${{ secrets.CAP_GH_RELEASE_TOKEN }}
35 | NPM_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }}
--------------------------------------------------------------------------------
/.github/workflows/release-rc.yml:
--------------------------------------------------------------------------------
1 | name: "Release RC"
2 |
3 | on: workflow_dispatch
4 |
5 | jobs:
6 | setup:
7 | uses: ./.github/workflows/reusable_setup.yml
8 | secrets:
9 | CAP_GH_RELEASE_TOKEN: ${{ secrets.CAP_GH_RELEASE_TOKEN }}
10 |
11 | lint-test:
12 | needs: 'setup'
13 | uses: ./.github/workflows/reusable_lint-packages.yml
14 | secrets:
15 | CAP_GH_RELEASE_TOKEN: ${{ secrets.CAP_GH_RELEASE_TOKEN }}
16 |
17 | build-packages:
18 | needs: 'setup'
19 | uses: ./.github/workflows/reusable_build-packages.yml
20 | secrets:
21 | CAP_GH_RELEASE_TOKEN: ${{ secrets.CAP_GH_RELEASE_TOKEN }}
22 |
23 | release-npm-rc:
24 | needs: ['build-packages', 'lint-test']
25 | uses: ./.github/workflows/reusable_release-npm.yml
26 | with:
27 | create-dev-release: false
28 | create-rc-release: true
29 | secrets:
30 | CAP_GH_RELEASE_TOKEN: ${{ secrets.CAP_GH_RELEASE_TOKEN }}
31 | NPM_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }}
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: "Release"
2 |
3 | on: workflow_dispatch
4 |
5 | jobs:
6 | setup:
7 | uses: ./.github/workflows/reusable_setup.yml
8 | secrets:
9 | CAP_GH_RELEASE_TOKEN: ${{ secrets.CAP_GH_RELEASE_TOKEN }}
10 |
11 | lint-test:
12 | needs: 'setup'
13 | uses: ./.github/workflows/reusable_lint-packages.yml
14 | secrets:
15 | CAP_GH_RELEASE_TOKEN: ${{ secrets.CAP_GH_RELEASE_TOKEN }}
16 |
17 | build-packages:
18 | needs: 'setup'
19 | uses: ./.github/workflows/reusable_build-packages.yml
20 | secrets:
21 | CAP_GH_RELEASE_TOKEN: ${{ secrets.CAP_GH_RELEASE_TOKEN }}
22 |
23 | release-npm:
24 | needs: ['build-packages', 'lint-test']
25 | uses: ./.github/workflows/reusable_release-npm.yml
26 | with:
27 | create-dev-release: false
28 | create-rc-release: false
29 | secrets:
30 | CAP_GH_RELEASE_TOKEN: ${{ secrets.CAP_GH_RELEASE_TOKEN }}
31 | NPM_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }}
--------------------------------------------------------------------------------
/.github/workflows/reusable_build-packages.yml:
--------------------------------------------------------------------------------
1 | name: "Build Packages"
2 |
3 | on:
4 | workflow_call:
5 | secrets:
6 | CAP_GH_RELEASE_TOKEN:
7 | required: true
8 |
9 | jobs:
10 | build:
11 | runs-on: 'ubuntu-22.04'
12 | timeout-minutes: 30
13 | steps:
14 | - uses: actions/checkout@v3
15 | with:
16 | fetch-depth: 0
17 | token: ${{ secrets.CAP_GH_RELEASE_TOKEN }}
18 |
19 | - name: 'Setup Tools'
20 | uses: ./.github/actions/setup-tools
21 |
22 | - uses: nrwl/nx-set-shas@v3
23 | with:
24 | main-branch-name: 'main'
25 |
26 | - name: 'Build Packages'
27 | shell: bash
28 | run: |
29 | pnpm nx affected --target=build --parallel=5
30 |
--------------------------------------------------------------------------------
/.github/workflows/reusable_lint-packages.yml:
--------------------------------------------------------------------------------
1 | name: "Lint Packages"
2 |
3 | on:
4 | workflow_call:
5 | secrets:
6 | CAP_GH_RELEASE_TOKEN:
7 | required: true
8 |
9 | jobs:
10 | lint:
11 | runs-on: 'macos-12'
12 | timeout-minutes: 30
13 | steps:
14 | - uses: actions/checkout@v3
15 | with:
16 | fetch-depth: 0
17 | token: ${{ secrets.CAP_GH_RELEASE_TOKEN }}
18 |
19 | - name: 'Setup Tools'
20 | uses: ./.github/actions/setup-tools
21 |
22 | - uses: nrwl/nx-set-shas@v3
23 | with:
24 | main-branch-name: 'main'
25 |
26 | - name: 'Lint Packages'
27 | shell: bash
28 | run: |
29 | pnpm nx affected --target=lint --parallel=5
30 |
--------------------------------------------------------------------------------
/.github/workflows/reusable_release-npm.yml:
--------------------------------------------------------------------------------
1 | name: "Release to NPM"
2 |
3 | on:
4 | workflow_call:
5 | inputs:
6 | create-dev-release:
7 | required: false
8 | type: boolean
9 | default: false
10 | create-rc-release:
11 | required: false
12 | type: boolean
13 | default: false
14 | secrets:
15 | CAP_GH_RELEASE_TOKEN:
16 | required: true
17 | NPM_TOKEN:
18 | required: true
19 |
20 | jobs:
21 | build:
22 | runs-on: 'ubuntu-22.04'
23 | timeout-minutes: 30
24 | steps:
25 | - uses: actions/checkout@v3
26 | with:
27 | fetch-depth: 0
28 | token: ${{ secrets.CAP_GH_RELEASE_TOKEN }}
29 |
30 | - name: 'Setup Tools'
31 | uses: ./.github/actions/setup-tools
32 |
33 | - name: 'Check Current Release Type'
34 | shell: bash
35 | run: |
36 | RELEASE_TYPE="$([[ "$(git describe --abbrev=0 --tags)" =~ ^[0-9]{1,}[\.][0-9]{1,}[\.][0-9]{1,}[-]((beta)|(rc)|(alpha))[\.][0-9]{1,}$ ]] && echo "prerelease" || echo "release")"
37 | echo "releasetype=$RELEASE_TYPE" >> $GITHUB_ENV
38 |
39 | - name: 'Set GitHub Identity'
40 | shell: bash
41 | run: |
42 | git config user.name "Github Workflow (on behalf of ${{ github.actor }})"
43 | git config user.email "users.noreply.github.com"
44 |
45 | - name: "NPM Identity"
46 | run: |
47 | echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" >> ~/.npmrc
48 | npm whoami
49 |
50 | - name: 'Dev Release'
51 | if: inputs.create-dev-release
52 | shell: bash
53 | env:
54 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
55 | GH_TOKEN: ${{ secrets.CAP_GH_RELEASE_TOKEN }}
56 | run: |
57 | pnpm run publish:dev
58 |
59 | - name: 'RC Release'
60 | if: "!inputs.create-dev-release && inputs.create-rc-release"
61 | shell: bash
62 | env:
63 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
64 | GH_TOKEN: ${{ secrets.CAP_GH_RELEASE_TOKEN }}
65 | run: |
66 | pnpm run publish:rc
67 |
68 | - name: 'Release from PreRelease'
69 | if: ${{ !inputs.create-dev-release && !inputs.create-rc-release && env.releasetype == 'prerelease' }}
70 | shell: bash
71 | env:
72 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
73 | GH_TOKEN: ${{ secrets.CAP_GH_RELEASE_TOKEN }}
74 | run: |
75 | pnpm run publish:release-from-prerelease
76 |
77 | - name: 'Release from Release'
78 | if: ${{ !inputs.create-dev-release && !inputs.create-rc-release && env.releasetype == 'release' }}
79 | shell: bash
80 | env:
81 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
82 | GH_TOKEN: ${{ secrets.CAP_GH_RELEASE_TOKEN }}
83 | run: |
84 | pnpm run publish:latest
--------------------------------------------------------------------------------
/.github/workflows/reusable_setup.yml:
--------------------------------------------------------------------------------
1 | name: "Setup"
2 |
3 | on:
4 | workflow_call:
5 | secrets:
6 | CAP_GH_RELEASE_TOKEN:
7 | required: true
8 |
9 | jobs:
10 | setup:
11 | strategy:
12 | matrix:
13 | os: ['ubuntu-22.04', 'macos-12']
14 | runs-on: ${{ matrix.os }}
15 | timeout-minutes: 30
16 | steps:
17 | - uses: actions/checkout@v3
18 | with:
19 | fetch-depth: 0
20 | token: ${{ secrets.CAP_GH_RELEASE_TOKEN }}
21 |
22 | - name: 'Setup Tools'
23 | uses: ./.github/actions/setup-tools
24 | with:
25 | skip-install-on-cache-hit: 'true'
26 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | .DS_Store
4 | *.tgz
5 | .vscode
6 | .eslintcache
7 | .env
8 | cache%
9 |
--------------------------------------------------------------------------------
/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.1.12](https://github.com/ionic-team/CapacitorWatch/compare/0.1.11...0.1.12) (2024-04-08)
7 |
8 | **Note:** Version bump only for package root
9 |
10 |
11 |
12 |
13 |
14 | ## [0.1.11](https://github.com/ionic-team/CapacitorWatch/compare/0.1.10...0.1.11) (2024-04-05)
15 |
16 | **Note:** Version bump only for package root
17 |
18 |
19 |
20 |
21 |
22 | ## [0.1.10](https://github.com/ionic-team/CapacitorWatch/compare/0.1.9...0.1.10) (2023-11-02)
23 |
24 | **Note:** Version bump only for package root
25 |
26 |
27 |
28 |
29 |
30 | ## [0.1.9](https://github.com/ionic-team/CapacitorWatch/compare/0.1.8...0.1.9) (2023-11-02)
31 |
32 | **Note:** Version bump only for package root
33 |
34 |
35 |
36 |
37 |
38 | ## [0.1.8](https://github.com/ionic-team/CapacitorWatch/compare/0.1.7...0.1.8) (2023-08-24)
39 |
40 | **Note:** Version bump only for package root
41 |
42 |
43 |
44 |
45 |
46 | ## [0.1.7](https://github.com/ionic-team/CapacitorWatch/compare/0.1.6...0.1.7) (2023-08-09)
47 |
48 | **Note:** Version bump only for package root
49 |
50 |
51 |
52 |
53 |
54 | ## [0.1.6](https://github.com/ionic-team/CapacitorWatch/compare/0.1.5...0.1.6) (2023-08-01)
55 |
56 | **Note:** Version bump only for package root
57 |
58 |
59 |
60 |
61 |
62 | ## [0.1.5](https://github.com/ionic-team/CapacitorWatch/compare/0.1.4...0.1.5) (2023-07-18)
63 |
64 | **Note:** Version bump only for package root
65 |
66 |
67 |
68 |
69 |
70 | ## [0.1.4](https://github.com/ionic-team/CapacitorWatch/compare/0.1.3...0.1.4) (2023-07-13)
71 |
72 | **Note:** Version bump only for package root
73 |
74 |
75 |
76 |
77 |
78 | ## [0.1.3](https://github.com/ionic-team/CapacitorWatch/compare/0.1.2...0.1.3) (2023-07-13)
79 |
80 | **Note:** Version bump only for package root
81 |
82 |
83 |
84 |
85 |
86 | ## [0.1.2](https://github.com/ionic-team/CapacitorWatch/compare/0.1.1...0.1.2) (2023-07-13)
87 |
88 | **Note:** Version bump only for package root
89 |
90 |
91 |
92 |
93 |
94 | ## 0.1.1 (2023-07-13)
95 |
96 | **Note:** Version bump only for package root
97 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Code of Conduct
2 |
3 | As contributors and maintainers of the Capacitor project, we pledge to respect everyone who contributes by posting issues, updating documentation, submitting pull requests, providing feedback in comments, and any other activities.
4 |
5 | Communication through any of Capacitor's channels (GitHub, Slack, Forum, IRC, mailing lists, Twitter, etc.) must be constructive and never resort to personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
6 |
7 | We promise to extend courtesy and respect to everyone involved in this project regardless of gender, gender identity, sexual orientation, disability, age, race, ethnicity, religion, or level of experience. We expect anyone contributing to the Capacitor project to do the same.
8 |
9 | If any member of the community violates this code of conduct, the maintainers of the Capacitor project may take action, removing issues, comments, and PRs or blocking accounts as deemed appropriate.
10 |
11 | If you are subject to or witness unacceptable behavior, or have any other concerns, please email us at [hi@ionicframework.com](mailto:hi@ionicframework.com).
12 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | You will need to use `PNPM` instead of `NPM` (direct replacement for NPM, ie: `pnpm i`):
2 |
3 |
4 | - On POSIX systems, you may install pnpm even if you don't have Node.js installed, using the following script:
5 | ```
6 | curl -fsSL https://get.pnpm.io/install.sh | sh -
7 | ```
8 |
9 | - If you don't have curl installed, you would like to use wget:
10 | ```
11 | wget -qO- https://get.pnpm.io/install.sh | sh -
12 | ```
13 |
14 | - On Windows (PowerShell):
15 | ```
16 | iwr https://get.pnpm.io/install.ps1 -useb | iex
17 | ```
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017-present Drifty Co.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # @capacitor/watch
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | ---
15 |
16 | _CapacitorLABS_ - This project is experimental. Support is not provided. Please open issues when needed.
17 |
18 | ---
19 |
20 | The Capacitor Watch plugin allows you to define a UI for a watch in your web code and show it on a paired watch.
21 |
22 | This currently only supports iOS. This guide assumes you've already added iOS to your capcacitor project.
23 |
24 | Also note - all of this will only work with an actual Apple Watch. Simulators don't allow the app<->watch communcation like real devices do.
25 |
26 | ## Install
27 |
28 | Step 1
29 |
30 | Add the watch plugin to your capacitor project, and then open the Xcode project:
31 |
32 | ```bash
33 | npm install @capacitor/watch
34 | npx cap sync
35 | npx cap open ios
36 | ```
37 |
38 | Step 2
39 |
40 | Go to add capabilities:
41 |
42 |
43 |
44 | Add the 'Background Modes' and 'Push Notification' capabilities. Then in the Background Modes options, select 'Background Fetch', 'Remote Notifications', and 'Background Processing'. Your App target should look like this:
45 |
46 |
47 |
48 | Step 3
49 |
50 | Open `AppDelegate.swift` and add `import WatchConnectivity` and `import CapactiorWatch` to the top of the file, and the following code inside the `application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?)` method:
51 |
52 | ```swift
53 | assert(WCSession.isSupported(), "This sample requires Watch Connectivity support!")
54 | WCSession.default.delegate = CapWatchSessionDelegate.shared
55 | WCSession.default.activate()
56 | ```
57 |
58 | Step 4
59 |
60 | Select File -> New -> Target in Xcode, and then the watchOS tab, and 'App':
61 |
62 |
63 |
64 | Click 'Next' then fill out the options like so:
65 |
66 |
67 |
68 | This dialog can be a little confusing, the key thing is your 'Bundle Identifier' must be `[your apps bundle ID].watchapp` for the watch<->app pairing to work. You must also pick SwiftUI for the Interface and Swift for the language. The project should be `App`.
69 |
70 | Step 5
71 |
72 | We're going to add the code that makes Capacitor Watch work in the watch application.
73 |
74 | ---
75 |
76 | If you are using Xcode 15 or beyond you then need to add the Capacitor Watch Swift Package from your node_modules:
77 |
78 | First go to the project package dependancies
79 |
80 |
81 |
82 | Then choose 'Add Local'
83 |
84 |
85 |
86 | Then navigate into the `node_modules/@capacitor/watch/CapWatch-Watch-SPM` folder and click 'Add Package'
87 |
88 |
89 |
90 | Then in the column on the right pick your watch app to be the target and click 'Add Package'
91 |
92 |
93 |
94 | Once this is done your Package Dependancies should look like this:
95 |
96 |
97 |
98 | ---
99 |
100 | With Xcode 14 you will need to go here https://github.com/ionic-team/CapacitorWatch/tree/main/packages/iOS-capWatch-watch/Sources/iOS-capWatch-watch and copy all the files into your Watch project and make sure the target selected is your watch app. It should look like so:
101 |
102 |
103 |
104 | Step 6
105 |
106 | Then open the watch app's 'Main' file which should be `watchappApp.swift`. Add the lines `import WatchConnectivity` and `import iOS_capWatch_watch` above the `@main` statement. Then replace the line that says `ContentView()` with this:
107 |
108 | The finished file should look like this:
109 |
110 | ```swift
111 | import SwiftUI
112 | import WatchConnectivity
113 | import iOS_capWatch_watch
114 |
115 | @main
116 | struct watchddgg_Watch_AppApp: App {
117 | var body: some Scene {
118 | WindowGroup {
119 | CapWatchContentView()
120 | .onAppear {
121 | assert(WCSession.isSupported(), "This sample requires Watch Connectivity support!")
122 | WCSession.default.delegate = WatchViewModel.shared
123 | WCSession.default.activate()
124 | }
125 | }
126 | }
127 | }
128 | ```
129 |
130 | Step 7
131 |
132 | Add the 'Background Modes' capability to the watch app target, and enable 'Remote Notifications':
133 |
134 |
135 |
136 | You should be ready to develop for Capcacitor Watch now!
137 |
138 | ## Development workflow
139 |
140 | You can still develop your iOS app like a normal capacitor app, but getting things to run on the watch requires you to change the target and destination in Xcode. You can change this with the 'Target Dropdown' near the center-top of Xcode:
141 |
142 |
143 |
144 | The right half of this bar lets you pick the destination device or simulator. You will need to pick the watch paired with the phone and then hit the 'Run' button or use the 'cmd+r' run shortcut.
145 |
146 | There can be some challenges in syncing the watch and phone apps. Sometimes you will get an error in the xcode console complaining the compainion app is not present. The best solution in this case is to re-build and re-install the apps on both devices.
147 |
148 | ## Building the watch UI and sending it to the watch
149 |
150 | You will use a long string to define the watch UI. A newline delimits components. Currently this plugin only supports a vertical scroll view of either Text or Button components.
151 |
152 | Once you've defined your UI you can send it to the watch using the `updateWatchUI()` method:
153 |
154 | ```typescript
155 | async uploadMyWatchUI() {
156 | const watchUI =
157 | `Text("Capacitor WATCH")
158 | Button("Add One", "inc")`;
159 |
160 | await Watch.updateWatchUI({"watchUI": watchUI});
161 | }
162 | ```
163 |
164 | Will produce this:
165 |
166 |
167 |
168 | ## Communicating with the watch
169 |
170 | This article provides a great summary on the native methods and their implications: https://alexanderweiss.dev/blog/2023-01-18-three-ways-to-communicate-via-watchconnectivity
171 |
172 | On the phone side, you can implement these methods using the Capacitor Background Runner Plugin (https://github.com/ionic-team/capacitor-background-runner). Currently the watch plugin will mainly handle the `didReceiveUserInfo` method, and you can recieve envents from the watch while your app is in the background using the following code in your runner.js:
173 |
174 | ```javascript
175 | addEventListener("WatchConnectivity_didReceiveUserInfo", (args) => {
176 | console.log(args.message.jsCommand);
177 | })
178 | ```
179 |
180 | You can also implment the `runCommand` event listener for foreground procesing:
181 |
182 | ```typescript
183 | Watch.addListener("runCommand", (data: {command: string}) => {
184 | console.log("PHONE got command - " + data.command);
185 | })
186 | ```
187 |
188 | The commands are the 2nd paramter in the `Button()` definition of the watch UI. This can be any string.
189 |
190 | ## Updating watch data
191 |
192 | You can add variables to `Text()` elements by using a `$` variable and updating with the `updateWatchData` command:
193 |
194 | ```
195 | Text("Show my $number")
196 | ```
197 |
198 | This example will update `$number` when executed:
199 |
200 | ```typescript
201 | var stateData = {
202 | number: 0
203 | }
204 |
205 | async function counterIncrement() {
206 | stateData.counter++
207 | await Watch.updateWatchData({"data": convertValuesOfObjectToStringValues(stateData)})
208 | }
209 | ```
210 |
211 | # Persistance on the Watch
212 |
213 | Capacitor Watch will persist the last UI you sent with `updateWatchUI()`. State from `updateWatchData()` is NOT preserved.
214 |
215 |
--------------------------------------------------------------------------------
/img/add-capability.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ionic-team/CapacitorWatch/f5d619cb2d637d8d9c1296ad528c0eda12b09830/img/add-capability.png
--------------------------------------------------------------------------------
/img/capabilities-final.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ionic-team/CapacitorWatch/f5d619cb2d637d8d9c1296ad528c0eda12b09830/img/capabilities-final.png
--------------------------------------------------------------------------------
/img/example-watchui.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ionic-team/CapacitorWatch/f5d619cb2d637d8d9c1296ad528c0eda12b09830/img/example-watchui.png
--------------------------------------------------------------------------------
/img/spm-add-local.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ionic-team/CapacitorWatch/f5d619cb2d637d8d9c1296ad528c0eda12b09830/img/spm-add-local.png
--------------------------------------------------------------------------------
/img/spm-finished.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ionic-team/CapacitorWatch/f5d619cb2d637d8d9c1296ad528c0eda12b09830/img/spm-finished.png
--------------------------------------------------------------------------------
/img/spm-nav-to-package.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ionic-team/CapacitorWatch/f5d619cb2d637d8d9c1296ad528c0eda12b09830/img/spm-nav-to-package.png
--------------------------------------------------------------------------------
/img/spm-pick-target.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ionic-team/CapacitorWatch/f5d619cb2d637d8d9c1296ad528c0eda12b09830/img/spm-pick-target.png
--------------------------------------------------------------------------------
/img/spm-project-dependancies.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ionic-team/CapacitorWatch/f5d619cb2d637d8d9c1296ad528c0eda12b09830/img/spm-project-dependancies.png
--------------------------------------------------------------------------------
/img/target-dropdown.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ionic-team/CapacitorWatch/f5d619cb2d637d8d9c1296ad528c0eda12b09830/img/target-dropdown.png
--------------------------------------------------------------------------------
/img/target-watch.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ionic-team/CapacitorWatch/f5d619cb2d637d8d9c1296ad528c0eda12b09830/img/target-watch.png
--------------------------------------------------------------------------------
/img/watch-main-code.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ionic-team/CapacitorWatch/f5d619cb2d637d8d9c1296ad528c0eda12b09830/img/watch-main-code.png
--------------------------------------------------------------------------------
/img/watch-remote-not.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ionic-team/CapacitorWatch/f5d619cb2d637d8d9c1296ad528c0eda12b09830/img/watch-remote-not.png
--------------------------------------------------------------------------------
/img/watch-sources-added.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ionic-team/CapacitorWatch/f5d619cb2d637d8d9c1296ad528c0eda12b09830/img/watch-sources-added.png
--------------------------------------------------------------------------------
/img/watch-target-options.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ionic-team/CapacitorWatch/f5d619cb2d637d8d9c1296ad528c0eda12b09830/img/watch-target-options.png
--------------------------------------------------------------------------------
/lerna.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "node_modules/lerna/schemas/lerna-schema.json",
3 | "npmClient": "pnpm",
4 | "useNx": true,
5 | "version": "0.1.12",
6 | "command": {
7 | "version": {
8 | "allowBranch": [
9 | "main",
10 | "feat/*",
11 | "fix/*",
12 | "chore/*",
13 | "feat!/*"
14 | ],
15 | "conventionalCommits": true,
16 | "createRelease": "github",
17 | "message": "Release %s",
18 | "tagVersionPrefix": ""
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/nx.json:
--------------------------------------------------------------------------------
1 | {
2 | "tasksRunnerOptions": {
3 | "default": {
4 | "runner": "nx/tasks-runners/default",
5 | "options": {
6 | "cacheableOperations": [
7 | "lint",
8 | "build"
9 | ],
10 | "cacheDirectory": "node_modules/.cache/nx"
11 | }
12 | }
13 | },
14 | "targetDefaults": {
15 | "lint": {
16 | "dependsOn": [
17 | "^lint"
18 | ]
19 | },
20 | "fmt": {
21 | "dependsOn": [
22 | "^fmt"
23 | ]
24 | },
25 | "build": {
26 | "dependsOn": [
27 | "^build"
28 | ],
29 | "outputs": [
30 | "{projectRoot}/dist"
31 | ]
32 | },
33 | "start-web": {
34 | "dependsOn": [
35 | "^start-web"
36 | ]
37 | },
38 | "sync": {
39 | "dependsOn": [
40 | "^sync"
41 | ]
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "root",
3 | "scripts": {
4 | "build": "nx run-many --target=build --parallel=5 && nx run-many --target=build-docs --parallel=5",
5 | "fmt": "nx run-many --target=fmt --parallel=5",
6 | "lint": "nx run-many --target=lint --parallel=5",
7 | "publish:dev": "lerna publish prerelease --conventional-commits --conventional-prerelease --preid dev-$(date +\"%Y%m%dT%H%M%S\") --dist-tag dev --force-publish --no-verify-access --no-changelog --no-git-tag-version --no-push --yes",
8 | "publish:rc": "lerna publish prerelease --conventional-commits --conventional-prerelease --preid rc --dist-tag next --force-publish --no-verify-access --yes",
9 | "publish:release-from-prerelease": "lerna publish --conventional-graduate --conventional-commits --dist-tag latest --force-publish --no-verify-access --yes",
10 | "publish:latest": "lerna publish --conventional-commits --dist-tag latest --force-publish --no-verify-access --yes",
11 | "prepare": "husky install"
12 | },
13 | "devDependencies": {
14 | "@types/node": "^20.4.1",
15 | "husky": "^8.0.3",
16 | "lerna": "^7.1.3",
17 | "typescript": "~5.1.6"
18 | },
19 | "engines": {
20 | "node": ">=18",
21 | "pnpm": ">=7"
22 | },
23 | "dependencies": {
24 | "nx": "^16.5.1"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/packages/capacitor-plugin/.eslintignore:
--------------------------------------------------------------------------------
1 | build
2 | dist
3 | types
4 | android
5 | ios
--------------------------------------------------------------------------------
/packages/capacitor-plugin/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | env: {
4 | node: true,
5 | },
6 | extends: ['plugin:vue/vue3-essential', 'eslint:recommended', '@vue/typescript/recommended'],
7 | parserOptions: {
8 | ecmaVersion: 2020,
9 | },
10 | rules: {
11 | 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
12 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
13 | 'vue/no-deprecated-slot-attribute': 'off',
14 | '@typescript-eslint/no-explicit-any': 'off',
15 | 'no-unused-vars': 'off',
16 | '@typescript-eslint/no-unused-vars': 'off',
17 | },
18 | overrides: [
19 | {
20 | files: ['**/__tests__/*.{j,t}s?(x)', '**/tests/unit/**/*.spec.{j,t}s?(x)'],
21 | env: {
22 | jest: true,
23 | },
24 | },
25 | ],
26 | };
27 |
--------------------------------------------------------------------------------
/packages/capacitor-plugin/.gitignore:
--------------------------------------------------------------------------------
1 | # node files
2 | dist
3 | node_modules
4 |
5 | # iOS files
6 | Pods
7 | Podfile.lock
8 | Build
9 | xcuserdata
10 |
11 | # macOS files
12 | .DS_Store
13 |
14 |
15 |
16 | # Based on Android gitignore template: https://github.com/github/gitignore/blob/HEAD/Android.gitignore
17 |
18 | # Built application files
19 | *.apk
20 | *.ap_
21 |
22 | # Files for the ART/Dalvik VM
23 | *.dex
24 |
25 | # Java class files
26 | *.class
27 |
28 | # Generated files
29 | bin
30 | gen
31 | out
32 |
33 | # Gradle files
34 | .gradle
35 | build
36 |
37 | # Local configuration file (sdk path, etc)
38 | local.properties
39 |
40 | # Proguard folder generated by Eclipse
41 | proguard
42 |
43 | # Log Files
44 | *.log
45 |
46 | # Android Studio Navigation editor temp files
47 | .navigation
48 |
49 | # Android Studio captures folder
50 | captures
51 |
52 | # IntelliJ
53 | *.iml
54 | .idea
55 |
56 | # Keystore files
57 | # Uncomment the following line if you do not want to check your keystore files in.
58 | #*.jks
59 |
60 | # External native build folder generated in Android Studio 2.2 and later
61 | .externalNativeBuild
62 |
--------------------------------------------------------------------------------
/packages/capacitor-plugin/.npmignore:
--------------------------------------------------------------------------------
1 | scripts/
2 | rollup.config.js
3 | tsconfig.json
4 | .prettierignore
5 | .prettierrc.js
6 | .eslintignore
7 | src/
8 | cli-scripts/
--------------------------------------------------------------------------------
/packages/capacitor-plugin/.prettierignore:
--------------------------------------------------------------------------------
1 | build
2 | dist
3 |
--------------------------------------------------------------------------------
/packages/capacitor-plugin/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | ...require('@ionic/prettier-config'),
3 | plugins: ['./node_modules/prettier-plugin-java'],
4 | };
5 |
--------------------------------------------------------------------------------
/packages/capacitor-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.1.12](https://github.com/ionic-team/CapacitorWatch/compare/0.1.11...0.1.12) (2024-04-08)
7 |
8 | **Note:** Version bump only for package @capacitor/watch
9 |
10 |
11 |
12 |
13 |
14 | ## [0.1.11](https://github.com/ionic-team/CapacitorWatch/compare/0.1.10...0.1.11) (2024-04-05)
15 |
16 | **Note:** Version bump only for package @capacitor/watch
17 |
18 |
19 |
20 |
21 |
22 | ## [0.1.10](https://github.com/ionic-team/CapacitorWatch/compare/0.1.9...0.1.10) (2023-11-02)
23 |
24 | **Note:** Version bump only for package @capacitor/watch
25 |
26 |
27 |
28 |
29 |
30 | ## [0.1.9](https://github.com/ionic-team/CapacitorWatch/compare/0.1.8...0.1.9) (2023-11-02)
31 |
32 | **Note:** Version bump only for package @capacitor/watch
33 |
34 |
35 |
36 |
37 |
38 | ## [0.1.8](https://github.com/ionic-team/CapacitorWatch/compare/0.1.7...0.1.8) (2023-08-24)
39 |
40 | **Note:** Version bump only for package @capacitor/watch
41 |
42 |
43 |
44 |
45 |
46 | ## [0.1.7](https://github.com/ionic-team/CapacitorWatch/compare/0.1.6...0.1.7) (2023-08-09)
47 |
48 | **Note:** Version bump only for package @capacitor/watch
49 |
50 |
51 |
52 |
53 |
54 | ## [0.1.6](https://github.com/ionic-team/CapacitorWatch/compare/0.1.5...0.1.6) (2023-08-01)
55 |
56 | **Note:** Version bump only for package @capacitor/watch
57 |
58 |
59 |
60 |
61 |
62 | ## [0.1.5](https://github.com/ionic-team/CapacitorWatch/compare/0.1.4...0.1.5) (2023-07-18)
63 |
64 | **Note:** Version bump only for package @capacitor/watch
65 |
66 |
67 |
68 |
69 |
70 | ## [0.1.4](https://github.com/ionic-team/CapacitorWatch/compare/0.1.3...0.1.4) (2023-07-13)
71 |
72 | **Note:** Version bump only for package @capacitor/watch
73 |
74 |
75 |
76 |
77 |
78 | ## [0.1.3](https://github.com/ionic-team/CapacitorWatch/compare/0.1.2...0.1.3) (2023-07-13)
79 |
80 | **Note:** Version bump only for package @capacitor/watch
81 |
82 |
83 |
84 |
85 |
86 | ## [0.1.2](https://github.com/ionic-team/CapacitorWatch/compare/0.1.1...0.1.2) (2023-07-13)
87 |
88 | **Note:** Version bump only for package @capacitor/watch
89 |
90 |
91 |
92 |
93 |
94 | ## 0.1.1 (2023-07-13)
95 |
96 | **Note:** Version bump only for package @capacitor/watch
97 |
--------------------------------------------------------------------------------
/packages/capacitor-plugin/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | This guide provides instructions for contributing to this Capacitor plugin.
4 |
5 | ## Developing
6 |
7 | ### Local Setup
8 |
9 | 1. Fork and clone the repo.
10 | 1. Install the dependencies.
11 |
12 | ```shell
13 | npm install
14 | ```
15 |
16 | 1. Install SwiftLint if you're on macOS.
17 |
18 | ```shell
19 | brew install swiftlint
20 | ```
21 |
22 | ### Scripts
23 |
24 | #### `npm run build`
25 |
26 | Build the plugin web assets and generate plugin API documentation using [`@capacitor/docgen`](https://github.com/ionic-team/capacitor-docgen).
27 |
28 | It will compile the TypeScript code from `src/` into ESM JavaScript in `dist/esm/`. These files are used in apps with bundlers when your plugin is imported.
29 |
30 | Then, Rollup will bundle the code into a single file at `dist/plugin.js`. This file is used in apps without bundlers by including it as a script in `index.html`.
31 |
32 | #### `npm run verify`
33 |
34 | Build and validate the web and native projects.
35 |
36 | This is useful to run in CI to verify that the plugin builds for all platforms.
37 |
38 | #### `npm run lint` / `npm run fmt`
39 |
40 | Check formatting and code quality, autoformat/autofix if possible.
41 |
42 | This template is integrated with ESLint, Prettier, and SwiftLint. Using these tools is completely optional, but the [Capacitor Community](https://github.com/capacitor-community/) strives to have consistent code style and structure for easier cooperation.
43 |
44 | ## Publishing
45 |
46 | There is a `prepublishOnly` hook in `package.json` which prepares the plugin before publishing, so all you need to do is run:
47 |
48 | ```shell
49 | npm publish
50 | ```
51 |
52 | > **Note**: The [`files`](https://docs.npmjs.com/cli/v7/configuring-npm/package-json#files) array in `package.json` specifies which files get published. If you rename files/directories or add files elsewhere, you may need to update it.
53 |
--------------------------------------------------------------------------------
/packages/capacitor-plugin/CapWatch-Watch-SPM/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | /.build
3 | /Packages
4 | /*.xcodeproj
5 | xcuserdata/
6 | DerivedData/
7 | .swiftpm/config/registries.json
8 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
9 | .netrc
10 |
--------------------------------------------------------------------------------
/packages/capacitor-plugin/CapWatch-Watch-SPM/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version: 5.7
2 |
3 | import PackageDescription
4 |
5 | let package = Package(
6 | name: "iOS-capWatch-watch",
7 | platforms: [.iOS(.v16), .watchOS(.v9)],
8 | products: [
9 | .library(
10 | name: "iOS-capWatch-watch",
11 | targets: ["iOS-capWatch-watch"]),
12 | ],
13 | dependencies: [
14 | ],
15 | targets: [
16 | .target(
17 | name: "iOS-capWatch-watch",
18 | dependencies: []),
19 | .testTarget(
20 | name: "iOS-capWatch-watchTests",
21 | dependencies: ["iOS-capWatch-watch"]),
22 | ]
23 | )
24 |
--------------------------------------------------------------------------------
/packages/capacitor-plugin/CapWatch-Watch-SPM/README.md:
--------------------------------------------------------------------------------
1 | # CapWatch-watch-SPM
2 |
3 | This is the Swift Package fo the watch half of the CapacitorWatch plugin. See the main readme for more details
4 |
5 | ```
6 | import SwiftUI
7 | import WatchConnectivity
8 |
9 | let WATCHUI_KEY = "CAPWATCH_UI"
10 |
11 | @main
12 | struct my_Watch_App: App {
13 | var body: some Scene {
14 | WindowGroup {
15 | ContentView()
16 | .onAppear {
17 | WCSession.default.delegate = WatchViewModel.shared
18 | WCSession.default.activate()
19 | }
20 | }
21 | }
22 | }
23 | ```
24 |
--------------------------------------------------------------------------------
/packages/capacitor-plugin/CapWatch-Watch-SPM/Sources/iOS-capWatch-watch/CapWatchComponentView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CapWatchComponentView.swift
3 | //
4 | //
5 | // Created by Dan Giralté on 2/24/23.
6 | //
7 |
8 | import SwiftUI
9 | import WatchConnectivity
10 |
11 | struct CapWatchComponentView : View, Identifiable {
12 | var id: UUID
13 |
14 | var controlType: String
15 | var controlParams: String
16 | var splitParams: [Substring]
17 |
18 | var viewModel: [String: String]?
19 |
20 | init(_ control: String, _ vm: [String: String]? = nil) {
21 | viewModel = vm
22 |
23 | let getType = control.split(separator: #"(""#)
24 | controlType = String(getType[0])
25 |
26 | let getParams = getType[1].split(separator: #"")"#)
27 | controlParams = String(getParams[0])
28 |
29 | splitParams = controlParams.replacing("\"", with: "").split(separator: ", ")
30 |
31 | id = UUID()
32 | }
33 |
34 | var body: some View {
35 | if controlType == "Text" {
36 | CapWatchText(controlParams, viewModel)
37 | .foregroundColor(.white)
38 | } else if controlType == "Button" {
39 | Button(
40 | action: { WCSession.default.transferUserInfo([COMMAND_KEY:splitParams[1]]) },
41 | label: {
42 | CapWatchText(String(splitParams[0]), viewModel)
43 | }
44 | ).foregroundColor(.white)
45 | }
46 | }
47 | }
48 |
49 |
--------------------------------------------------------------------------------
/packages/capacitor-plugin/CapWatch-Watch-SPM/Sources/iOS-capWatch-watch/CapWatchContentView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CapWatchContentView.swift
3 | //
4 | //
5 | // Created by Dan Giralté on 2/24/23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | public struct CapWatchContentView: View {
11 | @StateObject private var model = WatchViewModel.shared
12 |
13 | public init() { }
14 |
15 | public var body: some View {
16 | VStack {
17 | if model.watchUI == "" {
18 | Button(action: {
19 | WatchViewModel.shared.session.sendMessage(REQUESTUI, replyHandler: nil) { error in
20 | print("UI Delivery failed")
21 | }
22 | }) {
23 | Image(systemName: "globe")
24 | .imageScale(.large)
25 | .foregroundColor(.accentColor)
26 | }
27 | Rectangle().frame(height: 1)
28 | } else {
29 | ScrollView {
30 | CapWatchView()
31 | }
32 | }
33 | }
34 | }
35 | }
36 |
37 | struct ContentView_Previews: PreviewProvider {
38 | static var previews: some View {
39 | CapWatchContentView()
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/packages/capacitor-plugin/CapWatch-Watch-SPM/Sources/iOS-capWatch-watch/CapWatchText.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CapWatchText.swift
3 | //
4 | //
5 | // Created by Dan Giralté on 2/24/23.
6 | //
7 |
8 | import SwiftUI
9 | import RegexBuilder
10 |
11 | struct CapWatchText: View {
12 | var text: String
13 |
14 | init(_ txt: String, _ vm: [String: String]? = nil) {
15 | text = txt
16 |
17 | text = replaceVars(txt, vm)
18 | }
19 |
20 | var body: some View {
21 | Text(text)
22 | }
23 |
24 | func replaceVars(_ text: String, _ vm: [String: String]? = nil) -> String {
25 | if let vm {
26 | let varSearch = Regex {
27 | Anchor.wordBoundary
28 | "$"
29 | ZeroOrMore {
30 | CharacterClass(
31 | ("a"..."z"),
32 | ("A"..."Z"),
33 | ("0"..."9")
34 | )
35 | }
36 | }
37 |
38 | let ranges = text.ranges(of: varSearch)
39 |
40 | var finalText = text
41 |
42 | for range in ranges {
43 | let ph = text[range]
44 |
45 | // this seems dumb?
46 | var varName = ph
47 | varName.removeFirst()
48 |
49 | let varVal = vm[String(varName)] ?? String(varName)
50 |
51 | finalText = finalText.replacing(ph, with: varVal)
52 | }
53 |
54 | return finalText
55 | } else {
56 | return text
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/packages/capacitor-plugin/CapWatch-Watch-SPM/Sources/iOS-capWatch-watch/CapWatchView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CapWatchView.swift
3 | //
4 | //
5 | // Created by Dan Giralté on 2/24/23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct CapWatchView: View {
11 | @StateObject private var viewModel = WatchViewModel.shared
12 |
13 | var views: [CapWatchComponentView] = []
14 |
15 | init() {
16 | // parse viewModel.watchUI
17 | let components = viewModel.watchUI.lines
18 |
19 | // iterate over components and add views to views array
20 | for element in components {
21 | views.append(CapWatchComponentView(String(element), viewModel.viewData))
22 | }
23 | }
24 |
25 | var body: some View {
26 | VStack {
27 | if views.isEmpty {
28 | Text("Waiting for UI from Phone...")
29 | }
30 | ForEach(views) { thisView in
31 | thisView
32 | }
33 | }
34 | .onAppear() {
35 |
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/packages/capacitor-plugin/CapWatch-Watch-SPM/Sources/iOS-capWatch-watch/StringExtensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StringExtensions.swift
3 | //
4 | //
5 | // Created by Dan Giralté on 2/24/23.
6 | //
7 |
8 | extension StringProtocol {
9 | var lines: [SubSequence] { split(whereSeparator: \.isNewline) }
10 | }
11 |
--------------------------------------------------------------------------------
/packages/capacitor-plugin/CapWatch-Watch-SPM/Sources/iOS-capWatch-watch/WatchViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WatchViewModel.swift
3 | //
4 | //
5 | // Created by Dan Giralté on 2/24/23.
6 | //
7 |
8 | import Foundation
9 | import WatchConnectivity
10 | import SwiftUI
11 |
12 | public class WatchViewModel: NSObject, WCSessionDelegate, ObservableObject {
13 | var session: WCSession
14 |
15 | public static var shared = WatchViewModel()
16 |
17 | @AppStorage(SAVEDUI_KEY) var savedUI: String = ""
18 |
19 | @Published var watchUI = "Text(\"Capacitor WATCH\")\nButton(\"Add One\", \"inc\")"
20 | @Published var viewData: [String: String]?
21 |
22 | init(session: WCSession = .default, viewData: [String: String]? = nil) {
23 | self.session = session
24 | self.viewData = viewData
25 |
26 | super.init()
27 |
28 | if savedUI != "" {
29 | self.watchUI = self.savedUI
30 | }
31 | }
32 |
33 | public func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
34 | // apple docs say this won't work on simulator
35 |
36 | if WatchViewModel.shared.watchUI.isEmpty {
37 | let _ = session.transferUserInfo(REQUESTUI)
38 | }
39 | }
40 |
41 | public func session(_ session: WCSession, didReceiveMessage message: [String : Any]) {
42 | handlePhoneMessage(message)
43 | }
44 |
45 | public func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String : Any]) {
46 | handlePhoneMessage(applicationContext)
47 | }
48 |
49 | public func session(_ session: WCSession, didReceiveUserInfo userInfo: [String : Any] = [:]) {
50 | handlePhoneMessage(userInfo)
51 | }
52 |
53 | // required protocol stubs?
54 | //func sessionDidBecomeInactive(_ session: WCSession) {}
55 |
56 | //func sessionDidDeactivate(_ session: WCSession) {}
57 |
58 | func handlePhoneMessage(_ userInfo: [String: Any]) {
59 | DispatchQueue.main.async {
60 | if let newUI = userInfo[UI_KEY] as? String {
61 | self.watchUI = newUI
62 | print("new watchUI: \(self.watchUI)")
63 | self.savedUI = self.watchUI
64 | }
65 |
66 | if let newViewData = userInfo[DATA_KEY] as? [String: String] {
67 | self.viewData = newViewData
68 | }
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/packages/capacitor-plugin/CapWatch-Watch-SPM/Sources/iOS-capWatch-watch/iOS_capWatch_watch.swift:
--------------------------------------------------------------------------------
1 | public let UI_KEY = "watchUI"
2 | public let DATA_KEY = "viewData"
3 | public let SAVEDUI_KEY = "CAPWATCH_UI"
4 | public let REQUESTUI_KEY = "capWatch"
5 | public let REQUESTUI_VALUE = "uiPlease"
6 | public let REQUESTUI = [REQUESTUI_KEY: "uiPlease"]
7 | public let COMMAND_KEY = "jsCommand"
--------------------------------------------------------------------------------
/packages/capacitor-plugin/CapWatch-Watch-SPM/Tests/iOS-capWatch-watchTests/iOS_capWatch_watchTests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 | @testable import iOS_capWatch_watch
3 |
4 | final class iOS_capWatch_watchTests: XCTestCase {
5 | func testExample() throws {
6 | // This is an example of a functional test case.
7 | // Use XCTAssert and related functions to verify your tests produce the correct
8 | // results.
9 | //XCTAssertEqual(iOS_capWatch_watch().text, "Hello, World!")
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/packages/capacitor-plugin/CapacitorWatch.podspec:
--------------------------------------------------------------------------------
1 | require 'json'
2 |
3 | package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
4 |
5 | Pod::Spec.new do |s|
6 | s.name = 'CapacitorWatch'
7 | s.version = package['version']
8 | s.summary = package['description']
9 | s.license = package['license']
10 | s.homepage = package['repository']['url']
11 | s.author = package['author']
12 | s.source = { :git => package['repository']['url'], :tag => s.version.to_s }
13 | s.source_files = 'ios/Plugin/**/*.{swift,h,m,c,cc,mm,cpp}', 'packages/capacitor-plugin/ios/Plugin/**/*.{swift,h,m,c,cc,mm,cpp}'
14 | s.ios.deployment_target = '13.0'
15 | s.dependency 'Capacitor'
16 | s.dependency 'CapacitorBackgroundRunner'
17 | s.swift_version = '5.7'
18 | end
19 |
--------------------------------------------------------------------------------
/packages/capacitor-plugin/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017-present Drifty Co.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/packages/capacitor-plugin/README.md:
--------------------------------------------------------------------------------
1 | # @capacitor/watch
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | ---
15 |
16 | _CapacitorLABS_ - This project is experimental. Support is not provided. Please open issues when needed.
17 |
18 | ---
19 |
20 | The Capacitor Watch plugin allows you to define a UI for a watch in your web code and show it on a paired watch.
21 |
22 | This currently only supports iOS. This guide assumes you've already added iOS to your capcacitor project.
23 |
24 | Also note - all of this will only work with an actual Apple Watch. Simulators don't allow the app<->watch communcation like real devices do.
25 |
26 | ## Install
27 |
28 | Step 1
29 |
30 | Add the watch plugin to your capacitor project, and then open the Xcode project:
31 |
32 | ```bash
33 | npm install @capacitor/watch
34 | npx cap sync
35 | npx cap open ios
36 | ```
37 |
38 | Step 2
39 |
40 | Go to add capabilities:
41 |
42 |
43 |
44 | Add the 'Background Modes' and 'Push Notification' capabilities. Then in the Background Modes options, select 'Background Fetch', 'Remote Notifications', and 'Background Processing'. Your App target should look like this:
45 |
46 |
47 |
48 | Step 3
49 |
50 | Open `AppDelegate.swift` and add `import WatchConnectivity` and `import CapactiorWatch` to the top of the file, and the following code inside the `application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?)` method:
51 |
52 | ```swift
53 | assert(WCSession.isSupported(), "This sample requires Watch Connectivity support!")
54 | WCSession.default.delegate = CapWatchSessionDelegate.shared
55 | WCSession.default.activate()
56 | ```
57 |
58 | Step 4
59 |
60 | Select File -> New -> Target in Xcode, and then the watchOS tab, and 'App':
61 |
62 |
63 |
64 | Click 'Next' then fill out the options like so:
65 |
66 |
67 |
68 | This dialog can be a little confusing, the key thing is your 'Bundle Identifier' must be `[your apps bundle ID].watchapp` for the watch<->app pairing to work. You must also pick SwiftUI for the Interface and Swift for the language. The project should be `App`.
69 |
70 | Step 5
71 |
72 | We're going to add the code that makes Capacitor Watch work in the watch application.
73 |
74 | ---
75 |
76 | If you are using Xcode 15 or beyond you then need to add the Capacitor Watch Swift Package from your node_modules:
77 |
78 | First go to the project package dependancies
79 |
80 |
81 |
82 | Then choose 'Add Local'
83 |
84 |
85 |
86 | Then navigate into the `node_modules/@capacitor/watch/CapWatch-Watch-SPM` folder and click 'Add Package'
87 |
88 |
89 |
90 | Then in the column on the right pick your watch app to be the target and click 'Add Package'
91 |
92 |
93 |
94 | Once this is done your Package Dependancies should look like this:
95 |
96 |
97 |
98 | ---
99 |
100 | With Xcode 14 you will need to go here https://github.com/ionic-team/CapacitorWatch/tree/main/packages/iOS-capWatch-watch/Sources/iOS-capWatch-watch and copy all the files into your Watch project and make sure the target selected is your watch app. It should look like so:
101 |
102 |
103 |
104 | Step 6
105 |
106 | Then open the watch app's 'Main' file which should be `watchappApp.swift`. Add the lines `import WatchConnectivity` and `import iOS_capWatch_watch` above the `@main` statement. Then replace the line that says `ContentView()` with this:
107 |
108 | The finished file should look like this:
109 |
110 | ```swift
111 | import SwiftUI
112 | import WatchConnectivity
113 | import iOS_capWatch_watch
114 |
115 | @main
116 | struct watchddgg_Watch_AppApp: App {
117 | var body: some Scene {
118 | WindowGroup {
119 | CapWatchContentView()
120 | .onAppear {
121 | assert(WCSession.isSupported(), "This sample requires Watch Connectivity support!")
122 | WCSession.default.delegate = WatchViewModel.shared
123 | WCSession.default.activate()
124 | }
125 | }
126 | }
127 | }
128 | ```
129 |
130 | Step 7
131 |
132 | Add the 'Background Modes' capability to the watch app target, and enable 'Remote Notifications':
133 |
134 |
135 |
136 | You should be ready to develop for Capcacitor Watch now!
137 |
138 | ## Development workflow
139 |
140 | You can still develop your iOS app like a normal capacitor app, but getting things to run on the watch requires you to change the target and destination in Xcode. You can change this with the 'Target Dropdown' near the center-top of Xcode:
141 |
142 |
143 |
144 | The right half of this bar lets you pick the destination device or simulator. You will need to pick the watch paired with the phone and then hit the 'Run' button or use the 'cmd+r' run shortcut.
145 |
146 | There can be some challenges in syncing the watch and phone apps. Sometimes you will get an error in the xcode console complaining the compainion app is not present. The best solution in this case is to re-build and re-install the apps on both devices.
147 |
148 | ## Building the watch UI and sending it to the watch
149 |
150 | You will use a long string to define the watch UI. A newline delimits components. Currently this plugin only supports a vertical scroll view of either Text or Button components.
151 |
152 | Once you've defined your UI you can send it to the watch using the `updateWatchUI()` method:
153 |
154 | ```typescript
155 | async uploadMyWatchUI() {
156 | const watchUI =
157 | `Text("Capacitor WATCH")
158 | Button("Add One", "inc")`;
159 |
160 | await Watch.updateWatchUI({"watchUI": watchUI});
161 | }
162 | ```
163 |
164 | Will produce this:
165 |
166 |
167 |
168 | ## Communicating with the watch
169 |
170 | This article provides a great summary on the native methods and their implications: https://alexanderweiss.dev/blog/2023-01-18-three-ways-to-communicate-via-watchconnectivity
171 |
172 | On the phone side, you can implement these methods using the Capacitor Background Runner Plugin (https://github.com/ionic-team/capacitor-background-runner). Currently the watch plugin will mainly handle the `didReceiveUserInfo` method, and you can recieve envents from the watch while your app is in the background using the following code in your runner.js:
173 |
174 | ```javascript
175 | addEventListener("WatchConnectivity_didReceiveUserInfo", (args) => {
176 | console.log(args.message.jsCommand);
177 | })
178 | ```
179 |
180 | You can also implment the `runCommand` event listener for foreground procesing:
181 |
182 | ```typescript
183 | Watch.addListener("runCommand", (data: {command: string}) => {
184 | console.log("PHONE got command - " + data.command);
185 | })
186 | ```
187 |
188 | The commands are the 2nd paramter in the `Button()` definition of the watch UI. This can be any string.
189 |
190 | ## Updating watch data
191 |
192 | You can add variables to `Text()` elements by using a `$` variable and updating with the `updateWatchData` command:
193 |
194 | ```
195 | Text("Show my $number")
196 | ```
197 |
198 | This example will update `$number` when executed:
199 |
200 | ```typescript
201 | var stateData = {
202 | number: 0
203 | }
204 |
205 | async function counterIncrement() {
206 | stateData.counter++
207 | await Watch.updateWatchData({"data": convertValuesOfObjectToStringValues(stateData)})
208 | }
209 | ```
210 |
211 | # Persistance on the Watch
212 |
213 | Capacitor Watch will persist the last UI you sent with `updateWatchUI()`. State from `updateWatchData()` is NOT preserved.
214 |
215 |
216 |
217 | ## Install
218 |
219 | ```bash
220 | npm install @capacitor/watch
221 | npx cap sync
222 | ```
223 |
224 | ## API
225 |
226 |
227 |
228 | * [`addListener('runCommand', ...)`](#addlistenerruncommand-)
229 | * [`updateWatchUI(...)`](#updatewatchui)
230 | * [`updateWatchData(...)`](#updatewatchdata)
231 | * [Interfaces](#interfaces)
232 |
233 |
234 |
235 |
236 |
237 |
238 | ### addListener('runCommand', ...)
239 |
240 | ```typescript
241 | addListener(eventName: 'runCommand', listenerFunc: (data: { command: string; }) => void) => Promise & PluginListenerHandle
242 | ```
243 |
244 | Listen for a command from the watch
245 |
246 | | Param | Type |
247 | | ------------------ | ---------------------------------------------------- |
248 | | **`eventName`** | 'runCommand'
|
249 | | **`listenerFunc`** | (data: { command: string; }) => void
|
250 |
251 | **Returns:** Promise<PluginListenerHandle> & PluginListenerHandle
252 |
253 | --------------------
254 |
255 |
256 | ### updateWatchUI(...)
257 |
258 | ```typescript
259 | updateWatchUI(options: { watchUI: string; }) => Promise
260 | ```
261 |
262 | Replaces the current watch UI with watchUI
263 |
264 | | Param | Type |
265 | | ------------- | --------------------------------- |
266 | | **`options`** | { watchUI: string; }
|
267 |
268 | --------------------
269 |
270 |
271 | ### updateWatchData(...)
272 |
273 | ```typescript
274 | updateWatchData(options: { data: { [key: string]: string; }; }) => Promise
275 | ```
276 |
277 | Updates the watch's state data
278 |
279 | | Param | Type |
280 | | ------------- | -------------------------------------------------- |
281 | | **`options`** | { data: { [key: string]: string; }; }
|
282 |
283 | --------------------
284 |
285 |
286 | ### Interfaces
287 |
288 |
289 | #### PluginListenerHandle
290 |
291 | | Prop | Type |
292 | | ------------ | ----------------------------------------- |
293 | | **`remove`** | () => Promise<void>
|
294 |
295 |
296 |
--------------------------------------------------------------------------------
/packages/capacitor-plugin/android/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/packages/capacitor-plugin/android/build.gradle:
--------------------------------------------------------------------------------
1 | ext {
2 | junitVersion = project.hasProperty('junitVersion') ? rootProject.ext.junitVersion : '4.13.2'
3 | androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.6.1'
4 | androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.1.5'
5 | androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.5.1'
6 | }
7 |
8 | buildscript {
9 | repositories {
10 | google()
11 | mavenCentral()
12 | }
13 | dependencies {
14 | classpath 'com.android.tools.build:gradle:8.0.2'
15 | }
16 | }
17 |
18 | apply plugin: 'com.android.library'
19 |
20 | android {
21 | namespace "io.ionic.watch"
22 | compileSdkVersion project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 33
23 | defaultConfig {
24 | minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 22
25 | targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 33
26 | versionCode 1
27 | versionName "1.0"
28 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
29 | }
30 | buildTypes {
31 | release {
32 | minifyEnabled false
33 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
34 | }
35 | }
36 | lintOptions {
37 | abortOnError false
38 | }
39 | compileOptions {
40 | sourceCompatibility JavaVersion.VERSION_17
41 | targetCompatibility JavaVersion.VERSION_17
42 | }
43 | }
44 |
45 | repositories {
46 | google()
47 | mavenCentral()
48 | }
49 |
50 |
51 | dependencies {
52 | implementation fileTree(dir: 'libs', include: ['*.jar'])
53 | implementation project(':capacitor-android')
54 | implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
55 | testImplementation "junit:junit:$junitVersion"
56 | androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
57 | androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
58 | }
59 |
--------------------------------------------------------------------------------
/packages/capacitor-plugin/android/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | org.gradle.jvmargs=-Xmx1536m
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | # org.gradle.parallel=true
18 |
19 | # AndroidX package structure to make it clearer which packages are bundled with the
20 | # Android operating system, and which are packaged with your app's APK
21 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
22 | android.useAndroidX=true
23 |
24 |
--------------------------------------------------------------------------------
/packages/capacitor-plugin/android/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ionic-team/CapacitorWatch/f5d619cb2d637d8d9c1296ad528c0eda12b09830/packages/capacitor-plugin/android/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/packages/capacitor-plugin/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-all.zip
4 | networkTimeout=10000
5 | zipStoreBase=GRADLE_USER_HOME
6 | zipStorePath=wrapper/dists
7 |
--------------------------------------------------------------------------------
/packages/capacitor-plugin/android/gradlew:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | #
4 | # Copyright © 2015-2021 the original authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | #
21 | # Gradle start up script for POSIX generated by Gradle.
22 | #
23 | # Important for running:
24 | #
25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
26 | # noncompliant, but you have some other compliant shell such as ksh or
27 | # bash, then to run this script, type that shell name before the whole
28 | # command line, like:
29 | #
30 | # ksh Gradle
31 | #
32 | # Busybox and similar reduced shells will NOT work, because this script
33 | # requires all of these POSIX shell features:
34 | # * functions;
35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»;
37 | # * compound commands having a testable exit status, especially «case»;
38 | # * various built-in commands including «command», «set», and «ulimit».
39 | #
40 | # Important for patching:
41 | #
42 | # (2) This script targets any POSIX shell, so it avoids extensions provided
43 | # by Bash, Ksh, etc; in particular arrays are avoided.
44 | #
45 | # The "traditional" practice of packing multiple parameters into a
46 | # space-separated string is a well documented source of bugs and security
47 | # problems, so this is (mostly) avoided, by progressively accumulating
48 | # options in "$@", and eventually passing that to Java.
49 | #
50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
52 | # see the in-line comments for details.
53 | #
54 | # There are tweaks for specific operating systems such as AIX, CygWin,
55 | # Darwin, MinGW, and NonStop.
56 | #
57 | # (3) This script is generated from the Groovy template
58 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
59 | # within the Gradle project.
60 | #
61 | # You can find Gradle at https://github.com/gradle/gradle/.
62 | #
63 | ##############################################################################
64 |
65 | # Attempt to set APP_HOME
66 |
67 | # Resolve links: $0 may be a link
68 | app_path=$0
69 |
70 | # Need this for daisy-chained symlinks.
71 | while
72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
73 | [ -h "$app_path" ]
74 | do
75 | ls=$( ls -ld "$app_path" )
76 | link=${ls#*' -> '}
77 | case $link in #(
78 | /*) app_path=$link ;; #(
79 | *) app_path=$APP_HOME$link ;;
80 | esac
81 | done
82 |
83 | # This is normally unused
84 | # shellcheck disable=SC2034
85 | APP_BASE_NAME=${0##*/}
86 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
87 |
88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
90 |
91 | # Use the maximum available, or set MAX_FD != -1 to use that value.
92 | MAX_FD=maximum
93 |
94 | warn () {
95 | echo "$*"
96 | } >&2
97 |
98 | die () {
99 | echo
100 | echo "$*"
101 | echo
102 | exit 1
103 | } >&2
104 |
105 | # OS specific support (must be 'true' or 'false').
106 | cygwin=false
107 | msys=false
108 | darwin=false
109 | nonstop=false
110 | case "$( uname )" in #(
111 | CYGWIN* ) cygwin=true ;; #(
112 | Darwin* ) darwin=true ;; #(
113 | MSYS* | MINGW* ) msys=true ;; #(
114 | NONSTOP* ) nonstop=true ;;
115 | esac
116 |
117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
118 |
119 |
120 | # Determine the Java command to use to start the JVM.
121 | if [ -n "$JAVA_HOME" ] ; then
122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
123 | # IBM's JDK on AIX uses strange locations for the executables
124 | JAVACMD=$JAVA_HOME/jre/sh/java
125 | else
126 | JAVACMD=$JAVA_HOME/bin/java
127 | fi
128 | if [ ! -x "$JAVACMD" ] ; then
129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
130 |
131 | Please set the JAVA_HOME variable in your environment to match the
132 | location of your Java installation."
133 | fi
134 | else
135 | JAVACMD=java
136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
137 |
138 | Please set the JAVA_HOME variable in your environment to match the
139 | location of your Java installation."
140 | fi
141 |
142 | # Increase the maximum file descriptors if we can.
143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
144 | case $MAX_FD in #(
145 | max*)
146 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
147 | # shellcheck disable=SC3045
148 | MAX_FD=$( ulimit -H -n ) ||
149 | warn "Could not query maximum file descriptor limit"
150 | esac
151 | case $MAX_FD in #(
152 | '' | soft) :;; #(
153 | *)
154 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
155 | # shellcheck disable=SC3045
156 | ulimit -n "$MAX_FD" ||
157 | warn "Could not set maximum file descriptor limit to $MAX_FD"
158 | esac
159 | fi
160 |
161 | # Collect all arguments for the java command, stacking in reverse order:
162 | # * args from the command line
163 | # * the main class name
164 | # * -classpath
165 | # * -D...appname settings
166 | # * --module-path (only if needed)
167 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
168 |
169 | # For Cygwin or MSYS, switch paths to Windows format before running java
170 | if "$cygwin" || "$msys" ; then
171 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
172 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
173 |
174 | JAVACMD=$( cygpath --unix "$JAVACMD" )
175 |
176 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
177 | for arg do
178 | if
179 | case $arg in #(
180 | -*) false ;; # don't mess with options #(
181 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
182 | [ -e "$t" ] ;; #(
183 | *) false ;;
184 | esac
185 | then
186 | arg=$( cygpath --path --ignore --mixed "$arg" )
187 | fi
188 | # Roll the args list around exactly as many times as the number of
189 | # args, so each arg winds up back in the position where it started, but
190 | # possibly modified.
191 | #
192 | # NB: a `for` loop captures its iteration list before it begins, so
193 | # changing the positional parameters here affects neither the number of
194 | # iterations, nor the values presented in `arg`.
195 | shift # remove old arg
196 | set -- "$@" "$arg" # push replacement arg
197 | done
198 | fi
199 |
200 | # Collect all arguments for the java command;
201 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
202 | # shell script including quotes and variable substitutions, so put them in
203 | # double quotes to make sure that they get re-expanded; and
204 | # * put everything else in single quotes, so that it's not re-expanded.
205 |
206 | set -- \
207 | "-Dorg.gradle.appname=$APP_BASE_NAME" \
208 | -classpath "$CLASSPATH" \
209 | org.gradle.wrapper.GradleWrapperMain \
210 | "$@"
211 |
212 | # Stop when "xargs" is not available.
213 | if ! command -v xargs >/dev/null 2>&1
214 | then
215 | die "xargs is not available"
216 | fi
217 |
218 | # Use "xargs" to parse quoted args.
219 | #
220 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed.
221 | #
222 | # In Bash we could simply go:
223 | #
224 | # readarray ARGS < <( xargs -n1 <<<"$var" ) &&
225 | # set -- "${ARGS[@]}" "$@"
226 | #
227 | # but POSIX shell has neither arrays nor command substitution, so instead we
228 | # post-process each arg (as a line of input to sed) to backslash-escape any
229 | # character that might be a shell metacharacter, then use eval to reverse
230 | # that process (while maintaining the separation between arguments), and wrap
231 | # the whole thing up as a single "set" statement.
232 | #
233 | # This will of course break if any of these variables contains a newline or
234 | # an unmatched quote.
235 | #
236 |
237 | eval "set -- $(
238 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
239 | xargs -n1 |
240 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
241 | tr '\n' ' '
242 | )" '"$@"'
243 |
244 | exec "$JAVACMD" "$@"
245 |
--------------------------------------------------------------------------------
/packages/capacitor-plugin/android/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%"=="" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%"=="" set DIRNAME=.
29 | @rem This is normally unused
30 | set APP_BASE_NAME=%~n0
31 | set APP_HOME=%DIRNAME%
32 |
33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
35 |
36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
38 |
39 | @rem Find java.exe
40 | if defined JAVA_HOME goto findJavaFromJavaHome
41 |
42 | set JAVA_EXE=java.exe
43 | %JAVA_EXE% -version >NUL 2>&1
44 | if %ERRORLEVEL% equ 0 goto execute
45 |
46 | echo.
47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
48 | echo.
49 | echo Please set the JAVA_HOME variable in your environment to match the
50 | echo location of your Java installation.
51 |
52 | goto fail
53 |
54 | :findJavaFromJavaHome
55 | set JAVA_HOME=%JAVA_HOME:"=%
56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
57 |
58 | if exist "%JAVA_EXE%" goto execute
59 |
60 | echo.
61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
62 | echo.
63 | echo Please set the JAVA_HOME variable in your environment to match the
64 | echo location of your Java installation.
65 |
66 | goto fail
67 |
68 | :execute
69 | @rem Setup the command line
70 |
71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
72 |
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if %ERRORLEVEL% equ 0 goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | set EXIT_CODE=%ERRORLEVEL%
85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
87 | exit /b %EXIT_CODE%
88 |
89 | :mainEnd
90 | if "%OS%"=="Windows_NT" endlocal
91 |
92 | :omega
93 |
--------------------------------------------------------------------------------
/packages/capacitor-plugin/android/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/packages/capacitor-plugin/android/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':capacitor-android'
2 | project(':capacitor-android').projectDir = new File('../node_modules/@capacitor/android/capacitor')
--------------------------------------------------------------------------------
/packages/capacitor-plugin/android/src/androidTest/java/com/getcapacitor/android/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.getcapacitor.android;
2 |
3 | import static org.junit.Assert.*;
4 |
5 | import android.content.Context;
6 | import androidx.test.ext.junit.runners.AndroidJUnit4;
7 | import androidx.test.platform.app.InstrumentationRegistry;
8 | import org.junit.Test;
9 | import org.junit.runner.RunWith;
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * @see Testing documentation
15 | */
16 | @RunWith(AndroidJUnit4.class)
17 | public class ExampleInstrumentedTest {
18 |
19 | @Test
20 | public void useAppContext() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
23 |
24 | assertEquals("com.getcapacitor.android", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/packages/capacitor-plugin/android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
--------------------------------------------------------------------------------
/packages/capacitor-plugin/android/src/main/java/io/ionic/sslpinning/Watch.java:
--------------------------------------------------------------------------------
1 | package io.ionic.watch;
2 |
3 | import android.util.Log;
4 |
5 | public class Watch {
6 |
7 | public String echo(String value) {
8 | Log.i("Echo", value);
9 | return value;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/packages/capacitor-plugin/android/src/main/java/io/ionic/sslpinning/WatchPlugin.java:
--------------------------------------------------------------------------------
1 | package io.ionic.watch;
2 |
3 | import com.getcapacitor.JSObject;
4 | import com.getcapacitor.Plugin;
5 | import com.getcapacitor.PluginCall;
6 | import com.getcapacitor.PluginMethod;
7 | import com.getcapacitor.annotation.CapacitorPlugin;
8 |
9 | @CapacitorPlugin(name = "Watch")
10 | public class WatchPlugin extends Plugin {
11 |
12 | private Watch implementation = new Watch();
13 |
14 | @PluginMethod
15 | public void echo(PluginCall call) {
16 | String value = call.getString("value");
17 |
18 | JSObject ret = new JSObject();
19 | ret.put("value", implementation.echo(value));
20 | call.resolve(ret);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/packages/capacitor-plugin/android/src/main/res/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ionic-team/CapacitorWatch/f5d619cb2d637d8d9c1296ad528c0eda12b09830/packages/capacitor-plugin/android/src/main/res/.gitkeep
--------------------------------------------------------------------------------
/packages/capacitor-plugin/android/src/test/java/com/getcapacitor/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.getcapacitor;
2 |
3 | import static org.junit.Assert.*;
4 |
5 | import org.junit.Test;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 |
14 | @Test
15 | public void addition_isCorrect() throws Exception {
16 | assertEquals(4, 2 + 2);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/packages/capacitor-plugin/ios/Plugin.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/packages/capacitor-plugin/ios/Plugin.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/packages/capacitor-plugin/ios/Plugin.xcodeproj/xcshareddata/xcschemes/Plugin.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
53 |
54 |
60 |
61 |
67 |
68 |
69 |
70 |
72 |
73 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/packages/capacitor-plugin/ios/Plugin.xcodeproj/xcshareddata/xcschemes/PluginTests.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
53 |
54 |
60 |
61 |
63 |
64 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/packages/capacitor-plugin/ios/Plugin.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/packages/capacitor-plugin/ios/Plugin.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/packages/capacitor-plugin/ios/Plugin/CapWatch/CapWatchDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WatchSessionDelegate.swift
3 | //
4 | //
5 | // Created by Dan Giralté on 2/24/23.
6 | //
7 |
8 | import WatchConnectivity
9 | import CapacitorBackgroundRunner
10 |
11 | public class CapWatchSessionDelegate : NSObject, WCSessionDelegate {
12 | var WATCH_UI = ""
13 |
14 | public static var shared = CapWatchSessionDelegate()
15 |
16 | public func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
17 | print("PHONE WatchDelagate activationDidCompleteWith")
18 | }
19 |
20 | #if os(iOS)
21 |
22 | public func sessionDidBecomeInactive(_ session: WCSession) {
23 |
24 | }
25 |
26 | public func sessionDidDeactivate(_ session: WCSession) {
27 | // dcg - do we want this?
28 | session.activate()
29 | }
30 |
31 | public func session(_ session: WCSession, didReceiveMessage message: [String : Any]) {
32 | var args: [String: Any] = [:]
33 | args["message"] = message
34 |
35 | do {
36 | try BackgroundRunner.shared.dispatchEvent(event: "WatchConnectivity_didReceiveUserInfo", inputArgs: args)
37 | } catch {
38 | print(error)
39 | }
40 |
41 | handleWatchMessage(message)
42 | }
43 |
44 | public func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String: Any]) {
45 | handleWatchMessage(applicationContext)
46 | }
47 |
48 | public func session(_ session: WCSession, didReceiveUserInfo userInfo: [String : Any] = [:]) {
49 | // print("PHONE got didReceiveUserInfo: \(userInfo)")
50 | var args: [String: Any] = [:]
51 | args["userInfo"] = userInfo
52 |
53 | do {
54 | try BackgroundRunner.shared.dispatchEvent(event: "WatchConnectivity_didReceiveUserInfo", inputArgs: args)
55 | } catch {
56 | print(error)
57 | }
58 |
59 | handleWatchMessage(userInfo)
60 | }
61 |
62 | func updateViewData(_ data: [String: String]) {
63 | DispatchQueue.main.async {
64 | let _ = WCSession.default.transferUserInfo([DATA_KEY: data])
65 | }
66 | }
67 |
68 | func sendUI() {
69 | let _ = WCSession.default.transferUserInfo([UI_KEY : WATCH_UI])
70 | }
71 |
72 | func commandToJS(_ command: String) {
73 | NotificationCenter.default.post(name: Notification.Name(COMMAND_KEY),
74 | object: nil,
75 | userInfo: [COMMAND_KEY: command])
76 | }
77 |
78 | func handleWatchMessage(_ userInfo: [String: Any]) {
79 | if let command = userInfo[REQUESTUI_KEY] as? String {
80 | if command == REQUESTUI_VALUE {
81 | sendUI()
82 | }
83 | }
84 |
85 | if let command = userInfo[COMMAND_KEY] as? String {
86 | print("PHONE process: \(command)")
87 | commandToJS(command)
88 | }
89 | }
90 |
91 | #endif
92 | }
93 |
--------------------------------------------------------------------------------
/packages/capacitor-plugin/ios/Plugin/CapWatch/Consts.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Consts.swift
3 | // Plugin
4 | //
5 | // Created by Dan Giralté on 3/16/23.
6 | // Copyright © 2023 Max Lynch. All rights reserved.
7 | //
8 |
9 | public let UI_KEY = "watchUI"
10 | public let DATA_KEY = "viewData"
11 | public let SAVEDUI_KEY = "CAPWATCH_UI"
12 | public let REQUESTUI_KEY = "capWatch"
13 | public let REQUESTUI_VALUE = "uiPlease"
14 | public let REQUESTUI = [REQUESTUI_KEY: REQUESTUI_VALUE]
15 | public let COMMAND_KEY = "jsCommand"
16 |
--------------------------------------------------------------------------------
/packages/capacitor-plugin/ios/Plugin/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | $(CURRENT_PROJECT_VERSION)
21 | NSPrincipalClass
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/packages/capacitor-plugin/ios/Plugin/WatchPlugin.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | //! Project version number for Plugin.
4 | FOUNDATION_EXPORT double PluginVersionNumber;
5 |
6 | //! Project version string for Plugin.
7 | FOUNDATION_EXPORT const unsigned char PluginVersionString[];
8 |
9 | // In this header, you should import all the public headers of your framework using statements like #import
10 |
11 |
--------------------------------------------------------------------------------
/packages/capacitor-plugin/ios/Plugin/WatchPlugin.m:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 |
4 | // Define the plugin using the CAP_PLUGIN Macro, and
5 | // each method the plugin supports using the CAP_PLUGIN_METHOD macro.
6 | CAP_PLUGIN(WatchPlugin, "Watch",
7 | CAP_PLUGIN_METHOD(setWatchUI, CAPPluginReturnPromise);
8 | CAP_PLUGIN_METHOD(updateWatchUI, CAPPluginReturnPromise);
9 | CAP_PLUGIN_METHOD(updateWatchData, CAPPluginReturnPromise);
10 | );
11 |
--------------------------------------------------------------------------------
/packages/capacitor-plugin/ios/Plugin/WatchPlugin.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import Capacitor
3 | import WatchConnectivity
4 |
5 | @objc(WatchPlugin)
6 | public class WatchPlugin: CAPPlugin {
7 |
8 | override public func load() {
9 | NotificationCenter.default.addObserver(self,
10 | selector: #selector(self.handleApplicationActive(notification:)),
11 | name: UIApplication.didBecomeActiveNotification,
12 | object: nil)
13 | NotificationCenter.default.addObserver(self,
14 | selector: #selector(self.handleUrlOpened(notification:)),
15 | name: Notification.Name.capacitorOpenURL,
16 | object: nil)
17 | NotificationCenter.default.addObserver(self,
18 | selector: #selector(self.handleUniversalLink(notification:)),
19 | name: Notification.Name.capacitorOpenUniversalLink,
20 | object: nil)
21 | NotificationCenter.default.addObserver(self,
22 | selector: #selector(self.handleCommandFromWatch(_:)),
23 | name: Notification.Name(COMMAND_KEY),
24 | object: nil)
25 |
26 | }
27 |
28 | @objc func handleApplicationActive(notification: NSNotification) {
29 | assert(WCSession.isSupported(), "This sample requires Watch Connectivity support!")
30 | WCSession.default.delegate = CapWatchSessionDelegate.shared
31 | WCSession.default.activate()
32 | }
33 |
34 | @objc func handleUrlOpened(notification: NSNotification) {
35 |
36 | }
37 |
38 | @objc func handleUniversalLink(notification: NSNotification) {
39 |
40 | }
41 |
42 | @objc func handleCommandFromWatch(_ notification: NSNotification) {
43 | if let command = notification.userInfo![COMMAND_KEY] as? String {
44 | print("WATCH process: \(command)")
45 | notifyListeners("runCommand", data: ["command": command])
46 | }
47 | }
48 |
49 | @objc func updateWatchUI(_ call: CAPPluginCall) {
50 | guard let newUI = call.getString("watchUI") else {
51 | return
52 | }
53 |
54 | CapWatchSessionDelegate.shared.WATCH_UI = newUI
55 | CapWatchSessionDelegate.shared.sendUI()
56 |
57 | call.resolve()
58 | }
59 |
60 | @objc func updateWatchData(_ call: CAPPluginCall) {
61 | guard let newData = call.getObject("data") as? [String: String] else {
62 | return
63 | }
64 |
65 | CapWatchSessionDelegate.shared.updateViewData(newData)
66 | call.resolve()
67 | }
68 |
69 | }
70 |
--------------------------------------------------------------------------------
/packages/capacitor-plugin/ios/PluginTests/CapWatchTests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 | @testable import Plugin
3 |
4 | class CapWatchTests: XCTestCase {
5 | override func setUp() {
6 | super.setUp()
7 | // Put setup code here. This method is called before the invocation of each test method in the class.
8 | }
9 |
10 | override func tearDown() {
11 | // Put teardown code here. This method is called after the invocation of each test method in the class.
12 | super.tearDown()
13 | }
14 |
15 | func testEcho() {
16 | // This is an example of a functional test case for a plugin.
17 | // Use XCTAssert and related functions to verify your tests produce the correct results.
18 |
19 | // let implementation = WatchPlugin()
20 | // let value = "Hello, World!"
21 | // let result = implementation.echo(value)
22 | //
23 | // XCTAssertEqual(value, result)
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/packages/capacitor-plugin/ios/PluginTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/packages/capacitor-plugin/ios/Podfile:
--------------------------------------------------------------------------------
1 | platform :ios, '13.0'
2 |
3 | def capacitor_pods
4 | # Comment the next line if you're not using Swift and don't want to use dynamic frameworks
5 | use_frameworks!
6 | pod 'Capacitor'
7 | pod 'CapacitorCordova'
8 | pod 'CapacitorBackgroundRunner'
9 | end
10 |
11 | target 'Plugin' do
12 | capacitor_pods
13 | end
14 |
15 | target 'PluginTests' do
16 | capacitor_pods
17 | end
18 |
--------------------------------------------------------------------------------
/packages/capacitor-plugin/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@capacitor/watch",
3 | "version": "0.1.12",
4 | "license": "MIT",
5 | "description": "Provide Watch interfaces and communication",
6 | "main": "dist/plugin.cjs.js",
7 | "module": "dist/esm/index.js",
8 | "types": "dist/esm/index.d.ts",
9 | "unpkg": "dist/plugin.js",
10 | "files": [
11 | "android/src/main/",
12 | "android/build.gradle",
13 | "dist/",
14 | "ios/Plugin/",
15 | "CapacitorWatch.podspec",
16 | "CapWatch-Watch-SPM"
17 | ],
18 | "author": "Ionic Team",
19 | "repository": {
20 | "type": "git",
21 | "url": "https://github.com/ionic-team/CapacitorWatch.git"
22 | },
23 | "bugs": {
24 | "url": "https://github.com/ionic-team/capacitorwatch/issues"
25 | },
26 | "keywords": [
27 | "capacitor",
28 | "plugin",
29 | "native"
30 | ],
31 | "scripts": {
32 | "verify": "pnpm run verify:ios && pnpm run verify:android && pnpm run verify:web",
33 | "verify:ios": "cd ios && pod install && xcodebuild -workspace Plugin.xcworkspace -scheme Plugin -destination generic/platform=iOS && cd ..",
34 | "verify:android": "cd android && ./gradlew clean build test && cd ..",
35 | "verify:web": "pnpm run build",
36 | "lint": "pnpm run eslint . --ext ts && pnpm run prettier \"**/*.{css,html,ts,js,java}\" --check && pnpm run swiftlint -- lint",
37 | "fmt": "pnpm run eslint . --ext ts --fix && pnpm run prettier \"**/*.{css,html,ts,js,java}\" --write && pnpm run swiftlint -- --fix --format",
38 | "eslint": "eslint",
39 | "prettier": "prettier",
40 | "swiftlint": "node-swiftlint",
41 | "docgen": "docgen --api WatchPlugin --output-readme README.md --output-json dist/docs.json",
42 | "build": "pnpm run clean && pnpm run docgen && tsc && rollup -c rollup.config.js",
43 | "clean": "rimraf ./dist",
44 | "watch": "tsc --watch",
45 | "prepublishOnly": "pnpm run build"
46 | },
47 | "devDependencies": {
48 | "@capacitor/android": "^5.1.1",
49 | "@capacitor/cli": "^5.1.1",
50 | "@capacitor/core": "^5.1.1",
51 | "@capacitor/docgen": "^0.2.2",
52 | "@capacitor/ios": "^5.1.1",
53 | "@ionic/eslint-config": "^0.3.0",
54 | "@ionic/prettier-config": "^3.1.0",
55 | "@ionic/swiftlint-config": "^1.1.2",
56 | "@typescript-eslint/eslint-plugin": "^5.50.0",
57 | "eslint": "^8.44.0",
58 | "eslint-config-love": "^44.0.0",
59 | "eslint-plugin-import": "^2.25.2",
60 | "eslint-plugin-n": "^15.0.0",
61 | "eslint-plugin-promise": "^6.0.0",
62 | "prettier": "^2.8.8",
63 | "prettier-plugin-java": "~2.2.0",
64 | "rimraf": "^5.0.1",
65 | "rollup": "^2.78.1",
66 | "swiftlint": "^1.0.2",
67 | "typescript": "~5.1.6"
68 | },
69 | "peerDependencies": {
70 | "@capacitor/core": "^5.0.0"
71 | },
72 | "swiftlint": "@ionic/swiftlint-config",
73 | "eslintConfig": {
74 | "extends": "@ionic/eslint-config/recommended"
75 | },
76 | "capacitor": {
77 | "ios": {
78 | "src": "ios"
79 | },
80 | "android": {
81 | "src": "android"
82 | }
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/packages/capacitor-plugin/rollup.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | input: 'dist/esm/index.js',
3 | output: [
4 | {
5 | file: 'dist/plugin.js',
6 | format: 'iife',
7 | name: 'capWatch',
8 | globals: {
9 | '@capacitor/core': 'capacitorExports',
10 | },
11 | sourcemap: true,
12 | inlineDynamicImports: true,
13 | },
14 | {
15 | file: 'dist/plugin.cjs.js',
16 | format: 'cjs',
17 | sourcemap: true,
18 | inlineDynamicImports: true,
19 | },
20 | ],
21 | external: ['@capacitor/core'],
22 | };
23 |
--------------------------------------------------------------------------------
/packages/capacitor-plugin/src/definitions.ts:
--------------------------------------------------------------------------------
1 | ///
2 | import type { PluginListenerHandle } from '@capacitor/core';
3 |
4 | // eslint-disable-next-line @typescript-eslint/no-empty-interface
5 | export interface WatchPlugin {
6 | /**
7 | * Listen for a command from the watch
8 | */
9 | addListener(
10 | eventName: 'runCommand',
11 | listenerFunc: (data: { command: string }) => void
12 | ): Promise & PluginListenerHandle;
13 |
14 | /**
15 | * Replaces the current watch UI with watchUI
16 | */
17 | updateWatchUI(options: { watchUI: string }): Promise;
18 |
19 | /**
20 | * Updates the watch's state data
21 | */
22 | updateWatchData(options: { data: { [key: string]: string } }): Promise;
23 | }
24 |
--------------------------------------------------------------------------------
/packages/capacitor-plugin/src/index.ts:
--------------------------------------------------------------------------------
1 | import { registerPlugin } from '@capacitor/core';
2 |
3 | import type { WatchPlugin } from './definitions';
4 |
5 | const Watch = registerPlugin('Watch', {
6 | web: () => import('./web').then((m) => new m.WatchWeb()),
7 | });
8 |
9 | export * from './definitions';
10 | export { Watch };
11 |
--------------------------------------------------------------------------------
/packages/capacitor-plugin/src/web.ts:
--------------------------------------------------------------------------------
1 | import { WebPlugin } from '@capacitor/core';
2 |
3 | import type { WatchPlugin } from './definitions';
4 |
5 | export class WatchWeb extends WebPlugin implements WatchPlugin {
6 | async setWatchUI(_options: { watchUI: string }): Promise {
7 | return Promise.reject('method not implemented on web');
8 | }
9 |
10 | async updateWatchUI(_options: { watchUI: string }): Promise {
11 | return Promise.reject('method not implemented on web');
12 | }
13 |
14 | async updateWatchData(_options: { data: { [key: string]: string } }): Promise {
15 | return Promise.reject('method not implemented on web');
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/packages/capacitor-plugin/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "allowUnreachableCode": false,
4 | "declaration": true,
5 | "esModuleInterop": true,
6 | "inlineSources": true,
7 | "lib": ["dom", "es2017"],
8 | "module": "esnext",
9 | "moduleResolution": "node",
10 | "noFallthroughCasesInSwitch": true,
11 | "noUnusedLocals": true,
12 | "noUnusedParameters": true,
13 | "outDir": "dist/esm",
14 | "pretty": true,
15 | "sourceMap": true,
16 | "strict": true,
17 | "target": "es2017"
18 | },
19 | "files": ["src/index.ts"]
20 | }
21 |
--------------------------------------------------------------------------------
/packages/capacitor-plugin/typedoc.json:
--------------------------------------------------------------------------------
1 | {
2 | "excludeExternals": true,
3 | "disableSources": true,
4 | "out": "api",
5 | "theme": "markdown",
6 | "name": "capWatch",
7 | "hideBreadcrumbs": true,
8 | "hideGenerator": true,
9 | "plugin": ["typedoc-plugin-markdown"]
10 | }
11 |
--------------------------------------------------------------------------------
/packages/example-app/.browserslistrc:
--------------------------------------------------------------------------------
1 | Chrome >=60
2 | ChromeAndroid >=60
3 | Firefox >=63
4 | Edge >=79
5 | Safari >=13
6 | iOS >=13
7 |
--------------------------------------------------------------------------------
/packages/example-app/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | env: {
4 | node: true,
5 | },
6 | extends: [
7 | "plugin:vue/vue3-essential",
8 | "eslint:recommended",
9 | "@vue/typescript/recommended",
10 | ],
11 | parserOptions: {
12 | ecmaVersion: 2020,
13 | },
14 | rules: {
15 | "no-console": process.env.NODE_ENV === "production" ? "warn" : "off",
16 | "no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off",
17 | "vue/no-deprecated-slot-attribute": "off",
18 | "@typescript-eslint/no-explicit-any": "off",
19 | "no-unused-vars": "off",
20 | "@typescript-eslint/no-unused-vars": "off",
21 | },
22 | overrides: [
23 | {
24 | files: [
25 | "**/__tests__/*.{j,t}s?(x)",
26 | "**/tests/unit/**/*.spec.{j,t}s?(x)",
27 | ],
28 | env: {
29 | jest: true,
30 | },
31 | },
32 | ],
33 | };
34 |
--------------------------------------------------------------------------------
/packages/example-app/.gitignore:
--------------------------------------------------------------------------------
1 | # Specifies intentionally untracked files to ignore when using Git
2 | # http://git-scm.com/docs/gitignore
3 |
4 | *~
5 | *.sw[mnpcod]
6 | .tmp
7 | *.tmp
8 | *.tmp.*
9 | *.sublime-project
10 | *.sublime-workspace
11 | .DS_Store
12 | Thumbs.db
13 | UserInterfaceState.xcuserstate
14 | $RECYCLE.BIN/
15 |
16 | *.log
17 | log.txt
18 | npm-debug.log*
19 |
20 | /.idea
21 | /.ionic
22 | /.sass-cache
23 | /.sourcemaps
24 | /.versions
25 | /.vscode/*
26 | !/.vscode/extensions.json
27 | /coverage
28 | /dist
29 | /node_modules
30 | /platforms
31 | /plugins
32 | /www
33 |
--------------------------------------------------------------------------------
/packages/example-app/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.1.12](https://github.com/ionic-team/CapacitorWatch/compare/0.1.11...0.1.12) (2024-04-08)
7 |
8 | **Note:** Version bump only for package example-app
9 |
10 |
11 |
12 |
13 |
14 | ## [0.1.11](https://github.com/ionic-team/CapacitorWatch/compare/0.1.10...0.1.11) (2024-04-05)
15 |
16 | **Note:** Version bump only for package example-app
17 |
18 |
19 |
20 |
21 |
22 | ## [0.1.10](https://github.com/ionic-team/CapacitorWatch/compare/0.1.9...0.1.10) (2023-11-02)
23 |
24 | **Note:** Version bump only for package example-app
25 |
26 |
27 |
28 |
29 |
30 | ## [0.1.9](https://github.com/ionic-team/CapacitorWatch/compare/0.1.8...0.1.9) (2023-11-02)
31 |
32 | **Note:** Version bump only for package example-app
33 |
34 |
35 |
36 |
37 |
38 | ## [0.1.8](https://github.com/ionic-team/CapacitorWatch/compare/0.1.7...0.1.8) (2023-08-24)
39 |
40 | **Note:** Version bump only for package example-app
41 |
42 |
43 |
44 |
45 |
46 | ## [0.1.7](https://github.com/ionic-team/CapacitorWatch/compare/0.1.6...0.1.7) (2023-08-09)
47 |
48 | **Note:** Version bump only for package example-app
49 |
50 |
51 |
52 |
53 |
54 | ## [0.1.6](https://github.com/ionic-team/CapacitorWatch/compare/0.1.5...0.1.6) (2023-08-01)
55 |
56 | **Note:** Version bump only for package example-app
57 |
58 |
59 |
60 |
61 |
62 | ## [0.1.5](https://github.com/ionic-team/CapacitorWatch/compare/0.1.4...0.1.5) (2023-07-18)
63 |
64 | **Note:** Version bump only for package example-app
65 |
66 |
67 |
68 |
69 |
70 | ## [0.1.4](https://github.com/ionic-team/CapacitorWatch/compare/0.1.3...0.1.4) (2023-07-13)
71 |
72 | **Note:** Version bump only for package example-app
73 |
74 |
75 |
76 |
77 |
78 | ## [0.1.3](https://github.com/ionic-team/CapacitorWatch/compare/0.1.2...0.1.3) (2023-07-13)
79 |
80 | **Note:** Version bump only for package example-app
81 |
82 |
83 |
84 |
85 |
86 | ## [0.1.2](https://github.com/ionic-team/CapacitorWatch/compare/0.1.1...0.1.2) (2023-07-13)
87 |
88 | **Note:** Version bump only for package example-app
89 |
90 |
91 |
92 |
93 |
94 | ## 0.1.1 (2023-07-13)
95 |
96 | **Note:** Version bump only for package example-app
97 |
--------------------------------------------------------------------------------
/packages/example-app/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: ["@vue/cli-plugin-babel/preset"],
3 | };
4 |
--------------------------------------------------------------------------------
/packages/example-app/capacitor.config.ts:
--------------------------------------------------------------------------------
1 | import { CapacitorConfig } from "@capacitor/cli";
2 |
3 | const config: CapacitorConfig = {
4 | appId: "io.ionic.starter",
5 | appName: "example-app",
6 | webDir: "dist",
7 | bundledWebRuntime: false,
8 | };
9 |
10 | export default config;
11 |
--------------------------------------------------------------------------------
/packages/example-app/cypress.json:
--------------------------------------------------------------------------------
1 | {
2 | "pluginsFile": "tests/e2e/plugins/index.js"
3 | }
4 |
--------------------------------------------------------------------------------
/packages/example-app/ionic.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example-app",
3 | "integrations": {
4 | "capacitor": {}
5 | },
6 | "type": "vue"
7 | }
8 |
--------------------------------------------------------------------------------
/packages/example-app/ios/.gitignore:
--------------------------------------------------------------------------------
1 | App/build
2 | App/Pods
3 | App/Podfile.lock
4 | App/App/public
5 | DerivedData
6 | xcuserdata
7 |
8 | # Cordova plugins for Capacitor
9 | capacitor-cordova-ios-plugins
10 |
11 | # Generated Config files
12 | App/App/capacitor.config.json
13 | App/App/config.xml
14 |
--------------------------------------------------------------------------------
/packages/example-app/ios/App/App.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/packages/example-app/ios/App/App.xcodeproj/xcshareddata/xcschemes/App.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
31 |
32 |
42 |
44 |
50 |
51 |
52 |
53 |
59 |
61 |
67 |
68 |
69 |
70 |
72 |
73 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/packages/example-app/ios/App/App.xcodeproj/xcshareddata/xcschemes/example-watch Watch App.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
29 |
35 |
36 |
37 |
38 |
39 |
45 |
46 |
56 |
58 |
64 |
65 |
66 |
67 |
73 |
75 |
81 |
82 |
83 |
84 |
86 |
87 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/packages/example-app/ios/App/App.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/packages/example-app/ios/App/App.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/packages/example-app/ios/App/App/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import WatchConnectivity
3 | import CapacitorWatch
4 | import Capacitor
5 |
6 | @UIApplicationMain
7 | class AppDelegate: UIResponder, UIApplicationDelegate {
8 |
9 | var window: UIWindow?
10 |
11 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
12 | // Override point for customization after application launch.
13 | assert(WCSession.isSupported(), "This sample requires Watch Connectivity support!")
14 | WCSession.default.delegate = CapWatchSessionDelegate.shared
15 | WCSession.default.activate()
16 |
17 | return true
18 | }
19 |
20 | func applicationWillResignActive(_ application: UIApplication) {
21 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
22 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
23 | }
24 |
25 | func applicationDidEnterBackground(_ application: UIApplication) {
26 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
27 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
28 | }
29 |
30 | func applicationWillEnterForeground(_ application: UIApplication) {
31 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
32 | }
33 |
34 | func applicationDidBecomeActive(_ application: UIApplication) {
35 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
36 | }
37 |
38 | func applicationWillTerminate(_ application: UIApplication) {
39 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
40 | }
41 |
42 | func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
43 | // Called when the app was launched with a url. Feel free to add additional processing here,
44 | // but if you want the App API to support tracking app url opens, make sure to keep this call
45 | return ApplicationDelegateProxy.shared.application(app, open: url, options: options)
46 | }
47 |
48 | func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
49 | // Called when the app was launched with an activity, including Universal Links.
50 | // Feel free to add additional processing here, but if you want the App API to support
51 | // tracking app url opens, make sure to keep this call
52 | return ApplicationDelegateProxy.shared.application(application, continue: userActivity, restorationHandler: restorationHandler)
53 | }
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/packages/example-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ionic-team/CapacitorWatch/f5d619cb2d637d8d9c1296ad528c0eda12b09830/packages/example-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@1x.png
--------------------------------------------------------------------------------
/packages/example-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@2x-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ionic-team/CapacitorWatch/f5d619cb2d637d8d9c1296ad528c0eda12b09830/packages/example-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@2x-1.png
--------------------------------------------------------------------------------
/packages/example-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ionic-team/CapacitorWatch/f5d619cb2d637d8d9c1296ad528c0eda12b09830/packages/example-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@2x.png
--------------------------------------------------------------------------------
/packages/example-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ionic-team/CapacitorWatch/f5d619cb2d637d8d9c1296ad528c0eda12b09830/packages/example-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@3x.png
--------------------------------------------------------------------------------
/packages/example-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ionic-team/CapacitorWatch/f5d619cb2d637d8d9c1296ad528c0eda12b09830/packages/example-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@1x.png
--------------------------------------------------------------------------------
/packages/example-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@2x-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ionic-team/CapacitorWatch/f5d619cb2d637d8d9c1296ad528c0eda12b09830/packages/example-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@2x-1.png
--------------------------------------------------------------------------------
/packages/example-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ionic-team/CapacitorWatch/f5d619cb2d637d8d9c1296ad528c0eda12b09830/packages/example-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@2x.png
--------------------------------------------------------------------------------
/packages/example-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ionic-team/CapacitorWatch/f5d619cb2d637d8d9c1296ad528c0eda12b09830/packages/example-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@3x.png
--------------------------------------------------------------------------------
/packages/example-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ionic-team/CapacitorWatch/f5d619cb2d637d8d9c1296ad528c0eda12b09830/packages/example-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@1x.png
--------------------------------------------------------------------------------
/packages/example-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@2x-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ionic-team/CapacitorWatch/f5d619cb2d637d8d9c1296ad528c0eda12b09830/packages/example-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@2x-1.png
--------------------------------------------------------------------------------
/packages/example-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ionic-team/CapacitorWatch/f5d619cb2d637d8d9c1296ad528c0eda12b09830/packages/example-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@2x.png
--------------------------------------------------------------------------------
/packages/example-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ionic-team/CapacitorWatch/f5d619cb2d637d8d9c1296ad528c0eda12b09830/packages/example-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@3x.png
--------------------------------------------------------------------------------
/packages/example-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ionic-team/CapacitorWatch/f5d619cb2d637d8d9c1296ad528c0eda12b09830/packages/example-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png
--------------------------------------------------------------------------------
/packages/example-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-60x60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ionic-team/CapacitorWatch/f5d619cb2d637d8d9c1296ad528c0eda12b09830/packages/example-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-60x60@2x.png
--------------------------------------------------------------------------------
/packages/example-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-60x60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ionic-team/CapacitorWatch/f5d619cb2d637d8d9c1296ad528c0eda12b09830/packages/example-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-60x60@3x.png
--------------------------------------------------------------------------------
/packages/example-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-76x76@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ionic-team/CapacitorWatch/f5d619cb2d637d8d9c1296ad528c0eda12b09830/packages/example-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-76x76@1x.png
--------------------------------------------------------------------------------
/packages/example-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-76x76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ionic-team/CapacitorWatch/f5d619cb2d637d8d9c1296ad528c0eda12b09830/packages/example-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-76x76@2x.png
--------------------------------------------------------------------------------
/packages/example-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-83.5x83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ionic-team/CapacitorWatch/f5d619cb2d637d8d9c1296ad528c0eda12b09830/packages/example-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-83.5x83.5@2x.png
--------------------------------------------------------------------------------
/packages/example-app/ios/App/App/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "20x20",
5 | "idiom" : "iphone",
6 | "filename" : "AppIcon-20x20@2x.png",
7 | "scale" : "2x"
8 | },
9 | {
10 | "size" : "20x20",
11 | "idiom" : "iphone",
12 | "filename" : "AppIcon-20x20@3x.png",
13 | "scale" : "3x"
14 | },
15 | {
16 | "size" : "29x29",
17 | "idiom" : "iphone",
18 | "filename" : "AppIcon-29x29@2x-1.png",
19 | "scale" : "2x"
20 | },
21 | {
22 | "size" : "29x29",
23 | "idiom" : "iphone",
24 | "filename" : "AppIcon-29x29@3x.png",
25 | "scale" : "3x"
26 | },
27 | {
28 | "size" : "40x40",
29 | "idiom" : "iphone",
30 | "filename" : "AppIcon-40x40@2x.png",
31 | "scale" : "2x"
32 | },
33 | {
34 | "size" : "40x40",
35 | "idiom" : "iphone",
36 | "filename" : "AppIcon-40x40@3x.png",
37 | "scale" : "3x"
38 | },
39 | {
40 | "size" : "60x60",
41 | "idiom" : "iphone",
42 | "filename" : "AppIcon-60x60@2x.png",
43 | "scale" : "2x"
44 | },
45 | {
46 | "size" : "60x60",
47 | "idiom" : "iphone",
48 | "filename" : "AppIcon-60x60@3x.png",
49 | "scale" : "3x"
50 | },
51 | {
52 | "size" : "20x20",
53 | "idiom" : "ipad",
54 | "filename" : "AppIcon-20x20@1x.png",
55 | "scale" : "1x"
56 | },
57 | {
58 | "size" : "20x20",
59 | "idiom" : "ipad",
60 | "filename" : "AppIcon-20x20@2x-1.png",
61 | "scale" : "2x"
62 | },
63 | {
64 | "size" : "29x29",
65 | "idiom" : "ipad",
66 | "filename" : "AppIcon-29x29@1x.png",
67 | "scale" : "1x"
68 | },
69 | {
70 | "size" : "29x29",
71 | "idiom" : "ipad",
72 | "filename" : "AppIcon-29x29@2x.png",
73 | "scale" : "2x"
74 | },
75 | {
76 | "size" : "40x40",
77 | "idiom" : "ipad",
78 | "filename" : "AppIcon-40x40@1x.png",
79 | "scale" : "1x"
80 | },
81 | {
82 | "size" : "40x40",
83 | "idiom" : "ipad",
84 | "filename" : "AppIcon-40x40@2x-1.png",
85 | "scale" : "2x"
86 | },
87 | {
88 | "size" : "76x76",
89 | "idiom" : "ipad",
90 | "filename" : "AppIcon-76x76@1x.png",
91 | "scale" : "1x"
92 | },
93 | {
94 | "size" : "76x76",
95 | "idiom" : "ipad",
96 | "filename" : "AppIcon-76x76@2x.png",
97 | "scale" : "2x"
98 | },
99 | {
100 | "size" : "83.5x83.5",
101 | "idiom" : "ipad",
102 | "filename" : "AppIcon-83.5x83.5@2x.png",
103 | "scale" : "2x"
104 | },
105 | {
106 | "size" : "1024x1024",
107 | "idiom" : "ios-marketing",
108 | "filename" : "AppIcon-512@2x.png",
109 | "scale" : "1x"
110 | }
111 | ],
112 | "info" : {
113 | "version" : 1,
114 | "author" : "xcode"
115 | }
116 | }
--------------------------------------------------------------------------------
/packages/example-app/ios/App/App/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/packages/example-app/ios/App/App/Assets.xcassets/Splash.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "splash-2732x2732-2.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "splash-2732x2732-1.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "splash-2732x2732.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/packages/example-app/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ionic-team/CapacitorWatch/f5d619cb2d637d8d9c1296ad528c0eda12b09830/packages/example-app/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-1.png
--------------------------------------------------------------------------------
/packages/example-app/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ionic-team/CapacitorWatch/f5d619cb2d637d8d9c1296ad528c0eda12b09830/packages/example-app/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-2.png
--------------------------------------------------------------------------------
/packages/example-app/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ionic-team/CapacitorWatch/f5d619cb2d637d8d9c1296ad528c0eda12b09830/packages/example-app/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732.png
--------------------------------------------------------------------------------
/packages/example-app/ios/App/App/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/packages/example-app/ios/App/App/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/packages/example-app/ios/App/App/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | example-app
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | $(MARKETING_VERSION)
21 | CFBundleVersion
22 | $(CURRENT_PROJECT_VERSION)
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UIRequiredDeviceCapabilities
30 |
31 | armv7
32 |
33 | UISupportedInterfaceOrientations
34 |
35 | UIInterfaceOrientationPortrait
36 | UIInterfaceOrientationLandscapeLeft
37 | UIInterfaceOrientationLandscapeRight
38 |
39 | UISupportedInterfaceOrientations~ipad
40 |
41 | UIInterfaceOrientationPortrait
42 | UIInterfaceOrientationPortraitUpsideDown
43 | UIInterfaceOrientationLandscapeLeft
44 | UIInterfaceOrientationLandscapeRight
45 |
46 | UIViewControllerBasedStatusBarAppearance
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/packages/example-app/ios/App/Podfile:
--------------------------------------------------------------------------------
1 | require_relative '../../node_modules/@capacitor/ios/scripts/pods_helpers'
2 |
3 | platform :ios, '13.0'
4 | use_frameworks!
5 |
6 | # workaround to avoid Xcode caching of Pods that requires
7 | # Product -> Clean Build Folder after new Cordova plugins installed
8 | # Requires CocoaPods 1.6 or newer
9 | install! 'cocoapods', :disable_input_output_paths => true
10 |
11 | def capacitor_pods
12 | pod 'Capacitor', :path => '../../../../node_modules/.pnpm/@capacitor+ios@4.7.1_@capacitor+core@4.7.1/node_modules/@capacitor/ios'
13 | pod 'CapacitorCordova', :path => '../../../../node_modules/.pnpm/@capacitor+ios@4.7.1_@capacitor+core@4.7.1/node_modules/@capacitor/ios'
14 | pod 'CapacitorApp', :path => '../../../../node_modules/.pnpm/@capacitor+app@4.1.1_@capacitor+core@4.7.1/node_modules/@capacitor/app'
15 | pod 'CapacitorHaptics', :path => '../../../../node_modules/.pnpm/@capacitor+haptics@4.1.0_@capacitor+core@4.7.1/node_modules/@capacitor/haptics'
16 | pod 'CapacitorKeyboard', :path => '../../../../node_modules/.pnpm/@capacitor+keyboard@4.1.1_@capacitor+core@4.7.1/node_modules/@capacitor/keyboard'
17 | pod 'CapacitorStatusBar', :path => '../../../../node_modules/.pnpm/@capacitor+status-bar@4.1.1_@capacitor+core@4.7.1/node_modules/@capacitor/status-bar'
18 | pod 'CapacitorWatch', :path => '../../../capacitor-plugin'
19 | end
20 |
21 | target 'App' do
22 | capacitor_pods
23 | # Add your Pods here
24 | end
25 |
26 | post_install do |installer|
27 | assertDeploymentTarget(installer)
28 | end
29 |
--------------------------------------------------------------------------------
/packages/example-app/ios/App/example-watch Watch App/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/packages/example-app/ios/App/example-watch Watch App/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "platform" : "watchos",
6 | "size" : "1024x1024"
7 | }
8 | ],
9 | "info" : {
10 | "author" : "xcode",
11 | "version" : 1
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/packages/example-app/ios/App/example-watch Watch App/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/packages/example-app/ios/App/example-watch Watch App/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/packages/example-app/ios/App/example-watch Watch App/example_watchApp.swift:
--------------------------------------------------------------------------------
1 | //
2 | // example_watchApp.swift
3 | // example-watch Watch App
4 | //
5 | // Created by Dan Giralté on 3/17/23.
6 | //
7 |
8 | import SwiftUI
9 | import WatchConnectivity
10 |
11 | @main
12 | struct example_watch_Watch_AppApp: App {
13 | var body: some Scene {
14 | WindowGroup {
15 | CapWatchContentView()
16 | .onAppear {
17 | assert(WCSession.isSupported(), "This sample requires Watch Connectivity support!")
18 | WCSession.default.delegate = WatchViewModel.shared
19 | WCSession.default.activate()
20 | }
21 | }
22 | }
23 | }
24 |
25 |
--------------------------------------------------------------------------------
/packages/example-app/ios/App/example-watch Watch App/faux-spm/CapWatchComponentView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CapWatchComponentView.swift
3 | //
4 | //
5 | // Created by Dan Giralté on 2/24/23.
6 | //
7 |
8 | import SwiftUI
9 | import WatchConnectivity
10 |
11 | struct CapWatchComponentView : View, Identifiable {
12 | var id: UUID
13 |
14 | var controlType: String
15 | var controlParams: String
16 | var splitParams: [Substring]
17 |
18 | var viewModel: [String: String]?
19 |
20 | init(_ control: String, _ vm: [String: String]? = nil) {
21 | viewModel = vm
22 |
23 | let getType = control.split(separator: #"(""#)
24 | controlType = String(getType[0])
25 |
26 | let getParams = getType[1].split(separator: #"")"#)
27 | controlParams = String(getParams[0])
28 |
29 | splitParams = controlParams.replacing("\"", with: "").split(separator: ", ")
30 |
31 | id = UUID()
32 | }
33 |
34 | var body: some View {
35 | if controlType == "Text" {
36 | CapWatchText(controlParams, viewModel)
37 | .foregroundColor(.white)
38 | } else if controlType == "Button" {
39 | Button(splitParams[0]) {
40 | WCSession.default.transferUserInfo([COMMAND_KEY: "\(splitParams[1])"])
41 | }
42 | .foregroundColor(.white)
43 | }
44 | }
45 | }
46 |
47 |
48 |
--------------------------------------------------------------------------------
/packages/example-app/ios/App/example-watch Watch App/faux-spm/CapWatchContentView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CapWatchContentView.swift
3 | //
4 | //
5 | // Created by Dan Giralté on 2/24/23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct CapWatchContentView: View {
11 | @StateObject private var model = WatchViewModel.shared
12 |
13 | var body: some View {
14 | VStack {
15 | if model.watchUI == "" {
16 | Button(action: {
17 | WatchViewModel.shared.session.sendMessage(REQUESTUI, replyHandler: nil) { error in
18 | print("UI Delivery failed")
19 | }
20 | }) {
21 | Image(systemName: "globe")
22 | .imageScale(.large)
23 | .foregroundColor(.accentColor)
24 | }
25 | Rectangle().frame(height: 1)
26 | } else {
27 | ScrollView {
28 | CapWatchView()
29 | }
30 | }
31 | }
32 | }
33 | }
34 |
35 | struct ContentView_Previews: PreviewProvider {
36 | static var previews: some View {
37 | CapWatchContentView()
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/packages/example-app/ios/App/example-watch Watch App/faux-spm/CapWatchText.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CapWatchText.swift
3 | //
4 | //
5 | // Created by Dan Giralté on 2/24/23.
6 | //
7 |
8 | import SwiftUI
9 | import RegexBuilder
10 |
11 | struct CapWatchText: View {
12 | var text: String
13 |
14 | init(_ txt: String, _ vm: [String: String]? = nil) {
15 | text = txt
16 |
17 | text = replaceVars(txt, vm)
18 | }
19 |
20 | var body: some View {
21 | Text(text)
22 | }
23 |
24 | func replaceVars(_ text: String, _ vm: [String: String]? = nil) -> String {
25 | if let vm {
26 | let varSearch = Regex {
27 | Anchor.wordBoundary
28 | "$"
29 | ZeroOrMore {
30 | CharacterClass(
31 | ("a"..."z"),
32 | ("A"..."Z"),
33 | ("0"..."9")
34 | )
35 | }
36 | }
37 |
38 | let ranges = text.ranges(of: varSearch)
39 |
40 | var finalText = text
41 |
42 | for range in ranges {
43 | let ph = text[range]
44 |
45 | // this seems dumb?
46 | var varName = ph
47 | varName.removeFirst()
48 |
49 | let varVal = vm[String(varName)] ?? String(varName)
50 |
51 | finalText = finalText.replacing(ph, with: varVal)
52 | }
53 |
54 | return finalText
55 | } else {
56 | return text
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/packages/example-app/ios/App/example-watch Watch App/faux-spm/CapWatchView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CapWatchView.swift
3 | //
4 | //
5 | // Created by Dan Giralté on 2/24/23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct CapWatchView: View {
11 | @StateObject private var viewModel = WatchViewModel.shared
12 |
13 | var views: [CapWatchComponentView] = []
14 |
15 | init() {
16 | // parse viewModel.watchUI
17 | let components = viewModel.watchUI.lines
18 |
19 | // iterate over components and add views to views array
20 | for element in components {
21 | views.append(CapWatchComponentView(String(element), viewModel.viewData))
22 | }
23 | }
24 |
25 | var body: some View {
26 | VStack {
27 | if views.isEmpty {
28 | Text("Waiting for UI from Phone...")
29 | }
30 | ForEach(views) { thisView in
31 | thisView
32 | }
33 | }
34 | .onAppear() {
35 |
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/packages/example-app/ios/App/example-watch Watch App/faux-spm/StringExtensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StringExtensions.swift
3 | //
4 | //
5 | // Created by Dan Giralté on 2/24/23.
6 | //
7 |
8 | extension StringProtocol {
9 | var lines: [SubSequence] { split(whereSeparator: \.isNewline) }
10 | }
11 |
--------------------------------------------------------------------------------
/packages/example-app/ios/App/example-watch Watch App/faux-spm/WatchViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WatchViewModel.swift
3 | //
4 | //
5 | // Created by Dan Giralté on 2/24/23.
6 | //
7 |
8 | import Foundation
9 | import WatchConnectivity
10 | import SwiftUI
11 |
12 | class WatchViewModel: NSObject, WCSessionDelegate, ObservableObject {
13 | var session: WCSession
14 |
15 | static var shared = WatchViewModel()
16 |
17 | @AppStorage(SAVEDUI_KEY) var savedUI: String = ""
18 |
19 | @Published var watchUI = ""
20 | @Published var viewData: [String: String]?
21 |
22 | init(session: WCSession = .default, viewData: [String: String]? = nil) {
23 | self.session = session
24 | self.viewData = viewData
25 |
26 | super.init()
27 |
28 | if savedUI != "" {
29 | self.watchUI = self.savedUI
30 | }
31 | }
32 |
33 | func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
34 | // apple docs say this won't work on simulator
35 |
36 | if WatchViewModel.shared.watchUI.isEmpty {
37 | let _ = session.transferUserInfo(REQUESTUI)
38 | }
39 | }
40 |
41 | func session(_ session: WCSession, didReceiveMessage message: [String : Any]) {
42 | handlePhoneMessage(message)
43 | }
44 |
45 | func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String : Any]) {
46 | handlePhoneMessage(applicationContext)
47 | }
48 |
49 | func session(_ session: WCSession, didReceiveUserInfo userInfo: [String : Any] = [:]) {
50 | handlePhoneMessage(userInfo)
51 | }
52 |
53 | // required protocol stubs?
54 | func sessionDidBecomeInactive(_ session: WCSession) {
55 |
56 | }
57 |
58 | func sessionDidDeactivate(_ session: WCSession) {
59 |
60 | }
61 |
62 | func handlePhoneMessage(_ userInfo: [String Any] {
63 | DispatchQueue.main.async {
64 | if let newUI = userInfo[UI_KEY] as? String {
65 | self.watchUI = newUI
66 | print("new watchUI: \(self.watchUI)")
67 | self.savedUI = self.watchUI
68 | }
69 |
70 | if let newViewData = userInfo[DATA_KEY] as? [String: String] {
71 | self.viewData = newViewData
72 | }
73 | }
74 | })
75 | }
76 |
--------------------------------------------------------------------------------
/packages/example-app/ios/App/example-watch Watch App/faux-spm/iOS_capWatch_watch.swift:
--------------------------------------------------------------------------------
1 | public let UI_KEY = "watchUI"
2 | public let DATA_KEY = "viewData"
3 | public let SAVEDUI_KEY = "CAPWATCH_UI"
4 | public let REQUESTUI_KEY = "capWatch"
5 | public let REQUESTUI_VALUE = "uiPlease"
6 | public let REQUESTUI = [REQUESTUI_KEY: "uiPlease"]
7 | public let COMMAND_KEY = "jsCommand"
8 |
--------------------------------------------------------------------------------
/packages/example-app/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | preset: "@vue/cli-plugin-unit-jest/presets/typescript-and-babel",
3 | transformIgnorePatterns: [
4 | "/node_modules/(?!@ionic/vue|@ionic/vue-router|@ionic/core|@stencil/core|ionicons)",
5 | ],
6 | };
7 |
--------------------------------------------------------------------------------
/packages/example-app/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example-app",
3 | "version": "0.1.12",
4 | "private": true,
5 | "scripts": {
6 | "serve": "vue-cli-service serve",
7 | "build": "vue-cli-service build",
8 | "test:unit": "vue-cli-service test:unit",
9 | "test:e2e": "vue-cli-service test:e2e",
10 | "lint": "vue-cli-service lint"
11 | },
12 | "dependencies": {
13 | "@capacitor/app": "5.0.6",
14 | "@capacitor/core": "5.1.1",
15 | "@capacitor/haptics": "5.0.6",
16 | "@capacitor/ios": "^5.1.1",
17 | "@capacitor/keyboard": "5.0.6",
18 | "@capacitor/status-bar": "5.0.6",
19 | "@capacitor/watch": "workspace:*",
20 | "@ionic/vue": "^7.1.3",
21 | "@ionic/vue-router": "^7.1.3",
22 | "core-js": "^3.31.1",
23 | "ionicons": "^7.1.2",
24 | "vue": "^3.3.4",
25 | "vue-router": "^4.2.4"
26 | },
27 | "devDependencies": {
28 | "@babel/core": "~7.22.9",
29 | "@capacitor/cli": "5.1.1",
30 | "@types/jest": "^29.5.3",
31 | "@types/node": "^20.4.1",
32 | "@typescript-eslint/eslint-plugin": "^6.0.0",
33 | "@typescript-eslint/parser": "^6.0.0",
34 | "@vue/cli-plugin-babel": "~5.0.8",
35 | "@vue/cli-plugin-e2e-cypress": "~5.0.8",
36 | "@vue/cli-plugin-eslint": "~5.0.8",
37 | "@vue/cli-plugin-router": "~5.0.8",
38 | "@vue/cli-plugin-typescript": "~5.0.8",
39 | "@vue/cli-plugin-unit-jest": "~5.0.8",
40 | "@vue/cli-service": "~5.0.8",
41 | "@vue/eslint-config-typescript": "^11.0.3",
42 | "@vue/test-utils": "^2.4.0",
43 | "@vue/vue3-jest": "^29.2.4",
44 | "babel-jest": "^29.6.1",
45 | "cypress": "^12.17.1",
46 | "eslint": "^8.44.0",
47 | "eslint-plugin-vue": "^9.15.1",
48 | "jest": "^29.6.1",
49 | "ts-jest": "^29.1.1",
50 | "typescript": "^5.1.6"
51 | },
52 | "description": "An Ionic project"
53 | }
54 |
--------------------------------------------------------------------------------
/packages/example-app/public/assets/icon/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ionic-team/CapacitorWatch/f5d619cb2d637d8d9c1296ad528c0eda12b09830/packages/example-app/public/assets/icon/favicon.png
--------------------------------------------------------------------------------
/packages/example-app/public/assets/icon/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ionic-team/CapacitorWatch/f5d619cb2d637d8d9c1296ad528c0eda12b09830/packages/example-app/public/assets/icon/icon.png
--------------------------------------------------------------------------------
/packages/example-app/public/assets/shapes.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/packages/example-app/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Ionic App
6 |
7 |
8 |
9 |
10 |
14 |
15 |
16 |
17 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/packages/example-app/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
10 |
--------------------------------------------------------------------------------
/packages/example-app/src/main.ts:
--------------------------------------------------------------------------------
1 | import { createApp } from "vue";
2 | import App from "./App.vue";
3 | import router from "./router";
4 |
5 | import { IonicVue } from "@ionic/vue";
6 |
7 | /* Core CSS required for Ionic components to work properly */
8 | import "@ionic/vue/css/core.css";
9 |
10 | /* Basic CSS for apps built with Ionic */
11 | import "@ionic/vue/css/normalize.css";
12 | import "@ionic/vue/css/structure.css";
13 | import "@ionic/vue/css/typography.css";
14 |
15 | /* Optional CSS utils that can be commented out */
16 | import "@ionic/vue/css/padding.css";
17 | import "@ionic/vue/css/float-elements.css";
18 | import "@ionic/vue/css/text-alignment.css";
19 | import "@ionic/vue/css/text-transformation.css";
20 | import "@ionic/vue/css/flex-utils.css";
21 | import "@ionic/vue/css/display.css";
22 |
23 | /* Theme variables */
24 | import "./theme/variables.css";
25 |
26 | const app = createApp(App).use(IonicVue).use(router);
27 |
28 | router.isReady().then(() => {
29 | app.mount("#app");
30 | });
31 |
--------------------------------------------------------------------------------
/packages/example-app/src/router/index.ts:
--------------------------------------------------------------------------------
1 | import { createRouter, createWebHistory } from "@ionic/vue-router";
2 | import { RouteRecordRaw } from "vue-router";
3 | import HomePage from "../views/HomePage.vue";
4 |
5 | const routes: Array = [
6 | {
7 | path: "/",
8 | redirect: "/home",
9 | },
10 | {
11 | path: "/home",
12 | name: "Home",
13 | component: HomePage,
14 | },
15 | ];
16 |
17 | const router = createRouter({
18 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment
19 | // @ts-ignore
20 | history: createWebHistory(process.env.BASE_URL),
21 | routes,
22 | });
23 |
24 | export default router;
25 |
--------------------------------------------------------------------------------
/packages/example-app/src/shims-vue.d.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | declare module "*.vue" {
3 | import type { DefineComponent } from "vue";
4 | const component: DefineComponent<{}, {}, any>;
5 | export default component;
6 | }
7 |
--------------------------------------------------------------------------------
/packages/example-app/src/theme/variables.css:
--------------------------------------------------------------------------------
1 | /* Ionic Variables and Theming. For more info, please see:
2 | http://ionicframework.com/docs/theming/ */
3 |
4 | /** Ionic CSS Variables **/
5 | :root {
6 | /** primary **/
7 | --ion-color-primary: #3880ff;
8 | --ion-color-primary-rgb: 56, 128, 255;
9 | --ion-color-primary-contrast: #ffffff;
10 | --ion-color-primary-contrast-rgb: 255, 255, 255;
11 | --ion-color-primary-shade: #3171e0;
12 | --ion-color-primary-tint: #4c8dff;
13 |
14 | /** secondary **/
15 | --ion-color-secondary: #3dc2ff;
16 | --ion-color-secondary-rgb: 61, 194, 255;
17 | --ion-color-secondary-contrast: #ffffff;
18 | --ion-color-secondary-contrast-rgb: 255, 255, 255;
19 | --ion-color-secondary-shade: #36abe0;
20 | --ion-color-secondary-tint: #50c8ff;
21 |
22 | /** tertiary **/
23 | --ion-color-tertiary: #5260ff;
24 | --ion-color-tertiary-rgb: 82, 96, 255;
25 | --ion-color-tertiary-contrast: #ffffff;
26 | --ion-color-tertiary-contrast-rgb: 255, 255, 255;
27 | --ion-color-tertiary-shade: #4854e0;
28 | --ion-color-tertiary-tint: #6370ff;
29 |
30 | /** success **/
31 | --ion-color-success: #2dd36f;
32 | --ion-color-success-rgb: 45, 211, 111;
33 | --ion-color-success-contrast: #ffffff;
34 | --ion-color-success-contrast-rgb: 255, 255, 255;
35 | --ion-color-success-shade: #28ba62;
36 | --ion-color-success-tint: #42d77d;
37 |
38 | /** warning **/
39 | --ion-color-warning: #ffc409;
40 | --ion-color-warning-rgb: 255, 196, 9;
41 | --ion-color-warning-contrast: #000000;
42 | --ion-color-warning-contrast-rgb: 0, 0, 0;
43 | --ion-color-warning-shade: #e0ac08;
44 | --ion-color-warning-tint: #ffca22;
45 |
46 | /** danger **/
47 | --ion-color-danger: #eb445a;
48 | --ion-color-danger-rgb: 235, 68, 90;
49 | --ion-color-danger-contrast: #ffffff;
50 | --ion-color-danger-contrast-rgb: 255, 255, 255;
51 | --ion-color-danger-shade: #cf3c4f;
52 | --ion-color-danger-tint: #ed576b;
53 |
54 | /** dark **/
55 | --ion-color-dark: #222428;
56 | --ion-color-dark-rgb: 34, 36, 40;
57 | --ion-color-dark-contrast: #ffffff;
58 | --ion-color-dark-contrast-rgb: 255, 255, 255;
59 | --ion-color-dark-shade: #1e2023;
60 | --ion-color-dark-tint: #383a3e;
61 |
62 | /** medium **/
63 | --ion-color-medium: #92949c;
64 | --ion-color-medium-rgb: 146, 148, 156;
65 | --ion-color-medium-contrast: #ffffff;
66 | --ion-color-medium-contrast-rgb: 255, 255, 255;
67 | --ion-color-medium-shade: #808289;
68 | --ion-color-medium-tint: #9d9fa6;
69 |
70 | /** light **/
71 | --ion-color-light: #f4f5f8;
72 | --ion-color-light-rgb: 244, 245, 248;
73 | --ion-color-light-contrast: #000000;
74 | --ion-color-light-contrast-rgb: 0, 0, 0;
75 | --ion-color-light-shade: #d7d8da;
76 | --ion-color-light-tint: #f5f6f9;
77 | }
78 |
79 | @media (prefers-color-scheme: dark) {
80 | /*
81 | * Dark Colors
82 | * -------------------------------------------
83 | */
84 |
85 | body {
86 | --ion-color-primary: #428cff;
87 | --ion-color-primary-rgb: 66, 140, 255;
88 | --ion-color-primary-contrast: #ffffff;
89 | --ion-color-primary-contrast-rgb: 255, 255, 255;
90 | --ion-color-primary-shade: #3a7be0;
91 | --ion-color-primary-tint: #5598ff;
92 |
93 | --ion-color-secondary: #50c8ff;
94 | --ion-color-secondary-rgb: 80, 200, 255;
95 | --ion-color-secondary-contrast: #ffffff;
96 | --ion-color-secondary-contrast-rgb: 255, 255, 255;
97 | --ion-color-secondary-shade: #46b0e0;
98 | --ion-color-secondary-tint: #62ceff;
99 |
100 | --ion-color-tertiary: #6a64ff;
101 | --ion-color-tertiary-rgb: 106, 100, 255;
102 | --ion-color-tertiary-contrast: #ffffff;
103 | --ion-color-tertiary-contrast-rgb: 255, 255, 255;
104 | --ion-color-tertiary-shade: #5d58e0;
105 | --ion-color-tertiary-tint: #7974ff;
106 |
107 | --ion-color-success: #2fdf75;
108 | --ion-color-success-rgb: 47, 223, 117;
109 | --ion-color-success-contrast: #000000;
110 | --ion-color-success-contrast-rgb: 0, 0, 0;
111 | --ion-color-success-shade: #29c467;
112 | --ion-color-success-tint: #44e283;
113 |
114 | --ion-color-warning: #ffd534;
115 | --ion-color-warning-rgb: 255, 213, 52;
116 | --ion-color-warning-contrast: #000000;
117 | --ion-color-warning-contrast-rgb: 0, 0, 0;
118 | --ion-color-warning-shade: #e0bb2e;
119 | --ion-color-warning-tint: #ffd948;
120 |
121 | --ion-color-danger: #ff4961;
122 | --ion-color-danger-rgb: 255, 73, 97;
123 | --ion-color-danger-contrast: #ffffff;
124 | --ion-color-danger-contrast-rgb: 255, 255, 255;
125 | --ion-color-danger-shade: #e04055;
126 | --ion-color-danger-tint: #ff5b71;
127 |
128 | --ion-color-dark: #f4f5f8;
129 | --ion-color-dark-rgb: 244, 245, 248;
130 | --ion-color-dark-contrast: #000000;
131 | --ion-color-dark-contrast-rgb: 0, 0, 0;
132 | --ion-color-dark-shade: #d7d8da;
133 | --ion-color-dark-tint: #f5f6f9;
134 |
135 | --ion-color-medium: #989aa2;
136 | --ion-color-medium-rgb: 152, 154, 162;
137 | --ion-color-medium-contrast: #000000;
138 | --ion-color-medium-contrast-rgb: 0, 0, 0;
139 | --ion-color-medium-shade: #86888f;
140 | --ion-color-medium-tint: #a2a4ab;
141 |
142 | --ion-color-light: #222428;
143 | --ion-color-light-rgb: 34, 36, 40;
144 | --ion-color-light-contrast: #ffffff;
145 | --ion-color-light-contrast-rgb: 255, 255, 255;
146 | --ion-color-light-shade: #1e2023;
147 | --ion-color-light-tint: #383a3e;
148 | }
149 |
150 | /*
151 | * iOS Dark Theme
152 | * -------------------------------------------
153 | */
154 |
155 | .ios body {
156 | --ion-background-color: #000000;
157 | --ion-background-color-rgb: 0, 0, 0;
158 |
159 | --ion-text-color: #ffffff;
160 | --ion-text-color-rgb: 255, 255, 255;
161 |
162 | --ion-color-step-50: #0d0d0d;
163 | --ion-color-step-100: #1a1a1a;
164 | --ion-color-step-150: #262626;
165 | --ion-color-step-200: #333333;
166 | --ion-color-step-250: #404040;
167 | --ion-color-step-300: #4d4d4d;
168 | --ion-color-step-350: #595959;
169 | --ion-color-step-400: #666666;
170 | --ion-color-step-450: #737373;
171 | --ion-color-step-500: #808080;
172 | --ion-color-step-550: #8c8c8c;
173 | --ion-color-step-600: #999999;
174 | --ion-color-step-650: #a6a6a6;
175 | --ion-color-step-700: #b3b3b3;
176 | --ion-color-step-750: #bfbfbf;
177 | --ion-color-step-800: #cccccc;
178 | --ion-color-step-850: #d9d9d9;
179 | --ion-color-step-900: #e6e6e6;
180 | --ion-color-step-950: #f2f2f2;
181 |
182 | --ion-item-background: #000000;
183 |
184 | --ion-card-background: #1c1c1d;
185 | }
186 |
187 | .ios ion-modal {
188 | --ion-background-color: var(--ion-color-step-100);
189 | --ion-toolbar-background: var(--ion-color-step-150);
190 | --ion-toolbar-border-color: var(--ion-color-step-250);
191 | }
192 |
193 | /*
194 | * Material Design Dark Theme
195 | * -------------------------------------------
196 | */
197 |
198 | .md body {
199 | --ion-background-color: #121212;
200 | --ion-background-color-rgb: 18, 18, 18;
201 |
202 | --ion-text-color: #ffffff;
203 | --ion-text-color-rgb: 255, 255, 255;
204 |
205 | --ion-border-color: #222222;
206 |
207 | --ion-color-step-50: #1e1e1e;
208 | --ion-color-step-100: #2a2a2a;
209 | --ion-color-step-150: #363636;
210 | --ion-color-step-200: #414141;
211 | --ion-color-step-250: #4d4d4d;
212 | --ion-color-step-300: #595959;
213 | --ion-color-step-350: #656565;
214 | --ion-color-step-400: #717171;
215 | --ion-color-step-450: #7d7d7d;
216 | --ion-color-step-500: #898989;
217 | --ion-color-step-550: #949494;
218 | --ion-color-step-600: #a0a0a0;
219 | --ion-color-step-650: #acacac;
220 | --ion-color-step-700: #b8b8b8;
221 | --ion-color-step-750: #c4c4c4;
222 | --ion-color-step-800: #d0d0d0;
223 | --ion-color-step-850: #dbdbdb;
224 | --ion-color-step-900: #e7e7e7;
225 | --ion-color-step-950: #f3f3f3;
226 |
227 | --ion-item-background: #1e1e1e;
228 |
229 | --ion-toolbar-background: #1f1f1f;
230 |
231 | --ion-tab-bar-background: #1f1f1f;
232 |
233 | --ion-card-background: #1e1e1e;
234 | }
235 | }
236 |
--------------------------------------------------------------------------------
/packages/example-app/src/views/HomePage.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Blank
6 |
7 |
8 |
9 |
10 |
11 |
12 | CapacitorWatch
13 |
14 |
15 |
16 |
17 |
{{ stateData.counter }}
18 |
Send WatchUI
19 |
Increment
20 |
21 |
22 |
23 |
24 |
25 |
83 |
84 |
117 |
--------------------------------------------------------------------------------
/packages/example-app/tests/e2e/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: ["cypress"],
3 | env: {
4 | mocha: true,
5 | "cypress/globals": true,
6 | },
7 | rules: {
8 | strict: "off",
9 | },
10 | };
11 |
--------------------------------------------------------------------------------
/packages/example-app/tests/e2e/plugins/index.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable arrow-body-style */
2 | // https://docs.cypress.io/guides/guides/plugins-guide.html
3 |
4 | // if you need a custom webpack configuration you can uncomment the following import
5 | // and then use the `file:preprocessor` event
6 | // as explained in the cypress docs
7 | // https://docs.cypress.io/api/plugins/preprocessors-api.html#Examples
8 |
9 | // /* eslint-disable import/no-extraneous-dependencies, global-require */
10 | // const webpack = require('@cypress/webpack-preprocessor')
11 |
12 | module.exports = (on, config) => {
13 | // on('file:preprocessor', webpack({
14 | // webpackOptions: require('@vue/cli-service/webpack.config'),
15 | // watchOptions: {}
16 | // }))
17 |
18 | return Object.assign({}, config, {
19 | fixturesFolder: "tests/e2e/fixtures",
20 | integrationFolder: "tests/e2e/specs",
21 | screenshotsFolder: "tests/e2e/screenshots",
22 | videosFolder: "tests/e2e/videos",
23 | supportFile: "tests/e2e/support/index.js",
24 | });
25 | };
26 |
--------------------------------------------------------------------------------
/packages/example-app/tests/e2e/specs/test.js:
--------------------------------------------------------------------------------
1 | // https://docs.cypress.io/api/introduction/api.html
2 |
3 | describe("My First Test", () => {
4 | it("Visits the app root url", () => {
5 | cy.visit("/");
6 | cy.contains("#container", "Ready to create an app?");
7 | });
8 | });
9 |
--------------------------------------------------------------------------------
/packages/example-app/tests/e2e/support/commands.js:
--------------------------------------------------------------------------------
1 | // ***********************************************
2 | // This example commands.js shows you how to
3 | // create various custom commands and overwrite
4 | // existing commands.
5 | //
6 | // For more comprehensive examples of custom
7 | // commands please read more here:
8 | // https://on.cypress.io/custom-commands
9 | // ***********************************************
10 | //
11 | //
12 | // -- This is a parent command --
13 | // Cypress.Commands.add("login", (email, password) => { ... })
14 | //
15 | //
16 | // -- This is a child command --
17 | // Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
18 | //
19 | //
20 | // -- This is a dual command --
21 | // Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
22 | //
23 | //
24 | // -- This is will overwrite an existing command --
25 | // Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
26 |
--------------------------------------------------------------------------------
/packages/example-app/tests/e2e/support/index.js:
--------------------------------------------------------------------------------
1 | // ***********************************************************
2 | // This example support/index.js is processed and
3 | // loaded automatically before your test files.
4 | //
5 | // This is a great place to put global configuration and
6 | // behavior that modifies Cypress.
7 | //
8 | // You can change the location of this file or turn off
9 | // automatically serving support files with the
10 | // 'supportFile' configuration option.
11 | //
12 | // You can read more here:
13 | // https://on.cypress.io/configuration
14 | // ***********************************************************
15 |
16 | // Import commands.js using ES2015 syntax:
17 | import "./commands";
18 |
19 | // Alternatively you can use CommonJS syntax:
20 | // require('./commands')
21 |
--------------------------------------------------------------------------------
/packages/example-app/tests/unit/example.spec.ts:
--------------------------------------------------------------------------------
1 | import { mount } from "@vue/test-utils";
2 | import HomePage from "@/views/HomePage.vue";
3 |
4 | describe("HomePage.vue", () => {
5 | it("renders home vue", () => {
6 | const wrapper = mount(HomePage);
7 | expect(wrapper.text()).toMatch("Ready to create an app?");
8 | });
9 | });
10 |
--------------------------------------------------------------------------------
/packages/example-app/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "esnext",
4 | "module": "esnext",
5 | "strict": true,
6 | "jsx": "preserve",
7 | "importHelpers": true,
8 | "moduleResolution": "node",
9 | "skipLibCheck": true,
10 | "esModuleInterop": true,
11 | "allowSyntheticDefaultImports": true,
12 | "sourceMap": true,
13 | "baseUrl": ".",
14 | "types": [
15 | "webpack-env",
16 | "jest"
17 | ],
18 | "paths": {
19 | "@/*": [
20 | "src/*"
21 | ]
22 | },
23 | "lib": [
24 | "esnext",
25 | "dom",
26 | "dom.iterable",
27 | "scripthost"
28 | ]
29 | },
30 | "include": [
31 | "src/**/*.ts",
32 | "src/**/*.tsx",
33 | "src/**/*.vue",
34 | "tests/**/*.ts",
35 | "tests/**/*.tsx"
36 | ],
37 | "exclude": [
38 | "node_modules"
39 | ]
40 | }
41 |
--------------------------------------------------------------------------------
/packages/iOS-capWatch-sdk/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | /.build
3 | /Packages
4 | /*.xcodeproj
5 | xcuserdata/
6 | DerivedData/
7 | .swiftpm/config/registries.json
8 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
9 | .netrc
10 |
--------------------------------------------------------------------------------
/packages/iOS-capWatch-sdk/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version: 5.7
2 | import PackageDescription
3 |
4 | let package = Package(
5 | name: "CapWatch",
6 | platforms: [.iOS(.v16), .watchOS(.v9)],
7 | products: [
8 | .library(
9 | name: "CapWatch",
10 | targets: ["CapWatch"]),
11 | ],
12 | dependencies: [
13 | ],
14 | targets: [
15 | .target(
16 | name: "CapWatch",
17 | dependencies: []),
18 | .testTarget(
19 | name: "iOS-capWatch-sdkTests",
20 | dependencies: ["CapWatch"]),
21 | ]
22 | )
23 |
--------------------------------------------------------------------------------
/packages/iOS-capWatch-sdk/README.md:
--------------------------------------------------------------------------------
1 | # iOS-capWatch-sdk
2 |
3 | A description of this package.
4 |
--------------------------------------------------------------------------------
/packages/iOS-capWatch-sdk/Sources/CapWatch/WatchSessionDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WatchSessionDelegate.swift
3 | //
4 | //
5 | // Created by Dan Giralté on 2/24/23.
6 | //
7 |
8 | import WatchConnectivity
9 |
10 | public class CapWatchSessionDelegate : NSObject, WCSessionDelegate {
11 | var WATCH_UI = ""
12 |
13 | public static var shared = CapWatchSessionDelegate()
14 |
15 | public func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
16 |
17 | }
18 |
19 | #if os(iOS)
20 |
21 | public func sessionDidBecomeInactive(_ session: WCSession) {
22 |
23 | }
24 |
25 | public func sessionDidDeactivate(_ session: WCSession) {
26 | // dcg - do we want this?
27 | session.activate()
28 | }
29 |
30 | public func session(_ session: WCSession, didReceiveMessage message: [String : Any]) {
31 | handleWatchMessage(message)
32 | }
33 |
34 | public func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String: Any]) {
35 | handleWatchMessage(applicationContext)
36 | }
37 |
38 | public func session(_ session: WCSession, didReceiveUserInfo userInfo: [String : Any] = [:]) {
39 | // print("PHONE got didReceiveUserInfo: \(userInfo)")
40 | handleWatchMessage(userInfo)
41 | }
42 |
43 | func updateViewData(_ data: [String: String]) {
44 | DispatchQueue.main.async {
45 | let _ = WCSession.default.transferUserInfo([DATA_KEY: data])
46 | }
47 | }
48 |
49 | func sendUI() {
50 | let _ = WCSession.default.transferUserInfo([UI_KEY : WATCH_UI])
51 | }
52 |
53 | func commandToJS(_ command: String) {
54 | NotificationCenter.default.post(name: Notification.Name(COMMAND_KEY),
55 | object: nil,
56 | userInfo: [COMMAND_KEY: command])
57 | }
58 |
59 | func handleWatchMessage(_ userInfo: [String: Any]) {
60 | if let command = userInfo[REQUESTUI_KEY] as? String {
61 | if command == REQUESTUI_VALUE {
62 | sendUI()
63 | }
64 | }
65 |
66 | if let command = userInfo[COMMAND_KEY] as? String {
67 | print("PHONE process: \(command)")
68 | commandToJS(command)
69 | }
70 | }
71 |
72 | #endif
73 | }
74 |
--------------------------------------------------------------------------------
/packages/iOS-capWatch-sdk/Sources/CapWatch/iOS_capWatch_sdk.swift:
--------------------------------------------------------------------------------
1 | public let UI_KEY = "watchUI"
2 | public let DATA_KEY = "viewData"
3 | public let SAVEDUI_KEY = "CAPWATCH_UI"
4 | public let REQUESTUI_KEY = "capWatch"
5 | public let REQUESTUI_VALUE = "uiPlease"
6 | public let REQUESTUI = [REQUESTUI_KEY: REQUESTUI_VALUE]
7 | public let COMMAND_KEY = "jsCommand"
8 |
--------------------------------------------------------------------------------
/packages/iOS-capWatch-sdk/Tests/iOS-capWatch-sdkTests/iOS_capWatch_sdkTests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 | @testable import iOS_capWatch_sdk
3 |
4 | final class iOS_capWatch_sdkTests: XCTestCase {
5 | func testExample() throws {
6 | // This is an example of a functional test case.
7 | // Use XCTAssert and related functions to verify your tests produce the correct
8 | // results.
9 | //XCTAssertEqual(iOS_capWatch_sdk().text, "Hello, World!")
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/packages/iOS-capWatch-watch/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | /.build
3 | /Packages
4 | /*.xcodeproj
5 | xcuserdata/
6 | DerivedData/
7 | .swiftpm/config/registries.json
8 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
9 | .netrc
10 |
--------------------------------------------------------------------------------
/packages/iOS-capWatch-watch/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version: 5.7
2 |
3 | import PackageDescription
4 |
5 | let package = Package(
6 | name: "iOS-capWatch-watch",
7 | platforms: [.iOS(.v16), .watchOS(.v9)],
8 | products: [
9 | .library(
10 | name: "iOS-capWatch-watch",
11 | targets: ["iOS-capWatch-watch"]),
12 | ],
13 | dependencies: [
14 | ],
15 | targets: [
16 | .target(
17 | name: "iOS-capWatch-watch",
18 | dependencies: []),
19 | .testTarget(
20 | name: "iOS-capWatch-watchTests",
21 | dependencies: ["iOS-capWatch-watch"]),
22 | ]
23 | )
24 |
--------------------------------------------------------------------------------
/packages/iOS-capWatch-watch/README.md:
--------------------------------------------------------------------------------
1 | # CapWatch-watch-SPM
2 |
3 | This is the Swift Package fo the watch half of the CapacitorWatch plugin. See the main readme for more details
4 |
5 | ```
6 | import SwiftUI
7 | import WatchConnectivity
8 |
9 | let WATCHUI_KEY = "CAPWATCH_UI"
10 |
11 | @main
12 | struct my_Watch_App: App {
13 | var body: some Scene {
14 | WindowGroup {
15 | ContentView()
16 | .onAppear {
17 | WCSession.default.delegate = WatchViewModel.shared
18 | WCSession.default.activate()
19 | }
20 | }
21 | }
22 | }
23 | ```
24 |
25 | ```
26 |
--------------------------------------------------------------------------------
/packages/iOS-capWatch-watch/Sources/iOS-capWatch-watch/CapWatchComponentView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CapWatchComponentView.swift
3 | //
4 | //
5 | // Created by Dan Giralté on 2/24/23.
6 | //
7 |
8 | import SwiftUI
9 | import WatchConnectivity
10 |
11 | struct CapWatchComponentView : View, Identifiable {
12 | var id: UUID
13 |
14 | var controlType: String
15 | var controlParams: String
16 | var splitParams: [Substring]
17 |
18 | var viewModel: [String: String]?
19 |
20 | init(_ control: String, _ vm: [String: String]? = nil) {
21 | viewModel = vm
22 |
23 | let getType = control.split(separator: #"(""#)
24 | controlType = String(getType[0])
25 |
26 | let getParams = getType[1].split(separator: #"")"#)
27 | controlParams = String(getParams[0])
28 |
29 | splitParams = controlParams.replacing("\"", with: "").split(separator: ", ")
30 |
31 | id = UUID()
32 | }
33 |
34 | var body: some View {
35 | if controlType == "Text" {
36 | CapWatchText(controlParams, viewModel)
37 | .foregroundColor(.white)
38 | } else if controlType == "Button" {
39 | Button(
40 | action: { WCSession.default.transferUserInfo([COMMAND_KEY:splitParams[1]]) },
41 | label: {
42 | CapWatchText(String(splitParams[0]), viewModel)
43 | }
44 | ).foregroundColor(.white)
45 | }
46 | }
47 | }
48 |
49 |
--------------------------------------------------------------------------------
/packages/iOS-capWatch-watch/Sources/iOS-capWatch-watch/CapWatchContentView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CapWatchContentView.swift
3 | //
4 | //
5 | // Created by Dan Giralté on 2/24/23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | public struct CapWatchContentView: View {
11 | @StateObject private var model = WatchViewModel.shared
12 |
13 | public init() { }
14 |
15 | public var body: some View {
16 | VStack {
17 | if model.watchUI == "" {
18 | Button(action: {
19 | WatchViewModel.shared.session.sendMessage(REQUESTUI, replyHandler: nil) { error in
20 | print("UI Delivery failed")
21 | }
22 | }) {
23 | Image(systemName: "globe")
24 | .imageScale(.large)
25 | .foregroundColor(.accentColor)
26 | }
27 | Rectangle().frame(height: 1)
28 | } else {
29 | ScrollView {
30 | CapWatchView()
31 | }
32 | }
33 | }
34 | }
35 | }
36 |
37 | struct ContentView_Previews: PreviewProvider {
38 | static var previews: some View {
39 | CapWatchContentView()
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/packages/iOS-capWatch-watch/Sources/iOS-capWatch-watch/CapWatchText.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CapWatchText.swift
3 | //
4 | //
5 | // Created by Dan Giralté on 2/24/23.
6 | //
7 |
8 | import SwiftUI
9 | import RegexBuilder
10 |
11 | struct CapWatchText: View {
12 | var text: String
13 |
14 | init(_ txt: String, _ vm: [String: String]? = nil) {
15 | text = txt
16 |
17 | text = replaceVars(txt, vm)
18 | }
19 |
20 | var body: some View {
21 | Text(text)
22 | }
23 |
24 | func replaceVars(_ text: String, _ vm: [String: String]? = nil) -> String {
25 | if let vm {
26 | let varSearch = Regex {
27 | Anchor.wordBoundary
28 | "$"
29 | ZeroOrMore {
30 | CharacterClass(
31 | ("a"..."z"),
32 | ("A"..."Z"),
33 | ("0"..."9")
34 | )
35 | }
36 | }
37 |
38 | let ranges = text.ranges(of: varSearch)
39 |
40 | var finalText = text
41 |
42 | for range in ranges {
43 | let ph = text[range]
44 |
45 | // this seems dumb?
46 | var varName = ph
47 | varName.removeFirst()
48 |
49 | let varVal = vm[String(varName)] ?? String(varName)
50 |
51 | finalText = finalText.replacing(ph, with: varVal)
52 | }
53 |
54 | return finalText
55 | } else {
56 | return text
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/packages/iOS-capWatch-watch/Sources/iOS-capWatch-watch/CapWatchView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CapWatchView.swift
3 | //
4 | //
5 | // Created by Dan Giralté on 2/24/23.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct CapWatchView: View {
11 | @StateObject private var viewModel = WatchViewModel.shared
12 |
13 | var views: [CapWatchComponentView] = []
14 |
15 | init() {
16 | // parse viewModel.watchUI
17 | let components = viewModel.watchUI.lines
18 |
19 | // iterate over components and add views to views array
20 | for element in components {
21 | views.append(CapWatchComponentView(String(element), viewModel.viewData))
22 | }
23 | }
24 |
25 | var body: some View {
26 | VStack {
27 | if views.isEmpty {
28 | Text("Waiting for UI from Phone...")
29 | }
30 | ForEach(views) { thisView in
31 | thisView
32 | }
33 | }
34 | .onAppear() {
35 |
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/packages/iOS-capWatch-watch/Sources/iOS-capWatch-watch/StringExtensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StringExtensions.swift
3 | //
4 | //
5 | // Created by Dan Giralté on 2/24/23.
6 | //
7 |
8 | extension StringProtocol {
9 | var lines: [SubSequence] { split(whereSeparator: \.isNewline) }
10 | }
11 |
--------------------------------------------------------------------------------
/packages/iOS-capWatch-watch/Sources/iOS-capWatch-watch/WatchViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WatchViewModel.swift
3 | //
4 | //
5 | // Created by Dan Giralté on 2/24/23.
6 | //
7 |
8 | import Foundation
9 | import WatchConnectivity
10 | import SwiftUI
11 |
12 | public class WatchViewModel: NSObject, WCSessionDelegate, ObservableObject {
13 | var session: WCSession
14 |
15 | public static var shared = WatchViewModel()
16 |
17 | @AppStorage(SAVEDUI_KEY) var savedUI: String = ""
18 |
19 | @Published var watchUI = "Text(\"Capacitor WATCH\")\nButton(\"Add One\", \"inc\")"
20 | @Published var viewData: [String: String]?
21 |
22 | init(session: WCSession = .default, viewData: [String: String]? = nil) {
23 | self.session = session
24 | self.viewData = viewData
25 |
26 | super.init()
27 |
28 | if savedUI != "" {
29 | self.watchUI = self.savedUI
30 | }
31 | }
32 |
33 | public func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
34 | // apple docs say this won't work on simulator
35 |
36 | if WatchViewModel.shared.watchUI.isEmpty {
37 | let _ = session.transferUserInfo(REQUESTUI)
38 | }
39 | }
40 |
41 | public func session(_ session: WCSession, didReceiveMessage message: [String : Any]) {
42 | handlePhoneMessage(message)
43 | }
44 |
45 | public func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String : Any]) {
46 | handlePhoneMessage(applicationContext)
47 | }
48 |
49 | public func session(_ session: WCSession, didReceiveUserInfo userInfo: [String : Any] = [:]) {
50 | handlePhoneMessage(userInfo)
51 | }
52 |
53 | // required protocol stubs?
54 | //func sessionDidBecomeInactive(_ session: WCSession) {}
55 |
56 | //func sessionDidDeactivate(_ session: WCSession) {}
57 |
58 | func handlePhoneMessage(_ userInfo: [String: Any]) {
59 | DispatchQueue.main.async {
60 | if let newUI = userInfo[UI_KEY] as? String {
61 | self.watchUI = newUI
62 | print("new watchUI: \(self.watchUI)")
63 | self.savedUI = self.watchUI
64 | }
65 |
66 | if let newViewData = userInfo[DATA_KEY] as? [String: String] {
67 | self.viewData = newViewData
68 | }
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/packages/iOS-capWatch-watch/Sources/iOS-capWatch-watch/iOS_capWatch_watch.swift:
--------------------------------------------------------------------------------
1 | public let UI_KEY = "watchUI"
2 | public let DATA_KEY = "viewData"
3 | public let SAVEDUI_KEY = "CAPWATCH_UI"
4 | public let REQUESTUI_KEY = "capWatch"
5 | public let REQUESTUI_VALUE = "uiPlease"
6 | public let REQUESTUI = [REQUESTUI_KEY: "uiPlease"]
7 | public let COMMAND_KEY = "jsCommand"
--------------------------------------------------------------------------------
/packages/iOS-capWatch-watch/Tests/iOS-capWatch-watchTests/iOS_capWatch_watchTests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 | @testable import iOS_capWatch_watch
3 |
4 | final class iOS_capWatch_watchTests: XCTestCase {
5 | func testExample() throws {
6 | // This is an example of a functional test case.
7 | // Use XCTAssert and related functions to verify your tests produce the correct
8 | // results.
9 | //XCTAssertEqual(iOS_capWatch_watch().text, "Hello, World!")
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/pnpm-workspace.yaml:
--------------------------------------------------------------------------------
1 | packages:
2 | - 'packages/*'
3 |
--------------------------------------------------------------------------------