0

I want to separate my functions from SwiftUI Views. I have this function:

import Foundation
import CoreData

func deleteAll() {
    let fetchRequestItems: NSFetchRequest<NSFetchRequestResult> = Item.fetchRequest()
    let deleteRequestItems = NSBatchDeleteRequest(fetchRequest: fetchRequestItems)
    
    do {
        try viewContext.save()
        try viewContext.execute(deleteRequestItems)
        viewContext.reset()
    } catch let error as NSError {
        print(error)
    }
}

I am getting an error on 3 lines with viewContext:

Cannot find 'viewContext' in scope

The same function works when it's inside a SwiftUI View. But the views have viewContext injected like this:

ContentView()
.environment(\.managedObjectContext, persistenceController.container.viewContext)

So the question is: How to inject/use Core Data Context (viewContext) inside functions in separate Swift files?

EDIT: Persistence.swift is from a SwiftUI 2.0 template and looks like this:

import CoreData

struct PersistenceController {
    static let shared = PersistenceController()

    static var preview: PersistenceController = {
        let result = PersistenceController(inMemory: true)
        let viewContext = result.container.viewContext
        for _ in 0..<10 {
            let newItem = Item(context: viewContext)
            newItem.timestamp = Date()
        }
        
        do {
            try viewContext.save()
        } catch {
            // Replace this implementation with code to handle the error appropriately.
            // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
            let nsError = error as NSError
            fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
        }
        return result
    }()

    let container: NSPersistentCloudKitContainer

    init(inMemory: Bool = false) {
        container = NSPersistentCloudKitContainer(name: "Moneto")
        if inMemory {
            container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
        }
        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            if let error = error as NSError? {
                // Replace this implementation with code to handle the error appropriately.
                // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.

                /*
                Typical reasons for an error here include:
                * The parent directory does not exist, cannot be created, or disallows writing.
                * The persistent store is not accessible, due to permissions or data protection when the device is locked.
                * The device is out of space.
                * The store could not be migrated to the current model version.
                Check the error message to determine what the actual problem was.
                */
                fatalError("Unresolved error \(error), \(error.userInfo)")
            }
        })
    }
}
2
  • 1
    deleteAll(context: NSManagedObjectContext) { … } Commented Jul 3, 2021 at 9:13
  • It works! Thanks, @JoakimDanielson :) Can you send it as answer, please, so I could accept it? 2 small comments. 1. It's deleteAll(viewContext: NSManagedObjectContext) { … } in my case. 2. When I run function, I should use deleteAll(viewContext: viewContext) Commented Jul 3, 2021 at 16:50

2 Answers 2

3

You should inject the managed context into your function

func deleteAll(viewContext: NSManagedObjectContext) {
    //…
}

This also gives you the advantage that you can choose which context to delete from if you are working with background or child contexts

Sign up to request clarification or add additional context in comments.

Comments

0

Add this var in your view

@Environment(\.managedObjectContext) var viewContext

10 Comments

Thanks, but I have it in views. To be precise, I have: @Environment(\.managedObjectContext) private var viewContext. But even without private it doesn't work. Please notice that my function is in separate file. Not in a SwiftUI view. It's a Swift file with the function alone.
No. it's not good. please move your function to your view or view model.
So I cannot keep them in separate files? What's the proper way to break your view to smaller pieces if you have many functions there?
use extension. create an extension of your view like extension ContentView {...} and add this extension in separate file. A global function is not a good idea.
You can also make your view context static from Persistence.swift file and then use it everywhere .
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.