5

I have an app that stores some information in coredata and reads them. I'm writing a message extension of this application and I'd like to have this extension reading the same data but I always have empty response.

Here is the code I'm using in the main app:

context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
fetchImages(Date()){ (array, arrayData) in
    for image in array{
         imagesArray.insert(image, at:0)
    }
}

I'm using exactly the same code in the extension but it does not read the data. What I'm wondering about is that I'm not using the appGroupIdentifier anywhere in the code.

How can I do to achieve that?

Thanks.

Here is the code of fetchImages function:

func fetchImages(_ predicate:Date, completion:(_ array:[Image], _ arrayData:NSArray)->()){
    var arrData = [NSManagedObject]()
    var existingImages = [Image]()
    let request :NSFetchrequest<NSFetchrequestResult> = NSFetchrequest(entityName: "Photo")

    do {
        let results = try context?.fetch(request)
        var myImage = Image()
        if ((results?.count) != nil) {
            for result in results! {
                myImage.imageUrl = (resultat as! NSManagedObject).value(forKey:"url") as! String
                myImage.imageFileName = (resultat as! NSManagedObject).value(forKey:"imageFileName") as! String    
                existingImages.append(myImage)
                arrData.append(result as! NSManagedObject)
            }
        } else{
            print ("No photo.")
        }
        completion(existingImages, arrData as NSArray)

    } catch{
        print ("Error during CoreData request")
    }
}
radar
  • 500
  • 1
  • 6
  • 24
  • You are not calling the completion handler at all. By the way the parameter labels in the completion closure are meaningless in Swift 3+ – vadian Oct 10 '17 at 16:32
  • I removed the `completion` handler when cleaning the function. I edit the question to put it back. – radar Oct 10 '17 at 17:17
  • Is the context `nil`? By the way the context is supposed to be non-optional. And why for heavens sake do you cast specific `[NSManagedObject]` to unspecific `NSArray`. Don't do that. Use only Swift native collection types. – vadian Oct 10 '17 at 17:36
  • 2
    It looks like you're using the default stack that Xcode sets up for you. That won't work because the model is in the main app's container and inaccessible to the extension. Make sure the model is in a shared container, which is created when you turn on `App Groups`. – Wes Oct 10 '17 at 19:29
  • @vadian context is not nil. – radar Oct 10 '17 at 19:49
  • @Wes: any example I can follow? I have turned on `App Groups`. – radar Oct 10 '17 at 19:50
  • See what @Tom Harrington said – Wes Oct 10 '17 at 21:09

1 Answers1

4

Turning on app groups is the first step, but now you need to tell Core Data to use the app group.

First you get the location of the shared group container, from FileManager. Use containerURL(forSecurityApplicationGroupIdentifier:) to get a file URL for the directory.

You can use that URL without changes if you want. It's probably a good idea to create a subdirectory in it to hold your Core Data files. If you do that, add a directory name to the URL with the appendingPathComponent() method on URL. Then use FileManager to create the new directory with the createDirectory(at:withIntermediateDirectories:attributes:) method.

Now that you have a shared directory to use, tell NSPersistentContainer to put its files there. You do that by using NSPersistentStoreDescription. The initializer can take a URL that tells it where to store its data.

Your code will be something approximating this:

let directory: URL = // URL for your shared directory as described above
let containerName: String = // Your persistent container name

let persistentContainer = NSPersistentContainer(name: containerName)
let persistentStoreDirectoryUrl = directory.appendingPathComponent(containerName)

guard let _ = try? FileManager.default.createDirectory(at: persistentStoreDirectoryUrl, withIntermediateDirectories: true, attributes: nil) else {
    fatalError()
}

let persistentStoreUrl = persistentStoreDirectoryUrl.appendingPathComponent("\(containerName).sqlite")

let persistentStoreDescription = NSPersistentStoreDescription(url: persistentStoreUrl)
persistentContainer.persistentStoreDescriptions = [ persistentStoreDescription ]

persistentContainer.loadPersistentStores {
    ...
}
Tom Harrington
  • 69,312
  • 10
  • 146
  • 170
  • Thank you very much. That was really helping. Just need to mention that I'm not using any sql db and I'm storing my data directly in the context of the container. I used `persistentContainer.persistentStoreDescriptions` to make the container available for both main app and extension and it's working great now. Thanks again. – radar Oct 11 '17 at 15:01
  • Unless you set the `type` property on the `NSPersistentStoreDescription`, you're using SQLite through Core Data. Mostly it doesn't matter, but it's good to know about. It's common to use `.sqlite` in the filename but any valid filename is OK. – Tom Harrington Oct 11 '17 at 15:49
  • Good to know. Thanks again for the clear explanation. – radar Oct 11 '17 at 19:01