SIGSEGV crash on MagicalRecord MR_contextWillSave: - ios

I have an app in TestFlight that keeps crashing on the first core data save. I've never been able to recreate the issue myself, but It is happening multiple times to multiple users. Im getting a set of taxonomy from our server as JSON then saving the results to core data like so:
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {
[self createRestrictions:restrictions inContext:localContext];
} completion:^(BOOL success, NSError *error) {
if(success){
dispatch_async(dispatch_get_main_queue(), ^(void) {
DLog(#"got and saved all restrictions");
if(complete){
complete(YES);
}
});
}else{
TFLog(#"error saving restrictions: %#", error);
if(failure){
failure(#"Error saving restrictions");
}
}
}];
-(void)createRestrictions:(NSArray *)restrictions
inContext:(NSManagedObjectContext *)context
{
for(NSDictionary *_restriction in restrictions){
Restriction *restriction = [Restriction MR_findFirstByAttribute:#"tid"
withValue:[NSNumber numberWithInt:[_restriction[#"tid"] intValue]]
inContext:context];
if(!restriction){
restriction = [Restriction MR_createInContext:context];
restriction.tid = [NSNumber numberWithInt:[_restriction[#"tid"] intValue]];
}
...
}
}
Here is a screenshot of the crash log from TestFlight

Related

How to call same service multiple time and store data in ios

I have a situation where I will be getting more than 25000 records from web service, it is sending using pagination technique.
so the problem is I just want to store the data so for that I am thinking to run it in a loop but in future records may vary (i.e 30000,50000 etc)
from backend I am getting on each page 10000 records,but i dont know how many times i have run the loop so how do I handle this problem?
-(void)vendorsListCalling:(NSInteger)pageIndex{
[[ServicesHandler new] callVendorDetailsServiceWithParams:#{#"pageno":#(pageIndex)} CompletionBLock:^(NSDictionary *response, NSError *error) {
if (error) {
NSLog(#"error log %#",error.localizedDescription);
}else{
NSDictionary *dict = response[#"params"][#"data"];
[vendorDictionay addEntriesFromDictionary:dict];
pageCount++;
[[NSUserDefaults standardUserDefaults] setObject:vendorDictionay forKey:#"vendorsDict"];
}
}];
}
above block is where i stuck .
Any suggestions would be more appreciated.
You can store data into sqlite database. And for recursive calling for service, you can modify the same method as,
-(void)vendorsListCalling:(NSInteger)pageIndex {
if (!loader) {
//Write code to Show your loader here
}
[[ServicesHandler new] callVendorDetailsServiceWithParams:#{#"pageno":#(pageIndex)} CompletionBLock:^(NSDictionary *response, NSError *error) {
if (error) {
NSLog(#"error log %#",error.localizedDescription);
//If it fails you need to call the service again with the same Index
[self vendorsListCalling:pageCount];
} else {
if (!response[#"params"][#"data"]) {
//Stop loader since you didn't received any data
} else {
NSDictionary *dict = response[#"params"][#"data"];
[vendorDictionay addEntriesFromDictionary:dict];
pageCount++;
// Store Data in database here //
//Call service with incremented Index
[self vendorsListCalling:pageCount];
}
}
}];
}

Nil for MR_inContext

according to this tutorial (https://github.com/magicalpanda/MagicalRecord/blob/master/Docs/Working-with-Managed-Object-Contexts.md) I tried to find my device and update it.
Person *person = ...;
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext){
Person *localPerson = [person MR_inContext:localContext];
localPerson.firstName = #"John";
localPerson.lastName = #"Appleseed";
} completion:^(BOOL success, NSError *error) {
self.everyoneInTheDepartment = [Person findAll];
}];
So I made:
CDDevice *device = [CDDevice MR_findFirstByAttribute:#"deviceName"
withValue:uniqueName];
Which found my device. After few IF statements where i test if device have proper session and authorization code I want to update it.
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext){
CDDevice * localDevice = [device MR_inContext:localContext];
[localDevice updateFromDictionary:messageDictionary];
} completion:^(BOOL success, NSError *error) {
NET_LOG(#"Updating current device %#", device);
}];
But all the time my localDevice is nil. Is it because MR_findFirstByAttribute running in different context? What is correct way to update my device?
All of this happing on my custom serial queue, because this code is in network part of project. (Receviver method with GCDAsyncUdpSocket )
Make sure you save your data before retrieving it in the background context.

Doesn't MagicalRecord work in background thread?

It seems I tried everything but it seems it works in main thread only. For example:
[SomeClass MR_createEntity];
[[NSManagedObjectContext MR_defaultContext] MR_saveWithOptions:MRSaveSynchronously completion:^(BOOL success, NSError *error) {
if (success) {
NSLog(#"You successfully saved your context.");
} else if (error) {
NSLog(#"Error saving context: %#", error.description);
}
}];
If this code is run in main thread then success == YES otherwise (in background thread) it gives success == NO. In both cases error == nil.
So is it impossible to call the saving in background thread?
Completion blocks are always called from the main thread, here's an example that should work:
Person *person = ...;
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext){
Person *localPerson = [person MR_inContext:localContext];
localPerson.firstName = #"John";
localPerson.lastName = #"Appleseed";
} completion:^(BOOL success, NSError *error) {
self.everyoneInTheDepartment = [Person findAll];
}];
Reference: https://github.com/magicalpanda/MagicalRecord/blob/master/Docs/Working-with-Managed-Object-Contexts.md
Finally I hadn't to create a workable project with fully background MagicalRecord work.
The best solution for me is to update database in the main thread only and to read the database in any thread (including background). Additionally I show custom progress view on database updating.

AFNetworking 2.0, saving download state with CoreData (MagicalRecord)

i'm having an issue with a app that uses AFNetworking 2.0 and MagicalRecord. This app downloads data from a little JSON API in a podcast site and stores every podcast episode in a Podcast entity.
This Podcast Entity has another entity called PodcastDownload that keep information when a podcast is downloading (Download status and progress). In this PodcastDownload entity i use KVO to update this information observing the NSProgress object and state from the NSURLSessionDownloadTask. This works fine.
But I have a problem when I try to save the download status in my data context with this method in a singleton instance called PodcastManager:
- (void)saveContext{
[[NSManagedObjectContext defaultContext] saveToPersistentStoreWithCompletion:^(BOOL success, NSError *error) {
if (success) {
NSLog(#"Save OK");
} else if(error) {
NSLog(#"Error %#", error.description);
}
}];
}
When I call this method I get this EXC_BAD_ACCESS error in the AFURLSessionManager from AFNetworking, but saving seems tu run ok according to my log:
Context BACKGROUND SAVING (ROOT) is about to save. Obtaining permanent IDs for new 1 inserted objects
I don't know what is causing this error. I Also tried using MagicalRecord method saveWithBlock but I got the same result.
This is my source code for my KVO Observer inside PodcastDownload:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if (context == PodcastDownloadContext) {
if ([object isKindOfClass:[NSURLSessionDownloadTask class]]) {
NSURLSessionDownloadTask *task = (NSURLSessionDownloadTask *) object;
if (task.state == NSURLSessionTaskStateRunning) {
[self setStatus:PodcastDownloadRunning];
[[PodcastManager sharedManager] saveContext];
}
if (task.state == NSURLSessionTaskStateSuspended) {
[self setStatus:PodcastDownloadSuspended];
[[PodcastManager sharedManager] saveContext];
}
if (task.state == NSURLSessionTaskStateCanceling) {
[self setStatus:PodcastDownloadCanceling];
self.downloadTask = nil;
self.data = nil;
self.progress = 0;
[[PodcastManager sharedManager] saveContext];
}
if (task.state == NSURLSessionTaskStateCompleted) {
if (self.status == PodcastDownloadCanceling) {
[self setStatus:PodcastDownloadNotSet];
} else {
[self setStatus:PodcastDownloadCompleted];
self.downloadTask = nil;
self.data = nil;
self.progress = 0;
[[PodcastManager sharedManager] saveContext];
}
#try {
[object removeObserver:self forKeyPath:NSStringFromSelector(#selector(state)) context:PodcastDownloadContext];
[object removeObserver:self forKeyPath:NSStringFromSelector(#selector(fractionCompleted)) context:PodcastDownloadContext];
}
#catch (NSException * __unused exception) {}
}
}
}
}
Thank you so much
Well, making some changes I find out a solution, but not the cause.
I had three entities in my Core Data stack (Podcast, PodcastDownload and PodcastPlayback). The main entity is Podcast and PodcastDownload was used to save information about the download, as I explained in my question.
What I did was take the attributes out from PodcastDownload and put them in the Podcast entity. And now seems to work as it should.
I don't know the exact cause of the error, but I think it was related with threading (sorry, I'm still a little noob with objective-c).
Thanks for all the help.

Magical Record, how to use saveWithBlock and still access imported data afterwards

the idea is to import server JSON response into core data without blocking UI on main thread, I still need the imported entities afterwards, after a whole morning testing/googling, I failed to find the right way to do this.
__block NSMutableArray *cars;
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {
for (NSDictionary *carObject in carObjects) {
Notification *car = [Notification MR_importFromObject:carObject inContext:localContext];
[cars addObject:car];
}
} completion:^(BOOL success, NSError *error) {
if (success) {
for (Car *car in cars) {
// data may have invalid data or be nil
// [Car findAll] will have correct data though
}
}
}];
interestingly, when I use following code, it works. seems the importing is done in background thread as well!
I really don't know in which context the importing is done, but the UI blocking issue is gone.
__block NSMutableArray *cars;
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {
for (NSDictionary *carObject in carObjects) {
Notification *car = [Notification MR_importFromObject:carObject]; // not pass in localContext
[cars addObject:car];
}
} completion:^(BOOL success, NSError *error) {
if (success) {
for (Car *car in cars) {
// data may have invalid data or be nil
// [Car findAll] will have correct data though
}
}
}];

Resources