@wniu/react-native-local-network-permission 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/LICENSE +20 -0
  2. package/LocalNetworkPermission.podspec +22 -0
  3. package/README.md +147 -0
  4. package/ios/LocalNetworkPermission.h +5 -0
  5. package/ios/LocalNetworkPermission.mm +129 -0
  6. package/lib/module/LocalNetworkPermission.android.js +10 -0
  7. package/lib/module/LocalNetworkPermission.android.js.map +1 -0
  8. package/lib/module/LocalNetworkPermission.ios.js +18 -0
  9. package/lib/module/LocalNetworkPermission.ios.js.map +1 -0
  10. package/lib/module/LocalNetworkPermission.js +10 -0
  11. package/lib/module/LocalNetworkPermission.js.map +1 -0
  12. package/lib/module/NativeLocalNetworkPermission.js +13 -0
  13. package/lib/module/NativeLocalNetworkPermission.js.map +1 -0
  14. package/lib/module/index.js +4 -0
  15. package/lib/module/index.js.map +1 -0
  16. package/lib/module/package.json +1 -0
  17. package/lib/typescript/package.json +1 -0
  18. package/lib/typescript/src/LocalNetworkPermission.android.d.ts +6 -0
  19. package/lib/typescript/src/LocalNetworkPermission.android.d.ts.map +1 -0
  20. package/lib/typescript/src/LocalNetworkPermission.d.ts +6 -0
  21. package/lib/typescript/src/LocalNetworkPermission.d.ts.map +1 -0
  22. package/lib/typescript/src/LocalNetworkPermission.ios.d.ts +12 -0
  23. package/lib/typescript/src/LocalNetworkPermission.ios.d.ts.map +1 -0
  24. package/lib/typescript/src/NativeLocalNetworkPermission.d.ts +20 -0
  25. package/lib/typescript/src/NativeLocalNetworkPermission.d.ts.map +1 -0
  26. package/lib/typescript/src/index.d.ts +2 -0
  27. package/lib/typescript/src/index.d.ts.map +1 -0
  28. package/package.json +160 -0
  29. package/src/LocalNetworkPermission.android.ts +7 -0
  30. package/src/LocalNetworkPermission.ios.ts +15 -0
  31. package/src/LocalNetworkPermission.ts +7 -0
  32. package/src/NativeLocalNetworkPermission.ts +22 -0
  33. package/src/index.tsx +1 -0
