Skip to content

Add support for iOS Local Network permission #687

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ _📌  Note that these commands must be re-executed each time you update this
"Camera",
"Contacts",
"FaceID",
"LocalNetworkPrivacy",
"LocationAccuracy",
"LocationAlways",
"LocationWhenInUse",
Expand Down Expand Up @@ -97,6 +98,8 @@ Then update your `Info.plist` with wanted permissions usage descriptions:
<string>YOUR TEXT</string>
<key>NSFaceIDUsageDescription</key>
<string>YOUR TEXT</string>
<key>NSLocalNetworkUsageDescription</key>
<string>YOUR TEXT</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>YOUR TEXT</string>
<key>NSLocationAlwaysUsageDescription</key>
Expand Down Expand Up @@ -125,6 +128,13 @@ Then update your `Info.plist` with wanted permissions usage descriptions:
<key>NSUserTrackingUsageDescription</key>
<string>YOUR TEXT</string>

<!-- 🚨 This is required when requesting PERMISSIONS.IOS.LOCAL_NETWORK_PRIVACY 🚨 -->

<key>NSBonjourServices</key>
<array>
<string>_lnp._tcp.</string>
</array>

<!-- … -->

</dict>
Expand Down Expand Up @@ -488,6 +498,7 @@ PERMISSIONS.IOS.CALENDARS;
PERMISSIONS.IOS.CAMERA;
PERMISSIONS.IOS.CONTACTS;
PERMISSIONS.IOS.FACE_ID;
PERMISSIONS.IOS.LOCAL_NETWORK_PRIVACY;
PERMISSIONS.IOS.LOCATION_ALWAYS;
PERMISSIONS.IOS.LOCATION_WHEN_IN_USE;
PERMISSIONS.IOS.MEDIA_LIBRARY;
Expand Down
6 changes: 6 additions & 0 deletions example/ios/RNPermissionsExample/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@
</dict>
</dict>
</dict>
<key>NSBonjourServices</key>
<array>
<string>_lnp._tcp.</string>
</array>
<key>NSAppleMusicUsageDescription</key>
<string>Let me use your media library</string>
<key>NSBluetoothAlwaysUsageDescription</key>
Expand All @@ -49,6 +53,8 @@
<string>Let me use your contacts</string>
<key>NSFaceIDUsageDescription</key>
<string>Let me use FaceID</string>
<key>NSLocalNetworkUsageDescription</key>
<string>Let me use your local network</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Let me use your location, even in background</string>
<key>NSLocationAlwaysUsageDescription</key>
Expand Down
1 change: 1 addition & 0 deletions example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"Camera",
"Contacts",
"FaceID",
"LocalNetworkPrivacy",
"LocationAccuracy",
"LocationAlways",
"LocationWhenInUse",
Expand Down
5 changes: 5 additions & 0 deletions ios/LocalNetworkPrivacy/LocalNetworkPrivacy.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@interface LocalNetworkPrivacy : NSObject

- (void)checkAccessState:(void (^)(BOOL))completion;

@end
47 changes: 47 additions & 0 deletions ios/LocalNetworkPrivacy/LocalNetworkPrivacy.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#import <UIKit/UIKit.h>
#import "LocalNetworkPrivacy.h"

@interface LocalNetworkPrivacy () <NSNetServiceDelegate>

@property (nonatomic) NSNetService *service;
@property (nonatomic) void (^completion)(BOOL);
@property (nonatomic) NSTimer *timer;
@property (nonatomic) BOOL publishing;

@end

@implementation LocalNetworkPrivacy

- (instancetype)init {
if (self = [super init]) {
self.service = [[NSNetService alloc] initWithDomain:@"local." type:@"_lnp._tcp." name:@"LocalNetworkPrivacy" port:1100];
}
return self;
}

- (void)dealloc {
[self.service stop];
}

