Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use Core Data models without saving them?

I'm writing an application and I am using MagicalRecord as a framework for interacting with Core Data. The application fetches an array of posters from a server and then displays them. Posters can also be created on the app and then uploaded to the server if the user requires it.

So posters created by the user are stored in the local db using Core Data, while posters fetched from the server should only be displayed in the app but not saved locally. How can I use the same Poster class (which now is a subclass of NSManagedObject) to handle both these cases?

Here is my class:

@interface Poster : NSObject
@property (nonatomic, retain) NSNumber * posterID;
@property (nonatomic, retain) NSString * artists;
@end

When I fetch the posters array from the server I allocate a new poster and then assign attributes:

Poster *poster = [[Poster alloc] init];
if ([dict objectForKey:@"id"]) poster.posterID = [dict objectForKey:@"id"];
if ([dict objectForKey:@"artists"]) poster.artists = [dict objectForKey:@"artists"];

But when reaching the linked poster.posterID = [dict etc etc the application crashes with this error

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Poster setPosterID:]: unrecognized selector sent to instance 0xaa8b160'

If I create the new object with Poster *poster = [Poster createEntity]; instead of Poster *poster = [[Poster alloc] init];, the app doesn't crash, but when I save the context I find all the posters fetched from the server are saved locally.

How can I solve this?

like image 930
Paolo Sangregorio Avatar asked Dec 22 '12 15:12

Paolo Sangregorio


People also ask

How does Core Data save Data?

Most interactions with Core Data will occur through an instance of NSManagedObjectContext : the portal through which our app will create new entities, save changes, and fetch from the store. The persistent container comes with a NSManagedObjectContext as one of its built-in properties.

Can we use Core Data managed objects from background thread?

Core Data is designed to work in a multithreaded environment. However, not every object under the Core Data framework is thread safe. To use Core Data in a multithreaded environment, ensure that: Managed object contexts are bound to the thread (queue) that they are associated with upon initialization.

How do you use transformable Core Data?

To implement a Transformable attribute, configure it by setting its type to Transformable and specifying the transformer and custom class name in Data Model Inspector, then register a transformer with code before an app loads its Core Data stack.

Where is Core Data saved?

The persistent store should be located in the AppData > Library > Application Support directory.


2 Answers

You cannot just alloc/init a managed object, because a managed object must be associated with a managed object context. poster.posterID = ... crashes because the dynamically created accessor methods do not work without a managed object context. (Correction: As @noa correctly said, you can create objects without a managed object context, as long as you use the designated initializers. But those objects would not be "visible" to any fetch request.)

To create managed objects that should not be saved to disk you can work with two persistent stores: one SQLite store and a separate in-memory store.

I cannot tell you how to do that with MagicalRecord, but with "plain Core Data" it would work like this:

After creating the managed object context and the persistent core coordinator, you assign two persistent stores to the store coordinator:

NSPersistentStore *sqliteStore, *memStore;

sqliteStore = [coordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:nil error:&error];
if (sqliteStore == nil) {
    // ...
}
memStore = [coordinator addPersistentStoreWithType:NSInMemoryStoreType configuration:nil URL:nil options:nil error:&error];
if (memStore == nil) {
    // ...
}

Later, when you insert new objects to the context, you associate the new object either with the SQLite store or the in-memory store:

Poster *poster = [NSEntityDescription insertNewObjectForEntityForName:@"Poster" inManagedObjectContext:context];
[context assignObject:poster toPersistentStore:memStore];
// or: [context assignObject:poster toPersistentStore:sqliteStore];
poster.posterID = ...;
poster.artists = ...;

Only the objects assigned to the SQLite store are saved to disk. Objects assigned to the in-memory store will be gone if you restart the application. I think that objects that are not assigned explicitly to a store are automatically assigned to the first store, which would be the SQLite store in this case.

I haven't worked with MagicalRecord yet, but I see that there are methods MR_addInMemoryStore and MR_addSqliteStoreNamed, which would be the appropriate methods for this configuration.

like image 180
Martin R Avatar answered Sep 28 '22 09:09

Martin R


You could also try using the designated initializer -initWithEntity:insertIntoManagedObjectContext: with nil for the second parameter. (In my experience, some aspects of managed objects work fine without a context; others do not.)

There's a bit of further explanation in this answer.

like image 35
paulmelnikow Avatar answered Sep 28 '22 08:09

paulmelnikow