globulus / swiftui-pull-to-refresh

Pull to refresh functionality for any ScrollView in SwiftUI!

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

crash in PreferenceKey.reduce(value:nextValue:) in conformance PositionPreferenceKey

dmrschmidt opened this issue · comments

Hey,

thanks for creating this library! Super useful.

We've been seeing a couple crashes with it since recently, and not a real clue what could be causing this. From the app usage logs we can see in our Crashlytics, we don't see a clear pattern yet. There's nothing from our own code directly involved in the crashes, all we can find so far is that its in PreferenceKey.reduce(value:nextValue:) from PositionPreferenceKey.

Tapping in the dark here, so hoping you might have a clue. I'm myself not familiar enough yet with how PreferenceKeys are supposed to work.

Cheers

Crashed: com.apple.main-thread
EXC_BREAKPOINT 0x00000001a6751acc

Crashed: com.apple.main-thread
0  libswiftCore.dylib             0x1a6751acc _assertionFailure(_:_:file:line:flags:) + 1532
1  SwiftUI                        0x1a93f6b8c ViewCache.commitPlacedChildren(from:to:) + 2880
2  SwiftUI                        0x1a90d7430 specialized IncrementalChildPlacements.updateValue() + 1480
3  SwiftUI                        0x1a92b0664 partial apply for specialized implicit closure #2 in implicit closure #1 in closure #1 in closure #1 in Attribute.init<A>(_:) + 24
4  AttributeGraph                 0x1cc2ae77c AG::Graph::UpdateStack::update() + 492
5  AttributeGraph                 0x1cc2aebb4 AG::Graph::update_attribute(AG::data::ptr<AG::Node>, bool) + 332
6  AttributeGraph                 0x1cc2b42fc AG::Graph::input_value_ref_slow(AG::data::ptr<AG::Node>, AG::AttributeID, unsigned int, AGSwiftMetadata const*, bool*, long) + 364
7  AttributeGraph                 0x1cc2c609c AGGraphGetValue + 232
8  SwiftUI                        0x1a93fadc0 IncrementalPreference.children.getter + 68
9  SwiftUI                        0x1a93faf34 IncrementalPreference.value.getter + 252
10 SwiftUI                        0x1a9336130 implicit closure #2 in implicit closure #1 in closure #1 in closure #1 in Attribute.init<A>(_:) + 252
11 AttributeGraph                 0x1cc2ae77c AG::Graph::UpdateStack::update() + 492
12 AttributeGraph                 0x1cc2aebb4 AG::Graph::update_attribute(AG::data::ptr<AG::Node>, bool) + 332
13 AttributeGraph                 0x1cc2b42fc AG::Graph::input_value_ref_slow(AG::data::ptr<AG::Node>, AG::AttributeID, unsigned int, AGSwiftMetadata const*, bool*, long) + 364
14 AttributeGraph                 0x1cc2c609c AGGraphGetValue + 232
15 SwiftUI                        0x1a969d304 closure #1 in PreferenceCombiner.value.getter + 84
16 UI                             0x1017a4a3c protocol witness for static PreferenceKey.reduce(value:nextValue:) in conformance PositionPreferenceKey + 4314073660 (<compiler-generated>:4314073660)
17 SwiftUI                        0x1a969d24c PreferenceCombiner.value.getter + 488
18 SwiftUI                        0x1a9336130 implicit closure #2 in implicit closure #1 in closure #1 in closure #1 in Attribute.init<A>(_:) + 252
19 AttributeGraph                 0x1cc2ae77c AG::Graph::UpdateStack::update() + 492
20 AttributeGraph                 0x1cc2aebb4 AG::Graph::update_attribute(AG::data::ptr<AG::Node>, bool) + 332
21 AttributeGraph                 0x1cc2b42fc AG::Graph::input_value_ref_slow(AG::data::ptr<AG::Node>, AG::AttributeID, unsigned int, AGSwiftMetadata const*, bool*, long) + 364
22 AttributeGraph                 0x1cc2c609c AGGraphGetValue + 232
23 SwiftUI                        0x1a969d5ec closure #1 in PairPreferenceCombiner.value.getter + 84
24 UI                             0x1017a4a3c protocol witness for static PreferenceKey.reduce(value:nextValue:) in conformance PositionPreferenceKey + 4314073660 (<compiler-generated>:4314073660)
25 SwiftUI                        0x1a969d55c PairPreferenceCombiner.value.getter + 280
26 SwiftUI                        0x1a9336130 implicit closure #2 in implicit closure #1 in closure #1 in closure #1 in Attribute.init<A>(_:) + 252
27 AttributeGraph                 0x1cc2ae77c AG::Graph::UpdateStack::update() + 492
28 AttributeGraph                 0x1cc2aebb4 AG::Graph::update_attribute(AG::data::ptr<AG::Node>, bool) + 332
29 AttributeGraph                 0x1cc2b42fc AG::Graph::input_value_ref_slow(AG::data::ptr<AG::Node>, AG::AttributeID, unsigned int, AGSwiftMetadata const*, bool*, long) + 364
30 AttributeGraph                 0x1cc2c609c AGGraphGetValue + 232
31 SwiftUI                        0x1a9725ca0 PreferenceBinder.updateValue() + 360
32 SwiftUI                        0x1a9453b00 partial apply for implicit closure #2 in implicit closure #1 in closure #1 in closure #1 in Attribute.init<A>(_:) + 32
33 AttributeGraph                 0x1cc2ae77c AG::Graph::UpdateStack::update() + 492
34 AttributeGraph                 0x1cc2aebb4 AG::Graph::update_attribute(AG::data::ptr<AG::Node>, bool) + 332
35 AttributeGraph                 0x1cc2b7dc4 AG::Subgraph::update(unsigned int) + 884
36 SwiftUI                        0x1a9b081c0 GraphHost.runTransaction() + 180
37 SwiftUI                        0x1a9590170 ViewGraph.updateOutputs(at:) + 108
38 SwiftUI                        0x1a9a546b8 closure #1 in ViewRendererHost.render(interval:updateDisplayList:) + 1508
39 SwiftUI                        0x1a9a4ac0c ViewRendererHost.render(interval:updateDisplayList:) + 308
40 SwiftUI                        0x1a9be6960 _UIHostingView.layoutSubviews() + 200
41 SwiftUI                        0x1a9be6994 @objc _UIHostingView.layoutSubviews() + 28
42 UIKitCore                      0x1a5ae4be4 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 2576
43 QuartzCore                     0x1a5f6b670 -[CALayer layoutSublayers] + 308
44 QuartzCore                     0x1a5f6bb54 CA::Layer::layout_if_needed(CA::Transaction*) + 548
45 QuartzCore                     0x1a5f8078c CA::Layer::layout_and_display_if_needed(CA::Transaction*) + 144
46 QuartzCore                     0x1a5ec25e4 CA::Context::commit_transaction(CA::Transaction*, double, double*) + 500
47 QuartzCore                     0x1a5eee7f4 CA::Transaction::commit() + 684
48 QuartzCore                     0x1a5eefb20 CA::Transaction::observer_callback(__CFRunLoopObserver*, unsigned long, void*) + 96
49 CoreFoundation                 0x1a2b2bc74 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 36
50 CoreFoundation                 0x1a2b25d98 __CFRunLoopDoObservers + 572
51 CoreFoundation                 0x1a2b26344 __CFRunLoopRun + 1052
52 CoreFoundation                 0x1a2b259f4 CFRunLoopRunSpecific + 600
53 GraphicsServices               0x1ba1f6734 GSEventRunModal + 164
54 UIKitCore                      0x1a55a375c -[UIApplication _run] + 1072
55 UIKitCore                      0x1a55a8fcc UIApplicationMain + 168
56 Beams                          0x100d1d87c main + 11 (BeamsAnalyticsController.swift:11)
57 libdyld.dylib                  0x1a27e1cf8 start + 4

