[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M699QDP5NF6BWR2Z6VABWJ7M":3,"skill-same:01M699QDP5NF6BWR2Z6VABWJ7M":76},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":23,"publishedAt":45,"linkOnly":46,"markdownContent":47,"markdownBody":48,"frontMatter":49,"refName":52,"commitSha":53,"skillDirectory":54,"forksCount":55,"repositoryHtmlUrl":56,"repositoryHomepageUrl":57,"ownerHtmlUrl":58,"ownerBlogUrl":59,"ownerDescription":60,"updatedAt":61,"install":62,"resources":75},"01M699QDP5NF6BWR2Z6VABWJ7M","build-ios-binary",null,"Compile a PAM control's iOS Obj-C\u002FSwift 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\u002F` 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\u002Fstaging\u002Fios\u002F`. 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 \u002Fgenerate-ppmplugin-manifest for an iOS or Both target, before \u002Fassemble-ppmplugin.","microsoft\u002Fpower-platform-skills","microsoft","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F6154722?v=4","01M698VYDXZ6JW5WH3CRV650R8","Microsoft","01M699JVYJFB75D9JM70BN4H0Q",1143,"MIT","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fpower-platform-skills\u002Fblob\u002Fmain\u002Fplugins\u002Fpower-apps-mobile-extension\u002Fskills\u002Fbuild-ios-binary\u002FSKILL.md",[18],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000009","前端开发","SCENARIO","使用场景",[24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44],"build ios binary","ios","binary","build ios","build","references","OBJ","throwaway","host","critical","xcode","map","info","swift","only","never","assemble","Bash","PAM","ci","react","2026-11-10 10:12:30",false,"---\nname: build-ios-binary\ndescription: \"Compile a PAM control's iOS Obj-C\u002FSwift 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\u002F` 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\u002Fstaging\u002Fios\u002F`. 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 \u002Fgenerate-ppmplugin-manifest for an iOS or Both target, before \u002Fassemble-ppmplugin.\"\nallowed-tools: Read, Write, Edit, Bash, Glob, Grep, AskUserQuestion, Skill\nmodel: sonnet\n---\n\n# \u002Fbuild-ios-binary\n\nTurns the extension's iOS **source** (`ios\u002FRCT\u003CPascal>Module.{h,m}`) into the **flat `\u003CPascal>Plugin.framework\u002F`** (device slice) that the wrap runtime loads. iOS analogue of [`\u002Fbuild-android-binary`](..\u002Fbuild-android-binary\u002FSKILL.md): source in, a prebuilt framework out.\n\n> **Ship a FLAT `.framework`, NOT an `.xcframework`.** The wrap pipeline expects `ios\u002F\u003CName>.framework` and does not descend into an `.xcframework` — shipping one fails with *\"Framework '\u003CName>.framework' not found in plugin.\"* This skill builds the **device archive only** and copies out the flat `.framework`. See [`ppmplugin-format §5b`](..\u002F..\u002Fshared\u002Fppmplugin-format.md).\n\n> **Mac-only.** Requires Xcode. On Linux\u002FWindows this skill BLOCKs — Android-only contributors can't produce the iOS slice.\n>\n> **KNOWN LIMITATION — read this.** The single hard part is **React-Core**: the framework compiles against React's headers (`#import \u003CReact\u002FRCTBridgeModule.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 \u002F header-search-path setup aren't yet validated against a live wrap host — that's the main risk area. React symbol\u002Fheader errors mean the RN pin diverged from the host's RN, not a code bug.\n\nRead [`shared\u002Fppmplugin-format.md`](..\u002F..\u002Fshared\u002Fppmplugin-format.md) §5b (iOS binary requirements) and the [`naming-conventions.md`](..\u002F..\u002Fshared\u002Fnaming-conventions.md) iOS rows.\n\n## What this skill does NOT do\n- Does not author `manifest.json` (run [`\u002Fgenerate-ppmplugin-manifest`](..\u002Fgenerate-ppmplugin-manifest\u002FSKILL.md) first — this skill reads it for cross-checks and the framework\u002FmoduleClass names).\n- Does not zip the `.ppmplugin` — that's [`\u002Fassemble-ppmplugin`](..\u002Fassemble-ppmplugin\u002FSKILL.md).\n- Does not build Android — that's `\u002Fbuild-android-binary`.\n- Does not modify the canonical `ios\u002F` or the podspec — all adjustments live in a throwaway copy under `ppmplugin\u002Fstaging\u002Fios-build\u002F`.\n- 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.\n\n---\n\n## Step 1 — Read shared docs + prereq block\n\n1. Read [`shared\u002Fshared-instructions.md`](..\u002F..\u002Fshared\u002Fshared-instructions.md) and [`shared\u002Fppmplugin-format.md`](..\u002F..\u002Fshared\u002Fppmplugin-format.md).\n2. Read `ppmplugin\u002Fstaging\u002Fmanifest.json`. If absent, STOP with `NEEDS_CONTEXT: manifest.json missing — run \u002Fgenerate-ppmplugin-manifest first`. If it has no `entrypoints.ios`, STOP with `NEEDS_CONTEXT: manifest targets Android-only — re-run \u002Fgenerate-ppmplugin-manifest and choose iOS\u002FBoth`.\n3. Read the `## ppmplugin (third-party controls)` block in `.extension-state.md`. **Replace-existing gate:** if `ppmplugin\u002Fstaging\u002Fios\u002F\u003Cframework>.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`](..\u002F..\u002Fshared\u002Fppmplugin-format.md) replace-existing rule.\n4. 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):\n\n| Check | Verify | Auto-fix |\n|---|---|---|\n| **macOS** | `uname` = Darwin | NOT fixable — STOP with `BLOCKED: \u002Fbuild-ios-binary is Mac-only (iOS needs Xcode)` on Linux\u002FWindows |\n| Xcode 16+ (26.2+ recommended) | `xcodebuild -version` | NOT auto-fixable — print: install Xcode from the App Store \u002F xcodes; STOP if missing |\n| React-Core headers present | `node_modules\u002Freact-native\u002FReact\u002FBase\u002FRCTBridgeModule.h` exists (the `react-native` devDep, in the control repo) | if the devDep isn't installed: `pnpm install` in the repo |\n| 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. |\n\n---\n\n## Step 2 — React-Core source (self-contained) + confirm the config\n\nThe framework needs React's headers at compile time. **The source is the control repo's own `node_modules\u002Freact-native`** — the pinned `react-native` devDep (`0.79.7` for pen-input), which ships the React headers (`React\u002FBase\u002FRCTBridgeModule.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.\n\n**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\u002FReact-Codegen failures that bite under recent Xcode (see [`ppmplugin-format §5b`](..\u002F..\u002Fshared\u002Fppmplugin-format.md)):\n```bash\nmkdir -p ppmplugin\u002Fstaging\u002Fios-build\u002Finclude\u002FReact\n# copy the RN headers the module imports (Base + the Libraries it uses):\ncp node_modules\u002Freact-native\u002FReact\u002FBase\u002F*.h        ppmplugin\u002Fstaging\u002Fios-build\u002Finclude\u002FReact\u002F\ncp -R node_modules\u002Freact-native\u002FReact\u002FBase node_modules\u002Freact-native\u002FLibraries  ppmplugin\u002Fstaging\u002Fios-build\u002Finclude\u002FReact\u002F 2>\u002Fdev\u002Fnull || true\n```\nThen set on the framework target: `HEADER_SEARCH_PATHS = $(inherited) $(SRCROOT)\u002Finclude` 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).\n\n**Fallback: CocoaPods** — only if the header-only path doesn't suffice. A Podfile referencing the devDep (`pod 'React-Core', :path => '\u003Crepo>\u002Fnode_modules\u002Freact-native'`) gives CocoaPods the header map, but on recent Xcode\u002FRN-0.79 you'll need `post_install` patches (boost URL, Yoga `_pt`, RCT-Folly `clockid_t`, boost `std::unary_function`) and may still hit the React-Codegen sandbox failure — all documented in ppmplugin-format §5b.\n\nWhat matters either way is that the **pinned RN version** (`0.79.7`) matches the RN the wrap host ships at runtime (React is weak-linked, host-provided). Print the detected RN version and ask via `AskUserQuestion` to **confirm \u002F override** the source + version before building. State plainly: *\"React is provided by the wrap host at runtime; React symbol\u002Fheader errors at build or load mean the RN pin has diverged from the host's RN version.\"*\n\n---\n\n## Step 3 — Stage a standalone Xcode framework project (canonical `ios\u002F` stays pristine)\n\nLike the Android build, work from a throwaway copy so the source the engineer maintains is never touched.\n\n1. **Copy** `ios\u002F*.{h,m}` → `ppmplugin\u002Fstaging\u002Fios-build\u002FSources\u002F` (fresh each run).\n2. **Generate the umbrella header** `ppmplugin\u002Fstaging\u002Fios-build\u002F\u003CPascal>Plugin.h` and mark it a **Public** header (**build hygiene, not runtime-required** — the wrap player `dlopen`s the binary and uses `NSClassFromString`, it never `import`s the module, so the framework loads fine without a module map; the umbrella just silences the `DEFINES_MODULE` warning. Generate it anyway for a clean build). Minimal contents:\n   ```objc\n   #import \u003CFoundation\u002FFoundation.h>\n   FOUNDATION_EXPORT double \u003CPascal>PluginVersionNumber;\n   FOUNDATION_EXPORT const unsigned char \u003CPascal>PluginVersionString[];\n   ```\n3. **Generate a standalone Xcode framework project** `ppmplugin\u002Fstaging\u002Fios-build\u002F\u003CPascal>Plugin.xcodeproj`, product\u002Ftarget name `\u003CPascal>Plugin` (= `entrypoints.ios.framework`), compiling the copied sources, with the umbrella as the Public header. Derive from the canonical podspec:\n   - **System frameworks** to link: from the podspec `s.frameworks` (e.g. pen-input → `PencilKit, UIKit, CoreGraphics, QuartzCore`).\n   - **Deployment target**: from `s.platform` (e.g. `:ios, \"14.0\"`) — but floor it at the supported PAM shell minimum **iOS 16.0**.\n   - **xcodeproj-gem trap** (if generating via the `xcodeproj` gem): when a group already carries its path (e.g. `Sources`), add file refs **filename-only** — `new_group('Sources','Sources')` + `new_reference(\"Sources\u002F\u003Cfile>\")` doubles to `Sources\u002FSources\u002F\u003Cfile>` → file-not-found. Pin a known-good `gen_project.rb` rather than re-deriving it each run.\n4. **Set the build settings** (each maps to a documented wrap\u002Fon-device failure — see [`ppmplugin-format §5b`](..\u002F..\u002Fshared\u002Fppmplugin-format.md)):\n\n   | Setting | Value |\n   |---|---|\n   | Mach-O Type | **Dynamic Library** (NOT Static) |\n   | Dead Code Stripping | **NO** (preserves the `+moduleName` class method and `RCT_EXPORT_METHOD` metadata the host reads after `dlopen`) |\n   | Defines Module | **YES** |\n   | Enable Bitcode | **NO** |\n   | `HEADER_SEARCH_PATHS` | `$(inherited) $(SRCROOT)\u002Finclude` (header-only React from Step 2) |\n   | `OTHER_LDFLAGS` | `-undefined dynamic_lookup` (weak-link React; host-provided at runtime) |\n   | `BUILD_LIBRARY_FOR_DISTRIBUTION` | **YES** |\n   | `SKIP_INSTALL` | **NO** |\n   | `GENERATE_INFOPLIST_FILE` | **YES** (REQUIRED — a framework with no Info.plist is rejected by the wrap codesign step) |\n   | `INFOPLIST_KEY_CFBundleDisplayName` | `\u003CPascal>Plugin` |\n   | `MARKETING_VERSION` | `\u003Cversion from manifest>` |\n   | `CURRENT_PROJECT_VERSION` | `1` |\n   | `PRODUCT_BUNDLE_IDENTIFIER` | `com.powerapps.\u003Clowername>` |\n\nThe canonical `ios\u002F` + podspec are never modified — report the staging adjustments applied.\n\n---\n\n## Step 4 — Assert manifest conformance\n\nBefore building, verify the source matches the manifest (the iOS analogue of the Android DexClassLoader asserts):\n- Class name in `ios\u002FRCT\u003CPascal>Module.h` == `manifest.entrypoints.ios.moduleClass`.\n- Class name in the `.m` == `manifest.entrypoints.ios.moduleClass`, and `+ (NSString *)moduleName` returns `manifest.receivers[].nativeModule`.\n- Each `RCT_EXPORT_METHOD(\u003Cname>:…)` name is on `manifest.receivers[].methods`.\n- **The module class instantiates via no-arg `[cls new]`** — the runtime resolves `moduleClass` after loading the framework and constructs it with `[cls new]`. A custom designated initializer that takes arguments breaks instantiation → the plugin is skipped at load. If the `.m` declares an `- (instancetype)initWith…` and no plain `init`, STOP with `BLOCKED: \u003CmoduleClass> must support no-arg initialization`.\n- **Each `RCT_EXPORT_METHOD` takes one `NSDictionary *request` first param** (then resolver\u002Frejecter) — the wrap proxy spreads the PCF's `args: [request]` positionally. A method expanding the request into multiple positional params won't receive its data; surface a WARNING (a no-arg op like `getStatus` taking only resolver\u002Frejecter is fine — [ppmplugin-format §2](..\u002F..\u002Fshared\u002Fppmplugin-format.md)).\n\nA mismatch means the manifest and the binary disagree — the call won't reach the module on device. STOP with the specific mismatch rather than building a broken pair.\n\n---\n\n## Step 5 — Build the flat `.framework` (device slice only)\n\nRun for real; surface the artifact path, not the streaming log. **Archive the DEVICE slice only** (the wrap CI wants a flat `\u003CName>.framework`, not an `.xcframework` — see §5b), then copy the framework straight out. **No `-create-xcframework` step.**\n\n```bash\ncd ppmplugin\u002Fstaging\u002Fios-build\n# device archive only:\nxcodebuild archive -project \u003CPascal>Plugin.xcodeproj -scheme \u003CPascal>Plugin \\\n  -destination \"generic\u002Fplatform=iOS\" \\\n  -archivePath build\u002F\u003CPascal>Plugin-device.xcarchive \\\n  SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES\n\n# copy the flat framework out (NO xcframework wrapper):\nrm -rf ..\u002Fios\u002F\u003CPascal>Plugin.framework\ncp -R build\u002F\u003CPascal>Plugin-device.xcarchive\u002FProducts\u002FLibrary\u002FFrameworks\u002F\u003CPascal>Plugin.framework ..\u002Fios\u002F\u003CPascal>Plugin.framework\n\nstrip -x ..\u002Fios\u002F\u003CPascal>Plugin.framework\u002F\u003CPascal>Plugin   # optional, if size matters\n```\n\n(If you built with CocoaPods instead of header-only, archive the `.xcworkspace` scheme rather than the `.xcodeproj` — otherwise identical: still device-only, still copy the flat framework.)\n\nOn failure, print the failing step + the most relevant `xcodebuild` error line and STOP with `BLOCKED: xcodebuild failed — \u003Cline>`. The likely standalone failures (and where they come from) are pinned in ppmplugin-format §5b — most route back to **React linking** (Step 2): symbol-not-found at link → React not weak-linked (`-undefined dynamic_lookup` missing); `\u003CReact\u002F…>` not found → `HEADER_SEARCH_PATHS` wrong; symbol *mismatch* → the RN pin doesn't match the host's RN. Surface these as React\u002FRN-version coupling issues, not extension-code bugs.\n\n**Verify the framework.** `ppmplugin\u002Fstaging\u002Fios\u002F\u003CPascal>Plugin.framework\u002F` MUST contain the two runtime-required items:\n- `\u003CPascal>Plugin` — the Mach-O binary, named exactly `\u003CPascal>Plugin` (the player `dlopen`s `Frameworks\u002F\u003CPascal>Plugin.framework\u002F\u003CPascal>Plugin`; a differently-named binary won't load). REQUIRED.\n- `Info.plist` — REQUIRED (a framework bundle without it is invalid and fails the wrap codesign step).\n\nIf either is missing, STOP with `BLOCKED: framework incomplete — missing \u003CX>` and the §5b fix (`GENERATE_INFOPLIST_FILE=YES` \u002F the binary name == framework name). The umbrella header + `Modules\u002Fmodule.modulemap` are build hygiene — note them if absent, but do NOT block on them (the `dlopen` loader doesn't use them — §5b).\n\n---\n\n## Step 6 — Report + next step\n\nUpdate the `## ppmplugin (third-party controls)` block in `.extension-state.md`: `iOS framework: built \u003CISO timestamp> (\u003CPascal>Plugin.framework, device slice)`. Print a fenced summary with the `.framework` path (and note it carries umbrella + module.modulemap + Info.plist), then offer next steps via `AskUserQuestion` (shared-instructions §9.1 — **invoke the chosen skill via the Skill tool; execute, don't describe**):\n\n- **Run \u002Fassemble-ppmplugin** (recommended — zip the manifest + binaries into the `.ppmplugin`)\n- **Run \u002Fbuild-android-binary** (if you also need the Android slice for a Both bundle)\n- **Stay — I'll inspect the framework first**\n\nReturn `DONE` with the `.framework` path, or `BLOCKED` with the failing `xcodebuild` line (most often React linking \u002F an RN-version mismatch with the host — not an extension-code fix).\n","# \u002Fbuild-ios-binary\n\nTurns the extension's iOS **source** (`ios\u002FRCT\u003CPascal>Module.{h,m}`) into the **flat `\u003CPascal>Plugin.framework\u002F`** (device slice) that the wrap runtime loads. iOS analogue of [`\u002Fbuild-android-binary`](..\u002Fbuild-android-binary\u002FSKILL.md): source in, a prebuilt framework out.\n\n> **Ship a FLAT `.framework`, NOT an `.xcframework`.** The wrap pipeline expects `ios\u002F\u003CName>.framework` and does not descend into an `.xcframework` — shipping one fails with *\"Framework '\u003CName>.framework' not found in plugin.\"* This skill builds the **device archive only** and copies out the flat `.framework`. See [`ppmplugin-format §5b`](..\u002F..\u002Fshared\u002Fppmplugin-format.md).\n\n> **Mac-only.** Requires Xcode. On Linux\u002FWindows this skill BLOCKs — Android-only contributors can't produce the iOS slice.\n>\n> **KNOWN LIMITATION — read this.** The single hard part is **React-Core**: the framework compiles against React's headers (`#import \u003CReact\u002FRCTBridgeModule.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 \u002F header-search-path setup aren't yet validated against a live wrap host — that's the main risk area. React symbol\u002Fheader errors mean the RN pin diverged from the host's RN, not a code bug.\n\nRead [`shared\u002Fppmplugin-format.md`](..\u002F..\u002Fshared\u002Fppmplugin-format.md) §5b (iOS binary requirements) and the [`naming-conventions.md`](..\u002F..\u002Fshared\u002Fnaming-conventions.md) iOS rows.\n\n## What this skill does NOT do\n- Does not author `manifest.json` (run [`\u002Fgenerate-ppmplugin-manifest`](..\u002Fgenerate-ppmplugin-manifest\u002FSKILL.md) first — this skill reads it for cross-checks and the framework\u002FmoduleClass names).\n- Does not zip the `.ppmplugin` — that's [`\u002Fassemble-ppmplugin`](..\u002Fassemble-ppmplugin\u002FSKILL.md).\n- Does not build Android — that's `\u002Fbuild-android-binary`.\n- Does not modify the canonical `ios\u002F` or the podspec — all adjustments live in a throwaway copy under `ppmplugin\u002Fstaging\u002Fios-build\u002F`.\n- 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.\n\n---\n\n## Step 1 — Read shared docs + prereq block\n\n1. Read [`shared\u002Fshared-instructions.md`](..\u002F..\u002Fshared\u002Fshared-instructions.md) and [`shared\u002Fppmplugin-format.md`](..\u002F..\u002Fshared\u002Fppmplugin-format.md).\n2. Read `ppmplugin\u002Fstaging\u002Fmanifest.json`. If absent, STOP with `NEEDS_CONTEXT: manifest.json missing — run \u002Fgenerate-ppmplugin-manifest first`. If it has no `entrypoints.ios`, STOP with `NEEDS_CONTEXT: manifest targets Android-only — re-run \u002Fgenerate-ppmplugin-manifest and choose iOS\u002FBoth`.\n3. Read the `## ppmplugin (third-party controls)` block in `.extension-state.md`. **Replace-existing gate:** if `ppmplugin\u002Fstaging\u002Fios\u002F\u003Cframework>.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`](..\u002F..\u002Fshared\u002Fppmplugin-format.md) replace-existing rule.\n4. 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):\n\n| Check | Verify | Auto-fix |\n|---|---|---|\n| **macOS** | `uname` = Darwin | NOT fixable — STOP with `BLOCKED: \u002Fbuild-ios-binary is Mac-only (iOS needs Xcode)` on Linux\u002FWindows |\n| Xcode 16+ (26.2+ recommended) | `xcodebuild -version` | NOT auto-fixable — print: install Xcode from the App Store \u002F xcodes; STOP if missing |\n| React-Core headers present | `node_modules\u002Freact-native\u002FReact\u002FBase\u002FRCTBridgeModule.h` exists (the `react-native` devDep, in the control repo) | if the devDep isn't installed: `pnpm install` in the repo |\n| 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. |\n\n---\n\n## Step 2 — React-Core source (self-contained) + confirm the config\n\nThe framework needs React's headers at compile time. **The source is the control repo's own `node_modules\u002Freact-native`** — the pinned `react-native` devDep (`0.79.7` for pen-input), which ships the React headers (`React\u002FBase\u002FRCTBridgeModule.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.\n\n**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\u002FReact-Codegen failures that bite under recent Xcode (see [`ppmplugin-format §5b`](..\u002F..\u002Fshared\u002Fppmplugin-format.md)):\n```bash\nmkdir -p ppmplugin\u002Fstaging\u002Fios-build\u002Finclude\u002FReact\n# copy the RN headers the module imports (Base + the Libraries it uses):\ncp node_modules\u002Freact-native\u002FReact\u002FBase\u002F*.h        ppmplugin\u002Fstaging\u002Fios-build\u002Finclude\u002FReact\u002F\ncp -R node_modules\u002Freact-native\u002FReact\u002FBase node_modules\u002Freact-native\u002FLibraries  ppmplugin\u002Fstaging\u002Fios-build\u002Finclude\u002FReact\u002F 2>\u002Fdev\u002Fnull || true\n```\nThen set on the framework target: `HEADER_SEARCH_PATHS = $(inherited) $(SRCROOT)\u002Finclude` 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).\n\n**Fallback: CocoaPods** — only if the header-only path doesn't suffice. A Podfile referencing the devDep (`pod 'React-Core', :path => '\u003Crepo>\u002Fnode_modules\u002Freact-native'`) gives CocoaPods the header map, but on recent Xcode\u002FRN-0.79 you'll need `post_install` patches (boost URL, Yoga `_pt`, RCT-Folly `clockid_t`, boost `std::unary_function`) and may still hit the React-Codegen sandbox failure — all documented in ppmplugin-format §5b.\n\nWhat matters either way is that the **pinned RN version** (`0.79.7`) matches the RN the wrap host ships at runtime (React is weak-linked, host-provided). Print the detected RN version and ask via `AskUserQuestion` to **confirm \u002F override** the source + version before building. State plainly: *\"React is provided by the wrap host at runtime; React symbol\u002Fheader errors at build or load mean the RN pin has diverged from the host's RN version.\"*\n\n---\n\n## Step 3 — Stage a standalone Xcode framework project (canonical `ios\u002F` stays pristine)\n\nLike the Android build, work from a throwaway copy so the source the engineer maintains is never touched.\n\n1. **Copy** `ios\u002F*.{h,m}` → `ppmplugin\u002Fstaging\u002Fios-build\u002FSources\u002F` (fresh each run).\n2. **Generate the umbrella header** `ppmplugin\u002Fstaging\u002Fios-build\u002F\u003CPascal>Plugin.h` and mark it a **Public** header (**build hygiene, not runtime-required** — the wrap player `dlopen`s the binary and uses `NSClassFromString`, it never `import`s the module, so the framework loads fine without a module map; the umbrella just silences the `DEFINES_MODULE` warning. Generate it anyway for a clean build). Minimal contents:\n   ```objc\n   #import \u003CFoundation\u002FFoundation.h>\n   FOUNDATION_EXPORT double \u003CPascal>PluginVersionNumber;\n   FOUNDATION_EXPORT const unsigned char \u003CPascal>PluginVersionString[];\n   ```\n3. **Generate a standalone Xcode framework project** `ppmplugin\u002Fstaging\u002Fios-build\u002F\u003CPascal>Plugin.xcodeproj`, product\u002Ftarget name `\u003CPascal>Plugin` (= `entrypoints.ios.framework`), compiling the copied sources, with the umbrella as the Public header. Derive from the canonical podspec:\n   - **System frameworks** to link: from the podspec `s.frameworks` (e.g. pen-input → `PencilKit, UIKit, CoreGraphics, QuartzCore`).\n   - **Deployment target**: from `s.platform` (e.g. `:ios, \"14.0\"`) — but floor it at the supported PAM shell minimum **iOS 16.0**.\n   - **xcodeproj-gem trap** (if generating via the `xcodeproj` gem): when a group already carries its path (e.g. `Sources`), add file refs **filename-only** — `new_group('Sources','Sources')` + `new_reference(\"Sources\u002F\u003Cfile>\")` doubles to `Sources\u002FSources\u002F\u003Cfile>` → file-not-found. Pin a known-good `gen_project.rb` rather than re-deriving it each run.\n4. **Set the build settings** (each maps to a documented wrap\u002Fon-device failure — see [`ppmplugin-format §5b`](..\u002F..\u002Fshared\u002Fppmplugin-format.md)):\n\n   | Setting | Value |\n   |---|---|\n   | Mach-O Type | **Dynamic Library** (NOT Static) |\n   | Dead Code Stripping | **NO** (preserves the `+moduleName` class method and `RCT_EXPORT_METHOD` metadata the host reads after `dlopen`) |\n   | Defines Module | **YES** |\n   | Enable Bitcode | **NO** |\n   | `HEADER_SEARCH_PATHS` | `$(inherited) $(SRCROOT)\u002Finclude` (header-only React from Step 2) |\n   | `OTHER_LDFLAGS` | `-undefined dynamic_lookup` (weak-link React; host-provided at runtime) |\n   | `BUILD_LIBRARY_FOR_DISTRIBUTION` | **YES** |\n   | `SKIP_INSTALL` | **NO** |\n   | `GENERATE_INFOPLIST_FILE` | **YES** (REQUIRED — a framework with no Info.plist is rejected by the wrap codesign step) |\n   | `INFOPLIST_KEY_CFBundleDisplayName` | `\u003CPascal>Plugin` |\n   | `MARKETING_VERSION` | `\u003Cversion from manifest>` |\n   | `CURRENT_PROJECT_VERSION` | `1` |\n   | `PRODUCT_BUNDLE_IDENTIFIER` | `com.powerapps.\u003Clowername>` |\n\nThe canonical `ios\u002F` + podspec are never modified — report the staging adjustments applied.\n\n---\n\n## Step 4 — Assert manifest conformance\n\nBefore building, verify the source matches the manifest (the iOS analogue of the Android DexClassLoader asserts):\n- Class name in `ios\u002FRCT\u003CPascal>Module.h` == `manifest.entrypoints.ios.moduleClass`.\n- Class name in the `.m` == `manifest.entrypoints.ios.moduleClass`, and `+ (NSString *)moduleName` returns `manifest.receivers[].nativeModule`.\n- Each `RCT_EXPORT_METHOD(\u003Cname>:…)` name is on `manifest.receivers[].methods`.\n- **The module class instantiates via no-arg `[cls new]`** — the runtime resolves `moduleClass` after loading the framework and constructs it with `[cls new]`. A custom designated initializer that takes arguments breaks instantiation → the plugin is skipped at load. If the `.m` declares an `- (instancetype)initWith…` and no plain `init`, STOP with `BLOCKED: \u003CmoduleClass> must support no-arg initialization`.\n- **Each `RCT_EXPORT_METHOD` takes one `NSDictionary *request` first param** (then resolver\u002Frejecter) — the wrap proxy spreads the PCF's `args: [request]` positionally. A method expanding the request into multiple positional params won't receive its data; surface a WARNING (a no-arg op like `getStatus` taking only resolver\u002Frejecter is fine — [ppmplugin-format §2](..\u002F..\u002Fshared\u002Fppmplugin-format.md)).\n\nA mismatch means the manifest and the binary disagree — the call won't reach the module on device. STOP with the specific mismatch rather than building a broken pair.\n\n---\n\n## Step 5 — Build the flat `.framework` (device slice only)\n\nRun for real; surface the artifact path, not the streaming log. **Archive the DEVICE slice only** (the wrap CI wants a flat `\u003CName>.framework`, not an `.xcframework` — see §5b), then copy the framework straight out. **No `-create-xcframework` step.**\n\n```bash\ncd ppmplugin\u002Fstaging\u002Fios-build\n# device archive only:\nxcodebuild archive -project \u003CPascal>Plugin.xcodeproj -scheme \u003CPascal>Plugin \\\n  -destination \"generic\u002Fplatform=iOS\" \\\n  -archivePath build\u002F\u003CPascal>Plugin-device.xcarchive \\\n  SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES\n\n# copy the flat framework out (NO xcframework wrapper):\nrm -rf ..\u002Fios\u002F\u003CPascal>Plugin.framework\ncp -R build\u002F\u003CPascal>Plugin-device.xcarchive\u002FProducts\u002FLibrary\u002FFrameworks\u002F\u003CPascal>Plugin.framework ..\u002Fios\u002F\u003CPascal>Plugin.framework\n\nstrip -x ..\u002Fios\u002F\u003CPascal>Plugin.framework\u002F\u003CPascal>Plugin   # optional, if size matters\n```\n\n(If you built with CocoaPods instead of header-only, archive the `.xcworkspace` scheme rather than the `.xcodeproj` — otherwise identical: still device-only, still copy the flat framework.)\n\nOn failure, print the failing step + the most relevant `xcodebuild` error line and STOP with `BLOCKED: xcodebuild failed — \u003Cline>`. The likely standalone failures (and where they come from) are pinned in ppmplugin-format §5b — most route back to **React linking** (Step 2): symbol-not-found at link → React not weak-linked (`-undefined dynamic_lookup` missing); `\u003CReact\u002F…>` not found → `HEADER_SEARCH_PATHS` wrong; symbol *mismatch* → the RN pin doesn't match the host's RN. Surface these as React\u002FRN-version coupling issues, not extension-code bugs.\n\n**Verify the framework.** `ppmplugin\u002Fstaging\u002Fios\u002F\u003CPascal>Plugin.framework\u002F` MUST contain the two runtime-required items:\n- `\u003CPascal>Plugin` — the Mach-O binary, named exactly `\u003CPascal>Plugin` (the player `dlopen`s `Frameworks\u002F\u003CPascal>Plugin.framework\u002F\u003CPascal>Plugin`; a differently-named binary won't load). REQUIRED.\n- `Info.plist` — REQUIRED (a framework bundle without it is invalid and fails the wrap codesign step).\n\nIf either is missing, STOP with `BLOCKED: framework incomplete — missing \u003CX>` and the §5b fix (`GENERATE_INFOPLIST_FILE=YES` \u002F the binary name == framework name). The umbrella header + `Modules\u002Fmodule.modulemap` are build hygiene — note them if absent, but do NOT block on them (the `dlopen` loader doesn't use them — §5b).\n\n---\n\n## Step 6 — Report + next step\n\nUpdate the `## ppmplugin (third-party controls)` block in `.extension-state.md`: `iOS framework: built \u003CISO timestamp> (\u003CPascal>Plugin.framework, device slice)`. Print a fenced summary with the `.framework` path (and note it carries umbrella + module.modulemap + Info.plist), then offer next steps via `AskUserQuestion` (shared-instructions §9.1 — **invoke the chosen skill via the Skill tool; execute, don't describe**):\n\n- **Run \u002Fassemble-ppmplugin** (recommended — zip the manifest + binaries into the `.ppmplugin`)\n- **Run \u002Fbuild-android-binary** (if you also need the Android slice for a Both bundle)\n- **Stay — I'll inspect the framework first**\n\nReturn `DONE` with the `.framework` path, or `BLOCKED` with the failing `xcodebuild` line (most often React linking \u002F an RN-version mismatch with the host — not an extension-code fix).\n",{"name":5,"model":50,"description":7,"allowed-tools":51},"sonnet","Read, Write, Edit, Bash, Glob, Grep, AskUserQuestion, Skill","main","0a55a88fa2855404029d1e5450faf92878baf763","plugins\u002Fpower-apps-mobile-extension\u002Fskills\u002Fbuild-ios-binary",229,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fpower-platform-skills","https:\u002F\u002Fmicrosoft.github.io\u002Fpower-platform-skills\u002F","https:\u002F\u002Fgithub.com\u002Fmicrosoft","https:\u002F\u002Fopensource.microsoft.com","Open source projects and samples from Microsoft","2026-11-13 01:33:52",{"source":8,"skillName":5,"commands":63,"prompt":73,"zipAvailable":74},[64,67,70],{"tool":65,"command":66},"npx","npx skills add microsoft\u002Fpower-platform-skills --skill build-ios-binary",{"tool":68,"command":69},"bunx","bunx skills add microsoft\u002Fpower-platform-skills --skill build-ios-binary",{"tool":71,"command":72},"pnpm","pnpm dlx skills add microsoft\u002Fpower-platform-skills --skill build-ios-binary","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fpower-platform-skills\u002Fblob\u002Fmain\u002Fplugins\u002Fpower-apps-mobile-extension\u002Fskills\u002Fbuild-ios-binary\u002FSKILL.md",true,[],[77,98,121,139,153,163],{"skillNo":78,"name":79,"nameZh":6,"description":80,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":81,"categories":82,"keywords":83,"publishedAt":45},"01M699WVHX0XN59EPBTTPEKYPG","setup","Use when Process Intelligence needs building, explicit cloud\u002Faccount\u002Fenvironment configuration, sign-in, connection diagnostics, or recovery from an authentication error.","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fpower-platform-skills\u002Fblob\u002Fmain\u002Fplugins\u002Fprocess-intelligence\u002Fskills\u002Fsetup\u002FSKILL.md",[],[79,84,85,86,87,88,89,90,91,92,93,94,95,96,97,41],"read","when","connection","error","sign","intelligence","configuration","use","process","diagnostics","environment","authentication","cloud","recovery",{"skillNo":99,"name":100,"nameZh":6,"description":101,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":102,"categories":103,"keywords":109,"publishedAt":45},"01M699WSR9EX55YY1WD9J22FEG","report-issue","Use when the user wants a Process Intelligence bug-report draft or support summary for a failed operation.","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fpower-platform-skills\u002Fblob\u002Fmain\u002Fplugins\u002Fprocess-intelligence\u002Fskills\u002Freport-issue\u002FSKILL.md",[104],{"categoryNo":105,"categoryName":106,"taxonomyType":107,"taxonomyTypeDesc":108},"01JBSEEDCAT000000000000034","行政与办公人员","PROFESSION","适用职业",[110,111,112,113,114,92,91,115,116,89,117,118,119,85,120],"report issue","report","issue","support","user","bug","summary","draft","operation","for","bug-report",{"skillNo":122,"name":123,"nameZh":6,"description":124,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":125,"categories":126,"keywords":127,"publishedAt":45},"01M699WR26YQ9Q21KE6MJF5B2G","investigate-process","Use when a broad Process Mining business question, observed pattern or competing explanation needs investigation across analytical perspectives.","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fpower-platform-skills\u002Fblob\u002Fmain\u002Fplugins\u002Fprocess-intelligence\u002Fskills\u002Finvestigate-process\u002FSKILL.md",[],[92,128,129,130,85,131,132,133,134,84,135,91,136,137,138],"investigate","investigate process","mining","process mining","investigation","perspectives","question","business","pattern","across","analytical",{"skillNo":140,"name":141,"nameZh":6,"description":142,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":143,"categories":144,"keywords":145,"publishedAt":45},"01M699WP9SDG5ZR12P8VYNXKTN","derive-metric","Use when a requested measure is missing, a saved custom metric needs reuse, or an ephemeral formula or derived scalar is needed.","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fpower-platform-skills\u002Fblob\u002Fmain\u002Fplugins\u002Fprocess-intelligence\u002Fskills\u002Fderive-metric\u002FSKILL.md",[],[146,147,84,91,148,149,150,151,152,85],"metric","derive metric","formula","missing","reuse","saved","custom",{"skillNo":154,"name":155,"nameZh":6,"description":156,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":157,"categories":158,"keywords":159,"publishedAt":45},"01M699WMG5R8P6SMTDAS1TKF9E","compare-cohorts","Use when comparing process cohorts, departments, periods or before-and-after results under matched definitions.","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fpower-platform-skills\u002Fblob\u002Fmain\u002Fplugins\u002Fprocess-intelligence\u002Fskills\u002Fcompare-cohorts\u002FSKILL.md",[],[160,161,85,91,92,162,84],"compare","compare cohorts","results",{"skillNo":164,"name":165,"nameZh":6,"description":166,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":167,"categories":168,"keywords":169,"publishedAt":45},"01M699WJWNVTMP9XK3NCJHV14D","analyze-variants","Use when investigating common or slow variants, repeated work, unusual paths, or a specific Process Mining case.","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fpower-platform-skills\u002Fblob\u002Fmain\u002Fplugins\u002Fprocess-intelligence\u002Fskills\u002Fanalyze-variants\u002FSKILL.md",[],[170,171,172,131,85,130,173,174,91,92,84,175,176],"analyze","variants","analyze variants","case","paths","work","common"]