Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Check for object type fails with "is not a type" error

Tags:

ios

swift

I'm trying to check if an object is of a given type and I'm getting an error:

'expectedClass' is not a type

xcode

My code below.

func testInputValue(inputValue: AnyObject, isKindOfClass expectedClass: AnyClass) throws {
    guard let object = inputValue as? expectedClass else {
        // Throw an error
        let userInfo = [NSLocalizedDescriptionKey: "Expected an inputValue of type \(expectedClass), but got a \(inputValue.dynamicType)"]
        throw NSError(domain: RKValueTransformersErrorDomain, code: Int(RKValueTransformationError.UntransformableInputValue.rawValue), userInfo: userInfo)
    }
}

I'm trying to figure out what can be wrong here.

like image 745
Rafa de King Avatar asked Mar 13 '23 12:03

Rafa de King


2 Answers

You should be able to do this with generics:

func testInputValue<T>(inputValue: AnyObject, isKindOfClass expectedClass: T.Type) throws {
    guard let object = inputValue as? T else {
        ...
    }
}
like image 121
dan Avatar answered Mar 24 '23 05:03

dan


You should not do class comparisons with == as suggested in one of the other answers, unless you specifically want to test if the type of the object tested should exactly match the expected class and it is not allowed to be a subclass of the tested class.

You can use the instance method isKindOfClass to accomplish this, taking subclassing into account. See below for a code example.

NOTE: You may be surprised that this works on a pure Swift class type, given an instance method with the same name exists in NSObject / NSObjectProtocol. It does indeed work in pure Swift (as shown with the example code below – tested with Xcode 7.3, Swift 2.2.1), with no @objc types involved, as long as you import Foundation. I am presuming based on this that this instance method is added as an extension method in Foundation to all class types.

import Foundation

class A { }
class B { }
class C: B { }

enum ValueTestError: ErrorType {
    case UnexpectedClass(AnyClass)
}

func testInputValue(inputObj:AnyObject, isKindOfClass expectedClass:AnyClass) throws {
    guard inputObj.isKindOfClass(expectedClass) else {
        throw ValueTestError.UnexpectedClass(inputObj.dynamicType)
    }
}

let a = A()
let b = B()
let c = C()

do {
    try testInputValue(c, isKindOfClass: B.self)
} catch {
    print("This does not get printed as c is of type B (C is subclass of B).")
}

do {
    try testInputValue(a, isKindOfClass: B.self)
} catch {
    print("This gets printed as a is not of type B.")
}

Also, importantly although isKindOfClass is available as an instance method on AnyObject, trying to call it on an arbitrary Swift class typed object will only work if you first cast that object to AnyObject (which will always of course succeed). Example code illustrating this point is presented below, and there's more on this question over here.

import Foundation

class A {}
let a = A()

// the following compiles and returns 'true'
(a as AnyObject).isKindOfClass(A.self)

// the following fails to compile with "Value of type 'A' has no member 'isKindOfClass'"
a.isKindOfClass(A.self)
like image 35
mz2 Avatar answered Mar 24 '23 05:03

mz2