Skip to content

Commit f3ecc0a

Browse files
authored
Added setLocationShared check into LocationServices callback (#539)
* Added setLocationShared check into LocationServices callback * Now when a user allows LocationServices, we will not store a 'last_location' since our player update checks this variable along with mShareLocation to decide whether or not to push the location of the player * Added a unit test to test the mocked scenario of granting permission while mShareLocation is true and false * Enhanced OneSignalLocationOverrider * Better end to end mocking of showing a LocationServices alert and clicking allow * Then based on the setLocationShared setting, decide on whether or not to update the `lastLocation` to update a player record * Misunderstood when locationManager:didUpdateLocations: was called * Added different functionality for overriding the location services prompts * Added flags so when prompts are shown we can then mock a `Allow` * 3 checks now made for calling the override for locationManager:didUpdateLocations: * We check iOS version, and the two BOOL flags related to info.plist params * Update OneSignalLocationOverrider.m * Moved some test setup in UnitTests.m to fix broken tests
1 parent 7f7fc6b commit f3ecc0a

File tree

9 files changed

+275
-17
lines changed

9 files changed

+275
-17
lines changed

iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/project.pbxproj

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,8 @@
149149
9D3300F723145AF3000F0A83 /* OneSignalUserDefaults.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D3300F323145AF3000F0A83 /* OneSignalUserDefaults.m */; };
150150
9D3300F823145AF3000F0A83 /* OneSignalUserDefaults.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D3300F323145AF3000F0A83 /* OneSignalUserDefaults.m */; };
151151
9D3300FA23149DAE000F0A83 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9D3300F923149DAE000F0A83 /* CoreGraphics.framework */; };
152+
9D348537233C669E00EB81C9 /* CoreLocation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9D348536233C669E00EB81C9 /* CoreLocation.framework */; };
153+
9D34853A233D2E3600EB81C9 /* OneSignalLocationOverrider.m in Sources */ = {isa = PBXBuildFile; fileRef = 9D348539233D2E3600EB81C9 /* OneSignalLocationOverrider.m */; };
152154
9D59C2F82321C7720008ECCF /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CACBAAAB218A662B000ACAA5 /* WebKit.framework */; };
153155
9D59C2F92321C7780008ECCF /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9D3300F923149DAE000F0A83 /* CoreGraphics.framework */; };
154156
9DDFEEF223189C0800EAE0BB /* OneSignalViewHelper.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D3300F423145AF3000F0A83 /* OneSignalViewHelper.h */; };
@@ -373,6 +375,9 @@
373375
9D3300F323145AF3000F0A83 /* OneSignalUserDefaults.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OneSignalUserDefaults.m; sourceTree = "<group>"; };
374376
9D3300F423145AF3000F0A83 /* OneSignalViewHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OneSignalViewHelper.h; sourceTree = "<group>"; };
375377
9D3300F923149DAE000F0A83 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
378+
9D348536233C669E00EB81C9 /* CoreLocation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreLocation.framework; path = System/Library/Frameworks/CoreLocation.framework; sourceTree = SDKROOT; };
379+
9D348538233D2DCF00EB81C9 /* OneSignalLocationOverrider.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OneSignalLocationOverrider.h; sourceTree = "<group>"; };
380+
9D348539233D2E3600EB81C9 /* OneSignalLocationOverrider.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OneSignalLocationOverrider.m; sourceTree = "<group>"; };
376381
CA08FC711FE99AFD004C445F /* OneSignalClient.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OneSignalClient.h; sourceTree = "<group>"; };
377382
CA08FC721FE99AFD004C445F /* OneSignalClient.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OneSignalClient.m; sourceTree = "<group>"; };
378383
CA08FC761FE99B13004C445F /* OneSignalRequest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OneSignalRequest.h; sourceTree = "<group>"; };
@@ -471,6 +476,7 @@
471476
isa = PBXFrameworksBuildPhase;
472477
buildActionMask = 2147483647;
473478
files = (
479+
9D348537233C669E00EB81C9 /* CoreLocation.framework in Frameworks */,
474480
9D3300FA23149DAE000F0A83 /* CoreGraphics.framework in Frameworks */,
475481
CACBAAAC218A662B000ACAA5 /* WebKit.framework in Frameworks */,
476482
911E2CC81E399834003112A4 /* UserNotifications.framework in Frameworks */,
@@ -507,6 +513,7 @@
507513
37747F9519147D6500558FAD /* Frameworks */ = {
508514
isa = PBXGroup;
509515
children = (
516+
9D348536233C669E00EB81C9 /* CoreLocation.framework */,
510517
9D3300F923149DAE000F0A83 /* CoreGraphics.framework */,
511518
CACBAAAB218A662B000ACAA5 /* WebKit.framework */,
512519
911E2CC71E399834003112A4 /* UserNotifications.framework */,
@@ -562,6 +569,8 @@
562569
CA8E190A2194FE0B009DA223 /* OSMessagingControllerOverrider.m */,
563570
CAAE0DFB2195216900A57402 /* OneSignalOverrider.h */,
564571
CAAE0DFC2195216900A57402 /* OneSignalOverrider.m */,
572+
9D348538233D2DCF00EB81C9 /* OneSignalLocationOverrider.h */,
573+
9D348539233D2E3600EB81C9 /* OneSignalLocationOverrider.m */,
565574
);
566575
path = Shadows;
567576
sourceTree = "<group>";
@@ -1113,6 +1122,7 @@
11131122
912412201E73342200E41FD7 /* OneSignalJailbreakDetection.m in Sources */,
11141123
CAAEA68921ED68A40049CF15 /* OneSignalNotificationCategoryController.m in Sources */,
11151124
CA85C15320604AEA003AB529 /* RequestTests.m in Sources */,
1125+
9D34853A233D2E3600EB81C9 /* OneSignalLocationOverrider.m in Sources */,
11161126
CAE2E5A8215D80010036FD32 /* OneSignalTrackFirebaseAnalytics.m in Sources */,
11171127
912412381E73342200E41FD7 /* OneSignalTrackIAP.m in Sources */,
11181128
CA63AF8720211FF800E340FB /* UnitTestCommonMethods.m in Sources */,

iOS_SDK/OneSignalSDK/Source/OneSignal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,7 @@ typedef NS_ENUM(NSUInteger, ONE_S_LOG_LEVEL) {
442442
// - Request and track user's location
443443
+ (void)promptLocation;
444444
+ (void)setLocationShared:(BOOL)enable;
445+
+ (BOOL)isLocationShared;
445446

446447

447448
// Only used for wrapping SDKs, such as Unity, Cordova, Xamarin, etc.

iOS_SDK/OneSignalSDK/Source/OneSignal.m

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1302,6 +1302,10 @@ + (void) promptLocation {
13021302
[OneSignalLocation getLocation:true];
13031303
}
13041304

1305+
+ (BOOL)isLocationShared {
1306+
return mShareLocation;
1307+
}
1308+
13051309

13061310
+ (void) handleDidFailRegisterForRemoteNotification:(NSError*)err {
13071311
waitingForApnsResponse = false;

iOS_SDK/OneSignalSDK/Source/OneSignalLocation.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,12 @@ typedef struct os_last_location {
4141
double horizontalAccuracy;
4242
} os_last_location;
4343

44-
4544
@interface OneSignalLocation : NSObject
45+
46+
+ (OneSignalLocation*) sharedInstance;
47+
+ (bool)started;
48+
+ (void)internalGetLocation:(bool)prompt;
49+
- (void)locationManager:(id)manager didUpdateLocations:(NSArray *)locations;
4650
+ (void) getLocation:(bool)prompt;
4751
+ (void) sendLocation;
4852
+ (os_last_location*)lastLocation;

iOS_SDK/OneSignalSDK/Source/OneSignalLocation.m

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,11 @@ +(OneSignalLocation*) sharedInstance {
8686
+ (os_last_location*)lastLocation {
8787
return lastLocation;
8888
}
89+
90+
+ (bool)started {
91+
return started;
92+
}
93+
8994
+ (void)clearLastLocation {
9095
@synchronized(OneSignalLocation.mutexObjectForLastLocation) {
9196
lastLocation = nil;
@@ -113,7 +118,7 @@ + (void)onfocus:(BOOL)isActive {
113118
if ([OneSignal requiresUserPrivacyConsent])
114119
return;
115120

116-
if(!locationManager || !started) return;
121+
if(!locationManager || ![self started]) return;
117122

118123
/**
119124
We have a state switch
@@ -162,8 +167,8 @@ + (void) endTask {
162167

163168

164169

165-
+ (void) internalGetLocation:(bool)prompt {
166-
if (started)
170+
+ (void)internalGetLocation:(bool)prompt {
171+
if ([self started])
167172
return;
168173

169174
id clLocationManagerClass = NSClassFromString(@"CLLocationManager");
@@ -179,43 +184,42 @@ + (void) internalGetLocation:(bool)prompt {
179184
locationManager = [[clLocationManagerClass alloc] init];
180185
[locationManager setValue:[self sharedInstance] forKey:@"delegate"];
181186

182-
float deviceOSVersion = [[[UIDevice currentDevice] systemVersion] floatValue];
183-
if (deviceOSVersion >= 8.0) {
187+
if ([OneSignalHelper isIOSVersionGreaterThanOrEqual:@"8.0"]) {
184188

185189
//Check info plist for request descriptions
186190
//LocationAlways > LocationWhenInUse > No entry (Log error)
187191
//Location Always requires: Location Background Mode + NSLocationAlwaysUsageDescription
188192
NSArray* backgroundModes = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UIBackgroundModes"];
189193
NSString* alwaysDescription = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationAlwaysUsageDescription"] ?: [[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationAlwaysAndWhenInUseUsageDescription"];
190194
// use background location updates if always permission granted or prompt allowed
191-
if(backgroundModes && [backgroundModes containsObject:@"location"] && alwaysDescription && (permissionStatus == 3 || prompt)) {
195+
if (backgroundModes && [backgroundModes containsObject:@"location"] && alwaysDescription && (permissionStatus == 3 || prompt)) {
192196
[locationManager performSelector:@selector(requestAlwaysAuthorization)];
193-
if (deviceOSVersion >= 9.0) {
197+
if ([OneSignalHelper isIOSVersionGreaterThanOrEqual:@"9.0"]) {
194198
[locationManager setValue:@YES forKey:@"allowsBackgroundLocationUpdates"];
195199
}
196200
}
197201

198-
else if([[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationWhenInUseUsageDescription"]) {
202+
else if ([[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationWhenInUseUsageDescription"]) {
199203
if (permissionStatus == 0) [locationManager performSelector:@selector(requestWhenInUseAuthorization)];
200204
}
201205

202206
else onesignal_Log(ONE_S_LL_ERROR, @"Include a privacy NSLocationAlwaysUsageDescription or NSLocationWhenInUseUsageDescription in your info.plist to request location permissions.");
203207
}
204-
205-
// iOS 6 and 7 prompts for location here.
208+
209+
// For iOS 6 and 7, location services are prompted here
210+
// This method is also used for getting the location manager to obtain an initial location fix
211+
// and will notify your delegate by calling its locationManager:didUpdateLocations: method
206212
[locationManager performSelector:@selector(startUpdatingLocation)];
207213

208-
209-
210214
started = true;
211215
}
212216

213217
#pragma mark CLLocationManagerDelegate
214218

215219
- (void)locationManager:(id)manager didUpdateLocations:(NSArray *)locations {
216220

217-
// return if the user has not granted privacy permissions
218-
if ([OneSignal requiresUserPrivacyConsent])
221+
// return if the user has not granted privacy permissions or location shared is false
222+
if ([OneSignal requiresUserPrivacyConsent] || ![OneSignal isLocationShared])
219223
return;
220224

221225
[manager performSelector:@selector(stopUpdatingLocation)];
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/**
2+
* Modified MIT License
3+
*
4+
* Copyright 2019 OneSignal
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* 1. The above copyright notice and this permission notice shall be included in
14+
* all copies or substantial portions of the Software.
15+
*
16+
* 2. All copies of substantial portions of the Software may only be used in connection
17+
* with services provided by OneSignal.
18+
*
19+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25+
* THE SOFTWARE.
26+
*/
27+
28+
#ifndef OneSignalLocationOverrider_h
29+
#define OneSignalLocationOverrider_h
30+
31+
@interface OneSignalLocationOverrider : NSObject
32+
33+
+ (bool)overrideStarted;
34+
+ (void)grantLocationServices;
35+
+ (int)overrideAuthorizationStatus;
36+
- (void)overrideRequestAlwaysAuthorization;
37+
- (void)overrideRequestWhenInUseAuthorization;
38+
- (void)overrideStartUpdatingLocation;
39+
40+
@end
41+
42+
#endif /* OneSignalLocationOverrider_h */
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
/**
2+
* Modified MIT License
3+
*
4+
* Copyright 2019 OneSignal
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* 1. The above copyright notice and this permission notice shall be included in
14+
* all copies or substantial portions of the Software.
15+
*
16+
* 2. All copies of substantial portions of the Software may only be used in connection
17+
* with services provided by OneSignal.
18+
*
19+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25+
* THE SOFTWARE.
26+
*/
27+
28+
#import <Foundation/Foundation.h>
29+
#import <CoreLocation/CoreLocation.h>
30+
31+
#import "TestHelperFunctions.h"
32+
#import "OneSignalSelectorHelpers.h"
33+
#import "OneSignalHelperOverrider.h"
34+
#import "OneSignalLocation.h"
35+
#import "OneSignalLocationOverrider.h"
36+
37+
@implementation OneSignalLocationOverrider
38+
39+
// BOOL to track whetehr the LocationServices prompt has been seen
40+
bool startedMock;
41+
// int representing the current permission status for LocationServices
42+
int permissionStatusMock;
43+
// BOOL to track whether or not location request was made (NSLocationAlwaysUsageDescription, NSLocationAlwaysAndWhenInUseUsageDescription)
44+
bool calledRequestAlwaysAuthorization;
45+
// BOOL to track whether or not location request was made (NSLocationWhenInUseUsageDescription)
46+
bool calledRequestWhenInUseAuthorization;
47+
48+
// Location updates require a mocked manager and set of locations to be passed in
49+
CLLocationManager* locationManager;
50+
NSArray *locations;
51+
52+
+ (void)load {
53+
54+
injectStaticSelector([OneSignalLocationOverrider class], @selector(overrideStarted), [OneSignalLocation class], @selector(started));
55+
injectStaticSelector([OneSignalLocationOverrider class], @selector(overrideAuthorizationStatus), [CLLocationManager class], @selector(authorizationStatus));
56+
57+
injectSelector([OneSignalLocationOverrider class], @selector(overrideRequestAlwaysAuthorization), [CLLocationManager class], @selector(requestAlwaysAuthorization));
58+
injectSelector([OneSignalLocationOverrider class], @selector(overrideRequestWhenInUseAuthorization), [CLLocationManager class], @selector(requestWhenInUseAuthorization));
59+
injectSelector([OneSignalLocationOverrider class], @selector(overrideStartUpdatingLocation), [CLLocationManager class], @selector(startUpdatingLocation));
60+
61+
// Never asked use for location service permission
62+
startedMock = false;
63+
// Set permission status for location services to 0 (not granted)
64+
permissionStatusMock = 0;
65+
// Never made a request for location based on info.plist params
66+
calledRequestAlwaysAuthorization = false;
67+
calledRequestWhenInUseAuthorization = false;
68+
69+
// Create a mock location manager
70+
locationManager = [self createLocationManager];
71+
// Creater a mock array of locations
72+
id location = [self createLocation];
73+
locations = @[location];
74+
}
75+
76+
+ (bool)overrideStarted {
77+
return startedMock;
78+
}
79+
80+
// Create a mocked location manager for use in overrider
81+
+ (CLLocationManager*)createLocationManager {
82+
return [[CLLocationManager alloc] init];
83+
}
84+
85+
// Create a mocked location for use in overrider
86+
+ (CLLocation*)createLocation {
87+
return [[CLLocation alloc] initWithLatitude:3.0 longitude:4.0];
88+
}
89+
90+
// Simulate granting location services
91+
// The `locationManager` method is called after a user clicks the `Allow` button in the LocationServices alert because
92+
// a location update is triggered
93+
+ (void)grantLocationServices {
94+
95+
// Reset started to false (never seen prompt before)
96+
startedMock = false;
97+
98+
// Reset request flags
99+
calledRequestAlwaysAuthorization = false;
100+
calledRequestWhenInUseAuthorization = false;
101+
102+
[OneSignalLocation internalGetLocation:true];
103+
}
104+
105+
+ (int)overrideAuthorizationStatus {
106+
return permissionStatusMock;
107+
}
108+
109+
- (void)overrideRequestAlwaysAuthorization {
110+
// Overriden to do nothing, causes a info.plist warning failing our tests
111+
calledRequestAlwaysAuthorization = true;
112+
}
113+
114+
- (void)overrideRequestWhenInUseAuthorization {
115+
// Overriden to do nothing, causes a info.plist warning failing our tests
116+
calledRequestWhenInUseAuthorization = true;
117+
}
118+
119+
- (void)overrideStartUpdatingLocation {
120+
// If iOS is less than 8.0, the startUpdatingLocation prompts the user
121+
// Otherwise, we want to check if the location request was made for info.plist params
122+
if (OneSignalHelperOverrider.mockIOSVersion < 8.0 || calledRequestAlwaysAuthorization || calledRequestWhenInUseAuthorization)
123+
[[OneSignalLocation sharedInstance] locationManager:locationManager didUpdateLocations:locations];
124+
}
125+
126+
@end

iOS_SDK/OneSignalSDK/UnitTests/UnitTestCommonMethods.m

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
#import "NSTimerOverrider.h"
4747
#import "OSMessagingControllerOverrider.h"
4848
#import "OSInAppMessagingHelpers.h"
49+
#import "OneSignalLocation.h"
4950

5051
NSString * serverUrlWithPath(NSString *path) {
5152
return [NSString stringWithFormat:@"%@%@%@", SERVER_URL, API_VERSION, path];

0 commit comments

Comments
 (0)