Skip to content

Commit

Permalink
fix(ios): fix duplicate application delegate allocation
Browse files Browse the repository at this point in the history
  • Loading branch information
hansemannn committed May 4, 2024
1 parent a0a3aea commit 3513427
Show file tree
Hide file tree
Showing 9 changed files with 358 additions and 222 deletions.
12 changes: 12 additions & 0 deletions iphone/Classes/TiApp+Scenes.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//
// TiApp+Scenes.h
// Titanium
//
// Created by Hans Knöchel on 04.05.24.
//

#import <TitaniumKit/TiApp.h>

@interface TiApp (Scenes)

@end
223 changes: 223 additions & 0 deletions iphone/Classes/TiApp+Scenes.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
//
// TiApp+Scenes.m
// Titanium
//
// Created by Hans Knöchel on 04.05.24.
//

#import "TiApp+Scenes.h"

#import <CoreSpotlight/CoreSpotlight.h>

#import <TitaniumKit/ImageLoader.h>
#import <TitaniumKit/TiExceptionHandler.h>
#import <TitaniumKit/TiLogServer.h>

@implementation TiApp (Scenes)

- (void)sceneWillResignActive:(UIScene *)scene
{
[self tryToInvokeSelector:@selector(sceneWillResignActive:)
withArguments:[NSOrderedSet orderedSetWithObject:scene]];

if ([self forceSplashAsSnapshot]) {
[window addSubview:[self splashScreenView]];
}
[[NSNotificationCenter defaultCenter] postNotificationName:kTiSuspendNotification object:self];

// suspend any image loading
[[ImageLoader sharedLoader] suspend];
[kjsBridge gc];
}

- (void)sceneDidBecomeActive:(UIScene *)scene
{
[self tryToInvokeSelector:@selector(sceneDidBecomeActive:)
withArguments:[NSOrderedSet orderedSetWithObject:scene]];

if ([self forceSplashAsSnapshot] && splashScreenView != nil) {
[[self splashScreenView] removeFromSuperview];
RELEASE_TO_NIL(splashScreenView);
}

// NOTE: Have to fire a separate but non-'resume' event here because there is SOME information
// (like new URL) that is not passed through as part of the normal foregrounding process.
[[NSNotificationCenter defaultCenter] postNotificationName:kTiResumedNotification object:self];

// resume any image loading
[[ImageLoader sharedLoader] resume];
}

- (void)sceneDidEnterBackground:(UIScene *)scene
{
[self tryToInvokeSelector:@selector(sceneDidEnterBackground:)
withArguments:[NSOrderedSet orderedSetWithObject:scene]];

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

if (backgroundServices == nil) {
return;
}

UIApplication *app = [UIApplication sharedApplication];
TiApp *tiapp = self;
bgTask = [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;
}
},
NO);
}];
// Start the long-running task and return immediately.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Do the work associated with the task.
[tiapp beginBackgrounding];
});
}

- (void)sceneWillEnterForeground:(UIScene *)scene
{
[self tryToInvokeSelector:@selector(sceneWillEnterForeground:)
withArguments:[NSOrderedSet orderedSetWithObject:scene]];

[self flushCompletionHandlerQueue];
[sessionId release];
sessionId = [[TiUtils createUUID] retain];

//TIMOB-3432. Ensure url is cleared when resume event is fired.
[launchOptions removeObjectForKey:@"url"];
[launchOptions removeObjectForKey:@"source"];

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

if (backgroundServices == nil) {
return;
}

[self endBackgrounding];
}

- (void)scene:(UIScene *)scene openURLContexts:(NSSet<UIOpenURLContext *> *)URLContexts
{
UIOpenURLContext *primaryContext = URLContexts.allObjects.firstObject;

NSDictionary<UIApplicationOpenURLOptionsKey, id> *options = @{
UIApplicationOpenURLOptionsSourceApplicationKey : NULL_IF_NIL(primaryContext.options.sourceApplication)
};

[self application:[UIApplication sharedApplication] openURL:primaryContext.URL options:options];
}

- (UISceneConfiguration *)application:(UIApplication *)application configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession options:(UISceneConnectionOptions *)options
{
return [[UISceneConfiguration alloc] initWithName:@"Default Configuration" sessionRole:connectingSceneSession.role];
}

- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions
{
// Initialize the root-window
window = [[UIWindow alloc] initWithWindowScene:(UIWindowScene *)scene];

// Initialize the launch options to be used by the client
launchOptions = [[NSMutableDictionary alloc] init];

// Retain connectionOptions for later use
if (self.connectionOptions != connectionOptions) {
[self.connectionOptions release]; // Release any existing object
self.connectionOptions = [connectionOptions retain]; // Retain the new object
}

// If we have a APNS-UUID, assign it
NSString *apnsUUID = [[NSUserDefaults standardUserDefaults] stringForKey:@"APNSRemoteDeviceUUID"];
if (apnsUUID != nil) {
remoteDeviceUUID = [apnsUUID copy];
}

[[UNUserNotificationCenter currentNotificationCenter] setDelegate:self];

// Get some launch options to validate before finish launching. Some of them
// need to be mapepd from native to JS-types to be used by the client
NSURL *urlOptions = connectionOptions.URLContexts.allObjects.firstObject.URL;
NSString *sourceBundleId = connectionOptions.sourceApplication;
UNNotificationResponse *notification = connectionOptions.notificationResponse;
UIApplicationShortcutItem *shortcut = connectionOptions.shortcutItem;

// Map user activity if exists
NSUserActivity *userActivity = connectionOptions.userActivities.allObjects.firstObject;
if (userActivity != nil) {
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithDictionary:@{ @"activityType" : [userActivity activityType] }];

if ([TiUtils isIOSVersionOrGreater:@"9.0"] && [[userActivity activityType] isEqualToString:CSSearchableItemActionType]) {
if ([userActivity userInfo] != nil) {
[dict setObject:[[userActivity userInfo] objectForKey:CSSearchableItemActivityIdentifier] forKey:@"searchableItemActivityIdentifier"];
}
}

if ([userActivity title] != nil) {
[dict setObject:[userActivity title] forKey:@"title"];
}

if ([userActivity webpageURL] != nil) {
[dict setObject:[[userActivity webpageURL] absoluteString] forKey:@"webpageURL"];
}

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

// Update launchOptions so that we send only expected values rather than NSUserActivity
[launchOptions setObject:@{ @"UIApplicationLaunchOptionsUserActivityKey" : dict }
forKey:UIApplicationLaunchOptionsUserActivityDictionaryKey];
}

// Map launched URL
if (urlOptions != nil) {
[launchOptions setObject:[urlOptions absoluteString] forKey:@"url"];
}

// Map launched App-ID
if (sourceBundleId != nil) {
[launchOptions setObject:sourceBundleId forKey:@"source"];
}

// Generate remote notification if available
if (notification != nil && [notification.notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
[self generateNotification:@{ @"aps" : notification.notification.request.content.userInfo }];
}

// Save shortcut item for later
if (shortcut != nil) {
launchedShortcutItem = [shortcut retain];
}

// Queue selector for usage in modules / Hyperloop
[self tryToInvokeSelector:@selector(scene:willConnectToSession:options:)
withArguments:[NSOrderedSet orderedSetWithObjects:scene, connectionOptions, nil]];

// Catch exceptions
[TiExceptionHandler defaultExceptionHandler];

// Enable device logs (e.g. for physical devices)
if ([[TiSharedConfig defaultConfig] logServerEnabled]) {
[[TiLogServer defaultLogServer] start];
}

// Initialize the root-controller
[self initController];

// If a "application-launch-url" is set, launch it directly
[self launchToUrl];

// Boot our kroll-core
[self boot];

// Create application support directory if not exists
[self createDefaultDirectories];
}

@end
10 changes: 9 additions & 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 All @@ -21,6 +21,8 @@
/* End PBXAggregateTarget section */

