Hi all,
I've been experiencing some issues using SwiftUI's List on macOS Monterey and macOS Ventura Beta 3, where my app will lock up with an error if I add and select items at the same time:
NSOutlineView error inserting child indexes <NSIndexSet: 0x600003401ea0>[number of indexes: 1 (in 1 ranges), indexes: (10)] in parent 0x0 (which has 9 children).
Minimal Reproducible Example
// Built with Xcode 14 Beta 3
struct MyData: Identifiable {
let id = UUID()
var text: String
}
struct ContentView: View {
@State var data: [MyData] = []
@State var selection = Set<UUID>()
var body: some View {
NavigationView {
List(data, selection: $selection) { item in
Text(item.text)
}
Button("Press Me!") {
DispatchQueue(label: "Worker").async {
for i in 0..<500 {
DispatchQueue.main.async {
data.append(MyData(text: String(i)))
}
usleep(100_000) // 0.1 seconds
}
}
}
}
}
}
As you can see, the app is just an empty Sidebar. When you press the button, a worker thread starts and it adds a new item to the Sidebar every 0.1 seconds (protected by DispatchQueue.main.async).
You can reproduce the example like this:
- Run the app
- Press the "Press Me!" button
- As the items flow into the Sidebar, start randomly selecting some of those items in the Sidebar. Scrolling at the same time also helps in reproducing this issue.
- Eventually, the app should lock up and an error message should appear in the console.
You can see a video of how to reproduce here: https://imgur.com/a/GqjILi2
What I've tried
- I've tried replacing the
DispatchQueuewithTaskandTask.detached, and usingtry await Task.sleepinstead ofusleep, but this doesn't seem to help at all. - I've tried moving the work and
datainto a separateViewModelclass. The same issue occurs. - I've tried completely replacing the "Worker" queue, and instead just using
DispatchQueue.main.asyncAfter, and the issue still occurs. - I've tried using
Timer.scheduledTimerinstead of running in a background queue, and this actually works. However, in my real code base, I'm actually processing anAsyncStreamthat continuously emits data. Theusleepin the MRE is just to simulate work.
I'm really puzzled as to what's going on. It looks like this code is thread safe, but Timer.scheduledTimer works and it callbacks on the main queue.
Could this potentially be a bug with SwiftUI macOS? If so are there any ways to alleviate this issue?
Update: This post mentions a warning
Update NavigationAuthority bound path tried to update multiple times per frame
Which is similar to some of the log messages I'm also getting. They also mention how the app often freezes when navigated with a NavigationLink, similar to this issue.
Note: This post is mostly a duplicate of my StackOverflow post here.