How to safely access Core data NSManagedObject attributes from a SwiftUI view using swift concurrency model?

How do I safely access Core data NSManagedObject attributes from SwiftUI view using the swift concurrency model.

My understanding is we need to enclose any access to NSManageObject attributes in await context.perform {}. But if I use it anywhere in a view be that in the body(), or init() or .task() modifier I get the following warnings or errors:

for .task{} modifier or if within any Task {}:

Passing argument of non-sendable type 'NSManagedObjectContext.ScheduledTaskType' outside of main actor-isolated context may introduce data races

this happens even if I create a new context solely for this access.

if within body() or init():

'await' in a function that does not support concurrency

but we cant set body or init() to async

an example of the code is something along the lines of:

var attributeString: String = ""
let context = PersistentStore.shared.persistentContainer.newBackgroundContext()
await context.perform {
    attributeString = nsmanagedObject.attribute!
}

Replies

Passing argument of non-sendable type 'NSManagedObjectContext.ScheduledTaskType' outside of main actor-isolated context may introduce data races

What version of Xcode are you using?

The following code compiles for me:

import Foundation
import CoreData

func test(context: NSManagedObjectContext) {
    Task {
        await context.perform {
            print("Hello Cruel World!")
        }
    }
}

print("just a demo")

This doesn’t use NSManagedObjectContext.ScheduledTaskType, so I’m curious how you managed to get that error.

For context (hey hey!), I’m using Xcode 15.0 and compiling in a macOS command-line tool project.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

  • I get the following warnings with this code (I put the func in a View) in Version 15.0.1 (15A507), iOS17.0 target: "Passing argument of non-sendable type '() -> ()' outside of main actor-isolated context may introduce data races", "Passing argument of non-sendable type 'NSManagedObjectContext.ScheduledTaskType' outside of main actor-isolated context may introduce data races".

    I have SWIFT_STRICT_CONCURRENCY set to complete. and the com.apple.CoreData.ConcurrencyDebug complier flag set to 1.

Add a Comment

You probably want to use performAndWait in body, those closures don't support concurrency so you need to remove that from the scope. perform() and performAndWait were both introduced before Swift Concurrency was a thing. The scheduled task variant is meant to work with SwiftConcurrency.

let context = //new background context
context.performAndWait {
    //do your work
}
  • my understanding is the old performAndWait uses GCD and that we shouldn't be mixing the new swift concurrency model with gcd? So ive been migrating all my 'performAndWait' code over to swift concurrency. Does 'context.performAndWait' and 'await context.perform' play well together?

    if so no problem using performAndwait in body. Using 'await perform' in .task{} gives the warnings. I have SWIFT_STRICT_CONCURRENCY set to complete. and the com.apple.CoreData.ConcurrencyDebug complier flag set to 1.

  • I think it's best to choose the technique that is supported by the lexical scope you're working in. If your lexical scope doesn't support async / await, you need to use an alternative.

  • I mean if I have 1 thread using await context.perform and another using context.performAndWait accessing the same core data object on the same context, will I get the concurrency issues that they are supposed to stop?

Add a Comment