build-ios-binary
Compile a PAM control's iOS Obj-C/Swift module into the FLAT device-slice `.framework` for a `.ppmplugin` bundle (never an `.xcframework` — the wrap CI won't descend into one). Mac-only (Xcode). Builds from a throwaway staged copy so canonical `ios/` and the podspec stay untouched, references React-Core headers only (React is weak-linked and provided by the wrap host at runtime, no CocoaPods), generates the required umbrella header, module map and Info.plist, sets the critical build settings, asserts manifest conformance, then runs a device-only `xcodebuild archive` and copies out the flat framework to `ppmplugin/staging/ios/`. Known limitation — the React weak-link flags aren't yet validated against a live wrap host and the RN pin must match the host's. Run after /generate-ppmplugin-manifest for an iOS or Both target, before /assemble-ppmplugin.
git clone --depth 1 https://github.com/microsoft/power-platform-skills /tmp/build-ios-binary && cp -r /tmp/build-ios-binary/plugins/power-apps-mobile-extension/skills/build-ios-binary ~/.claude/skills/build-ios-binarySKILL.md
# /build-ios-binary
Turns the extension's iOS **source** (`ios/RCT<Pascal>Module.{h,m}`) into the **flat `<Pascal>Plugin.framework/`** (device slice) that the wrap runtime loads. iOS analogue of [`/build-android-binary`](../build-android-binary/SKILL.md): source in, a prebuilt framework out.
> **Ship a FLAT `.framework`, NOT an `.xcframework`.** The wrap pipeline expects `ios/<Name>.framework` and does not descend into an `.xcframework` — shipping one fails with *"Framework '<Name>.framework' not found in plugin."* This skill builds the **device archive only** and copies out the flat `.framework`. See [`ppmplugin-format §5b`](../../shared/ppmplugin-format.md).
> **Mac-only.** Requires Xcode. On Linux/Windows this skill BLOCKs — Android-only contributors can't produce the iOS slice.
>
> **KNOWN LIMITATION — read this.** The single hard part is **React-Core**: the framework compiles against React's headers (`#import <React/RCTBridgeModule.h>`) from the control repo's own pinned `react-native` devDep, while **never embedding React** — the wrap host provides it at runtime (weak-link). The coupling that matters is the **RN version pin** (`0.79.7`): it must match the RN the wrap host ships. The exact weak-link flags / header-search-path setup aren't yet validated against a live wrap host — that's the main risk area. React symbol/header errors mean the RN pin diverged from the host's RN, not a code bug.
Read [`shared/ppmplugin-format.md`](../../shared/ppmplugin-format.md) §5b (iOS binary requirements) and the [`naming-conventions.md`](../../shared/naming-conventions.md) iOS rows.
## What this skill does NOT do
- Does not author `manifest.json` (run [`/generate-ppmplugin-manifest`](../generate-ppmplugin-manifest/SKILL.md) first — this skill reads it for cross-checks and the framework/moduleClass names).
- Does not zip the `.ppmplugin` — that's [`/assemble-ppmplugin`](../assemble-ppmplugin/SKILL.md).
- Does not build Android — that's `/build-android-binary`.
- Does not modify the canonical `ios/` or the podspec — all adjustments live in a throwaway copy under `ppmplugin/staging/ios-build/`.
- Does not sign the framework — the wrap pipeline signs at packaging time (given `BUILD_LIBRARY_FOR_DISTRIBUTION=YES SKIP_INSTALL=NO`). No signing skill needed.
---
## Step 1 — Read shared docs + prereq block
1. Read [`shared/shared-instructions.md`](../../shared/shared-instructions.md) and [`shared/ppmplugin-format.md`](../../shared/ppmplugin-format.md).
2. Read `ppmplugin/staging/manifest.json`. If absent, STOP with `NEEDS_CONTEXT: manifest.json missing — run /generate-ppmplugin-manifest first`. If it has no `entrypoints.ios`, STOP with `NEEDS_CONTEXT: manifest targets Android-only — re-run /generate-ppmplugin-manifest and choose iOS/Both`.
3. Read the `## ppmplugin (third-party controls)` block in `.extension-state.md`. **Replace-existing gate:** if `ppmplugin/staging/ios/<framework>.framework` already exists, surface it (with its build timestamp) and ask via `AskUserQuestion` — **Keep existing** (skip to Step 6) vs **Replace (rebuild)**. Default Keep if source + manifest unchanged since the recorded build, else Replace (note Keep is stale). Per [`ppmplugin-format §1`](../../shared/ppmplugin-format.md) replace-existing rule.
4. Run prereq checks and print the visible block (shared-instructions §9.2). **Policy: resolve, don't punt** (locate tools by path; offer + run safe installs):
| Check | Verify | Auto-fix |
|---|---|---|
| **macOS** | `uname` = Darwin | NOT fixable — STOP with `BLOCKED: /build-ios-binary is Mac-only (iOS needs Xcode)` on Linux/Windows |
| Xcode 16+ (26.2+ recommended) | `xcodebuild -version` | NOT auto-fixable — print: install Xcode from the App Store / xcodes; STOP if missing |
| React-Core headers present | `node_modules/react-native/React/Base/RCTBridgeModule.h` exists (the `react-native` devDep, in the control repo) | if the devDep isn't installed: `pnpm install` in the repo |
| CocoaPods (**optional** — fallback path only) | `pod --version` | Not required for the recommended header-only build (Step 2). Only needed if you fall back to the Podfile path; `brew install cocoapods` on confirm. |
---
## Step 2 — React-Core source (self-contained) + confirm the config
The framework needs React's headers at compile time. **The source is the control repo's own `node_modules/react-native`** — the pinned `react-native` devDep (`0.79.7` for pen-input), which ships the React headers (`React/Base/RCTBridgeModule.h`). The build is fully self-contained in the control repo. If the devDep isn't installed, run `pnpm install`; only if it's genuinely absent, ask the user for a `react-native` path.
**Recommended: header-only React (no CocoaPods).** Aggregate the React headers into a flat include dir in the staging copy and point the framework target at it — cleaner and faster than the Pod chain, and it avoids the CocoaPods/React-Codegen failures that bite under recent Xcode (see [`ppmplugin-format §5b`](../../shared/ppmplugin-format.md)):
```bash
mkdir -p ppmplugin/staging/ios-build/include/React
# copy the RN headers the module imports (Base + the Libraries it uses):
cp node_modules/react-native/React/Base/*.h ppmplugin/staging/ios-build/include/React/
cp -R node_modules/react-native/React/Base node_modules/react-native/Libraries ppmplugin/staging/ios-build/include/React/ 2>/dev/null || true
```
Then set on the framework target: `HEADER_SEARCH_PATHS = $(inherited) $(SRCROOT)/include` and `OTHER_LDFLAGS = -undefined dynamic_lookup` (weak-links React's symbols — the host provides them at runtime). **`-undefined dynamic_lookup` is deprecated by Apple** (works today, may break in a future Xcode — known caveat).
**Fallback: CocoaPods** — only if the header-only path doesn't suffice. A Podfile referencing the devDep (`pod 'React-Core', :path => '<repo>/node_modules/react-native'`) gives CocoaPods the header map, but on recent Xcode/RN-0.79 you'll need `post_install` patches (boost URL,Guide the user to add a data source, connection, or API connector to a Canvas App via Power Apps Studio, then verify and continue. USE WHEN the user asks to add a data source, add a connection, add an API, add a connector, connect to SharePoint / Dataverse / SQL / Excel / OneDrive / Teams / Office 365, or any similar request to make new data available to the app. DO NOT USE WHEN the user is asking to list or describe existing data sources — call list_data_sources or list_apis directly instead.
Creates or edits a Power Apps Canvas App through the Canvas Authoring MCP coauthoring session. Handles new app generation, direct targeted edits, complex multi-screen changes, responsive layout, per-screen self-QA, and compile-error convergence. Trigger on requests to create, build, generate, modify, update, change, fix, or edit a Canvas App or .pa.yaml files.
Configure the Canvas Authoring MCP server for the current coauthoring session. USE WHEN "configure MCP", "set up MCP server", "MCP not working", "connect Canvas Apps MCP", "canvas-authoring not available", "MCP not configured", "set up canvas apps".
[DEPRECATED — use canvas-app instead] Generate a complete Power Apps canvas app.
>
Adds Azure DevOps connector to a Power Apps code app. Use when querying work items, creating bugs, managing pipelines, or making ADO API calls.
Use when adding a Power Platform connector to an Expo/React Native Power Apps mobile app and no dedicated mobile connector skill exists.
Use when adding an unspecified data source to an Expo/React Native Power Apps mobile app; routes to Dataverse, SharePoint, or another connector.