feat(perry/system): #675 — App Group / cross-process shared storage#974
Merged
Conversation
Exposes three perry/system entry points so widgets, share
extensions, watchOS targets, etc. can share data with the host app:
import { appGroupSet, appGroupGet, appGroupDelete } from "perry/system";
appGroupSet("sessionToken", "abc123");
const t = appGroupGet("sessionToken"); // "abc123" — "" if absent
appGroupDelete("sessionToken");
Without this, the only official way to wire a Perry app to a
Perry-built widget extension was to hand-write a Rust+Swift bridge
per project. Per the issue, this is a steep onboarding cliff that
creates a forest of subtly-different forks across the ecosystem.
| Platform | Status |
|------------|--------------------------------------------------------------|
| **macOS** | Real impl: NSUserDefaults(suiteName:), default suite name |
| | "group.perryapp" overridable via PERRY_APP_GROUP env var. |
| iOS | In-process HashMap MVP + #675 follow-up warning |
| tvOS | Stub (tvOS user apps rarely share data cross-process) |
| watchOS | Stub + #675 follow-up (UserDefaults(suiteName:) via |
| | iOS host's App Group entitlement) |
| visionOS | Stub + #675 follow-up (UIKit UserDefaults path) |
| Android | Stub + #675 follow-up (SharedPreferences via JNI) |
| Windows | Stub + #675 follow-up (%LOCALAPPDATA%-backed kv) |
| Linux/GTK4 | Stub + #675 follow-up (XDG-backed kv) |
| WASM | Stub via dispatch table fallback |
| HarmonyOS | Stub auto-generated by perry-runtime/build.rs |
Every stub funnels through perry_runtime::stub_diag::perry_stub_warn
so first call per process per symbol prints a [perry] warning naming
the platform and the #675 tracker.
NSUserDefaults initWithSuiteName: → setObject:forKey: / stringForKey:
/ removeObjectForKey: → synchronize. The suite name is read from
the PERRY_APP_GROUP environment variable (so an app's signing
identity can override the default without a recompile); when unset,
falls back to "group.perryapp".
For dev builds without the application-groups entitlement, the suite
still works — it just stores under
~/Library/Preferences/group.perryapp.plist instead of inside the
shared App Group container. That makes the API usable in local
iteration without setting up code-signing.
Get returns the empty string when the key is absent (per the issue's
contract). NSString -> UTF-8 bytes via UTF8String +
lengthOfBytesUsingEncoding:NSUTF8StringEncoding (= 4).
- perry-api-manifest/src/entries.rs — three method rows.
- perry-dispatch/src/lib.rs — three MethodRows (set: [Str, Str]
→ Void, get: [Str] → Str, delete: [Str] → Void). WASM falls
through to ui_method_to_runtime() in perry-codegen-wasm/src/emit.rs
— symbol mapping for free.
- Per-platform exports: perry-ui-macos (real),
perry-ui-ios (in-process HashMap MVP), and stubs in tvos,
watchos, visionos, android, gtk4, windows.
- perry-ui-test/src/lib.rs — three Feature rows.
- HarmonyOS auto-stubbed by perry-runtime/build.rs from the dispatch
table.
Compiled App({body}) + appGroupSet + appGroupGet + appGroupDelete
against release perry; symbols resolve end-to-end, link succeeds,
binary writes cleanly.
- writeBlob/readBlob — for file-backed shared data the issue calls
out (FileManager containerURL on iOS, XDG on Linux, etc.).
- Real iOS / Android / Windows / Linux / visionOS impls.
- perry.toml [ios].app_group + [android].shared_prefs_name
configuration plumbing — currently hardcoded to the
PERRY_APP_GROUP env var on macOS.
- Entitlement merge into Perry's signing pipeline for App Group
capability.
b5b1e31 to
11bd5d0
Compare
3 tasks
proggeramlug
added a commit
that referenced
this pull request
May 18, 2026
…Rs (#1019) #981 (PERRY_SANDBOX_BUILDRS), #988 (--emit-attest), and #969 (perry.permissions) each landed via admin-bypass with their SUMMARY.md entries intact but without the actual .md content files (or, for #969, without any docs entry at all). docs/src/cli/lockdown.md and docs/src/cli/emit-sandbox.md did make it into main and are fine; the others left dead links. Separately, #976 / #972 / #974 added perry/system runtime methods (getOSVersion, shareText, shareUrl, appGroupSet/Get/Delete) but never updated the hand-maintained types/perry/system/index.d.ts. TypeScript users importing those APIs from `perry/system` get a type error today. This PR: - Creates docs/src/cli/sandbox-buildrs.md (#505) - Creates docs/src/cli/emit-attest.md (#504) - Creates docs/src/cli/capabilities.md (#501) and adds the SUMMARY.md entry - Adds the six new perry/system signatures to types/perry/system/index.d.ts The auto-generated docs/api/perry.d.ts + docs/src/api/reference.md were regenerated during the original PRs and are already current. Pure docs-only diff. No code changes.
proggeramlug
added a commit
that referenced
this pull request
May 18, 2026
…from #974 rebase merge) The merge of #974 (#675 app-group) into main left several files with unclosed delimiters: every platform-stub crate's perry_system_share_url function was missing its closing );} and perry-ui-test's FEATURES list had two Feature entries collapsed together. Without this fix, cargo fmt --check --all errors out on every PR, blocking every PR's lint gate. - perry-ui-android/gtk4/ios/tvos/visionos/watchos/windows: close the stub function bodies that lost their delimiters in the merge. - perry-ui-test: reconstruct the share_text + share_url Feature entries (which had been merged with app_group_set and app_group_get respectively) and split out a proper app_group_get entry.
proggeramlug
added a commit
that referenced
this pull request
May 18, 2026
…from #974 rebase merge) The merge of #974 (#675 app-group) into main left several files with unclosed delimiters: every platform-stub crate's perry_system_share_url function was missing its closing );} and perry-ui-test's FEATURES list had two Feature entries collapsed together. Without this fix, cargo fmt --check --all errors out on every PR, blocking every PR's lint gate. - perry-ui-android/gtk4/ios/tvos/visionos/watchos/windows: close the stub function bodies that lost their delimiters in the merge. - perry-ui-test: reconstruct the share_text + share_url Feature entries (which had been merged with app_group_set and app_group_get respectively) and split out a proper app_group_get entry.
proggeramlug
added a commit
that referenced
this pull request
May 18, 2026
…from #974 rebase merge) (#1020) The merge of #974 (#675 app-group) into main left several files with unclosed delimiters: every platform-stub crate's perry_system_share_url function was missing its closing );} and perry-ui-test's FEATURES list had two Feature entries collapsed together. Without this fix, cargo fmt --check --all errors out on every PR, blocking every PR's lint gate. - perry-ui-android/gtk4/ios/tvos/visionos/watchos/windows: close the stub function bodies that lost their delimiters in the merge. - perry-ui-test: reconstruct the share_text + share_url Feature entries (which had been merged with app_group_set and app_group_get respectively) and split out a proper app_group_get entry.
proggeramlug
added a commit
that referenced
this pull request
May 18, 2026
…rry widget init + iOS .appex embed) (#1018) * feat(widget): #676 — WidgetKit build glue (perry.toml [[widget]] + perry widget init + iOS .appex embed) Adds the `[[widget]]` schema to `perry.toml`, a `perry widget init <name>` scaffolder for SwiftUI WidgetKit boilerplate, and a build hook that invokes `swiftc` per declared widget during `perry compile --target ios` and embeds the produced `.appex` into `<output>.app/Frameworks/`. Scope (iOS-only v1): - `[[widget]]` parser (`widget_build::WidgetEntry`) supports `name`, `swift_source`, `watchos_source`, `glance_source`, `display_name`, `description`, `intents`. - `perry widget init <Name>` writes `<Name>Widget.swift` (TimelineProvider + WidgetEntryView + `@main` Widget) and `<Name>Intent.swift` (WidgetConfigurationIntent stub), then appends the matching `[[widget]]` block to the nearest `perry.toml`. - iOS `.app` assembly invokes swiftc per widget with `iphoneos`/ `iphonesimulator` SDK, embeds the resulting binary at `Frameworks/<Name>.appex/<Name>` with a generated Info.plist that carries `NSExtensionPointIdentifier = com.apple.widgetkit-extension`. Deferred to follow-ups (warn-and-skip in v1): - watchOS build path (`watchos_source` slot accepted but ignored). - Android Glance build path (`glance_source` slot accepted but ignored). - Data-sharing wiring (App Group plumbing lives under #675). - Separate `@perryts/widgets` npm package skeleton. Tests cover schema parsing (minimal + full entries), Info.plist key emission, swift-file discovery filtering, and the scaffolder humanize helper. Closes #676 (iOS slice; watchOS / Android Glance / data-sharing tracked as explicit non-goals for this PR per the issue scope). * style: apply cargo fmt across touched files and stale main violations * fix(ui): repair broken share_url stubs + perry-ui-test Feature list (from #974 rebase merge) The merge of #974 (#675 app-group) into main left several files with unclosed delimiters: every platform-stub crate's perry_system_share_url function was missing its closing );} and perry-ui-test's FEATURES list had two Feature entries collapsed together. Without this fix, cargo fmt --check --all errors out on every PR, blocking every PR's lint gate. - perry-ui-android/gtk4/ios/tvos/visionos/watchos/windows: close the stub function bodies that lost their delimiters in the merge. - perry-ui-test: reconstruct the share_text + share_url Feature entries (which had been merged with app_group_set and app_group_get respectively) and split out a proper app_group_get entry.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #675 (MVP — see follow-up matrix).
Summary
Three new
perry/systementry points so widgets, share extensions, watchOS targets, etc. can share key/value data with the host app:Without this, the only official way to wire a Perry app to a Perry-built widget extension was to hand-write a Rust+Swift bridge per project. The issue calls this out as a steep onboarding cliff that creates a forest of subtly-different forks across the ecosystem.
Cross-platform coverage
NSUserDefaults(suiteName:), default suite name"group.perryapp"overridable viaPERRY_APP_GROUPenv varSharedPreferencesvia JNI)%LOCALAPPDATA%-backed kv)perry-runtime/build.rsEvery stub funnels through
perry_runtime::stub_diag::perry_stub_warnso first call per process per symbol prints a[perry] warningline naming the platform and the #675 tracker.macOS implementation detail
NSUserDefaults initWithSuiteName:→setObject:forKey:/stringForKey:/removeObjectForKey:→synchronize. The suite name is read from thePERRY_APP_GROUPenvironment variable; defaults to"group.perryapp".For dev builds without the
application-groupsentitlement, the suite still works — it just stores under~/Library/Preferences/group.perryapp.plistinstead of inside the shared App Group container. That makes the API usable in local iteration without setting up code-signing.appGroupGetreturns the empty string when the key is absent (per the issue's contract). NSString → UTF-8 bytes viaUTF8String+lengthOfBytesUsingEncoding:NSUTF8StringEncoding(= 4).Plumbing (mirrors #918 / #917 pattern)
perry-api-manifest/src/entries.rs— three method rows.perry-dispatch/src/lib.rs— threeMethodRowrows (set:[Str, Str] → Void, get:[Str] → Str, delete:[Str] → Void). WASM falls through toui_method_to_runtime()— symbol mapping for free.perry-ui-test/src/lib.rs— threeFeaturerows.perry-runtime/build.rs.Smoke test
App({body}) + appGroupSet + appGroupGet + appGroupDeleteagainst release perry compiles + links cleanly; symbol resolution verified end-to-end.What's deferred (#675 follow-ups)
writeBlob/readBlob— file-backed shared data the issue calls out (FileManager.containerURLon iOS, XDG on Linux, etc.).perry.toml[ios].app_group+[android].shared_prefs_nameconfiguration plumbing — currently hardcoded to thePERRY_APP_GROUPenv var on macOS.Notes
No
Cargo.tomlversion bump, noCLAUDE.mdtouch, noCHANGELOG.mdentry — maintainer folds those in at merge time.