16

Ever since I am working with preferences (PreferenceKey,..), I receive this message in the console:

Bound preference _ tried to update multiple times per frame.

After countless times of research, I haven't found any way to silence it. So... since there is yet no question specifically to this warning, what would you think are possible reasons?

If not, can this warning be ignored or did I have to fix it?

Thank you so much!

(I have tried to find an example, but somehow I didn't get any warnings with an easy one...)

zeytin
  • 5,545
  • 4
  • 14
  • 38
Kai Zheng
  • 6,640
  • 7
  • 43
  • 66
  • 1
    I'm experiencing the same issue. In my case, I'm using anchorPreferences inside a List. https://stackoverflow.com/questions/65386898/swiftui-anchorpreference-inside-list – Aнгел Dec 21 '20 at 02:36
  • 1
    I'm updating a preference value from a GeometryReader which embeds a ScrollView tracking the scroll offset and experiencing the same issue, Xcode Version 13.0 beta 3. I would guess, the effect is not harmful. – CouchDeveloper Aug 01 '21 at 12:08

3 Answers3

17

The SwiftUI change handlers such as onPreferenceChange are called on an arbitrary thread. So if these changes impact your View, you should re-dispatch to ensure that you make those updates on the main thread:

.onPreferenceChange(MyPreferenceKey.self) { newValue in
    DispatchQueue.main.async {
        widget.value = newValue
    }
}
mbxDev
  • 837
  • 1
  • 7
  • 17
  • 11
    Hmm. This didn't solve it for me. In my case, I'm using the same PreferenceKey in numerous different Views. Is that a valid approach? Specifically, I'm determining the widths of certain Views for purpose of sizing sister Views. – Matthew Rips Mar 20 '21 at 18:16
  • `onPreferenceChange` is a subscriber - it doesn't update the preference value. So, this attempt likely cannot work. – CouchDeveloper Aug 01 '21 at 12:02
  • 3
    This is very important and also somewhat surprising, though upon consideration it makes sense. Still, it should be better documented. Also, it does seem like using the same preference key on multiple child view will elicit this behavior. Not sure what the correct approach would be. – kball Aug 19 '21 at 14:59
  • 7
    'The SwiftUI change handlers such as onPreferenceChange are called on an arbitrary thread' is false. Running 'Thread.isMainThread' in handler will give 'true'. Something else is going on here – Arutyun Enfendzhyan Jan 08 '22 at 17:06
  • 1
    Hi, friendly neighborhood tip: I was struggling with the same task as @MatthewRips (determine width). After testing the same implementation with List rather than ScrollView + LazyVStack, the messages went away (along with stuttering issues I was also experiencing). So I recommend testing with List, if you can. – blu-Fox Feb 03 '22 at 08:58
  • I was getting this warning (and a crash) when the device was rotated and the View attempted to resize. Dispatching to the main thread fixed the crash but did not eliminate the warning. – David B. Sep 13 '22 at 18:34
  • 1
    onPreferenceChange is called on main – zumzum Sep 28 '22 at 20:34
12

I think this answer from an Apple engineer describes the general issue:

It sounds like you have a cycle in your updates. For example, a GeometryReader that writes a preference, that causes the containing view to resize, which causes the GeometryReader to write the preference again. It’s important to avoid creating such cycles. Often that can be done by moving the GeometryReader higher in the view hierarchy so that its size doesn’t change and it can communicate size to its subviews instead of using a preference. I’m afraid I can’t give any more specific guidance than that without seeing your code, but hopefully, that helps you track down the issue!

https://www.bigmountainstudio.com/community/public/posts/65727-wwdc-2021-questions-answers-from-slack-the-unofficial-archive

At least it inspired me to solve the related bug in my case and make the warnings go away (almost :)).

kulich
  • 161
  • 1
  • 6
  • This helped inspire my solution. In my case instead of using a ```GeometryReader``` wrapping a ```ZStack``` I switched to using the ```GeometryReader``` inside a ```.background``` for the specific view that needed it, thus moving the view having a problem out of the ```GeometryReader``` (since it didnt need it anyway) – Chris Nov 03 '22 at 07:23
0

I am building a calendar app, based on the month it set the choseDate as initial value, when not using main queue, it goes into an infinite loop with messages like onChange(of: String) action tried to update multiple times per frame. After change to use main queue solved my problem.

                DispatchQueue.main.async {
                    dateChosen = Date()
                }
Harry
  • 1
  • 1