add-native について
Power Apps Wrap のモバイルアプリに、対応するネイティブ機能を追加するスキルです。導入済みパッケージを確認し、専用ヘルパーがある機能は適切な処理に振り分け、それ以外の対象機能には型付きラッパーを生成します。
主な機能
- 01ネイティブパッケージと実行時制約の確認
- 02src/native/ への型付きラッパー生成
- 03カメラ、PDF、ペン入力、位置情報の専用フロー
- 04Dataverse の File/Image 列向けホストコントロールの案内
活用シーン
- 01モバイルアプリへの対応ネイティブ機能の追加
- 02カメラ、PDF、署名フローの実装
- 03Dataverse の File/Image フィールドの適切な処理
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ microsoft/ power-platform-skills --skill add-nativeインストール後、Claude Code で/add-nativeを入力して読み込まれたか確認します。
詳細
プラグインの確認:
node "${PLUGIN_ROOT}/scripts/check-version.js"を実行してください。メッセージが出力された場合は、先にユーザーに提示してから続行してください。
📋 共通の指示:shared-instructions.md — まずお読みください。
作業ディレクトリ: プロジェクトを読み込んだりコマンドを実行したりする前に、
native-artifact-compatibility.md Step 0 を実行してください。
そこで --working-dir/所有者のコンテキストを解決し、以前のシェルの作業ディレクトリではなく、
解決した絶対パスのルートをすべてのシェル呼び出しとファイルツールで使用してください。
ネイティブ機能の追加
エントリのルーティング: 以下のワークフローに進む前に、共通のアプリ機能のエントリポイントの 事前確認を行ってください。
このリーフは、アップストリームのテンプレートにすでに含まれているネイティブ機能向けの型付きラッパーを src/native/ の下に生成します。画面からは Expo モジュールに直接アクセスせず、このラッパーをインポートすることで、判別可能な共用体による結果の契約を一貫して維持できます。
厳守事項 — 絶対に破らないでください
- ネイティブモジュールに対して
npx expo install、npm install、yarn addを絶対に実行しないでください。package.jsonに含まれるネイティブモジュールは、アップストリームのテンプレートで固定されています。新しいモジュールを追加すると、リラップのパイプラインが壊れます(顧客のバイナリはpackage.jsonからではなく、ビルド済みのベースから構築されるためです)。 app.config.jsは絶対に編集しないでください — プラグイン、ios.infoPlist、android.permissionsなど、いかなる箇所も編集しないでください。テンプレートに含まれるネイティブ設定はすべて意図的に承認済みです。任意の追加設定はリラップ時に適用できません。- ネイティブモジュールの
package.jsonのdependenciesは絶対に編集しないでください。 ネイティブとは、そのパッケージがプラットフォームのソース/プロジェクト、podspec、codegen、Expo モジュール/設定プラグイン、またはreact-native.config.jsを含むことを指します。パッケージ名のプレフィックスだけでは、その証明になりません。純粋な JavaScript の依存関係は/add-nativeの対象外であり、/create-mobile-appまたは/edit-appによる、承認済みのJavaScript Dependenciesプランからインストールされます。 - 要求されたモジュールが
package.jsonに実際に含まれていない場合は、そこで中止してください。 これは、アップストリームのテンプレートにまだ含まれていないことを意味します。インストールして回避しようとしないでください。
ルーティング — /add-native は公開エントリーポイントです
一部の機能には、単純なラッパーを超える処理を行う専用の実装ヘルパーがあります(camera はスキャナー/アップロード用ヘルパーを書き込み、PDF レポート/ビューアー用ヘルパーはローカルと HTTPS の境界を適用し、pen はネイティブコントロール固有の検証を行います)。ユーザーは引き続き、ネイティブコントロールには /add-native <capability> を呼び出してください。専用の実装がある場合は、内部で実行し、ユーザーにそのヘルパーを直接実行するよう求めないでください。
検索規約: 機能名を正規化した後、まず以下の内部ヘルパーマップを確認してください。camera、image-picker、barcode-scanner、qr-scanner については、この /add-native の呼び出し内で ${PLUGIN_ROOT}/skills/add-native/add-camera/SKILL.md を読み、実行してください。pdf-report については、${PLUGIN_ROOT}/skills/add-native/add-pdf-report/SKILL.md を読み、実行してください。pdf-viewer については、${PLUGIN_ROOT}/skills/add-native/add-pdf-viewer/SKILL.md を読み、実行してください。pen-input については、${PLUGIN_ROOT}/skills/add-native/add-pen-input/SKILL.md を読み、実行してください。geolocation については、${PLUGIN_ROOT}/skills/add-native/add-geolocation/SKILL.md を読み、実行してください。ヘルパーが存在しない場合は、このスキルのインラインラッパーフローに進んでください。
現在の専用実装:
| 機能 | 専用スキル | 専用である理由 |
|---|---|---|
camera、take-photo、photo、expo-camera、image-picker、gallery、expo-image-picker、barcode-scanner、qr-scanner、scanner | add-camera 内部ヘルパー | expo-camera / expo-image-picker を使用した写真撮影、ギャラリーからの画像選択、バーコード/QR スキャナーコントロールを担当します |
pdf-report、pdf-export、generate-pdf、print-report、evidence-packet | add-pdf-report 内部ヘルパー | expo-print を使ってアプリ所有のローカル PDF を生成し、expo-sharing が存在する場合にのみ共有します |
pdf-viewer、native-pdf-viewer、pdf-control、open-pdf、@microsoft/power-apps-native-pdf-viewer | add-pdf-viewer 内部ヘルパー | https:// / file:// のビューアー入力と、ネイティブビューアーの結果処理を適用します |
pen-input、signature、ink、draw、@microsoft/power-apps-native-pen-input | add-pen-input 内部ヘルパー | PNG データ URI を取得し、Dataverse Image/File への保存方法を説明します |
geolocation、location-tracking、background-location、gps-tracking、geo-tracking、@microsoft/power-apps-native-bglocation | add-geolocation 内部ヘルパー | 永続ストレージとインライン Dataverse 同期を備えたネイティブのバックグラウンド GPS 追跡を行います。単発の expo-location とは異なります |
以下に記載されているその他すべての機能については、このスキルがラッパーを直接作成します。
ネイティブ機能の判定ゲート
ネイティブのコントロールやラッパーを追加する前に、すべてのゲートを適用してください。インテントを分類し、現在の package.json から正確なパッケージ/コントロールを特定し、ランタイムで禁止されていないことを確認し、入出力とストレージの制約を確認してから、以下の該当するルートを使用してください。いずれかのゲートを通過できない場合、このアプリのバージョンではネイティブ機能はサポートされていません。パッケージのインストール、ネイティブ設定の編集、偽のラッパーの作成は行わないでください。
| ユーザーの意図 | 追加/使用するもの | 必須パッケージまたはコントロール | 使用しないもの/フォールバック |
|---|---|---|---|
| Dataverse の File 列にバインドされたフォームフィールド | 画面の JSX で <FilePicker> ホストコントロールを使用 | @microsoft/power-apps-native-host ホストコントロール | このフィールド用にドキュメントピッカー/ファイルシステム/共有のラッパーを生成しないでください |
| Dataverse の Image 列にバインドされたフォームフィールド | 画面の JSX で <ImagePicker> ホストコントロールを使用 | @microsoft/power-apps-native-host ホストコントロール | 通常のフォームにバインドされた画像フィールドにカメラ/画像ピッカーのラッパーを使用しないでください |
| 専用の写真/ギャラリー/スキャナーのワークフロー | /add-native camera、image-picker、または barcode-scanner | 写真/ギャラリーには expo-image-picker、スキャナーには expo-camera、両方を使うリクエストには両方 | パッケージがない場合は、不足パッケージに関する案内を示して停止してください |
| ユーザーが選択した PDF/ドキュメントの選択/インポート/アップロード | /add-native document-picker、または Dataverse の File フィールドにはホストの <FilePicker> | expo-document-picker が存在するか、ホストの File コントロール | これを pdf-report やネイティブ PDF ビューアーとして扱わないでください |
| アプリが所有するレポート PDF の生成/エクスポート/印刷 | /add-native pdf-report | expo-print が存在すること | expo-print がない場合は、PDF レポート機能を追加しないでください |
| 生成したローカル PDF の共有 | pdfReport.ts の共有ヘルパー | expo-sharing が存在すること | expo-sharing がない場合は、共有 UI を表示しないでください |
| HTTPS またはローカルファイルの PDF を開く/プレビューする | /add-native pdf-viewer | @microsoft/power-apps-native-pdf-viewer 0.2.9+ が存在し、入力が https:// または file:// であること | content://、blob:、または http:// URI をビューアーに渡さないでください |
| 署名、インク、描画、または承認の記録 | /add-native pen-input | @microsoft/power-apps-native-pen-input が存在すること | 保存する場合は、まず Dataverse の Image/File/子 Evidence の保存先を計画してください |
| Dataverse への確実なアップロードを伴う継続的/バックグラウンド GPS トラッキング | /add-native geolocation | @microsoft/power-apps-native-bglocation が存在すること | バックグラウンドトラッキングに単発の expo-location を使用しないでください。GeolocationExtension/HostingSDK の経路も使用しないでください |
| 押下、選択、操作結果に対する触覚フィードバック | /add-native haptics | expo-haptics が存在すること | 型付きラッパーを使用し、すべての触覚フィードバックに視覚的な UI フィードバックを組み合わせてください |
| 生成された PDF/署名の成果物を保存する | 親行の作成後に生成された Dataverse サービス | File/Image 列、または子 Evidence/Attachment テーブルが存在すること | File のバイトデータを作成/更新 JSON に決して含めないでください |
| この表に記載されていないネイティブ機能 | package.json から特定し、一致するパッケージが存在しランタイムで禁止されていない場合に限り、インラインラッパーを追加する | package.json に該当する正確なパッケージが存在すること | 該当するパッケージがない場合、またはパッケージがランタイムで禁止されている場合は、透明性に関する注記を追加して停止してください |
複数の要素を含むリクエストは、行ごとに処理してください。例:「署名を取得して、署名済みレポート PDF を添付する」には、pen-input、pdf-report、Dataverse での成果物の保存、場合によっては sharing が必要です。ストレージや画面の状態を未定義のままにして、ネイティブ機能だけを追加しないでください。この一覧は固定ではありません。新たに出荷されたパッケージについては、機能の意味に基づいて特定し、安全であれば直接一致するパッケージを使用してください。インストール済みの複数のパッケージが該当する可能性がある場合に限り、一度だけ確認してください。
ファイル/画像ピッカーの担当範囲
このスキルは、ネイティブファイル機能の仕組みを担当します。画面ビルダーが担当するのは、最終的な JSX の配置のみです。
ケースは次の2種類に分かれます。
| ケース | 正しい実装 | 担当 |
|---|---|---|
Dataverse の File 列 | @microsoft/power-apps-native-host の <FilePicker> を使用します | 画面ビルダーの JSX ルール(ここに記載) |
Dataverse の Image 列 | @microsoft/power-apps-native-host の <ImagePicker> を使用します | 画面ビルダーの JSX ルール(ここに記載) |
| Dataverse の列にバインドされていない、ローカルデバイスのドキュメント/ファイルのワークフロー | src/native/documentPicker.ts、src/native/fileSystem.ts、および/または src/native/sharing.ts のラッパーを生成/使用します | /add-native |
| アプリで生成する PDF レポートのワークフロー | /add-native が内部で add-pdf-report に振り分けます。expo-print を使用し、expo-sharing は存在する場合にのみ使用します | /add-native |
| カメラでの撮影、ギャラリーからの画像選択、バーコードスキャナー、または QR スキャナーのワークフロー | /add-native が内部で add-camera に振り分けます | /add-native |
Dataverse の File/Image 列では、生の Expo モジュールやアプリ固有のネイティブラッパーではなく、ホストコントロールを使用します。 フォームフィールドが Dataverse の File 列(FileAttributeMetadata)にバインドされている場合は、@microsoft/power-apps-native-host の <FilePicker> を表示します。Dataverse の Image 列(ImageAttributeMetadata)にバインドされている場合は、@microsoft/power-apps-native-host の <ImagePicker> を表示します。これらのコントロールは、PowerAppsProvider / ThemeProvider からアクセントカラー、サーフェスカラー、テキストカラーを読み取り、Dataverse 互換のペイロードを生成します。
import { FilePicker, ImagePicker } from '@microsoft/power-apps-native-host';
import type { PickedFileInfo, PickedImageInfo } from '@microsoft/power-apps-native-host';
// Image column — seed preview from Dataverse bytes/base64 and capture upload-ready payload.
<ImagePicker
label="Site Photo"
initialBase64={imageBase64}
onImageChange={(image: PickedImageInfo | null) => { /* store image for upload */ }}
/>
// File column — edit-only pick/replace.
<FilePicker
label="Specification Sheet"
fileName={record.cr123_specsheet_name}
onChange={(file: PickedFileInfo) => { /* store file for save */ }}
/>
// File column — view + replace.
<FilePicker
label="Specification Sheet"
fileName={record.cr123_specsheet_name}
onDownload={() => Cr123_inspectionService.downloadFile(id, 'cr123_specsheet')}
onChange={(file: PickedFileInfo) => { /* store file for save */ }}
/>
ホストピッカーにバインドされた Dataverse 列の保存/読み取りパターン:
// FILE/IMAGE columns: do not include picker bytes in the PATCH body.
// Save normal fields first, then upload through generated service helpers.
await Cr123_inspectionService.update(id, { cr123_name: name.trim() });
await Cr123_inspectionService.upload(id, 'cr123_specsheet', file.file, file.name);
await Cr123_inspectionService.upload(id, 'cr123_sitephoto', image.file, image.name);
// Download helpers are used for read/view scenarios.
const fileBytes = await Cr123_inspectionService.downloadFile(id, 'cr123_specsheet');
const imageBytes = await Cr123_inspectionService.downloadImage(id, 'cr123_sitephoto');
ホストの ImagePicker / FilePicker と、保存用の生成された upload(...)、読み取り/表示用の downloadFile(...) / downloadImage(...) を使用します。ピッカー経由の画像/ファイルの保存に Service.update(...{ <imageColumn>: base64 }) を使用しないでください。
Dataverse の File/Image 列用のカスタム UI を構築するために、生の expo-document-picker、expo-image-picker、expo-file-system、または expo-sharing を使用しないでください。ホストの FilePicker と ImagePicker は、権限、読み取り、共有/ダウンロード機能、Dataverse 互換のペイロード形式をすでに処理します。
ワークフローが Dataverse の File/Image 列に該当しない場合に限り、/add-native のラッパーを使用してください。たとえば、オフライン解析用のローカル PDF のインポート、生成したレポートのエクスポート、ローカルの下書きパケットの保存、アプリで生成した要約の共有シートを開く場合などです。
対応機能
上記の Native 機能ゲートを適用してください。この表は、既知の機能とパッケージの対応表であり、記載された各パッケージがすべてのテンプレートバージョンに存在することを保証するものではありません。
| 機能 | モジュール | 生成するラッパー | 備考 |
|---|---|---|---|
camera、take-photo、photo、expo-camera | expo-image-picker | src/native/camera.ts | /add-native は photo アーティファクトに対して内部的に add-camera にルーティングします。expo-camera は機能のエイリアスであり、必須パッケージではありません |
image-picker、gallery、expo-image-picker | expo-image-picker | src/native/camera.ts(pickImage) | /add-native は内部的に add-camera にルーティングします。互換性のある既存の再エクスポートを維持してください |
barcode-scanner、qr-scanner、scanner、barcode、qr | expo-camera | src/native/barcodeScanner.tsx | /add-native は内部的に add-camera にルーティングします |
document-picker | expo-document-picker | src/native/documentPicker.ts | デバイスからユーザーが選択したファイル(PDF、ドキュメントなど)を選択/インポートします |
pdf-viewer、native-pdf-viewer、pdf-control、open-pdf、@microsoft/power-apps-native-pdf-viewer | @microsoft/power-apps-native-pdf-viewer | src/native/pdfViewer.ts | /add-native は内部的に add-pdf-viewer にルーティングします。0.2.9 以降では HTTPS URL とファイル URI を開きます |
pdf-report、pdf-export、generate-pdf、print-report、evidence-packet | expo-print(+オプションの expo-sharing) | src/native/pdfReport.ts | /add-native は内部的に add-pdf-report にルーティングします。生成されたローカルファイルは、expo-sharing がある場合にのみ共有されるか、Dataverse にアップロードされます |
pen-input、signature、ink、draw、@microsoft/power-apps-native-pen-input | @microsoft/power-apps-native-pen-input | src/native/penInput.ts | /add-native は内部的に add-pen-input にルーティングします。PNG データ URI を取得します |
geolocation、location-tracking、background-location、gps-tracking、geo-tracking、@microsoft/power-apps-native-bglocation | @microsoft/power-apps-native-bglocation | src/native/geolocation.ts | /add-native は内部的に add-geolocation にルーティングします。ネイティブのバックグラウンド追跡と永続的な Dataverse 同期に対応します。下記の単発の location とは異なります |
secure-store | expo-secure-store | src/native/secureStore.ts | |
file-system | expo-file-system | src/native/fileSystem.ts | |
sharing | expo-sharing | src/native/sharing.ts | |
location | expo-location | src/native/location.ts | 単発/フォアグラウンドでの位置情報の取得のみです。Dataverse 同期を伴う継続的なバックグラウンド追跡には、geolocation(@microsoft/power-apps-native-bglocation)を使用してください。現在のテンプレートパッケージに expo-location が含まれている場合にのみ使用してください |
biometrics、local-authentication | expo-local-authentication | src/native/biometrics.ts | 現在のテンプレートパッケージに expo-local-authentication が含まれている場合にのみ使用してください |
clipboard | expo-clipboard | src/native/clipboard.ts | 現在のテンプレートパッケージに expo-clipboard が含まれている場合にのみ使用してください |
mail-composer、email-draft | expo-mail-composer | src/native/mailComposer.ts | パッケージがある場合はネイティブのメール作成画面を開きます。サーバー側のメール送信は引き続きコネクタが担当します |
audio | expo-audio | src/native/audio.ts | パッケージがある場合に限り、音声の録音/再生に使用してください |
video | expo-video | src/native/video.ts | パッケージがある場合に限り、動画の再生に使用してください |
sensors | expo-sensors | src/native/sensors.ts | インストール済みパッケージで公開されているセンサー API にのみ使用してください |
screen-orientation | expo-screen-orientation | src/native/screenOrientation.ts | パッケージがある場合にのみ使用してください。ネイティブ設定は編集しないでください |
haptics、vibration-feedback、impact-feedback、selection-feedback、notification-feedback | expo-haptics | src/native/haptics.ts | 衝撃、選択、成功/警告/エラーの通知フィードバック |
device-info | expo-device / expo-application / expo-cellular | src/native/deviceInfo.ts | 読み取り専用のデバイス/アプリ/携帯通信メタデータのラッパー |
date-time-picker | @react-native-community/datetimepicker | 画面レベルでのコンポーネント使用 | screen-builder のルールに従ってフォーム画面で直接使用してください。/add-native のラッパーは不要です |
Dataverse の File/Image フォームフィールド以外のカスタムワークフローでは、ユーザーが選択した写真や動画には /add-native image-picker を使用して image-picker 機能を、ドキュメントやその他のファイルには /add-native document-picker を使用して document-picker 機能を利用してください。これらのラッパーは、スコープ制限されたシステムピッカーのフローを使用します。Dataverse の File/Image フォームフィールドは例外です。前述のとおり、ホストの <FilePicker>/<ImagePicker> コントロールを使用してください。/add-native ラッパーや Expo モジュールの直接インポートは使用しないでください。 |
PDF/ペンのルーティングルール
- PDF に関するリクエストをすべて
document-pickerとして扱わないでください。 - ユーザーがローカルの PDF/ドキュメントを選択、インポート、またはアップロードしたい場合は、
document-pickerを使用してください。この用途は引き続きサポートされているため、引き続き使用してください。 - アプリがレコード、証拠、点検データ、証明書、領収書、またはレポートから PDF を生成する場合は、
package.jsonにexpo-printが存在するときに限り、pdf-reportを使用してください。 - アプリが
@microsoft/power-apps-native-pdf-viewer0.2.9+ を使用して HTTPS の PDF URL またはローカルのfile://URI を開く/プレビューする場合は、native-pdf-viewer/pdf-controlを使用してください。content://、blob:、およびhttp://の URI はサポートされていません。 - 「PDF を表示/開く」というリクエストがあっても Power Apps ビューアーパッケージがない場合、アプリが独自のレポートを生成しており、
expo-printが存在するときに限り、pdf-reportにフォールバックしてください。expo-printによる汎用的な PDF 表示のサポートをうたわないでください。expo-printはローカルファイルを生成するものであり、任意の PDF を表示するものではありません。 - 署名、承認の手書き、インクによるメモ、スケッチ、および手書きのサインオフに限り、
@microsoft/power-apps-native-pen-inputを使用するpen-inputを使用してください。 - その他の用途では、関連する Expo モジュール、または
package.jsonにすでに存在する別の依存関係を使用してください。無関係なワークフローに Power Apps 拡張機能を無理に適用しないでください。 expo-printで生成したローカル PDF の場合、開く/プレビューには native PDF viewer 0.2.9+、共有にはexpo-sharing、保持には Dataverse File ストレージを使用してください。ローカル PDF のプレビューにexpo-sharingを必須としないでください。- ユーザーが選択する Dataverse の File/Image フォームフィールドには、引き続きホストの
FilePickerとImagePickerが適切です。生成した PDF とペンで取得したデータには、まずネイティブラッパーを使用し、その後 Dataverse の永続化ヘルパーを使用してください。
Dataverse アーティファクトの永続化ルール
- ペン入力は、
data:image/png;base64,...のような PNG データ URI を返します。 - Dataverse の Image 列では、データ URI を生成されたサービスのペイロード形式に正規化してください。生の base64 が必要な場合は、
data:image/png;base64,のプレフィックスを取り除いてください。 - Dataverse の File 列では、生成された PDF または署名の PNG をアップロード用のバイト列/File に変換し、まず親行を保存または更新してから、生成されたサービスヘルパーを通じてアップロードしてください。
- File 列のバイト列を作成/更新用の JSON ボディに含めないでください。File のバイト列をアップロードするのは、親行の ID が存在してからに限ります。
- 画面では、非対応、キャンセル、アップロード失敗、およびビューアー失敗の状態を明示的に処理してください。ペン入力のキャンセルはエラーではない結果なので、画面側で無視できます。
パッケージがない場合またはゲートされている場合: package.json とランタイムゲートを正とします。該当するパッケージ/コントロールが存在しない場合は、透明性を保つ注記を添えて処理を中止してください。
ワークフロー
- プロジェクトを確認 → 2. 機能を解決 → 3. 専用スキルがあれば自動で振り分け → 4. モジュールがテンプレートに含まれていることを確認 → 5. ラッパーを作成 → 6. 型チェック → 7. 要約
ステップ 1 — プロジェクトを確認
cd -- '<working_dir>' || { echo "BLOCKED: cannot enter working_dir" >&2; exit 1; }
test -f app.config.js && test -f power.config.json && test -f package.json || { echo "BLOCKED: working_dir is not an initialized app" >&2; exit 1; }
Step 2 — 機能を特定する
テレメトリチェックポイント:resolve_native_capability
$ARGUMENTS に機能名、パッケージ名、またはコントロール名が含まれている場合は、それを使用します。含まれていない場合は、native-app-plan.md の ## Native Capabilities セクションを探し、予定されている機能を提示して確認を求めます。どちらもない場合は、上記の対応機能一覧に加え、リクエストに直接関連する package.json 内のインストール済みパッケージがあれば、それらを提示してユーザーに確認を求めます。
機能名を小文字のハイフン区切り形式に正規化します(例:Camera → camera、ImagePicker → image-picker、SecureStore → secure-store)。また、エイリアスも正規化します:take-photo / photo / camera-control / expo-camera → camera;gallery / pick-image / expo-image-picker → image-picker;scanner / barcode / qr → barcode-scanner;open-pdf / view-pdf / pdf-control / pdf-viewer-control / @microsoft/power-apps-native-pdf-viewer → pdf-viewer;native-pdf-viewer → pdf-viewer;generate-pdf / pdf-export → pdf-report;signature / sign-off / ink / draw / pen-control / @microsoft/power-apps-native-pen-input → pen-input;location-tracking / background-location / gps-tracking / geo-tracking / track-location / power-apps-native-bglocation / @microsoft/power-apps-native-bglocation → geolocation;vibration / vibration-feedback / impact-feedback / selection-feedback / notification-feedback / expo-haptics → haptics。
ユーザーが「location」または「GPS」と指定した場合は、意図に応じて区別します:継続的な追跡/バックグラウンド追跡、または Dataverse への永続的なアップロードの場合は geolocation(@microsoft/power-apps-native-bglocation);フォアグラウンドで単一の座標を読み取る場合は location(expo-location)。意図が不明確な場合は、振り分ける前に一度だけ質問してください。
ユーザーが対応表にないものを指定した場合は、Native 機能ゲートを適用します:package.json から該当するパッケージを特定し、そのパッケージが存在し、かつ実行時に禁止されていない場合にのみ続行します。それ以外の場合は、透明性に関する注記を添えて停止します。
Step 3 — ネストされたヘルパーまたはインラインラッパーに振り分ける
テレメトリチェックポイント:dispatch_native_capability
正規化された機能が camera、image-picker、barcode-scanner、qr-scanner、pdf-report、pdf-viewer、pen-input、または geolocation の場合は、汎用ラッパーフローに進まず、別のスラッシュコマンドを実行するようユーザーに指示しないでください。ネストされたヘルパーを読み込み、この /add-native の呼び出し内でその手順に従ってください:
cd -- '<working_dir>' || { echo "BLOCKED: cannot enter working_dir" >&2; exit 1; }
case "<capability>" in
camera|image-picker|barcode-scanner|qr-scanner) test -f "${PLUGIN_ROOT}/skills/add-native/add-camera/SKILL.md" && echo "INTERNAL_HELPER:add-camera" ;;
pdf-report) test -f "${PLUGIN_ROOT}/skills/add-native/add-pdf-report/SKILL.md" && echo "INTERNAL_HELPER:add-pdf-report" ;;
pdf-viewer) test -f "${PLUGIN_ROOT}/skills/add-native/add-pdf-viewer/SKILL.md" && echo "INTERNAL_HELPER:add-pdf-viewer" ;;
pen-input) test -f "${PLUGIN_ROOT}/skills/add-native/add-pen-input/SKILL.md" && echo "INTERNAL_HELPER:add-pen-input" ;;
geolocation) test -f "${PLUGIN_ROOT}/skills/add-native/add-geolocation/SKILL.md" && echo "INTERNAL_HELPER:add-geolocation" ;;
*) echo "INLINE" ;;
esac
- INTERNAL_HELPER: 表示されたヘルパーファイルを読み込み、同じ絶対パスの
working_dir、引数、提供済みの回答、モードフラグ、現在の owner/phase/approved_scopeを使って、そのワークフローを実行します。別のディレクトリから起動された場合でも、すべてのヘルパーは Step 0 を使って、そのルートにすべての操作を紐付ける必要があります。各ヘルパーは、書き込み/再利用の前と成功サマリーの前に native-artifact-compatibility.md を実行する必要があります。この分岐では、外側の Step 5 には進みません。NEEDS_CONTEXT/BLOCKEDとアーティファクトの結果を owner に伝えた後、このリーフを停止します。owner のワークフローは停止しないでください。インライン生成に進んだり、ヘルパーが部分的な結果を返した場合に成功を報告したりしないでください。 これらのコントロールでユーザーが実行するコマンドは、引き続き/add-nativeのみです。 - INLINE: Step 4 に進みます。
Step 4 — モジュールがテンプレートに含まれていることを確認する
対象のネイティブ機能パッケージが実際にプロジェクトの package.json に存在することを確認します(ユーザーが手動で削除した場合や、テンプレートのバージョンが想定より古い場合に検出できます):
cd -- '<working_dir>' || { echo "BLOCKED: cannot enter working_dir" >&2; exit 1; }
node -e "const p = require('./package.json'); const m = '<expo-module-name>'; if (!p.dependencies?.[m]) { console.error('MISSING: ' + m + ' is not in package.json. The template should ship it. Re-scaffold via /create-mobile-app, restore it from upstream, or wait for the template release that adds it — this skill will not install it.'); process.exit(1); }"
確認に失敗した場合は、停止します。npx expo install は実行しないでください。エラーを一字一句そのまま出力してください。
ステップ 5 — ラッパーの作成
テレメトリチェックポイント:generate_native_wrapper
開始前に出力:
"→ Writing src/native/.ts (typed wrapper with discriminated-union result + iOS/Android platform guards)…"
出力を書き込む、または再利用する前に、インライン機能行について
native-artifact-compatibility.md
のステップ 1~3 を読み、実行してください。
対象範囲内でファイルが存在しない場合に限り、src/native/<wrapper-filename>.ts を作成します。ファイルがすでに存在する場合は、動作とストレージを含め、承認済みの機能コントラクトに照らしてエクスポートを確認してください。互換性があれば変更せずに再利用し、すでに承認済みであれば、追加確認せず対象範囲内で更新してください。それ以外の場合は、更新前に所有者へ NEEDS_CONTEXT を返すか、単独で作業している場合は確認してください。機能を黙って省略したり、カスタムコードを上書きしたりしないでください。
各ラッパーは次をエクスポートします。
request<Capability>Permission(): Promise<boolean>— 基盤となる API に権限モデルがある場合に限ります(位置情報、生体認証、通知)。権限モデルがない機能(secure-store、file-system、sharing、clipboard)では省略します。- 型付きの結果を返す、1 つまたは 2 つのドメインメソッド。
screen-builder エージェントが依拠するコントラクト:
- すべてのラッパーは判別可能なユニオン型の結果(
{ ok: true, ... } | { ok: false, reason, message? })を返します。例外をスローしてはいけません - サポートされていないランタイム/プラットフォームの状態は、適切にフォールバックするか、
{ ok: false, reason: 'unsupported' }を返します。クラッシュしてはいけません - iOS と Android で機能が異なる場合は、サポート対象のネイティブプラットフォームに応じて分岐します
- 画面でこれらのラッパーをインポートするのは、Dataverse 以外のネイティブワークフローに限ります。Dataverse の File/Image フィールドでは、上記の File/Image Picker Ownership セクションに記載された
@microsoft/power-apps-native-hostコントロールを使用します。
Haptics ラッパー
正規化された機能 haptics について、次のコントラクトに従って src/native/haptics.ts を生成します。
import * as Haptics from 'expo-haptics';
import { Platform } from 'react-native';
export type HapticImpactStyle = 'light' | 'medium' | 'heavy' | 'soft' | 'rigid';
export type HapticNotificationType = 'success' | 'warning' | 'error';
export type HapticResult =
| { ok: true }
| { ok: false; reason: 'unsupported' | 'error'; message: string };
const impactStyles: Record<HapticImpactStyle, Haptics.ImpactFeedbackStyle> = {
light: Haptics.ImpactFeedbackStyle.Light,
medium: Haptics.ImpactFeedbackStyle.Medium,
heavy: Haptics.ImpactFeedbackStyle.Heavy,
soft: Haptics.ImpactFeedbackStyle.Soft,
rigid: Haptics.ImpactFeedbackStyle.Rigid,
};
const notificationTypes: Record<HapticNotificationType, Haptics.NotificationFeedbackType> = {
success: Haptics.NotificationFeedbackType.Success,
warning: Haptics.NotificationFeedbackType.Warning,
error: Haptics.NotificationFeedbackType.Error,
};
async function runHaptic(effect: () => Promise<void>): Promise<HapticResult> {
if (Platform.OS !== 'ios' && Platform.OS !== 'android') {
return {
ok: false,
reason: 'unsupported',
message: 'Haptic feedback is available only on iOS and Android.',
};
}
try {
await effect();
return { ok: true };
} catch (error) {
return {
ok: false,
reason: 'error',
message: error instanceof Error ? error.message : 'The native haptic call failed.',
};
}
}
export function triggerImpact(style: HapticImpactStyle = 'medium'): Promise<HapticResult> {
return runHaptic(() => Haptics.impactAsync(impactStyles[style]));
}
export function triggerNotification(
type: HapticNotificationType = 'success'
): Promise<HapticResult> {
return runHaptic(() => Haptics.notificationAsync(notificationTypes[type]));
}
export function triggerSelection(): Promise<HapticResult> {
return runHaptic(() => Haptics.selectionAsync());
}
画面からラッパーを呼び出すのは、承認済みの native-capabilities と画面ごとの計画に haptics が含まれている場合に限ります。意味に応じてフィードバックを選択してください。
- 意図的な押下や物理的な感触を伴う操作には
triggerImpact('light' | 'medium' | 'heavy' | 'soft' | 'rigid')を使用します。 - ピッカー、セグメントコントロール、その他の選択が変更されたときは
triggerSelection()を使用します。 - 対応する結果が判明した後に
triggerNotification('success' | 'warning' | 'error')を使用します。 - 視覚的なフィードバックを主要なシグナルとして維持します。OK 以外の結果を確認してログに記録しますが、触覚フィードバックが利用できないことを理由に、本来成功する業務処理を失敗扱いにしないでください。
操作が成功した後の画面での使用例:
setSaveState({ kind: 'success', message: 'Saved' });
const hapticResult = await triggerNotification('success');
if (!hapticResult.ok) {
console.warn('[haptics] success feedback unavailable', hapticResult);
}
expo-haptics には権限リクエストも app.config.js の変更も必要ありません。
その他のインラインラッパー
メソッドのシグネチャと権限のパターンについては、モジュールの公開 API ドキュメント(npm ページからリンクされています)を参照してください。判別可能なユニオン型の形を示す標準例として、以下の secure-store のスケルトンを使用し、対象モジュールの API に合わせて書き換えてください。
secure-store の標準スケルトン:
// src/native/secureStore.ts
import * as SecureStore from 'expo-secure-store';
import { Platform } from 'react-native';
export type SecureResult<T> =
| { ok: true; value: T }
| { ok: false; reason: 'unsupported' | 'not-found' | 'error'; message?: string };
export async function getSecret(key: string): Promise<SecureResult<string>> {
if (Platform.OS !== 'ios' && Platform.OS !== 'android') {
return { ok: false, reason: 'unsupported', message: 'SecureStore is not available on this platform.' };
}
try {
const value = await SecureStore.getItemAsync(key);
if (value === null) return { ok: false, reason: 'not-found' };
return { ok: true, value };
} catch (e: any) {
return { ok: false, reason: 'error', message: e?.message };
}
}
export async function setSecret(key: string, value: string): Promise<SecureResult<true>> {
if (Platform.OS !== 'ios' && Platform.OS !== 'android') {
return { ok: false, reason: 'unsupported', message: 'SecureStore is not available on this platform.' };
}
try {
await SecureStore.setItemAsync(key, value);
return { ok: true, value: true };
} catch (e: any) {
return { ok: false, reason: 'error', message: e?.message };
}
}
ステップ 6 — 型チェック
テレメトリチェックポイント:validate_native_wrapper
開始前に出力:
"→ Running tsc to verify wrapper compiles (~10–20 seconds)."
cd -- '<working_dir>' || { echo "BLOCKED: cannot enter working_dir" >&2; exit 1; }
npx --no-install tsc --noEmit
対象範囲内のラッパー側エラーのみを修正してください。型チェック後、共有互換性コントラクトの
ステップ 4 を実行してください。成功を返す前に、要求されたすべての成果物、API/動作、ストレージに関する要件を再確認します。型チェックの成功だけでは不十分です。通常の /add-native の使用時は、プラットフォーム固有のネイティブビルドコマンドを実行しないでください。モジュールはすでに現在のテンプレートバイナリに含まれているためです。テンプレート/ベースのメンテナーが初めて expo-haptics を追加する場合、またはそのバージョンを変更する場合は、Expo のオートリンクにモジュールを含めるため、そのリリースで Android と iOS のベースバイナリを再ビルドする必要があります。
ステップ 7 — 概要
概要の前に共有互換性の結果を返してください。実際に作成/更新/再利用したパスを使用し、未解決の成果物について完了したと主張しないでください。
✅ Native wrapper generated: <capability>
─────────────────────────────────────────────
Module (template-shipped) : <expo-module>@<version-from-package.json>
Wrapper created : src/native/<capability>.ts
package.json : unchanged ✓
app.config.js : unchanged ✓
Type-check: PASS
Sample usage:
import { takePhoto } from '../../src/native/camera';
const result = await takePhoto();
if (result.ok) {
setPhotoUri(result.uri);
} else if (result.reason === 'permission-denied') {
showToast('Camera permission required');
}
⚠️ No per-app native rebuild required when using a binary produced from the current
template. The underlying native module is already linked; wrapper edits are picked
up by Metro. Template/base dependency changes require a new native base build.
─────────────────────────────────────────────
注意事項
- このスキルは
package.json、app.config.js、src/playerConfig.ts、src/generated/、またはいずれかの画面ファイルを変更しません。 - サポート対象の表に記載されていない機能(
expo-notifications、Bluetooth、NFC、BLE、AR — テンプレートに追加されるまでは)については、テンプレートにはまだ含まれていないとユーザーに伝え、上流のテンプレートリポジトリにリクエストを提出してください。自分でインストールや設定を試みてはいけません。 - 純粋な JavaScript ライブラリはこのスキルの対象外です。
/create-mobile-appまたは/edit-appがshared/references/javascript-dependency-planning.mdを通じて選択し、インストールします。ネイティブラッパーや Android/iOS の再ビルドは不要です。上記の禁止事項は、ネイティブのソース/設定を含むパッケージにのみ適用されます。