Is it possible to have multiple core data “databases” on one iOS app?

别来无恙 提交于 2019-11-28 02:15:01

问题


I'm wanting to write a "management" game that utilizes Core data heavily. The game requires a pre-set, pre-defined dataset that cannot be changed by the user/system; it is used to seed the game with data and is meant to be read-only.

The best example I can give is a football management game, but it could be anything. In some football management sims they give you scenarios and pre-set datasets.

As the user proceeds through the game they can save/load their progress which is saved to the core data.

In addition to this, the user can receive updates to the pre-defined data or can purchase scenarios packs of data; which is saved to their device.

So, there could be multiple "core data databases" (yes, I know core data isn't strictly a database) or "buckets" which the app can dive into and use.

The schema of the data would not change.

So we have:

  1. Pre-defined data (Default data) that is only used for seeding the game.
  2. The user's current save game.
  3. The user has downloaded a scenario from the Internet.
  4. Problem: What happens when the user saves the game whilst on a "scenario".
  5. Problem: How do I keep track of all the scenarios and all the user saved games in core data?

This sounds like multiple databases at a given time. Obviousily one should restrict how many save games a user can make.

An alternative solution to this is that the user's device exports a back-up copy of the data in JSON or XML and this serves as the "save data" and I could use this strategy for scenarios too. Obviousily some kind of encryption would be needed to prevent people simply changing stats in the game via the XML.

But I'm wondering from the outset what would be the best way to use Core data for iOS devices handle more than 1 core data "database"?

Thanks for your time


回答1:


If the data models are the same, you can just setup your MOC so that it uses both persistent stores... one which is read-only and the other that is read/write.

Or, you could use separate MOC for each store.

So, how you want to use it is your only decision factor, since you can have almost any combination of MOC/PSC.

Look at the documentation here for more information.


Edit:

The link given with this question is dead, someone else suggested this link in another deleted answer.




回答2:


NB: This is an old question, but the problems it describes are timeless, so I've written the answer as if the question were posted today.

Actually, none of this suggests the need for multiple databases. So we have:

1) Pre-defined data (Default data) that is only used for seeding the game.

Write a method that loads the data into the persistent store (database). Set a flag in user default, defaultDataHasBeenLoaded or something like that, and check that in the appDelegata.

2) The user's current save game.

You need a Users table and a Games table with a one-to-many relationship. In the Games table you add an isCurrentGame attribute.

3) The user has downloaded a scenario from the Internet.

Now it's getting interesting. You will need an import function or class for that and you'll want to run that on a background thread. That way, your user can continue playing, or looking looking at their scores or whatever, while the new scenario is being imported. When the scenario has been imported, the user should get a notification and the opportunity to switch to the new scenario.

The most efficiënt way to do this is to use NSPeristentContainer which is available from iOS 10.0, macOS 10.12, tvOS 10.0 and watchOS 3.0. Give NSPeristentContainer the name of the data model and it will create or load a persistent store and set the persistentStoreCoördinator and the managedObjectContext.

// AppDelegate.h or class header file
@property (readonly, strong, nonatomic) NSPersistentContainer *persistentContainer;
@property (readonly, weak, nonatomic) NSManagedObjectContext *managedObjectContext;


// AppDelegate.m or other implementation file
@synthesize persistentContainer = _ persistentContainer;
@synthesize managedObjectContext = _ managedObjectContext;

- (NSPersistentContainer *)persistentContainer
{
    @synchronized (self) {
        if (_persistentContainer == nil) {
            _persistentContainer = [[NSPersistentContainer alloc] initWithName:@"nameOfDataModel"];
            [_persistentContainer loadPersistentStoresWithCompletionHandler:^(NSPersistentStoreDescription *storeDescription, NSError *error) {
                if (error != nil) {
                    // Handle the error
                } else {
                    _managedObjectContext = _persistentContainer.viewContext; // NB new name for moc is viewContext!
                }
            }];
        }
    }
    return _persistentContainer;
}

To use the the container from the appDelegate in an NSViewController, you add the following to viewDidLoad:

self.representedObject = [(AppDelegate *)[[NSApplication sharedApplication] delegate] persistentContainer]; 

// Use representedObject in bindings, such as:
[_gameNameTextField bind:NSValueBinding toObject:self
                  withKeyPath:@"representedObject.game.name"
                      options:options];

To import the new scenario, use performBackgroundTask:, a block which will automatically create a new thread and a new managedObjectContext (here called moc_background). Use only moc_background for anything you do in the block -- if you call a method outside the block, pass it moc_background.

  NSPersistentContainer *pc = (NSPersistentContainer *)self.representedObject;
    pc.viewContext.automaticallyMergesChangesFromParent = YES; // this will ensure the main context will updated automatically

    __block id newScenario;
    [pc performBackgroundTask:^(NSManagedObjectContext * _Nonnull moc_background) {
        NSEntityDescription *scenarioDesc = [NSEntityDescription entityForName:@"Scenario" inManagedObjectContext:moc_background];
        NSManagedObject *scenario = [[NSManagedObject alloc] initWithEntity:scenarioDesc insertIntoManagedObjectContext:moc_background];
        // configure scenario with the data from newScenario

        NSError *error;
        BOOL saved = [moc_background save:&error];
        // send out a notification to let the rest of the app know whether the import was successfull
    }];

Problem: What happens when the user saves the game whilst on a "scenario".

That depends on who gets there first, the background thread that attempts to merge or the save operation. If you add a Scenario table with many-to-one relationship to the Game table, there should not be any problems.

Problem: How do I keep track of all the scenarios and all the user saved games in core data?

Data modeling can be tricky. Keep it simple at first and add tables and relationships when you find a clear need for them. And then test, test, test.



来源:https://stackoverflow.com/questions/13109257/is-it-possible-to-have-multiple-core-data-databases-on-one-ios-app

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!