Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

168
Views
Observing a list of Swift KeyPaths

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?

over 4 years ago · Santiago Trujillo
1 answers
Answer question

0

First of all, remember that KVO only works on instances of NSObject (or a subclass). So if the origin property of CirclePolygon is a CGPoint or some other struct, you'll get a run-time error when you try to register an observer of \CirclePolygon.origin.x, because KVO cannot apply to the x property of a CGPoint.

Second, the key paths you gave have different types. Let's say (to address my first point) you want to observe radius (a Double) and origin (a CGPoint). The key paths have different types:

\CirclePolygon.radius   // type: KeyPath<CirclePolygon, Double>
\CirclePolygon.origin   // type: KeyPath<CirclePolygon, CGPoint>

If you try to put these in a single array, without explicitly specifying the type of the array, Swift will deduce it as follows:

let keyPaths = [\CirclePolygon.origin, \CirclePolygon.radius]
// deduced type of keyPaths: [PartialKeyPath<CirclePolygon>]

It deduces [PartialKeyPath<CirclePolygon>] because PartialKeyPath<CirclePolygon> is the nearest common ancestor of both KeyPath<CirclePolygon, Double> and KeyPath<CirclePolygon, CGPoint>.

So, in the for loop where you iterate over the array, you're passing an object with static type PartialKeyPath<CirclePolygon> as the keyPath argument of the observe(_:options:changeHandler:) method. Unfortunately, that method is declared as follows:

public func observe<Value>(
        _ keyPath: KeyPath<Self, Value>,
        options: NSKeyValueObservingOptions = [],
        changeHandler: @escaping (Self, NSKeyValueObservedChange<Value>) -> Void)
    -> NSKeyValueObservation

Which is to say, the method requires a KeyPath<Self, Value> but you're passing a PartialKeyPath<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> to KeyPath<CirclePolygon, Any>.

One solution may be to use a local function to encapsulate the common code, like this:

func register(callback: @escaping () -> ()) -> [NSKeyValueObservation] {
    func r<Value>(_ keyPath: KeyPath<CirclePolygon, Value>) -> NSKeyValueObservation {
        return observe(keyPath, options: []) { (_, _) in
            callback()
        }
    }

    return [
        r(\.radius),
        r(\.origin),
    ]
}
over 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!