Skip to content
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

feat(ios): reviving PR #12411 for ios background tasks #14142

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
36 changes: 36 additions & 0 deletions iphone/Classes/TiAppiOSProxy.m
Original file line number Diff line number Diff line change
Expand Up @@ -161,9 +161,20 @@ - (void)_listenerRemoved:(NSString *)type count:(int)count
if ((count == 1) && [type isEqual:@"backgroundfetch"]) {
[[NSNotificationCenter defaultCenter] removeObserver:self name:kTiBackgroundFetchNotification object:nil];
}
if ((count == 1) && [type isEqual:@"backgroundprocess"]) {
[[NSNotificationCenter defaultCenter] removeObserver:self name:kTiBackgroundProcessNotification object:nil];
}
if ((count == 1) && [type isEqual:@"sessioneventscompleted"]) {
[[NSNotificationCenter defaultCenter] removeObserver:self name:kTiURLSessionEventsCompleted object:nil];
}
if ((count == 1) && [type isEqual:@"backgroundprocess"]) {
NSArray *backgroundModes = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UIBackgroundModes"];
if ([backgroundModes containsObject:@"processing"]) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveBackgroundProcessNotification:) name:kTiBackgroundProcessNotification object:nil];
} else {
DebugLog(@"[ERROR] Cannot add backgroundprocess eventListener. Please add `processing` to UIBackgroundModes inside info.plist ");
}
}
if ((count == 1) && [type isEqual:@"silentpush"]) {
[[NSNotificationCenter defaultCenter] removeObserver:self name:kTiSilentPushNotification object:nil];
}
Expand Down Expand Up @@ -407,6 +418,20 @@ - (TiAppiOSBackgroundServiceProxy *)registerBackgroundService:(id)args
return proxy;
}

- (void)registerBackgroundTask:(id)args
{
ENSURE_SINGLE_ARG(args, NSDictionary);
ENSURE_STRING(args[@"identifier"]);
ENSURE_STRING(args[@"type"]);

if ([TiUtils isIOSVersionLower:@"13.0"]) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

The Titanium SDK has a current minimum of iOS 13 already, so you can remove this

DebugLog(@"This API is not supported fo iOS < 13.0");
return;
}

[[TiApp app] addBackgroundTask:args];
}

- (void)registerUserNotificationSettings:(id)args
{
ENSURE_SINGLE_ARG(args, NSDictionary);
Expand Down Expand Up @@ -889,6 +914,11 @@ - (void)didReceiveBackgroundFetchNotification:(NSNotification *)note
[self fireEvent:@"backgroundfetch" withObject:[note userInfo]];
}

- (void)didReceiveBackgroundProcessNotification:(NSNotification *)note
{
[self fireEvent:@"backgroundprocess" withObject:[note userInfo]];
}

