Core Data not returning correct data sometimes - objective-c

I have a custom object (Data) inheriting from NSObject, that needs to be persisted into core data. Hence I created a NSManagedObject (Transaction) that contains Data, something like this:
#interface Transaction (CoreDataProperties)
#property (nonatomic) BOOL m_isUploaded;
#property (nullable, nonatomic, retain) id m_transactionData; // this is the Data class, stored under Transformable
#property (nonatomic) int64_t m_submitDateTimeEpochMilliseconds;
#property (nullable, nonatomic, retain) NSString *m_uuid;
#end
I created one context and did everything related to the context on the main thread.
I made Data comply with NSCoding and NSCopying protocols. Also made some custom classes used in Data comply with NSCoding as well.
This is a short extract from Data.h:
#interface Data : NSObject <NSCoding, NSCopying>
#property (strong, nonatomic, nullable) HeldItem *m_heldItem;
#property (strong, nonatomic, nullable) NSDecimalNumber *m_discountAmount;
#property (strong, nonatomic, nonnull) NSMutableArray<Record *> *m_records;
#property (strong, nonatomic, nonnull) NSString *m_transactionId;
#property (nonatomic) CLLocationCoordinate2D m_location;
#property (strong, nonatomic, nullable) NSString *m_status;
- (void)encodeWithCoder:(nonnull NSCoder *)aCoder;
- (nonnull id)initWithCoder:(nonnull NSCoder *)aDecoder;
...lots of variables/methods here...
#end
If I purely just do insert, there are no issues (I confirmed this by looking at the variables during encodeWithCoder:). Or if I do purely just reading, there aren't no issues as well.
However, if I were to insert a new Transaction (and Data) record, and search/read an existing record and modify it (Data), the existing record doesn't get saved, and the new record is mostly blank (as if new). This issue happens at random. I could run it 8 times before I encounter this situation.
Any idea where I might have gone wrong? I've been stuck for quite some time.
This is the part where it seems to give me the problem:
// Store the existing/new data into Core Data
Transaction *t = [MANAGER createTransaction];
Data *data = [Data new];
t.m_kmsTransactionData = data;
// ...some assigning stuffs to other variables...
// ========== if this whole section is omitted the newly created record saves fine =========
Transaction *old = [MANAGER getTransactionFromCoreDataWithId:someOldDataIdString];
// oldData sometimes is returning a blank object (i.e. booleans are no, objects are nil etc)
Data *oldData = [old.m_kmsTransactionData mutableCopy];
oldData.m_status = #"old"; // testing to see if the old record gets updated
old.m_isUploaded = NO;
old.m_kmsTransactionData = [oldData copy];
// This doesn't work because oldData.m_transactionId is nil
data.m_transactionId = [MANAGER generateNewTransactionIDBasedOn:oldData.m_transactionId];
//===============================================
// ... more assigning code here ....
// Save context
[MANAGER saveDatabase];
-
// Some helper function in some manager
- (Transaction * _Nonnull)getTransactionFromCoreDataWithId:(NSString * _Nonnull)dataId
{
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:[Transaction entityName]];
request.returnsObjectsAsFaults = NO;
NSError *error;
NSArray<Transaction *> *fetchedObjects = [self.m_coreDataStore.managedObjectContext executeFetchRequest:request error:&error];
NSAssert(fetchedObjects != nil, #"Failed to execute %#: %#", request, error);
if(fetchedObjects == nil || fetchedObjects.count == 0)
{
// error
return nil;
}
for(Transaction *t in fetchedObjects)
{
Data *td = t.m_kmsTransactionData;
if([td.m_salesRecordId isEqualToString:salesRecordId])
{
return t;
}
}
return nil;
}

I thought it was a Core Data quirk with transformable properties but it wasn't.
The part where I was
// ...some assigning stuffs to other variables...
I assigned m_salesRecordId to an existing id, which was already inside the database. So while searching through the getTransactionFromCoreDataWithId: method, it returned the newly created object based on the m_salesRecordId I've just made earlier.
As for why it worked sometimes and it didn't work sometimes was because the fetched query results wasn't sorted (I assume the order in which the results are returned are always not in sequence), so whichever record was returned first got returned as the searched object (which could either be the empty object I just created or the actual object that was already inside).
I still could not understand yet as to why even though the m_salesRecordId was nil after the screwup, the rest of the variables in the object could not be saved. But since everything is working as intended I'll take the win for now.
tl;dr:
Check your program logic again first

Related

Core Data ManagedObjectContext and Private Queue, role of parent context

