Nsarray count +1 from current item & loop - cocoa

I am writing a audio player App using AVAudio Player to play mp3's from the NSDocs Dir loading them with ALAssetsLibrary subclass & AVURLAsset When I select a mp3 from the tableview it will push to a player & play but Im struggling with the skip & previous buttons
I can skip to the last track with the below code, although I'm unsure if this is correct approach sending one AVURLAsset to play rather than a queue array.
Im using apple sample project AVPlayerDemo as the foundation of the ALAssetsLibrary subclass
- (IBAction)nextButtonTapped:(id)sender {
NSArray *sourceitems = [[finalSources objectAtIndex:_selectedPath.section] items];
[sourceitems enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
id next = nil;
next = [next objectAtIndex:_selectedPath.row];
if (idx + 1 < sourceitems.count) {
next = [sourceitems objectAtIndex:idx + 1];
AssetBrowserItem *item = next;
AVURLAsset *asset = (AVURLAsset*)item.asset;
[self setURL:asset.URL];
NSLog(#"next %#", next);
//here it continues to the last object and plays,
//but i just want to skip one track and play, then click again and move
//to the next one
}
}];
}
- (void)setURL:(NSURL*)URL
{
if (mURL != URL)
{
mURL = [URL copy];
/*
Create an asset for inspection of a resource referenced by a given URL.
Load the values for the asset keys "tracks", "playable".
*/
AVURLAsset *asset = [AVURLAsset URLAssetWithURL:mURL options:nil];
NSArray *requestedKeys = [NSArray arrayWithObjects:kTracksKey, kPlayableKey, nil];
/* Tells the asset to load the values of any of the specified keys that are not already loaded. */
[asset loadValuesAsynchronouslyForKeys:requestedKeys completionHandler:
^{
dispatch_async( dispatch_get_main_queue(),
^{
/* IMPORTANT: Must dispatch to main queue in order to operate on the AVPlayer and AVPlayerItem. */
[self prepareToPlayAsset:asset withKeys:requestedKeys];
[self syncLabels:mURL];
});
}];
}
}
Any pointers would be greatly appreciated.
Thanks in advance Gav.

Related

NSTableView reloadData method causes NSProgressIndicator in all rows to update and flicker

