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

Picker binding triggers runtime warnings in iOS 18 #100

Open
3 tasks done
watt opened this issue Oct 1, 2024 · 2 comments
Open
3 tasks done

Picker binding triggers runtime warnings in iOS 18 #100

watt opened this issue Oct 1, 2024 · 2 comments
Labels
bug Something isn't working due to a bug in the library.

Comments

@watt
Copy link

watt commented Oct 1, 2024

Description

A SwiftUI Picker bound to a @Perceptible model triggers the "Perceptible state was accessed but is not being tracked" runtime warnings in iOS 18.

It seems to be due to access to the selection binding from the resolved body style. Example stack trace below.

Example stack trace
#0	0x0000000103593d9c in PerceptionRegistrar.perceptionCheck(fileID:filePath:line:column:) at /Users/awatt/Development/swift-perception/Sources/Perception/PerceptionRegistrar.swift:214
#1	0x0000000103593ac0 in PerceptionRegistrar.access<Example.OptionModel, Int>(_:keyPath:fileID:filePath:line:column:) at /Users/awatt/Development/swift-perception/Sources/Perception/PerceptionRegistrar.swift:88
#2	0x0000000103580948 in OptionModel.access<Int>(keyPath:fileID:filePath:line:column:) at /var/folders/zj/tw5xtpzn7jq3lhcj08zq18br0000gn/T/swift-generated-sources/@__swiftmacro_7Example11OptionModel11PerceptiblefMm_.swift:10
#3	0x0000000103580420 in OptionModel.option.getter at /var/folders/zj/tw5xtpzn7jq3lhcj08zq18br0000gn/T/swift-generated-sources/@__swiftmacro_7Example11OptionModelC6option17PerceptionTrackedfMa_.swift:7
#4	0x00000001035802fc in key path getter for OptionModel.option : OptionModel ()
#5	0x00000001948594d4 in function signature specialization <Arg[5] = Stack Promoted from Box> of project2<τ_0_0, τ_0_1><τ_1_0><τ_2_0>(τ_2_0.Type) -> Swift.Optional<τ_0_1> ()
#6	0x00000001948590e0 in Swift.KeyPath._projectReadOnly(from: τ_0_0) -> τ_0_1 ()
#7	0x000000019485cc40 in swift_getAtKeyPath ()
#8	0x0000000194a366ec in swift_readAtKeyPath ()
#9	0x00000001d2f5efb0 in SwiftUI.ObjectLocation.get() -> τ_0_1 ()
#10	0x00000001d2c423ac in SwiftUI.LocationBox.get() -> τ_0_0.Value ()
#11	0x00000001d2c43c18 in SwiftUI.FlattenedCollectionLocation.get() -> τ_0_0 ()
#12	0x00000001d2cf2d30 in SwiftUI.Binding.init<τ_0_0 where τ_1_0: Swift.Collection, τ_1_0.Element == SwiftUI.Binding<τ_0_0>>(flattening: τ_1_0) -> SwiftUI.Binding<τ_0_0> ()
#13	0x00000001d1e6080c in SwiftUI.PickerStyleConfiguration.init(selection: Swift.Array<SwiftUI.Binding<τ_0_0>>) -> SwiftUI.PickerStyleConfiguration<τ_0_0> ()
#14	0x00000001d1ed17b4 in SwiftUI.Picker.body.getter : some ()
#15	0x00000001d2a51ef0 in function signature specialization <Arg[2] = Dead> of SwiftUI.ViewBodyAccessor.updateBody(of: τ_0_0, changed: Swift.Bool) -> () ()
#16	0x00000001d2b37818 in closure #1 () -> () in SwiftUI.StaticBody.updateValue() -> () ()
#17	0x00000001d2b37404 in SwiftUI.StaticBody.updateValue() -> () ()
#18	0x00000001d2bc4010 in partial apply forwarder for implicit closure #1 (Swift.UnsafeMutableRawPointer, __C.AGAttribute) -> () in closure #1 () -> (Swift.UnsafeMutableRawPointer, __C.AGAttribute) -> () in closure #1 (Swift.UnsafePointer<τ_1_0>) -> AttributeGraph.Attribute<τ_0_0> in AttributeGraph.Attribute.init<τ_0_0 where τ_0_0 == τ_1_0.Value, τ_1_0: AttributeGraph.StatefulRule>(τ_1_0) -> AttributeGraph.Attribute<τ_0_0> ()
#19	0x00000001bdf35f7c in AG::Graph::UpdateStack::update ()
#20	0x00000001bdf367cc in AG::Graph::update_attribute ()
#21	0x00000001bdf3e438 in AG::Graph::input_value_ref_slow ()
#22	0x00000001bdf53f9c in AGGraphGetValue ()
#23	0x00000001d1e608b4 in SwiftUI._PickerValue.Init1.base.getter : SwiftUI.ResolvedPicker<τ_0_1> ()
#24	0x00000001d1e609a8 in SwiftUI._PickerValue.Init1.value.getter : SwiftUI._PickerValue<τ_0_0, τ_0_1> ()
#25	0x00000001d2bc0cd0 in implicit closure #1 (Swift.UnsafeMutableRawPointer, __C.AGAttribute) -> () in closure #1 () -> (Swift.UnsafeMutableRawPointer, __C.AGAttribute) -> () in closure #1 (Swift.UnsafePointer<τ_1_0>) -> AttributeGraph.Attribute<τ_0_0> in AttributeGraph.Attribute.init<τ_0_0 where τ_0_0 == τ_1_0.Value, τ_1_0: AttributeGraph.Rule>(τ_1_0) -> AttributeGraph.Attribute<τ_0_0> ()
#26	0x00000001bdf35f7c in AG::Graph::UpdateStack::update ()
#27	0x00000001bdf367cc in AG::Graph::update_attribute ()
#28	0x00000001bdf3e438 in AG::Graph::input_value_ref_slow ()
#29	0x00000001bdf53f9c in AGGraphGetValue ()
#30	0x00000001d1d4e46c in SwiftUI.DefaultPickerStyle.Body.base.getter : SwiftUI._PickerValue<SwiftUI.DefaultPickerStyle, τ_0_0> ()
#31	0x00000001d1d4e95c in SwiftUI.DefaultPickerStyle.Body.value.getter : some ()
#32	0x00000001d2bc0cd0 in implicit closure #1 (Swift.UnsafeMutableRawPointer, __C.AGAttribute) -> () in closure #1 () -> (Swift.UnsafeMutableRawPointer, __C.AGAttribute) -> () in closure #1 (Swift.UnsafePointer<τ_1_0>) -> AttributeGraph.Attribute<τ_0_0> in AttributeGraph.Attribute.init<τ_0_0 where τ_0_0 == τ_1_0.Value, τ_1_0: AttributeGraph.Rule>(τ_1_0) -> AttributeGraph.Attribute<τ_0_0> ()
#33	0x00000001bdf35f7c in AG::Graph::UpdateStack::update ()
#34	0x00000001bdf367cc in AG::Graph::update_attribute ()
#35	0x00000001bdf3e438 in AG::Graph::input_value_ref_slow ()
#36	0x00000001bdf53f9c in AGGraphGetValue ()
#37	0x00000001d1e60b64 in SwiftUI._PickerValue.Init2.value.getter : SwiftUI._PickerValue<τ_0_0, τ_0_1> ()
#38	0x00000001d2bc0cd0 in implicit closure #1 (Swift.UnsafeMutableRawPointer, __C.AGAttribute) -> () in closure #1 () -> (Swift.UnsafeMutableRawPointer, __C.AGAttribute) -> () in closure #1 (Swift.UnsafePointer<τ_1_0>) -> AttributeGraph.Attribute<τ_0_0> in AttributeGraph.Attribute.init<τ_0_0 where τ_0_0 == τ_1_0.Value, τ_1_0: AttributeGraph.Rule>(τ_1_0) -> AttributeGraph.Attribute<τ_0_0> ()
#39	0x00000001bdf35f7c in AG::Graph::UpdateStack::update ()
#40	0x00000001bdf367cc in AG::Graph::update_attribute ()
#41	0x00000001bdf3e438 in AG::Graph::input_value_ref_slow ()
#42	0x00000001bdf53f9c in AGGraphGetValue ()
#43	0x00000001d242b148 in SwiftUI.MenuPickerStyle.Body.content.getter : some ()
#44	0x00000001d242ab74 in SwiftUI.MenuPickerStyle.Body.menu.getter : some ()
#45	0x00000001d242a9d4 in closure #1 () -> <<opaque return type of SwiftUI.MenuPickerStyle.Body.menu : some>>.0 in SwiftUI.MenuPickerStyle.Body.value.getter : some ()
#46	0x00000001d18bec48 in SwiftUI.StaticIf< where τ_0_1: SwiftUI.View, τ_0_2: SwiftUI.View>.init<τ_0_0 where τ_0_0 == SwiftUI.StyleContextAcceptsPredicate<τ_1_0>, τ_1_0: SwiftUI.StyleContext>(in: τ_1_0, then: () -> τ_0_1, else: () -> τ_0_2) -> SwiftUI.StaticIf<SwiftUI.StyleContextAcceptsPredicate<τ_1_0>, τ_0_1, τ_0_2> ()
#47	0x00000001d242a720 in SwiftUI.MenuPickerStyle.Body.value.getter : some ()
#48	0x00000001d2bc0cd0 in implicit closure #1 (Swift.UnsafeMutableRawPointer, __C.AGAttribute) -> () in closure #1 () -> (Swift.UnsafeMutableRawPointer, __C.AGAttribute) -> () in closure #1 (Swift.UnsafePointer<τ_1_0>) -> AttributeGraph.Attribute<τ_0_0> in AttributeGraph.Attribute.init<τ_0_0 where τ_0_0 == τ_1_0.Value, τ_1_0: AttributeGraph.Rule>(τ_1_0) -> AttributeGraph.Attribute<τ_0_0> ()
#49	0x00000001bdf35f7c in AG::Graph::UpdateStack::update ()
#50	0x00000001bdf367cc in AG::Graph::update_attribute ()
#51	0x00000001bdf3e438 in AG::Graph::input_value_ref_slow ()
#52	0x00000001bdf53f9c in AGGraphGetValue ()
#53	0x00000001d2b37244 in SwiftUI.StaticBody.container.getter : τ_0_0.Container ()
#54	0x00000001d2b377ec in closure #1 () -> () in SwiftUI.StaticBody.updateValue() -> () ()
#55	0x00000001d2b37404 in SwiftUI.StaticBody.updateValue() -> () ()
#56	0x00000001d2bc4010 in partial apply forwarder for implicit closure #1 (Swift.UnsafeMutableRawPointer, __C.AGAttribute) -> () in closure #1 () -> (Swift.UnsafeMutableRawPointer, __C.AGAttribute) -> () in closure #1 (Swift.UnsafePointer<τ_1_0>) -> AttributeGraph.Attribute<τ_0_0> in AttributeGraph.Attribute.init<τ_0_0 where τ_0_0 == τ_1_0.Value, τ_1_0: AttributeGraph.StatefulRule>(τ_1_0) -> AttributeGraph.Attribute<τ_0_0> ()
#57	0x00000001bdf35f7c in AG::Graph::UpdateStack::update ()
#58	0x00000001bdf367cc in AG::Graph::update_attribute ()
#59	0x00000001bdf3e438 in AG::Graph::input_value_ref_slow ()
#60	0x00000001bdf53f9c in AGGraphGetValue ()
#61	0x00000001d2dd9348 in SwiftUI.DynamicViewList.updateValue() -> () ()
#62	0x00000001d2bc4010 in partial apply forwarder for implicit closure #1 (Swift.UnsafeMutableRawPointer, __C.AGAttribute) -> () in closure #1 () -> (Swift.UnsafeMutableRawPointer, __C.AGAttribute) -> () in closure #1 (Swift.UnsafePointer<τ_1_0>) -> AttributeGraph.Attribute<τ_0_0> in AttributeGraph.Attribute.init<τ_0_0 where τ_0_0 == τ_1_0.Value, τ_1_0: AttributeGraph.StatefulRule>(τ_1_0) -> AttributeGraph.Attribute<τ_0_0> ()
#63	0x00000001bdf35f7c in AG::Graph::UpdateStack::update ()
#64	0x00000001bdf367cc in AG::Graph::update_attribute ()
#65	0x00000001bdf3e438 in AG::Graph::input_value_ref_slow ()
#66	0x00000001bdf53f9c in AGGraphGetValue ()
#67	0x00000001d2bd8ef0 in SwiftUI.DynamicLayoutViewAdaptor.updatedItems() -> Swift.Optional<SwiftUI.ViewList> ()
#68	0x00000001d287a29c in generic specialization <SwiftUI.DynamicLayoutViewAdaptor> of SwiftUI.DynamicContainerInfo.updateItems(disableTransitions: Swift.Bool) -> (changed: Swift.Bool, hasDepth: Swift.Bool) ()
#69	0x00000001d2879370 in generic specialization <SwiftUI.DynamicLayoutViewAdaptor> of SwiftUI.DynamicContainerInfo.updateValue() -> () ()
#70	0x00000001d28983ec in generic specialization <SwiftUI.DynamicContainer.Info, SwiftUI.DynamicContainerInfo<SwiftUI.DynamicLayoutViewAdaptor>> of implicit closure #1 (Swift.UnsafeMutableRawPointer, __C.AGAttribute) -> () in closure #1 () -> (Swift.UnsafeMutableRawPointer, __C.AGAttribute) -> () in closure #1 (Swift.UnsafePointer<τ_1_0>) -> AttributeGraph.Attribute<τ_0_0> in AttributeGraph.Attribute.init<τ_0_0 where τ_0_0 == τ_1_0.Value, τ_1_0: AttributeGraph.StatefulRule>(τ_1_0) -> AttributeGraph.Attribute<τ_0_0> ()
#71	0x00000001bdf35f7c in AG::Graph::UpdateStack::update ()
#72	0x00000001bdf367cc in AG::Graph::update_attribute ()
#73	0x00000001bdf44068 in AG::Subgraph::update ()
#74	0x00000001d2fc51d8 in SwiftUI.ViewGraph.updateOutputs(async: Swift.Bool) -> () ()
#75	0x00000001d2fbb8a8 in closure #2 () -> () in closure #1 () -> () in SwiftUI.ViewRendererHost.render(interval: Swift.Double, updateDisplayList: Swift.Bool, targetTimestamp: Swift.Optional<SwiftUI.Time>) -> () ()
#76	0x00000001d2fbb6c0 in closure #1 () -> () in SwiftUI.ViewRendererHost.render(interval: Swift.Double, updateDisplayList: Swift.Bool, targetTimestamp: Swift.Optional<SwiftUI.Time>) -> () ()
#77	0x00000001d2fb951c in SwiftUI.ViewRendererHost.render(interval: Swift.Double, updateDisplayList: Swift.Bool, targetTimestamp: Swift.Optional<SwiftUI.Time>) -> () ()
#78	0x00000001d23548c0 in SwiftUI._UIHostingView.layoutSubviews() -> () ()
#79	0x00000001d2354924 in @objc SwiftUI._UIHostingView.layoutSubviews() -> () ()
#80	0x000000018601c0c4 in -[UIView(CALayerDelegate) layoutSublayersOfLayer:] ()
#81	0x000000018b06ceb0 in CA::Layer::layout_if_needed ()
#82	0x000000018b077c34 in CA::Layer::layout_and_display_if_needed ()
#83	0x000000018afacc58 in CA::Context::commit_transaction ()
#84	0x000000018afdb468 in CA::Transaction::commit ()
#85	0x0000000185abb7b4 in __34-[UIApplication _firstCommitBlock]_block_invoke_2 ()
#86	0x000000018041b0ec in __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__ ()
#87	0x000000018041a824 in __CFRunLoopDoBlocks ()
#88	0x00000001804150c8 in __CFRunLoopRun ()
#89	0x0000000180414960 in CFRunLoopRunSpecific ()
#90	0x0000000190183b10 in GSEventRunModal ()
#91	0x0000000185aa2b40 in -[UIApplication _run] ()
#92	0x0000000185aa6d38 in UIApplicationMain ()
#93	0x00000001d1e2eab4 in closure #1 (Swift.UnsafeMutablePointer<Swift.Optional<Swift.UnsafeMutablePointer<Swift.Int8>>>) -> Swift.Never in SwiftUI.KitRendererCommon(Swift.AnyObject.Type) -> Swift.Never ()
#94	0x00000001d1e2e7dc in SwiftUI.runApp<τ_0_0 where τ_0_0: SwiftUI.App>(τ_0_0) -> Swift.Never ()
#95	0x00000001d1b70c8c in static SwiftUI.App.main() -> () ()
#96	0x0000000103582a88 in static ExampleApp.$main() ()
#97	0x0000000103582b38 in main at /Users/awatt/Development/swift-perception/Example/Example/ExampleApp.swift:4

