0

I'm getting an unusual unrecognized selector sent to instance crash when trying to access a shared data controller object from inside an app shortcuts AppEntity definition. It seems to relate to [NSCFString bytes].

The data controller is declared in my main app and passed through the environment:

struct myApp: App {
    
    @StateObject var dataController: DataController
 
    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(dataController)
        }
    }

    init() {
        _dataController = StateObject(wrappedValue: DataController.shared)
}

In my app entity and widgets, it is accessed as the shared instance. For example, here is an abridged version of the app entity and query.

struct MyAppEntity: AppEntity {
    
    @Property(title: "Name")
    var name: String?

    // ... some code omitted
    
    struct MyAppEntityQuery: EntityQuery {
        
        // Option 1: Having data controller here flags a warning about it
        // not being a Sendable type
        let dataController = DataController.shared
        
        func entities(for identifiers: [MyAppEntity.ID]) async throws -> [MyAppEntity] {
            // Option 2: Having data controller here in the function is
            // transient, so no Sendable warning as above
            let dataController = DataController.shared
            let items = dataController.getAllItems()

            // ... use items to provide list for disambiguation
        }
        
        // ... some code omitted
    }
}

Note the two options for location of the data controller. Both cause crashes, and using it as a top-level property raises a warning that it is not Sendable like the rest of the struct.

The problem is that the dataController.getAllItems() method causes the crash when it in turn calls viewContext.fetch(Item.fetchRequest().


Behind the scenes, the getAllItems() method does a Core Data fetch to return the items. The crash seems to happen (intermittently) if deleting or adding items, and sometimes on app startup (iOS seems to instantiate app entities at startup, so this code gets called). Interestingly, using the actual app shortcut works fine!

I had tried with the SwiftUI @FetchRequest wrappers, like I use in the main app, but there is no managed object context in the environment here.

I wonder if the Core Data stack is not ready in the shared instance when the App Entity code runs? Any advice would be appreciated.


One solution I'm considering is to save a lightweight representation of the data from Core Data as JSON and look this up to get the items for disambiguation. This is what I do for widgets as they are in an extension. I previously used an intent extension but this is iOS16 app shortcuts which (AFAIK) run in the main app process.


Edit

I was able to work around this by avoiding calling core data, and instead using the JSON-encoded data in the app group container that I already use for widgets. However that does not explain the issue here.

Chris
  • 4,009
  • 3
  • 21
  • 52
  • 1
    "unexpected selector sent to instance" What's the full error message then? Shouldn't there be a class object and a method? – Larme Jun 22 '23 at 13:09
  • @Larme There wasn’t much else, but I used a breakpoint to localise it to `dataController.getAllItems()`, and within that, the `viewContext.fetch(Item.fetchRequest()` core data method. – Chris Jun 22 '23 at 15:39
  • It's not a `-[SomeClass someMethod]: unrecognized selector sent to instance`? It's "unexpected", not "unrecognized"? – Larme Jun 22 '23 at 15:40
  • @Larme You’re right. That was my mistake when manually transcribing. Have edited this. It is `unrecognized`. – Chris Jun 22 '23 at 15:45
  • But there is not the "-[SomeClass someMethod]:" part? – Larme Jun 22 '23 at 15:46
  • @Larme Sorry I forgot to include that - it was `[NSCFString bytes]` or similar (not at computer right now). – Chris Jun 22 '23 at 15:48
  • 2
    It's hard to tell, but `bytes` being a `(NS)Data` method, it doesn't exists on `NSString` (or inner SDK `NSCFString`). So at some point, the app think it's using a `(NS)Data` object, so it can call `bytes` on it (it's legit), but it's in fact a `(NS)String` object, hence the crash and the error. To dig up on it, it's hard right now without more infos... Except if you have. `bytes` property yourself somewhere on a custom class... – Larme Jun 22 '23 at 16:18
  • @Larme Thanks that's helpful. I have a limited grasp of the Obj-C inner workings. There is no `bytes` property in the custom class. All are declared as Core Data classes in the usual way. At least I have a workaround for now, even if it's just avoiding the issue. It does seem to be something related to Core Data given avoiding using it is fine. – Chris Jun 22 '23 at 17:23

0 Answers0