How to set relationships using NSComboBoxCell, NSTableView, and Core Data - cocoa

I have an example application I'm working on to help me learn about Core Data. In this application I created a model consisting of the entities "Friend" and "City". The application list my friends and which city they are from in an NSTableView. In this table view I would like to have the City column be an NSComboBoxCell with a list of the cities. I got this far... now for the problem:
When I select the city from the combo box, the application takes the value of the selected city name and applies it to the name of the city the friend is currently from. Instead, I would like the application to actually change the city the user is from and not the name of the city... That's a bit confusing of a question, so here's an example: starting with a list of friends like
Andy Asheville
Francois Montreal
Jeff Asheville
If I use the NSComboBoxCell to change the city for Andy from Asheville to Montreal, the application actually changes the name of the City Asheville to Montreal, so the result looks like:
Andy Montreal
Francois Montreal
Jeff Montreal
There are still two distinct cities in the application, but they now both have the name Montreal.
This all makes sense to me given the way I set up my bindings. I bound the value of the city table column by setting Model Key Path to "city.name" and the Controller Key to arrangedObjects, which contains the list of friends. So of course, when the value of a cell changes it modifies city.name. So then my question becomes what's the proper way to do this so that the city changes instead of the city name?

The purpose of NSComboBox is to "allow you to either enter text directly (as you would with an NSTextField) or (...) select from a displayed list of items". (taken from Apple Cocoa Ref).
So The combo box is a glorified NSTextField. The behavior you expect is more in line with the NSPopupButton.
As you noted, the NSComboBox bindings change the name value of the current City, but do not alter the object relationship, hence they change the name of the current city represented by the relationship (which then gets "propagated" to other Friends pointing to the same City entity).
If you look at the available bindings for NSPopupButton you'll see the difference. You probably want to use the NSPopupButton(Cell) to assign Cities to Friends, with some sort of "on the side", NSTableView-based editor to manage your city names — which are really two distinct tasks.

Related

Multiple entities in one UITableview

I have a data model with two entitys with relationship one to many. Lets call them City<-->>Person.
In my tableview Im currently displaying each City, seperated by sections (currently one cell in each section).
Is there any way for me to add each Person connected to the specific city in the corresponding section? (adding as many cells as number of persons connected to the city)
Im using two different fetchedResultsControllers to access the Entities but cant quite figure out how to do this.
Ultimately Id like each sections first cell to be a city, followed by all the persons in that city.
Any help or pointer in the right direction would be highly appreciated.
As Michal mentioned in comments. A cleaner design would be for your City entities to be displayed in the section headers (pretty easy) and have your person objects in the sections as rows.
To do that you need to add a unique identifier from the city as your section path of your NSFetchedResultsController. Since that attribute takes a key path you can specify something like city.name and it will resolve through the relationship from person to city and get that value.
Your only other option would be to manually fetch the city objects and build your UITableViewDatasource to talk to your own construct of data. Doable but far, far more difficult than what Michal suggested.

Set value in CoreData with three related NSPopUpButton

I hope to express myself with appropriate terms.
I have three entities: customer, country, state. Between country and state, there are two relationships. So I can create a number of countries and select one of these I can create a number of states that belong to it.
For creating the interface of the entity "customer" I put threeNSPopUpButton's:
The first selects the country,
The second shows the states related to the selected country.
The last shows the zip code related to the chosen state.
My problem is that I can not pass data through the chosenNSPopUpButton entity "customer". The NSPopoUpButton works by binding:
Content (bind to:country, arrangedObjects),
Content Values (bind to:country, arrangedObjects, model key:country),
Selected Index (bind to:country, selectionIndex).
What can I do to ensure that the data selected by the three NSPopUpButton's are recorded in the appropriate fields of the entity "customer"?
It would seem that using 3 popups may work with your problem here, as you can return the int for each selected item, and then pass that information on to the next button press.
So, when they press 'country', return back the country id, and put that in your customer entity.
Then, when they press state, pass the country to this popup, and show the states, and pass the state id back, and do the same for city.
This way you don't have lots of managed objects around, as you really don't need the data, you just need the id, and perhaps the name, to show to the user, and these can be passed back in a notification back to your controller.

Bindings - master detail array controllers