- (void)checkAccessState:(void (^)(BOOL))completion {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this as reliable as:
(It seems you altered the code from: https://stackoverflow.com/a/65800221)

    func checkAccessState(completion: @escaping (Bool) -> Void) {
        self.completion = completion
        
        timer = .scheduledTimer(withTimeInterval: 2, repeats: true, block: { timer in
            guard UIApplication.shared.applicationState == .active else {
                return
            }
            
            if self.publishing {
                self.timer?.invalidate()
                self.completion?(false)
            }
            else {
                self.publishing = true
                self.service.delegate = self
                self.service.publish()
                
            }
        })
    }

self.completion = completion;

self.publishing = YES;
self.service.delegate = self;
[self.service publish];

self.timer = [NSTimer scheduledTimerWithTimeInterval:2 repeats:NO block:^(NSTimer * _Nonnull timer) {
[self.timer invalidate];
self.completion(NO);
}];
}


#pragma mark - NSNetServiceDelegate

- (void)netServiceDidPublish:(NSNetService *)sender {
[self.timer invalidate];
self.completion(YES);
}

@end
20 changes: 20 additions & 0 deletions ios/LocalNetworkPrivacy/Permission-LocalNetworkPrivacy.podspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
require 'json'
package = JSON.parse(File.read('../../package.json'))

Pod::Spec.new do |s|
s.name = "Permission-LocalNetworkPrivacy"
s.dependency "RNPermissions"

s.version = package["version"]
s.license = package["license"]
s.summary = package["description"]
s.authors = package["author"]
s.homepage = package["homepage"]

s.ios.deployment_target = "10.0"
s.tvos.deployment_target = "11.0"
Comment on lines +14 to +15

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have not personally checked, but have you checked all APIs are actually available on ios and tvos? out of curiosity, are there any restrictions for ipados / macCatalyst or macos? (just thinking cross-platform, though it's not a strict requirement or anything)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haven't checked yet, just based the podspec on one of the others.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to the docs of NetService it is available on all platforms - but it has been deprecated with no obvious replacement.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh that's unfortunate. And you're right, no real guidance. You probably found this as well but the only thing I found was the lower level stuff here https://developer.apple.com/library/archive/samplecode/DNSSDObjects/Introduction/Intro.html#//apple_ref/doc/uid/DTS40011371-Intro-DontLinkElementID_2 - everything links to it

s.requires_arc = true

s.source = { :git => package["repository"]["url"], :tag => s.version }
s.source_files = "*.{h,m}"
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#import "RNPermissions.h"

@interface RNPermissionHandlerLocalNetworkPrivacy : NSObject<RNPermissionHandler>

@end
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#import "RNPermissionHandlerLocalNetworkPrivacy.h"
#import "LocalNetworkPrivacy.h"

@implementation RNPermissionHandlerLocalNetworkPrivacy

+ (NSArray<NSString *> * _Nonnull)usageDescriptionKeys {
return @[@"NSLocalNetworkUsageDescription"];
}

+ (NSString * _Nonnull)handlerUniqueId {
return @"ios.permission.LOCAL_NETWORK_PRIVACY";
}

- (void)checkWithResolver:(void (^ _Nonnull)(RNPermissionStatus))resolve
rejecter:(void (__unused ^ _Nonnull)(NSError * _Nonnull))reject {
if (![RNPermissionsHelper isFlaggedAsRequested:[[self class] handlerUniqueId]]) {
return resolve(RNPermissionStatusNotDetermined);
}

[self requestWithResolver:resolve rejecter:reject];
}

- (void)requestWithResolver:(void (^ _Nonnull)(RNPermissionStatus))resolve
rejecter:(void (^ _Nonnull)(NSError * _Nonnull))reject {
[RNPermissionsHelper flagAsRequested:[[self class] handlerUniqueId]];

LocalNetworkPrivacy *local = [LocalNetworkPrivacy new];
[local checkAccessState:^(BOOL granted) {
resolve(granted ? RNPermissionStatusAuthorized : RNPermissionStatusDenied);
}];
}

@end
3 changes: 3 additions & 0 deletions ios/RNPermissionsHelper.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ typedef NS_ENUM(NSInteger, RNPermission) {
#if __has_include("RNPermissionHandlerPhotoLibraryAddOnly.h")
RNPermissionPhotoLibraryAddOnly = 17,
#endif
#if __has_include("RNPermissionHandlerLocalNetworkPrivacy.h")
RNPermissionLocalNetworkPrivacy = 18,
#endif
};

typedef enum {
Expand Down
14 changes: 14 additions & 0 deletions ios/RNPermissionsModule.mm
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@
#if __has_include("RNPermissionHandlerLocationAccuracy.h")
#import "RNPermissionHandlerLocationAccuracy.h"
#endif
#if __has_include("RNPermissionHandlerLocalNetworkPrivacy.h")
#import "RNPermissionHandlerLocalNetworkPrivacy.h"
#endif

@implementation RCTConvert(RNPermission)

Expand Down Expand Up @@ -113,6 +116,9 @@ @implementation RCTConvert(RNPermission)
#if __has_include("RNPermissionHandlerPhotoLibraryAddOnly.h")
[RNPermissionHandlerPhotoLibraryAddOnly handlerUniqueId]: @(RNPermissionPhotoLibraryAddOnly),
#endif
#if __has_include("RNPermissionHandlerLocalNetworkPrivacy.h")
[RNPermissionHandlerLocalNetworkPrivacy handlerUniqueId]: @(RNPermissionLocalNetworkPrivacy),
#endif
}), RNPermissionUnknown, integerValue);

@end
Expand Down Expand Up @@ -195,6 +201,9 @@ - (NSDictionary *)constantsToExport {
#if __has_include("RNPermissionHandlerLocationAccuracy.h")
[available addObject:[RNPermissionHandlerLocationAccuracy handlerUniqueId]];
#endif
#if __has_include("RNPermissionHandlerLocalNetworkPrivacy.h")
[available addObject:[RNPermissionHandlerLocalNetworkPrivacy handlerUniqueId]];
#endif

#if RCT_DEV
if ([available count] == 0) {
Expand Down Expand Up @@ -310,6 +319,11 @@ - (void)checkUsageDescriptionKeys:(NSArray<NSString *> * _Nonnull)keys {
case RNPermissionPhotoLibraryAddOnly:
handler = [RNPermissionHandlerPhotoLibraryAddOnly new];
break;
#endif
#if __has_include("RNPermissionHandlerLocalNetworkPrivacy.h")
case RNPermissionLocalNetworkPrivacy:
handler = [RNPermissionHandlerLocalNetworkPrivacy new];
break;
#endif
case RNPermissionUnknown:
break; // RCTConvert prevents this case
Expand Down
1 change: 1 addition & 0 deletions src/permissions.ios.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const IOS = Object.freeze({
CAMERA: 'ios.permission.CAMERA',
CONTACTS: 'ios.permission.CONTACTS',
FACE_ID: 'ios.permission.FACE_ID',
LOCAL_NETWORK_PRIVACY: 'ios.permission.LOCAL_NETWORK_PRIVACY',
LOCATION_ALWAYS: 'ios.permission.LOCATION_ALWAYS',
LOCATION_WHEN_IN_USE: 'ios.permission.LOCATION_WHEN_IN_USE',
MEDIA_LIBRARY: 'ios.permission.MEDIA_LIBRARY',
Expand Down