Reputation: 1913
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
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