- (void)didReceiveSilentPushNotification:(NSNotification *)note
{
[self fireEvent:@"silentpush" withObject:[note userInfo]];
Expand Down Expand Up @@ -975,6 +1005,9 @@ - (void)endBackgroundHandler:(id)handlerIdentifier

if ([handlerIdentifier rangeOfString:@"Session"].location != NSNotFound) {
[[TiApp app] performCompletionHandlerForBackgroundTransferWithKey:handlerIdentifier];
} else if ([handlerIdentifier hasPrefix:@"BgTask-"]) {
// handlerId = @"BgTask-" + BgTask.identifier. So remove @"BgTask-" to get actual BgTask identifier.
[[TiApp app] backgroundTaskCompletedForIdentifier:[handlerIdentifier substringFromIndex:7]];
} else {
[[TiApp app] performCompletionHandlerWithKey:handlerIdentifier andResult:UIBackgroundFetchResultNoData removeAfterExecution:NO];
}
Expand Down Expand Up @@ -1338,6 +1371,9 @@ - (NSString *)applicationOpenSettingsURL
MAKE_SYSTEM_PROP(USER_NOTIFICATION_ALERT_STYLE_ALERT, UNAlertStyleAlert);
MAKE_SYSTEM_PROP(USER_NOTIFICATION_ALERT_STYLE_BANNER, UNAlertStyleBanner);

MAKE_SYSTEM_STR(BACKGROUND_TASK_TYPE_REFRESH, @"refresh");
MAKE_SYSTEM_STR(BACKGROUND_TASK_TYPE_PROCESS, @"process");

@end

#endif
2 changes: 1 addition & 1 deletion iphone/TitaniumKit/TitaniumKit.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 52;
objectVersion = 54;
objects = {

/* Begin PBXAggregateTarget section */
Expand Down
10 changes: 9 additions & 1 deletion iphone/TitaniumKit/TitaniumKit/Sources/API/TiApp.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#import "KrollBridge.h"
#import "TiHost.h"
#import "TiRootViewController.h"
#import <BackgroundTasks/BackgroundTasks.h>
#import <JavaScriptCore/JavaScriptCore.h>

extern BOOL applicationInMemoryPanic; // TODO: Remove in SDK 9.0+
Expand Down Expand Up @@ -56,8 +57,12 @@ TI_INLINE void waitForMemoryPanicCleared() // WARNING: This must never be run on

NSString *sessionId;

UIBackgroundTaskIdentifier bgTask;
UIBackgroundTaskIdentifier bgTaskIdentifier;
NSMutableArray *backgroundServices;

NSMutableArray *backgroundTasks;
NSMutableArray *registeredBackgroundTasks;

NSMutableArray *runningServices;
NSDictionary *localNotification;
UIApplicationShortcutItem *launchedShortcutItem;
Expand Down Expand Up @@ -294,6 +299,9 @@ TI_INLINE void waitForMemoryPanicCleared() // WARNING: This must never be run on
*/
- (void)tryToPostBackgroundModeNotification:(NSMutableDictionary *)userInfo withNotificationName:(NSString *)notificationName;

- (void)addBackgroundTask:(NSDictionary *)bgTask;
- (void)backgroundTaskCompletedForIdentifier:(NSString *)identifier;

- (void)registerBackgroundService:(TiProxy *)proxy;
- (void)unregisterBackgroundService:(TiProxy *)proxy;
- (void)stopBackgroundService:(TiProxy *)proxy;
Expand Down
131 changes: 124 additions & 7 deletions iphone/TitaniumKit/TitaniumKit/Sources/API/TiApp.m
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,10 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(
// Create application support directory if not exists
[self createDefaultDirectories];

if ([TiUtils isIOSVersionOrGreater:@"13.0"]) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Remove guard, not necessary

[self registerBackgroundTasks];
}

return YES;
}

Expand Down Expand Up @@ -1083,20 +1087,26 @@ - (void)applicationDidEnterBackground:(UIApplication *)application

[[NSNotificationCenter defaultCenter] postNotificationName:kTiPausedNotification object:self];

if ([TiUtils isIOSVersionOrGreater:@"13.0"]) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Same

for (NSDictionary *backgroundTask in backgroundTasks) {
[self submitBackgroundTask:backgroundTask];
}
}

if (backgroundServices == nil) {
return;
}

