diff --git a/README.md b/README.md index d4f3fcb..6077838 100644 --- a/README.md +++ b/README.md @@ -50,11 +50,10 @@ companion app — one `main.dart`, running on both devices. ## List of plugins Every plugin below has a watchOS implementation, and each package's example -builds and starts on the watchOS 27.0 Simulator. Three of those examples +builds and starts on the watchOS 27.0 Simulator. Two of those examples start with an error: `sensors_plus_watchos` (the barometer is not -implemented), `in_app_purchase_watchos` (the StoreKit platform addition it -asks for fails) and `network_info_plus_watchos` (the example's -`permission_handler` has no watchOS implementation). What each package +implemented) and `in_app_purchase_watchos` (the StoreKit platform addition +it asks for fails). What each package covers and what it leaves out are in its `README.md`. Its `PORTING_REPORT.md` records how the package was ported and what was checked at the time; the last full run on the Simulator is under diff --git a/packages/network_info_plus_watchos/CHANGELOG.md b/packages/network_info_plus_watchos/CHANGELOG.md index a70b9bd..4f9bd90 100644 --- a/packages/network_info_plus_watchos/CHANGELOG.md +++ b/packages/network_info_plus_watchos/CHANGELOG.md @@ -1,3 +1,17 @@ +## 0.1.1 + +* Example: `lib/main.dart` is network_info_plus 8.2.2's own again. Its + location request at start, behind `Platform.isIOS`, which is true on the + watch, is now answered by `permission_handler_watchos`, which the + example's pubspec adds in place of `flutter_watchos` and the + `FlutterWatchosPlatform.isIos` guard. The pubspec names + `permission_handler: ^13.0.2`, as upstream's, and the Info.plist has + upstream's `NSLocationWhenInUseUsageDescription`. +* Example: the start test checks a start without an error, and then the + network info, which waits for the location prompt: it is skipped on a + fresh install with no one to answer the prompt, unless the run passes + `--dart-define=NIP_PROMPT=true`. + ## 0.1.0 * The version moves from 0.0.1 to 0.1.0, so that a `^0.1.0` constraint diff --git a/packages/network_info_plus_watchos/PORTING_REPORT.md b/packages/network_info_plus_watchos/PORTING_REPORT.md index 9d30023..b032685 100644 --- a/packages/network_info_plus_watchos/PORTING_REPORT.md +++ b/packages/network_info_plus_watchos/PORTING_REPORT.md @@ -8,7 +8,7 @@ | watchOS capability | Partial — Wi-Fi IP only; SSID/BSSID unsupported on watchOS | | Host unit tests (`flutter-watchos test`) | ✅ pass | | Upstream integration test | ✅ passes verbatim | -| Example deviations | `example/lib/main.dart`: both permission requests use `FlutterWatchosPlatform.isIos` instead of `Platform.isIOS` (true on the watch), because `permission_handler` has no watchOS implementation and the upstream example started with a `MissingPluginException`; `example/pubspec.yaml` adds `flutter_watchos: ^0.1.0` for it. `example/watchos/Runner/Info.plist` sets `FlutterWatchOSContentScale` to 0.4, because the phone app overflowed the watch screen; `example/test/watchos_content_scale_test.dart` lays it out at that scale on every watch screen size. `example/integration_test/watchos_example_start_test.dart` checks that the example starts cleanly; it passes on the watchOS 27.0 Simulator (Apple Watch Series 12, 46 mm, 2026-10-04) | +| Example deviations | `example/lib/main.dart` is upstream's, unchanged: its location request at start, behind `Platform.isIOS` (true on the watch), is answered by `permission_handler_watchos`, which `example/pubspec.yaml` adds; the Info.plist has upstream's `NSLocationWhenInUseUsageDescription`. Until 0.1.1 the request was guarded with `FlutterWatchosPlatform.isIos`, because `permission_handler` had no watchOS implementation. `example/watchos/Runner/Info.plist` sets `FlutterWatchOSContentScale` to 0.4, because the phone app overflowed the watch screen; `example/test/watchos_content_scale_test.dart` lays it out at that scale on every watch screen size. `example/integration_test/watchos_example_start_test.dart` checks that the example starts cleanly, then that it shows its network info once the location prompt is answered; with no one to answer it, that part is skipped. On the watchOS 27.0 Simulator (Apple Watch Series 12, 46 mm, 2026-11-02) the example showed no exception and no overflow in its first 15 seconds, asked for location with upstream's text, and showed its network info once allowed; the start test passed with location answered before and with the prompt answered during the run (`--dart-define=NIP_PROMPT=true`), and skipped its second part on a fresh install; upstream's `network_info_plus_test.dart` passed | | Internal unified demo | ✅ included | Marking: ✅ full / passes · ◐ partial — reason given · ○ not applicable (no upstream test) · ✗ unsupported on watchOS. diff --git a/packages/network_info_plus_watchos/README.md b/packages/network_info_plus_watchos/README.md index d9de794..e787182 100644 --- a/packages/network_info_plus_watchos/README.md +++ b/packages/network_info_plus_watchos/README.md @@ -46,16 +46,22 @@ both unchanged except for these watchOS deviations: | Where | Change | Why | |---|---|---| -| `example/lib/main.dart`, both permission requests | `Platform.isIOS` becomes `FlutterWatchosPlatform.isIos` | `Platform.isIOS` is true on the watch, and `permission_handler` has no watchOS implementation, so the request failed with `MissingPluginException` at start | -| `example/pubspec.yaml` | adds `flutter_watchos: ^0.1.0` | for `FlutterWatchosPlatform` | +| `example/pubspec.yaml` | adds `permission_handler_watchos` | the example asks `permission_handler` for location at start, behind `Platform.isIOS`, which is true on the watch; this package answers it there | | `example/watchos/Runner/Info.plist` | sets `FlutterWatchOSContentScale` to `0.4` | the app is designed for a phone and overflowed the watch screen (by 107 pixels on a 46 mm Simulator); the scale makes it lay out in a logical space 2.5 times the screen's size and draws it smaller, without touching the Dart code | +`example/watchos/Runner/Info.plist` has upstream's +`NSLocationWhenInUseUsageDescription`, which the location prompt shows. + `example/integration_test/watchos_example_start_test.dart` is a watchOS test -beside the upstream one: it starts the example and checks that it shows its -network info without an error. The watchOS widget test -`example/test/watchos_content_scale_test.dart` lays the app out at its content -scale on every watch screen size, with a global IPv6 address on screen, and -fails on any overflow. +beside the upstream one: it starts the example and checks that it starts +without an error and then shows its network info. The network info waits for +the answer to the location prompt, so on a fresh install with no one to +answer it that check is skipped; run with `--dart-define=NIP_PROMPT=true` and +answer the prompt, or grant location first with `xcrun simctl privacy`. + +The watchOS widget test `example/test/watchos_content_scale_test.dart` lays +the app out at its content scale on every watch screen size, with a global +IPv6 address on screen, and fails on any overflow. ## Not supported on watchOS diff --git a/packages/network_info_plus_watchos/example/integration_test/watchos_example_start_test.dart b/packages/network_info_plus_watchos/example/integration_test/watchos_example_start_test.dart index 4fed453..f6dc18a 100644 --- a/packages/network_info_plus_watchos/example/integration_test/watchos_example_start_test.dart +++ b/packages/network_info_plus_watchos/example/integration_test/watchos_example_start_test.dart @@ -4,11 +4,19 @@ // // A watchOS supplement to the upstream network_info_plus_test.dart, which // stays verbatim. The upstream example asks permission_handler for location -// access behind Platform.isIOS, which is true on the watch, and -// permission_handler has no watchOS implementation: the example started with -// a MissingPluginException. The example now guards that call with -// FlutterWatchosPlatform.isIos. This starts the example and checks that it -// reaches its network info without an error. +// access at start, behind Platform.isIOS, which is true on the watch; +// permission_handler_watchos answers it there. This starts the example, +// checks that it starts without an error, and then that it reaches its +// network info. +// +// The network info waits for the answer to the location prompt. A run where +// location was answered before (the Simulator keeps the answer across +// installs) checks it at once. On a fresh install the prompt stays up with +// no one to answer it, so that check is skipped unless the run passes +// --dart-define=NIP_PROMPT=true, where someone answers the prompt within a +// minute. Answering it in advance also works: +// xcrun simctl privacy grant location \ +// com.example.networkInfoPlusExample.watchkitapp // // Run on the watch Simulator: // flutter-watchos drive --driver=test_driver/integration_test.dart \ @@ -17,19 +25,41 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'package:network_info_plus_example/main.dart' as app; +import 'package:permission_handler/permission_handler.dart'; + +/// Whether someone answers the location prompt on this run +/// (`--dart-define=NIP_PROMPT=true`). +const bool _prompt = bool.fromEnvironment('NIP_PROMPT'); void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); testWidgets('the example starts and shows its network info', (WidgetTester tester) async { + // "denied" is a location prompt not answered yet: the example's request + // at start shows it, and the network info waits for it. + final bool answered = + await Permission.locationWhenInUse.status != PermissionStatus.denied; + app.main(); + final Stopwatch elapsed = Stopwatch()..start(); + while (elapsed.elapsed < const Duration(seconds: 3)) { + await tester.pump(const Duration(milliseconds: 100)); + } + expect(tester.takeException(), isNull); - // The info arrives after seven native reads; give it up to 10 s. + if (!answered && !_prompt) { + markTestSkipped('the location prompt is up with no one to answer it: ' + 'run with --dart-define=NIP_PROMPT=true and answer it, or grant ' + 'location first'); + return; + } + + // The info arrives after the answer and seven native reads. + final Duration limit = + answered ? const Duration(seconds: 10) : const Duration(seconds: 60); final Finder info = find.textContaining('Wifi IPv4:'); - final Stopwatch elapsed = Stopwatch()..start(); - while (info.evaluate().isEmpty && - elapsed.elapsed < const Duration(seconds: 10)) { + while (info.evaluate().isEmpty && elapsed.elapsed < limit) { await tester.pump(const Duration(milliseconds: 100)); } diff --git a/packages/network_info_plus_watchos/example/lib/main.dart b/packages/network_info_plus_watchos/example/lib/main.dart index 6075181..c6b548e 100644 --- a/packages/network_info_plus_watchos/example/lib/main.dart +++ b/packages/network_info_plus_watchos/example/lib/main.dart @@ -11,7 +11,6 @@ import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:flutter_watchos/flutter_watchos.dart'; import 'package:network_info_plus/network_info_plus.dart'; import 'package:permission_handler/permission_handler.dart'; @@ -99,9 +98,7 @@ class _MyHomePageState extends State { wifiSubmask; try { - // watchOS deviation: Platform.isIOS is true on the watch, where - // permission_handler has no implementation, so this asks on iPhone only. - if (!kIsWeb && (Platform.isAndroid || FlutterWatchosPlatform.isIos)) { + if (!kIsWeb && (Platform.isAndroid || Platform.isIOS)) { // Request permissions as recommended by the plugin documentation: // https://github.com/fluttercommunity/plus_plugins/tree/main/packages/network_info_plus/network_info_plus if (await Permission.locationWhenInUse.request().isGranted) { @@ -118,9 +115,7 @@ class _MyHomePageState extends State { } try { - // watchOS deviation: Platform.isIOS is true on the watch, where - // permission_handler has no implementation, so this asks on iPhone only. - if (!kIsWeb && (Platform.isAndroid || FlutterWatchosPlatform.isIos)) { + if (!kIsWeb && (Platform.isAndroid || Platform.isIOS)) { // Request permissions as recommended by the plugin documentation: // https://github.com/fluttercommunity/plus_plugins/tree/main/packages/network_info_plus/network_info_plus if (await Permission.locationWhenInUse.request().isGranted) { diff --git a/packages/network_info_plus_watchos/example/pubspec.yaml b/packages/network_info_plus_watchos/example/pubspec.yaml index 0c2a5da..70d9b37 100644 --- a/packages/network_info_plus_watchos/example/pubspec.yaml +++ b/packages/network_info_plus_watchos/example/pubspec.yaml @@ -9,13 +9,14 @@ environment: dependencies: flutter: sdk: flutter - # For FlutterWatchosPlatform.isIos, which the watchOS guard in lib/main.dart - # uses. - flutter_watchos: ^0.1.0 network_info_plus: ^8.2.1 network_info_plus_watchos: path: ../ - permission_handler: ^12.0.0+1 + # Upstream's example asks permission_handler for location; this answers it + # on the watch. + permission_handler: ^13.0.2 + permission_handler_watchos: + path: ../../permission_handler_watchos dev_dependencies: network_info_plus_platform_interface: ^3.1.0 diff --git a/packages/network_info_plus_watchos/example/test/watchos_content_scale_test.dart b/packages/network_info_plus_watchos/example/test/watchos_content_scale_test.dart index 3305c92..0ddbfe4 100644 --- a/packages/network_info_plus_watchos/example/test/watchos_content_scale_test.dart +++ b/packages/network_info_plus_watchos/example/test/watchos_content_scale_test.dart @@ -3,8 +3,8 @@ // found in the LICENSE file. // // A watchOS supplement, not an upstream test. The example is network_info_plus's -// own phone app, kept verbatim but for the watchOS guard on its permission -// request; it fits a watch only through the FlutterWatchOSContentScale in +// own phone app, kept verbatim; it fits a watch only through the +// FlutterWatchOSContentScale in // watchos/Runner/Info.plist. This lays the app out in the logical space that // scale gives each watch screen, with room for the system insets, and fails on // any overflow. diff --git a/packages/network_info_plus_watchos/example/watchos/Runner/App.swift b/packages/network_info_plus_watchos/example/watchos/Runner/App.swift index c6b7f6a..93fefb1 100644 --- a/packages/network_info_plus_watchos/example/watchos/Runner/App.swift +++ b/packages/network_info_plus_watchos/example/watchos/Runner/App.swift @@ -9,6 +9,12 @@ import FlutterWatchOS @main struct NetworkInfoPlusExampleApp: App { #if !arch(arm64_32) + // Remote-notification plumbing (APNs device token, notification payloads) + // for plugins that need it, e.g. firebase_messaging. Safe to keep even if + // no plugin uses notifications. + @WKApplicationDelegateAdaptor(FlutterWatchOSAppDelegate.self) + private var flutterAppDelegate + init() { // Native platform views: register a SwiftUI factory for every // `viewType` your Dart code embeds with WatchPlatformView diff --git a/packages/network_info_plus_watchos/example/watchos/Runner/Info.plist b/packages/network_info_plus_watchos/example/watchos/Runner/Info.plist index b03c89b..304ddfe 100644 --- a/packages/network_info_plus_watchos/example/watchos/Runner/Info.plist +++ b/packages/network_info_plus_watchos/example/watchos/Runner/Info.plist @@ -22,6 +22,8 @@ $(FLUTTER_BUILD_NUMBER) FlutterWatchOSContentScale 0.4 + NSLocationWhenInUseUsageDescription + Obtain WIFI Name WKApplication WKWatchOnly diff --git a/packages/network_info_plus_watchos/pubspec.yaml b/packages/network_info_plus_watchos/pubspec.yaml index 7191f3c..ac22466 100644 --- a/packages/network_info_plus_watchos/pubspec.yaml +++ b/packages/network_info_plus_watchos/pubspec.yaml @@ -1,6 +1,6 @@ name: network_info_plus_watchos description: "watchOS implementation of the network_info_plus plugin, exposing Wi-Fi IPv4 and IPv6 addresses via getifaddrs and dart:ffi (SSID is unavailable on watchOS)." -version: 0.1.0 +version: 0.1.1 homepage: https://flutterwatch.dev repository: https://github.com/flutterwatch/plugins/tree/main/packages/network_info_plus_watchos issue_tracker: https://github.com/flutterwatch/plugins/issues