RESTKit: Detect attribute changes in fetched Objects - core-data

iOS 7, RESTKit 0.20.X
I'm using the following code to compare local Core Data objects with the new and modified fetched objects to determine what all values of attributes have changed. I have transient properties that end with "M" for modified. i.e. actual attribute: startDate and transient: startDateM. I loop through the array of fetched objects and if the values of attributes have changed, I mark the transient property.
The results are intermittent. At times, the loop marks the correct attributes that have changed; however, often the loop marks attribute that hasn't been modified as modified, or never detects some or any of the attributes that have been modified. I can see Charles log and have access to the Database to know for a FACT what all attributes were modified.
RKManagedObjectRequestOperation *operation = [[RKManagedObjectRequestOperation alloc]initWithRequest:request responseDescriptors:#[responseDescriptor]];
operation.managedObjectContext = self.objectManager.managedObjectStore.mainQueueManagedObjectContext;
operation.managedObjectCache = appDelegate.managedObjectStore.managedObjectCache;
operation.savesToPersistentStore = NO;
[operation setCompletionBlockWithSuccess:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
NSSet *updatedObjects = [self.managedObjectContext updatedObjects];
NSArray *_updatedObjects = updatedObjects.allObjects;
for (int i = 0; i<_updatedObjects.count; i++)
{
if ([[_updatedObjects objectAtIndex:i] isMemberOfClass:[Invite class]])
{
Invite *invite = [_updatedObjects objectAtIndex:i];
NSDictionary *changedValues = [invite changedValues];
NSArray *allKeys = [changedValues allKeys];
invite.startDateM = [allKeys containsObject:#"startDate"] ? [NSNumber numberWithBool:YES] : [NSNumber numberWithBool:NO];
invite.commentM = [allKeys containsObject:#"comment"] ? [NSNumber numberWithBool:YES] : [NSNumber numberWithBool:NO];
invite.locationM = [allKeys containsObject:#"location"] ? [NSNumber numberWithBool:YES] : [NSNumber numberWithBool:NO];
}
else if ([[_updatedObjects objectAtIndex:i] isMemberOfClass:[Activity class]])
{
Activity *activity = [_updatedObjects objectAtIndex:i];
NSDictionary *changedValues = [activity changedValues];
NSArray *allKeys = [changedValues allKeys];
activity.customDataM = [allKeys containsObject:#"customData"] ? [NSNumber numberWithBool:YES] : [NSNumber numberWithBool:NO];
activity.latitudeM = [allKeys containsObject:#"latitude"] ? [NSNumber numberWithBool:YES] : [NSNumber numberWithBool:NO];
activity.locationNameM = [allKeys containsObject:#"locationName"] ? [NSNumber numberWithBool:YES] : [NSNumber numberWithBool:NO];
activity.longitudeM = [allKeys containsObject:#"longitude"] ? [NSNumber numberWithBool:YES] : [NSNumber numberWithBool:NO];
activity.startTimeM = [allKeys containsObject:#"startTime"] ? [NSNumber numberWithBool:YES] : [NSNumber numberWithBool:NO];
activity.activityStatusM = [allKeys containsObject:#"activityStatus"] ? [NSNumber numberWithBool:YES] : [NSNumber numberWithBool:NO];
}
}
NSError *error;
if ([self.managedObjectContext saveToPersistentStore:&error])
{
NSLog (#"******* INBOX OBJECTS SAVED AFTER DETECTING CHANGES**********");
}
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
}];
[operation start];
[operation waitUntilFinished];
}
What am I doing wrong? What's the alternative way/best practice to detect attribute changes in each object?

Related

core data simple fetch request template

I have a small core data base "Guests" and I am trying to get the results from a fetch request template called FetchRequestA, I made a button to trigger in the console the results from the request but I keep on getting a null answer, the request is set to display all guestlastnames that contain a d ? here is the code that i am using :
- (IBAction)fetchA:(id)sender {
NSFetchRequest *request2 = [[[self managedObjectModel] fetchRequestTemplateForName:#"FetchRequestA"] copy];
NSSortDescriptor *sort = [[NSSortDescriptor alloc]initWithKey:#"guestlastname" ascending:YES];
[request2 setSortDescriptors:[NSArray arrayWithObject:sort]];
NSArray *sortDescriptors = [[NSArray alloc]initWithObjects:sort, nil];
[request2 setSortDescriptors:sortDescriptors];
NSError *error = nil;
NSArray *fetchedObjects = [[self managedObjectContext] executeFetchRequest:request2 error:&error];
if (fetchedObjects == nil) {
NSLog(#"problem %#", error);
}
for (Guests *guestlastname in fetchedObjects) {
NSLog(#"Fetched Object = %#", guestlastname.guestlastname);
}
}
Am I missing a method ? have perused around but to no avail, thanks in advance.
Here is solution :
(IBAction)gettemplatebutton:(id)sender {
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSManagedObjectModel* model = [[context persistentStoreCoordinator] managedObjectModel];
NSDictionary* dict = [[NSDictionary alloc]initWithObjectsAndKeys: self.fetchedObjects, #"guestlastname",nil];
NSFetchRequest* request2 = [model fetchRequestFromTemplateWithName:#"FetchRequestA" substitutionVariables: dict];
NSError* error = nil;
NSArray *Guests2 = [context executeFetchRequest:request2 error:&error];
NSString *g3 = #"";
for(NSManagedObject *guestlastname in Guests2)
{
g3 = [g3 stringByAppendingString:[NSString stringWithFormat:#"%#\n", [guestlastname valueForKey:#"guestlastname"]]];
}
self.displaytemplateLabel.text = g3;
[_displaytemplateLabel setNumberOfLines:0];
for (NSManagedObject *guestlastname in Guests2)
{
{
NSLog(#"%#", [guestlastname valueForKey:#"guestlastname"]);
}
}
}
and added #property (nonatomic, retain) NSArray *fetchedObjects; in header file.

Lightweight migration with iCloud

I have an app that can be run two ways: Using Local Storage or using iCloud. When I use local storage, my lightweight migration works fine, but when I choose iCloud, it causing all my data to be lost. Here is the code I use to initialize the persistent store coordinator:
NSMutableDictionary *options = [NSMutableDictionary dictionary];
NSFileManager *fm = [NSFileManager defaultManager];
NSURL *ubContainer = [fm URLForUbiquityContainerIdentifier:nil];
NSURL *logsDir = [ubContainer URLByAppendingPathComponent:#"logsDir"];
[options setObject:[NSNumber numberWithBool:YES] forKey:NSMigratePersistentStoresAutomaticallyOption];
[options setObject:[NSNumber numberWithBool:YES] forKey:NSInferMappingModelAutomaticallyOption];
if ([[NSUserDefaults standardUserDefaults] boolForKey:kUserDefaultsUseICloud]) {
// Construct the dictionary that tells Core Data where the transaction log should be stored
[options setObject:#"comcompanyappname" forKey:NSPersistentStoreUbiquitousContentNameKey];
[options setObject:logsDir forKey:NSPersistentStoreUbiquitousContentURLKey];
}
// Read in xcdatamodeld
model = [NSManagedObjectModel mergedModelFromBundles:nil];
psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
NSURL *nosyncDir = [ubContainer URLByAppendingPathComponent:#"appname.nosync"];
NSURL *storeURL;
if (ubContainer) {
[fm createDirectoryAtURL:nosyncDir withIntermediateDirectories:YES attributes:nil error:nil];
storeURL = [nosyncDir URLByAppendingPathComponent:#"store.data"];
}
else{
NSString *path = [self itemArchivePath];
storeURL = [NSURL fileURLWithPath:path];
}
NSError *error = nil;
[psc lock];
if (![psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error]) {
[NSException raise:#"Open failed" format:#"Reason: %#", [error localizedDescription]];
}
[psc unlock];
// Create the managed object context
context = [[NSManagedObjectContext alloc] init];
[context setPersistentStoreCoordinator:psc];
[context setMergePolicy:NSMergeByPropertyObjectTrumpMergePolicy];
// The managed object context can manage undo, but we don't need it
[context setUndoManager:nil];

Copy item from iPod Library

I'm trying to copy an item from the iPod Library to my local storage space - for later playback. I've got the item URl but it's (ipod-library://item/item.mp3?id=2398084975506389321) any idea how to access the actual file?
Thanks,
Rick
This will work https://gist.github.com/3304992
-(void)mediaPicker:(MPMediaPickerController *)mediaPicker didPickMediaItems:(MPMediaItemCollection *)mediaItemCollection{
NSString *tempPath = NSTemporaryDirectory();
int i=1;
for (MPMediaItem *theItem in mediaItemCollection.items) {
NSURL *url = [theItem valueForProperty:MPMediaItemPropertyAssetURL];
AVURLAsset *songAsset = [AVURLAsset URLAssetWithURL:url options:nil];
AVAssetExportSession *exporter = [[AVAssetExportSession alloc] initWithAsset: songAsset presetName: AVAssetExportPresetPassthrough];
exporter.outputFileType = #"com.apple.coreaudio-format";
NSString *fname = [[NSString stringWithFormat:#"%d",i] stringByAppendingString:#".caf"];
++i;
NSString *exportFile = [tempPath stringByAppendingPathComponent: fname];
exporter.outputURL = [NSURL fileURLWithPath:exportFile];
[exporter exportAsynchronouslyWithCompletionHandler:^{
//Code for completion Handler
}];
}
[picker dismissViewControllerAnimated:YES completion:Nil];
}
use MPMediaPickerController to pick the media
This is how I'm doing it in Objective-C:
#import <CoreMedia/CoreMedia.h>
#import <AVFoundation/AVFoundation.h>
#import <CoreAudio/CoreAudio.h>
// or [NSURL URLWithString:#"ipod-library://item/item.mp3?id=2398084975506389321"]
NSURL *assetURL = [item valueForProperty:MPMediaItemPropertyAssetURL];
NSMutableData *data = [[NSMutableData alloc] init];
const uint32_t sampleRate = 16000;
const uint16_t bitDepth = 16;
const uint16_t channels = 2;
NSDictionary *opts = [NSDictionary dictionary];
AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:assetURL options:opts];
AVAssetReader *reader = [[AVAssetReader alloc] initWithAsset:asset error:NULL];
NSDictionary *settings = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:kAudioFormatLinearPCM], AVFormatIDKey,
[NSNumber numberWithFloat:(float)sampleRate], AVSampleRateKey,
[NSNumber numberWithInt:bitDepth], AVLinearPCMBitDepthKey,
[NSNumber numberWithBool:NO], AVLinearPCMIsNonInterleaved,
[NSNumber numberWithBool:NO], AVLinearPCMIsFloatKey,
[NSNumber numberWithBool:NO], AVLinearPCMIsBigEndianKey,
nil];
AVAssetReaderTrackOutput *output = [[AVAssetReaderTrackOutput alloc] initWithTrack:[[asset tracks] objectAtIndex:0] outputSettings:settings];
[asset release];
[reader addOutput:output];
[reader startReading];
// read the samples from the asset and append them subsequently
while ([reader status] != AVAssetReaderStatusCompleted) {
CMSampleBufferRef buffer = [output copyNextSampleBuffer];
if (buffer == NULL) continue;
CMBlockBufferRef blockBuffer = CMSampleBufferGetDataBuffer(buffer);
size_t size = CMBlockBufferGetDataLength(blockBuffer);
uint8_t *outBytes = malloc(size);
CMBlockBufferCopyDataBytes(blockBuffer, 0, size, outBytes);
CMSampleBufferInvalidate(buffer);
CFRelease(buffer);
[data appendBytes:outBytes length:size];
free(outBytes);
}
[output release];
Here data will contain the raw PCM data of the track. Please note that you cannot directly access the file of a song or video, only its data through this method. You can compress it using e. g. FLAC (that's how I'm processing it in my tweak).
Since MonoTouch has an 1:1 mapping to Objective-C class and method names, this should be fairly easy to copy over. :)

Strange Core Data Error During Migration

I'm absolutely pulling my hair out with Core Data after yet another bizarre error that I can't seem to solve.
This will be the fourth version of the Data model, the previous migrations have worked (albeit with some headaches).
All I'm trying to do is add a property of String type to an 'Engines' entity. I create a new version of the model (version 4) based on the current version (v3). I select the newly created version 4 as the 'Current model' and add the string property to the Engines entity. I then create a new mapping model, using v3 as the source and v4 as the target. I delete the previous Engines NSManagedObject subclass, and create a new one using the new, modified Engines entity, checking to make sure that the new String property is in the header file. I clean build the app and run it, and boom! I get this error, about 18 times:
{NSDetailedErrors=(
"Error Domain=NSCocoaErrorDomain Code=1570 \"The operation couldn\U2019t be completed. (Cocoa error 1570.)\" UserInfo=0x6015820 {NSValidationErrorObject= (entity: BodySet; id: 0x60ba670 ; data: ), NSValidationErrorKey=availableCar, NSLocalizedDescription=The operation couldn\U2019t be completed. (Cocoa error 1570.)}",
BodySet is another entity in the model, but I haven't touched it during this migration, so why is it causing all these errors?
I'm not sure if this is a help or not, but here's my Core Data code:
- (NSManagedObjectContext *) managedObjectContext {
if (managedObjectContext != nil) {
return managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
managedObjectContext = [[NSManagedObjectContext alloc] init];
[managedObjectContext setPersistentStoreCoordinator: coordinator];
}
return managedObjectContext;
}
- (NSManagedObjectModel *)managedObjectModel {
if (managedObjectModel != nil) {
return managedObjectModel;
}
NSString *path = [[NSBundle mainBundle] pathForResource:#"CoreDataTest" ofType:#"momd"];
NSURL *momURL = [NSURL fileURLWithPath:path];
managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:momURL];
return managedObjectModel;
}
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (persistentStoreCoordinator != nil) {
return persistentStoreCoordinator;
}
stringByAppendingPathComponent:#"<Project Name>.sqlite"]];
//get the DB from the Documents directory:
NSURL *storeUrl = [NSURL fileURLWithPath: [[self applicationDocumentsDirectory]
stringByAppendingPathComponent: #"<Project Name>.sqlite"]];
NSLog(#"Loading DB at path: %#", [storeUrl path]);
NSError *error = nil;
persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc]
initWithManagedObjectModel:[self managedObjectModel]];
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
if(![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil URL:storeUrl options:options error:&error]) {
/*Error for store creation should be handled in here*/
NSLog(#"Something went wrong....%#", [error description]);
}
return persistentStoreCoordinator;
}
Any help with this is very much appreciated.
i have fixed it ,maybe you can change "availableCar“ item to optional

Sample of light weight data migration of iOS core data?

Apple provide doc of light weight migration, here is good sample prior to xcode4.
Seem like a little different in xcode4.
Thanks for any clues for simple samples in xcode4 to illustrate data migration of core data.
Just note the addition of the options NSDictionary and NSPersistentStoreCoordinator initializer now has the options argument set from nil to options
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
if (__persistentStoreCoordinator != nil)
{
return __persistentStoreCoordinator;
}
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"TradiesDB.sqlite"];
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
NSError *error = nil;
__persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if (![__persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error])
{
/*
...
*/
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return __persistentStoreCoordinator;
}

Resources