Loading data taking too much time CoreData - ios

I am facing problems when i tries to save 40,000 records into CoreData Entity.
I am getting 40,000 records by consuming the webservice using AFNetworking, the response is in JSON. Than i divide the data into 4 , 10000 record chunks and then assign these 4 chunks to separate NSOperation objects (i have created subclass of NSOperation) and add these NSOperation Objects to NSOperationQueue.
The problem is that this way it is taking too much time to save the data into CoreData. And i want to find a solution where i can load the data very quickly.
This is the code in which i am creating NSOperation objects and adding them to NSOperationQueue.
- (void)casesResponseReceived:(NSArray*)array
{
id responseObject = [array objectAtIndex:0];
NSManagedObjectContext *moc = [array objectAtIndex:1];
NSString *responseString = [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding];
NSArray *response = [responseString JSONValue];
NSString *responseStr = [response JSONRepresentation];
NSRange range = [responseStr rangeOfString:#"["];
int index = 0;
int objectsCount = 5000;
if (range.location == 0) {
NSInteger count = objectsCount;
totalOperationsCount = 0;
completedOperationsCount = 0;
self.myQueue = [[NSOperationQueue alloc] init];
while (count == objectsCount) {
if ((index+count) > [response count]) {
count = [response count] - index;
}
NSArray *subArray = [response subarrayWithRange:NSMakeRange(index, count)];
index += objectsCount;
CaseParseOperation *operation = [[CaseParseOperation alloc] initWithData:subArray MOC:moc];
operation.delegate = self;
totalOperationsCount++;
[self.myQueue addOperation:operation];
}
/*
if (self.delegate && [self.delegate respondsToSelector:#selector(serviceHelperDidCasesReceivedSuccessful:)]) {
[self.delegate serviceHelperDidCasesReceivedSuccessful:self];
}*/
}
else {
if (self.delegate && [self.delegate respondsToSelector:#selector(serviceHelperDidCasesReceivedFailed:)]) {
[self.delegate serviceHelperDidCasesReceivedFailed:self];
}
}}
CaseOperation.h
#class CaseParseOperation;
#protocol CaseParseOperationProtocol <NSObject>
-(void)caseParseOperationDidOperationComplete: (CaseParseOperation*)caseParseOperation;
#end
#interface CaseParseOperation : NSOperation
#property (nonatomic, weak) id<CaseParseOperationProtocol> delegate;
-(id)initWithData:(NSArray*)parseData MOC:(NSManagedObjectContext*)moc;
#end
CaseOperation.m
#interface CaseParseOperation()
#property (nonatomic, copy) NSArray *casesData;
#property (nonatomic, strong) NSManagedObjectContext *mainMOC;
#property (nonatomic, strong) NSManagedObjectContext *localMOC;
#end
#implementation CaseParseOperation
- (id)initWithData:(NSArray*)parseData MOC:(NSManagedObjectContext*)moc
{
self = [super init];
if (self) {
self.casesData = [parseData copy];
self.mainMOC = moc;
}
return self;
}
- (void)main
{
#autoreleasepool {
self.localMOC = [[NSManagedObjectContext alloc] init];
self.localMOC.persistentStoreCoordinator = self.mainMOC.persistentStoreCoordinator;
[[NSNotificationCenter defaultCenter] addObserver: self
selector: #selector(mergeChanges:)
name: NSManagedObjectContextDidSaveNotification
object: self.localMOC];
[self parseData];
}
}
-(void) mergeChanges: (NSNotification*) saveNotification {
dispatch_async(dispatch_get_main_queue(), ^{
[self.mainMOC mergeChangesFromContextDidSaveNotification:saveNotification];
});
if (self.delegate && [self.delegate respondsToSelector:#selector(caseParseOperationDidOperationComplete:)]) {
[self.delegate caseParseOperationDidOperationComplete:self];
}
}
- (void)parseData
{
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *ent = [NSEntityDescription entityForName:#"Case" inManagedObjectContext:self.localMOC];
fetchRequest.entity = ent;
NSString *predicateString = [NSString stringWithFormat:#"caseNumber == $caseNumber"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:predicateString];
//NSMutableArray *insertedObjects = [[NSMutableArray alloc] init];
for (NSMutableDictionary *dic in self.casesData) {
if (self.isCancelled) {
break;
}
NSString *desc = [dic valueForKey:#"description"];
BOOL enabled = [[dic valueForKey:#"enabled"] boolValue];
NSString *billToCustomerNo = [dic valueForKey:#"billToCustomerNo"];
NSString *caseNo = [dic valueForKey:#"caseNo"];
NSString *billToName = [dic valueForKey:#"billToName"];
NSString *personResponsible = [dic valueForKey:#"personResponsible"];
NSDictionary *variables = #{ #"caseNumber" : caseNo };
fetchRequest.predicate = [predicate predicateWithSubstitutionVariables:variables];
NSArray *matchedObj = [self.localMOC executeFetchRequest:fetchRequest error:nil];
if ([matchedObj count] > 0) {
Case *caseObj = [matchedObj objectAtIndex:0];
caseObj.isEnabled = [NSNumber numberWithBool:enabled];
caseObj.caseDescription = desc;
caseObj.customerNumber = billToCustomerNo;
caseObj.customerName = billToName;
caseObj.personResponsible = personResponsible;
}
else {
/*
Case *caseObj = [[Case alloc] initWithEntity:[NSEntityDescription entityForName:#"Case"
inManagedObjectContext:self.localMOC] insertIntoManagedObjectContext:nil];
caseObj.caseNumber = caseNo;
caseObj.customerName = billToName;
caseObj.customerAddress = #"";
caseObj.customerPhone = #"";
caseObj.caseDescription = desc;
caseObj.customerNumber = billToCustomerNo;
caseObj.isEnabled = [NSNumber numberWithBool:enabled];
caseObj.personResponsible = personResponsible;
[insertedObjects addObject:caseObj];
*/
[Case createObjectWithCaseNumber:caseNo customerName:billToName customerAddress:#"" customerPhone:#"" caseDescription:desc customerNumber:billToCustomerNo isEnabled:enabled personResponsible:personResponsible MOC:self.localMOC];
}
}
/*
if ([insertedObjects count] > 0) {
NSError *error = nil;
BOOL isInserted = [self.localMOC obtainPermanentIDsForObjects:insertedObjects error:&error];
if (error || !isInserted) {
NSLog(#"Error occured");
}
}
*/
if ([self.localMOC hasChanges]) {
[self.localMOC save:nil];
}
}
#end

The first thing to do is run Instruments and find the bottlenecks, as #jrturton recommends.
But there's one huge glaring bottleneck that's apparent from reading the code. To avoid duplicates, you're doing a fetch-- for every incoming instance. With 40k records you'll have to do 40k fetches during the import process, and that's going to be slow no matter what.
You can improve that by processing the data in batches:
Get a bunch of caseNumber values into an array
Do a fetch with a predicate of caseNumber IN %#, with the array as the argument.
Use that array to check for duplicates.
You'll need to experiment a little to see how many "a bunch" is in step 1. Higher numbers mean fewer fetches, which is good for speed. But higher numbers also mean more memory use.
For a more detailed discussion, see Apple's Efficiently Importing Data guide, especially the section named "Implementing Find-or-Create Efficiently".

Thanks guys valuable suggestions. But i have solved that issue by just altering some technique in the parseData function.
-(void)parseData
{
NSString *predicateString = [NSString stringWithFormat:#"caseNumber == $caseNumber"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:predicateString];
NSArray *allCases = [Case getAllCaseObjectsWithMOC:self.localMOC];
for (NSMutableDictionary *dic in self.casesData) {
if (self.isCancelled) {
break;
}
NSString *caseNo = [dic valueForKey:#"caseNo"];
NSDictionary *variables = #{ #"caseNumber" : caseNo };
predicate = [predicate predicateWithSubstitutionVariables:variables];
NSArray *matchedObj = [allCases filteredArrayUsingPredicate:predicate];
if ([matchedObj count] == 0) {
NSString *desc = [dic valueForKey:#"description"];
BOOL enabled = [[dic valueForKey:#"enabled"] boolValue];
NSString *billToCustomerNo = [dic valueForKey:#"billToCustomerNo"];
NSString *billToName = [dic valueForKey:#"billToName"];
NSString *personResponsible = [dic valueForKey:#"personResponsible"];
[Case createObjectWithCaseNumber:caseNo customerName:billToName customerAddress:#"" customerPhone:#"" caseDescription:desc customerNumber:billToCustomerNo isEnabled:enabled personResponsible:personResponsible MOC:self.localMOC];
}
}
if ([self.localMOC hasChanges]) {
[self.localMOC save:nil];
}
}

Related

Is it possible to create a dispatch_async(dipatch_get_main_queue(), ^{}); with a completion go get when the dispatch block is finished?

I have a piece of code that execute a coredata update of the database, and I would like to know when that block is finished. Is there a way to get it knowing when the coredata has finished to update the tables?
Main function:
NSMutableArray* responseArray = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL];
dispatch_async(dispatch_get_main_queue(), ^{
[self parseAndAddLovAll:responseArray toArray:self.objects];
});
Function used in dispatch:
- (void)parseAndAddLovAll:(NSMutableArray*)responseArray toArray:(NSMutableArray*)destinationArray
{
NSError *error;
DB_ListOfValue_manage *elements_to_store = [[DB_ListOfValue_manage alloc] init];
NSManagedObjectContext * context = [elements_to_store managedObjectContext];
for (int index=0; index < [responseArray count]; index++)
{
NSDictionary * responseArray2 = [[NSDictionary alloc] initWithDictionary:responseArray[index]];
NSString * table_to_store = [[NSString alloc] initWithString:[responseArray2 objectForKey:#"table"]];
NSArray * lignes = [[NSArray alloc] initWithObjects:[responseArray2 objectForKey:#"lignes"], nil];
id value;
// Check if LOV table or contact table
if ((([#"Table_contact" compare:table_to_store])!=NSOrderedSame)&&
(([#"Table_event" compare:table_to_store])!=NSOrderedSame))
{
for (NSDictionary * item in lignes[0])
{
value = [item objectForKey:#"codeevent"];
if ([value isEqualToNumber:[NSNumber numberWithInt:EVENT_ID]])
{//FIXME: bug to check when SYNC
elements_to_store = (DB_ListOfValue_manage*)[NSEntityDescription insertNewObjectForEntityForName:table_to_store inManagedObjectContext:context];
elements_to_store.code_event = [value isKindOfClass:[NSNull class]] ? #"" : value;
value = [item objectForKey:#"id"];
elements_to_store.id = [value isKindOfClass:[NSNull class]] ? #"" : value;
value = [item objectForKey:#"used"];
elements_to_store.used = [value isKindOfClass:[NSNull class]] ? #"" : value;
if (![context save:&error]) {
#ifdef DEBUG
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
#endif
}
else{
#ifdef DEBUG
NSLog(#"Data saved to DB, table %# %# %#", table_to_store, elements_to_store.label1, elements_to_store.label2);
#endif
}
}
}
}
}
}

EXC_BAD_ACCESS. Completion block

ANSWER
I heve a mistake when i call the fetchDailyForecast it should looks like
[self.fetchController fetchDailyForecast:self.currentLocation.coordinate completionBlock:^(DailyModel *newModel) {
_dailyCondition = newModel;
NSLog(#"newModel = %#", _dailyCondition);
}];
END
I try to use bloc's and wait but have a bad_access exaction. Firstly i call method from MangeDataController.m
[self.fetchController
fetchDailyForecast:self.currentLocation.coordinate
completionBlock:(void(^)(DailyModel *))_dailyCondition];
where the dailyCondition is instance of DailyModel.
Secondly here is fetchDailyForecast method realization in FetchController.m.
-(void)fetchDailyForecast:(CLLocationCoordinate2D)coordinate completionBlock:(void(^)(DailyModel *))completionBlock {
NSString *urlString = [NSString stringWithFormat:#"http://api.openweathermap.org/data/2.5/forecast/daily?lat=%f&lon=%f&units=imperial&cnt=7%#", coordinate.latitude, coordinate.longitude, _key];
urlString = [urlString stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLFragmentAllowedCharacterSet]];
[self fetchJSONfromURL:urlString completionBlock:^(NSDictionary *weatherData) {
completionBlock([[DailyModel alloc] dataFromDictionaryDaily:weatherData]); --- (here the bad access);
}];
}
and thirdly in fatchDailyForecast i call fetchJSONfromURL here is realization:
-(void)fetchJSONfromURL:(NSString *)urlString completionBlock:(void (^)(NSDictionary *))completionBlock {
NSLog(#"url = %#", urlString);
[_manager GET:urlString parameters:nil progress:nil success:^(NSURLSessionTask *task, id responceObject) {
if (responceObject) {
completionBlock(responceObject);
} else {
NSLog(#"responce object error");
}
}
failure:^(NSURLSessionTask *operation, NSError *error){
NSLog(#"error %#", error);
}];
}
P.S. I think my mistake is that i try to pass day _dailyCondition but should pass something different. Thanks for any help!
**EDIT ------- DailyModel.m **
#import "DailyModel.h"
#implementation DailyModel
-(id)dataFromDictionaryDaily:(NSDictionary *)dic {
NSArray *arrayWithDictionarys = [dic objectForKey:#"list"];
NSDictionary *dictionaryWithWeekDays = [self indexKeyedDictionaryFromArray:arrayWithDictionarys];
// NSLog(#"dictionary with dayweeks = %#", dictionaryWithWeekDays);
_arrayWithTemp = [NSMutableArray new];
_arrayWithDate = [NSMutableArray new];
_arrayWithIcon = [NSMutableArray new];
for (int i = 0; i <= 6; i++) {
NSNumber* key = [NSNumber numberWithInt:i];
[self addValuesToArrayWithTemp:dictionaryWithWeekDays key:key index:i];
[self addValuesToArrayWithDate:dictionaryWithWeekDays key:key index:i];
[self addValueToArrayWithImage:dictionaryWithWeekDays key:key index:i];
}
return self;
}
- (NSDictionary *) indexKeyedDictionaryFromArray:(NSArray *)array
{
NSMutableDictionary *mutableDictionary = [[NSMutableDictionary alloc] init];
[array enumerateObjectsUsingBlock:
^(id obj, NSUInteger idx, BOOL *stop){
NSNumber *index = [NSNumber numberWithInteger:idx];
[mutableDictionary setObject:obj forKey:index];
}];
NSDictionary *result = [NSDictionary.alloc initWithDictionary:mutableDictionary];
return result;
}
-(void) addValuesToArrayWithTemp:(NSDictionary *)inputDic key:(NSNumber *)key index:(int)ind {
NSDictionary *currentDic = [inputDic objectForKey:key];
NSDictionary *temp = [currentDic objectForKey:#"temp"];
_tempLow = [temp objectForKey:#"min"];
_tempHigh = [temp objectForKey:#"max"];
_tempLow = [self convertToCelsius:_tempLow];
_tempHigh = [self convertToCelsius:_tempHigh];
NSString *tempString = [NSString stringWithFormat:#"%.02f / %.02f", _tempLow.floatValue , _tempHigh.floatValue];
[_arrayWithTemp insertObject:tempString atIndex:ind];
}
-(NSNumber *)convertToCelsius:(NSNumber *)far {
double f = (far.doubleValue - 32) / 1.8;
NSNumber *celsius = [NSNumber numberWithDouble:f];
return celsius;
}
-(void) addValuesToArrayWithDate:(NSDictionary *)inputDic key:(NSNumber *)key index:(int)ind {
NSDictionary *currenDic = [inputDic objectForKey:key];
NSNumber *tempNumber = [currenDic objectForKey:#"dt"];
double unixTimeStamp = tempNumber.doubleValue;
NSString* weekDay = [self convertToWeekDay:unixTimeStamp];
[_arrayWithDate insertObject:weekDay atIndex:ind];
}
-(NSString *)convertToWeekDay:(double) unixtime {
NSTimeInterval _interval = unixtime;
NSDate *date = [NSDate dateWithTimeIntervalSince1970:_interval];
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setLocale:[NSLocale currentLocale]];
[formatter setDateFormat:#"EEEE"];
NSString *dateString = [formatter stringFromDate:date];
return dateString;
}
-(void) addValueToArrayWithImage:(NSDictionary *)inputDic key:(NSNumber *)key index:(int)ind {
NSDictionary *currenDic = [inputDic objectForKey:key];
NSArray *weatherArray = [currenDic objectForKey:#"weather"];
NSDictionary *weather = weatherArray[0];
_icon = [weather objectForKey:#"icon"];
_icon = [self currentImageString:_icon];
[_arrayWithIcon insertObject:_icon atIndex:ind];
}
-(NSString *)currentImageString:(NSString *)ico {
NSDictionary *dic = #{
#"01d" : #"weather-clear",
#"02d" : #"weather-few",
#"03d" : #"weather-few",
#"04d" : #"weather-broken",
#"09d" : #"weather-shower",
#"10d" : #"weather-rain",
#"11d" : #"weather-tstorm",
#"13d" : #"weather-snow",
#"50d" : #"weather-mist",
#"01n" : #"weather-moon",
#"02n" : #"weather-few-night",
#"03n" : #"weather-few-night",
#"04n" : #"weather-broken",
#"09n" : #"weather-shower",
#"10n" : #"weather-rain-night",
#"11n" : #"weather-tstorm",
#"13n" : #"weather-snow",
#"50n" : #"weather-mist",
};
ico = [dic objectForKey:ico];
return ico;
}
#end
EDIT 2
I add the condition into (void)fetchDailyForecast:(CLLocationCoordinate2D)coordinate completionBlock:(void(^)(DailyModel *))completionBlock and now app don't crash but it's still not work and nslog say #"nothing"
[self fetchJSONfromURL:urlString completionBlock:^(NSDictionary
*weatherData) { if (completionBlock) { completionBlock(model = [[DailyModel alloc] dataFromDictionaryDaily:weatherData]); } else {
NSLog(#"nothing"); } }];

iOS How to add an object in array at 0 index and show in tableview cell?

I have an issue that an array having three main objects and i want to add one object on each array's 0 index
Here is URL link
In three sections Homes Plots and Commercial and i want to add All Homes All Plots and All Commercial in each section and added up their results in each section, mean in each section at top All Homes, All Plots and All Commercial
- (void) loadFromDictionary:(NSDictionary *)theDictionary{
_parent_id = -1;
_type_id = [[theDictionary objectForKey:#"type_id"] intValue];
_title = [[NSString alloc] initWithString:[theDictionary objectForKey:#"title"]];
_title_alt1 = [[NSString alloc] initWithString:[theDictionary objectForKey:#"title_alt1"]];
_title_alt2 = [[NSString alloc] initWithString:[theDictionary objectForKey:#"title_alt2"]];
if([theDictionary objectForKey:#"parent_id"])
_parent_id = [[theDictionary objectForKey:#"parent_id"] intValue];
if([theDictionary objectForKey:#"child_list"])
_child_list = [[NSMutableArray alloc] initWithArray:[[theDictionary objectForKey:#"child_list"] componentsSeparatedByString:#","]];
}
+ (void)getTypesWith:(void (^)(NSArray *, NSError *))completionHandler
{
[ZNetworkManager postDataForBackGround:nil atURL:[ZMappingManager getRequestURLToGetPropertiesTypes] completionHandler:^(NSArray *array, NSError *error)
{
NSMutableArray *typesDictionariesArray =[NSMutableArray array];
NSMutableDictionary* details = [NSMutableDictionary dictionary];
if (!error)
{
NSDictionary *fetchedDictionary = (NSDictionary*) array;
if([fetchedDictionary isKindOfClass:[NSDictionary class]] == NO)
{
[details setValue:#"Fetched dictionary is null" forKey:#"desription"];
completionHandler(nil ,[NSError errorWithDomain:#"MyDomain" code:1 userInfo:details]);
}
else
{
if([[[fetchedDictionary objectForKey:#"meta"] objectForKey:#"status"] isEqualToString:#"200"]){
NSDictionary *data = [fetchedDictionary objectForKey:#"response"];
if([data isKindOfClass:[NSDictionary class]] == NO)
{
[details setValue:#"Fetched dictionary is null" forKey:#"desription"];
completionHandler(nil ,[NSError errorWithDomain:#"MyDomain" code:1 userInfo:details]);
}
else
{
NSArray *allTypes = [data objectForKey:#"type"];
if([allTypes count] == 0)
{
[details setValue:#"Fetched dictionary is null" forKey:#"desription"];
completionHandler(nil ,[NSError errorWithDomain:#"MyDomain" code:1 userInfo:details]);
}
else
{
NSMutableArray *searchTypes = [[NSMutableArray alloc] init];
for (NSDictionary *typeDic in allTypes)
{
[typesDictionariesArray addObject:typeDic];
ZZameenType *newType = [[ZZameenType alloc] init];
[newType loadFromDictionary:typeDic];
[searchTypes addObject:newType];
NSArray *arrayforChild = [typeDic objectForKey:#"childs"];
for(NSDictionary *typeChild in arrayforChild){
[typesDictionariesArray addObject:typeChild];
ZZameenType *newChild = [[ZZameenType alloc] init];
[newChild loadFromDictionary:typeChild];
[searchTypes addObject:newChild];
newChild = nil;
}
newType = nil;
}
NSSortDescriptor *typeID_sort = [NSSortDescriptor sortDescriptorWithKey:#"type_id" ascending:YES];
[searchTypes sortUsingDescriptors:[NSArray arrayWithObjects:typeID_sort,nil]];
[ZGlobals saveSearchTypes:typesDictionariesArray];
completionHandler(searchTypes ,nil);
searchTypes = nil;
details = nil;
}
}
}else{
}
}
}
}];
}
Not entirely sure what issue you're having. If you're just wanting to insert objects into an array and a specific index - you'd do something like this:
[searchTypes insertObject: addObject:newType atIndex:0];

CoreData objects not saved between screens

I have a screen that holds a UITableView, in this screen I have an array of NSManagedObjects. It's working just fine, but as I try move to another screen (click on a specific cell, and push a new screen), then return to the same UITableView screen, all the objects got lost.
What does it means? I try to print the array of the NSManagedObjects and it's fine, all the objects there, but as I print the description of each object, I get nil from all the object attributes.
Someone knows whats the cause of it? I don't know why but it worked just fine 12 hours ago, but now it's all messed up and I don't have a clue what have I done.
Thanks in advance!
Save method:
- (void)saveContext {
NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
if (managedObjectContext != nil) {
NSError *error = nil;
if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
else {
NSLog(#"Context saved!");
}
}
}
This is how I save the objects:
NSDictionary *response = responseObject;
if ([[response valueForKey:#"status"] rangeOfString:#"ok"].location != NSNotFound)
{
NSArray *data = [response objectForKey:#"data"];
if (data.count != 0)
{
if (page.integerValue == 0) {
[[DownloadData sharedData] deleteAllObjectsFromEntityName:#"DbHomeCuisine"];
[[DownloadData sharedData] deleteAllObjectsFromEntityName:#"DbHomeCategory"];
[[DownloadData sharedData] deleteAllObjectsFromEntityName:#"DbHomeDish"];
}
NSMutableArray *homePageObjects = [[NSMutableArray alloc] initWithCapacity:data.count];
for (NSDictionary *object in data)
{
NSNumber *type = [object objectForKey:#"type"];
switch (type.integerValue) {
case 1:
{
NSDictionary *content = [object objectForKey:#"content"];
NSManagedObjectContext *context = [[MainDb sharedDb] managedObjectContext];
DbHomeCuisine *homeCuisine = [NSEntityDescription insertNewObjectForEntityForName:#"DbHomeCuisine" inManagedObjectContext:context];
NSInteger cuisineId = [[content valueForKey:#"cuisine_id"] integerValue];
homeCuisine.cuisine = [self gCuisineWithCuisineId:[NSNumber numberWithInteger:cuisineId]];
NSInteger count = [[content valueForKey:#"count"] integerValue];
homeCuisine.count = [NSNumber numberWithInteger:count];
homeCuisine.type = type;
[homePageObjects addObject:homeCuisine];
}
break;
case 2:
{
NSDictionary *content = [object objectForKey:#"content"];
NSManagedObjectContext *context = [[MainDb sharedDb] managedObjectContext];
DbHomeCategory *homeCategory = [NSEntityDescription insertNewObjectForEntityForName:#"DbHomeCategory" inManagedObjectContext:context];
NSInteger categoryId = [[content valueForKey:#"category_id"] integerValue];
homeCategory.category = [self gCategoryWithCategoryId:[NSNumber numberWithInteger:categoryId]];
NSInteger count = [[content valueForKey:#"count"] integerValue];
homeCategory.count = [NSNumber numberWithInteger:count];
homeCategory.type = type;
[homePageObjects addObject:homeCategory];
}
break;
case 3:
{
NSDictionary *content = [object objectForKey:#"content"];
NSManagedObjectContext *context = [[MainDb sharedDb] managedObjectContext];
DbHomeDish *homeDish = [NSEntityDescription insertNewObjectForEntityForName:#"DbHomeDish" inManagedObjectContext:context];
homeDish.dishId = [self gInt:content forKey:#"dish_id"];
homeDish.headline = [AppUtils checkForEmptyValue:[content valueForKey:#"title"]];
homeDish.text = [AppUtils checkForEmptyValue:[content valueForKey:#"description"]];
homeDish.cuisineId = [self gInt:content forKey:#"cuisine_id"];
homeDish.cuisine = [self gCuisineWithCuisineId:homeDish.cuisineId];
homeDish.creationDate = [AppUtils checkForEmptyValue:[content valueForKey:#"creation_time"]];
homeDish.userId = [self gInt:content forKey:#"user_id"];
homeDish.longitude = [self gDouble:content forKey:#"lng"];
homeDish.latitude = [self gDouble:content forKey:#"lat"];
homeDish.lastPromoteDate = [AppUtils checkForEmptyValue:[content valueForKey:#"last_promote_time"]];
homeDish.price = [self gInt:content forKey:#"price"];
homeDish.currency = [AppUtils checkForEmptyValue:[content valueForKey:#"currency"]];
homeDish.countryName = [AppUtils checkForEmptyValue:[content valueForKey:#"country_name"]];
homeDish.baseCurrency = [self gFloat:content forKey:#"base_currency"];
homeDish.exchangeRate = [self gFloat:content forKey:#"exchange_rate"];
homeDish.countryIsoCode = [AppUtils checkForEmptyValue:[content valueForKey:#"country_iso_code"]];
homeDish.mainPhoto = [AppUtils checkForEmptyValue:[content valueForKey:#"main_photo"]];
homeDish.like = [self gLikeWithDishId:homeDish.dishId];
homeDish.profileImageURL = [AppUtils checkForEmptyValue:[content valueForKey:#"profile_img_url"]];
homeDish.likeCount = [self gInt:content forKey:#"likes"];
homeDish.type = type;
[homePageObjects addObject:homeDish];
}
break;
default:
break;
}
}
// ##log -- Save data to core data and device
//
//
[[MainDb sharedDb] saveContext];
if (success) {
success(operation, homePageObjects);
}
}
}
Seriously, you should consider refactoring using a NSFetchedResultsController. Start from the template provided in Xcode (New Project -> Master/Detail -> check Core Data, the code is in MasterViewController.m).
I strongly discourage loading Core Data objects into an array to be displayed in a table view. Your problem is typical for such a setup, and you will run into memory and performance issues eventually as well.

NSInternalInconsistencyException for NSMutableArray

I'm trying to add objects to an NSMutableArray but it keeps giving me this error.:
NSInternalInconsistencyException', reason: '-[__NSCFArray insertObject:atIndex:]: mutating method sent to immutable object
I have researched this problem, and I'm not doing anything wrong that past people have done, so I have no idea what's wrong. Here is my code:
Group.h
#property (strong, nonatomic) NSString *custom_desc;
#property (strong, nonatomic) NSMutableArray *attributes; //I define the array as mutable
Group.m
#import "Group.h"
#implementation Group
-(id)init
{
self = [super init];
if(self)
{
//do your object initialization here
self.attributes = [NSMutableArray array]; //I initialize the array to be a NSMutableArray
}
return self;
}
#end
GroupBuilder.m
#import "GroupBuilder.h"
#import "Group.h"
#implementation GroupBuilder
+ (NSArray *)groupsFromJSON:(NSData *)objectNotation error:(NSError **)error
{
NSError *localError = nil;
NSDictionary *parsedObject = [NSJSONSerialization JSONObjectWithData:objectNotation options:0 error:&localError];
if (localError != nil) {
*error = localError;
return nil;
}
NSMutableArray *groups = [[NSMutableArray alloc] init];
NSDictionary *results = [parsedObject objectForKey:#"result"];
NSArray *items = results[#"items" ];
for (NSDictionary *groupDic in items) {
Group *group = [[Group alloc] init];
for (NSString *key in groupDic) {
if ([group respondsToSelector:NSSelectorFromString(key)]) {
[group setValue:[groupDic valueForKey:key] forKey:key];
}
}
[groups addObject:group];
}
for(NSInteger i = 0; i < items.count; i++) {
//NSLog(#"%#", [[items objectAtIndex:i] objectForKey:#"attributes"]);
NSMutableArray *att = [[items objectAtIndex:i] objectForKey:#"attributes"]; //this returns a NSArray object understandable
Group *g = [groups objectAtIndex:i];
[g.attributes addObjectsFromArray:[att mutableCopy]]; //I use mutable copy here so that i'm adding objects from a NSMutableArray and not an NSArray
}
return groups;
}
#end
Use options:NSJSONReadingMutableContainers on your NSJSONSerialization call.
Then all the dictionaries and arrays it creates will be mutable.
According to the error message you are trying to insert an object into an instance of NSArray, not NSMutableArray.
I think it is here:
NSMutableArray *att = [[items objectAtIndex:i] objectForKey:#"attrib`enter code here`utes"]; //this returns a NSArray object understandable
Items is fetched from JSON and therefore not mutable. You can configure JSONSerialization in a way that it creates mutable objects, but how exactly I don't know out of the top of my head. Check the references on how to do that or make a mutable copy:
NSMutableArray *att = [[items objectAtIndex:i] objectForKey:#"attributes"] mutableCopy];
Next try, considering your replies to the first attempt:
#import "Group.h"
#implementation Group
-(NSMutableArray*)attributes
{
return [[super attributes] mutableCopy];
}
#end

Resources