When saving to CoreData from Today Extension, Data only accesable from Widget - NOT from Main Application

1k views Asked by At

I'm writing an Application for iOS with Swift 3.

In my Main iOS App I'm using an NSFetchedResultsController to show saved items as an TableView. It's (of course) possible to add new items from another ViewController. -> Thats all working really awesome.

So I thought it would be great if I could add an new item really fast from an TodayWidget.

What I did:

  1. Created an SharedCode Framework and added AppGroup to my Main App and the Today Widget.

  2. Moved my CoreDataStack.swift Class, the .xcdatamodeled and my Item+CoreDataClass.swift and Item+CoreDataProperties.swift files to my SharedCode Framework.

  3. Sublcassed NSPersistentContainer to addforSecurityApplicationGroupIdentifier for my appGroupID

  4. Rewrote my CoreData code in my ViewController to use the created CoreDataStack.shared.managedContext

  5. Testing. AWESOME. My NSFetchedResultsController is working, and adding new Items works as expected. Nice. -> moveing on.

  6. In my Today Widget I'm using an simple NSFetchRequest to get the last entered Item from CoreData. Works perfectly!

  7. Added Buttons to modify the data and saving it to CoreData. Here I'm also using CoreDataStack.shared.managedContext and CoreDataStack.shared.save()

  8. Automatically reloading my Data AND?! AWESOME. All working very nice. Data is saved and new data is shown in the Today Extension. Also when I count the results from my NSFetchRequest the number of Items is increased.

NOW TO MY PROBLEM:

All the data that I'm adding through the Extension is not showing in my main iOS App. There when I'm fetching the Items from CoreData there are only the ones showing that I created in the main app. -.-

I have no Idea whats going wrong. It's like I have two different CoreDataStores. Also after I once added an Item trough the Widget - the Widget does not fetch an Item from the main App. Only the last one entered from the Widget.

- Here is my Code:

CoreDataStack.swift

public class CoreDataStack {

    public static let shared = CoreDataStack()
    public var errorHandler: (Error) -> Void = {_ in }

    //#1
    lazy var persistentContainer: PersistentContainer = {

        let container = PersistentContainer(name: ServiceConsts.modelName)
        var persistentStoreDescriptions: NSPersistentStoreDescription

        let storeUrl =  FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: ServiceConsts.appGroupID)!.appendingPathComponent("\(ServiceConsts.modelName).sqlite")

        let description = NSPersistentStoreDescription()
        description.shouldInferMappingModelAutomatically = true
        description.shouldMigrateStoreAutomatically = true
        description.url = storeUrl

        container.persistentStoreDescriptions = [NSPersistentStoreDescription(url:  FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: ServiceConsts.appGroupID)!.appendingPathComponent("\(ServiceConsts.modelName).sqlite"))]

        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            if let error = error as NSError? {
                fatalError("Unresolved error \(error), \(error.userInfo)")
            }
        })
        return container 
    }()

    //#2
    public lazy var managedContext: NSManagedObjectContext = {
        return self.persistentContainer.viewContext
    }()

    //#3
    // Optional
    public lazy var backgroundContext: NSManagedObjectContext = {
        return self.persistentContainer.newBackgroundContext()
    }()

    //#4
    public func performForegroundTask(_ block: @escaping (NSManagedObjectContext) -> Void) {
        self.managedContext.perform {
            block(self.managedContext)
        }
    }

    //#5
    public func performBackgroundTask(_ block: @escaping (NSManagedObjectContext) -> Void) {
        self.persistentContainer.performBackgroundTask(block)
    }

    //#6
    public func saveContext () {
        guard managedContext.hasChanges else { return }
        do {
            try managedContext.save()
        } catch let error as NSError {
            print("Unresolved error \(error), \(error.userInfo)")
        }
    }
}

PersistentContainer.swift

class PersistentContainer: NSPersistentContainer {
    internal override class func defaultDirectoryURL() -> URL {
        var url = super.defaultDirectoryURL()
        if let newURL =
            FileManager.default.containerURL(
                forSecurityApplicationGroupIdentifier: ServiceConsts.appGroupID) {
            url = newURL
        }
        return url
    }
}

Can anyone help? I have really no idea what I'm doing wrong.

Would be so awesome if anyone can give me a tip :)

Thanks <3

1

There are 1 answers

1
lks On BEST ANSWER

I finally fixed my issue <3.

It was so simple.

After hours and hours of testing testing crying and putting an axed through my MacBook ;) I found the thing thats what close killing me.

I testet if a simple Fetch Request in the Main App would get the added Items from the Today Extension. And that was working.

And than I saw it. In my Main App. The NSFetchedResultController. I used an cache.

So now I notify the Main App when a new Weight was added via the widget - and than I call my refresh function.

func handleRefresh(_ refreshControl: UIRefreshControl) {

    NSFetchedResultsController<NSFetchRequestResult>.deleteCache(withName: "weightCache")

    do {
        try fetchedResultsController.performFetch()
        setupView()
        self.tableView.reloadData()
    } catch let error as NSError {
        print("Fetching error: \(error), \(error.userInfo)")
    }

    refreshControl.endRefreshing()
}

So simple.

Just delete the cache.