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): support iOS 13 background task API #12411

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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
185 changes: 183 additions & 2 deletions apidoc/Titanium/App/iOS/iOS.yml
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,46 @@ methods:
local JavaScript file to execute when the application is placed in the background.
type: Dictionary

- name: registerBackgroundTask
summary: Registers a background task to run when the application is placed in the background.
description: |
Two type of tasks can be performed in background- refresh(fetch) and processing.

Refresh task can be used for updating your app with small bits of information. `UIBackgroundModes` key should be added in tiapp.xml with value `fetch`.
In this case [backgroundfetch](Titanium.App.iOS.backgroundfetch) event will be fired by system.

Processing task can be used for long data updates, processing data, and app maintenance. `UIBackgroundModes` key should be added in tiapp.xml with value `processing`.
In this case [backgroundrefresh](Titanium.App.iOS.backgroundrefresh) event will be fired by system.

`BGTaskSchedulerPermittedIdentifiers` key should also be registered with unique value for each task. See the following example.

``` xml
<ios>
<plist>
<dict>
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
<string>com.test.bgrefresh</string>
<string>com.test.bgprocessing</string>
</array>
<key>UIBackgroundModes</key>
<array>
<string>fetch</string>
<string>processing</string>
</array>
</dict>
</plist>
</ios>
```
parameters:
- name: params
summary: |
Parameters used to create the background task.
type: BackgroundTaskObject
since: "10.2.0"
osver: { ios: { min: "13.0" } }
platforms: [iphone, ipad, macos]

- name: registerUserNotificationSettings
summary: |
Registers the application to use the requested notification types and categories.
Expand Down Expand Up @@ -163,7 +203,7 @@ methods:
parameters:
- name: handlerID
summary: |
Unique string identifier for the event (`backgroundfetch`, `silentpush` or `backgroundtransfer`)
Unique string identifier for the event (`backgroundfetch`, `backgroundrefresh`, `silentpush` or `backgroundtransfer`)
that initiated the background opertation mode.
type: String
since: "3.2.0"
Expand Down Expand Up @@ -488,6 +528,30 @@ properties:
since: "9.1.0"
notes: Use <Titanium.UI.USER_INTERFACE_STYLE_DARK> instead, which is supported cross-platform.

- name: BACKGROUND_TASK_TYPE_REFRESH
summary: Background task type refresh.
description: |
Used to set the `type` attribute when registering background task using method <Titanium.App.iOS.registerBackgroundTask>.
This is used to refresh content that’s run while the app is in the background. Use this for updating your app with small bits of information,
such as the latest stock values.
type: String
permission: read-only
since: "10.2.0"
osver: { ios: { min: "13.0" } }
value: "refresh"

- name: BACKGROUND_TASK_TYPE_PROCESS
summary: Background task type process.
description: |
Used to set the `type` attribute when registering background task using method <Titanium.App.iOS.registerBackgroundTask>.
This is used for time-consuming processing task that runs while the app is in the background. Use processing tasks for long data updates,
processing data, and app maintenance.
type: String
permission: read-only
since: "10.2.0"
osver: { ios: { min: "13.0" } }
value: "process"

- name: UTTYPE_TEXT
summary: |
Uniform type identifier for all text types.
Expand Down Expand Up @@ -974,14 +1038,47 @@ events:
The event returns the dictionary containing the `handlerID` property, which is a unique handler ID for the
current event. This identifier should be passed as the argument to the
[endBackgroundHandler](Titanium.App.iOS.endBackgroundHandler) method.

In iOS 13+, it is recommended to register refresh task using <Titanium.App.iOS.registerBackgroundTask>.
If you have added `BGTaskSchedulerPermittedIdentifiers` in `tiapp.xml`, it is mandatory to register refresh task
using method <Titanium.App.iOS.registerBackgroundTask>. Otherwise this event will not fire.
On macOS, this event will only be fired when refresh task is registered using <Titanium.App.iOS.registerBackgroundTask>.
properties:
- name: handlerId
summary: |
Unique string identifier for the `backgroundfetch` event. This identifier should be passed as the argument
to the [endBackgroundHandler](Titanium.App.iOS.endBackgroundHandler) method.
type: String
platforms: [iphone, ipad, macos]
since: {iphone: "3.2.0", ipad: "3.2.0", macos: "9.2.0"}
osver: {ios: {min: "7.0"}}
since: {iphone: "3.2.0", ipad: "3.2.0", macos: "10.2.0"}

- name: backgroundprocess
summary: Fired when the application is woken up for a processing operation. Available on macOS and iOS 13+ .
description: |
Add this event if your app has registered a process task using <Titanium.App.iOS.registerBackgroundTask> and supports the `processing` UIBackground mode.
Use this event for long data updates, processing data, and app maintenance. The system will launch your app and fire this event when conditions are favorable for battery life to handle deferrable,
longer-running processing, such as syncing, database maintenance, or similar tasks. The system will attempt to fulfill this request to the best of its
ability within the next two days as long as the user has used your app within the past week.

