(ugh, reposted due to size moderation, again)

On Sep 11, 2015, at 13:40 , Alex Hall <mehg...@icloud.com> wrote:

> What I've done so far is set things up, but not gotten the app to run. At 
> first, I was getting some odd errors, but I eventually realized that 
> populating my tweetsArray object in viewDidLoad was a bad idea, so now I do 
> that in the property declaration:
>       dynamic var tweetsArray:[Tweet]=[Tweet(text:"tweet 11"), 
> Tweet(text:"tweet 12”)]

You’ve *still* got a conceptual problem, but this one is about KVC, not 
bindings. KVC deals with properties which are values identified by *name* (key) 
within classes. There are scalar (one-to-one) properties — simple values — and 
collection (one-to-many) properties — multiple values which are isolated by 
(say) a numeric index.

In theory, there can be an “array” property, but that would be a scalar 
property whose single value happened to be a NSArray. Given the other details 
of KVC’s machinery, trying to use such an array property is a nightmare you 
don’t want to be stuck in. What you want, and what you really have, is an 
indexed collection of tweets, and you need to stop thinking of the property as 
an array — even though the backing store may have type [Tweet] and the KVC 
machinery accesses the collection via the NSArray class-cluster API — but not 
necessarily actual arrays (sometimes it uses proxy objects that refer to actual 
arrays, but that’s another discussion). Indeed, you should call your property 
“tweets”, not “tweetsArray”.

The internal magic of KVC is such that it provides a standardized mechanism of 
translating between property names (for a given class) and methods (of the 
class) the produce the actual values. Such methods are called accessors, and 
the method (naming) requirements depend on the kind of properties.

For scalar properties, there are two accessor methods: a getter and a setter 
(xxx and setXxx:, for a property named “xxx”). For indexed collection 
properties, there are about 8 different accessor methods, but you usually 
implement only a minimum required subset (of 2 accessors, one for inserting and 
one for removing). All the rest of the accessors default to standardized 
behavior and/or translate themselves into your custom accessors.

> As to my controller and bindings:
> 
> array controller:
> * content array: view controller.tweetsArray (which again, is dynamic); 
> controller key is empty

Correct, though as I said, you’re doing nothing but creating confusion by 
calling this “tweetsArray” rather than “tweets”, in your data model.

> * Content Array For Multiple Selection: View Controller.tweetsArray (though I 
> don't need this, as I've disable multiple selection in my table)

Don’t need. Leave it alone and let the binding provide default behavior.

> * Selection Indexes: controller to bind to is View Controller, but I don't 
> know what to put for a key path here. This is a property of the array 
> controller itself, not anything it needs to get anywhere else, so I don't 
> quite know why this field is here. Unless it's observing itself?

Don’t need, probably. If your view controller wants to have it’s own “mirror” 
of the selection that the array controller maintains, you can give it a 
suitable property, bind it here, and it will reflect the selection through the 
magic of bindings. There’s actually nothing interesting going on here, it’s 
just a piece of array controller convenience functionality.

> * Managed Object Context: same as Selection Indexes. I'm not quite sure what 
> to put for values here.

Don’t need. This is for Core Data, and absolutely nothing else.

> Table View:
> * Content: array controller.arrangedObjects

Correct.

> * Selection Indexes: Array Controller.selectionIndexes
> * Sort Descriptors: Array Controller.sortDescriptors

Don’t really need. IIRC table views establish these particular bindings by 
default, if you don’t. You only ever set these manually if you’re bindings 
targets are non-standard. But what you’ve done isn’t wrong.

> Cell text field: this one's odd, because I thought the controller was 
> supposed to be TableCellViewController, but that's not in the popup list of 
> available options.
> * Value: Array Controller.selection.displayText (recall that displayText is a 
> string property of the Tweet objects of which tweetsArray is full)

Nope. A table cell is a placeholder, since it obviously has to be different at 
every row. If you bind subviews to a particular target, all rows will show the 
same thing, and that’s not what you want.

Generally, you bind a subview’s value to TableViewCell (which *should* be in 
the popup), and use a model key of “objectValue.something”. In this case, it 
sounds like it should be “objectValue.displayText”.

There’s a completely separate piece of table view machinery that ensures the 
“objectValue”, for each table cell, contains a reference to the correct tweet 
for its row. This can itself be a binding, a default table behavior, or it can 
be done in a delegate method.

BTW, if you look at the table view documentation to find out how to supply this 
piece, you’ll find a lack of information. It’ll tell you that binding a table 
view’s content is an “advanced” topic, and give you no further help than a 
comically inadequate example.

> Right now, I'm getting an error:
> [Bindings_Tests.ViewController count]: unrecognized selector sent to instance 
> 0x6080000c3db0
> 
> Clearly, I'm missing a key somewhere, because that should be [ViewController 
> countOfTweetsArray]

Nope. It should be “count”. The bound object, whatever it was, asked for the 
“count” of what it expected to be an indexed collection property. KVC takes 
such a request, and translates it into an accessor internally, or resolves it 
into default behavior. The latter is the correct outcome in this case. If it 
used the accessor, the method *would have been* called ‘countOfTweetsArray’, 
but the client — the bound UI element — doesn’t know or care. It just wants the 
“count”.

The actual problem here is that you left out the *property* key. I don’t know 
which binding this was, but it looks like you chose the view controller as 
target, and omitted to specify “tweetsArray” as the model key. (Or, due to one 
of the above problems, the entire binding should have been to a different 
target.)

> , which I *did* implement, just in case it would help:
>       private func countOfTweetsArray() -> Int {
>               return tweetsArray.count
>       }
> 
> I've also added:
>       private func objectInTweetsArrayAtIndex(index:Int)  -> Tweet{
>               return tweetsArray[index]
>       }

Don’t bother with these. They *are* the correct accessors for read access to an 
indexed one-to-one property called “tweetsArray”, but due to a horrible 
long-standing defect in KVC, it’s not really practical to use them (in many 
cases), and they won’t be used in this case, and you don’t need them in this 
case, simply because you have a property called “tweetsArray” which does their 
duty.


_______________________________________________

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Reply via email to