Iterating through NSArray with asynchronous block - objective-c

Not sure if this is the right title for this question or not..anyways,
I have an array of dictionaries and I am iterating through the dictionaries, passing in a block that gets executed sometimes in the future (asynchronous) and saving the result in a dictionary. Here's my method:
- (void)elcImagePickerController:(ELCImagePickerController *)picker didFinishPickingMediaWithInfo:(NSArray *)info {
NSLog(#"%#", info);
NSMutableDictionary *images = [NSMutableDictionary dictionaryWithCapacity:[info count]];
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
for (NSDictionary *dict in info) {
NSURL *assetURL = [dict objectForKey:UIImagePickerControllerReferenceURL];
[library assetForURL:assetURL
resultBlock:^(ALAsset *asset) {
NSString *filename = asset.defaultRepresentation.filename;
UIImage *image = [dict objectForKey:UIImagePickerControllerOriginalImage];
[images setObject:filename forKey:image];
}
failureBlock:^(NSError *error) {
[SVProgressHUD dismissWithError:#"Error occured."];
}
];
}
NSLog(#"%#", images);
[library autorelease];
[self dismissImagePickerVC];
}
Ofcourse, at the end of this method, images is empty, but my question is, how can I get a call back when all the blocks for each iteration has been executed?
Thansk

From Apple's documentation:
When the enumeration is done, enumerationBlock is invoked with group set to nil.
When enumeration is done, your first block will see asset == nil.

Related

Creating video preview on iCarousel using PFFiles from Parse IOS

I have created an iCarousel View and am trying to display a video preview in each "cell"/view.
I have the videos stored in Parse and am trying to
Query from the cloud
Retrieve the data from the PFFile
Convert the data to URL
Play URL using AVPlayer
Here my code so far.
-(void)getPast{
dataArray = [[NSMutableArray alloc]init];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
PFQuery *query = [PFQuery queryWithClassName:#"History"];
[query whereKey:#"Location" containsString:[defaults objectForKey:#"location"]];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
pastArray = [NSMutableArray arrayWithArray:objects];
for (PFObject *files in objects){
PFFile *file = [files objectForKey:#"File"];
[file getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {
[dataArray addObject:data];
}];
}
[self.carouselView reloadData];
}];
}
Im getting an error saying that my dataArray is empty,
I think the problem here could be that since I'm querying in the background, the For loop is finishing before I have received the data and therefore the array is empty, although I could be wrong and I don't know how to fix this even if I was right.
Code for displaying preview
- (UIView *)carousel:(iCarousel *)carousel viewForItemAtIndex:(NSUInteger)index reusingView:(UIView *)view {
PFObject *object = [pastArray objectAtIndex:index];
NSData *data = [dataArray objectAtIndex:index];
NSString *dataString = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
NSURL *URL = [NSURL URLWithString:dataString];
NSLog(#"URL %#",URL);
view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 275)];
self.playerViewController = [[AVPlayerViewController alloc]init];
self.playerViewController.player = [AVPlayer playerWithURL:URL];
self.playerViewController.view.frame = view.bounds;
self.playerViewController.showsPlaybackControls = NO;
[view addSubview:self.playerViewController.view];
view.autoresizesSubviews = YES;
self.playerViewController.player.actionAtItemEnd = AVPlayerActionAtItemEndNone;
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(playerItemDidReachEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:self.playerViewController.player.currentItem];
[self.playerViewController.player play];
return view;
}
How can I fix my code so that each view autoplays the PFFile video corresponding to its index in the Array.
My problems:
Array is empty
Playing content for each view isn't working
Ps. Im aware that I'm not using PFObject *object.
As you guessed the for cycle finishes it's execution way before the blocks are called, you have to make sure the data is loaded before you call reloadData
The first thing that comes to my mind on how to handle this will be something like
for (PFObject *files in objects){
PFFile *file = [files objectForKey:#"File"];
[file getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {
[dataArray addObject:data];
[self checkData];
}];
}
- (void)checkData {
//Check the if the data is completed
if(dataArray.count == numberOfFiles) { //Maybe a more complex if is required here but you get the idea
//All files are downloaded
dispatch_async(dispatch_get_main_queue(), ^{
//We are sure the data is ready so we reload it
[self.carouselView reloadData];
});
}
}
Also you should always check if NSData is valid before loading it

NSManagedObjectContext crashing when accessed on external thread

I'm currently having a threading issue with the managedObjectContext within my application. Currently, I have a background thread running that MUST be in the background, but accesses the managedObjectContext at the same time. Another ViewController calls on the method processAllApplications shown below that then calls checkCompletedApplicationsFor24HourExpiration which then calls getAppsWithStatus. The thread seems to be currently locked causing this operation to halt where the warning below is. I need a way to process this through and am quite a noob when it comes to Core Data. Would anyone be able to advise. I was reading that I may have to create multiple instances of my managedObject and merge them. How would I go about that if that is the case?
AppDelegate:
- (NSManagedObjectContext *)managedObjectContext
{
[__managedObjectContext lock];
if (__managedObjectContext != nil) {
[__managedObjectContext unlock];
return __managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
__managedObjectContext = [[NSManagedObjectContext alloc] init];
[__managedObjectContext setPersistentStoreCoordinator:coordinator];
}
[__managedObjectContext unlock];
return __managedObjectContext;
}
- (NSMutableArray*)getAppsWithStatus:(int)intStatus {
NSLog(#"%i on main thread getAppsWithStatus", [NSThread currentThread].isMainThread);
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:#"Application" inManagedObjectContext:self.managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entityDescription];
// Set example predicate and sort orderings...
NSNumber *status = [NSNumber numberWithInt:intStatus];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"status = %# && username = %#", status, [[NSUserDefaults standardUserDefaults] objectForKey:#"username"]];
#warning FAILS HERE INTO ABYSS
[request setPredicate:predicate];
NSError *error = nil;
NSMutableArray* applications = [[NSMutableArray alloc] initWithArray:[self.managedObjectContext executeFetchRequest:request error:&error]];
for (Application* eachApp in applications)
eachApp.applicationNumber = nil;
[self saveDB];
return applications;
}
- (void)processAllApplications:(id)userInfo {
[self.processApplicationsLock lock];
if ([[NSUserDefaults standardUserDefaults] objectForKey:#"username"] == nil) return; // Not logged in
NSLog(#"processing");
[self checkCompletedApplicationsFor24HourExpiration];
[self alertFor12HourCompletedApplications];
[self alertForExpiredDraftApplications];
if ([DeleteAllDraftApplicationsForCurrentApplicationYear isSatisifiedByDate:[DateTimeFactory currentApplicationDate]]) {
[self deleteExpiredApps];
}
[self performSelector:#selector(sendApplications:) withObject:nil afterDelay:3];
[self.processApplicationsLock unlock];
}
- (void)checkCompletedApplicationsFor24HourExpiration {
NSLog(#"OutboxSender - (void)checkCompletedApplicationsFor24HourExpiration");
NSLog(#"%i on main thread checkCompletedApplicationsFor24HourExpiration", [NSThread currentThread].isMainThread);
NSArray* completedApps = [self getAppsWithStatus:STATUS_COMPLETED];
NSDate* targetDate = [self offsetDate:[DateTimeFactory currentApplicationDate] withDay:-1 withMonth:0 withHour:0];
for (Application* theApplication in completedApps) {
if ([MoveCompletedApplicationToDraftApplicationSpec isSatisfiedByApplication:theApplication cutOffDate:targetDate]) {
NSLog(#"Sending To draft with date: %#", theApplication.submittedDate);
theApplication.status = [NSNumber numberWithInt:STATUS_DRAFT];
[self deleteSignatures:theApplication];
}
}
NSString* message = [NSString stringWithFormat:#"%i completed application/s have been sent to drafts", [completedApps count]];
echo_Alert(#"", message);
[self saveDB];
}
create separate managed object context
+(NSManagedObjectContext *)getManagedObjectContext
{
NSManagedObjectContext *managedObjectContext;
#try {
NSPersistentStoreCoordinator * coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[managedObjectContext setPersistentStoreCoordinator: coordinator];
}
}
#catch (NSException *exception) {
NSLog(#"Exception occur %#",exception);
}
return managedObjectContext;
Use this separate managed object context in your fetching method,
- (NSMutableArray*)getAppsWithStatus:(int)intStatus {
NSMutableArray * mutableObjects;
NSLog(#"%i on main thread getAppsWithStatus", [NSThread currentThread].isMainThread);
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:#"Application" inManagedObjectContext:[self getManagedObjectContext]]; // Here use separate managed object context
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entityDescription];
// Set example predicate and sort orderings...
NSNumber *status = [NSNumber numberWithInt:intStatus];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"status = %# && username = %#", status, [[NSUserDefaults standardUserDefaults] objectForKey:#"username"]];
#warning FAILS HERE INTO ABYSS
[request setPredicate:predicate];
NSError *error = nil;
NSMutableArray* applications = [[NSMutableArray alloc] initWithArray:[[self getManagedObjectContext] executeFetchRequest:request error:&error]];
NSMutableArray * resultedArray = [applications mutableCopy];
NSMutableArray * objectIds = [[NSMutableArray alloc] initWithCapacity:[resultedArray count]];
for (NSManagedObject *obj in resultedArray) {
[objectIds addObject:obj.objectID];
}
mutableObjects = [[NSMutableArray alloc] initWithCapacity:[objectIds count]];
for (NSManagedObjectID * objectID in objectIds) {
NSManagedObject * obj = [self.managedObjectContext
objectWithID:objectID]; // Here use self.managedObjectContext in which you already created.
[mutableObjects addObject:obj];
}
for (Application* eachApp in mutableObjects)
eachApp.applicationNumber = nil;
[self saveDB];
return mutableObjects;
}

NSMutableArray isn't adding my NSString

Im trying to add a NSString to an NSMutableArray and then make the array into NSData and save it with NSUserDefaults. But the array is always nil.
Here is my code:
- (void)viewDidLoad {
[super viewDidLoad];
library = [[ALAssetsLibrary alloc] init];
groups = [[NSMutableArray alloc] init];
userDefaults = [[NSUserDefaults alloc] init];
NSData *data = [userDefaults objectForKey:#"GroupArray"];
groups = [NSKeyedUnarchiver unarchiveObjectWithData:data];
NSLog(#"%i", [groups count]);
}
-(IBAction)newFolder {
if (textField.text != nil) {
NSString *string = textField.text.capitalizedString;
[library addAssetsGroupAlbumWithName:string
resultBlock:^(ALAssetsGroup *group) {
NSLog(#"Created a folder named: %#", group);
}
failureBlock:^(NSError *error) {
NSLog(#"An error occured! - %#", error);
}
];
[groups addObject:string];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:groups];
[userDefaults setObject:data forKey:#"GroupArray"];
NSLog(#"%i", [groups count]);
}
[self.view sendSubviewToBack:subView];
}
When the app starts i get a message in the console that the array is NULL. When I try to add the string the NSLog(#"%i", [groups count]); always return 0.
Why does this happen?
userDefaults = [[NSUserDefaults alloc] init];
NSData *data = [userDefaults objectForKey:#"GroupArray"];
In this case, data will be nil when the code is executed for the first time, since there is yet no "GroupArray" property present.
groups = [NSKeyedUnarchiver unarchiveObjectWithData:data];
This causes groups be become nil as well, because calling unarchiveObjectWithData: with nil as an argument will return nil as well.
And because of all that, in -newFolder, [groups addObject:string] becomes [nil addObject:string]
Calling a method on nil is allowed in Objective-C, so you get no exception there. The return value of any method called on nil is, again, nil or 0.
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:groups];
This causes data to be nil because groups is nil.
So you are always calling:
[userDefaults setObject:data forKey:#"GroupArray"];
with data = nil
Quick fix:
Add if (groups == nil) groups = [NSMutableArray array]; to the beginning of -newFolder
In view didLoad method every time you are initialising new instance of NSUserDefaults and calling
NSData *data = [userDefaults objectForKey:#"GroupArray"]; on the newly created object
which will return nil all the time because in the new instance there wont be any object for key #"GroupArray". Instead replace userDefaults with singleton object [NSUserDefaults standardUserDefaults]
Modify your code as shown below
- (void)viewDidLoad
{
[super viewDidLoad];
library = [[ALAssetsLibrary alloc] init];
NSData *data = [[NSUserDefaults standardUserDefaults] objectForKey:#"GroupArray"];
groups = [NSKeyedUnarchiver unarchiveObjectWithData:data];
if (!groups)
{
groups = [[NSMutableArray alloc] init];
}
NSLog(#"group %#", groups);
NSLog(#"%i", [groups count]);
}
Your newFolder method
-(IBAction)newFolder
{
if (textField.text != nil)
{
NSString *string = textField.text.capitalizedString;
[library addAssetsGroupAlbumWithName:string
resultBlock:^(ALAssetsGroup *group) {
NSLog(#"Created a folder named: %#", group);
}
failureBlock:^(NSError *error) {
NSLog(#"An error occured! - %#", error);
}
];
[groups addObject:string];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:groups];
[[NSUserDefaults standardUserDefaults] setObject:data forKey:#"GroupArray"];
[[NSUserDefaults standardUserDefaults] synchronize];
NSLog(#"%i", [groups count]);
}
[self.view sendSubviewToBack:subView];
}
This should work. As it worked for me.

How to open view controller after data has been loaded into model object?

How can I check if the NSData dataWithContentsOfURLparsing in my secondary thread are finished? When every image is finished I want to open my view controller. Not before. Now I can open my view controller directly, and sometimes if I'm to quick my table view has no images, because they're not finished yet. Any ideas?
The following code happens in didFinishLaunchingWithOptions in AppDelegate. Im using the SBJSON framework for parsing.
(Im using the storyboard in this project so there's no code for opening the first view controller)
Code:
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"json_template" ofType:#"json"];
NSString *contents = [NSString stringWithContentsOfFile: filePath encoding: NSUTF8StringEncoding error: nil];
SBJsonParser *jsonParser = [[SBJsonParser alloc] init];
NSMutableDictionary *json = [jsonParser objectWithString: contents];
tabs = [[NSMutableArray alloc] init];
jsonParser = nil;
//parsing json into model objects
for (NSString *tab in json)
{
Tab *tabObj = [[Tab alloc] init];
tabObj.title = tab;
NSDictionary *categoryDict = [[json valueForKey: tabObj.title] objectAtIndex: 0];
for (NSString *key in categoryDict)
{
Category *catObj = [[Category alloc] init];
catObj.name = key;
NSArray *items = [categoryDict objectForKey:key];
for (NSDictionary *dict in items)
{
Item *item = [[Item alloc] init];
item.title = [dict objectForKey: #"title"];
item.desc = [dict objectForKey: #"description"];
item.url = [dict objectForKey: #"url"];
if([dict objectForKey: #"image"] != [NSNull null])
{
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^(void)
{
NSURL *imgUrl = [NSURL URLWithString: [dict objectForKey: #"image"]];
NSData *imageData = [NSData dataWithContentsOfURL: imgUrl];
dispatch_async( dispatch_get_main_queue(), ^(void)
{
item.image = [UIImage imageWithData: imageData];
});
});
}
else
{
UIImage *image = [UIImage imageNamed: #"standard3.png"];
item.image = image;
}
[catObj.items addObject: item];
}
[tabObj.categories addObject: catObj];
}
[tabs addObject: tabObj];
}
//sort array
[tabs sortUsingComparator:^NSComparisonResult(id obj1, id obj2){
Tab *r1 = (Tab*) obj1;
Tab *r2 = (Tab*) obj2;
return [r1.title caseInsensitiveCompare: r2.title];
}];
/***** END PARSING JSON *****/
[[UINavigationBar appearance] setTitleTextAttributes: #{
UITextAttributeTextShadowOffset: [NSValue valueWithUIOffset:UIOffsetMake(0.0f, 0.0f)],
UITextAttributeFont: [UIFont fontWithName:#"GreatLakesNF" size:20.0f]
}];
UIImage *navBackgroundImage = [UIImage imageNamed:#"navbar.png"];
[[UINavigationBar appearance] setBackgroundImage:navBackgroundImage forBarMetrics:UIBarMetricsDefault];
UIImage *backButtonImage = [[UIImage imageNamed:#"backBtn.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 0, 0, 0)];
UIImage *backButtonSelectedImage = [[UIImage imageNamed:#"backBtn_selected.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 0, 0, 0)];
[[UIBarButtonItem appearance] setBackButtonBackgroundImage:backButtonImage forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
[[UIBarButtonItem appearance] setBackButtonBackgroundImage:backButtonSelectedImage forState: UIControlStateHighlighted barMetrics:UIBarMetricsDefault];
return YES;
Also, if this way of parsing is bad, please tell me!
First of all, you shouldn't use such way of downloading any content from remote host.
There are lots of libraries like AFNetworking, ASIHTTPRequest
which work around CFNetwork or NSURLConnection to handle such things as redirects, error handling etc.
So you should definitely move to one of those (or implement your own based on NSURLConnection).
As a direct answer to your question:
You should use some kind of identifier for counting downloaded images (i.e. for-loop iteration counter) and pass it via +[UINotificationCenter defaultCenter] as a parameter of some custom notification.
Example (assuming that you are blocking current thread by +[NSData dataWithContentsOfURL:]):
for (int i = 0; i < 10; i++) {
[[NSNotificationCenter defaultCenter] postNotificationName:#"someCustomNotificationClassName" object:nil userInfo:#{ #"counter" : #(i) }];
}
More expanded example of NSNotification-based approach:
- (id)init {
self = [super init];
if (self) {
// subscribing for notification
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(handleDataDownload:) name:#"someCustomNotificationClassName" object:nil];
}
return self;
}
- (void)dealloc {
// unsubscribing from notification on -dealloc
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
#pragma mark - downloading delegation
- (void)handleDataDownload:(NSNotification *)notification {
NSDictionary *userInfo = [notification userInfo];
int counter = [userInfo[#"counter"] intValue];
if (counter == 10) {
// do some work afterwards
// assuming that last item was downloaded
}
}
Also you can use callback technique to manage handling of download state:
void (^callback)(id result, int identifier) = ^(id result, int identifier) {
if (identifier == 10) {
// do some work afterwards
}
};
for (int i = 0; i < 10; i++) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, kNilOptions), ^{
// some downloading stuff which blocks thread
id data = nil;
callback(data, i);
});
}

Stopping a loop

As explained in my earlier question …
This code …
- (void)syncKVO:(id)sender {
NSManagedObjectContext *moc = [self managedObjectContext];
[syncButton setTitle:#"Syncing..."];
NSString *dateText = (#"Last Sync : %d", [NSDate date]);
[syncDate setStringValue:dateText];
NSEntityDescription *entityDescription = [NSEntityDescription
entityForName:#"projects" inManagedObjectContext:moc];
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
[request setEntity:entityDescription];
NSError *error = nil;
NSArray *array = [moc executeFetchRequest:request error:&error];
if (array == nil)
{
NSAlert *anAlert = [NSAlert alertWithError:error];
[anAlert runModal];
}
NSArray *namesArray = [array valueForKey:#"name"];
NSPredicate *predicate = [CalCalendarStore taskPredicateWithCalendars:[[CalCalendarStore defaultCalendarStore] calendars]];
NSArray *tasksNo = [[CalCalendarStore defaultCalendarStore] tasksWithPredicate:predicate];
NSArray *tasks = [tasksNo valueForKey:#"title"];
NSMutableArray *namesNewArray = [NSMutableArray arrayWithArray:namesArray];
[namesNewArray removeObjectsInArray:tasks];
NSLog(#"%d", [namesNewArray count]);
NSInteger *popIndex = [calenderPopup indexOfSelectedItem];
//Load the array
CalCalendarStore *store = [CalCalendarStore defaultCalendarStore];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
NSString *supportDirectory = [paths objectAtIndex:0];
NSString *fileName = [supportDirectory stringByAppendingPathComponent:#"oldtasks.plist"];
NSMutableArray *oldTasks = [[NSMutableArray alloc] initWithContentsOfFile:fileName];
[oldTasks removeObjectsInArray:namesArray];
NSLog(#"%d",[oldTasks count]);
//Use the content
NSPredicate* taskPredicate = [CalCalendarStore taskPredicateWithCalendars:[[CalCalendarStore defaultCalendarStore] calendars]];
NSArray* allTasks = [[CalCalendarStore defaultCalendarStore] tasksWithPredicate:taskPredicate];
// Get the calendar
CalCalendar *calendar = [[store calendars] objectAtIndex:popIndex];
// Note: you can change which calendar you're adding to by changing the index or by
// using CalCalendarStore's -calendarWithUID: method
// Loop, adding tasks
for(NSString *title in namesNewArray) {
// Create task
CalTask *task = [CalTask task];
task.title = title;
task.calendar = calendar;
// Save task
if(![[CalCalendarStore defaultCalendarStore] saveTask:task error:&error]) {
NSLog(#"Error");
// Diagnostic error handling
NSAlert *anAlert = [NSAlert alertWithError:error];
[anAlert runModal];
}
}
NSMutableArray *tasksNewArray = [NSMutableArray arrayWithArray:tasks];
[tasksNewArray removeObjectsInArray:namesArray];
NSLog(#"%d", [tasksNewArray count]);
for(NSString *title in tasksNewArray) {
NSManagedObjectContext *moc = [self managedObjectContext];
JGManagedObject *theParent =
[NSEntityDescription insertNewObjectForEntityForName:#"projects"
inManagedObjectContext:moc];
[theParent setValue:nil forKey:#"parent"];
// This is where you add the title from the string array
[theParent setValue:title forKey:#"name"];
[theParent setValue:[NSNumber numberWithInt:0] forKey:#"position"];
}
for(CalTask* task in allTasks)
if([oldTasks containsObject:task.title]) {
[store removeTask:task error:nil];
}
// Create a predicate for an array of names.
NSPredicate *mocPredicate = [NSPredicate predicateWithFormat:#"name IN %#", oldTasks];
[request setPredicate:mocPredicate];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES];
[request setSortDescriptors:[NSArray arrayWithObject:sortDescriptor]];
// Execute the fetch request put the results into array
NSArray *resultArray = [moc executeFetchRequest:request error:&error];
if (resultArray == nil)
{
// Diagnostic error handling
NSAlert *anAlert = [NSAlert alertWithError:error];
[anAlert runModal];
}
// Enumerate through the array deleting each object.
// WARNING, this will delete everything in the array, so you may want to put more checks in before doing this.
for (JGManagedObject *objectToDelete in resultArray ) {
// Delete the object.
[moc deleteObject:objectToDelete];
}
//Save the array
[namesArray writeToFile:fileName atomically:YES];
[syncButton setTitle:#"Sync Now"];
NSLog(#"Sync Completed");
}
triggers this code …
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if ([keyPath isEqualToString:#"name"]) {
[self performSelector:#selector(syncKVO:)];
}
}
because I am adding objects and the KVO method is triggered when the Core Data 'name' property is changed.
I need to stop the observeValueForKeyPath:ofObject:change:context: method if it was triggered by the syncKVO method. How would I do this?
The simplest thing you could do is use an instance variable which keeps track of whether you’re syncing and ignore observer changes when it’s set. It may be better to stop and start observing at the beginning and end of syncKVO:, but it depends on what you’re actually observing: you don’t want to mass unsubscribe and resubscribe if you’re watching a large collection.
Looking at your code I wonder if you really want to do this syncing when entities are saved, and not as soon as the object keys changed. I think you’d be better off ditching observing completely and watching for the NSManagedObjectContextObjectsDidChangeNotification, using the values of the userInfo keys specified in the CoreData documentation to determine which entities need to be updated.