Instantiating generic class in custom Decoder

I'm creating a custom Decoder class with the corresponding KeyedDecodingContainerProtocol adhering class. When the generic decode method is called with the property to decode, is there a way to instantiate a class/struct if it adheres to my own DBObject protocol that has an init method?

I can create a custom method for decoding and instantiate the object:

	func decodeObject<T: DBObject>(_ type: DBObject.Type, forKey key: K) throws -> T {
		guard let value = dict[key.stringValue] as? String else {
			throw DictDecoderError.missingValueForKey(key.stringValue)
		}

		guard let dbObject = T(db: db, key: value) else {
			throw DictDecoderError.invalidNestedObject(key.stringValue, value)
		}

		return dbObject
	}

But then how do I call it properly?

if T.self is DBObject.Type {
			return try decodeObject(T.Type, forKey: key) as! T

Doesn't work because it cannot infer T

Assuming DBObject is not PAT:

func decodeObject(_ type: DBObject.Type, forKey key: K) throws -> DBObject {
    guard let value = dict[key.stringValue] as? String else {
        throw DictDecoderError.missingValueForKey(key.stringValue)
    }

    guard let dbObject = type.init(db: db, key: value) else {
        throw DictDecoderError.invalidNestedObject(key.stringValue, value)
    }

    return dbObject
}

// call it
if let dynamicType = T.self as? DBObject.Type {
    return try decodeObject(dynamicType, forKey: key) as! T
}
1 Like

That worked perfectly. Thanks! How would I decode an array of DBObjects? (and what is a PAT protocol?)

I have this for testing the property is a DBObject array:

else if let dynamicType = T.self as? [DBObject].Type {
	return try decodeObjectArray(dynamicType, forKey: key) as! T
}

But not sure how to write the decode function:

func decodeObjectArray(_ type: [DBObject].Type, forKey key: K) throws -> [DBObject] {
   	guard let values = dict[key.stringValue] as? [String] else {
    	throw DictDecoderError.missingValueForKey(key.stringValue)
    }

    var objectValues: [DBObject] = []
    for value in values {
    	if let dbObject = // how do I initialize? {
    		objectValues.append(dbObject)
    	}
    }

    return objectValues
}

Because you can't convert a dynamic type:

let dynamicType = T.self as? DBObject.Type

to a static type:

func decodeObject<StaticType: DBObject>(_ type: StaticType, forKey key: K) throws -> T

Also you can't create a dynamic type from PAT:

let dynamicType = T.self as? RawRepresentable.Type // NO
let value = dynamicType.init(rawValue: /* what type? */)

As for DBObject array:

protocol DBObjectArrayMarker {
    static var elementType: DBObject.Type { get }
}

extension Array: DBObjectArrayMarker where Element: DBObject {
    static var elementType: DBObject.Type {
        return Element.self
    }
}

func decodeObjectArray(_ type: DBObjectArrayMarker.Type, forKey key: K) throws -> [DBObject] {
    guard let values = dict[key.stringValue] as? [String] else {
        throw DictDecoderError.missingValueForKey(key.stringValue)
    }

    var objectValues: [DBObject] = []
    for value in values {
        if let dbObject = type.elementType.init(db: db, key: value) {
            objectValues.append(dbObject)
        }
    }

    return objectValues
}

// call it
if let dynamicType = T.self as? DBObjectArrayMarker.Type {
    return try decodeObjectArray(dynamicType, forKey: key) as! T
}

Thanks for your help! You can see the results of your help here:

Terms of Service

Privacy Policy

Cookie Policy