Checklist

  • I have determined that this bug is not reproducible using Swift's observation tools. If the bug is reproducible using the @Observable macro or another tool from the Observation framework, please file it directly with Apple.
  • If possible, I've reproduced the issue using the main branch of this package.
  • This issue hasn't been addressed in an existing GitHub issue or discussion.

Expected behavior

No warning for this case.

Actual behavior

Spurious runtime warnings.

Steps to reproduce

I was able to reproduce with this view dropped into the Perception example app. The warning is triggered on first render.

@Perceptible
class OptionModel {
    var option: Int = 0
}

struct OptionView: View {
    @Perception.Bindable var model: OptionModel
    
    var body: some View {
        WithPerceptionTracking {
            Picker("Option", selection: $model.option) {
                Text("A").tag(0)
                Text("B").tag(1)
            }
        }
    }
}

Perception version information

1.3.5

Destination operating system

iOS 18.0

Xcode version information

Version 16.0 (16A242d)

Swift Compiler version information

swift-driver version: 1.115 Apple Swift version 6.0 (swiftlang-6.0.0.9.10 clang-1600.0.26.2)
Target: arm64-apple-macosx14.0
@watt watt added the bug Something isn't working due to a bug in the library. label Oct 1, 2024
@chwo
Copy link

chwo commented Oct 17, 2024