Note: I have searched Stack Overflow for similar problems, and none of the questions I found seem to address this particular problem.
I've written a small sample app (complete Xcode project with source code available here: http://jollyroger.kicks-ass.org/stackoverflow/FlickeringTableView.zip) that plays all of the sounds in /System/Library/Sounds/ sequentially and displays the sounds in a window as they are played to show the issue I am seeing. The window in MainMenu.xib has a single-column NSTableView with one row defined as a cell template with three items in it:
an NSTextField to hold the sound name
another NSTextField to hold the sound details
a NSProgressIndicator to show play progress while the sound is playing
I have subclassed NSTableCellView (SoundsTableCellView.h) to define each of the items in the cell view so that I can access and set them when the time arises.
I have defined a MySound class that encapsulates properties and methods needed to handle the playing of sound files via AVAudioPlayer APIs. This class defines a MySoundDelegate protocol to allow the app delegate to receive events whenever sounds start or finish playing.
The application delegate adheres to the NSTableViewDelegate and NSTableViewDataSource protocols to allow it to store the table data as an array of MySound objects and update the table with relevant information when needed. It also adheres to the MySoundDelegate protocol to receive events when sounds start or finish playing. The delegate also has an NSTimer task that periodically calls a refreshWindow method to update the progress indicator for the currently playing sound.
The app delegate's refreshWindow method displays and resizes the window if needed based on the number of sounds in the list, and updates the stored reference to the associated NSProgressIndicator for the sound that is playing.
The app delegate's tableView: viewForTableColumn (NSTableViewDelegate protocol) method gets called to populate the table cells. In it, I use Apple's standard "Populating a Table View Programmatically" advice to:
check the table column identifier to ensure it matches the
identifier (sound column) I set in Interface Builder (Xcode) for the table column,
get the corresponding table cell with identifier (sound cell) by calling thisTableView makeViewWithIdentifier,
use the incoming row parameter to locate the matching array element
of the data source (app delegate sounds array), then
set the string values of NSTextFields and set the maxValue and doubleValue of the NSProgressIndicator in the cell to corresponding details of the associated sound object,
store a reference to the associated NSProgressIndicator control in the associated sound object for later updating
Here's the viewForTableColumn method:
- (NSView *)tableView:(NSTableView *)thisTableView viewForTableColumn:(NSTableColumn *)thisTableColumn row:(NSInteger)thisRow
{
SoundsTableCellView *cellView = nil;
// get the table column identifier
NSString *columnID = [thisTableColumn identifier];
if ([columnID isEqualToString:#"sound column"])
{
// get the sound corresponding to the specified row (sounds array index)
MySound *sound = [sounds objectAtIndex:thisRow];
// get an existing cell from IB with our hard-coded identifier
cellView = [thisTableView makeViewWithIdentifier:#"sound cell" owner:self];
// display sound name
[cellView.soundName setStringValue:[sound name]];
[cellView.soundName setLineBreakMode:NSLineBreakByTruncatingMiddle];
// display sound details (source URL)
NSString *details = [NSString stringWithFormat:#"%#", [sound sourceURL]];
[cellView.soundDetails setStringValue:details];
[cellView.soundDetails setLineBreakMode:NSLineBreakByTruncatingMiddle];
// update progress indicators
switch ([sound state])
{
case kMySoundStateQueued:
break;
case kMySoundStateReadyToPlay:
break;
case kMySoundStatePlaying:
if (sound.playProgress == nil)
{
sound.playProgress = cellView.playProgress;
}
NSTimeInterval duration = [sound duration];
NSTimeInterval position = [sound position];
NSLog(#"row %ld: %# (%f / %f)", (long)thisRow, [sound name], position, duration);
NSLog(#" %#: %#", [sound name], sound.playProgress);
[cellView.playProgress setMaxValue:duration];
[cellView.playProgress setDoubleValue:position];
break;
case kMySoundStatePaused:
break;
case kMySoundStateFinishedPlaying:
break;
default:
break;
}
}
return cellView;
}
And here's the refreshWindow method:
- (void) refreshWindow
{
if ([sounds count] > 0)
{
// show window if needed
if ([window isVisible] == false)
{
[window makeKeyAndOrderFront:self];
}
// resize window to fit all sounds in the list if needed
NSRect frame = [self.window frame];
int screenHeight = self.window.screen.frame.size.height;
long maxRows = ((screenHeight - 22) / 82) - 1;
long displayedRows = ([sounds count] > maxRows ? maxRows : [sounds count]);
long actualHeight = frame.size.height;
long desiredHeight = 22 + (82 * displayedRows);
long delta = desiredHeight - actualHeight;
if (delta != 0)
{
frame.size.height += delta;
frame.origin.y -= delta;
[self.window setFrame:frame display:YES];
}
// update play position of progress indicator for all sounds in the list
for (MySound *nextSound in sounds)
{
switch ([nextSound state])
{
case kMySoundStatePlaying:
if (nextSound.playProgress != nil)
{
[nextSound.playProgress setDoubleValue:[nextSound position]];
NSLog(#" %#: %# position: %f", [nextSound name], nextSound.playProgress, [nextSound position]);
}
break;
case kMySoundStateQueued:
case kMySoundStateReadyToPlay:
case kMySoundStatePaused:
case kMySoundStateFinishedPlaying:
default:
break;
}
}
}
else
{
// hide window
if ([window isVisible])
{
[window orderOut:self];
}
}
// reload window table view
[tableView reloadData];
}
During init, the application delegate scans the /System/Library/Sounds/ folder to get a list of AIFF sound files in that folder, and creates a sounds array holding sound objects for each of the sounds in that folder. The applicationDidFinishLaunching method then starts playing the first sound in the list sequentially.
The problem (which you can see by running the sample project) is that rather than only updating the top table row for the sound that is currently playing, the progress indicators in all of the following rows seem to update and flicker as well. The way it displays is somewhat inconsistent (sometimes they all flicker, and sometimes they are all blank as expected); but when they do update and flicker the progress indicators do seem to roughly correspond to the sound that is currently playing. So I am pretty sure the issue must be somehow related to the way I am updating the table; I'm just not sure where the problem is or how to solve it.
Here's a screen shot of what the window looks like to give you an idea:
Table View Screen Shot
Any ideas or guidance would be greatly appreciated!
Here's what I changed.
tableView:viewForTableColumn:row: returns "a view to display the specified row and column". The value of the progress bar is always set.
- (NSView *)tableView:(NSTableView *)thisTableView viewForTableColumn:(NSTableColumn *)thisTableColumn row:(NSInteger)thisRow
{
SoundsTableCellView *cellView = nil;
// get the table column identifier
NSString *columnID = [thisTableColumn identifier];
if ([columnID isEqualToString:#"sound column"])
{
// get the sound corresponding to the specified row (sounds array index)
MySound *sound = [sounds objectAtIndex:thisRow];
// get an existing cell from IB with our hard-coded identifier
cellView = [thisTableView makeViewWithIdentifier:#"sound cell" owner:self];
// display sound name
[cellView.soundName setStringValue:[sound name]];
// display sound details (source URL)
NSString *details = [NSString stringWithFormat:#"%#", [sound sourceURL]];
[cellView.soundDetails setStringValue:details];
// update progress indicators
// [cellView.playProgress setUsesThreadedAnimation:NO];
NSTimeInterval duration = [sound duration];
NSTimeInterval position = [sound position];
[cellView.playProgress setMaxValue:duration];
[cellView.playProgress setDoubleValue:position];
}
// end updates
// [thisTableView endUpdates];
return cellView;
}
refreshWindow is split into refreshProgress and refreshWindow. refreshProgress refreshes the row of the playing sound and is called on a timer.
- (void)refreshProgress
{
if ([sounds count] > 0)
{
[sounds enumerateObjectsUsingBlock:^(MySound *nextSound, NSUInteger rowNr, BOOL *stop)
{
switch ([nextSound state])
{
case kMySoundStatePlaying:
// refresh row
[tableView reloadDataForRowIndexes:[NSIndexSet indexSetWithIndex:rowNr]
columnIndexes:[NSIndexSet indexSetWithIndex:0]];
break;
case kMySoundStateQueued:
case kMySoundStateReadyToPlay:
case kMySoundStatePaused:
case kMySoundStateFinishedPlaying:
default:
break;
}
}];
}
}
refreshWindow refreshes the size and visibility of the window and is called when the number of sounds changes.
- (void) refreshWindow
{
if ([sounds count] > 0)
{
// show window if needed
if ([window isVisible] == false)
{
[window makeKeyAndOrderFront:self];
}
// resize window to fit all sounds in the list if needed
... calculate new window frame
}
else
{
// hide window
if ([window isVisible])
{
[window orderOut:self];
}
}
}
When a sound is removed, the row is also removed so the other rows still display the same sound and don't need an update.
- (void) soundFinishedPlaying:(MySound *)sound encounteredError:(NSError *)error
{
if (error != NULL)
{
// display an error dialog box to the user
[NSApp presentError:error];
}
else
{
// remove sound from array
NSLog(#"deleting: [%#|%#]", [sound truncatedID], [sound name]);
NSUInteger index = [sounds indexOfObject:sound];
[sounds removeObject:sound];
[tableView removeRowsAtIndexes:[NSIndexSet indexSetWithIndex:index] withAnimation:NSTableViewAnimationEffectNone];
}
// refresh window
[self refreshWindow];
// play the next sound in the queue
[self play];
}
[tableView reloadData] isn't called. sound.playProgress isn't used.

Xcode Search Bar - Can't search through NSObjects containing NSStrings

So I'm adding a search bar to my custom tableview with custom cells, and it's giving an error.
-[Game rangeOfString:options:]: unrecognized selector sent to instance 0x170244e60
2016-10-15 15:41:49.557956 CustomCellApp[25930:7889617]
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Game rangeOfString:options:]: unrecognized selector sent to instance 0x170244e60
I'm populating my tableview by an array containing NSObjects which contain strings (gamesArray)and those strings I'm getting it from a JSON array (jsonArray), all from a php file connecting to a MYSQL server which have all the strings.
Here is the code for the search bar:
-(void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
if (searchText.length == 0) {
isFiltered = NO;
} else {
isFiltered = YES;
filteredGamesArray = [[NSMutableArray alloc] init];
// ----- Problem Method ----- //
for (NSString *str in gamesArray) {
// ----- Crashes Here ----- //
NSRange stringRange = [str rangeOfString:searchText options:NSCaseInsensitiveSearch];
if (stringRange.location != NSNotFound) {
[filteredGamesArray addObject:str];
}
}
// ------------------------------------ //
}
[myTableView reloadData];
}
Here is the code for the tableView:
// Configuring the cell
Game * gameObject;
// Showing either Normal List or Filtered List
if (!isFiltered) {
gameObject = [gamesArray objectAtIndex:indexPath.row];
}
else {
gameObject = [filteredGamesArray objectAtIndex:indexPath.row];
}
Here is the code from server
It adds the strings from the server to a jsonarray which is then added to an NSObject to an array called gamesArray
NSString * gID = [[jsonArray objectAtIndex:i] objectForKey:#"id"];
NSString * gName = [[jsonArray objectAtIndex:i] objectForKey:#"gameName"];
NSString * gLabel1 = [[jsonArray objectAtIndex:i] objectForKey:#"gameLabel1"];
NSString * gLabel2 = [[jsonArray objectAtIndex:i] objectForKey:#"gameLabel2"];
NSString * gLabel3 = [[jsonArray objectAtIndex:i] objectForKey:#"gameLabel3"];
Any help would be greatly appreciated!
How can I fix this? Any ideas? Thank you
I think the problem is that when searching, the program is seeing that it has an array to search through, gamesArray, but when going inside it has 5 NSObjects, each containing strings, so it's not working because it doesn't know what to do.
As a solution, how can I make it that the search bar searches through the gamesArray, directly at 1 NSObject, the gameName , reading all the strings in that NSObject which is called gName
For example, this piece of code targets a specific NSObject inside the gamesArray
[[gamesArray objectAtIndex:indexPath.row] valueForKey:#"gameName"];
My first guess is: objects in games array are not strings, they are Game objects.
for (Game *game in gamesArray) {
NSString *str = game.fieldOfInterest;
// ----- Crashes Here ----- //
NSRange stringRange = [str rangeOfString:searchText options:NSCaseInsensitiveSearch];
if (stringRange.location != NSNotFound) {
[filteredGamesArray addObject:game];
}
}
This should address the issue of your crash. If the search behavior is not correct, please ask a new question related to your new issue. As a best guess, I would suggest you inspect your table delegate methods for how they behave when filtered is true.

contacts data in xcode

Everybody..
I am trying to develop an app for iPhone which basicly deals with ABAddressBook and contacts data...
My goal is to send all the contacts data (at first step, names and phones) in a file by e-mail..
Now, I am trying to reach the contacts data, and I want to add them to two different arrays, names and phones arrays..
At first, I am trying to see all the datas in the screen when i pressed a button "List Contacts". the datas should be seen on the screen. and then when i pressed the second button "Send Contacts", it should send the file to an e-mail account.. This is how my apps will work..
I am having problems at showing the data on the screen.. I wrote something but it doesn't give anything on the screen in a textView..
Can you help me to solve this problem?
Here's the code for listing the contacts (listCon method):
-(IBAction)listCon:(id)sender
{
NSMutableArray *names = [[NSMutableArray alloc] init];
NSMutableArray *numbers1= [[NSMutableArray array] init];
NSMutableArray *numbers2= [[NSMutableArray array] init];
NSMutableArray *numbers3= [[NSMutableArray array] init];
ABAddressBookRef addressBook = ABAddressBookCreate();
if (addressBook != nil)
{
NSLog(#"Successfully accessed the address book.");
CFArrayRef allPeople = ABAddressBookCopyArrayOfAllPeople(addressBook);
CFIndex nPeople= ABAddressBookGetPersonCount(addressBook);
NSUInteger peopleCounter = 0;
for (peopleCounter = 0;peopleCounter < nPeople; peopleCounter++)
{
ABRecordRef thisPerson = CFArrayGetValueAtIndex(allPeople,peopleCounter);
NSString *contactFirstLast = [NSString stringWithFormat:#"%,%",ABRecordCopyValue(thisPerson, kABPersonFirstNameProperty), ABRecordCopyValue(thisPerson,kABPersonLastNameProperty)];
[names insertObject:contactFirstLast atIndex:peopleCounter];
ABMultiValueRef phoneNumbers = ABRecordCopyValue(thisPerson,kABPersonPhoneProperty);
NSString *firstPhone = (__bridge_transfer NSString*) ABMultiValueCopyValueAtIndex(phoneNumbers, 0);
NSString *secondPhone = (__bridge_transfer NSString*) ABMultiValueCopyValueAtIndex(phoneNumbers, 1);
NSString *thirdPhone = (__bridge_transfer NSString*) ABMultiValueCopyValueAtIndex(phoneNumbers, 2);
[numbers1 insertObject:firstPhone atIndex:peopleCounter];
[numbers2 insertObject:secondPhone atIndex:peopleCounter];
[numbers3 insertObject:thirdPhone atIndex:peopleCounter];
}
}
myView.text=[names componentsJoinedByString:#"\n\n"];
myView.text=[numbers1 componentsJoinedByString:#"\n\n"];
myView.text=[numbers2 componentsJoinedByString:#"\n\n"];
myView.text=[numbers3 componentsJoinedByString:#"\n\n"];
}
Just glancing at your code, you can't do this:
NSString *contactFirstLast = [NSString stringWithFormat:#"%,%",ABRecordCopyValue(thisPerson, kABPersonFirstNameProperty), ABRecordCopyValue(thisPerson,kABPersonLastNameProperty)];
There are several errors: first off % in your stringWithFormat: is not a format specifier; you probably are thinking of %#. Second off, copying the value of kABPersonFirstNameProperty will return a CFStringRef, and that's not what you want to display the name in a text field. You'll have to toll-free bridge the result of ABRecordCopyValue(). You can do this by adding this line - (__bridge_transfer NSString *) - in front of your ABRecordCopyValue()'s. With all the corrections, it should look like this:
NSString *contactFirstLast = [NSString stringWithFormat:#"%#,%#", (__bridge_transfer NSString *)ABRecordCopyValue(thisPerson, kABPersonFirstNameProperty), (__bridge_transfer NSString *)ABRecordCopyValue(thisPerson,kABPersonLastNameProperty)];
Hope this help (might not cover all errors)!

Core Data, caching NSManagedObjects in NSMutableDictionary, Problems

I am writing a dictionary application, and i am trying to import raw data from strings, one string per word. Amongst other things, the raw input strings contain the names of the parts of speech the corresponding word belongs to. In my datamodel I have a separate entity for Words and PartOfSpeech, and i want to create one entity of the type PartOfSpeech for each unique part of speech there may be in the input strings, and establish the relationships from the Words to the relevant pars of speech. The PartOfSpeech entity has just one Atribute, name, and one-to-many relationship to the Word:
My first implementation of getting unique PartOfSpeech entities involved caching them in a mutable array and filtering it each time with a predicate. It worked, but it was slow. I decided to speed it up a bit by caching the PartsOfSpeech in an NSDictionary, and now when i try and save the datastore after the import, i get the error "Cannot save objects with references outside of their own stores.". It looks like the problem is in the dictionary, but how can i solve it?
Here is the code that worked:
(in both sniplets managedObjectContext is an ivar, and processStringsInBackground: method runs on a background thread using performSelectorInBackground:withObject: method)
- (void) processStringsInBackground:(NSFetchRequest *)wordStringsReq {
NSError *err = NULL;
NSFetchRequest *req = [[NSFetchRequest alloc] init];
[req setEntity:[NSEntityDescription entityForName:#"PartOfSpeech" inManagedObjectContext:managedObjectContext]];
err = NULL;
NSMutableArray *selectedPartsOfSpeech = [[managedObjectContext executeFetchRequest:req error:&err] mutableCopy];
NSPredicate *p = [NSPredicate predicateWithFormat:#"name like[c] $name"];
// NSPredicate *formNamePredicate = [NSPredicate predicateWithFormat:<#(NSString *)predicateFormat#>]
...
for (int i = 0; i < count; i++){
...
currentPos = [self uniqueEntityWithName:#"PartOfSpeech" usingMutableArray:selectedPartsOfSpeech predicate:p andDictionary:[NSDictionary dictionaryWithObject:partOfSpeech forKey:#"name"]];
...
}
}
- (NSManagedObject *) uniqueEntityWithName:(NSString *) entityName usingMutableArray:(NSMutableArray *)objects predicate:(NSPredicate *)aPredicate andDictionary:(NSDictionary *) params {
NSPredicate *p = [aPredicate predicateWithSubstitutionVariables:params];
NSArray *filteredArray = [objects filteredArrayUsingPredicate:p];
if ([filteredArray count] > 0) {
return [filteredArray objectAtIndex:0];
}
NSManagedObject *newObject = [NSEntityDescription insertNewObjectForEntityForName:entityName inManagedObjectContext:managedObjectContext];
NSArray *dicKeys = [params allKeys];
for (NSString *key in dicKeys) {
[newObject willChangeValueForKey:key];
[newObject setPrimitiveValue:[params valueForKey:key] forKey:key];
[newObject didChangeValueForKey:key];
}
[objects addObject:newObject];
return newObject;
}
And here is the same, but with caching using NSMutableDictionary, which fails to save afterwards:
- (void) processStringsInBackground:(NSFetchRequest *)wordStringsReq {
NSError *err = NULL;
[req setEntity:[NSEntityDescription entityForName:#"PartOfSpeech" inManagedObjectContext:managedObjectContext]];
NSArray *selectedPartsOfSpeech = [managedObjectContext executeFetchRequest:req error:&err];
NSMutableDictionary *partsOfSpeechChache = [[NSMutableDictionary alloc] init];
for (PartOfSpeech *pos in selectedPartsOfSpeech) {
[partsOfSpeechChache setObject:pos forKey:pos.name];
}
...
for (int i = 0; i < count; i++){
...
currentPos = [self uniqueEntity:#"PartOfSpeech" withName:partOfSpeech usingDictionary:partsOfSpeechChache];
...
}
}
- (NSManagedObject *)uniqueEntity:(NSString *) entityName withName:(NSString *) name usingDictionary:(NSMutableDictionary *) dic {
NSManagedObject *pos = [dic objectForKey:name];
if (pos != nil) {
return pos;
}
NSManagedObject *newPos = [NSEntityDescription insertNewObjectForEntityForName:entityName inManagedObjectContext:managedObjectContext];
[newPos willChangeValueForKey:#"name"];
[newPos setPrimitiveValue:name forKey:#"name"];
[newPos didChangeValueForKey:#"name"];
[dic setObject:newPos forKey:name];
return newPos;
}
Could you help me to find the problem?
Best regards,
Timofey.
The error is caused by forming a relationship between managedObjects that don't share the same persistent store. You can do that by:
Creating a managed object with initialization without inserting it into a context.
Deleting a managed object from a context while retaining it in another object e.g. array, and then forming a relationship with it.
Accidentally creating two Core Data stacks so that you have two context and two stores.
Confusing configurations in a multi-store context.
I don't see any part of the code you provided that would trigger the problem.
It turns out, that it is wrong to pass NSManagedContext to a thread different from the one it was created in. Instead, one should pass the NSPersistenceStroreCoordinator to another thread, and create a new managed object context there. In order to merge the changes into the "main" context, one should save the other thread's context, receive the notification on the completion of the save on the main thread and merge the changes (see apple docs regarding Core Data and concurrency, can't give you the link, because i read it in Xcode). So here are the changes i made to my code to make it work (only posting the changed lines):
— (void) processStringsInBackground:(NSDictionary *) params {
NSFetchRequest *wordStringsReq = [params objectForKey:#"wordStringsReq"];
NSPersistentStoreCoordinator *coordinator = [params objectForKey:#"coordinator"];
NSManagedObjectContext *localContext = [[NSManagedObjectContext alloc] init];
[localContext setPersistentStoreCoordinator:coordinator];
(all the references to the managedObjectContext were replaced by localContext
And on the main thread, i call this method thusly:
.......
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:req, #"wordStringsReq", persistentStoreCoordinator, #"coordinator", nil]; //the params i pass to the background method
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(handleNotification:) name:#"NSManagingContextDidSaveChangesNotification" object:nil]; //register to receive the notification from the save
[self performSelectorInBackground:#selector(processStringsInBackground:) withObject:dict];
}
- (void) handleNotification:(NSNotification *) notific {
NSLog(#"got notification, %#", [notific name]);
[managedObjectContext mergeChangesFromContextDidSaveNotification:notific];
}
Good luck!
Good answers, though a bit dated. The fine documentation notes that the main NSManagedObjectContext should never be used in worker threads. Instead, create a separate NSManagedObjectContext private to the worker using the "main" MOC as a parent, and then that instead. Here's the relevant "Concurrency" page from the Core Data Programming Guide:
https://developer.apple.com/library/prerelease/ios/documentation/Cocoa/Conceptual/CoreData/Concurrency.html
Snippet (Swift)
let jsonArray = … //JSON data to be imported into Core Data
let moc = … //Our primary context on the main queue
let privateMOC = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType)
privateMOC.parentContext = moc
privateMOC.performBlock {
for jsonObject in jsonArray {
let mo = … //Managed object that matches the incoming JSON structure
//update MO with data from the dictionary
}
do {
try privateMOC.save()
} catch {
fatalError("Failure to save context: \(error)")
}
}

NSRuleEditor: Criteria for new row

When I hit the "+" button on a row in NSRuleEditor, a new row is created. How can I take influence on the criteria used for that row.
It seems NSRuleEditor defaults to selecting the first criterion sequentially from the list of possible values. I would much rather have the new row match the row where the "+" was clicked.
I was able to fake it by subclassing a private method:
- (void)_addOptionFromSlice:(id)slice ofRowType:(unsigned int)type
{
int rowIndex = [(NSRuleEditorViewSlice*)slice rowIndex];
NSArray *criteriaForRow = [self criteriaForRow:rowIndex];
NSArray *displayValuesForRow = [self displayValuesForRow:rowIndex];
self.template = [NSArray arrayWithObjects:criteriaForRow, displayValuesForRow, nil];
[super _addOptionFromSlice:slice ofRowType:type];
}
- (void)insertRowAtIndex:(NSInteger)rowIndex withType:(NSRuleEditorRowType)rowType asSubrowOfRow:(NSInteger)parentRow animate:(BOOL)shouldAnimate
{
[super insertRowAtIndex:rowIndex withType:rowType asSubrowOfRow:parentRow animate:shouldAnimate];
NSArray *template = self.template;
if (template != nil) {
[self setCriteria:[template objectAtIndex:0] andDisplayValues:[template objectAtIndex:1] forRowAtIndex:rowIndex];
}
}

Resources