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

[Fix] swizzling not forwarding with apps that use UIApplicationDelegateAdaptor #1091

Merged
merged 8 commits into from May 24, 2022

Conversation

jkasten2
Copy link
Member

@jkasten2 jkasten2 commented May 19, 2022

Description

One Line Summary

Fix some AppDelegate methods not being called when the app uses @UIApplicationDelegateAdaptor. Includes a secondary fix for rare stack overflow bug due to duplicate implementations with swizzling the UNUserNotificationCenterDelegate.

Details

Motivation

OneSignal should not cause side effects to AppDelegate events. The following are not firing when @UIApplicationDelegateAdaptor is used:

  • application(_:didReceiveRemoteNotification:fetchCompletionHandler:)
  • application(_:didFailToRegisterForRemoteNotificationsWithError:)
  • application(_:didRegisterForRemoteNotificationsWithDeviceToken:)
  • applicationWillTerminate(_:)

The motivation to fix the stack overflow bug due to double swizzling in this PR is that it was required to address to write tests. It's possible to run into in an app setup too, however it's questionable if such a situation would happen.

Scope

Effects swizzling logic, includes double swizzling detection to prevent infinite loops for both AppDelegate and UNUserNotificationCenterDelegate. Also calling original implementation of AppDelegate selectors noted above in the Motivation section.

Related Issues

fixes #1045

Apple's @UIApplicationDelegateAdaptor and SwiftUI.AppDelegate

The inter workings of @UIApplicationDelegateAdaptor is unknown it was discovered that the AppDelegate set is an private SwiftUI.AppDelegate class. This class doesn't include selectors for the events noted above in the Motivation section. When these selectors are added to this class via our swizzling code the methods on the app's @UIApplicationDelegateAdaptor instance are no longer called. We discovered however that calling forwardingTargetForSelector: on SwiftUI.AppDelegate returns the instance of @UIApplicationDelegateAdaptor set by the app. We ended up using this as a fallback if respondsToSelector: is false.

Follow up PR, use SwizzlingForwarder consistency

This PR added a new SwizzlingForwarder class to make sure we forward the event to prevent side effects. However this PR did not add this to UNUserNotificationCenterDelegate to keep this scope down.

Testing

Unit testing

Manual testing

Tested on Xcode 13.3.1 on an iOS simulator and an iPhone 6s with iOS 14.4.1.
Tested with Objective-C and Swift with SwiftUI.
Tested to ensure didFailToRegisterForRemoteNotificationsWithError: and didRegisterForRemoteNotificationsWithDeviceToken: fire in the app's AppDelegate.

Affected code checklist

  • Notifications
    • Display
    • Open
    • Push Processing
    • Confirm Deliveries
  • Outcomes
  • Sessions
  • In-App Messaging
  • REST API requests
  • Public API changes
  • Swizzling

Checklist

Overview

  • I have filled out all REQUIRED sections above
  • PR does one thing
  • Any Public API changes are explained in the PR details and conform to existing APIs

Testing

  • I have included test coverage for these changes, or explained why they are not needed
  • All automated tests pass, or I explained why that is not possible
  • I have personally tested this on my device, or explained why that is not possible

Final pass

  • Code is as readable as possible.
  • I have reviewed this PR myself, ensuring it meets each checklist item

This change is Reviewable

Add testAddsMissingSelectors to ensure we add all selectors we need to
get events from iOS.

The change made to setOneSignalDelegate was required to make it
testable. This equality check is already done in setOneSignalUNDelegate
so this is also helping match our logic between these.
Added one failing test for the case where forwardingTargetForSelector
is not accounted for in swizzling. The most common case for this is a
SwiftUI app with @UIApplicationDelegateAdaptor.

Just added one test and fixed one case to keep this PR small and
readable, will add the rest in a follow up commit.
Base automatically changed from test/fix_iam_test_carryover to main May 19, 2022 17:12
iOS 9 selectors we only swizzle on iOS 9 since
UIApplicationDelegateAdaptor requires a newer version of iOS.
This fixes a bug where an infinite loop happens when we call the origin
implementation if delegates we swizzle if something attempts to set the
delegate back to the original class. This would only happen if the class
didn't have the target selector to begin with when we run this swizzle
logic, but then it is called a 2nd time with same target class. We have
a check in the setOneSignalDelegate and setOneSignalUNDelegate but if
the app developer or another library changes the delegate then puts it
back this scenario can happen.

Added a test for this scenario, ensuring it fails and then passing
after. This also fixes an issue in the last commit where this bug was
causing tests after UIApplicationDelegateSwizzingTest to infinitely
loop.
This test broke in the last commit since the test was relying on
a 2nd call to injectSelector to unswizzle. This was intentionally done
as we don't want app or other SDKs swapping delegates back and forth to
cause OneSignal to no longer receive events.

This forced me to rewrite the OneSignalUNUserNotificationCenterHelper
used for tests to not reply on this behavior and in the process made
much simpler. It is declarative of the methods to swap back and
therefore much easier to understand.
Fix missing selector warns in this file from the last commit since.
Add test to ensure our swizzling still calls the original methods.
@jkasten2 jkasten2 changed the title [WIP] [Fix] swizzling not forwarding with apps that use UIApplicationDelegateAdaptor [Fix] swizzling not forwarding with apps that use UIApplicationDelegateAdaptor May 24, 2022
@jkasten2 jkasten2 requested review from emawby and a team May 24, 2022 09:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

OneSignal seems to swizzle app delegate methods and didReceiveRemoteNotification is not called
2 participants