Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cocoa Touch Framework & CoreData

I'm trying to use CoreData inside my Cocoa Touch Framework. I create a simple project as an example: https://github.com/JakubMazur/SO41698466

Basically I do it in steps:

  1. Create a framework with Unit Tests
  2. Add CoreData to project someDataModel
  3. Fill CoreData with some dummy Entity and change module to Current Product Module
  4. Then I created SomeClass to start with and it's basically called :

.

public class func entityCreation() {
    Entity(context: CoreDataClass().persistentContainer.viewContext)
}

So with this lazy autogenerated code from CoreData this should create model.

  1. In test I wrote:

.

func testExample() {
    SomeClass.entityCreation()
}
  1. And fire the test
  2. In CoreDataClass I put a breakpoint below line: let container = NSPersistentContainer(name: "someDataModel") and then I see in console:

[error] error: Failed to load model named someDataModel CoreData: error: Failed to load model named someDataModel

So I have a 2 questions:

  1. Is it possible to include Core Data inside framework
  2. If 1 is true maybe I should use different NSPersistentContainer name? There is a way to check what should I put there?

Download a project here: https://github.com/JakubMazur/SO41698466

Solution Found And Added below

let modelURL = Bundle(for: type(of: self)).url(forResource: "someDataModel", withExtension: "momd")! 
let managedObjectModel = NSManagedObjectModel(contentsOf: modelURL) 
let container = NSPersistentContainer(name: "someDataModel", managedObjectModel: managedObjectModel!)
like image 327
Jakub Avatar asked Jan 17 '17 13:01

Jakub


1 Answers

TLDR;

Apple recommends that you subclass NSPersistentContainer in your framework. If you do this then the framework which the subclass is defined in will be searched.

So you can simply:

final class PersistentContainer: NSPersistentContainer { }

Then use PersistentContainer rather than NSPersistentContainer in your framework.

Explanation

The name initializer only searches through the main bundle.

To find the managed object model in a different framework you can get the model URL as suggested in the comments.

However, as an alternative, you can also subclass the NSPersistentContainer in your framework, then the name initializer will work.

Here is an excerpt from the WWDC Core Data Best Practices video:

... let's say we want to factor our model layer into its own framework.

We can do that by creating a new framework target in Xcode and moving our code into it.

It's all super easy, but when we move our model into the new target, in the built product, targets move from the app into the new framework, which is what's supposed to happen, but now NSPersistentContainer doesn't know where to find our model anymore.

This is because it only checks the main bundle by default.

Why stop there?

Well, searching all of the app's bundles could get really slow for a complicated app and it's not a cost you want to pay every time you spin up a stack.

How do we fix this?

Well, we could resuscitate the model out of the framework bundle ourselves and use one of the container's other initializers, like one that takes an explicit managed object model, but NSPersistentContainer actually has a way for you to change which bundle it searches.

See, NSPersistentContainer knows when it's been subclassed and will use the type of the subclass as a hint when it looks for the model.

All we need to do to take advantage of this is to create a subclass.

It doesn't even need to have anything in it.

Then, any code setting up through the container that wants to use our model can just adopt that subclass and the persistent container will check in our frameworks bundle for our model instead.

https://asciiwwdc.com/2018/sessions/224

like image 115
josh-fuggle Avatar answered Nov 13 '22 17:11

josh-fuggle