xcodeswiftuiswiftdata

No exact matches in call to macro 'Preview' when trying to pre-select a radio button


I'm trying to understand why SwiftUI Preview won't let me do this:

struct ItemCreateView: View {
    
    @Environment(\.dismiss) var dismiss
    @Environment(\.modelContext) private var modelContext
    
    @Query private var collections: [Collection]
    let selectedCollection: Collection
    
    @State private var item = Item()
    @State private var selectedImage: PhotosPickerItem? = nil
    
    var body: some View {...}
}

#Preview {
    let config = ModelConfiguration(isStoredInMemoryOnly: true)
    let container = try! ModelContainer(for: Collection.self, configurations: config)
    
    for number in 1...10 {
        let col = Collection(name: "Test Collection \(number)")
        container.mainContext.insert(col)
    }
    
    let selectedCollection = container.mainContext.fetch(Collection.self).first!
    
    ItemCreateView(selectedCollection: selectedCollection).modelContainer(container)
}

The error is:

No exact matches in call to macro 'Preview'
Closure containing control flow statement cannot be used with
result builder 'PreviewMacroBodyBuilder'

However, if I add "Return" to the last item. It'd then complain: Cannot use explicit 'return' statement in the body of result builder 'ViewBuilder'

But everything works if I don't try to fetch a random element from the container.

#Preview {
    let config = ModelConfiguration(isStoredInMemoryOnly: true)
    let container = try! ModelContainer(for: Collection.self, configurations: config)
    
    for number in 1...10 {
        let col = Collection(name: "Test Collection \(number)")
        container.mainContext.insert(col)
    }
    
    let selectedCollection = Collection(name: "Test Collection 1")
    
    return ItemCreateView(selectedCollection: selectedCollection).modelContainer(container)
}

^ this works.

I'm not sure if I understand the correlation of everything here and I can't seem to find much online regarding that error.

Thank you for your help in advance.

Models for reference

@Model
final class Collection {
    var name: String
    @Relationship(inverse: \Item.collection) var items: [Item]
    var timestamp: Date
    
    init(name: String = "", items: [Item] = [], timestamp: Date = Date()) {
        self.name = name
        self.items = items
        self.timestamp = timestamp
    }
}

Solution

  • The code being in a macro, and als in a result builder makes the compiler produce very bad error messages. Let's move everything out of the #Preview macro, and put it in a regular @ViewBuilder function.

    @ViewBuilder
    @MainActor
    func f() -> some View {
        // everything in #Preview goes here...
    }
    

    Now there is an error at the let selectedCollection line

    Cannot convert value of type Collection.Type to expected argument type FetchDescriptor<T>

    fetch doesn't take a metatype. You should pass in a FetchDescriptor<Collection>().

    Now we get

    Call can throw, but it is not marked with 'try' and the error is not handled

    So just add try!.

    Finally, there are no more errors, and you can put everything back in the #Preview.

    #Preview {
        let config = ModelConfiguration(isStoredInMemoryOnly: true)
        let container = try! ModelContainer(for: Collection.self, configurations: config)
        
        for number in 1...10 {
            let col = Collection(name: "Test Collection \(number)")
            container.mainContext.insert(col)
        }
        
        let selectedCollection = try! container.mainContext.fetch(FetchDescriptor<Collection>()).first!
        
        return ItemCreateView(selectedCollection: selectedCollection).modelContainer(container)
    }