package/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 zephyr
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the "Software"), to deal
6
+ in the Software without restriction, including without limitation the rights
7
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is
9
+ furnished to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all
12
+ copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ SOFTWARE.
@@ -0,0 +1,22 @@
1
+ require "json"
2
+
3
+ package = JSON.parse(File.read(File.join(__dir__, "package.json")))
4
+
5
+ Pod::Spec.new do |s|
6
+ s.name = "LocalNetworkPermission"
7
+ s.version = package["version"]
8
+ s.summary = package["description"]
9
+ s.homepage = package["homepage"]
10
+ s.license = package["license"]
11
+ s.authors = package["author"]
12
+
13
+ s.platforms = { :ios => min_ios_version_supported }
14
+ s.source = { :git => "https://github.com/voyageh/react-native-local-network-permission.git", :tag => "#{s.version}" }
15
+
16
+ s.source_files = "ios/**/*.{h,m,mm,swift,cpp}"
17
+ s.private_header_files = "ios/**/*.h"
18
+
19
+ s.frameworks = "Network"
20
+
21
+ install_modules_dependencies(s)
22
+ end
package/README.md ADDED
@@ -0,0 +1,147 @@
1
+ # react-native-local-network-permission
2
+
3
+ A React Native library for handling **iOS Local Network permission**, built on TurboModule architecture.
4
+
5
+ > **This is a React Native library, not an Expo-only module.** It works in bare React Native projects and can also be used in Expo projects via prebuild (`npx expo prebuild`).
6
+
7
+ ## Installation
8
+
9
+ ```sh
10
+ npm install react-native-local-network-permission
11
+ # or
12
+ yarn add react-native-local-network-permission
13
+ ```
14
+
15
+ ### iOS Setup
16
+
17
+ ```sh
18
+ cd ios && pod install
19
+ ```
20
+
21
+ #### Required Info.plist Configuration
22
+
23
+ You **must** add the following keys to your `ios/<YourApp>/Info.plist`:
24
+
25
+ ```xml
26
+ <key>NSLocalNetworkUsageDescription</key>
27
+ <string>This app needs access to the local network to discover nearby devices.</string>
28
+
29
+ <key>NSBonjourServices</key>
30
+ <array>
31
+ <string>_lnp_check._tcp</string>
32
+ </array>
33
+ ```
34
+
35
+ - **`NSLocalNetworkUsageDescription`**: The message shown to the user in the permission dialog. Customize this to explain why your app needs local network access.
36
+ - **`NSBonjourServices`**: Must include `_lnp_check._tcp` — this is the Bonjour service type used internally by this library to detect permission status.
37
+
38
+ > If your app also browses for other Bonjour services, add those to the array as well.
39
+
40
+ ### Expo Setup
41
+
42
+ This library is **not** an Expo Module — it uses React Native TurboModule architecture. To use it in an Expo managed project:
43
+
44
+ 1. Install the library as above.
45
+ 2. Run `npx expo prebuild` to generate native projects.
46
+ 3. Add the Info.plist keys manually, or create a [config plugin](https://docs.expo.dev/guides/config-plugins/) to automate it.
47
+
48
+ The library is fully compatible with Expo projects that use prebuild (Continuous Native Generation).
49
+
50
+ ## Usage
51
+
52
+ ```ts
53
+ import {
54
+ requestPermission,
55
+ isAvailable,
56
+ } from 'react-native-local-network-permission';
57
+
58
+ // Request / check permission (may show system dialog on first call)
59
+ const { status, granted, canAskAgain } = await requestPermission();
60
+
61
+ // Check if local network permission is available on this platform
62
+ const available = await isAvailable(); // true on iOS 14+, false on Android
63
+ ```
64
+
65
+ ## API Reference
66
+
67
+ ### `requestPermission()`
68
+
69
+ ```ts
70
+ requestPermission(): Promise<LocalNetworkPermissionResponse>
71
+ ```
72
+
73
+ Requests local network permission. On iOS, this triggers a detection mechanism that:
74
+ - Shows the system permission dialog if the user has not yet made a choice.
75
+ - Returns the current status without a dialog if already granted or denied.
76
+
77
+ On Android, always returns `{ status: 'granted', granted: true, canAskAgain: false }` since Android does not restrict local network access.
78
+
79
+ #### Why only one method instead of separate "get" and "request"?
80
+
81
+ Apple provides **no API to silently query** local network permission status. Any detection attempt is itself a local network operation that may trigger the system dialog. Providing separate `getPermission()` and `requestPermission()` methods would be misleading — they would do exactly the same thing.
82
+
83
+ ### `isAvailable()`
84
+
85
+ ```ts
86
+ isAvailable(): Promise<boolean>
87
+ ```
88
+
89
+ Returns `true` on iOS 14+, `false` on Android and older iOS versions.
90
+
91
+ ### Types
92
+
93
+ ```ts
94
+ type PermissionStatus = 'granted' | 'denied';
95
+
96
+ type LocalNetworkPermissionResponse = {
97
+ status: PermissionStatus;
98
+ granted: boolean; // true when status === 'granted'
99
+ canAskAgain: boolean; // false — iOS only shows the dialog once
100
+ };
101
+ ```
102
+
103
+ ## How It Works
104
+
105
+ ### The iOS Local Network Permission Challenge
106
+
107
+ Unlike Camera, Location, or Microphone permissions, **Apple does not provide any public API** to directly query or request local network permission. There is no equivalent of `AVCaptureDevice.authorizationStatus` for local network access. This is true across all iOS versions including iOS 18.
108
+
109
+ ### Detection Mechanism
110
+
111
+ This library uses the widely-adopted **NWBrowser/NWListener self-discovery technique**:
112
+
113
+ 1. A Bonjour `NWListener` is started on the device, advertising a custom service (`_lnp_check._tcp`).
114
+ 2. A `NWBrowser` is started to search for that same service.
115
+ 3. **If permission is granted:** The browser discovers the listener -> returns `"granted"`.
116
+ 4. **If permission is denied:** The browser receives a DNS policy error -> returns `"denied"`.
117
+ 5. **If permission is undetermined:** The system shows the permission dialog, then the result is determined.
118
+
119
+ ### Limitations
120
+
121
+ - **No silent query**: `requestPermission()` may trigger the system dialog on first call. This is an iOS platform limitation, not a library design choice.
122
+ - **One-time dialog**: iOS shows the local network permission dialog only once. After the user makes a choice, they must go to **Settings > Privacy & Security > Local Network** to change it.
123
+ - **`canAskAgain` is always `false`**: Since we cannot determine whether the dialog has been shown before, and it can only be shown once, this field is always `false`.
124
+ - **iOS 14+ only**: Local network permission was introduced in iOS 14. On older versions, the library returns `"granted"` (unrestricted access).
125
+ - **Android**: Local network access is not restricted on Android. `requestPermission()` returns granted, and `isAvailable()` returns `false`.
126
+
127
+ ## Platform Support
128
+
129
+ | Platform | Supported | Notes |
130
+ |----------|-----------|-------|
131
+ | iOS 14+ | Yes | Full support via NWBrowser/NWListener |
132
+ | iOS < 14 | Partial | Always returns granted (no restriction) |
133
+ | Android | N/A | Returns granted; `isAvailable()` returns false |
134
+
135
+ ## Contributing
136
+
137
+ - [Development workflow](CONTRIBUTING.md#development-workflow)
138
+ - [Sending a pull request](CONTRIBUTING.md#sending-a-pull-request)
139
+ - [Code of conduct](CODE_OF_CONDUCT.md)
140
+
141
+ ## License
142
+
143
+ MIT
144
+
145
+ ---
146
+
147
+ Made with [create-react-native-library](https://github.com/callstack/react-native-builder-bob)
@@ -0,0 +1,5 @@
1
+ #import <LocalNetworkPermissionSpec/LocalNetworkPermissionSpec.h>
2
+
3
+ @interface LocalNetworkPermission : NSObject <NativeLocalNetworkPermissionSpec>
4
+
5
+ @end
@@ -0,0 +1,129 @@
1
+ #import "LocalNetworkPermission.h"
2
+ #import <Network/Network.h>
3
+
4
+ static NSString *const kServiceType = @"_preflight_check._tcp";
5
+
6
+ @implementation LocalNetworkPermission
7
+
8
+ - (void)requestPermission:(RCTPromiseResolveBlock)resolve
9
+ reject:(RCTPromiseRejectBlock)reject
10
+ {
11
+ if (@available(iOS 14.0, *)) {
12
+ [self checkWithResolve:resolve];
13
+ } else {
14
+ resolve(@(YES));
15
+ }
16
+ }
17
+
18
+ - (void)checkWithResolve:(RCTPromiseResolveBlock)resolve API_AVAILABLE(ios(14.0))
19
+ {
20
+ __block nw_listener_t listener = nil;
21
+ __block nw_browser_t browser = nil;
22
+ __block BOOL didResolve = NO;
23
+
24
+ void (^finish)(BOOL) = ^(BOOL granted) {
25
+ if (didResolve) return;
26
+ didResolve = YES;
27
+
28
+ if (listener) {
29
+ nw_listener_set_state_changed_handler(listener, NULL);
30
+ nw_listener_set_new_connection_handler(listener, NULL);
31
+ nw_listener_cancel(listener);
32
+ listener = nil;
33
+ }
34
+ if (browser) {
35
+ nw_browser_set_state_changed_handler(browser, NULL);
36
+ nw_browser_set_browse_results_changed_handler(browser, NULL);
37
+ nw_browser_cancel(browser);
38
+ browser = nil;
39
+ }
40
+
41
+ resolve(@(granted));
42
+ };
43
+
44
+ // --- Listener ---
45
+ nw_parameters_t listenerParams = nw_parameters_create_secure_tcp(
46
+ NW_PARAMETERS_DISABLE_PROTOCOL,
47
+ NW_PARAMETERS_DEFAULT_CONFIGURATION
48
+ );
49
+
50
+ listener = nw_listener_create(listenerParams);
51
+ if (!listener) {
52
+ finish(NO);
53
+ return;
54
+ }
55
+
56
+ NSString *serviceName = [[NSUUID UUID] UUIDString];
57
+ nw_advertise_descriptor_t ad = nw_advertise_descriptor_create_bonjour_service(
58
+ serviceName.UTF8String, kServiceType.UTF8String, NULL
59
+ );
60
+ nw_listener_set_advertise_descriptor(listener, ad);
61
+ nw_listener_set_new_connection_handler(listener, ^(nw_connection_t connection) {});
62
+
63
+ nw_listener_set_state_changed_handler(listener, ^(nw_listener_state_t state, nw_error_t error) {
64
+ switch (state) {
65
+ case nw_listener_state_ready:
66
+ break;
67
+ case nw_listener_state_failed:
68
+ case nw_listener_state_waiting:
69
+ finish(NO);
70
+ break;
71
+ default:
72
+ break;
73
+ }
74
+ });
75
+
76
+ nw_listener_set_queue(listener, dispatch_get_main_queue());
77
+ nw_listener_start(listener);
78
+
79
+ // --- Browser ---
80
+ nw_browse_descriptor_t desc = nw_browse_descriptor_create_bonjour_service(
81
+ kServiceType.UTF8String, NULL
82
+ );
83
+ nw_parameters_t browserParams = nw_parameters_create();
84
+ nw_parameters_set_include_peer_to_peer(browserParams, true);
85
+
86
+ browser = nw_browser_create(desc, browserParams);
87
+
88
+ nw_browser_set_state_changed_handler(browser, ^(nw_browser_state_t state, nw_error_t error) {
89
+ switch (state) {
90
+ case nw_browser_state_ready:
91
+ break;
92
+ case nw_browser_state_waiting:
93
+ case nw_browser_state_failed:
94
+ finish(NO);
95
+ break;
96
+ default:
97
+ break;
98
+ }
99
+ });
100
+
101
+ nw_browser_set_browse_results_changed_handler(browser, ^(
102
+ nw_browse_result_t oldResult,
103
+ nw_browse_result_t newResult,
104
+ bool batchComplete
105
+ ) {
106
+ nw_browse_result_change_t changes = nw_browse_result_get_changes(oldResult, newResult);
107
+ if (changes & nw_browse_result_change_result_added) {
108
+ finish(YES);
109
+ }
110
+ });
111
+
112
+ nw_browser_set_queue(browser, dispatch_get_main_queue());
113
+ nw_browser_start(browser);
114
+ }
115
+
116
+ #pragma mark - TurboModule
117
+
118
+ - (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
119
+ (const facebook::react::ObjCTurboModule::InitParams &)params
120
+ {
121
+ return std::make_shared<facebook::react::NativeLocalNetworkPermissionSpecJSI>(params);
122
+ }
123
+
124
+ + (NSString *)moduleName
125
+ {
126
+ return @"LocalNetworkPermission";
127
+ }
128
+
129
+ @end
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Android does not restrict local network access.
5
+ * Always returns `true`.
6
+ */
7
+ export async function requestPermission() {
8
+ return true;
9
+ }
10
+ //# sourceMappingURL=LocalNetworkPermission.android.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["requestPermission"],"sourceRoot":"../../src","sources":["LocalNetworkPermission.android.ts"],"mappings":";;AAAA;AACA;AACA;AACA;AACA,OAAO,eAAeA,iBAAiBA,CAAA,EAAqB;EAC1D,OAAO,IAAI;AACb","ignoreList":[]}
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+
3
+ import NativeModule from "./NativeLocalNetworkPermission.js";
4
+
5
+ /**
6
+ * Request / check local network permission on iOS.
7
+ *
8
+ * On iOS 14+, this triggers the NWBrowser/NWListener detection mechanism:
9
+ * - If the user has not yet made a choice, the system permission dialog will appear.
10
+ * - If permission has already been granted or denied, the current status is returned
11
+ * without showing a dialog.
12
+ *
13
+ * @returns `true` if granted, `false` if denied.
14
+ */
15
+ export async function requestPermission() {
16
+ return NativeModule.requestPermission();
17
+ }
18
+ //# sourceMappingURL=LocalNetworkPermission.ios.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["NativeModule","requestPermission"],"sourceRoot":"../../src","sources":["LocalNetworkPermission.ios.ts"],"mappings":";;AAAA,OAAOA,YAAY,MAAM,mCAAgC;;AAEzD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,eAAeC,iBAAiBA,CAAA,EAAqB;EAC1D,OAAOD,YAAY,CAACC,iBAAiB,CAAC,CAAC;AACzC","ignoreList":[]}
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Default fallback (web and other platforms).
5
+ * Always returns `true`.
6
+ */
7
+ export async function requestPermission() {
8
+ return true;
9
+ }
10
+ //# sourceMappingURL=LocalNetworkPermission.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["requestPermission"],"sourceRoot":"../../src","sources":["LocalNetworkPermission.ts"],"mappings":";;AAAA;AACA;AACA;AACA;AACA,OAAO,eAAeA,iBAAiBA,CAAA,EAAqB;EAC1D,OAAO,IAAI;AACb","ignoreList":[]}
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+
3
+ import { TurboModuleRegistry } from 'react-native';
4
+
5
+ /**
6
+ * Native module spec for codegen.
7
+ *
8
+ * The return type uses inline object shapes because TurboModule codegen
9
+ * does not support importing custom TypeScript types.
10
+ */
11
+
12
+ export default TurboModuleRegistry.getEnforcing('LocalNetworkPermission');
13
+ //# sourceMappingURL=NativeLocalNetworkPermission.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["TurboModuleRegistry","getEnforcing"],"sourceRoot":"../../src","sources":["NativeLocalNetworkPermission.ts"],"mappings":";;AAAA,SAASA,mBAAmB,QAA0B,cAAc;;AAEpE;AACA;AACA;AACA;AACA;AACA;;AAYA,eAAeA,mBAAmB,CAACC,YAAY,CAC7C,wBACF,CAAC","ignoreList":[]}
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+
3
+ export { requestPermission } from './LocalNetworkPermission';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["requestPermission"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,SAASA,iBAAiB,QAAQ,0BAA0B","ignoreList":[]}
@@ -0,0 +1 @@
1
+ {"type":"module"}
@@ -0,0 +1 @@
1
+ {"type":"module"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Android does not restrict local network access.
3
+ * Always returns `true`.
4
+ */
5
+ export declare function requestPermission(): Promise<boolean>;
6
+ //# sourceMappingURL=LocalNetworkPermission.android.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LocalNetworkPermission.android.d.ts","sourceRoot":"","sources":["../../../src/LocalNetworkPermission.android.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,OAAO,CAAC,CAE1D"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Default fallback (web and other platforms).
3
+ * Always returns `true`.
4
+ */
5
+ export declare function requestPermission(): Promise<boolean>;
6
+ //# sourceMappingURL=LocalNetworkPermission.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LocalNetworkPermission.d.ts","sourceRoot":"","sources":["../../../src/LocalNetworkPermission.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,OAAO,CAAC,CAE1D"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Request / check local network permission on iOS.
3
+ *
4
+ * On iOS 14+, this triggers the NWBrowser/NWListener detection mechanism:
5
+ * - If the user has not yet made a choice, the system permission dialog will appear.
6
+ * - If permission has already been granted or denied, the current status is returned
7
+ * without showing a dialog.
8
+ *
9
+ * @returns `true` if granted, `false` if denied.
10
+ */
11
+ export declare function requestPermission(): Promise<boolean>;
12
+ //# sourceMappingURL=LocalNetworkPermission.ios.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LocalNetworkPermission.ios.d.ts","sourceRoot":"","sources":["../../../src/LocalNetworkPermission.ios.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,OAAO,CAAC,CAE1D"}
@@ -0,0 +1,20 @@
1
+ import { type TurboModule } from 'react-native';
2
+ /**
3
+ * Native module spec for codegen.
4
+ *
5
+ * The return type uses inline object shapes because TurboModule codegen
6
+ * does not support importing custom TypeScript types.
7
+ */
8
+ export interface Spec extends TurboModule {
9
+ /**
10
+ * Request (or check) local network permission.
11
+ * On iOS this uses the NWBrowser/NWListener trick and may trigger the
12
+ * system permission dialog if the user has not yet made a choice.
13
+ *
14
+ * Resolves with `true` if granted, `false` if denied.
15
+ */
16
+ requestPermission(): Promise<boolean>;
17
+ }
18
+ declare const _default: Spec;
19
+ export default _default;
20
+ //# sourceMappingURL=NativeLocalNetworkPermission.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NativeLocalNetworkPermission.d.ts","sourceRoot":"","sources":["../../../src/NativeLocalNetworkPermission.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAErE;;;;;GAKG;AACH,MAAM,WAAW,IAAK,SAAQ,WAAW;IACvC;;;;;;OAMG;IACH,iBAAiB,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;CACvC;;AAED,wBAEE"}
@@ -0,0 +1,2 @@
1
+ export { requestPermission } from './LocalNetworkPermission';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC"}
package/package.json ADDED
@@ -0,0 +1,160 @@
1
+ {
2
+ "name": "@wniu/react-native-local-network-permission",
3
+ "version": "0.1.0",
4
+ "description": "Handles iOS local network permission requests.",
5
+ "main": "./lib/module/index.js",
6
+ "types": "./lib/typescript/src/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "source": "./src/index.tsx",
10
+ "types": "./lib/typescript/src/index.d.ts",
11
+ "default": "./lib/module/index.js"
12
+ },
13
+ "./package.json": "./package.json"
14
+ },
15
+ "files": [
16
+ "src",
17
+ "lib",
18
+ "ios",
19
+ "*.podspec",
20
+ "!ios/build",
21
+ "!**/__tests__",
22
+ "!**/__fixtures__",
23
+ "!**/__mocks__",
24
+ "!**/.*"
25
+ ],
26
+ "scripts": {
27
+ "example": "yarn workspace react-native-local-network-permission-example",
28
+ "clean": "del-cli lib",
29
+ "prepare": "bob build",
30
+ "typecheck": "tsc",
31
+ "lint": "eslint \"**/*.{js,ts,tsx}\"",
32
+ "test": "jest",
33
+ "release": "release-it --only-version"
34
+ },
35
+ "keywords": [
36
+ "react-native",
37
+ "ios",
38
+ "android"
39
+ ],
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "git+https://github.com/voyageh/react-native-local-network-permission.git"
43
+ },
44
+ "author": "zephyr <zephyr.d@qq.com> (https://github.com/voyageh)",
45
+ "license": "MIT",
46
+ "bugs": {
47
+ "url": "https://github.com/voyageh/react-native-local-network-permission/issues"
48
+ },
49
+ "homepage": "https://github.com/voyageh/react-native-local-network-permission#readme",
50
+ "publishConfig": {
51
+ "registry": "https://registry.npmjs.org/"
52
+ },
53
+ "devDependencies": {
54
+ "@commitlint/config-conventional": "^20.5.0",
55
+ "@eslint/compat": "^2.0.3",
56
+ "@eslint/eslintrc": "^3.3.5",
57
+ "@eslint/js": "^10.0.1",
58
+ "@jest/globals": "^30.0.0",
59
+ "@react-native/babel-preset": "0.85.0",
60
+ "@react-native/eslint-config": "0.85.0",
61
+ "@react-native/jest-preset": "0.85.0",
62
+ "@release-it/conventional-changelog": "^10.0.6",
63
+ "@types/react": "^19.2.0",
64
+ "commitlint": "^20.5.0",
65
+ "del-cli": "^7.0.0",
66
+ "eslint": "^9.39.4",
67
+ "eslint-config-prettier": "^10.1.8",
68
+ "eslint-plugin-ft-flow": "^3.0.11",
69
+ "eslint-plugin-prettier": "^5.5.5",
70
+ "jest": "^30.3.0",
71
+ "lefthook": "^2.1.4",
72
+ "prettier": "^3.8.1",
73
+ "react": "19.2.0",
74
+ "react-native": "0.83.4",
75
+ "react-native-builder-bob": "^0.41.0",
76
+ "release-it": "^19.2.4",
77
+ "turbo": "^2.8.21",
78
+ "typescript": "^6.0.2"
79
+ },
80
+ "peerDependencies": {
81
+ "react": "*",
82
+ "react-native": "*"
83
+ },
84
+ "workspaces": [
85
+ "example"
86
+ ],
87
+ "packageManager": "yarn@4.11.0",
88
+ "react-native-builder-bob": {
89
+ "source": "src",
90
+ "output": "lib",
91
+ "targets": [
92
+ [
93
+ "module",
94
+ {
95
+ "esm": true
96
+ }
97
+ ],
98
+ [
99
+ "typescript",
100
+ {
101
+ "project": "tsconfig.build.json"
102
+ }
103
+ ]
104
+ ]
105
+ },
106
+ "codegenConfig": {
107
+ "name": "LocalNetworkPermissionSpec",
108
+ "type": "modules",
109
+ "jsSrcsDir": "src"
110
+ },
111
+ "prettier": {
112
+ "quoteProps": "consistent",
113
+ "singleQuote": true,
114
+ "tabWidth": 2,
115
+ "trailingComma": "es5",
116
+ "useTabs": false
117
+ },
118
+ "jest": {
119
+ "preset": "@react-native/jest-preset",
120
+ "modulePathIgnorePatterns": [
121
+ "<rootDir>/example/node_modules",
122
+ "<rootDir>/lib/"
123
+ ]
124
+ },
125
+ "commitlint": {
126
+ "extends": [
127
+ "@commitlint/config-conventional"
128
+ ]
129
+ },
130
+ "release-it": {
131
+ "git": {
132
+ "commitMessage": "chore: release ${version}",
133
+ "tagName": "v${version}"
134
+ },
135
+ "npm": {
136
+ "publish": true
137
+ },
138
+ "github": {
139
+ "release": true
140
+ },
141
+ "plugins": {
142
+ "@release-it/conventional-changelog": {
143
+ "preset": {
144
+ "name": "angular"
145
+ }
146
+ }
147
+ }
148
+ },
149
+ "create-react-native-library": {
150
+ "type": "turbo-module",
151
+ "languages": "kotlin-objc",
152
+ "tools": [
153
+ "eslint",
154
+ "jest",
155
+ "lefthook",
156
+ "release-it"
157
+ ],
158
+ "version": "0.62.0"
159
+ }
160
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Android does not restrict local network access.
3
+ * Always returns `true`.
4
+ */
5
+ export async function requestPermission(): Promise<boolean> {
6
+ return true;
7
+ }
@@ -0,0 +1,15 @@
1
+ import NativeModule from './NativeLocalNetworkPermission';
2
+
3
+ /**
4
+ * Request / check local network permission on iOS.
5
+ *
6
+ * On iOS 14+, this triggers the NWBrowser/NWListener detection mechanism:
7
+ * - If the user has not yet made a choice, the system permission dialog will appear.
8
+ * - If permission has already been granted or denied, the current status is returned
9
+ * without showing a dialog.
10
+ *
11
+ * @returns `true` if granted, `false` if denied.
12
+ */
13
+ export async function requestPermission(): Promise<boolean> {
14
+ return NativeModule.requestPermission();
15
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Default fallback (web and other platforms).
3
+ * Always returns `true`.
4
+ */
5
+ export async function requestPermission(): Promise<boolean> {
6
+ return true;
7
+ }
@@ -0,0 +1,22 @@
1
+ import { TurboModuleRegistry, type TurboModule } from 'react-native';
2
+
3
+ /**
4
+ * Native module spec for codegen.
5
+ *
6
+ * The return type uses inline object shapes because TurboModule codegen
7
+ * does not support importing custom TypeScript types.
8
+ */
9
+ export interface Spec extends TurboModule {
10
+ /**
11
+ * Request (or check) local network permission.
12
+ * On iOS this uses the NWBrowser/NWListener trick and may trigger the
13
+ * system permission dialog if the user has not yet made a choice.
14
+ *
15
+ * Resolves with `true` if granted, `false` if denied.
16
+ */
17
+ requestPermission(): Promise<boolean>;
18
+ }
19
+
20
+ export default TurboModuleRegistry.getEnforcing<Spec>(
21
+ 'LocalNetworkPermission'
22
+ );
package/src/index.tsx ADDED
@@ -0,0 +1 @@
1
+ export { requestPermission } from './LocalNetworkPermission';