My macOS app needs to periodically download user read-only data (like stock prices). To do this I have built a dual-context system:
#interface MyCoreDataStackManager : NSObject
#property (nonatomic, readonly) NSManagedObjectModel* managedObjectModel;
#property (nonatomic, readonly) NSPersistentStoreCoordinator* persistentStoreCoordinator;
#property (nonatomic, readonly) NSManagedObjectContext* managedObjectContext;
#property (nonatomic, readonly) NSURL* applicationSupportDirectory;
#property (nonatomic, readonly) NSURL* storeURL;
During init, the stack is constructed with an NSSQLiteStoreType and NSMainQueueConcurrencyType.
To be able to do background downloading and processing, I also have a method to create a separate context using the same model and store but with it's own NSPersistentStoreCoordinator. The private context uses NSPrivateQueueConcurrencyType.
-(NSManagedObjectContext *)privateContext
{
NSManagedObjectContext* privateContext = nil;
NSError* error = nil;
// Use the same store and model, but a new persistent store coordinator unique to this context.
NSPersistentStoreCoordinator* privateCoordinator = [[[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]] autorelease];
if (privateCoordinator)
{
NSPersistentStore* privateStore = [privateCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:[self storeURL] options:nil error:&error];
if (privateStore)
{
privateContext = [[[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType] autorelease];
if (privateContext)
{
[privateContext setPersistentStoreCoordinator:privateCoordinator];
[privateContext setUndoManager:nil];
}
}
}
return (privateContext);
}
This blog post does it a similar way but makes the private context be the parent of the main-thread managedObjectContext:
http://martiancraft.com/blog/2015/03/core-data-stack/
[[self managedObjectContext] setParentContext:[self privateContext]];
This blog post also does it in a similar way but makes the main-thread context be the parent of the private context (under "Strategy 2"):
https://code.tutsplus.com/tutorials/core-data-from-scratch-concurrency--cms-22131
[self.privateManagedObjectContext setParentContext:self.mainManagedObjectContext];
The way mine works right now is that neither context is the parent of the other, they just use the same store and it seems to work fine. This methodology is based on an Apple example for downloading earthquake data which used to be in Obj-C but is now only available in Swift.
https://developer.apple.com/library/archive/samplecode/Earthquakes/Introduction/Intro.html
Why are the first two opposite and what are the pros/cons/differences of doing it each way? Why does the Apple example not use a parent at all?
Furthermore, some examples (in similar cases) show both contexts sharing a single NSPersistentStoreCoordinator but mine (as the examples above) have each context owning its own PSC, even though they each point to the same store file. What is the better way?
I have one case where the user can edit the downloaded data. Would it make a difference there as to who (if any) is the parent context?

Problems with an NSMutableArray

I'm trying to write an app that has two scenes in it. The first page is a UITableView that will contain a list of note entries. The second scene has 2 text fields (note summary and note description). I'm entering details on the second scene and then clicking a "Save" button which saves the data:
NoteListViewController.m
- (IBAction)saveAndGoBack:(id)sender {
NSLog(#"NoteDetailViewController.m: %#", NSStringFromSelector(_cmd));
NSString * desc = [[NSString alloc]init];
NSString * detail = [[NSString alloc]init];
desc = _noteTitle.text;
detail = _noteDesc.text;
[[NoteStore sharedStore]createNoteWithDesc:desc AndDetail:detail];
[self.navigationController popViewControllerAnimated:YES];
}
NoteStore is a static sharedStore that I am saving the data into.
NoteStore.m
-(Notes *)createNoteWithDesc:(NSString *)desc AndDetail:(NSString *)detail {
NSLog(#"NoteStore.m: %#", NSStringFromSelector(_cmd));
Notes * newNote = [[Notes alloc]initNoteWithDesc:desc AndDetail:detail];
[self.privateItems addObject:newNote];
return newNote;
}
So, the note is added to an NSMutableArray called "privateItems". I confirmed that the Note object gets added properly.
*****The problem happens when I try to retrieve the Note object (desc and detail) from the privateItems array later on using an accessor method which has a public property in the NoteStore.h file called allItems (it's an NSArray readonly, nonatomic and a copy):
NoteStore.m
-(NSArray *)allItems{
NSLog(#"NoteStore.m: %#", NSStringFromSelector(_cmd));
return [self.privateItems copy];
}
Everytime I try to retrieve it, the first property (desc) comes up as nil while the second property (detail) has the data I saved in the second text field of the second scene. Why is the first field constantly coming up as nil???
Just for clarity, a Note object is declared as follows
#interface Notes : NSObject
// What are the properties of a note?
#property (nonatomic, weak) NSString * noteDesc;
#property (nonatomic, weak) NSString * noteDetail;
#property (nonatomic, weak) NSString * test;
// Designated Initializer
-(instancetype)initNoteWithDesc:(NSString *)desc AndDetail:(NSString *)detail;
#end
Any help would be greatly appreciated.
When you call the designated initialiser you pass in two NSString objects. At this point they are owned by the method where they are created.
When they are assigned to the properties they only have a weak reference and therefor the retain count is not bumped up. Weak references are good for things like delegate objects. In this case you want your objects to stick around, so by declaring them as strong you're saying I want these properties to stick around in memory and take ownership of them.

Objective C Adding custom objects into NSMutableArray

I want to store a list of data records in a NSMutableArray for use in a UITableView. In other languages I would have used a simple 'type' structure to define the record structure but I understand the way to do this in Obj-C is to define a new class. I've done this as follows :
#interface CustSuppListItem : NSObject
#property (nonatomic, copy, readwrite) NSString *acCode;
#property (nonatomic, copy, readwrite) NSString *acCompany;
#property (nonatomic, copy, readwrite) NSString *acContact;
#property (nonatomic, assign, readwrite) double osBalBase;
#property (nonatomic, assign, readwrite) unsigned int acAccStatus;
#end
#implementation CustSuppListItem
#synthesize acCode, acCompany, acContact, osBalBase, acAccStatus;
#end
In the viewDidLoad of my UITableViewController I instantiate the array :
tableListDataArray = [[NSMutableArray alloc] init];
Once I have retrieved my data, I add it to the array as follows :
CustSuppListItem *custSuppItem = [[CustSuppListItem alloc] init];
[custSuppItem setAcCode:[jsonCustSuppRecord getStringForKey:#"acCode"]];
[custSuppItem setAcCompany:[jsonCustSuppRecord getStringForKey:#"acCompany"]];
[custSuppItem setAcContact:[jsonCustSuppRecord getStringForKey:#"acContact"]];
[custSuppItem setOsBalBase:[jsonCustSuppRecord getDoubleForKey:#"osBalBase"]];
[custSuppItem setAcAccStatus:[jsonCustSuppRecord getIntForKey:#"acAccStatus"]];
[tableListDataArray addObject:custSuppItem];
[custSuppItem release];
In my table cellForRowAtIndexPath method, I retrieve the data for the current cell as follows:
CustSuppListItem *listDataRecord = [tableListDataArray objectAtIndex:indexPath.row];
[cell.lblCompanyName setText:listDataRecord.acCompany]; // EXC_BAD_ACCESS here
[cell.lblAcCodeContact setText:[NSString stringWithFormat:#"%#, %#",
listDataRecord.acCode, listDataRecord.acContact]];
[cell.lblBalance setText:[Utils fmtNumber:listDataRecord.osBalBase withDecPlaces:2]];
[cell.lblStatus setText:[Utils exchAccStatusDesc:listDataRecord.acAccStatus]];
return cell;
In the dealloc method for the view controller I release the NSMutableArray :
[tableListDataArray release];
I'm very new to Obj-C so it would be great if somebody could confirm everything I've done so far makes sense and is in order. I am getting an intermittent EXC_BAD_ACCESS error when trying to read the acCompany property (see comment next to line) so something must not be right.
Any help appreciated,
Jonathan
All your code looks reasonable and correct to me at first glance.
A few things that I would look at are:
Confirm that cell definitely has a property lblCompanyName. If you're trying to assign to a property that doesn't exist then you will get this type of error. Have you defined a custom cell object type?
Confirm that it is always the acCompany property that is causing the EXC_BAD_ACCESS, and not just any property on the object. One way to do this would be to change the ordering of the lines in the cellForRowAtIndexPath method.
Confirm that the listDataRecord that's causing the crash is getting populated correctly in the first place. In other words, confirm that your jsonCustSuppRecord is always valid. What does jsonCustSuppRecord getStringForKey: return if the key doesn't exist in the jsonCustSuppRecord?
Set a breakpoint at this line: [tableListDataArray addObject:custSuppItem]; and examine the contents of the custSuppItem each time (this is an extension of point 3. above)

Core Data custom accessor doesn't even get called

I've got a Core Data property that I'm trying to set at runtime, with a value derived from another property. However, curiously, the custom accessor I built never even seems to get called.
The property, seasonNameVisible, only gets called from a predicate with terms from a search field, as shown here:
// Add our search predicates
for (NSString *term in searchTerms) {
NSPredicate *searchTermPredicate = [NSPredicate predicateWithFormat:#"(episodeName contains[cd] %#) OR (fromSeason.seasonNameVisible contains[cd] %#) OR (fromSeason.fromSeries.seriesName contains[cd] %#)", term, term, term];
[subPredicates addObject:searchTermPredicate];
}
If I change that property to seasonName, that part of the predicate returns a result just fine, so I'm not suspecting the predicate or any of the other search code.
My plan is to derive the seasonNameVisible NSString from seasonName at runtime. So, I've modified the Season NSManagedObject subclass to override the accessor and setter, using primitive accessors. But as far as I can tell, my accessor never gets called.
Here is the header/interface:
//
// Season.h
//
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#class Episode, Series;
#interface Season : NSManagedObject
#property (nonatomic, retain) NSNumber * seasonIndex;
#property (nonatomic, retain) NSString * seasonName;
#property (nonatomic, retain) NSString * seasonNameVisible;
#property (nonatomic, retain) NSSet *episodes;
#property (nonatomic, retain) Series *fromSeries;
#property (nonatomic, retain) NSString * primitiveSeasonName;
#property (nonatomic, retain) NSString * primitiveSeasonNameVisible;
#end
#interface Season (CoreDataGeneratedAccessors)
- (void)addEpisodesObject:(Episode *)value;
- (void)removeEpisodesObject:(Episode *)value;
- (void)addEpisodes:(NSSet *)values;
- (void)removeEpisodes:(NSSet *)values;
#end
// my additions
#interface Season (PrimitiveAccessors)
- (NSString *)primitiveSeasonName;
- (NSString *)primitiveSeasonNameVisible;
#end
...and the implementation:
//
// Season.m
//
#import "Season.h"
#import "Episode.h"
#import "Series.h"
#implementation Season
#dynamic seasonIndex;
#dynamic seasonName;
#dynamic seasonNameVisible;
#dynamic episodes;
#dynamic fromSeries;
// my additions
#dynamic primitiveSeasonName;
#dynamic primitiveSeasonNameVisible;
- (NSString *)seasonNameVisible
{
NSString *visible;
[self willAccessValueForKey:#"seasonNameVisible"];
visible = [self primitiveValueForKey:#"seasonNameVisible"];
[self didAccessValueForKey:#"seasonNameVisible"];
if (visible != nil) {
return visible;
} else {
[self willAccessValueForKey:#"seasonName"];
visible = [[self primitiveValueForKey:#"seasonName"] substringFromIndex:2];
[self didAccessValueForKey:#"seasonName"];
[self setSeasonNameVisible:visible];
return visible;
}
}
- (void)setSeasonNameVisible:(NSString *)seasonNameVisible
{
[self willChangeValueForKey:#"seasonNameVisible"];
[self setPrimitiveValue:seasonNameVisible forKey:#"seasonNameVisible"];
[self didChangeValueForKey:#"seasonNameVisible"];
}
#end
I've read Apple's docs, and searched around for help with custom accessor methods on StackOverflow, and I think I have the code right (this is the first time I've ever tried to use primitive accessors, or to override an NSManagedObject method, so I'm a tad out of my usual depth), but even when I put a breakpoint on it, it never seems to get called.
You're asking core data to fetch based on a lazily loaded attribute. I dont think this is going to work. You either need to set your seasonNameVisible when you set the season name or, which may make more sense, separate out the storage of your season name into whatever is the prefix (that you are removing to give the visible name) and whatever is the real name.
After the additional information in the comments, I would advise the following:
split your season into two attributes, a season number (int) and season name
sort your fetch request on season number
section name key path is a new read-only property on your object, which returns a string made of the number and name
The error you are seeing in your comment is normal when altering a fetch request used with an FRC. Delete the app from the simulator and re-build and it will go away, or use a nil cache while developing. The FRC stores its cache permanently (eg between runs) so any changes upset it.
Section name key path can be any key path or property name you like, as long as the sorting is the same. From the docs for NSFetchedResultsController:
sectionNameKeyPath
A key path on result objects that returns the section name. Pass nil to indicate that the controller should generate a single section.
The section name is used to pre-compute the section information.
If this key path is not the same as that specified by the first sort descriptor in fetchRequest, they must generate the same relative orderings. For example, the first sort descriptor in fetchRequest might specify the key for a persistent property; sectionNameKeyPath might specify a key for a transient property derived from the persistent property.
So, you'd have a Season object with two persistent attributes, seasonNumber and seasonName. You'd sort your fetch request by season number. Your section name key path (for a fetch presumably on episodes, which have a season relationship) would be #"season.seasonSectionName", implemented as follows - no changes in your managed object model, just changes to your Season object:
Season.h:
#property(nonatomic,readonly) NSString *seasonSectionName;
Season.m:
-(NSString*)seasonSectionName
{
return [NSString stringWithFormat:#"%d - %#",self.seasonNumber,self.seasonName];
}
All you're really doing is decorating the season number with another property.

How to manage one-to-many relationship in Core Data?

I am having a hard time figuring out some things about how Core Data handles its database. I will explain my problems with a concrete example:
Let's just say that we have the following schema where there are departments which hold numerous accounts (our addresses).
I have created my Core Data file using the editor. In the CachedDepartment class, I have added the necessary attributes, and I have created a to-many relationship, and I have selected "Inverse" to the "Department property of my CachedAccount. CacheAccount also has attributes, and a relationship inverse to the "addresses" relationship of CachedDepartment.
I have the following sources (I give you the 2 header files. The rest contain nothing of interest):
#interface CachedAccount : NSManagedObject {
#private
}
#property (nonatomic, retain) NSNumber * accountID;
#property (nonatomic, retain) NSString * email;
#property (nonatomic, retain) NSString * name;
#property (nonatomic, retain) CachedDepartment *department;
#end
#class CachedAccount;
#interface CachedDepartment : NSManagedObject {
#private
}
#property (nonatomic, retain) NSString * departmentID;
#property (nonatomic, retain) NSString * departmentName;
#property (nonatomic, retain) NSSet *addresses;
#end
#interface CachedDepartment (CoreDataGeneratedAccessors)
- (void)addAddressesObject:(CachedAccount *)value;
- (void)removeAddressesObject:(CachedAccount *)value;
- (void)addAddresses:(NSSet *)values;
- (void)removeAddresses:(NSSet *)values;
#end
And now the questions:
-- How should an account and department objects be created so that a one-to-many relationship will be established?
-- What should I do with CoreDataGeneratedAccessors?After creation, should I call the -addAddressesObject function of CachedDepartment or I will need to make something like anAccount.department = aDepartment and that's it?
-- In each case when creating objects how can I make sure that adding an address to a department won't create a double instance of the address and store it?
I would appreciate any insights on this.
EDIT:
Should inserting new objects for CachedDepartment entity look like the following code:
NSManagedObject *cachedDepartment= [NSEntityDescription
insertNewObjectForEntityForName:#"CachedDepartment"
inManagedObjectContext:[self managedObjectContext]];
/*
set values for failedBankInfo here
*/
NSError *error;
if (![context save:&error]) {
NSLog(#"The following error occured: %#", [error localizedDescription]);
}
then would adding a new record for the cachedAccounts which will be in one-to-many relationship with the newly inserted cachedDepartment object be something like that:
NSManagedObject *cachedAccounts = [NSEntityDescription
insertNewObjectForEntityForName:#"CachedAccounts"
inManagedObjectContext:[<b>cachedDepartment managedObjectContext</b>]];
/*
set values for failedBankInfo here
*/
//setting the one-to-many relationship
[cachedDepartment addCachedAccountObject:cachedAccount];
[cachedAccount setCachedDepartment:cachedDepartment];
//
NSError *error;
if (![context save:&error]) {
NSLog(#" couldn't save: %#", [error localizedDescription]);
How should an account and department objects be created so that a one-to-many relationship will be established?
Core data will automatically populate the inverse relationship for you if you have configured it in the model. So, depending on how you are structuring your code, you can just put newAccount.department = aDepartment; and your relationship and inverse are both created.
What should I do with CoreDataGeneratedAccessors?After creation, should I call the -addAddressesObject function of CachedDepartment or I will need to make something like anAccount.department = aDepartment and that's it?
Yes, that's it. See previous sub-answer. The generated accessors are in case the flow of your program makes it more sensible to work the other way around, or if you haven't defined an inverse relationship.
You could do
[aDepartment addAddressesObject:anAccount];
instead of
anAccount.department = aDepartment;
but you don't need to do both.
In each case when creating objects how can I make sure that adding an address to a department won't create a double instance of the address and store it?
If you did call both methods, this would have no effect, since the relationship is stored as an NSMutableSet and you can't add the same object to a set more than once.