Skip to content

Commit

Permalink
Merge pull request #9295 from hansemannn/TIMOB-20272-6_2_X
Browse files Browse the repository at this point in the history
[TIMOB-20272] (6_2_X) Attempt to fix notifications in cold app-start
  • Loading branch information
mukherjee2 committed Aug 10, 2017
2 parents 21ccbb7 + 3d14eeb commit 5330ce6
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 84 deletions.
23 changes: 12 additions & 11 deletions iphone/Classes/TiApp.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,34 +37,35 @@ TI_INLINE void waitForMemoryPanicCleared() //WARNING: This must never be run o

TiContextGroupRef contextGroup;
KrollBridge *kjsBridge;

#ifdef USE_TI_UIWEBVIEW
XHRBridge *xhrBridge;
#endif

NSMutableDictionary *launchOptions;
NSTimeInterval started;

int32_t networkActivityCount;

TiRootViewController *controller;
NSString *userAgent;
NSString *remoteDeviceUUID;

id remoteNotificationDelegate;
NSDictionary* remoteNotification;
NSMutableDictionary* pendingCompletionHandlers;
NSMutableDictionary* pendingReplyHandlers;
NSMutableDictionary* backgroundTransferCompletionHandlers;
BOOL appBooted;

NSDictionary *remoteNotification;
NSMutableDictionary *pendingCompletionHandlers;
NSMutableDictionary *pendingReplyHandlers;
NSMutableDictionary *backgroundTransferCompletionHandlers;
NSMutableDictionary *queuedBootEvents;
BOOL appBooted;

NSString *sessionId;

UIBackgroundTaskIdentifier bgTask;
NSMutableArray *backgroundServices;
NSMutableArray *runningServices;
NSDictionary *localNotification;
UIApplicationShortcutItem *launchedShortcutItem;
UIApplicationShortcutItem *launchedShortcutItem;
}

@property (nonatomic) BOOL forceSplashAsSnapshot;
Expand Down
121 changes: 62 additions & 59 deletions iphone/Classes/TiApp.m
Original file line number Diff line number Diff line change
Expand Up @@ -100,12 +100,19 @@ -(TiContextGroupRef)contextGroup
return contextGroup;
}


+(TiContextGroupRef)contextGroup
{
return [sharedApp contextGroup];
}

- (NSMutableDictionary *)queuedBootEvents
{
if (queuedBootEvents == nil) {
queuedBootEvents = [[[NSMutableDictionary alloc] init] retain];
}

return queuedBootEvents;
}

-(void)startNetwork
{
Expand Down Expand Up @@ -296,13 +303,17 @@ - (void)booted:(id)bridge
appBooted = YES;

if(launchedShortcutItem != nil) {
[self handleShortcutItem:launchedShortcutItem waitForBootIfNotLaunched:YES];
[self handleShortcutItem:launchedShortcutItem queueToBootIfNotLaunched:YES];
RELEASE_TO_NIL(launchedShortcutItem);
}

if (localNotification != nil) {
[[NSNotificationCenter defaultCenter] postNotificationName:kTiLocalNotification object:localNotification userInfo:nil];
if (queuedBootEvents != nil) {
for (NSString *notificationName in queuedBootEvents) {
[[NSNotificationCenter defaultCenter] postNotificationName:notificationName object:self userInfo:[queuedBootEvents objectForKey:notificationName]];
}
RELEASE_TO_NIL(queuedBootEvents);
}

TiThreadPerformOnMainThread(^{[self validator];}, YES);
}
}
Expand Down Expand Up @@ -495,11 +506,9 @@ -(void)application:(UIApplication*)application performFetchWithCompletionHandler

[pendingCompletionHandlers setObject:[[completionHandler copy] autorelease ]forKey:key];

// Handling the case, where the app is not running and backgroundfetch launches the app into background. In this case, the delegate gets called
// the bridge completes processing of app.js (adding the event into notification center).

[self postNotificationwithKey:[NSMutableDictionary dictionaryWithObjectsAndKeys:key, @"handlerId", nil] withNotificationName:kTiBackgroundFetchNotification] ;

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