I really hope someone can help on this because I'm learning cocoa and have hit a road block.
I am trying to model a simple poker tournament. For now, my entities are simply a Tournament (with a number) and a Player (with a Name). A Tournament has an array of Players.
I can bind two independent table views to display the tournaments and the players just fine. But I want the players table view to just show the players that belong to the selected tournament from the first table view.
Each has it's own array controller. I have tried a variety of different bindings for the second (players) table but to no avail. Has anyone accomplished this? If so maybe you could spell it out for me, as I there are few examples online.
Update
I can now ALMOST get where I need to, mostly through rial and error and hours of googling. I have bound the player AC's content to the tournament AC, with controller key 'selected objects' and Model Key Path 'players', which is the name of the array in my Tournament entity.
I have the bound the column in the players table view to this second Player AC, controller key arranged objects. But what to put in the Model Key Path? I know it is working because if I stick #count in there I get the correct number of players for the selected tournament. But 'name' and 'player.name' are no good. Is there any kind of 'item.name' or 'players.item.name' I can try?
Sooo close, thanks for the help so far:
I think this tutorial will help you. They also create a master/detail view.
In short: Bind the contentArray of your player's array controller to the tournament's array controller, set ControllerKey to selection and the remaining properties accordingly to your model.
I found the answer here:
Implementing parent->child drill down in Cocoa with Core Data bindings that span multiple entities.
The child controller needs to know about the managedObjectContext through its own binding.
The child controller must not be in Entity Mode, but rather operate as a NSMutableDictionary class.
And, finally, the child controller does not prepare its data. It retrieves it from the parent, through the Content Set binding. Use the controller key selection, and the model key path that connects to the children.
I'm surprised this is not a more commonly used practice, and hope the next person reading this doesn't spend so long finding the answer!

Cocoa data hierarchy and browser

I'm trying to implement something similar to the iTunes browser, to browser a simple database of Books. I have the following entities - Author, Genre and Book. I would like to display an author list and a genre list, which act to filter the main Book list.
I have tried doing this in 2 different ways - modeled as:
Author ( has many ) Genres ( has many ) Books
...with multiple instances of the same Genre so each author has their own for a given genre name, I have a nice drill-down hierarchy to display in my table views (albeit a bit illogical to duplicate Genres). However, when I select multiple Authors, I end up displaying dupes of the same Genre, because they are, in fact, distinct objects.
So, I tried doing it, more sensibly, with these relationships:
Author ( has many ) Books
Book ( has one ) Genre
I can get the Genre array by taking the distinct union of Genre's in the current selected Author(s) book array, but now i'm left with the problem of filtering the book list displayed based on the selected Genre(s). Because the Genre's are shared, I can't just use CurrentGenre.books, or I lose the selected Author filtering. I have noticed the 'filter predicate' field in interface builder, available on the object controllers, but am stuck working out how to actually use it to apply the selected Genre as a filter to an episode list. The apple documentation says:
"You can type a predicate directly
into the predicate editor text field
in the inspector panel of Interface
Builder or you can set it
programmatically using
setFetchPredicate:.
which gives me the impression i'm on the right track, but that's about the end of it. I'm trying to lock down the model in a nice Cocoa-esque fashion now, so as to minimize 'glue code' bits and changes later down the track. It seems like a fairly simple problem I should be able to sort out graphically in IB, but so far it's eluded me!
Thanks in advance.
I am a bit confused by your introducing "episodes" in the middle of the discussion, but I will assume you just mean "books" still.
You are definitely on the right track. You want a data model like this:
Author <-->> Book
Genre <-->> Book
Or maybe even:
Author <<-->> Book (if you support reference books, etc)
Genre <<-->> Book (if you want multi-genre support)
Once a user has selected author(s) and genre(s) you will want a Book array controller to use a filter predicate that only shows books with those author(s) or genre(s).
UPDATE
This should work:
Bind the Book array controller's filter predicate to a new predicate property "bookFilterPredicate" in your app delegate.
Add outlets for the Author and Genre array controllers.
Observe changes to the selectedObjects properties of both array controllers.
When either changes, update the filter predicate property like this: self.bookFilterPredicate = [NSPredicate predicateWithFormat:#"author IN %# && genre IN %#",authorArrayController.selectedObjects,genreArrayController.selectedObjects];

Tinkering under the hood with Bindings in Xcode

Let's take two Core Data entities, set up as follows:
Entity A: Car
Attributes:
year
model
Relationships:
manufacturer (<<-> Manufacturer)
Entity B: Manufacturer
Attributes:
name
country
Relationships:
cars (<->> Car)
Now, what I want to do is bind the display to an NSTableView where we have the model of the car in one column, followed by the manufacturer, followed by the year. Binding the model and year are no problem, but if I bind the relationship to a column in the table, I get the text of a relationship fault error in each cell in that column instead of anything I'm looking for. How can I play with the binding to allow me to display the proper manufacturer name associated with the car?
Extending the question a bit further, how could I set up another table view to display, say, other Car entries with the same manufacturer relationship?
A bit more information about how you have it set up currently would be helpful. You should be able to bind to your Array Controller in exactly the same fashion as your other attributes, with the same binding and controller key. Just use the full key path manufacturer.name for the model key path.
For a to-many relationship, you use two array controllers. Set up the 'master' array controller to prepare its own content from your Core Data Manufacturer class (in Entity mode). Then, you create a secondary, 'detail' array controller. Leave the detail array controller in Class mode (with the default NSMutableDictionary class), and bind its content set to your master array controller, with the controller key set to selection and the model key path to cars.
Many many tutorials exist out there that do exactly this. I highly recommend running through one or two; I found this MacResearch.org tutorial particularly helpful. The entire series is great.

Resources