I have the following code which adds an observer on two properties:
obs = []
let xa = self.observe(\CirclePolygon.radius, options: [.new]) { (op, ch) in
callback()
}
obs.append(xa)
let xb = self.observe(\CirclePolygon.origin.x, options: [.new]) { (op, ch) in
callback()
}
obs.append(xb)
It works, but I don't like the duplciation. I have tried to pass a list of KeyPaths:
obs = []
let keyPaths = [\CirclePolygon.radius, \CirclePolygon.origin.x]
for keyPath in keyPaths {
let xa = self.observe(keyPath, options: [.new]) { (op, ch) in
callback()
}
obs.append(xa)
}
But I get the compiler error: Generic parameter 'Value' could not be inferred
Is there anyway to do this?
First of all, remember that KVO only works on instances of
NSObject
(or a subclass). So if theorigin
property ofCirclePolygon
is aCGPoint
or some otherstruct
, you'll get a run-time error when you try to register an observer of\CirclePolygon.origin.x
, because KVO cannot apply to thex
property of aCGPoint
.Second, the key paths you gave have different types. Let's say (to address my first point) you want to observe
radius
(aDouble
) andorigin
(aCGPoint
). The key paths have different types:If you try to put these in a single array, without explicitly specifying the type of the array, Swift will deduce it as follows:
It deduces
[PartialKeyPath<CirclePolygon>]
becausePartialKeyPath<CirclePolygon>
is the nearest common ancestor of bothKeyPath<CirclePolygon, Double>
andKeyPath<CirclePolygon, CGPoint>
.So, in the
for
loop where you iterate over the array, you're passing an object with static typePartialKeyPath<CirclePolygon>
as thekeyPath
argument of theobserve(_:options:changeHandler:)
method. Unfortunately, that method is declared as follows:Which is to say, the method requires a
KeyPath<Self, Value>
but you're passing aPartialKeyPath<Self>
, so Swift emits an error. As is often the case with Swift errors around generic type problems, the error isn't very helpful.You cannot solve this problem by casting, because you cannot (for example) cast
KeyPath<CirclePolygon, Double>
toKeyPath<CirclePolygon, Any>
.One solution may be to use a local function to encapsulate the common code, like this: