mirror of
https://github.com/rzuasti/oott.git
synced 2026-07-08 19:21:54 +02:00
Request iOS local-network permission at app launch
The iOS local-network permission prompt fires on the first local-network access. That used to be the user's "Test" tap in settings, so the prompt appeared mid-request and the first connection attempt always failed. Provoke the prompt at launch instead via an mDNS multicast datagram, so the permission is settled before the user reaches settings. Platform exclusion is handled with a conditional import: web gets a no-op stub (keeping dart:io out of web builds) and the native path no-ops off iOS. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
87af782985
commit
40a7f3a432
@@ -45,6 +45,8 @@
|
||||
<true/>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
<key>NSLocalNetworkUsageDescription</key>
|
||||
<string>OOTT connects to your OOTT backend on your local network to monitor devices and deliver alerts.</string>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsLocalNetworking</key>
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'dart:async';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:frontend/theme/catppuccin_mocha_theme.dart';
|
||||
import 'package:frontend/utils/local_network_permission.dart';
|
||||
import 'package:frontend/utils/pref_utils.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'navigation.dart';
|
||||
@@ -30,6 +31,10 @@ void main() {
|
||||
);
|
||||
}
|
||||
|
||||
// Settle iOS's local-network permission now, at launch, so it isn't
|
||||
// still being decided when the user first taps "Test" in settings.
|
||||
unawaited(requestLocalNetworkPermission());
|
||||
|
||||
runApp(const MainApp());
|
||||
},
|
||||
(error, stack) {
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
/// Provokes iOS's local-network access prompt at app launch.
|
||||
///
|
||||
/// iOS shows the "allow access to devices on your local network" prompt the
|
||||
/// first time an app touches the local network. Until this ran at startup, that
|
||||
/// first touch was the user's "Test" tap in settings — so the prompt appeared
|
||||
/// mid-request and the very first connection attempt always failed while the
|
||||
/// user was still deciding. Triggering it here means the permission is settled
|
||||
/// before the user ever reaches the settings screen.
|
||||
///
|
||||
/// The real work lives in the `_io` implementation, which depends on `dart:io`
|
||||
/// and is therefore only compiled on native platforms. Web gets the `_stub`
|
||||
/// no-op, so `dart:io` never reaches a web build. Android, while native, also
|
||||
/// no-ops since only iOS has this prompt.
|
||||
library;
|
||||
|
||||
export 'local_network_permission_stub.dart'
|
||||
if (dart.library.io) 'local_network_permission_io.dart';
|
||||
@@ -0,0 +1,26 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
/// Native implementation of [requestLocalNetworkPermission]. Only iOS has a
|
||||
/// local-network permission prompt, so this is a no-op on every other native
|
||||
/// platform (Android, desktop).
|
||||
///
|
||||
/// Sending a datagram toward the mDNS multicast group is enough to make iOS
|
||||
/// surface the prompt; nothing needs to be received and no backend has to be
|
||||
/// reachable.
|
||||
Future<void> requestLocalNetworkPermission() async {
|
||||
if (!Platform.isIOS) return;
|
||||
|
||||
RawDatagramSocket? socket;
|
||||
try {
|
||||
socket = await RawDatagramSocket.bind(InternetAddress.anyIPv4, 0);
|
||||
// 224.0.0.251:5353 is the mDNS group; the send itself is what trips the
|
||||
// permission prompt, regardless of whether anything is listening.
|
||||
socket.send(const [0], InternetAddress('224.0.0.251'), 5353);
|
||||
} catch (e) {
|
||||
debugPrint('Local network permission trigger failed: $e');
|
||||
} finally {
|
||||
socket?.close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
/// Web/no-`dart:io` fallback for [requestLocalNetworkPermission]. There is no
|
||||
/// local-network prompt to provoke off-device, so this does nothing.
|
||||
Future<void> requestLocalNetworkPermission() async {}
|
||||
@@ -0,0 +1,10 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:frontend/utils/local_network_permission.dart';
|
||||
|
||||
void main() {
|
||||
test('is a no-op off iOS and never throws', () async {
|
||||
// Tests run on the host VM (not iOS), so this exercises the guard: it must
|
||||
// return cleanly without touching the network or raising.
|
||||
await expectLater(requestLocalNetworkPermission(), completes);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user