Search code examples
macosswiftstructcocoa-bindings

How to use Cocoa bindings against Swift structs


I'm learning Swift.

I mostly work in iOS these days, but I'm currently working on a small project for OS X.

On OSX I like using Cocoa bindings to link values from my model to UI elements. It saves writing tons of glue code.

I am writing a program that compares the performance of Swift to that of C/Objective-C. I'm using a prime number generator as a test project.

I have create a Swift Struct ComputeSettings that encapsulates the settings (and results) for running the prime number generator in both Swift and Objective-C. The struct looks like this:

struct ComputeResults
{
  var totalCalculated:  Int = 0
  var primesPerSecond:  Int = 0
  var totalTime:        NSTimeInterval = 0
}
struct ComputeSettings
{
  var totalToCalculate: Int = 10000000
  var swiftResults:     ComputeResults = ComputeResults()
  var objCResults:      ComputeResults = ComputeResults()
}

I then have an instance variable of type ComputeSettings in my view controller:

class ViewController: NSViewController
{
  var theComputeSettings = ComputeSettings()
  var foo: Int = 12
  //...
}

In my UI, I have a text field that lets the user input the number of prime numbers to calculate. In IB, I select the field, choose the bindings tab in the utilities area, then open the values bindings. I bind to my ViewController class. I enter a model key path of self.theComputeSettings.totalToCalculate.

When I run my project it fails with an exception:

Failed to set (contentViewController) user defined inspected property on (NSWindow): [ addObserver: forKeyPath:@"theComputeSettings.totalToCalculate" options:256 context:0x0] was sent to an object that is not KVC-compliant for the "theComputeSettings" property.

If, instead, I bind to the dummy property foo (so the model key path is set to self.foo) it works perfectly. In that case I see my text field display my value of 12.

Is there some way to use Swift structs and still get Cocoa bindings to work? I did a fair amount of googling, but can't find anything.

I can certainly do it the manual way, and write a bunch of glue code to install my values from my struct into my window's views, but I'd much rather use Cocoa bindings. That's what they're for.


Solution

  • You can't use Cocoa bindings with Swift structs.

    The Cocoa bindings system is implemented on top of Key-Value Observing, and Key-Value Observing is implemented by creating a subclass to intercept the set<Property>: messages sent to the observed object. Swift structs cannot have subclasses and don't necessarily use messages or function calls to set struct fields, so there is no way in general to intercept the setting of a Swift struct field.

    The message complains about a lack of KVC compliance because Key-Value Coding defines the set<Property>: (and <property> getter) convention for message names.