14

I am trying to move my @fetchrequest property to an auxiliar class, which is not a View, but every time I try to do that, I get a bad instruction error.

Can anyone help me?

This is a sample of my code:

ViewModel:

class ViewModel {
    @FetchRequest(entity: Teste.entity(), sortDescriptors: []) var teste: FetchedResults<Teste>
}

View:


struct ContentView: View {

    let viewModel: ViewModel

    init(viewModel: ViewModel) {
        self.viewModel = viewModel
    }

    var body: some View {

        List(viewModel.teste) { item in // error happens in this line
            Text(item.id ?? "")
        }

    }
}

Thank you!

Laura Corssac
  • 1,217
  • 1
  • 13
  • 23

2 Answers2

13

I came across the your question while trying to figure out how achieve a similar result myself. I'm guessing you may have cracked this by now, but for the benefit of those stumbling across the same question I'll have a go at answering, or at least pointing at where the answer might be be found :-)

As has been mentioned the @FetchRequest property wrapper doesn't work outside of View components (which seems a bit of an odd functional omission)

The best alternative I've found is to implement essentially the same thing with a Combine publisher for updates from the NSFetchedResultsController as suggested by Apostolos in his answer here and detailed in a linked Medium post

I've put together a simple proof of concept example that tests and demonstrates the approach with iOS14 and its new life cycle management. This demo app can be found in my GitHub repo over here

Good luck.

shufflingb
  • 1,847
  • 16
  • 21
  • Thank you very much for this! It's close to what I did with UserDefaults and it solves my last concern with CoreData. I'm curious – how would you suggest to break down AppModel? Should several FooModel classes have their own NSPersistentContainer instances? Should they just list static methods with business logic while @Published props go into the main AppModel? Should the logic go into the entity extensions? – Viktor Sec Jun 05 '21 at 23:37
  • Hi Viktor, Longish answer, so will have to split over multiple comments >> Should several FooModel classes have their own NSPersistentContainer instances? 1 of 2 I think by default no. I'm using the VMM architecture, so my FooModel classes are the View Models that drive the pure Views. What's working for me is to use a single NSPersistentContainer by default in the App Model. But to then fork child contexts from it for any View Model where the transactional isolation on the Core Data is helpful, e.g. a View that needs editing with roll back, new item entry etc. – shufflingb Jun 07 '21 at 14:46
  • >> Should several FooModel classes have their own NSPersistentContainer instances? 2 of 2 // In terms of implementation, all of the App Model's Core Data "Intents" are implemented as pairs. One is a static method that takes a MOC parameter (and is only exposed iff necessary). The other is an App Model instance method that calls the static method with the App Model's (and parent) instance of the MOC. // The App Model has a create Child Context method that View Model's can use with the static Core Data "Intents" if they need the previously mentioned transactional isolation. – shufflingb Jun 07 '21 at 14:49
  • >> ..@Published props go into the main AppModel? If they are App-level Sources of Truth, then I think yes, @Published props should go in the App Model. However, if there's any doubt, and it's convenient - use Combine Publisher functions directly in the View Models - they are more flexible and possibly a bit easier to refactor later if desired. – shufflingb Jun 07 '21 at 14:52
  • >Should the logic go into the entity extensions? Certainly can. I've got mine in AppModel+FooEntity.swift, AppModel+BarEntity.swift etc, makes it fairly easy to find things. – shufflingb Jun 07 '21 at 14:52
  • Finally, if using Combine to republish an existing "@Published" prop to another "@Published" prop say in a View Model, I'd strongly recommend using the @Published private(set) var item: Item with a separate func itemSet() {appModel.itemSet(...)} pattern for making changes. It's all too easy otherwise to setup infinite, App exploding loops otherwise :-) Good luck. – shufflingb Jun 07 '21 at 14:56
5

@FetchRequest is a DynamicProperty and latter is

/// Represents a stored variable in a `View` type that is dynamically
/// updated from some external property of the view. These variables
/// will be given valid values immediately before `body()` is called.
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
public protocol DynamicProperty {

So it is, at least, "out of design" to try to use it outside of View. If it is really use-case then use NSFetchRequest directly as previously in UIKit+CoreData and integrate results with SwiftUI View manually.

Asperi
  • 228,894
  • 20
  • 464
  • 690