Swift's 'observe()' doesn't work for key paths with optionals?


(William Shipley) #1

When I try to observe a key path that includes an optional (“controller” in this case) in Swift, I get a compiler error saying ‘Type of expression is ambiguous without more context’ on the backslash in this statement:

        submodelObservation = observe(\.controller.submodel) { object, change in
    // code
        }

    @objc dynamic internal var controller: Controller?

in the the “Controller" class:

    @objc dynamic internal var submodel: SubmodelForImporting?

I’m not clear why this would be, but I admit I missed the discussion the first time it happened. If I make “Controller” non-optional the code compiles fine.

Certainly with traditional KVO you can observe key paths that have ‘nil’ values in them! Is this something stupid I’m doing, a compiler bug, or a design decision? I’m asking here because if it’s a design decision I’d like to appeal it, certainly.

-Wil


(Brent Royal-Gordon) #2

I believe you're meant to actually construct a key path which looks through an `Optional` like this:

  \.controller?.submodel

But if you try that, you'll get a different, temporary error:

  error: key path support for optional chaining components is not implemented

My understanding is that optional chaining in key paths *is* planned for Swift 4; it just hasn't landed yet. If making `controller` an implicitly-unwrapped optional would be a suitable workaround for you, I see that a couple pull requests to support IUOs were merged to swift-4.0-branch recently[1]; they might be in the open-source Swift 4 snapshot[2]. If not, my best suggestion would be to temporarily add a computed `controller_submodel` property and use the traditional `keyPathsForValuesAffecting…` mechanism to tie it to the underlying properties.

  [1] https://github.com/apple/swift/pull/11005
  [2] https://swift.org/download/#snapshots

···

On Jul 22, 2017, at 12:12 AM, William Shipley via swift-evolution <swift-evolution@swift.org> wrote:

        submodelObservation = observe(\.controller.submodel) { object, change in
    // code
        }

    @objc dynamic internal var controller: Controller?

in the the “Controller" class:

    @objc dynamic internal var submodel: SubmodelForImporting?

--
Brent Royal-Gordon
Architechies