/* Begin PBXBuildFile section */
3A39A6B12BE6AFFB0019C843 /* TiSceneDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A39A6B02BE6AFFB0019C843 /* TiSceneDelegate.m */; };
3A39A6B22BE6AFFB0019C843 /* TiSceneDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A39A6AF2BE6AFFB0019C843 /* TiSceneDelegate.h */; };
4A175276218B64E70094C7B6 /* KrollTimerManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A175274218B64E70094C7B6 /* KrollTimerManager.h */; };
4A175277218B64E70094C7B6 /* KrollTimerManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A175275218B64E70094C7B6 /* KrollTimerManager.m */; };
4A1FF4432523262A00A0F923 /* ErrorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A1FF4422523262A00A0F923 /* ErrorView.swift */; };
Expand Down Expand Up @@ -191,6 +193,8 @@
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
3A39A6AF2BE6AFFB0019C843 /* TiSceneDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TiSceneDelegate.h; sourceTree = "<group>"; };
3A39A6B02BE6AFFB0019C843 /* TiSceneDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TiSceneDelegate.m; sourceTree = "<group>"; };
4A175274218B64E70094C7B6 /* KrollTimerManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KrollTimerManager.h; sourceTree = "<group>"; };
4A175275218B64E70094C7B6 /* KrollTimerManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KrollTimerManager.m; sourceTree = "<group>"; };
4A1FF4422523262A00A0F923 /* ErrorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -504,6 +508,8 @@
DB15FD051F0A892500A82C45 /* Ti3DMatrix.m */,
DB15FC921F0A860600A82C45 /* TiAnimation.h */,
DB15FC931F0A860600A82C45 /* TiAnimation.m */,
3A39A6AF2BE6AFFB0019C843 /* TiSceneDelegate.h */,
3A39A6B02BE6AFFB0019C843 /* TiSceneDelegate.m */,
DB15FC4B1F0A848200A82C45 /* TiApp.h */,
DB15FC4C1F0A848200A82C45 /* TiApp.m */,
DB258D231F097680000D0D8D /* TiBase.h */,
Expand Down Expand Up @@ -723,6 +729,7 @@
DB15FD201F0A8A5E00A82C45 /* TiComplexValue.h in Headers */,
DB5813E72075FCF200CA1437 /* TiDefines.h in Headers */,
DB24E0502076824C0033B2B1 /* TiSharedConfig.h in Headers */,
3A39A6B22BE6AFFB0019C843 /* TiSceneDelegate.h in Headers */,
DB15FC3D1F0A833100A82C45 /* KrollObject.h in Headers */,
DB15FCA81F0A866300A82C45 /* TiRect.h in Headers */,
DB15FC891F0A859800A82C45 /* TiExceptionHandler.h in Headers */,
Expand Down Expand Up @@ -834,6 +841,7 @@
files = (
DB15FD091F0A892500A82C45 /* Ti3DMatrix.m in Sources */,
DB15FCB91F0A874F00A82C45 /* TopTiModule.m in Sources */,
3A39A6B12BE6AFFB0019C843 /* TiSceneDelegate.m in Sources */,
DB15FC4E1F0A848200A82C45 /* TiApp.m in Sources */,
DB15FCB11F0A86E600A82C45 /* TiBindingTiValue.m in Sources */,
DB15FC461F0A838E00A82C45 /* TiColor.m in Sources */,
Expand Down
42 changes: 34 additions & 8 deletions iphone/TitaniumKit/TitaniumKit/Sources/API/TiApp.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
/**
TiApp represents an instance of an application. There is always only one instance per application which could be accessed through <app> class method.
*/
@interface TiApp : TiHost <UIApplicationDelegate, NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDownloadDelegate, UNUserNotificationCenterDelegate, UIWindowSceneDelegate> {
@interface TiApp : TiHost <UIApplicationDelegate, NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDownloadDelegate, UNUserNotificationCenterDelegate> {
UIWindow *window;
UIImageView *loadView;
UIView *splashScreenView;
Expand Down Expand Up @@ -113,13 +113,6 @@
*/
@property (nonatomic, readonly) NSDictionary *localNotification;

/**
Returns details for the last remote notification.
Dictionary containing details about remote notification, or _nil_.
*/
@property (nonatomic, readonly) UISceneConnectionOptions *connectionOptions;

/**
Returns the application's root view controller.
*/
Expand Down Expand Up @@ -276,6 +269,23 @@
*/
- (void)tryToInvokeSelector:(SEL)selector withArguments:(NSOrderedSet<id> *)arguments;

/**
Returns details for the last remote notification.
Dictionary containing details about remote notification, or _nil_.
*/
@property (nonatomic, strong) UISceneConnectionOptions *connectionOptions;

/**
The splash screen view used when the app is booted.
*/
- (UIView *)splashScreenView;

/**
Clear out the pending completion handlers.
*/
- (void)flushCompletionHandlerQueue;

/**
Tries to post a given notification with the given name. If the app did not finish launching so far, it will be queued
and processed once the JSCore bridge is ready.
Expand All @@ -302,4 +312,20 @@
- (void)performCompletionHandlerForBackgroundTransferWithKey:(NSString *)key;
- (void)watchKitExtensionRequestHandler:(id)key withUserInfo:(NSDictionary *)userInfo;

#pragma mark UIWindowSceneDelegate

- (void)sceneWillResignActive:(UIScene *)scene;

- (void)sceneDidBecomeActive:(UIScene *)scene;

- (void)sceneDidEnterBackground:(UIScene *)scene;

- (void)sceneWillEnterForeground:(UIScene *)scene;

- (void)scene:(UIScene *)scene openURLContexts:(NSSet<UIOpenURLContext *> *)URLContexts;

- (UISceneConfiguration *)application:(UIApplication *)application configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession options:(UISceneConnectionOptions *)options;

- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions;

@end

0 comments on commit 3513427

Please sign in to comment.