📜 ⬆️ ⬇️

Using Multiple Persistent Store in Core Data

All iOS (and Mac OS X) developers know such a system framework as Core Data. This thing is a fairly powerful ORM (at least for a mobile platform).

Initially, our application used one database for all information that needed to be stored in the application. But as the functionality swells, it became clear that it is more logical to place some entities in different databases, or even in different types of storage (persistent store). I will not go into details, the main thing is that the initially monolithic NSSQLiteStore needed to be divided into several.

In documentation this question is lit very poorly. It is only said that it is possible to use several NSPersistentStore inside one NSPersistentStoreCoordinator, and even more, use them directly from one NSManagedObjectContext. It looks fantastic.
Desperate to understand the issue exclusively on Apple documentation had to switch to Google. After wandering around the expanses of the network, mainly in the Stack Overflow area, I gathered up a lot of pieces of information, and after several hours of war with XCode, I achieved a less working solution. In the course of studying the issue, the following was revealed:

Indeed, in one NSManagedObjectContext information from several NSPersistentStore may be collected. This is a detailed article that describes the mechanism for connecting plug-ins to an application with Core Data. You can get acquainted here: www.cimgf.com/2009/05/03/core-data-and-plug-ins
')
Only one NSManagedObjectModel can correspond to one NSManagedObjectContext. How to specify which entities in which storage should be stored? This is done using the configuration name. You can create a set of configurations in one NSManagedObjectModel, or you can take several NSManagedObjectModel and merge them into one (while creating each, you still need to create a named configuration).

-(NSManagedObjectModel*)managedObjectModel; { if(managedObjectModel)return managedObjectModel; NSBundle*myBundle =[NSBundle bundleForClass:[self class]]; NSArray*bundles =[NSArray arrayWithObject:myBundle]; managedObjectModel =[[NSManagedObjectModel mergedModelFromBundles:bundles] retain]; return managedObjectModel; } 


Once we have a prepared (second-hand or pre-created) NSManagedObjectModel, you can add storages to the coordinator using the function
 - (NSPersistentStore *)addPersistentStoreWithType:(NSString *)storeType configuration:(NSString *)configuration URL:(NSURL *)storeURLoptions:(NSDictionary *)options error:(NSError **)error 

using the configuration name for each storage type. Like that:
  NSPersistentStore * store = [_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:CONFIG_NAME URL:databaseURL options:options error:&error]; 

The file with storage will be created automatically. After the goal was achieved, I really wanted to see with my own eyes the vaults that Core Data created there. Specifically, under the magnifying glass was considered NSSQLiteStore. An interesting thing turned out - whatever the configuration name for the file, it still creates labels for all the entities from the object model. However, exactly those entities that are specified in the configuration are written to the file. Otherwise, everything worked in a magical way, from one NSManagedObjectModel, entities were actually pushed into different bases!

Separately, you need to say about the link storage. You cannot create relationships between entities from different repositories. But you can use fetched properties. This is more or less clearly described in the Apple dock at developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CoreData/Articles/cdRelationships.html#//apple_ref/doc/uid/TP4000185757-SW5

But that is not all. Since the old version of the application stored a bunch of useful data in its only database, I wanted to somehow migrate this information to our new repositories. For this, I have sketched something like this in the category NSPersistentStoreCoordinator

 - (BOOL)migratePersistentStore:(NSURL *)sourceStoreURL withType:(NSString *)sourceType to:(NSURL *)destinationStoreURL type:(NSString *)destinationType andAddWithConfiguration:(NSString *)configuration { NSError *error = nil; NSDictionary *sourceMetadata = [self.class metadataForPersistentStoreOfType:sourceType URL:sourceStoreURL error:&error]; if(sourceMetadata==nil) return NO; NSManagedObjectModel *sourceModel = [NSManagedObjectModel mergedModelFromBundles:nil forStoreMetadata:sourceMetadata]; NSMigrationManager *migrationManager = [[[NSMigrationManager alloc] initWithSourceModel:sourceModel destinationModel:self.managedObjectModel] autorelease]; NSMappingModel *mappingModel = [NSMappingModel mappingModelFromBundles:nil forSourceModel:sourceModel destinationModel:self.managedObjectModel]; if(mappingModel==nil) return NO; BOOL result = [migrationManager migrateStoreFromURL:sourceStoreURL type:sourceType options:nil withMappingModel:mappingModel toDestinationURL:destinationStoreURL destinationType:destinationType destinationOptions:nilerror:&error]; if(result==NO) return NO; NSLog(@"Successful DB migration"); NSDictionary *options = [self.class migrationOptionsWithAutoMigration:NO]; NSPersistentStore * addedStore = [self addPersistentStoreWithType:destinationType configuration:configuration URL:destinationStoreURL options:options error:&error]; return addedStore!= nil; } 

Unfortunately, decomposing one base in several ways in this way failed. The first method call works out with a bang. However, on the second call, already with a different configuration name, Core Data tragically tells me "Can't add the same store twice." As a result, I decided to migrate only the configuration where important data is stored, and the bases for all logs and caches that can be rebuilt, create from scratch.

Source: https://habr.com/ru/post/146896/


All Articles