Appboy / appboy-ios-sdk

Public repo for the Braze iOS SDK
https://www.braze.com
Other
165 stars 140 forks source link

[Bug]: ABKInAppMessageWindow stays key window after modal InAppMessage got dismissed and therefore blocks app #333

Closed PatrickDotStar closed 2 years ago

PatrickDotStar commented 2 years ago

Platform

iOS

Platform Version

iOS 15.2

Braze SDK Version

4.3.4

Xcode Version

13.2.1 (13C100)

Integration Method

Cocoapods

Computer Processor

Apple (M1)

Repro Rate

100%

Steps To Reproduce

When presenting a modal InAppMessage and dismissing it with either the close button or one of the two configurable buttons, the ABKInAppMessageWindow stays in die view hierarchy and therefore blocks further user interaction. After ~5-10 seconds the ABKInAppMessageWindow is either completely removed or moved down in the UIWindowScene hierarchy. This issue, for us, is only reproducible with the modal InAppMessage typ. Full-Screen and Simple Survey for example work fine.

Here are some screenshots of the UIWindow view hierarchy and you can see that for "Modal dismissed" the ABKInAppMessageWindow is still on top of the view hierarchy and therefore blocks further user interaction. After ~5-10 seconds later after the modal has been dismissed, the ABKInAppMessageWindow is no longer in the view hierarchy and therefore the app is no longer blocked by this window.

Full-Screen Modal
Full-Screen visible Modal visible
fullScreenVisible modalVisible
Full-Screen dismissed Modal dismissed
fullScreenDismissed modalDismissed
Full-Screen dismissed + ~10 seconds later Modal dismissed + ~10 seconds later
fullScreenDismissedPlusExtraTime modalDismissedPlusExtraTime

As a current workaround I implemented these 2 delegate methods of ABKInAppMessageUIDelegate to make our UIWindow key and visible again.

func on(inAppMessageDismissed inAppMessage: ABKInAppMessage) {
    if inAppMessage is ABKInAppMessageModal {
        let deadline: DispatchTime = inAppMessage.animateOut ? .now() + InAppMessageAnimationDuration : .now()
        DispatchQueue.main.asyncAfter(deadline: deadline, execute: {
            self.window?.makeKeyAndVisible()
        })
    }
}

func on(inAppMessageButtonClicked inAppMessage: ABKInAppMessageImmersive, button: ABKInAppMessageButton) -> Bool {
    if inAppMessage is ABKInAppMessageModal {
        let deadline: DispatchTime = inAppMessage.animateOut ? .now() + InAppMessageAnimationDuration : .now()
        DispatchQueue.main.asyncAfter(deadline: deadline, execute: {
            self.window?.makeKeyAndVisible()
        })
    }

    return false
}

Expected Behavior

ABKInAppMessageWindow is properly handled after being dismissed for modal InAppMessages.

Actual Incorrect Behavior

ABKInAppMessageWindow stays on top of UIWindowScene view hierarchy and therefore blocks further user interaction.

Verbose Logs

No response

Additional Information

No response

Bucimis commented 2 years ago

@PatrickDotStar thanks for the info. We have not been able to repro on our end.

1) Are you doing any custom deep link handling in the SDK or button click handling on IAMs? 2) Were you using the same click action and deep-link/url to test across all IAM types? 3) Does changing the click action/deep-link/url combo of the buttons changed the observed behavior?

PatrickDotStar commented 2 years ago

@Bucimis Thanks for your reply.

1: No we don't do anything special. We only implemented the before(inAppMessageDisplayed inAppMessage: ABKInAppMessage) -> ABKInAppMessageDisplayChoice delegate method to avoid showing any InAppMessages on the lock screen but even if the messages are presented right after app start we encounter this issue.

2+3: I configured the buttons that one just closes the message and the second to open a DeepLink. The DeepLink, which then presents a new view, also seems to fixes the issue. But when just closing the message with either regular or the close button, it freezes the app again.

