fix(mobile): allow iOS device pairing to LAN relay in dev

- add NSCameraUsageDescription, NSMicrophoneUsageDescription, and
  NSLocalNetworkUsageDescription to iOS Info.plist so the app no
  longer aborts on launch when touching camera/local-network APIs
- permit private-network relay URLs in pairing validator when
  kDebugMode (mirrors the existing localhost carve-out) so a
  physical device can reach a relay on the host Mac's LAN IP
- pick up iOS Pod/project updates from pod install

Production behavior is unchanged — private IPs and localhost remain
blocked outside of debug builds.

Made-with: Cursor
This commit is contained in:
Thomas Petersen
2026-04-16 18:29:25 -04:00
parent 12467417fe
commit 4f87036750
4 changed files with 19 additions and 6 deletions
+6
View File
@@ -56,6 +56,8 @@ PODS:
- shared_preferences_foundation (0.0.1):
- Flutter
- FlutterMacOS
- url_launcher_ios (0.0.1):
- Flutter
DEPENDENCIES:
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
@@ -63,6 +65,7 @@ DEPENDENCIES:
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
- mobile_scanner (from `.symlinks/plugins/mobile_scanner/ios`)
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
SPEC REPOS:
trunk:
@@ -89,6 +92,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/mobile_scanner/ios"
shared_preferences_foundation:
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
url_launcher_ios:
:path: ".symlinks/plugins/url_launcher_ios/ios"
SPEC CHECKSUMS:
connectivity_plus: cb623214f4e1f6ef8fe7403d580fdad517d2f7dd
@@ -107,6 +112,7 @@ SPEC CHECKSUMS:
nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb
url_launcher_ios: 7a95fa5b60cc718a708b8f2966718e93db0cef1b
PODFILE CHECKSUM: bd29822c3d5baf6b44b726f00ea3293a19339ef2
+3 -4
View File
@@ -107,7 +107,6 @@
7CF2415588E96D5723581BA9 /* Pods-RunnerTests.release.xcconfig */,
529B9B84AD8748153EB60BCF /* Pods-RunnerTests.profile.xcconfig */,
);
name = Pods;
path = Pods;
sourceTree = "<group>";
};
@@ -493,7 +492,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = JMTDPW9CG3;
DEVELOPMENT_TEAM = L36K5BLDZJ;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
@@ -676,7 +675,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = JMTDPW9CG3;
DEVELOPMENT_TEAM = L36K5BLDZJ;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
@@ -699,7 +698,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = JMTDPW9CG3;
DEVELOPMENT_TEAM = L36K5BLDZJ;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
+6
View File
@@ -26,6 +26,12 @@
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSCameraUsageDescription</key>
<string>Sprout uses the camera to scan pairing QR codes so you can link this device to your account.</string>
<key>NSMicrophoneUsageDescription</key>
<string>Sprout does not record audio. The system may request microphone access while the camera is in use for QR scanning.</string>
<key>NSLocalNetworkUsageDescription</key>
<string>Sprout connects to your Sprout relay on the local network to sync messages in real time.</string>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
@@ -133,9 +133,11 @@ class PairingNotifier extends Notifier<PairingState> {
return;
}
// Block private and link-local IPs.
// Block private and link-local IPs in production. In debug mode we
// intentionally allow them so developers can pair to a relay running on
// their LAN (e.g. http://192.168.1.23:3000) from a physical device.
final ip = Uri.tryParse('http://$host')?.host ?? host;
if (_isPrivateHost(ip)) {
if (_isPrivateHost(ip) && !kDebugMode) {
throw const FormatException(
'Relay URL cannot target private network addresses',
);