// We will go ahead and keeper a timer just in case the user returns the value too late - this is the worst case scenario.
NSTimer* flushTimer = [NSTimer timerWithTimeInterval:TI_BACKGROUNDFETCH_MAX_INTERVAL target:self selector:@selector(fireCompletionHandler:) userInfo:key repeats:NO] ;
[[NSRunLoop mainRunLoop] addTimer:flushTimer forMode:NSDefaultRunLoopMode];
Expand All @@ -512,7 +521,7 @@ -(void)application:(UIApplication*)application performFetchWithCompletionHandler

- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings
{
[[NSNotificationCenter defaultCenter] postNotificationName:kTiUserNotificationSettingsNotification object:notificationSettings userInfo:nil];
[[NSNotificationCenter defaultCenter] postNotificationName:kTiUserNotificationSettingsNotification object:self userInfo:notificationSettings];
}

- (void) application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forLocalNotification:(UILocalNotification *)notification withResponseInfo:(NSDictionary *)responseInfo completionHandler:(void (^)())completionHandler {
Expand All @@ -523,8 +532,7 @@ - (void) application:(UIApplication *)application handleActionWithIdentifier:(NS
[localNotification setValue:responseInfo[UIUserNotificationActionResponseTypedTextKey] forKey:@"typedText"];
}

[[NSNotificationCenter defaultCenter] postNotificationName:kTiLocalNotificationAction object:localNotification userInfo:nil];
completionHandler();
[self tryToPostNotification:localNotification withNotificationName:kTiLocalNotificationAction completionHandler:completionHandler];
}

// iOS 9+
Expand Down Expand Up @@ -589,8 +597,27 @@ -(void)watchKitExtensionRequestHandler:(id)key withUserInfo:(NSDictionary*)userI

#pragma mark Helper Methods

-(void)postNotificationwithKey:(NSMutableDictionary*)userInfo withNotificationName:(NSString*)notificationName{
- (void)tryToPostNotification:(NSDictionary *)_notification withNotificationName:(NSString *)_notificationName completionHandler:(void (^)())completionHandler
{
typedef void (^NotificationBlock)();

NotificationBlock myNotificationBlock = ^void() {
[[NSNotificationCenter defaultCenter] postNotificationName:_notificationName object:self userInfo:_notification];

if (completionHandler != nil) {
completionHandler();
}
};

if (appBooted) {
myNotificationBlock();
} else {
[[self queuedBootEvents] setObject:_notification forKey:_notificationName];
}
}

-(void)tryToPostBackgroundModeNotification:(NSMutableDictionary*)userInfo withNotificationName:(NSString*)notificationName
{
//Check to see if the app booted and we still have the completionhandler in the system
NSString* key = [userInfo objectForKey:@"handlerId"] ;
BOOL shouldContinue = NO;
Expand All @@ -607,17 +634,13 @@ -(void)postNotificationwithKey:(NSMutableDictionary*)userInfo withNotificationNa
if (appBooted ) {
[[NSNotificationCenter defaultCenter] postNotificationName:notificationName object:self userInfo:userInfo];
} else {
//Try again in 2 sec. TODO: should we reduce this value ?
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self postNotificationwithKey:userInfo withNotificationName:notificationName];
});
[[self queuedBootEvents] setObject:userInfo forKey:notificationName];
}
}

