SwiftUI View and @FetchRequest predicate with vari

2020-02-19 09:20发布

问题:

I have a view showing messages in a team that are filtered using @Fetchrequest with a fixed predicate 'Developers'.

struct ChatView: View {

@FetchRequest(
    sortDescriptors: [NSSortDescriptor(keyPath: \Message.createdAt, ascending: true)],
    predicate: NSPredicate(format: "team.name == %@", "Developers"),
    animation: .default) var messages: FetchedResults<Message>

@Environment(\.managedObjectContext)
var viewContext

var body: some View {
    VStack {
        List {
            ForEach(messages, id: \.self) { message in
                VStack(alignment: .leading, spacing: 0) {
                    Text(message.text ?? "Message text Error")
                    Text("Team \(message.team?.name ?? "Team Name Error")").font(.footnote)
                }
            }...

I want to make this predicate dynamic so that when the user switches team the messages of that team are shown. The code below gives me the following error

Cannot use instance member 'teamName' within property initializer; property initializers run before 'self' is available

struct ChatView: View {

@Binding var teamName: String

@FetchRequest(
    sortDescriptors: [NSSortDescriptor(keyPath: \Message.createdAt, ascending: true)],
    predicate: NSPredicate(format: "team.name == %@", teamName),
    animation: .default) var messages: FetchedResults<Message>

@Environment(\.managedObjectContext)
var viewContext

...

I can use some help with this, so far I'm not able to figure this out on my own.

回答1:

had the same problem, and a comment of Brad Dillon showed the solution:

var predicate:String
var wordsRequest : FetchRequest<Word>
var words : FetchedResults<Word>{wordsRequest.wrappedValue}

    init(predicate:String){
        self.predicate = predicate
        self.wordsRequest = FetchRequest(entity: Word.entity(), sortDescriptors: [], predicate:
            NSPredicate(format: "%K == %@", #keyPath(Word.character),predicate))

    }

in this example, you can modify the predicate in the initializer.



回答2:

Another possibility is:

struct ChatView: View {

@Binding var teamName: String

@FetchRequest() var messages: FetchedResults<Message>

init() {
    let fetchRequest: NSFetchRequest<Message> = Message.fetchRequest()
    fetchRequest.sortDescriptors = NSSortDescriptor(keyPath: \Message.createdAt, ascending: true)
    fetchRequest = NSPredicate(format: "team.name == %@", teamName),
    self._messages = FetchRequest(fetchRequest:fetchRequest, animation: .default)
}

...


回答3:

Modified @FKDev answer to work, as it throws an error, I like this answer because of its cleanliness and consistency with the rest of SwiftUI. Just need to remove the parentheses from the fetch request. Although @Antoine Weber answer works just the same.

But I am experience an issue with both answers, include mine below. This causes a weird side effect where some rows not related to the fetch request animate off screen to the right then back on screen from the left only the first time the fetch request data changes. This does not happen when the fetch request is implemented the default SwiftUI way.

UPDATE: Fixed the issue of random rows animating off screen by simply removing the fetch request animation argument. Although if you need that argument, i'm not sure of a solution. Its very odd as you would expect that animation argument to only affect data related to that fetch request.

@Binding var teamName: String

@FetchRequest var messages: FetchedResults<Message>

init() {

    var predicate: NSPredicate?
    // Can do some control flow to change the predicate here
    predicate = NSPredicate(format: "team.name == %@", teamName)

    self._messages = FetchRequest(
    entity: Message.entity(),
    sortDescriptors: [],
    predicate: predicate,
//    animation: .default)
}