0
votes

First iOS app. If items in the VM is replaced with an Array with one less element, I get the following exception, which placing exception breakpoints gives no useful information about:

*** Terminating app due to uncaught exception 'NSRangeException', reason: 'Index (1176) out of bounds (1176)'

View:

struct LibrarySongsPageView: View {
    
    @StateObject var viewModel = LibrarySongsViewModel()
    
    var body: some View {
        print(viewModel.items.count)
        return List(viewModel.items, id: \.persistentID) { mediaItem in
            HStack {
                if let art = mediaItem.artwork, let uiImg = art.image(at: CGSize(width: 60, height: 60)) {
                    Image(uiImage: uiImg)
                        .toIcon()
                        .padding(.trailing)
                } else {
                    Image(systemName: "music.note")
                }
                Text(mediaItem.title ?? "Unknown")
                    .font(.title2)
            }
        }.navigationTitle("Songs")
    }
}

VM:

class BaseLibraryViewModel: ObservableObject {
    let provider = LibraryProvider.instance
    
    @Published var items = [MPMediaItem]()
    
    let sortOrder = SortOrder.title
    var subscription: AnyCancellable!
    
    init() {
        subscription = provider.setActive(active: true, type: .songs)!.sink { [weak self] items in
            self?.items = items
        }
    }
}
class LibrarySongsViewModel: BaseLibraryViewModel {}

I have already fixed the problem by changing the first lines of the body to:

return List(viewModel.items.indices, id: \.self) { index in
            let mediaItem = viewModel.items[index]
}

but I have no idea why the first version produces the exception. I checked the viewModel.items.count and po viewModel.items when the view body property is being accessed and it looks the the Array is the updated array with one less item than previous that I am expecting, yet the exception is still thrown during body access.

Can someone please explain?

1

1 Answers

0
votes

I’m guessing there is a concurrency issue.

Before the .sink in your subscription, put .receiveOn(DispatchQueue.main)