Adam
Adam

Reputation: 1913

Swift generics. How to check if the type of Array's Element inherits from other class

I want to determine in my method whether the generic type is an array and if so, read some static variable of the type that is the Element of the array.

I thought that this would work:

    print(T.self)
    print(T.self is Array<SPTBaseObject>.Type)
    if let A = T.self as? Array<SPTBaseObject>.Type {
        print(A.Element.pluralKey)
    }

But the output from the console reads:

Array<SPTArtist>
false

Changing the second line to

    print(T.self is Array<SPTArtist>.Type)

Prints true, but it seems pointless to check against every possible type. I just want it to return true when the Element is a class inheriting from the SPTBaseObject.

The SPTArtist is a class inheriting from the SPTBaseObject.

public class SPTArtist: SPTBaseObject {
...
}

public class SPTBaseObject: Codable {
    internal class var pluralKey: String? {
        return nil
    }
}

EDIT:

My method looks like this

private class func perform<T>(request: URLRequestConvertible, completion: @escaping (Result<T, Error>) -> Void) where T: Decodable {
    AF.request(request).responseData { response in
        if let error = response.error {
            completion(.failure(error))
        }
        guard let data = response.value else {
            completion(.failure(SPTError.noDataReceivedError))
            return
        }
        if let error = try? JSONDecoder().decode(SPTErrorResponse.self, from: data) {
            completion(.failure(error.error))
            return
        }
        // Check if T is an array, if so try to decode a Root<T> object. Here's an error, I cannot construct the Root type like this although the Element is guaranteed to be a SPTBaseObject subclass
        if let A = T.self as? Array<SPTBaseObject>.Type,
            let root = try? JSONDecoder().decode(Root<A.Element.self>, from: data) {
            completion(.success(root.items))
        } else if let object = try? JSONDecoder().decode(T.self, from: data) {
            completion(.success(object))
        } else {
            completion(.failure(SPTError.decodingError))
        }
    }
}

The Root class which will utilize the pluralKey property of SPTBaseObject

class Root<T>: Decodable where T: SPTBaseObject {
    let items: [T]
    
    private struct CodingKeys: CodingKey {
        var stringValue: String
        init?(stringValue: String) {
            self.stringValue = stringValue
        }
        var intValue: Int?
        init?(intValue: Int) {
            return nil
        }
    }
    
    required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        guard let key = T.pluralKey, let codingKey = CodingKeys(stringValue: key) else {
            fatalError()
        }
        items = try container.decode([T].self, forKey: codingKey)
    }
}

Upvotes: 0

Views: 162

Answers (1)

gcharita
gcharita

Reputation: 8347

If I understood correctly, you can use a function that takes an array of [T] where T should be subclass of SPTBaseObject and an overload that takes only one T object, like this:

func getPluralKey<T: SPTBaseObject>(_ array: [T]) {
    print(T.pluralKey ?? "nil")
}

func getPluralKey<T: SPTBaseObject>(_ object: T) {
    print(T.pluralKey ?? "nil")
}

Upvotes: 1

Related Questions