Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CoreData inside CocoaPods library

I've developed an iOS framework several months ago, and now I would like to use CocoaPods to develop and distribute it easily. Now I distribute it sending my .framework file, I know this is not the best way and because of that I would like to use CocoaPods.

My problem is, inside my framework I use CoreData and I cannot make it work with Pods. If I get my old .framework and import it into my app project, everything works nice and CoreData works like a charm.

Digging into the issue, it seems that the Database.momd is not found into the bundle.

I'm going to attach the piece of code inside the framework that fails:

- (NSManagedObjectModel *)managedObjectModel {    
    if (_managedObjectModel != nil) {
        return _managedObjectModel;
    }

    NSURL *modelURL = [[NSBundle bundleWithIdentifier:@"kemmitorz.myFramework"] URLForResource:@"Database" withExtension:@"momd"];

    // HERE modelURL RETURNS NIL with Pods

    _managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];

    return _managedObjectModel;
}

I know this could be caused by wrong framework configuration, or wrong Pods configuration, but I don't know what I'm missing.

Also, here is my myFramework.podspec file:

Pod::Spec.new do |s|
    s.name             = 'myFramework'
    s.version          = '0.1.0'
    s.summary          = 'A short description of myFramework.'

    .... 

    s.ios.deployment_target = '8.0'

    s.source_files = 'myFramework/**/*.{h,m}'

    s.frameworks = 'Foundation', 'Security', 'CFNetwork', 'CoreData'
    s.library = 'icucore'

    s.dependency 'SocketRocket'
    s.dependency 'AFNetworking'
    s.dependency 'OCMapper'
end

I've also tried to add {h,m,xcdatamodeld} to the type of source files unsuccessfully.

What I'm missing? Repeat, if I compile and import the framework on my own, everything works, so I suppose is something related with CocoaPods configuration.

like image 845
kemmitorz Avatar asked Jun 14 '16 12:06

kemmitorz


2 Answers

It depends on the cocoapods version you are using, The recommended way to do it in 1.0.x is to add the s.resource_bundles with a list of resource files that should be included in the bundle Cocoapods resource_bundles, for example:

...
s.source_files = 'myFramework/**/*.{h,m}'
s.resource_bundles = {'myFramework' => ['myFramework/*.xcdatamodeld']}
...

And then use some class of your framework to get the bundle and the resource:

guard let bundleURL = NSBundle(forClass: FrameworkClass.self).URLForResource("myFramework", withExtension: "bundle") else { throw Error }
guard let frameworkBundle = NSBundle(URL: bundleURL) else { throw Error }
guard let momURL = frameworkBundle.URLForResource("Database", withExtension: "momd") else { throw Error }

Things can get a little tricky here, but if you use the right paths everything works as expected.

like image 74
juanjo Avatar answered Sep 22 '22 07:09

juanjo


A little update for swift 5 as I had been struggling to get this to work.

In the podspec I needed to define the s.resource_bundles as

  s.resource_bundles = {
      'FrameworkNameHere' => ['FrameworkNameHere/**/*.{xcdatamodeld}']
  }

and in code to get the managed object model

guard let bundleUrl = Bundle(for: self).url(forResource: "FrameworkName", withExtension: "bundle"),
      let frameworkBundle = Bundle(url: bundleUrl),
      let modelUrl = frameworkBundle.url(forResource: "CoreDataModelName", withExtension: "momd"),
      let managedObjectModel = NSManagedObjectModel(contentsOf: modelUrl) else {
            fatalError("Managed object model not found")
}

Although this seems to be failing my unit tests that were originally built explicitly using the frameworks bundle id. I am thinking an initialiser for my core data class that allows me to do one or the other based on unit test/ app environment may be the answer.

like image 44
Trevor Avatar answered Sep 19 '22 07:09

Trevor