0

So, I have a `NSManagedObject's User, Boundary, and Preset. A Preset is always tied to a single user. A user can have many Presets. Each Preset may be tied a boundary.

Basicly, the User has a Preset that he can save to each Boundary he has.

User has a to-many relationship to Preset. Boundary has a many-to-many relationship to Preset.

I am trying to generate a list of Presets that the User has minus the ones already tied to the boundary.

I am using Magical Records.

My issue is when I create a new User and Boundary, this works:

Boundary *boundary = [Boundary MR_createEntity];
boundary.name = @"test boundary";

UserDB *user = [UserDB MR_createEntity];
user.username = @"test User";

Preset *preset01 = [Preset MR_findFirstByAttribute:@"nameDisplay" withValue:@"C4"];
DLog(@"preset01.nameDisplay: %@", preset01.nameDisplay);
Preset *preset02 = [Preset MR_findFirstByAttribute:@"nameDisplay" withValue:@"B"];
DLog(@"preset02.nameDisplay: %@", preset02.nameDisplay);

[boundary setPresets:[NSSet setWithObject:preset01]];
[user setPresets:[NSSet setWithObjects:preset01, preset02, nil]];

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"user == %@ AND boundary != %@", user, boundary];

NSArray *presetsList = [Preset MR_findAllWithPredicate:predicate];

DLog(@"presetsList: %@", presetsList);

So I set preset01 to the boundary. I set preset01 and preset02 to the user. So I need a list that shows preset02 to the user (since preset01 is already tied to the boundary, the user shouldn't be able to add it again).

DEBUG | -[LoginViewController viewDidLoad] | preset01.nameDisplay: C4
DEBUG | -[LoginViewController viewDidLoad] | preset02.nameDisplay: B
DEBUG | -[LoginViewController viewDidLoad] | presetList: (
    "<SoilTestPointPreset: 0x1e06b8b0> (entity: Preset; id: 0x1e06bca0 <x-coredata://7476DB86-AF79-445C-B3AE-6C91088704A0/Preset/p98> ; data: {\n    attributes = \"<relationship fault: 0x1e074000 'attributes'>\";\n    boundary = nil;\n    gpsLocation = nil;\n    nameDisplay = B;\n    nameTitle = nil;\n    rgbColor = \"0x1e06b1d0 <x-coredata://7476DB86-AF79-445C-B3AE-6C91088704A0/RGBColor/p49>\";\n    testing = nil;\n    user = \"0x1e05ff60 <x-coredata:///UserDB/t59757D99-2FBF-4FBF-97AF-39582FC4B5503>\";\n})"

)

Thats what I expected. But now when I fetch the User and Boundary objects:

Boundary *boundary = [Boundary MR_findFirstByAttribute:@"boundaryID" withValue:@3748];
DLog(@"boundary.name: %@", boundary.name);

UserDB *user = [UserDB MR_findFirstByAttribute:@"uid" withValue:@99];
DLog(@"user.username: %@", user.username);

My array is empty:

DEBUG | -[LoginViewController viewDidLoad] | boundary.name: 997677
DEBUG | -[LoginViewController viewDidLoad] | user.username: thatPerson
DEBUG | -[LoginViewController viewDidLoad] | preset01.nameDisplay: C4
DEBUG | -[LoginViewController viewDidLoad] | preset02.nameDisplay: B
DEBUG | -[LoginViewController viewDidLoad] | presetList: (
)

Why does fetching the User and Boundary from Core Data change the results as opposed to creating them?

UPDATE:

I added:

Boundary *boundary = [Boundary MR_findFirstByAttribute:@"boundaryID" withValue:@3748];
DLog(@"boundary.name: %@", boundary.name);
DLog(@"boundary.presets.count: %d", boundary.presets.count); // Added

UserDB *user = [UserDB MR_findFirstByAttribute:AVI_UID withValue:@99];
DLog(@"user.username: %@", user.username);
DLog(@"user.presets.count: %d", user.presets.count); // Added

DLog(@"AFTER | boundary.presets.count: %d", boundary.presets.count); //Added
DLog(@"AFTER | user.presets.count: %d", user.presets.count); //Added

[[NSManagedObjectContext MR_contextForCurrentThread] MR_saveToPersistentStoreAndWait]; //Added

They User and Boundary have Presets relationships:

DEBUG | -[LoginViewController viewDidLoad] | boundary.name: 997677
DEBUG | -[LoginViewController viewDidLoad] | BEFORE | boundary.presets.count: 0
DEBUG | -[LoginViewController viewDidLoad] | user.username: iDealer
DEBUG | -[LoginViewController viewDidLoad] | BEFORE | user.presets.count: 0
DEBUG | -[LoginViewController viewDidLoad] | preset01.nameDisplay: C4
DEBUG | -[LoginViewController viewDidLoad] | preset02.nameDisplay: B
DEBUG | -[LoginViewController viewDidLoad] | AFTER | boundary.presets.count: 1
DEBUG | -[LoginViewController viewDidLoad] | AFTER | user.presets.count: 2
-[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0x20831a80) → Saving <NSManagedObjectContext (0x20831a80): *** DEFAULT ***> on *** MAIN THREAD ***
-[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0x20831a80) → Save Parents? 1
-[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0x20831a80) → Save Synchronously? 1
-[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0x1f59e860) → Saving <NSManagedObjectContext (0x1f59e860): *** BACKGROUND SAVING (ROOT) ***> on *** MAIN THREAD ***
-[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0x1f59e860) → Save Parents? 0
-[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0x1f59e860) → Save Synchronously? 1
__70-[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:]_block_invoke21(0x1f59e860) → Finished saving: <NSManagedObjectContext (0x1f59e860): *** BACKGROUND SAVING (ROOT) ***> on *** MAIN THREAD ***
DEBUG | -[LoginViewController viewDidLoad] | presetList: (
)
Padin215
  • 7,444
  • 13
  • 65
  • 103
  • I think the issue is with my `NSPredicte`, I don't think `AND boundary != %@` behaves as I think it should. – Padin215 Oct 31 '13 at 20:29

2 Answers2

0

You need to save your data before you can fetch it from a store. Use MR_saveToPersistentStoreAndWait or other variants. Fetching always goes it 'disk', so saving first should fix this.

casademora
  • 67,775
  • 17
  • 69
  • 78
  • And isn't the context a copy of the store? What ever changes you make in that context are saved in that context. So if you fetch to that context, you get the new changes? Its basically a local temp store? If you need persistent (saving data between app sessions), you need to save the to the persistent store. – Padin215 Oct 31 '13 at 15:49
  • I don't think you fully understand how these pieces work together. I highly suggest rereading the core data docs and understand how the core data stack works. A context is not a store. And a store is not a context. If you need a temporary data store, I recommend looking at the in memory store details. – casademora Oct 31 '13 at 19:12
  • I am using the term store loosely. The store is used for permanent (aka, between app sessions). A context loads with all the entities in the store and maintains them temporary until either the context is discarded or its changes are saved back to the store. So you can fetch something in a context, alter, re-fetch it and it will reflect the change you just made (as long as they are all done in the same context). If you fetch the same entity in a different context, the change you made will not have happened. – Padin215 Oct 31 '13 at 20:01
0

After a bunch of playing around, I narrowed down the issue to the NSPredicate I was using:

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"user == %@ AND boundary != %@", user, boundary];

There seems to be an issue with NOT and NONE in the queries. Got the answer in a different question I post: NSPredicate with a !=?

The answer involves using a SUBQUERY instead of NOT.

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"user == %@ AND SUBQUERY(boundary, $p, $p == %@).@count == 0", user, boundary];

I'm guessing the fetches I was making were fine, it would return 0 because of the bad NSPredicate, but I have not positive.

Community
  • 1
  • 1
Padin215
  • 7,444
  • 13
  • 65
  • 103