From bbde9379c8e5bab6ae0732dcd30a4fa9fa67af30 Mon Sep 17 00:00:00 2001 From: emawby Date: Thu, 13 Oct 2022 14:39:11 -0700 Subject: [PATCH 1/2] remove armv7s from build architectures This is causing a build failure in Xcode 14 since armv7s is deprecated --- .../OneSignal.xcodeproj/project.pbxproj | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/project.pbxproj b/iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/project.pbxproj index 08969f29f..1e7d95e72 100644 --- a/iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/project.pbxproj +++ b/iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/project.pbxproj @@ -2701,7 +2701,6 @@ ALWAYS_SEARCH_USER_PATHS = NO; ARCHS = ( "$(ARCHS_STANDARD)", - armv7s, x86_64h, x86_64, ); @@ -2765,6 +2764,11 @@ CA2951B82167F4120064227A /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = ( + "$(ARCHS_STANDARD)", + x86_64h, + x86_64, + ); CLANG_ANALYZER_NONNULL = YES; CLANG_ENABLE_CODE_COVERAGE = NO; CLANG_ENABLE_MODULES = YES; @@ -2859,7 +2863,6 @@ ALWAYS_SEARCH_USER_PATHS = NO; ARCHS = ( "$(ARCHS_STANDARD)", - armv7s, x86_64h, x86_64, ); @@ -2923,6 +2926,11 @@ CA2951C42167FB950064227A /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = ( + "$(ARCHS_STANDARD)", + x86_64h, + x86_64, + ); CLANG_ANALYZER_NONNULL = YES; CLANG_ENABLE_CODE_COVERAGE = NO; CLANG_ENABLE_MODULES = YES; @@ -3016,6 +3024,11 @@ isa = XCBuildConfiguration; buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; + ARCHS = ( + "$(ARCHS_STANDARD)", + x86_64h, + x86_64, + ); CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; @@ -3072,6 +3085,11 @@ isa = XCBuildConfiguration; buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; + ARCHS = ( + "$(ARCHS_STANDARD)", + x86_64h, + x86_64, + ); CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; From 0102f9a47de5a7742552c70c526eb8eddad5235f Mon Sep 17 00:00:00 2001 From: emawby Date: Thu, 13 Oct 2022 14:40:25 -0700 Subject: [PATCH 2/2] move location fetch to a background thread running locationServicesEnabled on the main thread causes an Xcode warning in Xcode 14 about a potential UI holdup. However the location manager class needs to be created and run on a thread with a runloop or it's updates won't come through. To handle this we will do locationServicesEnabled on a background thread and location fetch on main thread --- .../OneSignalSDK/Source/OneSignalLocation.m | 155 ++++++++++-------- .../UnitTests/RemoteParamsTests.m | 3 + 2 files changed, 86 insertions(+), 72 deletions(-) diff --git a/iOS_SDK/OneSignalSDK/Source/OneSignalLocation.m b/iOS_SDK/OneSignalSDK/Source/OneSignalLocation.m index 044d81658..48001f2b3 100644 --- a/iOS_SDK/OneSignalSDK/Source/OneSignalLocation.m +++ b/iOS_SDK/OneSignalSDK/Source/OneSignalLocation.m @@ -200,81 +200,92 @@ + (void)sendCurrentAuthStatusToListeners { } + (void)internalGetLocation:(bool)prompt fallbackToSettings:(BOOL)fallback { - fallbackToSettings = fallback; - id clLocationManagerClass = NSClassFromString(@"CLLocationManager"); - - // On the application init we are always calling this method - // If location permissions was not asked "started" will never be true - if ([self started]) { - // We evaluate the following cases after permissions were asked (denied or given) - CLAuthorizationStatus permissionStatus = [clLocationManagerClass performSelector:@selector(authorizationStatus)]; - BOOL showSettings = prompt && fallback && permissionStatus == kCLAuthorizationStatusDenied; - [OneSignal onesignalLog:ONE_S_LL_DEBUG message:[NSString stringWithFormat:@"internalGetLocation called showSettings: %@", showSettings ? @"YES" : @"NO"]]; - // Fallback to settings alert view when the following condition are true: - // - On a prompt flow - // - Fallback to settings is enabled - // - Permission were denied - if (showSettings) - [self showLocationSettingsAlertController]; - else - [self sendCurrentAuthStatusToListeners]; - return; - } - - // Check for location in plist - if (![clLocationManagerClass performSelector:@selector(locationServicesEnabled)]) { - [OneSignal onesignalLog:ONE_S_LL_DEBUG message:@"CLLocationManager locationServices Disabled."]; - [self sendAndClearLocationListener:ERROR]; - return; - } - - CLAuthorizationStatus permissionStatus = [clLocationManagerClass performSelector:@selector(authorizationStatus)]; - // return if permission not determined and should not prompt - if (permissionStatus == kCLAuthorizationStatusNotDetermined && !prompt) { - [OneSignal onesignalLog:ONE_S_LL_DEBUG message:@"internalGetLocation kCLAuthorizationStatusNotDetermined."]; - return; - } - - [self sendCurrentAuthStatusToListeners]; - locationManager = [[clLocationManagerClass alloc] init]; - [locationManager setValue:[self sharedInstance] forKey:@"delegate"]; - + /* + Do permission checking on a background thread to resolve locationServicesEnabled warning. + */ + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + fallbackToSettings = fallback; + id clLocationManagerClass = NSClassFromString(@"CLLocationManager"); - //Check info plist for request descriptions - //LocationAlways > LocationWhenInUse > No entry (Log error) - //Location Always requires: Location Background Mode + NSLocationAlwaysUsageDescription - NSArray* backgroundModes = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UIBackgroundModes"]; - NSString* alwaysDescription = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationAlwaysUsageDescription"] ?: [[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationAlwaysAndWhenInUseUsageDescription"]; - // use background location updates if always permission granted or prompt allowed - BOOL backgroundLocationEnable = backgroundModes && [backgroundModes containsObject:@"location"] && alwaysDescription; - BOOL permissionEnable = permissionStatus == kCLAuthorizationStatusAuthorizedAlways || prompt; - - [OneSignal onesignalLog:ONE_S_LL_DEBUG message:[NSString stringWithFormat:@"internalGetLocation called backgroundLocationEnable: %@ permissionEnable: %@", backgroundLocationEnable ? @"YES" : @"NO", permissionEnable ? @"YES" : @"NO"]]; - - if (backgroundLocationEnable && permissionEnable) { - #pragma clang diagnostic push - #pragma clang diagnostic ignored "-Warc-performSelector-leaks" - [locationManager performSelector:NSSelectorFromString(@"requestAlwaysAuthorization")]; - #pragma clang diagnostic pop - if ([OneSignalHelper isIOSVersionGreaterThanOrEqual:@"9.0"]) - [locationManager setValue:@YES forKey:@"allowsBackgroundLocationUpdates"]; - } - - else if ([[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationWhenInUseUsageDescription"]) { - if (permissionStatus == kCLAuthorizationStatusNotDetermined) - [locationManager performSelector:@selector(requestWhenInUseAuthorization)]; - } - - else { - [OneSignal onesignalLog:ONE_S_LL_ERROR message:@"Include a privacy NSLocationAlwaysUsageDescription or NSLocationWhenInUseUsageDescription in your info.plist to request location permissions."]; - [self sendAndClearLocationListener:LOCATION_PERMISSIONS_MISSING_INFO_PLIST]; - } + // On the application init we are always calling this method + // If location permissions was not asked "started" will never be true + if ([self started]) { + // We evaluate the following cases after permissions were asked (denied or given) + CLAuthorizationStatus permissionStatus = [clLocationManagerClass performSelector:@selector(authorizationStatus)]; + BOOL showSettings = prompt && fallback && permissionStatus == kCLAuthorizationStatusDenied; + [OneSignal onesignalLog:ONE_S_LL_DEBUG message:[NSString stringWithFormat:@"internalGetLocation called showSettings: %@", showSettings ? @"YES" : @"NO"]]; + // Fallback to settings alert view when the following condition are true: + // - On a prompt flow + // - Fallback to settings is enabled + // - Permission were denied + if (showSettings) + [self showLocationSettingsAlertController]; + else + [self sendCurrentAuthStatusToListeners]; + return; + } - // This method is used for getting the location manager to obtain an initial location fix - // and will notify your delegate by calling its locationManager:didUpdateLocations: method - [locationManager performSelector:@selector(startUpdatingLocation)]; + // Check for location in plist + if (![clLocationManagerClass performSelector:@selector(locationServicesEnabled)]) { + [OneSignal onesignalLog:ONE_S_LL_DEBUG message:@"CLLocationManager locationServices Disabled."]; + [self sendAndClearLocationListener:ERROR]; + return; + } + + CLAuthorizationStatus permissionStatus = [clLocationManagerClass performSelector:@selector(authorizationStatus)]; + // return if permission not determined and should not prompt + if (permissionStatus == kCLAuthorizationStatusNotDetermined && !prompt) { + [OneSignal onesignalLog:ONE_S_LL_DEBUG message:@"internalGetLocation kCLAuthorizationStatusNotDetermined."]; + return; + } + + [self sendCurrentAuthStatusToListeners]; + /* + The location manager must be created on a thread with a run loop so we will go back to the main thread. + */ + dispatch_async(dispatch_get_main_queue(), ^{ + locationManager = [[clLocationManagerClass alloc] init]; + [locationManager setValue:[self sharedInstance] forKey:@"delegate"]; + + + //Check info plist for request descriptions + //LocationAlways > LocationWhenInUse > No entry (Log error) + //Location Always requires: Location Background Mode + NSLocationAlwaysUsageDescription + NSArray* backgroundModes = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UIBackgroundModes"]; + NSString* alwaysDescription = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationAlwaysUsageDescription"] ?: [[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationAlwaysAndWhenInUseUsageDescription"]; + // use background location updates if always permission granted or prompt allowed + BOOL backgroundLocationEnable = backgroundModes && [backgroundModes containsObject:@"location"] && alwaysDescription; + BOOL permissionEnable = permissionStatus == kCLAuthorizationStatusAuthorizedAlways || prompt; + + [OneSignal onesignalLog:ONE_S_LL_DEBUG message:[NSString stringWithFormat:@"internalGetLocation called backgroundLocationEnable: %@ permissionEnable: %@", backgroundLocationEnable ? @"YES" : @"NO", permissionEnable ? @"YES" : @"NO"]]; + + if (backgroundLocationEnable && permissionEnable) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" + [locationManager performSelector:NSSelectorFromString(@"requestAlwaysAuthorization")]; +#pragma clang diagnostic pop + if ([OneSignalHelper isIOSVersionGreaterThanOrEqual:@"9.0"]) + [locationManager setValue:@YES forKey:@"allowsBackgroundLocationUpdates"]; + } + + else if ([[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationWhenInUseUsageDescription"]) { + if (permissionStatus == kCLAuthorizationStatusNotDetermined) + [locationManager performSelector:@selector(requestWhenInUseAuthorization)]; + } + + else { + [OneSignal onesignalLog:ONE_S_LL_ERROR message:@"Include a privacy NSLocationAlwaysUsageDescription or NSLocationWhenInUseUsageDescription in your info.plist to request location permissions."]; + [self sendAndClearLocationListener:LOCATION_PERMISSIONS_MISSING_INFO_PLIST]; + } + + // This method is used for getting the location manager to obtain an initial location fix + // and will notify your delegate by calling its locationManager:didUpdateLocations: method + + [locationManager performSelector:@selector(startUpdatingLocation)]; + started = true; + }); + }); - started = true; } + (void)showLocationSettingsAlertController { diff --git a/iOS_SDK/OneSignalSDK/UnitTests/RemoteParamsTests.m b/iOS_SDK/OneSignalSDK/UnitTests/RemoteParamsTests.m index 9705cc053..475cf1a32 100644 --- a/iOS_SDK/OneSignalSDK/UnitTests/RemoteParamsTests.m +++ b/iOS_SDK/OneSignalSDK/UnitTests/RemoteParamsTests.m @@ -90,6 +90,7 @@ - (void)testLocationPromptAcceptedWithSetLocationShared_iOS9_WhenInUseUsage { [OneSignal setLocationShared:true]; // Simulate user granting location services [OneSignalLocationOverrider grantLocationServices]; + [UnitTestCommonMethods runLongBackgroundThreads]; // Last location should exist since we are sharing location XCTAssertTrue([OneSignalLocation lastLocation]); } @@ -108,6 +109,7 @@ - (void)testLocationPromptAcceptedWithSetLocationSharedTrueFromRemoteParams_iOS9 XCTAssertFalse([OneSignalLocation lastLocation]); // Simulate user granting location services [OneSignalLocationOverrider grantLocationServices]; + [UnitTestCommonMethods runLongBackgroundThreads]; // Last location should exist since we are sharing location XCTAssertTrue([OneSignalLocation lastLocation]); } @@ -144,6 +146,7 @@ - (void)testLocationSharedTrueFromRemoteParams { XCTAssertFalse([OneSignalLocation lastLocation]); // Simulate user granting location services [OneSignalLocationOverrider grantLocationServices]; + [UnitTestCommonMethods runLongBackgroundThreads]; // Last location should exist since we are sharing location XCTAssertTrue([OneSignalLocation lastLocation]); }