Parse heterogenous JSON array
First of all, the models need to be defined, conforming to the Decodable protocol. Starting with the base class and then the subclass, see the example below:

class FieldDefinition : NSObject, Codable {
    let type: String

    enum CodingKeys: String, CodingKey {
        case type
    }

    required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.type = try container.decode(String.self, forKey: .type)
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(self.type, forKey: .type)
    }
}

class TextFieldDefinition: FieldDefinition {
    let maxLength: Int

    enum TextCodingKeys: String, CodingKey {
        case maxLength
    }

    required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: TextCodingKeys.self)
        self.maxLength = try container.decode(Int.self, forKey: .maxLength)
        try super.init(from: decoder)
    }

    override func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: TextCodingKeys.self)
        try container.encode(self.maxLength, forKey: .maxLength)
        try super.encode(to: encoder)
    }
}


And here comes the tricky part. Next we create an enum DemoClassKeys, which is used to decode the dictionary containing the array of fields.

We also define an enum FieldDefinitionTypeKey which is used to serialize the type field (wrapperType in your case) for each object in the array.
And a final enum FieldDefinitionTypes with specifies all the possible FieldDefinition types.

With all the enums specified we can get to the serialization :

class DemoClass: Decodable {
    var fieldDefinitions: [FieldDefinition]

    enum DemoClassKeys: String, CodingKey {
        case fieldDefinitions
    }

    enum FieldDefinitionTypeKey: CodingKey {
        case type
    }

    enum FieldDefinitionTypes: String, Decodable {
        case textFieldDefinition = "TextFieldDefinition"
        case deterministicFieldDefinition = "DeterministicFieldDefinition"
    }

    required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: DemoClassKeys.self)
        var arrayOfFieldType = try container.nestedUnkeyedContainer(forKey: DemoClassKeys.fieldDefinitions)
        var fieldDefinitions = [FieldDefinition]()

        var tmp = arrayOfFieldType
        while(!arrayOfFieldType.isAtEnd) {
            let fieldDefinition = try arrayOfFieldType.nestedContainer(keyedBy: FieldDefinitionTypeKey.self)
            let type = try fieldDefinition.decode(FieldDefinitionTypes.self, forKey: FieldDefinitionTypeKey.type)
            switch type {
            case .textFieldDefinition:
                fieldDefinitions.append(try tmp.decode(TextFieldDefinition.self))
            case .deterministicFieldDefinition:
                fieldDefinitions.append(try tmp.decode(DeterministicFieldDefinition.self))
            }
        }
        self.fieldDefinitions = fieldDefinitions
    }
}


And Here is the full working Demo:

var json = """
{
    "fieldDefinitions": [
        {
            "type": "TextFieldDefinition",
            "maxLength": 5
        },
        {
            "type": "DeterministicFieldDefinition",
            "defaultValue": "VISA"
        }
    ]
}
"""

let jsonDecoder = JSONDecoder()
do {
    let results = try jsonDecoder.decode(DemoClass.self, from: json.data(using: .utf8)!)
    for result in results.fieldDefinitions {
        print(result.self)
    }
} catch {
    print("caught: \(error)")
}