Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
# Unreleased
- [fixed] Deliver App Delegate callbacks to delegates that handle them by
forwarding rather than by implementing them, such as the one SwiftUI installs
for `UIApplicationDelegateAdaptor`. (#247)

# 8.1.4
- [fixed] `GULMutableDictionary` now ignores a `nil` key (and a `nil` object in
`setObject:forKey:`) instead of throwing on its internal queue, which crashed
Expand Down
116 changes: 116 additions & 0 deletions GoogleUtilities/AppDelegateSwizzler/GULAppDelegateSwizzler.m
Original file line number Diff line number Diff line change
Expand Up @@ -547,6 +547,48 @@ + (nullable NSValue *)originalImplementationForSelector:(SEL)selector object:(id
return realImplementationBySelector[NSStringFromSelector(selector)];
}

// The App Delegate SwiftUI installs for UIApplicationDelegateAdaptor implements a handful of
// selectors itself and forwards the rest to the app's own delegate. Such a delegate has no
// implementation for the runtime to find, so the donor methods have nothing to call.
+ (nullable id<GULApplicationDelegate>)forwardingTargetForProxiedSelector:(SEL)selector
object:(id)object {
if (![object respondsToSelector:@selector(forwardingTargetForSelector:)]) {
return nil;
}
id target = [object forwardingTargetForSelector:selector];
if (target == nil || target == object || ![target respondsToSelector:selector]) {
return nil;
}
return target;
}

+ (nullable NSInvocation *)forwardSelector:(SEL)selector
fromObject:(id)object
argumentSetter:(void (^)(NSInvocation *invocation))argumentSetter {
id target = [self forwardingTargetForProxiedSelector:selector object:object];
if (target == nil) {
return nil;
}
NSInvocation *invocation = [self appDelegateInvocationForSelector:selector];
if (invocation == nil) {
return nil;
}
[invocation setTarget:target];
[invocation setSelector:selector];
argumentSetter(invocation);
[invocation invoke];
return invocation;
}

+ (BOOL)boolReturnValueOfInvocation:(nullable NSInvocation *)invocation {
if (invocation == nil) {
return NO;
}
BOOL returnValue = NO;
[invocation getReturnValue:&returnValue];
return returnValue;
}

+ (void)proxyDestinationSelector:(SEL)destinationSelector
implementationsFromSourceSelector:(SEL)sourceSelector
fromClass:(Class)sourceClass
Expand Down Expand Up @@ -696,6 +738,17 @@ - (BOOL)application:(GULApplication *)application
#pragma clang diagnostic pop
if (openURLOptionsIMP) {
returnedValue |= openURLOptionsIMP(self, methodSelector, application, url, options);
} else {
returnedValue |= [GULAppDelegateSwizzler
boolReturnValueOfInvocation:[GULAppDelegateSwizzler
forwardSelector:methodSelector
fromObject:self
argumentSetter:^(NSInvocation *invocation) {
[invocation setArgument:(void *)(&application)
atIndex:2];
[invocation setArgument:(void *)(&url) atIndex:3];
[invocation setArgument:(void *)(&options) atIndex:4];
}]];
}
return returnedValue;
}
Expand Down Expand Up @@ -731,6 +784,19 @@ - (BOOL)application:(GULApplication *)application
if (openURLSourceApplicationAnnotationIMP) {
returnedValue |= openURLSourceApplicationAnnotationIMP(self, methodSelector, application, url,
sourceApplication, annotation);
} else {
returnedValue |= [GULAppDelegateSwizzler
boolReturnValueOfInvocation:[GULAppDelegateSwizzler
forwardSelector:methodSelector
fromObject:self
argumentSetter:^(NSInvocation *invocation) {
[invocation setArgument:(void *)(&application)
atIndex:2];
[invocation setArgument:(void *)(&url) atIndex:3];
[invocation setArgument:(void *)(&sourceApplication)
atIndex:4];
[invocation setArgument:(void *)(&annotation) atIndex:5];
}]];
}
return returnedValue;
}
Expand Down Expand Up @@ -765,6 +831,14 @@ - (void)application:(GULApplication *)application
// Call the real implementation if the real App Delegate has any.
if (handleBackgroundSessionIMP) {
handleBackgroundSessionIMP(self, methodSelector, application, identifier, completionHandler);
} else {
[GULAppDelegateSwizzler forwardSelector:methodSelector
fromObject:self
argumentSetter:^(NSInvocation *invocation) {
[invocation setArgument:(void *)(&application) atIndex:2];
[invocation setArgument:(void *)(&identifier) atIndex:3];
[invocation setArgument:(void *)(&completionHandler) atIndex:4];
}];
}
}

Expand Down Expand Up @@ -795,6 +869,19 @@ - (BOOL)application:(GULApplication *)application
if (continueUserActivityIMP) {
returnedValue |= continueUserActivityIMP(self, methodSelector, application, userActivity,
restorationHandler);
} else {
returnedValue |= [GULAppDelegateSwizzler
boolReturnValueOfInvocation:[GULAppDelegateSwizzler
forwardSelector:methodSelector
fromObject:self
argumentSetter:^(NSInvocation *invocation) {
[invocation setArgument:(void *)(&application)
atIndex:2];
[invocation setArgument:(void *)(&userActivity)
atIndex:3];
[invocation setArgument:(void *)(&restorationHandler)
atIndex:4];
}]];
}
return returnedValue;
}
Expand Down Expand Up @@ -825,6 +912,13 @@ - (void)application:(GULApplication *)application
// Call the real implementation if the real App Delegate has any.
if (didRegisterForRemoteNotificationsIMP) {
didRegisterForRemoteNotificationsIMP(self, methodSelector, application, deviceToken);
} else {
[GULAppDelegateSwizzler forwardSelector:methodSelector
fromObject:self
argumentSetter:^(NSInvocation *invocation) {
[invocation setArgument:(void *)(&application) atIndex:2];
[invocation setArgument:(void *)(&deviceToken) atIndex:3];
}];
}
}

Expand All @@ -851,6 +945,13 @@ - (void)application:(GULApplication *)application
// Call the real implementation if the real App Delegate has any.
if (didFailToRegisterForRemoteNotificationsIMP) {
didFailToRegisterForRemoteNotificationsIMP(self, methodSelector, application, error);
} else {
[GULAppDelegateSwizzler forwardSelector:methodSelector
fromObject:self
argumentSetter:^(NSInvocation *invocation) {
[invocation setArgument:(void *)(&application) atIndex:2];
[invocation setArgument:(void *)(&error) atIndex:3];
}];
}
}

Expand Down Expand Up @@ -896,6 +997,21 @@ - (void)application:(GULApplication *)application

didReceiveRemoteNotificationWithCompletionIMP(self, methodSelector, application, userInfo,
localCompletionHandler);
} else {
dispatch_group_enter(callbackGroup);

NSInvocation *forwardedInvocation = [GULAppDelegateSwizzler
forwardSelector:methodSelector
fromObject:self
argumentSetter:^(NSInvocation *invocation) {
[invocation setArgument:(void *)(&application) atIndex:2];
[invocation setArgument:(void *)(&userInfo) atIndex:3];
[invocation setArgument:(void *)(&localCompletionHandler) atIndex:4];
}];
// Nothing was forwarded, so the completion handler will never leave the group.
if (forwardedInvocation == nil) {
dispatch_group_leave(callbackGroup);
}
}

dispatch_group_notify(callbackGroup, dispatch_get_main_queue(), ^() {
Expand Down
Loading
Loading