SwiftMessages
SwiftMessages copied to clipboard
Crash in installMaskingView
Hello :)
I've posted almost 1 year ago in #253 an issue where my app crashed due to autolayout issue. At the time i've updated SwiftMessage to 6.0.2 and the crash is still there, but slightly differs from the old one. With SwiftMessage 4.1.4:
Fatal Exception: NSInternalInconsistencyException
Impossible to set up layout with view hierarchy unprepared for constraint.
0 CoreFoundation 0x1835a2d8c __exceptionPreprocess
1 libobjc.A.dylib 0x18275c5ec objc_exception_throw
2 CoreFoundation 0x1835a2bf8 +[NSException raise:format:]
3 Foundation 0x183f92fa0 -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:]
4 UIKit 0x18dcd1f78 __120-[UIView(UIConstraintBasedLayout) _layoutEngine_didAddLayoutConstraint:roundingAdjustment:mutuallyExclusiveConstraints:]_block_invoke_2
5 UIKit 0x18d1c29ec -[UIView(UIConstraintBasedLayout) _layoutEngine_didAddLayoutConstraint:roundingAdjustment:mutuallyExclusiveConstraints:]
6 UIKit 0x18d1c2804 -[UIView(UIConstraintBasedLayout) _tryToAddConstraintWithoutUpdatingConstraintsArray:roundingAdjustment:mutuallyExclusiveConstraints:]
7 UIKit 0x18d4b1254 __50-[UIView(UIConstraintBasedLayout) addConstraints:]_block_invoke
8 Foundation 0x183ef1500 -[NSISEngine withBehaviors:performModifications:]
9 UIKit 0x18d2670cc -[UIView(UIConstraintBasedLayout) addConstraints:]
10 SwiftMessages 0x104e08dac specialized installMaskingView #1 (containerView:) in Presenter.install() (Presenter.swift:344)
11 SwiftMessages 0x104e065d0 Presenter.install() (Presenter.swift)
12 SwiftMessages 0x104e097e4 specialized Presenter.show(completion:) (Presenter.swift:116)
13 SwiftMessages 0x104e0c3b8 closure #1 in SwiftMessages.dequeueNext() (SwiftMessages.swift:539)
14 SwiftMessages 0x104e19088 _T0Ieg_IeyB_TR (TopBottomAnimation.swift)
15 libdispatch.dylib 0x182e94aa0 _dispatch_call_block_and_release
16 libdispatch.dylib 0x182e94a60 _dispatch_client_callout
17 libdispatch.dylib 0x182ea165c _dispatch_main_queue_callback_4CF$VARIANT$mp
18 CoreFoundation 0x18354b070 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__
19 CoreFoundation 0x183548bc8 __CFRunLoopRun
20 CoreFoundation 0x183468da8 CFRunLoopRunSpecific
21 GraphicsServices 0x18544e020 GSEventRunModal
22 UIKit 0x18d488758 UIApplicationMain
23 MyApp 0x1026ff028 main (AppDelegate.swift:28)
24 libdyld.dylib 0x182ef9fc0 start
With SwiftMessage 6.0.2:
Fatal Exception: NSGenericExceptionUnable to activate constraint with anchors <NSLayoutYAxisAnchor:0x282e77d00 "SwiftMessages.MaskingView:0x108b19e20.bottom"> and <NSLayoutYAxisAnchor:0x282e71e80 "UITabBar:0x108a04ee0.top"> because they have no common ancestor. Does the constraint or its anchors reference items in different view hierarchies? That's illegal. Raw Text
--
0 | CoreFoundation | __exceptionPreprocess
1 | libobjc.A.dylib | objc_exception_throw
2 | CoreFoundation | -[NSCache init]
3 | Foundation | -[NSLayoutConstraint _setActive:mutuallyExclusiveConstraints:]
4 | SwiftMessages | <compiler-generated> line 0installMaskingView #1 (containerView:) in Presenter.install()
5 | SwiftMessages | Presenter.swift line 401Presenter.install() + 401
6 | SwiftMessages | <compiler-generated> line 0Presenter.show(completion:)
7 | SwiftMessages | SwiftMessages.swift line 547closure #1 in SwiftMessages.dequeueNext() + 547
8 | SwiftMessages | <compiler-generated> line 0thunk for @escaping @callee_guaranteed () -> ()
9 | libdispatch.dylib | _dispatch_call_block_and_release
10 | libdispatch.dylib | _dispatch_client_callout
11 | libdispatch.dylib | _dispatch_main_queue_callback_4CF$VARIANT$mp
12 | CoreFoundation | __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__
13 | CoreFoundation | __CFRunLoopRun
14 | CoreFoundation | CFRunLoopRunSpecific
15 | GraphicsServices | GSEventRunModal
16 | UIKitCore | UIApplicationMain
17 | MyApp | AppDelegate.swift line 28main + 28
18 | libdyld.dylib | start
Here is an example of the way I show a message:
let view = MessageView.viewFromNib(layout: .messageView)
let buttonTapHandler: ((UIButton ) -> Void)? = { _ in
SwiftMessages.hideAll()
}
let buttonImage = Asset.Common.erroriconsubtlecustom.image
view.configureContent(title: title, body: body, iconImage: nil, iconText: nil, buttonImage: buttonImage, buttonTitle: nil, buttonTapHandler: buttonTapHandler)
view.backgroundColor = ColorName.weather.color
view.titleLabel?.textColor = .white
view.bodyLabel?.textColor = .white
view.button?.tintColor = .white
view.titleLabel?.font = FontFamily.SanFranciscoText.heavy.font(size: 12)
view.bodyLabel?.numberOfLines = 3
view.bodyLabel?.font = FontFamily.SanFranciscoText.regular.font(size: 13)
view.tapHandler = tapHandler
var config = SwiftMessages.defaultConfig
config.presentationStyle = .bottom
config.duration = .forever
config.interactiveHide = true
SwiftMessages.show(config: config, view: view)
Messages that seems to crash are presented in theviewDidAppear of a UITabBarViewController subclass, with a UINavigationController as parent of the message
Updating to SwiftMessage 7.0.1 isn't an option for now as it requires Xcode 11 which I don't plan to use really soon (bugs, project compatibility, etc.).
Any reason why you don’t go with 7.0.0?
Nah, I've seen that 7.0.1 needed Xcode 11 and I supposed it was also the case for the 7.0.0. I'll make the update if possible.
I have a similar issue in 7.0.0.
0 CoreFoundation 0x1aa285c30 __exceptionPreprocess
1 libobjc.A.dylib 0x1a9fa00c8 objc_exception_throw
2 Foundation 0x1aa55eb8c -[NSLayoutConstraint isActive]
3 MyApp 0x105841d2c installMaskingView #1 (containerView:) in Presenter.install() (<compiler-generated>)
4 MyApp 0x1058414e0 Presenter.install() + 412 (Presenter.swift:412)
5 MyApp 0x10583f298 Presenter.show(completion:) (<compiler-generated>)
6 MyApp 0x105848a58 closure #1 in SwiftMessages.dequeueNext() + 552 (SwiftMessages.swift:552)
7 MyApp 0x104ce8554 thunk for @escaping @callee_guaranteed () -> () (<compiler-generated>)
8 libdispatch.dylib 0x1a9f2bbb0 _dispatch_call_block_and_release
9 libdispatch.dylib 0x1a9f2d00c _dispatch_client_callout
10 libdispatch.dylib 0x1a9f38cd8 _dispatch_main_queue_callback_4CF
11 CoreFoundation 0x1aa200e20 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__
12 CoreFoundation 0x1aa1fbb7c __CFRunLoopRun
13 CoreFoundation 0x1aa1fb098 CFRunLoopRunSpecific
14 GraphicsServices 0x1b4365534 GSEventRunModal
15 UIKitCore 0x1ae31b7ac UIApplicationMain
16 MyApp 0x104cb4c90 main + 15 (main.m:15)
17 libdyld.dylib 0x1aa07af30 (Missing)
It crashes with the exception:
Unable to activate constraint with anchors <NSLayoutYAxisAnchor:0x281594b80 "SwiftMessages.MaskingView:0x1564e2a60.bottom"> and <NSLayoutYAxisAnchor:0x281594d80 "UITabBar:0x151d09c30.top"> because they have no common ancestor. Does the constraint or its anchors reference items in different view hierarchies? That's illegal.
My SwiftMessages are displayed using the block creation pattern SwiftMessages.show(config, {}), inside UIViewController subclasses that are the root view controller of a UINavigationController that is in the viewControllers of a UITabBarController.
This happens to approximately 0.18% of users in production. I've not yet been able to reproduce it myself.
I'm upgrading to 7.0.1 this release and will keep thread apprised.
Ugh...18% of users is the worst kind of crash since my chance of reproducing it is very slim :(
Any chance that they're jailbroken? Or any other commonalities? I've had crashes with jailbroken users in the past.
Here are some additional infos from Crashlytics for this crash: 100% of crashes are on iPhone 68% on iOS 13, 29% on iOS 12. 0% in background 0% jailbroken
While the audience of the app is: 80% on iPhone, 20% on iPad 57% on iOS 13, 34% on iOS 12
It looks it only crashes on iPhone, and has a bit more probability to happen using iOS 13.
My best guess is UITabBar does some weird internal stuff that leads to this. Do you need to install the message into the tab bar? The only reason I know if is if you’re trying to slide up from behind the tab bar. If you don’t need that, then I’d think you’d use config.presentationContext = .window(...).
Ah my bad, I made a misstake in my original post. The SwiftMessage view is presented from the TabbarController, true, but the tabbarController is not the parent, the parent is one UINavigationController used in the tabBarController. This way the SwiftMessage's view is correctly above the tabBar and doesn't overlap it.
Can you use config.presentationContext = .window(...)?
Unfortunately using a .window() context will make the message overlap the UITabBar which we don't want. The message should be displayed above the UITabBar.
I have some more information to add to this issue, maybe it'll help others.
• 7.0.1 does not prevent the crashes.
• I finally managed to reproduce the issue in my debugger. What appears to be happening to me is:
- View Controller begins a UIView.animate() { pushViewController(animated:false) custom transition to a TabBarController subclass, that will eventually contain the SwiftMessage.
- A (probably unrelated) database failure occurs, which triggers (3)
- This causes the view controllers navigation controller to begin a separate UIView.animate() (similar) transition to a different UIViewController.
- I believe in the process of creating and rendering both view controllers for the animations, when the SwiftMessage attempts to grab a UITabBar to bind to, (which I assume is through view.window because my presentationContext is .view() ) it grabs the wrong viewController, and gets the TabBar associated with the other view controller.
In this case, fixing the database issue prevented the two animations from playing simultaneously when they shouldn't have, which prevents the issue.
I have released an update with this fix, and will update the thread again if i see any more of these crashes.
Thanks for the update. Just be clarify one thing:
presentationContextis.view()
When you use this presentation context, the message is displayed in the view you specify. It doesn’t have any reason to try and find a tab bar.
Okay, if it doesn't try to find a tab bar through .view, then it would have to be one of my other .viewController() presentation contexts (we use SwiftMessage a lot) that crashed... and I can't explain how it's finding a tabBar reference that's not associated with the viewController that is used as the presentationContext. Unless the fact that its during that animation transition is the problem, but all my SwiftMessages are displayed in viewDidAppear or later. It's a really weird crash.
I honestly would've blamed the crash i could reproduce in XCode (described above) on the database issue, but it crashes in the InstallMaskingView every time. If it was just the database crashing on a background thread, i'd get some different stack traces occasionally.
My version with this fix has been live for 40 hours now, without one of these crashes. That is positive.
I am still seeing some of these crashes. I'm going to look for other places that SwiftMessages might be created by viewControllers during a custom animated transition.
I've managed to reproduce this once on my device:
I have some view controllers that can both show a .bottom aligned SwiftMessage and push another view controller in viewDidAppear
In cases where both happen, and the pushed view controller has .hideBottomBarOnPush = true, sometimes there's a timing issue that causes this crash. I'm going to make these items mutually exclusive to prevent this issue.
@MaoDaAmargura thanks for sharing your findings!