Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to check generic class type is array?

Tags:

swift

swift3

I want to check whether the generic class type is an Array:

func test<T>() -> Wrapper<T> {
  let isArray = T.self is Array<Any>
  ... 
}

But it warns

Cast from 'T.type' to unrelated type 'Array' always fails

How can I solve this problem?

added: I've uploaded my codes to Gist. https://gist.github.com/nallwhy/6dca541a2d1d468e0be03c97add384de

What I want to do is to parse json response according to it's an array of model or just one model.

like image 585
mayTree Avatar asked Jan 07 '17 06:01

mayTree


People also ask

Can a generic be an array?

– Using Object Array In the above code demonstration, A generic class array is defined. The object array is a member of the class, instantiated with a constructor and a length variable. The generic getter and setter methods are used to read and set an array element of a particular type.

How do you initialize a generic array?

Array#newInstance to initialize our generic array, which requires two parameters. The first parameter specifies the type of object inside the new array. The second parameter specifies how much space to create for the array.


2 Answers

As commentator @Holex says, you can use Any. Combine it with Mirror and you could, for example, do something like this:

func isItACollection(_ any: Any) -> [String : Any.Type]? {
    let m = Mirror(reflecting: any)
    switch m.displayStyle {
    case .some(.collection):
        print("Collection, \(m.children.count) elements \(m.subjectType)")
        var types: [String: Any.Type] = [:]
        for (_, t) in m.children {
            types["\(type(of: t))"] = type(of: t)
        }
        return types
    default: // Others are .Struct, .Class, .Enum
        print("Not a collection")
        return nil
    }
}

func test(_ a: Any) -> String {
    switch isItACollection(a) {
    case .some(let X):
        return "The argument is an array of \(X)"
    default:
        return "The argument is not an array"
    }
}

test([1, 2, 3]) // The argument is an array of ["Int": Swift.Int]
test([1, 2, "3"]) // The argument is an array of ["Int": Swift.Int, "String": Swift.String]
test(["1", "2", "3"]) // The argument is an array of ["String": Swift.String]
test(Set<String>()) // The argument is not an array
test([1: 2, 3: 4]) // The argument is not an array
test((1, 2, 3)) // The argument is not an array
test(3) // The argument is not an array
test("3") // The argument is not an array
test(NSObject()) // The argument is not an array
test(NSArray(array:[1, 2, 3])) // The argument is an array of ["_SwiftTypePreservingNSNumber": _SwiftTypePreservingNSNumber]
like image 120
Grimxn Avatar answered Oct 03 '22 09:10

Grimxn


Try out define an array marker protocol. Refer to this answer.

protocol AnyTypeOfArray {}
extension Array: AnyTypeOfArray {}
extension NSArray: AnyTypeOfArray {}

func isArray<T>(type: T.Type) -> Bool {
    return T.self is AnyTypeOfArray.Type
}

print(isArray(type: [String].self))   // true
print(isArray(type: String.self))     // false
like image 26
Ethan11 Avatar answered Oct 03 '22 10:10

Ethan11