Forgot to mention that we've seen this so far on iOS 14.7.1 & 14.8.0. So far only on iPhone 12 Pro. I'm seeing that for iOS 15 it's already switching to the new refreshable so assuming that crash won't happen there.

Hi, glad you're happy with the library!

TL;DR - the issue is unfixable. It comes from SwiftUI internals and is beyond control.

Longer version:

I just tested this on an iPhone 7 with 14.7.1 and an SE with 14.8 and haven't seen any crashes (and I have the component used in a production app, although I have no clue how many 12 Pro users on 14.8 do we have :)). Additionally, I can't try this on a simulator since there aren't any simulator components between 14.5 and 15.0.

Based on the stack trace, the issue isn't really with PreferenceKeys (which are just the expected way of propagating state changes triggered by other state changes). Normally, there'd be two possible causes for the issues you're seeing:

  1. Issue with your view hierarchy - e.g, SwiftUI occasionally doesn't like multi-layered GeometryReaders (and RefreshableScrollView already has one).
  2. Issue with the specific SwiftUI version / implementation on phone.

Since you're only seeing this on a single phone , I'd say it's the latter. Regardless, the issue is (most likely) unfixable as it's a faulty implementation on SwiftUI side (and apparently just for 12 Pro). This is what generally makes SwiftUI not suitable for production yet - it's plagued with internal bugs that are tied to OS version, and you have no choice but to wait for Apple to fix them in a coming version. iOS 13.* was incredibly unstable, 14.* fixed that for the most part, 15.* betas seem even more stable as it seems.

As far as as iOS 15 goes, the component doesn't use refreshable directly unless you specify it with refreshableCompat - which uses refreshable on SwiftUI 3 targets, and the regular code on SwiftUI 1 and 2. That being said, I expect Apple to fix this issue in 15 anyhow, as it's not just this lib that is affected by the bug. For what it's worth, I did try this on 12 Pro 15.0 Beta simulator, and haven't seen any issues.

Thanks for your quick and detailed response! Resonates with my assessment as well. In this instance there aren't any other GeometryReaders involved, so I'll just file it as a #wontfix for us and wait until it resolves itself by people upgrading to iOS 15.