Skip to content
This repository was archived by the owner on Nov 8, 2023. It is now read-only.

Commit 65d39e9

Browse files
authored
Merge pull request #185 from NativeScript/tbozhikov/add-allow-location-ios
Tbozhikov/add allow location ios
2 parents 10e919b + 47d50c4 commit 65d39e9

File tree

7 files changed

+113
-77
lines changed

7 files changed

+113
-77
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ geolocation.getCurrentLocation({ desiredAccuracy: Accuracy.high, maximumAge: 500
7676
| timeout | 5 minutes | How long to wait for a location in ms. |
7777
| iosAllowsBackgroundLocationUpdates | false | If enabled, UIBackgroundModes key in info.plist is required (check the hint below). Allow the application to receive location updates in background (ignored on Android). Read more in [Apple document](https://developer.apple.com/documentation/corelocation/cllocationmanager/1620568-allowsbackgroundlocationupdates?language=objc) |
7878
| iosPausesLocationUpdatesAutomatically | true | Allow deactivation of the automatic pause of location updates (ignored on Android). Read more in [Apple document](https://developer.apple.com/documentation/corelocation/cllocationmanager/1620553-pauseslocationupdatesautomatical?language=objc)|
79+
| iosOpenSettingsIfLocationHasBeenDenied | false | Argument on the `enableLocationRequest`. If true, the settings app will open on iOS so the user can change the location services permission. |
7980

8081
> If iosAllowsBackgroundLocationUpdates is set to true, the following code is required in the info.plist file:
8182
>```

demo/app/main-page.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ export function stopBackgroundTap() {
6161
export function enableLocationTap() {
6262
geolocation.isEnabled().then(function (isEnabled) {
6363
if (!isEnabled) {
64-
geolocation.enableLocationRequest().then(function () {
64+
geolocation.enableLocationRequest(false, true).then(function () {
6565
}, function (e) {
6666
console.log("Error: " + (e.message || e));
6767
});

demo/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,4 @@
3030
"ci.tslint": "npm i && tslint --config '../tslint.json' 'app/**/*.ts' --exclude '**/node_modules/**'",
3131
"build.plugin": "cd ../src && npm run build"
3232
}
33-
}
33+
}

src/geolocation.android.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { on as applicationOn, android as androidAppInstance, AndroidApplication, uncaughtErrorEvent, UnhandledErrorEventData } from "application";
22
import { Accuracy } from "tns-core-modules/ui/enums";
3-
import { setTimeout, clearTimeout } from "timer";
3+
import { setTimeout, clearTimeout } from "tns-core-modules/timer";
44
import { LocationBase, defaultGetLocationTimeout, fastestTimeUpdate, minTimeUpdate } from "./geolocation.common";
55
import { Options, successCallbackType, errorCallbackType } from "./location-monitor";
66
import * as permissions from "nativescript-permissions";

src/geolocation.common.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,4 @@ export class LocationBase implements LocationDef {
1414
export const defaultGetLocationTimeout = 5 * 60 * 1000; // 5 minutes
1515
export const minRangeUpdate = 0.1; // 0 meters
1616
export const minTimeUpdate = 1 * 60 * 1000; // 1 minute
17-
export const fastestTimeUpdate = 5 * 1000; // 5 secs
17+
export const fastestTimeUpdate = 5 * 1000; // 5 secs

src/geolocation.ios.ts

Lines changed: 40 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Accuracy } from "tns-core-modules/ui/enums";
2-
import { setTimeout, clearTimeout } from "timer";
3-
import { on as applicationOn, uncaughtErrorEvent, UnhandledErrorEventData } from "application";
2+
import { setTimeout, clearTimeout } from "tns-core-modules/timer";
3+
import { on as applicationOn, uncaughtErrorEvent, UnhandledErrorEventData } from "tns-core-modules/application";
4+
import * as utils from "tns-core-modules/utils/utils";
45

56
import {
67
LocationBase,
@@ -239,48 +240,64 @@ export function clearWatch(_watchId: number): void {
239240
LocationMonitor.stopLocationMonitoring(_watchId);
240241
}
241242

242-
export function enableLocationRequest(always?: boolean): Promise<void> {
243+
export function enableLocationRequest(always?: boolean, iosOpenSettingsIfLocationHasBeenDenied?: boolean): Promise<void> {
243244
return new Promise<void>(function (resolve, reject) {
244-
if (_isEnabled()) {
245+
const locationIsEnabled = _isEnabled();
246+
247+
if (locationIsEnabled) {
245248
resolve();
246249
return;
247-
}
248-
249-
let listener = LocationListenerImpl.initWithPromiseCallbacks(resolve, reject, always);
250-
try {
251-
let manager = getIOSLocationManager(listener, null);
252-
if (always) {
253-
manager.requestAlwaysAuthorization();
250+
} else {
251+
const status = getIOSLocationManagerStatus();
252+
if (status === CLAuthorizationStatus.kCLAuthorizationStatusDenied &&
253+
iosOpenSettingsIfLocationHasBeenDenied) {
254+
// now open the Settings so the user can toggle the Location permission
255+
utils.ios.getter(UIApplication, UIApplication.sharedApplication).openURL(NSURL.URLWithString(UIApplicationOpenSettingsURLString));
254256
} else {
255-
manager.requestWhenInUseAuthorization();
257+
let listener = LocationListenerImpl.initWithPromiseCallbacks(resolve, reject, always);
258+
try {
259+
let manager = getIOSLocationManager(listener, null);
260+
if (always) {
261+
manager.requestAlwaysAuthorization();
262+
} else {
263+
manager.requestWhenInUseAuthorization();
264+
}
265+
} catch (e) {
266+
LocationMonitor.stopLocationMonitoring(listener.id);
267+
reject(e);
268+
}
256269
}
257-
} catch (e) {
258-
LocationMonitor.stopLocationMonitoring(listener.id);
259-
reject(e);
260270
}
261271
});
262272
}
263273

264-
function _isEnabled(options?: Options): boolean {
274+
function _isEnabled(): boolean {
265275
if (CLLocationManager.locationServicesEnabled()) {
276+
const status = getIOSLocationManagerStatus();
277+
266278
// CLAuthorizationStatus.kCLAuthorizationStatusAuthorizedWhenInUse and
267279
// CLAuthorizationStatus.kCLAuthorizationStatusAuthorizedAlways are options that are available in iOS 8.0+
268280
// while CLAuthorizationStatus.kCLAuthorizationStatusAuthorized is here to support iOS 8.0-.
269-
const AUTORIZED_WHEN_IN_USE = CLAuthorizationStatus.kCLAuthorizationStatusAuthorizedWhenInUse;
270-
271-
return (CLLocationManager.authorizationStatus() === AUTORIZED_WHEN_IN_USE
272-
|| CLLocationManager.authorizationStatus() === CLAuthorizationStatus.kCLAuthorizationStatusAuthorizedAlways
273-
|| CLLocationManager.authorizationStatus() === CLAuthorizationStatus.kCLAuthorizationStatusAuthorized);
281+
return (status === CLAuthorizationStatus.kCLAuthorizationStatusAuthorizedWhenInUse
282+
|| status === CLAuthorizationStatus.kCLAuthorizationStatusAuthorizedAlways
283+
// @ts-ignore: Types have no overlap error
284+
|| status === CLAuthorizationStatus.kCLAuthorizationStatusAuthorized);
274285
}
275286
return false;
276287
}
277288

278-
export function isEnabled(): Promise<boolean> {
289+
export function isEnabled(options: Options): Promise<boolean> {
279290
return new Promise(function (resolve, reject) {
280-
resolve(_isEnabled());
291+
const isEnabledResult = _isEnabled();
292+
293+
resolve(isEnabledResult);
281294
});
282295
}
283296

297+
export function getIOSLocationManagerStatus(): CLAuthorizationStatus {
298+
return CLLocationManager.authorizationStatus();
299+
}
300+
284301
export function distance(loc1: Location, loc2: Location): number {
285302
if (!loc1.ios) {
286303
loc1.ios = clLocationFromLocation(loc1);

src/location-monitor.d.ts

Lines changed: 68 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,60 @@
11
import { Location } from "./location";
22

33
/**
4-
* Provides options for location monitoring.
5-
*/
4+
* Provides options for location monitoring.
5+
*/
66
export interface Options {
7-
/**
8-
* Specifies desired accuracy in meters. Defaults to DesiredAccuracy.HIGH
9-
*/
10-
desiredAccuracy?: number;
11-
12-
/**
13-
* Update distance filter in meters. Specifies how often to update. Default is no filter
14-
*/
15-
updateDistance?: number;
16-
17-
/**
18-
* Interval between location updates, in milliseconds (ignored on iOS)
19-
*/
20-
updateTime?: number;
21-
22-
/**
23-
* Minimum time interval between location updates, in milliseconds (ignored on iOS)
24-
*/
25-
minimumUpdateTime?: number;
26-
27-
/**
28-
* How old locations to receive in ms.
29-
*/
30-
maximumAge?: number;
31-
32-
/**
33-
* How long to wait for a location in ms.
34-
*/
35-
timeout?: number;
36-
37-
/**
38-
* A Boolean value which has to be set to true on iOS versions > 9.0 to allow the application to receive location updates in
39-
* background in combination with the UIBackgroundModes key 'location' in the Info.plist. An exception is thrown if the
40-
* property is enabled without the UIBackgroundModes key set to true. The value is ignored on Android.
41-
* @see {@link https://developer.apple.com/reference/corelocation/cllocationmanager/1620568-allowsbackgroundlocationupdates|allowsBackgroundLocationUpdates}
42-
*/
43-
iosAllowsBackgroundLocationUpdates?: boolean;
44-
45-
/**
46-
* A Boolean value which has to be set to false on iOS to deactivate the automatic pause of location updates. The location manager might pause
47-
* location updates for a period of time to improve battery life. This behavior may stop a long-running background task. Set this flag to false
48-
* to prevent this behavior. The value is ignored on Android.
49-
* @see {@link https://developer.apple.com/reference/corelocation/cllocationmanager/1620553-pauseslocationupdatesautomatical|pausesLocationUpdatesAutomatically}
50-
*/
51-
iosPausesLocationUpdatesAutomatically?: boolean;
7+
/**
8+
* Specifies desired accuracy in meters. Defaults to DesiredAccuracy.HIGH
9+
*/
10+
desiredAccuracy?: number;
11+
12+
/**
13+
* Update distance filter in meters. Specifies how often to update. Default is no filter
14+
*/
15+
updateDistance?: number;
16+
17+
/**
18+
* Interval between location updates, in milliseconds (ignored on iOS)
19+
*/
20+
updateTime?: number;
21+
22+
/**
23+
* Minimum time interval between location updates, in milliseconds (ignored on iOS)
24+
*/
25+
minimumUpdateTime?: number;
26+
27+
/**
28+
* How old locations to receive in ms.
29+
*/
30+
maximumAge?: number;
31+
32+
/**
33+
* How long to wait for a location in ms.
34+
*/
35+
timeout?: number;
36+
37+
/**
38+
* A Boolean value which has to be set to true on iOS versions > 9.0 to allow the application to receive location updates in
39+
* background in combination with the UIBackgroundModes key 'location' in the Info.plist. An exception is thrown if the
40+
* property is enabled without the UIBackgroundModes key set to true. The value is ignored on Android.
41+
* @see {@link https://developer.apple.com/reference/corelocation/cllocationmanager/1620568-allowsbackgroundlocationupdates|allowsBackgroundLocationUpdates}
42+
*/
43+
iosAllowsBackgroundLocationUpdates?: boolean;
44+
45+
/**
46+
* A Boolean value which has to be set to false on iOS to deactivate the automatic pause of location updates. The location manager might pause
47+
* location updates for a period of time to improve battery life. This behavior may stop a long-running background task. Set this flag to false
48+
* to prevent this behavior. The value is ignored on Android.
49+
* @see {@link https://developer.apple.com/reference/corelocation/cllocationmanager/1620553-pauseslocationupdatesautomatical|pausesLocationUpdatesAutomatically}
50+
*/
51+
iosPausesLocationUpdatesAutomatically?: boolean;
52+
53+
/**
54+
* A boolean value which if set to true, the application will open the Settings
55+
* app only after the user has previously denied the location permission.
56+
*/
57+
iosOpenSettingsIfLocationHasBeenDenied?: boolean;
5258
}
5359

5460
declare type successCallbackType = (location: Location) => void;
@@ -64,7 +70,11 @@ export function getCurrentLocation(options: Options): Promise<Location>;
6470
* Monitor for location change.
6571
* @returns {number} The watch id
6672
*/
67-
export function watchLocation(successCallback: successCallbackType, errorCallback: errorCallbackType, options: Options): number;
73+
export function watchLocation(
74+
successCallback: successCallbackType,
75+
errorCallback: errorCallbackType,
76+
options: Options
77+
): number;
6878

6979
/**
7080
* Stop monitoring for location change. Parameter expected is the watchId returned from `watchLocation`.
@@ -76,11 +86,12 @@ export function clearWatch(watchId: number): void;
7686
* Ask for permissions to use location services. The option 'always' is applicable to iOS only. Read more: https://developer.apple.com/documentation/corelocation/cllocationmanager/1620551-requestalwaysauthorization.
7787
* @param always iOS only. https://developer.apple.com/documentation/corelocation/cllocationmanager/1620551-requestalwaysauthorization
7888
*/
79-
export function enableLocationRequest(always?: boolean): Promise<void>;
89+
export function enableLocationRequest(always?: boolean, iosOpenSettingsIfLocationHasBeenDenied?: boolean): Promise<void>;
8090

8191
/**
8292
* Check if location services are enabled
83-
* @param options android only. Check the availability based on the specified options.
93+
* @param options Check the availability based on the specified options.
94+
* ** iOS Only ** utilizes the iosOpenSettingsIfLocationHasBeenDenied value **
8495
* @returns {boolean} True if location services are enabled
8596
*/
8697
export function isEnabled(options?: Options): Promise<boolean>;
@@ -92,3 +103,10 @@ export function isEnabled(options?: Options): Promise<boolean>;
92103
* @returns {number} The calculated distance in meters.
93104
*/
94105
export function distance(loc1: Location, loc2: Location): number;
106+
107+
/**
108+
* ** iOS Only **
109+
* Returns the value for the CLLocationManager on iOS.
110+
* @returns {CLAuthorizationStatus} The status of the Location Authorization permission.
111+
*/
112+
export function getIOSLocationManagerStatus(): CLAuthorizationStatus;

0 commit comments

Comments
 (0)