Use callback of this event for long data updates, processing data, and app maintenance. Call the
[endBackgroundHandler](Titanium.App.iOS.endBackgroundHandler) method with the `handlerID` parameter from this event.

When this event is fired, your app has minutes to perform the processing and call the [endBackgroundHandler](Titanium.App.iOS.endBackgroundHandler) method.
Your app should call the [endBackgroundHandler](Titanium.App.iOS.endBackgroundHandler) method as soon as your task is completed.

The event returns the dictionary containing the `handlerID` property, which is a unique handler ID for the
current event. This identifier should be passed as the argument to the
[endBackgroundHandler](Titanium.App.iOS.endBackgroundHandler) method.
properties:
- name: handlerId
summary: |
Unique string identifier for the `backgroundprocess` event. This identifier should be passed as the argument
to the [endBackgroundHandler](Titanium.App.iOS.endBackgroundHandler) method.
type: String
platforms: [iphone, ipad, macos]
osver: { ios: { min: "13.0" } }
since: "10.2.0"

- name: silentpush
summary: Fired when the application is woken up by a silent remote notification. Available only on iOS 7 and later.
Expand Down Expand Up @@ -1566,3 +1663,87 @@ properties:
- name: categories
summary: Set of categories of user notification actions required by the applicaiton to use.
type: Array<Titanium.App.iOS.UserNotificationCategory>

---
name: BackgroundTaskObject
summary: |
Dictionary object of parameters used to register the background task using
the <Titanium.App.iOS.registerBackgroundTask> method.
since: "10.2.0"
platforms: [iphone, ipad, macos]

properties:
- name: type
summary: Background task type.
type: String
constants: [Titanium.App.iOS.BACKGROUND_TASK_TYPE_REFRESH, Titanium.App.iOS.BACKGROUND_TASK_TYPE_PROCESS]

- name: identifier
vijaysingh-axway marked this conversation as resolved.
Show resolved Hide resolved
summary: |
Unique value to identify the task. It should be one of the same value which is added in tiapp.xml for key `BGTaskSchedulerPermittedIdentifiers`.
type: String

- name: networkConnect
summary: |
Whether the background task requires network connectivity. This is valid with background task type <Titanium.App.iOS.BACKGROUND_TASK_TYPE_PROCESS>.
If this property is set to `true`, the system will only launch your app to fulfill this request when the device has a network connection.
type: Boolean
default: false

- name: powerConnect
summary: |
Whether the background task rrequires external power. This is valid with background task type <Titanium.App.iOS.BACKGROUND_TASK_TYPE_PROCESS>.
If this property is set to `true`, the system will launch your app to fulfill this request only while the device is connected to external power.
Specify `true` if this task is resource intensive to minimize impact to battery life.
type: Boolean
default: false

- name: beginDate
summary: |
The earliest date at which the task may run. Setting this property does not guarantee that the task will begin at the specified date,
but only that it will not begin sooner. If not specified, no start delay is used.
type: Date
optional: true

examples:
- title: Background task example
example: |

``` js
const win = Ti.UI.createWindow({
backgroundColor: '#fff'
});

const label = Ti.UI.createButton({
text: 'Background task'
});

win.add(label);
win.open();

Ti.App.iOS.registerBackgroundTask({
'identifier': 'com.test.bgrefresh',
'type': Ti.App.iOS.BACKGROUND_TASK_TYPE_REFRESH
});

Ti.App.iOS.registerBackgroundTask({
'identifier': 'com.test.bgprocessing',
'type': Ti.App.iOS.BACKGROUND_TASK_TYPE_PROCESS
});

Ti.App.iOS.addEventListener('backgroundfetch', function(e) {
Ti.API.info('backgroundfetch: ' + JSON.stringify(e));

label.text = 'backgroundfetch';

Ti.App.iOS.endBackgroundHandler(e.handlerId);
});

Ti.App.iOS.addEventListener('backgroundprocess', function(e) {
Ti.API.info('backgroundprocess: ' + JSON.stringify(e));

label.text = 'backgroundprocess';

Ti.App.iOS.endBackgroundHandler(e.handlerId);
});
```
36 changes: 36 additions & 0 deletions iphone/Classes/TiAppiOSProxy.m
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,14 @@ - (void)_listenerAdded:(NSString *)type count:(int)count
DebugLog(@"[ERROR] Cannot add backgroundfetch eventListener. Please add `fetch` to UIBackgroundModes inside info.plist ");
}
}
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"]) {
NSArray *backgroundModes = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UIBackgroundModes"];
if ([backgroundModes containsObject:@"remote-notification"]) {
Expand Down Expand Up @@ -160,6 +168,9 @@ - (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];
}
Expand Down Expand Up @@ -406,6 +417,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"]) {
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 @@ -883,6 +908,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 @@ -969,6 +999,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 @@ -1332,6 +1365,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
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