UIApplication *app = [UIApplication sharedApplication];
TiApp *tiapp = self;
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
bgTaskIdentifier = [app beginBackgroundTaskWithExpirationHandler:^{
// Synchronize the cleanup call on the main thread in case
// the task actually finishes at around the same time.
TiThreadPerformOnMainThread(
^{
if (bgTask != UIBackgroundTaskInvalid) {
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
if (bgTaskIdentifier != UIBackgroundTaskInvalid) {
[app endBackgroundTask:bgTaskIdentifier];
bgTaskIdentifier = UIBackgroundTaskInvalid;
}
},
NO);
Expand Down Expand Up @@ -1202,6 +1212,8 @@ - (void)dealloc
RELEASE_TO_NIL(queuedBootEvents);
RELEASE_TO_NIL(_queuedApplicationSelectors);
RELEASE_TO_NIL(_applicationDelegates);
RELEASE_TO_NIL(backgroundTasks);
RELEASE_TO_NIL(registeredBackgroundTasks);

[super dealloc];
}
Expand Down Expand Up @@ -1237,6 +1249,111 @@ - (KrollBridge *)krollBridge

#pragma mark Background Tasks

- (void)registerBackgroundTasks
{
NSArray *identifiers = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"BGTaskSchedulerPermittedIdentifiers"];

for (NSString *identifier in identifiers) {
if (registeredBackgroundTasks == nil) {
registeredBackgroundTasks = [[NSMutableArray alloc] init];
}
[[BGTaskScheduler sharedScheduler] registerForTaskWithIdentifier:identifier
usingQueue:nil
launchHandler:^(__kindof BGTask *_Nonnull task) {
[registeredBackgroundTasks addObject:task];
[self handleBGTask:task];
}];
}
}

- (void)handleBGTask:(BGTask *)task
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
- (void)handleBGTask:(BGTask *)task
- (void)handleBackgroundTask:(BGTask *)task

{
NSString *notificationName = kTiBackgroundProcessNotification;
if ([task isKindOfClass:[BGAppRefreshTask class]]) {
// Fo refresh task submit it again
[self submitTaskForIdentifier:task.identifier];
notificationName = kTiBackgroundFetchNotification;
}
NSString *key = [NSString stringWithFormat:@"BgTask-%@", task.identifier];

[self tryToPostBackgroundModeNotification:[NSMutableDictionary dictionaryWithObjectsAndKeys:key, @"handlerId", nil]
withNotificationName:notificationName];

task.expirationHandler = ^{
if ([task isKindOfClass:[BGProcessingTask class]]) {
// Fo processing task, if it is not completed in time then only submit it again.
[self submitTaskForIdentifier:task.identifier];
}
[task setTaskCompletedWithSuccess:false];
Copy link
Collaborator

Choose a reason for hiding this comment

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

Use proper Objc types

Suggested change
[task setTaskCompletedWithSuccess:false];
[task setTaskCompletedWithSuccess:NO];

[registeredBackgroundTasks removeObject:task];
};
}

- (void)submitTaskForIdentifier:(NSString *)identifier
{
NSDictionary *backgroundTask = [self backgroundTaskForIdentifier:identifier];
if (backgroundTask) {
[self submitBackgroundTask:backgroundTask];
}
}

- (void)submitBackgroundTask:(NSDictionary *)bgTask
{
BGTaskRequest *taskRequest;
if ([bgTask[@"type"] isEqualToString:@"process"]) {
taskRequest = [[[BGProcessingTaskRequest alloc] initWithIdentifier:bgTask[@"identifier"]] autorelease];
((BGProcessingTaskRequest *)taskRequest).requiresNetworkConnectivity = [TiUtils boolValue:bgTask[@"networkConnect"] def:NO];
((BGProcessingTaskRequest *)taskRequest).requiresExternalPower = [TiUtils boolValue:bgTask[@"powerConnect"] def:NO];
} else {
taskRequest = [[[BGAppRefreshTaskRequest alloc] initWithIdentifier:bgTask[@"identifier"]] autorelease];
}
taskRequest.earliestBeginDate = bgTask[@"beginDate"];

[BGTaskScheduler.sharedScheduler submitTaskRequest:taskRequest error:nil];
}

- (void)backgroundTaskCompletedForIdentifier:(NSString *)identifier
{
for (BGTask *task in registeredBackgroundTasks) {
if ([task.identifier isEqualToString:identifier]) {
[task setTaskCompletedWithSuccess:YES];
[registeredBackgroundTasks removeObject:task];
break;
}
}
}

- (NSDictionary *_Nullable)backgroundTaskForIdentifier:(NSString *)identifier
{
NSDictionary *bgTask = nil;
for (NSDictionary *backgroundTask in backgroundTasks) {
if (backgroundTask[@"identifier"] == identifier) {
bgTask = backgroundTask;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Simply return here, so you save the extra assignment. Return nil by default after the loop

break;
}
}
return bgTask;
}

- (void)addBackgroundTask:(NSDictionary *)backgroundTask
{
if (backgroundTasks == nil) {
backgroundTasks = [[NSMutableArray alloc] init];
}
NSArray *identifiers = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"BGTaskSchedulerPermittedIdentifiers"];
NSDictionary *oldTask = [self backgroundTaskForIdentifier:backgroundTask[@"identifier"]];
if ([identifiers containsObject:backgroundTask[@"identifier"]]) {
if (oldTask) {
[backgroundTasks removeObject:oldTask];
}
[backgroundTasks addObject:backgroundTask];
} else {
DebugLog(@"The identifier, %@, is not added in tiapp.xml. Add it against key BGTaskSchedulerPermittedIdentifiers", backgroundTask[@"identifier"]);
}
}