Perceptible state was accessed but is not being tracked. Track changes to state by wrapping your view in a 'WithPerceptionTracking' view. This must also be done for any escaping, trailing closures, such as 'GeometryReader', LazyVStack (and all lazy views), navigation APIs ('sheet', 'popover', 'fullScreenCover', etc.), and others.

I am experiencing the same issue in the context of a TCA feature. The warning is raised by the selection binding of the Picker. When building with Xcode 15.4.0 there is no runtime waring, but when building with Xcode 16.0.0 there is a runtime warning. Wrapping the Picker in WithPerceptionTracking does not solve the warning.

@Reducer
struct ContentFeature: Reducer {
    @ObservableState
    struct State: Equatable {
        let indices = [0, 1, 2]
        var index: Int = 0
    }

    enum Action {
        case indexChanged(Int)
    }

    var body: some Reducer<State, Action> {
        Reduce { state, action in
            switch action {
            case .indexChanged(let index):
                state.index = index
                return .none
            }
        }
    }
}

struct ContentView: View {
    @Perception.Bindable var store: StoreOf<ContentFeature>

    var body: some View {
        List {
            Label("Hello World", systemImage: "globe")

            WithPerceptionTracking {
                Picker("Picker", selection: $store.index.sending(\.indexChanged)) {
                    WithPerceptionTracking {
                        ForEach(store.indices, id: \.self) { index in
                            Text("Number \(index)")
                                .tag(index)
                        }
                    }
                }
            }
        }
    }
}

PickerPerceptionTrackingSample.zip

Technical information

  • Deployment target: iOS 16
  • Xcode version 15.4.0 and 16.0.0
  • TCA version 1.15.1
  • swift-perception version 1.3.5

@vladyslavsosiuk
Copy link

We are encountering the same issue. For now we managed to workaround the warning with a Picker wrapper type based on @KaiOelfke solution from a slack thread:

public struct _Picker<Label, SelectionValue, Content>: View
where Label: View, SelectionValue: Hashable, Content: View {
    let label: Label
    let content: Content
    let selection: Binding<SelectionValue>
    
    public init(
        _ titleKey: LocalizedStringKey,
        selection: Binding<SelectionValue>,
        @ViewBuilder content: () -> Content
    ) where Label == Text {
        self.label = Text(titleKey)
        self.content = content()
        self.selection = selection
    }
    
    public var body: some View {
        _PerceptionLocals.$skipPerceptionChecking.withValue(true) {
            Picker(selection: selection, content: { content }, label: { label })
        }
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working due to a bug in the library.
Projects
None yet
Development

No branches or pull requests

3 participants