7

When I filter an array of custom Swift classes using a Predicate I get the error:

*** NSForwarding: warning: object 0x78ed21a0 of class 'Plantivo1_6.Seed' does not implement methodSignatureForSelector: -- trouble ahead
Unrecognized selector -[Plantivo1_6.Seed valueForKey:]

If I remember correctly this would work in Objective-C. What's my mistake?

let names = ["Tom","Mike","Marc"]
println(names)
let searchPredicate = NSPredicate(format: "SELF CONTAINS[c] %@", "om")
let array = (names as NSArray).filteredArrayUsingPredicate(searchPredicate)
println(array)
println()

let mySeed1 = Seed()  // Seed is a class with a `culture` String property
let mySeed2 = Seed()
let mySeed3 = Seed()
mySeed1.culture = "Tom"
mySeed2.culture = "Mike"
mySeed3.culture = "Marc"

let mySeeds = [mySeed1,mySeed2,mySeed3]
println(mySeeds)
let searchPredicate1 = NSPredicate(format: "SELF.culture CONTAINS[c] %@", "om")
let array1 = (mySeeds as NSArray).filteredArrayUsingPredicate(searchPredicate1)
println(array1)
pkamb
  • 33,281
  • 23
  • 160
  • 191
Timm Kent
  • 1,123
  • 11
  • 25

4 Answers4

14

Does your Seed class inherit from NSObject?

If not, this is the message you will get.

Solution:

class Seed: NSObject {
   ...

Edit: stklieme is correct - to use NSPredicate, your object's class needs to implement -valueForKey as defined by the NSKeyValueCoding protocol. You can either define your own implementation of -valueForKey, or just make your class inherit from NSObject which takes care of that for you.

This is defined in the Apple docs for NSPredicate,

You can use predicates with any class of object, but the class must support key-value coding for the keys you want to use in a predicate.

Dan Loewenherz
  • 10,879
  • 7
  • 50
  • 81
jperl
  • 1,066
  • 7
  • 14
  • 1
    Made some edits, does that explain it better? It's just the way NSPredicate works internally to convert a string into a key that can be used to obtain a value from the class instance. – jperl Jun 25 '15 at 12:11
3

In case if you don't want to inherit from NSObject, you can implement value(forKey key: String) -> Any? method by yourself:

extension Model {
    @objc func value(forKey key: String) -> Any? {
        switch key {
        case "id":
            return id
        // Other fields
        default:
            return nil
        }
    }
}

Note @objc prefix of the method: it's important, as it's allowing NSPredicate to see that the method is implemented. You'll still receive does not implement methodSignatureForSelector: crash without it.

Or, even better, make your objects to conform this protocol:

@objc protocol UsableInPredicate {
    @objc func value(forKey key: String) -> Any?
}
Andrey Gordeev
  • 30,606
  • 13
  • 135
  • 162
1

valueForKey is a key value coding method. Declare the class Seed as a subclass of NSObject which conforms to KVC

vadian
  • 274,689
  • 30
  • 353
  • 361
  • 1
    You need KVC for this predicate syntax, but you don't need KVO. KVO places additional requirements on a class that are not relevant here. – Rob Jun 25 '15 at 18:29
0

Simply,

You need to inherit your model class by NSObject and your issue get fixed.

class Seed: NSObject {
   ...
}

Reason: -valueForKey as defined by the NSKeyValueCoding protocol. You can either define your own implementation of -valueForKey, or just make your class inherit from NSObject

Sandip Patel - SM
  • 3,346
  • 29
  • 27