Bucimis commented 2 years ago

@PatrickDotStar thanks for the update 1) Does upgrading to our latest release 4.4.2 fix the issue? There are some refinements to key window logic there. 2) Just to double confirm, the issue only appears with buttons that close the IAM, and only on the modal native type. 3) Would be able to send verbose logs (https://www.braze.com/docs/developer_guide/platform_integration_guides/ios/initial_sdk_setup/other_sdk_customizations/#verbose-logging) illustrating the issue to support@braze.com (over email is better for privacy reasons)? It's helpful even if logs don't show anything going wrong specifically.

PatrickDotStar commented 2 years ago

Hey @Bucimis

  1. I just updated to the latest version available (4.4.2) but the issue still exists.
  2. Yes the issue is only reproducible with the Modal type when pressing buttons to close it but also when dismissing the Modal by tapping outside. I tried again with Full-Screen and Simple-Survey but these just work fine.
  3. I will try to send some logs to the Email address you provided.
lowip commented 2 years ago

Hey @PatrickDotStar, thanks for the precisions. We still haven't been able to reproduce the issue on our side.

The ABKInAppMessageWindow is not supposed to linger for ~5-10s before being removed from memory. This is true whatever the type of in-app message being presented.

Our assumption is that something is holding a strong reference to the ABKInAppMessageWindow instance upon dismissal. To verify it, could you Inspect the Debug Memory Graph during those 5-10s. This might help us pinpoint the object preventing the ABKInAppMessageWindow to resign.

Example memory graph You should be able to filter for the `ABKInAppMessageWindow` instance and have a graph like this (hopefully a lot smaller) Screen Shot 2022-02-08 at 9 03 24 AM

Could you also try applying this tentative fix in ABKInAppMessageWindowController.m (Cocoapods should allow you to unlock the file):

// In the method -[ABKInAppMessageWindowController hideInAppMessageWindow]

  self.inAppMessageWindow.rootViewController = nil;
+ if (@available(iOS 13.0, *)) {
+   self.inAppMessageWindow.windowScene = nil
+ }
  self.inAppMessageWindow = nil;

Thanks,

PatrickDotStar commented 2 years ago

Hey @lowip

I attached 2 screenshot of the ABKInAppMessageWindow memory graph ~5 seconds after I dismissed it.

Screenshots ![ABKInAppMessageWindow1](https://user-images.githubusercontent.com/10392878/153015547-b16f6712-b580-4d9f-9613-246056b93313.png) ![ABKInAppMessageWindow2](https://user-images.githubusercontent.com/10392878/153015562-aedcccf4-88d9-40a7-948a-a432dad561fe.png)

Your proposed solution seems to fix the freezing of the app however I then get a EXC_BAD_ACCESS crash when pushing/presenting a new view when one button for example contains a DeepLink. I tried with 3 different DeepLinks but all of them caused the same crash. Removing your fix again also fixes the crashing. I removed my workaround before applying your fix.

Crashlog ``` Date/Time: 2022-02-08 16:07:34.7613 +0100 Launch Time: 2022-02-08 16:07:29.8853 +0100 OS Version: iPhone OS 15.2 (19C56) Release Type: User Baseband Version: 3.01.02 Report Version: 104 Exception Type: EXC_BAD_ACCESS (SIGSEGV) Exception Subtype: KERN_INVALID_ADDRESS at 0x0000000000000020 Exception Codes: 0x0000000000000001, 0x0000000000000020 VM Region Info: 0x20 is not in any region. Bytes before following region: 4306059232 REGION TYPE START - END [ VSIZE] PRT/MAX SHRMOD REGION DETAIL UNUSED SPACE AT START ---> __TEXT 100a94000-10230c000 [ 24.5M] r-x/r-x SM=COW Exception Note: EXC_CORPSE_NOTIFY Termination Reason: SIGNAL 11 Segmentation fault: 11 Terminating Process: exc handler [660] Triggered by Thread: 0 Thread 0 name: Dispatch queue: com.apple.main-thread Thread 0 Crashed: 0 libobjc.A.dylib 0x19a57e3f0 objc_retain + 16 1 UIKitCore 0x1843824bc +[UIWindowScene _keyWindowScene] + 68 2 UIKitCore 0x184322d58 +[UIKeyboardSceneDelegate activeKeyboardSceneDelegate] + 28 3 UIKitCore 0x184336478 -[UIViewController _keyboardSceneDelegate] + 136 4 UIKitCore 0x184318814 -[UIViewController _unloadHierarchyInputAccessoryViewIfNecessary] + 28 5 UIKitCore 0x1843f00c4 -[UIViewController __viewDidDisappear:] + 152 6 UIKitCore 0x18437e748 -[UIViewController _endAppearanceTransition:] + 224 7 UIKitCore 0x184398420 __48-[UIPresentationController transitionDidFinish:]_block_invoke + 296 8 UIKitCore 0x18448f2d4 -[_UIAfterCACommitBlock run] + 72 9 UIKitCore 0x1843a08f8 -[_UIAfterCACommitQueue flush] + 200 10 UIKitCore 0x1842c9278 _runAfterCACommitDeferredBlocks + 644 11 UIKitCore 0x1842c9990 _cleanUpAfterCAFlushAndRunDeferredBlocks + 132 12 UIKitCore 0x1842c9b40 _afterCACommitHandler + 60 13 CoreFoundation 0x181cfd610 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 36 14 CoreFoundation 0x181ccc8f4 __CFRunLoopDoObservers + 572 15 CoreFoundation 0x181cc798c __CFRunLoopRun + 1052 16 CoreFoundation 0x181cdb468 CFRunLoopRunSpecific + 600 17 GraphicsServices 0x19d86738c GSEventRunModal + 164 18 UIKitCore 0x18467d088 -[UIApplication _run] + 1100 19 UIKitCore 0x1843fb958 UIApplicationMain + 2092 20 APP_NAME 0x100ec72d8 main + 64 21 dyld 0x105a6daa4 start + 520 Thread 1: 0 libsystem_pthread.dylib 0x1f27aee8c start_wqthread + 0 Thread 2: 0 libsystem_pthread.dylib 0x1f27aee8c start_wqthread + 0 Thread 3: 0 libsystem_pthread.dylib 0x1f27aee8c start_wqthread + 0 Thread 4: 0 libsystem_pthread.dylib 0x1f27aee8c start_wqthread + 0 Thread 5 name: com.apple.uikit.eventfetch-thread Thread 5: 0 libsystem_kernel.dylib 0x1b9314504 mach_msg_trap + 8 1 libsystem_kernel.dylib 0x1b9314b9c mach_msg + 76 2 CoreFoundation 0x181cc3738 __CFRunLoopServiceMachPort + 372 3 CoreFoundation 0x181cc7a2c __CFRunLoopRun + 1212 4 CoreFoundation 0x181cdb468 CFRunLoopRunSpecific + 600 ```
lowip commented 2 years ago

@PatrickDotStar

Thanks for the quick reply, we'll look into that more closely. The memory graphs look clean, meaning that we don't have an Appboy object retaining the window. I'm not immediately seeing what could retain it.

A few additional questions:

Best,

PatrickDotStar commented 2 years ago
  1. No we have not implemented the UISceneDelegate protocol.
  2. No our entry point is just a regular UIViewController.
  3. Our min supported iOS Version is iOS 13. I was able to reproduce the bug on iOS 14 and 15 however.
antoniostrijdom commented 2 years ago

We are seeing the same issue and we've not yet implemented UISceneDelegate either. No SwiftUI entry point. Deployment target is iOS 13.0.

hokstuff commented 2 years ago

Hi @PatrickDotStar and @antoniostrijdom,

We have released iOS SDK version 4.4.3 that addresses this issue. Thanks for bringing it to our attention!