I am creating a tasks application which should support offline mode. I have used RestKit to download tasks and mapped that in local Core data.
This is working good in online mode. But in offline there is strange problem. I use NSPredicate to fetch data from local storage. For this I am using Magical Records.
+ (void)getIdeasTasksWithPageNo:(int)pageNo completionHandler:(void (^)(NSArray *, NSError *))completionHandler {
NSArray *tasks = [self MR_findAllWithPredicate:[NSPredicate predicateWithFormat:@"due_date = nil AND user_id = %@", [DBUsers currentUser].id]];
completionHandler(tasks, nil);
}
And I call it like this:
[DBTasks getIdeasTasksWithPageNo:1 completionHandler:^(NSArray *tasks, NSError *error) {
if (!error) {
[self displayTasksWithResults:tasks forPageNo:1];
} else {
NSLog(@"Error is %@", error);
}
}];
And this is how I am displaying it in UITableView
-(void)displayTasksWithResults:(NSArray *)tasks forPageNo:(int)pageNo {
if (!self.tasksArray) {
self.tasksArray = [[NSMutableArray alloc] init];
} else {
[self.tasksArray removeAllObjects];
}
[self.tasksArray addObjectsFromArray:tasks];
[self.tableview reloadData];
}
This is working only for first time and all tasks are populated in UITableView
.
Problem is after the UITableView
is populated, all records in self.tasksArray
become Null
. If I scroll UITableView
, the table rows start being empty.
But if I print self.tasksArray
in displayTasksWithResults
method, it prints perfectly.
(
"Title: Task 01",
"Title: You've gone incognito. Pages you view in incognito tabs won't stick around in your browser's history, cookie store, or search history after you've closed all of your incognito tabs. Any files you download or bookmarks you create will be kept. ",
"Title: Task 06",
"Title: Task 04",
"Title: Hi",
"Title: Task 3",
"Title: Task 4",
"Title: Hi 4",
"Title: hh",
"Title: Task 02",
"Title: Task 05\n",
"Title: Task 4",
"Title: Task 5",
"Title: Task 2 updated",
"Title: Here is a task. ",
"Title: Task 03",
"Title: Hi 3",
"Title: Task 2",
"Title: Hi 2",
"Title: Testing task email with Idea Task",
"Title: Task f6",
"Title: 1.117",
"Title: Task f5",
"Title: Task f12",
"Title: Task f4",
"Title: Task f3",
"Title: 111.0.113",
"Title: 111.0.115",
"Title: Pages you view in incognito tabs won't stick around in your browser's history, cookie store, or search history after you've closed all of your incognito tabs. Any files you download or bookmarks you create will be kept.",
"Title: Task f7",
"Title: 1.116",
"Title: 1.118",
"Title: Going incognito doesn't hide your browsing from your employer, your internet service provider, or the websites you visit. ",
"Title: 111.0.111"
)
If I print self.taskArray
later, may be in didSelectRow
delegate of UITableView
, it prints like below:
(
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)",
"Title: (null)"
)
I think this may be some thing related to NSManagedObjectContext
, but don't know how to fix it.
Please help!
Use Core Data to save your application's permanent data for offline use, to cache temporary data, and to add undo functionality to your app on a single device. To sync data across multiple devices in a single iCloud account, Core Data automatically mirrors your schema to a CloudKit container.
An object space to manipulate and track changes to managed objects.
Delete Everything (Delete All Objects, Reset Core Data) One approach to delete everything and reset Core Data is to destroy the persistent store. Deleting and re-creating the persistent store will delete all objects in Core Data.
Core Data is a framework that you use to manage the model layer objects in your application. It provides generalized and automated solutions to common tasks associated with object life cycle and object graph management, including persistence.
The problem is that (as I wrote in a comment) that the objects are fetched on a background thread, but used on the main (UI) thread. Managed objects can only "live" in the context that
they were created in. If the context is deallocated, the objects still exist, but the
property accessor methods return just nil
.
Possible solutions:
Use
NSManagedObject *copy = [[mainContext objectWithID:[object objectID]];
to "copy" the objects from the background context to the main context. (Perhaps MagicalRecord has a convenience method.)
Instead of fetching managed objects, set
[fetchRequest setResultType:NSDictionaryResultType];
[fetchRequest setPropertiesToFetch:@[@"title", ...]];
to fetch an array of dictionaries with the attributes you are interested in.
Finally found the reason for Null objects. I was calling fetching in background. So Magical Records creates a new Managed Object Context specific for that thread, in NSManagedObject+MagicalFinders.m
+ (NSManagedObjectContext *) MR_contextForCurrentThread;
{
if ([NSThread isMainThread])
{
return [self MR_defaultContext];
}
else
{
NSMutableDictionary *threadDict = [[NSThread currentThread] threadDictionary];
NSManagedObjectContext *threadContext = [threadDict objectForKey:kMagicalRecordManagedObjectContextKey];
if (threadContext == nil)
{
threadContext = [self MR_contextWithParent:[NSManagedObjectContext MR_defaultContext]];
[threadDict setObject:threadContext forKey:kMagicalRecordManagedObjectContextKey];
}
return threadContext;
}
}
So I needed to copy objects from background thread context to main thread context. I finally found the solution here and created my method like this.
+ (void)backgroundFetchWithPredicate:(NSPredicate *)predicate completion:(void(^)(NSArray *, NSError *))completion {
NSManagedObjectContext *privateContext = [NSManagedObjectContext MR_context];
[privateContext performBlock:^{
NSArray *privateObjects = [self MR_findAllWithPredicate:predicate inContext:privateContext];
NSArray *privateObjectIDs = [privateObjects valueForKey:@"objectID"];
// Return to our main thread
dispatch_async(dispatch_get_main_queue(), ^{
NSPredicate *mainPredicate = [NSPredicate predicateWithFormat:@"self IN %@", privateObjectIDs];
NSArray *finalResults = [self MR_findAllWithPredicate:mainPredicate];
completion(finalResults, nil);
});
}];
}
Now I just need to call it like this:
[self backgroundFetchWithPredicate:predicate completion:^(NSArray *results, NSError *error) {
completionHandler(results, error);
}];
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With