Files
com.pearcal/modules/local-network/index.ts
Your NameandClaude Opus 4.8 bdbb8ae859 feat(ios): Local Network force-prompt module (TODO #111, closes #37/#12)
iOS only surfaces the Local Network permission prompt the first time an app
touches the LAN (Bonjour/multicast/direct same-subnet connect). PearCal's
transport is Hyperswarm/hyperdht in the Bare worklet, which reaches peers via
the DHT + UDP holepunch and on first launch may never make a direct same-subnet
connection - so the prompt never fired and same-WiFi peers fell back to the slow
relayed path (the #37/#12 "iOS joins are slow / incomplete" bugs).

Ports the proven module from PearList/PearPetal: a tiny Expo module
(modules/local-network/) that, on boot, advertises + browses a throwaway
Bonjour service (`_pearcallan._tcp`) to make iOS evaluate LAN access and present
the prompt. Fire-and-forget from app/index.tsx before worklet bring-up; no-op
off iOS. Expo autolinking (use_expo_modules!) picks it up at pod install - no
manual Podfile/pbxproj edits. Added `_pearcallan._tcp` to NSBonjourServices.

Client-side only, no wire change. Validated on iPhone: app launches, the Local
Network prompt now fires on first launch. Suite measured cold-start join
112-147s -> ~3.4s with LN pre-granted.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Azfqdkyyt4MiGSwoQLhNvd
2026-07-11 22:52:48 -05:00

22 lines
915 B
TypeScript

import { Platform } from 'react-native'
import { requireOptionalNativeModule } from 'expo-modules-core'
// iOS-only native module (see ios/LocalNetworkModule.swift). On Android and web
// requireOptionalNativeModule returns null, so the export below is a no-op there.
const LocalNetwork = requireOptionalNativeModule<{
requestPermission(): Promise<boolean>
}>('LocalNetwork')
// Trigger the iOS Local Network permission prompt so same-WiFi Hyperswarm peers
// can be reached directly instead of via a relay. Fire-and-forget: it starts a
// short-lived Bonjour probe on the native side and resolves immediately. No-op
// off iOS or when the native module is unavailable. Never throws.
export async function requestLocalNetworkPermission (): Promise<boolean> {
if (Platform.OS !== 'ios' || !LocalNetwork) return false
try {
return await LocalNetwork.requestPermission()
} catch {
return false
}
}