#pragma mark Background Services

- (void)beginBackgrounding
{
if (runningServices == nil) {
Expand Down Expand Up @@ -1383,9 +1500,9 @@ - (void)checkBackgroundServices
// the expiration handler is fired at the same time.
TiThreadPerformOnMainThread(
^{
if (bgTask != UIBackgroundTaskInvalid) {
[[UIApplication sharedApplication] endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
if (bgTaskIdentifier != UIBackgroundTaskInvalid) {
[[UIApplication sharedApplication] endBackgroundTask:bgTaskIdentifier];
bgTaskIdentifier = UIBackgroundTaskInvalid;
}
},
NO);
Expand Down
1 change: 1 addition & 0 deletions iphone/TitaniumKit/TitaniumKit/Sources/API/TiBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -591,6 +591,7 @@ extern NSString *const kTiRemoteDeviceUUIDNotification;
extern NSString *const kTiGestureShakeNotification;
extern NSString *const kTiRemoteControlNotification;
extern NSString *const kTiBackgroundFetchNotification;
extern NSString *const kTiBackgroundProcessNotification;
extern NSString *const kTiSilentPushNotification;
extern NSString *const kTiBackgroundTransfer;
extern NSString *const kTiCurrentLocale;
Expand Down
1 change: 1 addition & 0 deletions iphone/TitaniumKit/TitaniumKit/Sources/API/TiBase.m
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ void TiLogMessage(NSString *str, ...)
NSString *const kTiGestureShakeNotification = @"TiGestureShake";
NSString *const kTiRemoteControlNotification = @"TiRemoteControl";
NSString *const kTiBackgroundFetchNotification = @"TiBackgroundFetch";
NSString *const kTiBackgroundProcessNotification = @"TiBackgroundProcess";
NSString *const kTiSilentPushNotification = @"TiSilentPush";
NSString *const kTiBackgroundTransfer = @"TiBackgroundTransfer";
NSString *const kTiCurrentLocale = @"kTiCurrentLocale";
Expand Down
2 changes: 2 additions & 0 deletions iphone/iphone/Titanium.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -2391,6 +2391,7 @@
DEVELOPMENT_TEAM = "";
ENABLE_BITCODE = NO;
"ENABLE_HARDENED_RUNTIME[sdk=macosx*]" = YES;
EXCLUDED_ARCHS = "";
Copy link
Collaborator

Choose a reason for hiding this comment

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

Unrelated change

GCC_DYNAMIC_NO_PIC = NO;
GCC_ENABLE_CPP_RTTI = NO;
GCC_OPTIMIZATION_LEVEL = 0;
Expand Down Expand Up @@ -2454,6 +2455,7 @@
ENABLE_BITCODE = NO;
"ENABLE_HARDENED_RUNTIME[sdk=macosx*]" = YES;
ENABLE_NS_ASSERTIONS = NO;
EXCLUDED_ARCHS = "";
Copy link
Collaborator

Choose a reason for hiding this comment

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

Unrelated change

GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = Titanium_Prefix.pch;
GCC_PREPROCESSOR_DEFINITIONS = (
Expand Down
Loading