0
votes

i have an issue in swift where i want to check for protocol conformance in a class func and then use some static property when the check succeeds. Here's the playground code:

import UIKit

@objc protocol SearchableObject {
  static var sortDescriptors: [NSSortDescriptor] { get }
}

class Test: NSObject {

}

extension Test {
    class func loadData() {
        if self is SearchableObject {
             println(valueForKey("sortDescriptors"))
        }
        else {
             println("not searchable")
        }
    }
}

class SearchableTest: Test, SearchableObject {
     class var sortDescriptors: [NSSortDescriptor] {
         return [NSSortDescriptor(key: "property", ascending: true)]
     }
 }

Test.loadData() // prints "not searchable"
SearchableTest.loadData() // prints "Optional((\n    "(property, ascending, compare:)"\n))"

the problem is i can check for protocol conformance, but i can't cast self, that's why i'm using valueForKey().

(self as SearchableObject).sortDescriptors
      ^ 'SearchableObject' does not have a member named 'sortDescriptors'

casts self to an instance conforming to SearchableObject making the static var inaccessible. As don't like the valueForKey() approach i want to ask whether there is a better way to achieve this?

Thanks in advance :)

2

2 Answers

4
votes

In class methods, self is a class, not instance. So the right - but non working - way is:

class func loadData() {
    if let cls = self as? SearchableObject.Type {
    //                                    ^^^^^
        println(cls.sortDescriptors)
        //      ^ [!] error: accessing members of protocol type value 'SearchableObject.Type' is unimplemented
    }

As you can see, it's not implemented :(

No worries, there is a workaround. As long as the protocol is declared as @objc, AnyClass has that properties. You can cast self to AnyClass, then call .sortDescriptors:

class func loadData() {
    if self is SearchableObject.Type {
        println((self as AnyClass).sortDescriptors)
    }

Essentially, this workaround is the same as valueForKey, but it doesn't use strings at least.

2
votes

self as SearchableObject returns a SearchableObject instance. But you want the class. That's SearchableObject.Type.

So you meant this:

extension Test {
    class func loadData() {
        if let s = self as? SearchableObject.Type { // <<===
            println(s.sortDescriptors)
        }
        else {
            println("not searchable")
        }
    }
}

But "accessing members of protocol type value...is unimplemented." So the compiler knows about this, and specifically can't handle it currently. Of course all of this is fine if you use instances:

import Foundation

protocol SearchableObject {
    var sortDescriptors: [NSSortDescriptor] { get }
}

class Test {}

extension Test {
    func loadData() {
        if let s = self as? SearchableObject {
            println(s.sortDescriptors)
        }
        else {
            println("not searchable")
        }
    }
}

class SearchableTest: Test, SearchableObject {
    static var sharedSortDescriptors = [NSSortDescriptor(key: "property", ascending: true)]
    var sortDescriptors: [NSSortDescriptor] { return self.dynamicType.sharedSortDescriptors }
}


Test().loadData() // prints "not searchable"
SearchableTest().loadData()