swiftkvc

How to learn about KVC with swift 4.2


I'm using Xcode 10.0 with swift 4.2 to learn about Key Value Coding from "Cocoa programming for OSX"

I'm asked to create a simple class, which is a subclass of NSObject. The codes below:

import Cocoa

class Student: NSObject {
    var name: String = ""
    var gradeLevel: Int = 0
}

let student1 = Student()


student1.setValue("Benny", forKeyPath: "name")

student1.setValue("Benny", forKeyPath: "name")

Generates the following error message:

Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0).

I've looked online and seem some issues regarding KVC such as : https://bugs.swift.org/browse/SR-5139

What am I doing wrong? The book was published in 2015.


Solution

  • In Swift 4 exposing code to the Objective-C runtime is no longer inferred for performance reasons.
    To avoid the crash you have to add the @objc attribute explicitly.

    @objc var name: String = ""
    

    But from the strong type perspective of Swift there are two better ways to get values with KVC:

    1. The #keyPath directive which uses the ObjC runtime, too, but checks the key path at compile time

      let keyPath = #keyPath(Student.name)
      student1.setValue("Benny", forKeyPath: keyPath)
      

      In this case you get a very descriptive compiler warning

      Argument of '#keyPath' refers to property 'name' in 'Student' that depends on '@objc' inference deprecated in Swift 4

    2. The (recommended) native way: Swift 4+ provides its own KVC pattern where subclassing NSObject is not required.
      Key paths are indicated by a leading backslash followed by the type and the property (or properties):

      class Student {
          var name: String = ""
          var gradeLevel: Int = 0
      }
      
      let student1 = Student()
      student1[keyPath: \Student.name] = "Benny"