//Clear out the pendingCompletionHandlerQueue
-(void)flushCompletionHandlerQueue
{
//FunctionName();
if (pendingCompletionHandlers !=nil) {
for (id key in pendingCompletionHandlers) {
[self completionHandler:key withResult:2]; //UIBackgroundFetchResultFailed
Expand All @@ -629,18 +652,16 @@ -(void)flushCompletionHandlerQueue
// This method gets called when the wall clock runs out and the completionhandler is still there.
-(void)fireCompletionHandler:(NSTimer*)timer
{
//FunctionName();
id key = timer.userInfo;
if ([pendingCompletionHandlers objectForKey:key]) {
[self completionHandler:key withResult:UIBackgroundFetchResultFailed];
//Send a event signalling the that backgroundfetch ended.
}
}

// gets called when user ends finishes with backgrounding stuff. By default this would always be called with UIBackgroundFetchResultNoData.
// Gets called when user ends finishes with backgrounding stuff. By default this would always be called with UIBackgroundFetchResultNoData.
-(void)completionHandler:(id)key withResult:(int)result
{
//FunctionName();
if ([pendingCompletionHandlers objectForKey:key]) {
void (^completionHandler)(UIBackgroundFetchResult);
completionHandler = [pendingCompletionHandlers objectForKey:key];
Expand All @@ -651,7 +672,7 @@ -(void)completionHandler:(id)key withResult:(int)result
}
}

//Called to mark the end of background transfer while in the background.
// Called to mark the end of background transfer while in the background.
-(void)completionHandlerForBackgroundTransfer:(id)key
{
if ([backgroundTransferCompletionHandlers objectForKey:key] != nil) {
Expand All @@ -668,16 +689,15 @@ -(void)completionHandlerForBackgroundTransfer:(id)key
#pragma mark Remote Notifications iOS 7

#ifdef USE_TI_SILENTPUSH
//Delegate callback for Silent Remote Notification.
// Delegate callback for Silent Remote Notification.
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler
{
//FunctionName();
//Forward the callback
// Forward the callback
if ([self respondsToSelector:@selector(application:didReceiveRemoteNotification:)]) {
[self application:application didReceiveRemoteNotification:userInfo];
}

//This only here for Simulator builds.
// This only here for Simulator builds.

NSArray* backgroundModes = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UIBackgroundModes"];
if ([backgroundModes containsObject:@"remote-notification"]) {
Expand All @@ -692,12 +712,11 @@ - (void)application:(UIApplication *)application didReceiveRemoteNotification:(N

[pendingCompletionHandlers setObject:[[completionHandler copy] autorelease ]forKey:key];

// Handling the case, where the app is not running and backgroundfetch launches the app into background. In this case, the delegate gets called
// the bridge completes processing of app.js (adding the event into notification center).

NSMutableDictionary* dict = [NSMutableDictionary dictionaryWithObjectsAndKeys:key, @"handlerId", nil];
[dict addEntriesFromDictionary:userInfo];
[self postNotificationwithKey:dict withNotificationName:kTiSilentPushNotification];

[self tryToPostBackgroundModeNotification:dict
withNotificationName:kTiSilentPushNotification];

// We will go ahead and keeper a timer just in case the user returns the value too late - this is the worst case scenario.
NSTimer* flushTimer = [NSTimer timerWithTimeInterval:TI_BACKGROUNDFETCH_MAX_INTERVAL target:self selector:@selector(fireCompletionHandler:) userInfo:key repeats:NO] ;
Expand All @@ -713,7 +732,6 @@ - (void)application:(UIApplication *)application didReceiveRemoteNotification:(N
//Delegate callback for Background Transfer completes.
- (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler
{
//FunctionName();
// Generate unique key with timestamp.
id key = [NSString stringWithFormat:@"Session-%f",[[NSDate date] timeIntervalSince1970]];

Expand All @@ -725,7 +743,7 @@ - (void)application:(UIApplication *)application handleEventsForBackgroundURLSes
[backgroundTransferCompletionHandlers setObject:[[completionHandler copy] autorelease ]forKey:key];
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithObjectsAndKeys:identifier, @"sessionId",
key, @"handlerId", nil];
[self postNotificationwithKey:dict withNotificationName:kTiBackgroundTransfer];
[self tryToPostBackgroundModeNotification:dict withNotificationName:kTiBackgroundTransfer];

}

Expand All @@ -735,8 +753,7 @@ - (void)application:(UIApplication *)application handleEventsForBackgroundURLSes

-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
{
//FunctionName();
//copy downloaded file from location to tempFile (in NSTemporaryDirectory), because file in location will be removed after delegate completes
// Copy downloaded file from location to tempFile (in NSTemporaryDirectory), because file in location will be removed after delegate completes
NSError *error;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *destinationFilename = location.lastPathComponent;
Expand Down Expand Up @@ -798,8 +815,6 @@ - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSend

-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
//FunctionName();

NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithUnsignedInteger:task.taskIdentifier], @"taskIdentifier",
nil];
Expand Down Expand Up @@ -974,7 +989,6 @@ - (void)applicationDidBecomeActive:(UIApplication *)application

-(void)applicationDidEnterBackground:(UIApplication *)application
{
//FunctionName();
[[NSNotificationCenter defaultCenter] postNotificationName:kTiPausedNotification object:self];

if (backgroundServices==nil)
Expand Down Expand Up @@ -1008,7 +1022,6 @@ -(void)applicationDidEnterBackground:(UIApplication *)application

-(void)applicationWillEnterForeground:(UIApplication *)application
{
//FunctionName(); Uncomment to see function name printed when it is called.
[self flushCompletionHandlerQueue];
[sessionId release];
sessionId = [[TiUtils createUUID] retain];
Expand Down Expand Up @@ -1063,9 +1076,7 @@ - (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserAct
if (appBooted) {
[[NSNotificationCenter defaultCenter] postNotificationName:kTiContinueActivity object:self userInfo:dict];
} else {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:kTiContinueActivity object:self userInfo:dict];
});
[[self queuedBootEvents] setObject:dict forKey:kTiContinueActivity];
}

return YES;
Expand Down Expand Up @@ -1244,14 +1255,13 @@ - (void)application:(UIApplication *)application didReceiveLocalNotification:(UI
{
RELEASE_TO_NIL(localNotification);
localNotification = [[[self class] dictionaryWithLocalNotification:notification] retain];
[[NSNotificationCenter defaultCenter] postNotificationName:kTiLocalNotification object:localNotification userInfo:nil];

[self tryToPostNotification:localNotification withNotificationName:kTiLocalNotification completionHandler:nil];
}

-(BOOL)handleShortcutItem:(UIApplicationShortcutItem*) shortcutItem
waitForBootIfNotLaunched:(BOOL) bootWait {


if(shortcutItem.type == nil) {
-(BOOL)handleShortcutItem:(UIApplicationShortcutItem *) shortcutItem queueToBootIfNotLaunched:(BOOL)bootWait
{
if (shortcutItem.type == nil) {
NSLog(@"[ERROR] The shortcut type property is required");
return NO;
}
Expand All @@ -1268,7 +1278,7 @@ -(BOOL)handleShortcutItem:(UIApplicationShortcutItem*) shortcutItem
[dict setObject:shortcutItem.localizedSubtitle forKey:@"subtitle" ];
}

if(shortcutItem.userInfo !=nil) {
if (shortcutItem.userInfo !=nil) {
[dict setObject:shortcutItem.userInfo forKey:@"userInfo"];
}

Expand All @@ -1278,13 +1288,8 @@ -(BOOL)handleShortcutItem:(UIApplicationShortcutItem*) shortcutItem
if (appBooted) {
[[NSNotificationCenter defaultCenter] postNotificationName:kTiApplicationShortcut
object:self userInfo:dict];
} else {
if(bootWait) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:kTiApplicationShortcut
object:self userInfo:dict];
});
}
} else if (bootWait) {
[[self queuedBootEvents] setObject:dict forKey:kTiApplicationShortcut];
}

return YES;
Expand Down Expand Up @@ -1313,17 +1318,15 @@ - (void)handleRemoteNotificationWithIdentifier:(NSString *)identifier
event[@"category"] = category;
}

[[NSNotificationCenter defaultCenter] postNotificationName:kTiRemoteNotificationAction object:event userInfo:nil];
[event autorelease];
completionHandler();
[self tryToPostNotification:[event autorelease] withNotificationName:kTiRemoteNotificationAction completionHandler:completionHandler];
}

- (void)application:(UIApplication *)application
performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem
completionHandler:(void (^)(BOOL succeeded))completionHandler
{

BOOL handledShortCutItem = [self handleShortcutItem:shortcutItem waitForBootIfNotLaunched:NO];
BOOL handledShortCutItem = [self handleShortcutItem:shortcutItem queueToBootIfNotLaunched:NO];
completionHandler(handledShortCutItem);

}
Expand Down

0 comments on commit 5330ce6

Please sign in to comment.