Repository navigation
Add permission_handler_watchos: permissions on the watch - #11
iamoguzhanarslantas wants to merge 6 commits into
Conversation
permission_handler had no watchOS implementation; network_info_plus's example is one app that started with that error. This package answers it over dart:ffi for the permissions watchOS has: location (when in use and always), notifications, sensors, the microphone, contacts, the calendar (full access and write only), reminders and Bluetooth, with the iOS implementation's statuses and error codes. The others answer as iOS answers for a permission it does not know. The iOS implementation keeps an app from referencing privacy APIs it never asks for with build macros, which a watch app cannot set. Here each framework but UserNotifications is loaded at runtime and only messaged; nm shows no class of the others in the objects. A request whose usage description is missing fails with MISSING_USAGE_DESCRIPTION instead of the app being terminated. Checked on the watchOS 27.0 Simulator: 6 host tests, a 4-case watchOS integration test, and every group's system prompt answered by hand, the "always" location kept at "while using" included. Not yet on a watch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MAUstaoglu
left a comment
There was a problem hiding this comment.
Thanks, the code is clean and follows AUTHORING's push rules. What blocks it is App Review safety, plus a few permissions answered with an invented status.
Must-fix
-
App Review linkage: every permission group's code is compiled into every app, with no opt-out.
- The Xcode 27 linker drops the unused frameworks, but class names, delegate metadata and request selectors stay in the binary.
- Apple doesn't document whether its ITMS-90683 scan reads those.
- Upstream leaves unused groups out at compile time.
Please don't claim otherwise in
README.md:39-42,CHANGELOG.md:11-13andPORTING_REPORT.md:24-30until it's proven. Today the CLI compiles plugin sources with fixed flags, so we'll look at a CLI feature for compile-time opt-in. Before merging, we'd like evidence: an App Store Connect upload of an app that uses only notifications, with no ITMS-90683. -
phw_location.m:19,120usesNSUserDefaults, a required-reason API, and it lands in the app binary. Please document that apps must declareNSPrivacyAccessedAPICategoryUserDefaults(CA92.1). Upstream ships a privacy manifest for the same reason. -
storagefalls through to the unknown strategy (permission_handler_watchos_ffi.m:144), so a request returnspermanentlyDenied. iOS answers granted. -
criticalAlerts(UNNotificationSettings.criticalAlertSetting, watchOS 5+) andassistant(INPreferences, watchOS 6+) exist on watchOS, yet the plugin answerspermanentlyDenied, which tells the app the user refused. Implement them (UserNotifications is already linked) or answerdenied.
Also:
phw_location.m:166ignores NotDetermined, so a dismissed prompt hangs.phw_contacts.m:22mapsdefault:to limited, although the SDK marks Limited as iOS 18 only.
Nice-to-have
- Add the
ERROR_ALREADY_REQUESTING_PERMISSIONSguard from iOS: overlapping requests overwrite_askedand_store(lib/permission_handler_watchos.dart:83). - Add an
example/test_driver, as all 20 other examples have. - Add a host test for an answer that arrives before the handler is registered.
- Remove the dead pre-watchOS-10 branches.
- LICENSE is MIT while the source headers say BSD.
- Both allow-list entries are justified (the MIT text and
free(). Please keep the list alphabetical and fix the reason text.
We'll start the CI run after your update. Thank you!
Posted from the wrong account; superseded by the maintainer review.
From the review of PR flutterwatch#11: * The README, CHANGELOG and PORTING_REPORT no longer say that apps carry no reference to the privacy APIs of groups they never ask for. Every group's code is in every app: the frameworks but UserNotifications are loaded at runtime instead of linked, but class names, selectors and delegate metadata are in the binary, and whether ITMS-90683 reads them is unchecked until an App Store Connect upload shows it. The README tells apps to expect to need every group's usage description until then. * The README documents the privacy manifest entry the location strategy's NSUserDefaults flag needs: NSPrivacyAccessedAPICategoryUserDefaults, CA92.1. * storage is granted, with its service not applicable, as on iOS. * criticalAlerts is implemented on UserNotifications, as on iOS. * assistant (Siri) is implemented on INPreferences, loaded at runtime. INPreferences throws in an app without the Siri entitlement (seen on the Simulator) and leaves its dispatch_once unfinished, so NSSiriUsageDescription is the opt-in that the iOS implementation's PERMISSION_ASSISTANT macro is: without it the status is denied. * A when-in-use location prompt closed with the crown sends no delegate call on watchOS, so the request waited forever. The app becoming active again now answers it, with denied if the status is still not determined a second later. * Contacts: the watchOS SDK has no CNAuthorizationStatusLimited, so an unknown status is denied, not limited. * A request while another waits fails with ERROR_ALREADY_REQUESTING_PERMISSIONS, as on iOS. * The pre-watchOS-10 branches are gone; Package.swift says watchOS 10. * The source headers name the MIT license that LICENSE holds. * example/test_driver, as the other examples have; a host test for an answer that lands before the handler is registered; the allow-list entry in alphabetical order, its reason naming the _release export. Checked: 8 host tests (2 new); the watchOS integration test, 5 cases (1 new), on the watchOS 27.0 Simulator; there, a when-in-use prompt closed with the crown answered denied, allowed answered granted, and "always" kept at "while using" answered permanentlyDenied. The example builds with all six exports and no class symbol of the runtime-loaded frameworks. The critical alerts and Siri prompts need entitlements the example lacks and were not asked for. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Thanks for the review. Pushed 57942d2: Must-fix
Also
Nice-to-have
Checked:
Not yet done:
🤖 Generated with Claude Code |
MAUstaoglu
left a comment
There was a problem hiding this comment.
Thanks, everything from round one is addressed, and the App Review wording is honest now. Please fix two things in the new code:
phw_location.m:166-172never cancels the 1 s re-read timer, which exists for the case where the delegate answers first. In that case, a location request made within the second getspermanentlyDeniedwhile its prompt is on screen, and the usual whenInUse-then-always pair does exactly this. Please cancel the timer inanswer:, and arm it only afterWillResignActive.- README.md:52 tells apps to add every group's key, but
NSSiriUsageDescriptionalso switches on theINPreferencescall, which terminates an app that lacks the Siri entitlement. Please give Siri a separate opt-in, or answerdenied.
Nit: the root README row leaves out Intents, and the root README still says every package is on pub.dev.
The App Store Connect upload no longer blocks the merge, since the package is staged at 0.0.1 and unpublished. #19 tracks the upload and a compile-time opt-in before 0.1.0.
MAUstaoglu
left a comment
There was a problem hiding this comment.
One more item, from our standards rather than the code.
Example (AUTHORING.md §3, README "Examples & tests"). A ported package ships upstream's own example, unchanged, ported with flutter-watchos plugin port --from-pub permission_handler_apple --include-example. This PR ships a hand-written watch demo instead. The report gives the reason as the template's links, but permission_handler_apple's example does run on the watch:
baseflow_plugin_templatecalls url_launcher only when a link is tapped (lib/src/info_page.dart).url_launcher_watchosin this repo opens http and https links on the watch, in the system browser sheet.
So please:
- Replace
example/lib/with upstream's, unchanged apart from the formatting our CI checks. - In the example pubspec, add what that code imports (
baseflow_plugin_template,permission_handler_platform_interface), each with a caret constraint, plusurl_launcher_watchos, so the template's links work. - Keep the Info.plist usage descriptions.
- Keep
integration_test/permission_handler_test.dart. Upstream has no integration test, so a watch test is right here, as in the Firebase packages. It imports onlypermission_handler, so it does not depend on the demo. - Update the report's Example section to match.
The root README's package table: main adds the sqflite_watchos row, this branch the permission_handler_watchos one, which now also names Intents, loaded at runtime for Siri. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
The example rule is now written down in AUTHORING.md §3 (#25). For this PR: port upstream's example. Adding |
From the second review of PR flutterwatch#11: * The location strategy's one-second re-read no longer answers a later request. It is armed only when the app resigned active since the request, as the prompt makes it, and an answer from the delegate stands it down, so "always" asked for right after "when in use" keeps its prompt. Checked on the watchOS 27.0 Simulator: a when-in-use prompt closed with the crown is answered and a second request prompts again; allowed answers granted; "always" asked for right after kept its prompt until answered, and "keep while using" answered permanentlyDenied. * Siri has an opt-in of its own, the Info.plist Boolean PermissionHandlerWatchosSiri, which only an app with the Siri capability sets. NSSiriUsageDescription alone no longer switches INPreferences on, since the README tells apps to add every group's key and INPreferences ends an app without the capability. Without the opt-in the status is denied, and so is a request. * example/lib/ is permission_handler_apple 9.6.3's own example, unchanged, with its res/images. Its pubspec adds baseflow_plugin_template and permission_handler_platform_interface, which it imports, and url_launcher_watchos for the template's links. On the Simulator it runs, and a calendar request from it prompted and answered granted; the template's link buttons sit below a scroll area too small to reach there and were not tapped. The Info.plist usage descriptions and the watchOS integration test stay. * The root README row names Intents (in the merge before this commit). Checked: 8 host tests; the watchOS integration test, 5 cases, on the Simulator with upstream's example in place. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Its content is permission_handler_apple's; only the layout changes, to the package's language version, so that CI's dart format step passes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
AUTHORING.md §3 now names the deviations a ported example may have and asks for each in the package README and PORTING_REPORT. This example has one: its pubspec adds url_launcher_watchos, for the template's links. The README gets an "Example deviations" table, as network_info_plus_watchos has, and the report's verification table a row. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Thanks! Pushed f5b0a28, ddc7592 and b502b47 on top of a merge of
Nit: the Intents mention in the root README row is in the merge. The pub.dev line had already been fixed on Checked: 8 host tests, and the integration test (5 cases) with upstream's example in place. 🤖 Generated with Claude Code |
Summary
Adds
permission_handler_watchos0.0.1, the watchOS implementation ofpermission_handler, over dart:ffi.notification(provisional included),location/locationWhenInUse/locationAlways(with the service status; "always" needs "when in use" first, as on iOS),sensors(Motion & Fitness),microphone(watchOS 10+),contacts(limitedfrom watchOS 11),calendarFullAccess/calendarWriteOnly/reminders, andbluetooth. One strategy per permission group, as in the iOS implementation.denied, a requestpermanentlyDenied, servicedisabled.criticalAlertsandassistantneed an entitlement each and are not implemented yet.openAppSettingsreturns false: watchOS has no settings page an app can open.dlopen, classes looked up by name) the first time a permission of its group is asked about, and only messaged.nm -uon the compiled objects shows no class of CoreLocation, CoreMotion, AVFAudio, Contacts, EventKit or CoreBluetooth.Info.plistkey and fails with aPlatformExceptionwhose code isMISSING_USAGE_DESCRIPTIONinstead.tool/upstream_versions.yamlgetspermission_handler. The license is upstream's MIT, as for the other Baseflow ports, so the word allow-list gets its "free of charge" line, plus the package's Cfree(.Verified
On the watchOS 27.0 Simulator (Apple Watch Series 12, 46 mm):
example/integration_test/permission_handler_test.dart(4 tests) passes. Baseflow's example is a manual demo with no integration test, so this one is the package's own.ffiSymbolsentry in the export table.Off the watch:
tool/check,dart formatas CI runs it, andpub publish --dry-runare clean.Not verified
nm -ucheck above is the evidence; no build has been uploaded.Notes for review
requestPermissionsone at a time, as the watch shows one prompt at a time.NativeCallable.listenerthat carries only a kind, never a payload.network_info_plus_watchos's example upstream's again: it asks permission_handler for location behindPlatform.isIOS, and with this package that call has an answer.tool/upstream_versions.yaml, so whichever merges first leaves the others a conflict there; I will rebase each one after a merge.🤖 Generated with Claude Code